kaxil commented on code in PR #71462:
URL: https://github.com/apache/airflow/pull/71462#discussion_r3765825944


##########
providers/anthropic/src/airflow/providers/anthropic/hooks/anthropic.py:
##########
@@ -123,6 +126,49 @@ def is_terminal(cls, status: str) -> bool:
 #: ``session.status_idle`` stop reason emitted when a session stops against 
its budget.
 BUDGET_REACHED = "budget_reached"
 
+#: What a caller may pass as a session budget: an amount in USD, or the raw 
API payload.
+BudgetSpec = str | int | float | Decimal | Mapping[str, Any]
+
+
+def build_budget(budget: BudgetSpec) -> dict[str, Any]:
+    """
+    Normalize a session budget into the API's ``max_list_cost`` payload.
+
+    A scalar is read as **US dollars** (``25``, ``25.0`` and ``"25.00"`` all 
mean $25.00).
+    The API wants minor units as an integer decimal string, so the conversion 
runs through
+    :class:`~decimal.Decimal` (never binary float) and rejects an amount finer 
than a cent
+    rather than silently rounding money. A mapping is deep-copied and 
otherwise returned
+    unchanged, so a raw payload the provider has not caught up with stays 
usable without a
+    provider release.
+
+    .. warning::
+        The ceiling is a stop trigger, not a cap: it is checked between model 
requests, so
+        a request already in flight can carry the session well past it.
+    """
+    if isinstance(budget, Mapping):
+        # Deep, not ``dict()``: a shallow copy leaves the nested 
``max_list_cost`` aliased
+        # to the caller's object, so a later edit of the returned payload 
would reach back
+        # into a templated operator field.
+        return deepcopy(dict(budget))
+    if isinstance(budget, bool):
+        raise ValueError(f"Invalid budget {budget!r}: expected an amount in 
USD or a mapping.")
+    try:
+        dollars = Decimal(str(budget))
+    except (InvalidOperation, ValueError) as e:
+        raise ValueError(f"Invalid budget {budget!r}: not a decimal amount in 
USD.") from e

Review Comment:
   Applied -- you are right that it was dead code. `Decimal(str(True))` raises 
`InvalidOperation` (`str(True)` is `'True'`), so bools were already rejected by 
the parse below; the guard only changed which message they got.
   
   One knock-on: `test_rejects_bad_amounts` had a case pinning the old 
`expected an amount` message for `True`, so that param now matches `not a 
decimal` instead. Without that the suite fails.



##########
providers/anthropic/docs/operators/anthropic.rst:
##########
@@ -147,9 +147,12 @@ Parameters
 * ``poll_interval`` — seconds between session status checks.
 * ``timeout`` — seconds to wait for a terminal status; defaults to 24 hours.
 * ``vault_ids`` — vault IDs providing MCP/credential access to the session.
+* ``budget`` -- spend ceiling for the session, in US dollars (``25.00``) or as 
the raw API
+  payload (a mapping). Templated. See `Session budgets`_ below.

Review Comment:
   Expanded -- it now reads "Templated, so it can come from a Variable, a 
params entry, or an upstream XCom." Agreed that the bare word assumes the 
reader already knows what it buys them.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to