Skip to content

Commit aed30d5

Browse files
authored
Merge pull request #45 from AgentX-ai/fix/score-groups
fix score correctness with group
2 parents 98f8286 + 844be54 commit aed30d5

5 files changed

Lines changed: 28 additions & 10 deletions

File tree

agentx/monitor/client.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -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
# ------------------------------------------------------------------

agentx/monitor/patterns.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ def builder(
114114
agent_ids=agent_ids,
115115
)
116116

117+
def delete(self, pattern_id: str) -> None:
118+
"""Delete a pattern. Its historical signals remain as history."""
119+
self._client._request("DELETE", f"/agent-monitoring/patterns/{pattern_id}", base=self._client._api_root())
120+
117121
def get(self, pattern_id: str) -> MonitorPattern:
118122
return self._client.get_pattern(pattern_id)
119123

agentx/monitor/scorer_groups.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ def create(
6767
"""``members``: [{"kind": "judge"|"pattern"|"custom", "refId": ..., "weight": 1, "gate": False}].
6868
``online``: {"enabled": True, "sampleRate": 0.1, "alertThreshold": 5, "severity": "medium"}.
6969
Add ``"scope": "session", "idleSeconds": 120`` to score whole multi-turn sessions once
70-
idle, instead of each sampled trace.
71-
or None for offline-only."""
70+
idle, instead of each sampled trace. Pass ``online=None`` (the default) for a group
71+
that only grades offline dataset runs."""
7272
payload: Dict[str, Any] = {"name": name, "members": members}
7373
if description is not None:
7474
payload["description"] = description

agentx/monitor/sessions.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ def spans(self, session_id: str) -> List[dict]:
2222

2323
def scores(self, session_id: str) -> List[dict]:
2424
"""Session-level verdicts, newest first. ``kind`` says who scored: a session-scoped
25-
online evaluator (``online-eval:<id>``) or a session-scoped scorer group
26-
(``scorer-group:<id>``)."""
25+
online evaluator (``online-eval:<id>``), a session-scoped scorer group
26+
(``scorer-group:<id>``), or legacy ``"coherence"`` rows written before the Session
27+
Baseline Judge existed - branch defensively on unknown kinds."""
2728
return self._client.list_session_scores(session_id)
2829

2930
def run_sweep(self) -> dict:

tests/test_selfhost_analysis_fallback.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,12 @@ def test_the_fallback_request_gets_the_long_analysis_timeout():
217217
assert kwargs["timeout"] > 60, "a synchronous judge pass needs more than the 30s default"
218218
assert kwargs["json"]["judges"] == [{"model": "gpt-5.6-luna"}]
219219

220+
# judges=None must OMIT the key - the engine then scores with its platform default model;
221+
# injecting a hosted-only default (the old "gpt-5.5") produced uncallable judges.
222+
client.analyze_run(RUN)
223+
_, _, kwargs = session.calls[-1]
224+
assert "judges" not in kwargs["json"]
225+
220226

221227
# ---------------------------------------------------------------------------
222228
# Only a 404 means "wrong engine"

0 commit comments

Comments
 (0)