agentx-python 0.8.7__tar.gz → 0.8.9__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.7 → agentx_python-0.8.9}/PKG-INFO +1 -1
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/cli.py +105 -13
- agentx_python-0.8.9/agentx/version.py +7 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx_python.egg-info/PKG-INFO +1 -1
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx_python.egg-info/SOURCES.txt +1 -0
- agentx_python-0.8.9/tests/test_cli_launcher.py +117 -0
- agentx_python-0.8.7/agentx/version.py +0 -1
- {agentx_python-0.8.7 → agentx_python-0.8.9}/LICENSE +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/README.md +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/__init__.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/agentx.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/evaluations/__init__.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/evaluations/_term.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/evaluations/adapters/__init__.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/evaluations/adapters/http_endpoint.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/evaluations/adapters/precomputed.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/evaluations/adapters/raw.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/evaluations/client.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/evaluations/datasets.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/evaluations/evaluation_settings.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/evaluations/models.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/evaluations/prompts.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/evaluations/reporting.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/evaluations/results.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/evaluations/runner.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/evaluations/tool_schemas.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/evaluations/tracing.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/exceptions.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/export.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/feedback.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/integrations/__init__.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/integrations/_traced_call.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/integrations/anthropic.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/integrations/autogen.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/integrations/crewai.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/integrations/databricks.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/integrations/google_adk.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/integrations/google_genai.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/integrations/langchain.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/integrations/litellm.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/integrations/llamaindex.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/integrations/moveworks.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/integrations/openai.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/integrations/openai_agents.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/monitor/__init__.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/monitor/agents.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/monitor/client.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/monitor/judge_scorers.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/monitor/models.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/monitor/online_evaluators.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/monitor/patterns.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/monitor/profile.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/monitor/review_queue.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/monitor/rules.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/monitor/scorers.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/monitor/sessions.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/monitor/signals.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/outcomes.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/projects.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/py.typed +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/resources/__init__.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/resources/agent.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/resources/conversation.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/resources/workforce.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/testing.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/traces.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/tracing/__init__.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/tracing/ci_types.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/tracing/eval_scope.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/tracing/ingest_client.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/tracing/tracer.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx/util.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx_python.egg-info/dependency_links.txt +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx_python.egg-info/entry_points.txt +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx_python.egg-info/not-zip-safe +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx_python.egg-info/requires.txt +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/agentx_python.egg-info/top_level.txt +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/setup.cfg +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/setup.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/tests/test_deep_dive_fixes.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/tests/test_docs_match_sdk.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/tests/test_eval_scope.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/tests/test_integration.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/tests/test_integrations.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/tests/test_judge_scorers.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/tests/test_pairwise.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/tests/test_review_queue.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/tests/test_runner_features.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/tests/test_selfhost_analysis_fallback.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/tests/test_span_tree.py +0 -0
- {agentx_python-0.8.7 → agentx_python-0.8.9}/tests/test_testing.py +0 -0
|
@@ -4,29 +4,43 @@ 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
|
+
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.
|
|
17
|
+
|
|
10
18
|
Usage:
|
|
11
19
|
agentx-trace-eval --dev
|
|
20
|
+
agentx-trace-eval --update --dev
|
|
21
|
+
AGENTX_TRACE_EVAL_VERSION=latest agentx-trace-eval --update --dev
|
|
12
22
|
agentx-trace-eval --port 5000 --db-url postgres://...
|
|
13
23
|
"""
|
|
14
24
|
|
|
15
25
|
import os
|
|
16
26
|
import platform
|
|
27
|
+
import re
|
|
17
28
|
import shutil
|
|
18
29
|
import stat
|
|
19
30
|
import sys
|
|
20
31
|
import tarfile
|
|
21
32
|
import tempfile
|
|
22
33
|
from pathlib import Path
|
|
23
|
-
from typing import Tuple
|
|
34
|
+
from typing import Optional, Tuple
|
|
24
35
|
|
|
25
36
|
import requests
|
|
26
37
|
|
|
38
|
+
from agentx.version import ENGINE_VERSION
|
|
39
|
+
|
|
27
40
|
REPO = "AgentX-ai/AgentX-trace-eval"
|
|
28
41
|
INSTALL_DIR = Path(os.environ.get("AGENTX_INSTALL_DIR", str(Path.home() / ".agentx" / "bin")))
|
|
29
42
|
_BIN_NAMES = ("agentx", "agentx-server", "agentx-engine")
|
|
43
|
+
_VERSION_STAMP = ".version"
|
|
30
44
|
|
|
31
45
|
|
|
32
46
|
def _platform_tag() -> Tuple[str, str]:
|
|
@@ -57,12 +71,22 @@ def _release_url(asset: str, version: str) -> str:
|
|
|
57
71
|
return f"https://github.com/{REPO}/releases/download/{version}/{asset}"
|
|
58
72
|
|
|
59
73
|
|
|
60
|
-
def
|
|
74
|
+
def _tag_from_release_url(url: str) -> Optional[str]:
|
|
75
|
+
# GitHub redirects releases/latest/download/... to releases/download/<tag>/...; the final
|
|
76
|
+
# URL is the one place the resolved tag shows up without a second API call.
|
|
77
|
+
match = re.search(r"/releases/download/([^/]+)/", url)
|
|
78
|
+
return match.group(1) if match else None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _download(url: str, dest: Path) -> Optional[str]:
|
|
82
|
+
"""Downloads url to dest; returns the release tag resolved from the final (post-redirect)
|
|
83
|
+
URL, or None if it can't be determined."""
|
|
61
84
|
response = requests.get(url, stream=True, timeout=60)
|
|
62
85
|
response.raise_for_status()
|
|
63
86
|
with open(dest, "wb") as f:
|
|
64
87
|
for chunk in response.iter_content(chunk_size=1 << 16):
|
|
65
88
|
f.write(chunk)
|
|
89
|
+
return _tag_from_release_url(response.url)
|
|
66
90
|
|
|
67
91
|
|
|
68
92
|
def _extract_tar(archive: Path, dest_dir: Path) -> None:
|
|
@@ -76,17 +100,42 @@ def _extract_tar(archive: Path, dest_dir: Path) -> None:
|
|
|
76
100
|
tar.extractall(dest_dir) # Python < 3.12: filter kwarg doesn't exist yet
|
|
77
101
|
|
|
78
102
|
|
|
103
|
+
def _stamped_version() -> Optional[str]:
|
|
104
|
+
try:
|
|
105
|
+
return (INSTALL_DIR / _VERSION_STAMP).read_text().strip() or None
|
|
106
|
+
except OSError:
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _latest_release_tag() -> Optional[str]:
|
|
111
|
+
"""The newest published tag, or None when it can't be determined (offline, rate-limited).
|
|
112
|
+
Fail-open by design: an update NOTICE must never break launching the engine."""
|
|
113
|
+
try:
|
|
114
|
+
response = requests.get(f"https://api.github.com/repos/{REPO}/releases/latest", timeout=3)
|
|
115
|
+
response.raise_for_status()
|
|
116
|
+
tag = response.json().get("tag_name")
|
|
117
|
+
return tag if isinstance(tag, str) and tag else None
|
|
118
|
+
except Exception:
|
|
119
|
+
return None
|
|
120
|
+
|
|
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
|
+
|
|
79
128
|
def _install(version: str = "latest") -> None:
|
|
80
129
|
os_name, arch = _platform_tag()
|
|
81
130
|
INSTALL_DIR.mkdir(parents=True, exist_ok=True)
|
|
82
131
|
|
|
83
|
-
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)
|
|
84
133
|
with tempfile.TemporaryDirectory() as tmp:
|
|
85
134
|
tmp_dir = Path(tmp)
|
|
86
135
|
archive = tmp_dir / "agentx.tar.gz"
|
|
87
136
|
url = _release_url(f"agentx_{os_name}_{arch}.tar.gz", version)
|
|
88
137
|
try:
|
|
89
|
-
_download(url, archive)
|
|
138
|
+
resolved_tag = _download(url, archive)
|
|
90
139
|
except requests.HTTPError as exc:
|
|
91
140
|
raise SystemExit(
|
|
92
141
|
f"agentx-trace-eval: failed to download {url} ({exc}).\n"
|
|
@@ -108,6 +157,11 @@ def _install(version: str = "latest") -> None:
|
|
|
108
157
|
if not found_any:
|
|
109
158
|
raise SystemExit(f"agentx-trace-eval: downloaded archive from {url} didn't contain any of {_BIN_NAMES}")
|
|
110
159
|
|
|
160
|
+
stamp = resolved_tag or (version if version != "latest" else None)
|
|
161
|
+
if stamp:
|
|
162
|
+
(INSTALL_DIR / _VERSION_STAMP).write_text(stamp + "\n")
|
|
163
|
+
print(f"agentx-trace-eval: installed {stamp}", file=sys.stderr)
|
|
164
|
+
|
|
111
165
|
if os.environ.get("AGENTX_TRACE_EVAL_SKIP_WEB"):
|
|
112
166
|
return
|
|
113
167
|
|
|
@@ -118,7 +172,9 @@ def _install(version: str = "latest") -> None:
|
|
|
118
172
|
web_dir = INSTALL_DIR / "web"
|
|
119
173
|
with tempfile.TemporaryDirectory() as tmp:
|
|
120
174
|
web_archive = Path(tmp) / "agentx-web.tar.gz"
|
|
121
|
-
|
|
175
|
+
# Pin the dashboard to the tag the engine actually resolved to, so the two halves of one
|
|
176
|
+
# install can't skew (releases/latest assets are updated independently).
|
|
177
|
+
web_url = _release_url("agentx-web.tar.gz", stamp or version)
|
|
122
178
|
try:
|
|
123
179
|
_download(web_url, web_archive)
|
|
124
180
|
except requests.HTTPError:
|
|
@@ -133,25 +189,61 @@ def _install(version: str = "latest") -> None:
|
|
|
133
189
|
_extract_tar(web_archive, web_dir)
|
|
134
190
|
|
|
135
191
|
|
|
136
|
-
def ensure_installed(version: str =
|
|
137
|
-
"""
|
|
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
|
|
194
|
+
there, when `force` is set, or when `version` is a specific tag that differs from the
|
|
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.
|
|
138
198
|
Returns the path to the agentx-server executable. Set AGENTX_INSTALL_DIR to change where
|
|
139
|
-
this looks/installs
|
|
199
|
+
this looks/installs."""
|
|
200
|
+
if version is None:
|
|
201
|
+
version = resolve_version()
|
|
140
202
|
server_path = INSTALL_DIR / "agentx-server"
|
|
141
|
-
|
|
203
|
+
stamped = _stamped_version()
|
|
204
|
+
pin_mismatch = version != "latest" and stamped != version
|
|
205
|
+
if force or pin_mismatch or not server_path.exists():
|
|
142
206
|
_install(version=version)
|
|
143
207
|
return server_path
|
|
144
208
|
|
|
145
209
|
|
|
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.)"""
|
|
214
|
+
latest = _latest_release_tag()
|
|
215
|
+
if installed is None:
|
|
216
|
+
print(
|
|
217
|
+
"agentx-trace-eval: installed engine version unknown"
|
|
218
|
+
+ (f" (newest release is {latest})" if latest else "")
|
|
219
|
+
+ "; run with --update to fetch the newest release",
|
|
220
|
+
file=sys.stderr,
|
|
221
|
+
)
|
|
222
|
+
elif latest and latest != installed:
|
|
223
|
+
print(
|
|
224
|
+
f"agentx-trace-eval: engine {installed} is installed but {latest} is available; "
|
|
225
|
+
"run with --update to upgrade",
|
|
226
|
+
file=sys.stderr,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
|
|
146
230
|
def main() -> None:
|
|
147
|
-
|
|
148
|
-
|
|
231
|
+
args = sys.argv[1:]
|
|
232
|
+
force_update = "--update" in args
|
|
233
|
+
# --update belongs to this launcher, not to agentx-server - consume it before the handoff.
|
|
234
|
+
args = [a for a in args if a != "--update"]
|
|
235
|
+
|
|
236
|
+
version = resolve_version()
|
|
237
|
+
server_path = ensure_installed(version=version, force=force_update)
|
|
149
238
|
if not server_path.exists():
|
|
150
239
|
raise SystemExit(f"agentx-trace-eval: {server_path} still missing after install, giving up")
|
|
151
240
|
|
|
241
|
+
if not force_update and version == "latest":
|
|
242
|
+
_maybe_print_update_notice(_stamped_version())
|
|
243
|
+
|
|
152
244
|
# os.execv replaces this process rather than spawning a subprocess: signals, stdio, and the
|
|
153
245
|
# exit code all pass straight through to agentx-server, same as invoking it directly.
|
|
154
|
-
os.execv(str(server_path), [str(server_path)] +
|
|
246
|
+
os.execv(str(server_path), [str(server_path)] + args)
|
|
155
247
|
|
|
156
248
|
|
|
157
249
|
if __name__ == "__main__":
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
VERSION = "0.8.9"
|
|
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"
|
|
@@ -73,6 +73,7 @@ agentx_python.egg-info/entry_points.txt
|
|
|
73
73
|
agentx_python.egg-info/not-zip-safe
|
|
74
74
|
agentx_python.egg-info/requires.txt
|
|
75
75
|
agentx_python.egg-info/top_level.txt
|
|
76
|
+
tests/test_cli_launcher.py
|
|
76
77
|
tests/test_deep_dive_fixes.py
|
|
77
78
|
tests/test_docs_match_sdk.py
|
|
78
79
|
tests/test_eval_scope.py
|
|
@@ -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.7"
|
|
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
|
|
File without changes
|