agentx-python 0.8.8__tar.gz → 0.8.10__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agentx_python-0.8.8 → agentx_python-0.8.10}/PKG-INFO +2 -2
- {agentx_python-0.8.8 → agentx_python-0.8.10}/README.md +1 -1
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/cli.py +34 -16
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/evaluations/client.py +12 -1
- agentx_python-0.8.10/agentx/version.py +7 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx_python.egg-info/PKG-INFO +2 -2
- agentx_python-0.8.10/tests/test_cli_launcher.py +117 -0
- agentx_python-0.8.8/agentx/version.py +0 -1
- agentx_python-0.8.8/tests/test_cli_launcher.py +0 -89
- {agentx_python-0.8.8 → agentx_python-0.8.10}/LICENSE +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/__init__.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/agentx.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/evaluations/__init__.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/evaluations/_term.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/evaluations/adapters/__init__.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/evaluations/adapters/http_endpoint.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/evaluations/adapters/precomputed.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/evaluations/adapters/raw.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/evaluations/datasets.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/evaluations/evaluation_settings.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/evaluations/models.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/evaluations/prompts.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/evaluations/reporting.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/evaluations/results.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/evaluations/runner.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/evaluations/tool_schemas.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/evaluations/tracing.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/exceptions.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/export.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/feedback.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/integrations/__init__.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/integrations/_traced_call.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/integrations/anthropic.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/integrations/autogen.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/integrations/crewai.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/integrations/databricks.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/integrations/google_adk.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/integrations/google_genai.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/integrations/langchain.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/integrations/litellm.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/integrations/llamaindex.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/integrations/moveworks.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/integrations/openai.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/integrations/openai_agents.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/monitor/__init__.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/monitor/agents.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/monitor/client.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/monitor/judge_scorers.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/monitor/models.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/monitor/online_evaluators.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/monitor/patterns.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/monitor/profile.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/monitor/review_queue.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/monitor/rules.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/monitor/scorers.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/monitor/sessions.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/monitor/signals.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/outcomes.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/projects.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/py.typed +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/resources/__init__.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/resources/agent.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/resources/conversation.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/resources/workforce.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/testing.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/traces.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/tracing/__init__.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/tracing/ci_types.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/tracing/eval_scope.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/tracing/ingest_client.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/tracing/tracer.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx/util.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx_python.egg-info/SOURCES.txt +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx_python.egg-info/dependency_links.txt +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx_python.egg-info/entry_points.txt +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx_python.egg-info/not-zip-safe +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx_python.egg-info/requires.txt +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/agentx_python.egg-info/top_level.txt +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/setup.cfg +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/setup.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/tests/test_deep_dive_fixes.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/tests/test_docs_match_sdk.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/tests/test_eval_scope.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/tests/test_integration.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/tests/test_integrations.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/tests/test_judge_scorers.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/tests/test_pairwise.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/tests/test_review_queue.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/tests/test_runner_features.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/tests/test_selfhost_analysis_fallback.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/tests/test_span_tree.py +0 -0
- {agentx_python-0.8.8 → agentx_python-0.8.10}/tests/test_testing.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentx-python
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.10
|
|
4
4
|
Summary: Official Python SDK for AgentX (https://www.agentx.so/)
|
|
5
5
|
Home-page: https://github.com/AgentX-ai/AgentX-python
|
|
6
6
|
Author: Robin Wang and AgentX Team
|
|
@@ -193,7 +193,7 @@ client.evaluations.run(
|
|
|
193
193
|
|
|
194
194
|
See [Prompt registry](EVALUATIONS.md#prompt-registry) in the full guide, or [self-host's docs](https://docs.agentx.so/improve/prompt-management) for the "Suggest improvement" dashboard flow (self-host only - no hosted-SaaS equivalent yet).
|
|
195
195
|
|
|
196
|
-
On self-host, a finalized run can also **gate a CI job**: `
|
|
196
|
+
On self-host, a finalized run can also **gate a CI job**: `run.gate(fail_under=7, no_regression=True)` (on the run context `.execute()` returns) checks the run's average rating against an absolute floor and/or the dataset's previous run, prints per-check verdicts into the CI log, and returns an exit code - `sys.exit(gate.exit_code)` blocks the merge on regression. Recorded gates appear in the dashboard's CI Gates tab. See [self-host's CI docs](https://docs.agentx.so/integrations/self-host-ci) for the GitHub Actions recipe.
|
|
197
197
|
|
|
198
198
|
See **[EVALUATIONS.md](EVALUATIONS.md)** for the full guide - dataset builder, framework adapters, similarity metrics, smoke testing, judge configuration, prompt registry, and the complete API reference.
|
|
199
199
|
|
|
@@ -129,7 +129,7 @@ client.evaluations.run(
|
|
|
129
129
|
|
|
130
130
|
See [Prompt registry](EVALUATIONS.md#prompt-registry) in the full guide, or [self-host's docs](https://docs.agentx.so/improve/prompt-management) for the "Suggest improvement" dashboard flow (self-host only - no hosted-SaaS equivalent yet).
|
|
131
131
|
|
|
132
|
-
On self-host, a finalized run can also **gate a CI job**: `
|
|
132
|
+
On self-host, a finalized run can also **gate a CI job**: `run.gate(fail_under=7, no_regression=True)` (on the run context `.execute()` returns) checks the run's average rating against an absolute floor and/or the dataset's previous run, prints per-check verdicts into the CI log, and returns an exit code - `sys.exit(gate.exit_code)` blocks the merge on regression. Recorded gates appear in the dashboard's CI Gates tab. See [self-host's CI docs](https://docs.agentx.so/integrations/self-host-ci) for the GitHub Actions recipe.
|
|
133
133
|
|
|
134
134
|
See **[EVALUATIONS.md](EVALUATIONS.md)** for the full guide - dataset builder, framework adapters, similarity metrics, smoke testing, judge configuration, prompt registry, and the complete API reference.
|
|
135
135
|
|
|
@@ -4,18 +4,21 @@ Evaluate, Monitor), published separately at github.com/AgentX-ai/AgentX-trace-ev
|
|
|
4
4
|
wrapping a Bun-compiled TypeScript engine, not Python). That compiled engine binary is tens of
|
|
5
5
|
megabytes; most `pip install agentx-python` installs are just this SDK talking to the hosted
|
|
6
6
|
AgentX SaaS and would never touch it, so it isn't bundled in this package. Instead, this command
|
|
7
|
-
downloads the
|
|
7
|
+
downloads the release into ~/.agentx/bin the first time it's needed (mirroring
|
|
8
8
|
AgentX-trace-eval's own install.sh) and then hands off to the real `agentx-server` binary.
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
Versioning: each SDK release pins the engine release it was tested against
|
|
11
|
+
(agentx.version.ENGINE_VERSION); the launcher installs that pin and converges to it - if the
|
|
12
|
+
stamped install (~/.agentx/bin/.version) differs from the pin, it re-installs, so upgrading the
|
|
13
|
+
SDK upgrades the engine to the matching pair. `--update` force-reinstalls the resolved version
|
|
14
|
+
(recovering a broken or pre-stamp install). Set AGENTX_TRACE_EVAL_VERSION to a tag or `latest`
|
|
15
|
+
to override the pin; in `latest` mode the launcher trusts whatever is installed, prints a
|
|
16
|
+
fail-open notice when GitHub has something newer, and `--update` fetches it.
|
|
15
17
|
|
|
16
18
|
Usage:
|
|
17
19
|
agentx-trace-eval --dev
|
|
18
20
|
agentx-trace-eval --update --dev
|
|
21
|
+
AGENTX_TRACE_EVAL_VERSION=latest agentx-trace-eval --update --dev
|
|
19
22
|
agentx-trace-eval --port 5000 --db-url postgres://...
|
|
20
23
|
"""
|
|
21
24
|
|
|
@@ -32,6 +35,8 @@ from typing import Optional, Tuple
|
|
|
32
35
|
|
|
33
36
|
import requests
|
|
34
37
|
|
|
38
|
+
from agentx.version import ENGINE_VERSION
|
|
39
|
+
|
|
35
40
|
REPO = "AgentX-ai/AgentX-trace-eval"
|
|
36
41
|
INSTALL_DIR = Path(os.environ.get("AGENTX_INSTALL_DIR", str(Path.home() / ".agentx" / "bin")))
|
|
37
42
|
_BIN_NAMES = ("agentx", "agentx-server", "agentx-engine")
|
|
@@ -114,11 +119,17 @@ def _latest_release_tag() -> Optional[str]:
|
|
|
114
119
|
return None
|
|
115
120
|
|
|
116
121
|
|
|
122
|
+
def resolve_version() -> str:
|
|
123
|
+
"""The engine version this launcher should be running: the AGENTX_TRACE_EVAL_VERSION
|
|
124
|
+
override (a tag, or `latest`) when set, else the SDK's tested pin."""
|
|
125
|
+
return os.environ.get("AGENTX_TRACE_EVAL_VERSION") or ENGINE_VERSION
|
|
126
|
+
|
|
127
|
+
|
|
117
128
|
def _install(version: str = "latest") -> None:
|
|
118
129
|
os_name, arch = _platform_tag()
|
|
119
130
|
INSTALL_DIR.mkdir(parents=True, exist_ok=True)
|
|
120
131
|
|
|
121
|
-
print(f"agentx-trace-eval: downloading agentx ({os_name}/{arch})...", file=sys.stderr)
|
|
132
|
+
print(f"agentx-trace-eval: downloading agentx {version} ({os_name}/{arch})...", file=sys.stderr)
|
|
122
133
|
with tempfile.TemporaryDirectory() as tmp:
|
|
123
134
|
tmp_dir = Path(tmp)
|
|
124
135
|
archive = tmp_dir / "agentx.tar.gz"
|
|
@@ -178,23 +189,30 @@ def _install(version: str = "latest") -> None:
|
|
|
178
189
|
_extract_tar(web_archive, web_dir)
|
|
179
190
|
|
|
180
191
|
|
|
181
|
-
def ensure_installed(version: str =
|
|
182
|
-
"""
|
|
192
|
+
def ensure_installed(version: Optional[str] = None, force: bool = False) -> Path:
|
|
193
|
+
"""Installs agentx-server (+ its engine and dashboard) into ~/.agentx/bin when it's missing
|
|
183
194
|
there, when `force` is set, or when `version` is a specific tag that differs from the
|
|
184
|
-
|
|
185
|
-
|
|
195
|
+
stamped install - the convergence that makes an SDK upgrade carry its tested engine along.
|
|
196
|
+
`version` defaults to resolve_version() (the AGENTX_TRACE_EVAL_VERSION override, else the
|
|
197
|
+
SDK's pin); pass "latest" to trust whatever is installed and only fetch when missing.
|
|
198
|
+
Returns the path to the agentx-server executable. Set AGENTX_INSTALL_DIR to change where
|
|
199
|
+
this looks/installs."""
|
|
200
|
+
if version is None:
|
|
201
|
+
version = resolve_version()
|
|
186
202
|
server_path = INSTALL_DIR / "agentx-server"
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
if force or
|
|
203
|
+
stamped = _stamped_version()
|
|
204
|
+
pin_mismatch = version != "latest" and stamped != version
|
|
205
|
+
if force or pin_mismatch or not server_path.exists():
|
|
190
206
|
_install(version=version)
|
|
191
207
|
return server_path
|
|
192
208
|
|
|
193
209
|
|
|
194
210
|
def _maybe_print_update_notice(installed: Optional[str]) -> None:
|
|
211
|
+
"""Only relevant in `latest` mode, where nothing converges automatically - a quick fail-open
|
|
212
|
+
check tells the user when the world has moved on. (Pinned mode needs no notice: a pin
|
|
213
|
+
mismatch re-installs instead of nagging.)"""
|
|
195
214
|
latest = _latest_release_tag()
|
|
196
215
|
if installed is None:
|
|
197
|
-
# Pre-stamp install (or a wiped stamp): age unknown, so always point at --update.
|
|
198
216
|
print(
|
|
199
217
|
"agentx-trace-eval: installed engine version unknown"
|
|
200
218
|
+ (f" (newest release is {latest})" if latest else "")
|
|
@@ -215,7 +233,7 @@ def main() -> None:
|
|
|
215
233
|
# --update belongs to this launcher, not to agentx-server - consume it before the handoff.
|
|
216
234
|
args = [a for a in args if a != "--update"]
|
|
217
235
|
|
|
218
|
-
version =
|
|
236
|
+
version = resolve_version()
|
|
219
237
|
server_path = ensure_installed(version=version, force=force_update)
|
|
220
238
|
if not server_path.exists():
|
|
221
239
|
raise SystemExit(f"agentx-trace-eval: {server_path} still missing after install, giving up")
|
|
@@ -37,6 +37,9 @@ _RETRY_BACKOFF = [1.0, 2.0, 4.0]
|
|
|
37
37
|
# wait out the whole job on one connection. Matches EvaluationRunContext.analyze()'s own
|
|
38
38
|
# default timeout.
|
|
39
39
|
_SELF_HOST_ANALYZE_TIMEOUT = 1800
|
|
40
|
+
# Batch result submission scores each result synchronously inside the request (one judge call
|
|
41
|
+
# per result on the sync path) - a big batch on a slow judge legitimately takes minutes.
|
|
42
|
+
_SELF_HOST_SCORING_TIMEOUT = 900
|
|
40
43
|
|
|
41
44
|
|
|
42
45
|
class AgentXEvaluationsError(Exception):
|
|
@@ -344,7 +347,15 @@ class EvaluationsClient:
|
|
|
344
347
|
"batchId": batch_id,
|
|
345
348
|
"results": [_result_to_payload(r) for r in results],
|
|
346
349
|
}
|
|
347
|
-
|
|
350
|
+
# Scoring is synchronous inside this request (a judge call per result - the runner's
|
|
351
|
+
# spinner says "~60s+" for a reason), so the default 30s timeout + silent backoff loop
|
|
352
|
+
# re-POSTed the batch WHILE the first submission was still scoring, and the two raced
|
|
353
|
+
# into the engine's (run_id, idempotency_key) unique constraint. Long timeout, no
|
|
354
|
+
# transport retry - the runner's own logged retry-once is the retry layer here.
|
|
355
|
+
data = self._request(
|
|
356
|
+
"POST", f"/runs/{run_id}/results", json=payload,
|
|
357
|
+
timeout=_SELF_HOST_SCORING_TIMEOUT, retry=False,
|
|
358
|
+
)
|
|
348
359
|
return BatchAppendResponse(**data)
|
|
349
360
|
|
|
350
361
|
def finalize_run(self, run_id: str) -> Dict[str, Any]:
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
VERSION = "0.8.10"
|
|
2
|
+
|
|
3
|
+
# The AgentX-trace-eval release this SDK version is tested against - what `agentx-trace-eval`
|
|
4
|
+
# installs and converges to (see agentx/cli.py). Bump together with VERSION when releasing, so
|
|
5
|
+
# every published SDK names a known-good engine+dashboard pair. Users can override with
|
|
6
|
+
# AGENTX_TRACE_EVAL_VERSION=<tag|latest>.
|
|
7
|
+
ENGINE_VERSION = "v0.3.1"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentx-python
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.10
|
|
4
4
|
Summary: Official Python SDK for AgentX (https://www.agentx.so/)
|
|
5
5
|
Home-page: https://github.com/AgentX-ai/AgentX-python
|
|
6
6
|
Author: Robin Wang and AgentX Team
|
|
@@ -193,7 +193,7 @@ client.evaluations.run(
|
|
|
193
193
|
|
|
194
194
|
See [Prompt registry](EVALUATIONS.md#prompt-registry) in the full guide, or [self-host's docs](https://docs.agentx.so/improve/prompt-management) for the "Suggest improvement" dashboard flow (self-host only - no hosted-SaaS equivalent yet).
|
|
195
195
|
|
|
196
|
-
On self-host, a finalized run can also **gate a CI job**: `
|
|
196
|
+
On self-host, a finalized run can also **gate a CI job**: `run.gate(fail_under=7, no_regression=True)` (on the run context `.execute()` returns) checks the run's average rating against an absolute floor and/or the dataset's previous run, prints per-check verdicts into the CI log, and returns an exit code - `sys.exit(gate.exit_code)` blocks the merge on regression. Recorded gates appear in the dashboard's CI Gates tab. See [self-host's CI docs](https://docs.agentx.so/integrations/self-host-ci) for the GitHub Actions recipe.
|
|
197
197
|
|
|
198
198
|
See **[EVALUATIONS.md](EVALUATIONS.md)** for the full guide - dataset builder, framework adapters, similarity metrics, smoke testing, judge configuration, prompt registry, and the complete API reference.
|
|
199
199
|
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""The agentx-trace-eval launcher's version pinning: each SDK release names the engine release
|
|
2
|
+
it was tested against (agentx.version.ENGINE_VERSION) and the launcher converges the local
|
|
3
|
+
install to it, so `pip install -U agentx-python` upgrades the engine to the matching pair.
|
|
4
|
+
Before this, the launcher downloaded "latest" once and then ran that binary forever - a July
|
|
5
|
+
install silently served a July engine months later, with no stamp and no way to update."""
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from agentx import cli
|
|
10
|
+
from agentx.version import ENGINE_VERSION
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.fixture
|
|
14
|
+
def install_dir(tmp_path, monkeypatch):
|
|
15
|
+
monkeypatch.setattr(cli, "INSTALL_DIR", tmp_path)
|
|
16
|
+
return tmp_path
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _fake_install(calls):
|
|
20
|
+
def fake(version="latest"):
|
|
21
|
+
calls.append(version)
|
|
22
|
+
|
|
23
|
+
return fake
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_engine_pin_is_a_release_tag():
|
|
27
|
+
# The pin the SDK ships must look like a real trace-eval release tag (v<series>.<n>), not
|
|
28
|
+
# "latest" - "latest" as a pin would silently disable the whole tested-pair contract.
|
|
29
|
+
assert cli.ENGINE_VERSION == ENGINE_VERSION
|
|
30
|
+
import re
|
|
31
|
+
|
|
32
|
+
assert re.fullmatch(r"v\d+\.\d+\.\d+", ENGINE_VERSION)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_resolve_version_prefers_env_override(monkeypatch):
|
|
36
|
+
monkeypatch.delenv("AGENTX_TRACE_EVAL_VERSION", raising=False)
|
|
37
|
+
assert cli.resolve_version() == ENGINE_VERSION
|
|
38
|
+
monkeypatch.setenv("AGENTX_TRACE_EVAL_VERSION", "latest")
|
|
39
|
+
assert cli.resolve_version() == "latest"
|
|
40
|
+
monkeypatch.setenv("AGENTX_TRACE_EVAL_VERSION", "v9.9.9")
|
|
41
|
+
assert cli.resolve_version() == "v9.9.9"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_tag_resolved_from_redirected_release_url():
|
|
45
|
+
url = "https://github.com/AgentX-ai/AgentX-trace-eval/releases/download/v0.3.1/agentx_darwin_arm64.tar.gz"
|
|
46
|
+
assert cli._tag_from_release_url(url) == "v0.3.1"
|
|
47
|
+
assert cli._tag_from_release_url("https://example.com/not-a-release") is None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_install_matching_pin_is_not_redownloaded(install_dir, monkeypatch):
|
|
51
|
+
(install_dir / "agentx-server").write_text("bin")
|
|
52
|
+
(install_dir / ".version").write_text(ENGINE_VERSION + "\n")
|
|
53
|
+
calls = []
|
|
54
|
+
monkeypatch.setattr(cli, "_install", _fake_install(calls))
|
|
55
|
+
cli.ensure_installed(version=ENGINE_VERSION)
|
|
56
|
+
assert calls == []
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_stamp_differing_from_pin_converges(install_dir, monkeypatch):
|
|
60
|
+
# The SDK-upgrade path: binaries exist but were installed for an older pin (or pre-stamp,
|
|
61
|
+
# where .version is missing entirely) - the launcher re-installs the pinned pair.
|
|
62
|
+
(install_dir / "agentx-server").write_text("bin")
|
|
63
|
+
(install_dir / ".version").write_text("v0.2.0\n")
|
|
64
|
+
calls = []
|
|
65
|
+
monkeypatch.setattr(cli, "_install", _fake_install(calls))
|
|
66
|
+
cli.ensure_installed(version=ENGINE_VERSION)
|
|
67
|
+
assert calls == [ENGINE_VERSION]
|
|
68
|
+
|
|
69
|
+
(install_dir / ".version").unlink()
|
|
70
|
+
calls.clear()
|
|
71
|
+
cli.ensure_installed(version=ENGINE_VERSION)
|
|
72
|
+
assert calls == [ENGINE_VERSION]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_latest_mode_trusts_existing_install(install_dir, monkeypatch):
|
|
76
|
+
(install_dir / "agentx-server").write_text("bin")
|
|
77
|
+
(install_dir / ".version").write_text("v0.2.0\n")
|
|
78
|
+
calls = []
|
|
79
|
+
monkeypatch.setattr(cli, "_install", _fake_install(calls))
|
|
80
|
+
cli.ensure_installed(version="latest")
|
|
81
|
+
assert calls == []
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_force_reinstalls_over_existing(install_dir, monkeypatch):
|
|
85
|
+
(install_dir / "agentx-server").write_text("bin")
|
|
86
|
+
(install_dir / ".version").write_text(ENGINE_VERSION + "\n")
|
|
87
|
+
calls = []
|
|
88
|
+
monkeypatch.setattr(cli, "_install", _fake_install(calls))
|
|
89
|
+
cli.ensure_installed(version=ENGINE_VERSION, force=True)
|
|
90
|
+
assert calls == [ENGINE_VERSION]
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_update_notice_names_both_versions(install_dir, monkeypatch, capsys):
|
|
94
|
+
monkeypatch.setattr(cli, "_latest_release_tag", lambda: "v0.9.0")
|
|
95
|
+
cli._maybe_print_update_notice("v0.3.1")
|
|
96
|
+
err = capsys.readouterr().err
|
|
97
|
+
assert "v0.3.1" in err and "v0.9.0" in err and "--update" in err
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_update_notice_handles_unstamped_install(install_dir, monkeypatch, capsys):
|
|
101
|
+
monkeypatch.setattr(cli, "_latest_release_tag", lambda: None)
|
|
102
|
+
cli._maybe_print_update_notice(None)
|
|
103
|
+
assert "--update" in capsys.readouterr().err
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def test_update_notice_is_silent_when_current(install_dir, monkeypatch, capsys):
|
|
107
|
+
monkeypatch.setattr(cli, "_latest_release_tag", lambda: "v0.3.1")
|
|
108
|
+
cli._maybe_print_update_notice("v0.3.1")
|
|
109
|
+
assert capsys.readouterr().err == ""
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def test_latest_release_tag_fails_open(monkeypatch):
|
|
113
|
+
def boom(*args, **kwargs):
|
|
114
|
+
raise OSError("offline")
|
|
115
|
+
|
|
116
|
+
monkeypatch.setattr(cli.requests, "get", boom)
|
|
117
|
+
assert cli._latest_release_tag() is None
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
VERSION = "0.8.8"
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
"""The agentx-trace-eval launcher's staleness fixes: before these, ensure_installed downloaded
|
|
2
|
-
once and then ran that binary forever - a July install silently served a July engine months
|
|
3
|
-
later, with no version stamp, no way to ask for an update, and the dashboard fetched from
|
|
4
|
-
releases/latest independently of the engine (so the two halves of one install could skew)."""
|
|
5
|
-
|
|
6
|
-
import io
|
|
7
|
-
import sys
|
|
8
|
-
|
|
9
|
-
import pytest
|
|
10
|
-
|
|
11
|
-
from agentx import cli
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@pytest.fixture
|
|
15
|
-
def install_dir(tmp_path, monkeypatch):
|
|
16
|
-
monkeypatch.setattr(cli, "INSTALL_DIR", tmp_path)
|
|
17
|
-
return tmp_path
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def _fake_install(calls):
|
|
21
|
-
def fake(version="latest"):
|
|
22
|
-
calls.append(version)
|
|
23
|
-
|
|
24
|
-
return fake
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def test_tag_resolved_from_redirected_release_url():
|
|
28
|
-
url = "https://github.com/AgentX-ai/AgentX-trace-eval/releases/download/v0.3.0/agentx_darwin_arm64.tar.gz"
|
|
29
|
-
assert cli._tag_from_release_url(url) == "v0.3.0"
|
|
30
|
-
assert cli._tag_from_release_url("https://example.com/not-a-release") is None
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def test_existing_install_is_not_redownloaded(install_dir, monkeypatch):
|
|
34
|
-
(install_dir / "agentx-server").write_text("bin")
|
|
35
|
-
calls = []
|
|
36
|
-
monkeypatch.setattr(cli, "_install", _fake_install(calls))
|
|
37
|
-
cli.ensure_installed(version="latest")
|
|
38
|
-
assert calls == []
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def test_force_reinstalls_over_existing(install_dir, monkeypatch):
|
|
42
|
-
(install_dir / "agentx-server").write_text("bin")
|
|
43
|
-
calls = []
|
|
44
|
-
monkeypatch.setattr(cli, "_install", _fake_install(calls))
|
|
45
|
-
cli.ensure_installed(version="latest", force=True)
|
|
46
|
-
assert calls == ["latest"]
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def test_changed_version_pin_reinstalls(install_dir, monkeypatch):
|
|
50
|
-
(install_dir / "agentx-server").write_text("bin")
|
|
51
|
-
(install_dir / ".version").write_text("v0.2.0\n")
|
|
52
|
-
calls = []
|
|
53
|
-
monkeypatch.setattr(cli, "_install", _fake_install(calls))
|
|
54
|
-
|
|
55
|
-
# Same pin: no download. Different pin: download. "latest": trusts what's there.
|
|
56
|
-
cli.ensure_installed(version="v0.2.0")
|
|
57
|
-
assert calls == []
|
|
58
|
-
cli.ensure_installed(version="v0.3.0")
|
|
59
|
-
assert calls == ["v0.3.0"]
|
|
60
|
-
calls.clear()
|
|
61
|
-
cli.ensure_installed(version="latest")
|
|
62
|
-
assert calls == []
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def test_update_notice_names_both_versions(install_dir, monkeypatch, capsys):
|
|
66
|
-
monkeypatch.setattr(cli, "_latest_release_tag", lambda: "v0.9.0")
|
|
67
|
-
cli._maybe_print_update_notice("v0.2.0")
|
|
68
|
-
err = capsys.readouterr().err
|
|
69
|
-
assert "v0.2.0" in err and "v0.9.0" in err and "--update" in err
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def test_update_notice_handles_unstamped_install(install_dir, monkeypatch, capsys):
|
|
73
|
-
monkeypatch.setattr(cli, "_latest_release_tag", lambda: None)
|
|
74
|
-
cli._maybe_print_update_notice(None)
|
|
75
|
-
assert "--update" in capsys.readouterr().err
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def test_update_notice_is_silent_when_current(install_dir, monkeypatch, capsys):
|
|
79
|
-
monkeypatch.setattr(cli, "_latest_release_tag", lambda: "v0.2.0")
|
|
80
|
-
cli._maybe_print_update_notice("v0.2.0")
|
|
81
|
-
assert capsys.readouterr().err == ""
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
def test_latest_release_tag_fails_open(monkeypatch):
|
|
85
|
-
def boom(*args, **kwargs):
|
|
86
|
-
raise OSError("offline")
|
|
87
|
-
|
|
88
|
-
monkeypatch.setattr(cli.requests, "get", boom)
|
|
89
|
-
assert cli._latest_release_tag() is None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|