@@ -187,10 +187,17 @@ def _api_root(self) -> str:
187187 return self ._base_url [: - len (suffix )]
188188 return self ._base_url
189189
190- def _request (self , method : str , path : str , timeout : int = 30 , base : Optional [str ] = None , ** kwargs ) -> Any :
190+ def _request (
191+ self , method : str , path : str , timeout : int = 30 , base : Optional [str ] = None , retry : bool = True , ** kwargs
192+ ) -> Any :
193+ # retry=False for non-idempotent judge-spending POSTs (sweep, coherence, portability,
194+ # tuning): a client-side timeout must not fire the same LLM-billing work a second time
195+ # while the first invocation is still running server-side. Same precedent as
196+ # EvaluationsClient._request / analyze_run.
191197 url = f"{ base or self ._base_url } { path } "
192198 last_exc : Optional [Exception ] = None
193- for attempt , wait in enumerate ([0.0 ] + _RETRY_BACKOFF ):
199+ schedule = [0.0 ] + _RETRY_BACKOFF if retry else [0.0 ]
200+ for attempt , wait in enumerate (schedule ):
194201 if wait :
195202 time .sleep (wait )
196203 try :
@@ -204,7 +211,7 @@ def _request(self, method: str, path: str, timeout: int = 30, base: Optional[str
204211 raise AgentXAuthError ("Invalid or missing API key" )
205212 if resp .status_code == 422 :
206213 raise AgentXValidationError (resp .text )
207- if resp .status_code in _RETRYABLE_STATUS and attempt < _MAX_RETRIES - 1 :
214+ if retry and resp .status_code in _RETRYABLE_STATUS and attempt < _MAX_RETRIES - 1 :
208215 logger .debug (
209216 "Retryable status %d (attempt %d)" , resp .status_code , attempt + 1
210217 )
@@ -421,7 +428,7 @@ def run_session_coherence_check(self, session_id: str) -> dict:
421428 button. Raises AgentXMonitorError if the engine has no judge key configured."""
422429 data = self ._request (
423430 "POST" , f"/agent-monitoring/sessions/{ session_id } /coherence-check" ,
424- base = self ._api_root (), timeout = 180 ,
431+ base = self ._api_root (), timeout = 180 , retry = False ,
425432 )
426433 return data .get ("score" , data ) if isinstance (data , dict ) else data
427434
@@ -444,7 +451,7 @@ def run_session_sweep(self) -> dict:
444451 engines run this automatically every minute; the manual trigger exists for demos,
445452 tests, and backfills. Returns ``{"judged": n}``."""
446453 return self ._request (
447- "POST" , "/agent-monitoring/session-sweep/run" , base = self ._api_root (), timeout = 300
454+ "POST" , "/agent-monitoring/session-sweep/run" , base = self ._api_root (), timeout = 300 , retry = False
448455 )
449456
450457 # ------------------------------------------------------------------
@@ -457,7 +464,7 @@ def run_model_portability(self, trace_id: str, model_ids: List[str]) -> dict:
457464 plus judging, so expect tens of seconds."""
458465 return self ._request (
459466 "POST" , f"/agent-monitoring/traces/{ trace_id } /portability" ,
460- base = self ._api_root (), json = {"modelIds" : model_ids }, timeout = 300 ,
467+ base = self ._api_root (), json = {"modelIds" : model_ids }, timeout = 300 , retry = False ,
461468 )
462469
463470 # ------------------------------------------------------------------
0 commit comments