22
33The generator drafts knowledge changes; the judge (a different model family)
44grades them. Both are pinned by (provider, model, reasoning effort) and
5- recorded on every call. An unavailable backend, a timeout or an unparseable
6- reply raises ``ModelUnavailable``; the caller leaves the work queued. It never
5+ recorded on every call. An unavailable backend, a timeout, a call stopped
6+ at its spend cap or an unparseable reply raises ``ModelUnavailable``; the caller leaves the work queued. It never
77switches to a weaker model: a verdict must always say which model made it.
88"""
99
@@ -61,6 +61,7 @@ class ModelReply:
6161 served_model : str
6262 usage : dict
6363 seconds : float
64+ cost_usd : float | None = None # the transport's reported spend (None: unknown)
6465
6566
6667def parse_json_object (text : str ) -> dict :
@@ -104,36 +105,57 @@ def _transport(self, provider: str):
104105 return transport
105106
106107 def call_json (self , role : ModelRole , * , system : str , prompt : str ,
107- validate : Callable [[dict ], None ] | None = None ) -> ModelReply :
108+ validate : Callable [[dict ], None ] | None = None ,
109+ max_budget_usd : float | None = None ) -> ModelReply :
110+ """``max_budget_usd`` is a per-call STOP THRESHOLD, not a hard cap:
111+ the transport starts no further API request once the call's spend
112+ reaches it, but the request that crosses it is billed in full. A
113+ caller enforcing a hard ceiling must reserve the threshold plus one
114+ request's worst case before calling. Requesting a threshold from a
115+ transport that cannot stop at one (``stops_at_spend`` False) is
116+ refused before dispatch; it is never approximated with ``max_tokens``."""
108117 transport = self ._transport (role .provider )
118+ cap : dict = {}
119+ if max_budget_usd is not None :
120+ if not max_budget_usd > 0 :
121+ raise ModelUnavailable (f"{ role .label ()} : max_budget_usd must be positive" )
122+ if not getattr (transport , "stops_at_spend" , False ):
123+ raise ModelUnavailable (f"{ role .provider } cannot stop a call at a spend threshold" )
124+ cap = {"max_budget_usd" : max_budget_usd }
109125 started = time .time ()
110126 identity = {"role" : role .name , "requested" : role .label (), "provider" : role .provider ,
111127 "model" : role .model , "effort" : role .effort }
112128 try :
113129 reply = transport .complete (
114130 system = system , messages = [{"role" : "user" , "content" : prompt }],
115- model = role .model , effort = role .effort , role = role .name )
131+ model = role .model , effort = role .effort , role = role .name , ** cap )
116132 except Exception as exc : # the transport's own failure modes
117133 if self ._recorder is not None :
118134 self ._recorder ({** identity , "served_model" : "" , "stop_reason" : "" , "usage" : {},
135+ "cost_usd" : None , "max_budget_usd" : max_budget_usd ,
119136 "seconds" : round (time .time () - started , 3 ), "system" : system ,
120137 "prompt" : prompt , "reply" : "" , "error" : str (exc )[:2000 ]})
121138 raise ModelUnavailable (f"{ role .label ()} failed: { exc } " ) from exc
122139 seconds = time .time () - started
123140 text = "" .join (getattr (block , "text" , "" ) or "" for block in getattr (reply , "blocks" , []))
141+ usage = dict (getattr (reply , "usage" , {}) or {})
142+ cost = usage .get ("cost_usd" )
143+ cost_usd = float (cost ) if isinstance (cost , (int , float )) and not isinstance (cost , bool ) else None
124144 record = {
125145 ** identity ,
126146 "served_model" : getattr (reply , "model" , "" ) or "" ,
127147 "stop_reason" : getattr (reply , "stop_reason" , "" ),
128- "usage" : dict ( getattr ( reply , "usage" , {}) or {}) ,
148+ "usage" : usage , "cost_usd" : cost_usd , "max_budget_usd" : max_budget_usd ,
129149 "seconds" : round (seconds , 3 ), "system" : system , "prompt" : prompt , "reply" : text ,
130150 }
131151 # the record carries the call's FINAL verdict: a truncated, empty,
132152 # unparseable or schema-failing reply is recorded as a failure, so it
133153 # never becomes a training example
134154 failure : ModelUnavailable | None = None
135155 data : dict = {}
136- if getattr (reply , "stop_reason" , "" ) == "max_tokens" or not text .strip ():
156+ if getattr (reply , "stop_reason" , "" ) == "max_budget" :
157+ failure = ModelUnavailable (f"{ role .label ()} stopped at its spend threshold ${ max_budget_usd } " )
158+ elif getattr (reply , "stop_reason" , "" ) == "max_tokens" or not text .strip ():
137159 failure = ModelUnavailable (f"{ role .label ()} timed out or returned nothing" )
138160 else :
139161 try :
@@ -149,4 +171,4 @@ def call_json(self, role: ModelRole, *, system: str, prompt: str,
149171 self ._recorder ({** record , "error" : str (failure ) if failure else "" })
150172 if failure is not None :
151173 raise failure
152- return ModelReply (role , data , text , record ["served_model" ], record [ " usage" ] , seconds )
174+ return ModelReply (role , data , text , record ["served_model" ], usage , seconds , cost_usd )
0 commit comments