agentx-python 0.4.9__tar.gz → 0.4.11__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.
Files changed (37) hide show
  1. agentx_python-0.4.11/PKG-INFO +198 -0
  2. agentx_python-0.4.11/README.md +169 -0
  3. {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/agentx.py +1 -0
  4. {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/_term.py +36 -14
  5. {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/adapters/precomputed.py +3 -1
  6. {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/adapters/raw.py +4 -1
  7. {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/client.py +45 -12
  8. {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/datasets.py +52 -9
  9. {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/models.py +188 -14
  10. {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/redaction.py +17 -5
  11. {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/reporting.py +34 -8
  12. {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/results.py +21 -5
  13. {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/runner.py +56 -15
  14. {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/resources/workforce.py +3 -1
  15. agentx_python-0.4.11/agentx/version.py +1 -0
  16. agentx_python-0.4.11/agentx_python.egg-info/PKG-INFO +198 -0
  17. {agentx_python-0.4.9 → agentx_python-0.4.11}/tests/test_integration.py +7 -0
  18. agentx_python-0.4.9/PKG-INFO +0 -141
  19. agentx_python-0.4.9/README.md +0 -112
  20. agentx_python-0.4.9/agentx/version.py +0 -1
  21. agentx_python-0.4.9/agentx_python.egg-info/PKG-INFO +0 -141
  22. {agentx_python-0.4.9 → agentx_python-0.4.11}/LICENSE +0 -0
  23. {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/__init__.py +0 -0
  24. {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/__init__.py +0 -0
  25. {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/adapters/__init__.py +0 -0
  26. {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/adapters/http_endpoint.py +0 -0
  27. {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/tracing.py +0 -0
  28. {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/resources/__init__.py +0 -0
  29. {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/resources/agent.py +0 -0
  30. {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/resources/conversation.py +0 -0
  31. {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/util.py +0 -0
  32. {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx_python.egg-info/SOURCES.txt +0 -0
  33. {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx_python.egg-info/dependency_links.txt +0 -0
  34. {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx_python.egg-info/requires.txt +0 -0
  35. {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx_python.egg-info/top_level.txt +0 -0
  36. {agentx_python-0.4.9 → agentx_python-0.4.11}/setup.cfg +0 -0
  37. {agentx_python-0.4.9 → agentx_python-0.4.11}/setup.py +0 -0
@@ -0,0 +1,198 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentx-python
3
+ Version: 0.4.11
4
+ Summary: Official Python SDK for AgentX (https://www.agentx.so/)
5
+ Home-page: https://github.com/AgentX-ai/AgentX-python
6
+ Author: Robin Wang and AgentX Team
7
+ Author-email: contact@agentx.so
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.6
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: urllib3>=1.26.11
15
+ Requires-Dist: certifi
16
+ Requires-Dist: requests
17
+ Requires-Dist: pydantic
18
+ Requires-Dist: pydantic_core
19
+ Dynamic: author
20
+ Dynamic: author-email
21
+ Dynamic: classifier
22
+ Dynamic: description
23
+ Dynamic: description-content-type
24
+ Dynamic: home-page
25
+ Dynamic: license-file
26
+ Dynamic: requires-dist
27
+ Dynamic: requires-python
28
+ Dynamic: summary
29
+
30
+ ![Logo](https://agentx-resources.s3.us-west-1.amazonaws.com/AgentX-logo-387x60.png)
31
+
32
+ [![PyPI version](https://img.shields.io/pypi/v/agentx-python)](https://pypi.org/project/agentx-python/)
33
+
34
+ The official Python SDK for **[AgentX](https://www.agentx.so/)** — build, chat with, and orchestrate AI agents in a few lines of code.
35
+
36
+ ---
37
+
38
+ ## Contents
39
+
40
+ - [Why AgentX](#why-agentx)
41
+ - [Installation](#installation)
42
+ - [Authentication](#authentication)
43
+ - [Quick start](#quick-start)
44
+ - [Working with agents](#working-with-agents)
45
+ - [List agents](#list-agents)
46
+ - [Start a conversation](#start-a-conversation)
47
+ - [Chat (streaming and non-streaming)](#chat-streaming-and-non-streaming)
48
+ - [Workforce (multi-agent orchestration)](#workforce-multi-agent-orchestration)
49
+ - [Agent Evaluations](#custom-agent-evaluations) — LLM-as-a-judge, cosine / Jaccard similarity
50
+ - [Links](#links)
51
+
52
+ ---
53
+
54
+ ## Why AgentX
55
+
56
+ - **Simple mental model** — `Agent → Conversation → Message`.
57
+ - **Chain-of-thought** is built in, no extra plumbing.
58
+ - **Bring any LLM** — works across major open and closed-source vendors.
59
+ - **Batteries included** — voice (ASR/TTS), image generation, document/CSV/Excel/OCR, RAG with built-in re-ranking.
60
+ - **MCP support** — connect any Model Context Protocol server.
61
+ - **Multi-agent orchestration** — workforces of agents with a designated manager, across LLM vendors.
62
+ - **Agent Evaluations** — score any agent (LangChain, CrewAI, OpenAI, Anthropic, HTTP, …) with LLM-as-a-judge ratings plus optional cosine and Jaccard similarity metrics.
63
+ - **A2A** — Each agent can be published with agent-to-agent protocol compatible.
64
+
65
+ ---
66
+
67
+ ## Installation
68
+
69
+ ```bash
70
+ pip install --upgrade agentx-python
71
+ ```
72
+
73
+ Requires Python 3.9 or newer.
74
+
75
+ ---
76
+
77
+ ## Authentication
78
+
79
+ Get your API key at [app.agentx.so](https://app.agentx.so), then either pass it inline or expose it as an environment variable.
80
+
81
+ ```python
82
+ # Option A — pass the key inline
83
+ from agentx import AgentX
84
+ client = AgentX(api_key="your-api-key-here")
85
+
86
+ # Option B — set AGENTX_API_KEY in your environment, then:
87
+ client = AgentX.from_env()
88
+ ```
89
+
90
+ ---
91
+
92
+ ## Quick start
93
+
94
+ ```python
95
+ from agentx import AgentX
96
+
97
+ client = AgentX.from_env()
98
+
99
+ # Pick an existing agent and chat with it
100
+ agent = client.list_agents()[0]
101
+ conversation = agent.new_conversation()
102
+ print(conversation.chat("Hello! What can you help me with?"))
103
+ ```
104
+
105
+ That's it. The remaining sections show the same primitives in more detail.
106
+
107
+ ---
108
+
109
+ ## Working with agents
110
+
111
+ ### List agents
112
+
113
+ ```python
114
+ agents = client.list_agents()
115
+ print(f"You have {len(agents)} agents")
116
+ ```
117
+
118
+ ### Start a conversation
119
+
120
+ ```python
121
+ agent = client.get_agent(id="<agent-id>")
122
+
123
+ # Either resume an existing conversation…
124
+ existing = agent.list_conversations()
125
+ last = existing[-1]
126
+ for msg in last.list_messages():
127
+ print(msg)
128
+
129
+ # …or start a fresh one
130
+ conversation = agent.new_conversation()
131
+ ```
132
+
133
+ ### Chat (streaming and non-streaming)
134
+
135
+ ```python
136
+ # Blocking — returns the full response once it's ready
137
+ response = conversation.chat("What is your name?")
138
+ print(response)
139
+
140
+ # Streaming — yields ChatResponse objects as the model produces them
141
+ for chunk in conversation.chat_stream("Hello, what is your name?"):
142
+ if chunk.text:
143
+ print(chunk.text, end="")
144
+ ```
145
+
146
+ Each `ChatResponse` chunk exposes the agent's `text` and, where applicable, its `cot` (chain-of-thought) reasoning, along with any retrieved references and tasks.
147
+
148
+ ---
149
+
150
+ ## Workforce (multi-agent orchestration)
151
+
152
+ A **workforce** is a team of agents coordinated by a designated manager agent. Workforces can mix LLM vendors and route work between specialists.
153
+
154
+ ```python
155
+ workforces = client.list_workforces()
156
+ workforce = workforces[0]
157
+
158
+ print(f"Workforce: {workforce.name}")
159
+ print(f"Manager: {workforce.manager.name}")
160
+ print(f"Agents: {[a.name for a in workforce.agents]}")
161
+
162
+ # Chat with the workforce — the manager decides which agent(s) to delegate to
163
+ conversation = workforce.new_conversation()
164
+ for chunk in workforce.chat_stream(conversation.id, "How can you help me with this project?"):
165
+ if chunk.text:
166
+ print(chunk.text, end="")
167
+ ```
168
+
169
+ ---
170
+
171
+ ## Custom agent evaluations
172
+
173
+ Evaluate **any** AI agent — LangChain, CrewAI, AutoGen, LlamaIndex, OpenAI, Anthropic, HTTP endpoints, or plain Python — using AgentX as the scoring and reporting backend. Includes optional **cosine** and **Jaccard** similarity metrics alongside LLM-graded ratings.
174
+
175
+ ```python
176
+ report = (
177
+ client.evaluations
178
+ .run(dataset_id="evds_…", subject={"kind": "custom_agent", "framework": "raw_python"})
179
+ .execute(my_agent_fn)
180
+ .finalize()
181
+ .analyze()
182
+ )
183
+
184
+ print(report.average_rating) # LLM-graded score, 0–10
185
+ print(report.cosine_similarity) # embedding cosine, 0–1 (None if not enabled)
186
+ print(report.jaccard_similarity) # token-set overlap, 0–1 (None if not enabled)
187
+ ```
188
+
189
+ See **[EVALUATIONS.md](EVALUATIONS.md)** for the full guide — dataset builder, framework adapters, similarity metrics, and the complete API reference.
190
+
191
+ ---
192
+
193
+ ## Links
194
+
195
+ - **Dashboard** — [app.agentx.so](https://app.agentx.so)
196
+ - **Website** — [agentx.so](https://www.agentx.so/)
197
+ - **PyPI** — [agentx-python](https://pypi.org/project/agentx-python/)
198
+ - **Evaluations docs** — [EVALUATIONS.md](EVALUATIONS.md)
@@ -0,0 +1,169 @@
1
+ ![Logo](https://agentx-resources.s3.us-west-1.amazonaws.com/AgentX-logo-387x60.png)
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/agentx-python)](https://pypi.org/project/agentx-python/)
4
+
5
+ The official Python SDK for **[AgentX](https://www.agentx.so/)** — build, chat with, and orchestrate AI agents in a few lines of code.
6
+
7
+ ---
8
+
9
+ ## Contents
10
+
11
+ - [Why AgentX](#why-agentx)
12
+ - [Installation](#installation)
13
+ - [Authentication](#authentication)
14
+ - [Quick start](#quick-start)
15
+ - [Working with agents](#working-with-agents)
16
+ - [List agents](#list-agents)
17
+ - [Start a conversation](#start-a-conversation)
18
+ - [Chat (streaming and non-streaming)](#chat-streaming-and-non-streaming)
19
+ - [Workforce (multi-agent orchestration)](#workforce-multi-agent-orchestration)
20
+ - [Agent Evaluations](#custom-agent-evaluations) — LLM-as-a-judge, cosine / Jaccard similarity
21
+ - [Links](#links)
22
+
23
+ ---
24
+
25
+ ## Why AgentX
26
+
27
+ - **Simple mental model** — `Agent → Conversation → Message`.
28
+ - **Chain-of-thought** is built in, no extra plumbing.
29
+ - **Bring any LLM** — works across major open and closed-source vendors.
30
+ - **Batteries included** — voice (ASR/TTS), image generation, document/CSV/Excel/OCR, RAG with built-in re-ranking.
31
+ - **MCP support** — connect any Model Context Protocol server.
32
+ - **Multi-agent orchestration** — workforces of agents with a designated manager, across LLM vendors.
33
+ - **Agent Evaluations** — score any agent (LangChain, CrewAI, OpenAI, Anthropic, HTTP, …) with LLM-as-a-judge ratings plus optional cosine and Jaccard similarity metrics.
34
+ - **A2A** — Each agent can be published with agent-to-agent protocol compatible.
35
+
36
+ ---
37
+
38
+ ## Installation
39
+
40
+ ```bash
41
+ pip install --upgrade agentx-python
42
+ ```
43
+
44
+ Requires Python 3.9 or newer.
45
+
46
+ ---
47
+
48
+ ## Authentication
49
+
50
+ Get your API key at [app.agentx.so](https://app.agentx.so), then either pass it inline or expose it as an environment variable.
51
+
52
+ ```python
53
+ # Option A — pass the key inline
54
+ from agentx import AgentX
55
+ client = AgentX(api_key="your-api-key-here")
56
+
57
+ # Option B — set AGENTX_API_KEY in your environment, then:
58
+ client = AgentX.from_env()
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Quick start
64
+
65
+ ```python
66
+ from agentx import AgentX
67
+
68
+ client = AgentX.from_env()
69
+
70
+ # Pick an existing agent and chat with it
71
+ agent = client.list_agents()[0]
72
+ conversation = agent.new_conversation()
73
+ print(conversation.chat("Hello! What can you help me with?"))
74
+ ```
75
+
76
+ That's it. The remaining sections show the same primitives in more detail.
77
+
78
+ ---
79
+
80
+ ## Working with agents
81
+
82
+ ### List agents
83
+
84
+ ```python
85
+ agents = client.list_agents()
86
+ print(f"You have {len(agents)} agents")
87
+ ```
88
+
89
+ ### Start a conversation
90
+
91
+ ```python
92
+ agent = client.get_agent(id="<agent-id>")
93
+
94
+ # Either resume an existing conversation…
95
+ existing = agent.list_conversations()
96
+ last = existing[-1]
97
+ for msg in last.list_messages():
98
+ print(msg)
99
+
100
+ # …or start a fresh one
101
+ conversation = agent.new_conversation()
102
+ ```
103
+
104
+ ### Chat (streaming and non-streaming)
105
+
106
+ ```python
107
+ # Blocking — returns the full response once it's ready
108
+ response = conversation.chat("What is your name?")
109
+ print(response)
110
+
111
+ # Streaming — yields ChatResponse objects as the model produces them
112
+ for chunk in conversation.chat_stream("Hello, what is your name?"):
113
+ if chunk.text:
114
+ print(chunk.text, end="")
115
+ ```
116
+
117
+ Each `ChatResponse` chunk exposes the agent's `text` and, where applicable, its `cot` (chain-of-thought) reasoning, along with any retrieved references and tasks.
118
+
119
+ ---
120
+
121
+ ## Workforce (multi-agent orchestration)
122
+
123
+ A **workforce** is a team of agents coordinated by a designated manager agent. Workforces can mix LLM vendors and route work between specialists.
124
+
125
+ ```python
126
+ workforces = client.list_workforces()
127
+ workforce = workforces[0]
128
+
129
+ print(f"Workforce: {workforce.name}")
130
+ print(f"Manager: {workforce.manager.name}")
131
+ print(f"Agents: {[a.name for a in workforce.agents]}")
132
+
133
+ # Chat with the workforce — the manager decides which agent(s) to delegate to
134
+ conversation = workforce.new_conversation()
135
+ for chunk in workforce.chat_stream(conversation.id, "How can you help me with this project?"):
136
+ if chunk.text:
137
+ print(chunk.text, end="")
138
+ ```
139
+
140
+ ---
141
+
142
+ ## Custom agent evaluations
143
+
144
+ Evaluate **any** AI agent — LangChain, CrewAI, AutoGen, LlamaIndex, OpenAI, Anthropic, HTTP endpoints, or plain Python — using AgentX as the scoring and reporting backend. Includes optional **cosine** and **Jaccard** similarity metrics alongside LLM-graded ratings.
145
+
146
+ ```python
147
+ report = (
148
+ client.evaluations
149
+ .run(dataset_id="evds_…", subject={"kind": "custom_agent", "framework": "raw_python"})
150
+ .execute(my_agent_fn)
151
+ .finalize()
152
+ .analyze()
153
+ )
154
+
155
+ print(report.average_rating) # LLM-graded score, 0–10
156
+ print(report.cosine_similarity) # embedding cosine, 0–1 (None if not enabled)
157
+ print(report.jaccard_similarity) # token-set overlap, 0–1 (None if not enabled)
158
+ ```
159
+
160
+ See **[EVALUATIONS.md](EVALUATIONS.md)** for the full guide — dataset builder, framework adapters, similarity metrics, and the complete API reference.
161
+
162
+ ---
163
+
164
+ ## Links
165
+
166
+ - **Dashboard** — [app.agentx.so](https://app.agentx.so)
167
+ - **Website** — [agentx.so](https://www.agentx.so/)
168
+ - **PyPI** — [agentx-python](https://pypi.org/project/agentx-python/)
169
+ - **Evaluations docs** — [EVALUATIONS.md](EVALUATIONS.md)
@@ -23,6 +23,7 @@ class AgentX:
23
23
  from agentx.evaluations.client import EvaluationsClient
24
24
  from agentx.evaluations.runner import EvaluationsRunner
25
25
  from agentx.version import VERSION
26
+
26
27
  _eval_client = EvaluationsClient(
27
28
  api_key=self.api_key,
28
29
  sdk_version=VERSION,
@@ -1,4 +1,5 @@
1
1
  """Minimal ANSI terminal helpers — no external dependencies."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  import itertools
@@ -13,23 +14,42 @@ def _c(code: str) -> str:
13
14
  return f"\033[{code}m" if _IS_TTY else ""
14
15
 
15
16
 
16
- RESET = _c("0")
17
- BOLD = _c("1")
18
- DIM = _c("2")
19
- GREEN = _c("32")
17
+ RESET = _c("0")
18
+ BOLD = _c("1")
19
+ DIM = _c("2")
20
+ GREEN = _c("32")
20
21
  YELLOW = _c("33")
21
- RED = _c("31")
22
- CYAN = _c("36")
22
+ RED = _c("31")
23
+ CYAN = _c("36")
23
24
  MAGENTA = _c("35")
24
25
 
25
26
 
26
- def green(s: str) -> str: return f"{GREEN}{s}{RESET}"
27
- def yellow(s: str) -> str: return f"{YELLOW}{s}{RESET}"
28
- def red(s: str) -> str: return f"{RED}{s}{RESET}"
29
- def cyan(s: str) -> str: return f"{CYAN}{s}{RESET}"
30
- def bold(s: str) -> str: return f"{BOLD}{s}{RESET}"
31
- def dim(s: str) -> str: return f"{DIM}{s}{RESET}"
32
- def magenta(s: str) -> str: return f"{MAGENTA}{s}{RESET}"
27
+ def green(s: str) -> str:
28
+ return f"{GREEN}{s}{RESET}"
29
+
30
+
31
+ def yellow(s: str) -> str:
32
+ return f"{YELLOW}{s}{RESET}"
33
+
34
+
35
+ def red(s: str) -> str:
36
+ return f"{RED}{s}{RESET}"
37
+
38
+
39
+ def cyan(s: str) -> str:
40
+ return f"{CYAN}{s}{RESET}"
41
+
42
+
43
+ def bold(s: str) -> str:
44
+ return f"{BOLD}{s}{RESET}"
45
+
46
+
47
+ def dim(s: str) -> str:
48
+ return f"{DIM}{s}{RESET}"
49
+
50
+
51
+ def magenta(s: str) -> str:
52
+ return f"{MAGENTA}{s}{RESET}"
33
53
 
34
54
 
35
55
  class Spinner:
@@ -64,6 +84,8 @@ class Spinner:
64
84
  for frame in itertools.cycle(self._FRAMES):
65
85
  if self._stop.is_set():
66
86
  break
67
- sys.stdout.write(f"\r {CYAN}{frame}{RESET} {self._message} {DIM}(this may take ~60s+){RESET}")
87
+ sys.stdout.write(
88
+ f"\r {CYAN}{frame}{RESET} {self._message} {DIM}(this may take ~60s+){RESET}"
89
+ )
68
90
  sys.stdout.flush()
69
91
  time.sleep(0.08)
@@ -27,7 +27,9 @@ class PrecomputedAdapter:
27
27
  self._lookup = {str(k): v for k, v in outputs.items()}
28
28
 
29
29
  def run(self, case: EvaluationCase) -> EvaluationResult:
30
- raw = self._lookup.get(case.case_id) or self._lookup.get(str(case.question_index))
30
+ raw = self._lookup.get(case.case_id) or self._lookup.get(
31
+ str(case.question_index)
32
+ )
31
33
  if raw is None:
32
34
  raw = ""
33
35
  return normalize_result(case, raw, latency_ms=0)
@@ -23,8 +23,11 @@ class RawCallableAdapter:
23
23
  def __init__(self, fn: Callable[[EvaluationCase], Any]):
24
24
  self._fn = fn
25
25
 
26
- def run(self, case: EvaluationCase, latency_ms: int | None = None) -> EvaluationResult:
26
+ def run(
27
+ self, case: EvaluationCase, latency_ms: int | None = None
28
+ ) -> EvaluationResult:
27
29
  import time
30
+
28
31
  start = time.monotonic()
29
32
  try:
30
33
  raw = self._fn(case)
@@ -14,6 +14,7 @@ from agentx.evaluations.models import (
14
14
  EvaluationResult,
15
15
  EvaluationRun,
16
16
  EvaluationSubject,
17
+ ModelInfo,
17
18
  Report,
18
19
  )
19
20
 
@@ -42,26 +43,33 @@ class AgentXValidationError(AgentXEvaluationsError):
42
43
 
43
44
 
44
45
  class EvaluationsClient:
45
- def __init__(self, api_key: str, sdk_version: str = "unknown", base_url: str = None):
46
+ def __init__(
47
+ self, api_key: str, sdk_version: str = "unknown", base_url: str = None
48
+ ):
46
49
  if not api_key:
47
50
  raise AgentXAuthError("AGENTX_API_KEY is required")
48
51
  self._api_key = api_key
49
52
  self._sdk_version = sdk_version
50
53
  # Priority: constructor arg > env var > SDK default
51
54
  # Always append /custom-agent-evaluations so users only need to provide /api/v1
52
- _api_base = (base_url or os.getenv("AGENTX_API_BASE_URL", _UTIL_API_BASE)).rstrip("/")
55
+ _api_base = (
56
+ base_url or os.getenv("AGENTX_API_BASE_URL", _UTIL_API_BASE)
57
+ ).rstrip("/")
53
58
  if not _api_base.endswith("/custom-agent-evaluations"):
54
59
  _api_base = f"{_api_base}/custom-agent-evaluations"
55
60
  self._base_url = _api_base
56
61
  self._session = requests.Session()
57
- self._session.headers.update({
58
- "x-api-key": self._api_key,
59
- "Content-Type": "application/json",
60
- "User-Agent": f"{SDK_NAME}/{self._sdk_version}",
61
- "accept": "*/*",
62
- })
62
+ self._session.headers.update(
63
+ {
64
+ "x-api-key": self._api_key,
65
+ "Content-Type": "application/json",
66
+ "User-Agent": f"{SDK_NAME}/{self._sdk_version}",
67
+ "accept": "*/*",
68
+ }
69
+ )
63
70
  # Expose dataset builder factory
64
71
  from agentx.evaluations.datasets import DatasetClient
72
+
65
73
  self.datasets = DatasetClient(self)
66
74
 
67
75
  # ------------------------------------------------------------------
@@ -86,7 +94,9 @@ class EvaluationsClient:
86
94
  if resp.status_code == 422:
87
95
  raise AgentXValidationError(resp.text)
88
96
  if resp.status_code in _RETRYABLE_STATUS and attempt < _MAX_RETRIES - 1:
89
- logger.debug("Retryable status %d (attempt %d)", resp.status_code, attempt + 1)
97
+ logger.debug(
98
+ "Retryable status %d (attempt %d)", resp.status_code, attempt + 1
99
+ )
90
100
  last_exc = AgentXEvaluationsError(f"HTTP {resp.status_code}")
91
101
  continue
92
102
  if not resp.ok:
@@ -97,6 +107,19 @@ class EvaluationsClient:
97
107
  return resp.text
98
108
  raise AgentXEvaluationsError(f"Request failed after retries: {last_exc}")
99
109
 
110
+ # ------------------------------------------------------------------
111
+ # Model registry
112
+ # ------------------------------------------------------------------
113
+
114
+ def list_models(self, provider: Optional[str] = None) -> List[ModelInfo]:
115
+ """List the LLM models AgentX supports — the same set selectable for
116
+ the Sovereignty & Portability Index. Pass ``provider`` (e.g. "Google")
117
+ to filter."""
118
+ params = {"provider": provider} if provider else None
119
+ data = self._request("GET", "/models", params=params)
120
+ items = data if isinstance(data, list) else data.get("models", [])
121
+ return [ModelInfo(**m) for m in items]
122
+
100
123
  # ------------------------------------------------------------------
101
124
  # Dataset endpoints
102
125
  # ------------------------------------------------------------------
@@ -107,7 +130,10 @@ class EvaluationsClient:
107
130
 
108
131
  def list_datasets(self) -> List[Dataset]:
109
132
  data = self._request("GET", "/datasets")
110
- return [Dataset(**d) for d in (data if isinstance(data, list) else data.get("datasets", []))]
133
+ return [
134
+ Dataset(**d)
135
+ for d in (data if isinstance(data, list) else data.get("datasets", []))
136
+ ]
111
137
 
112
138
  def get_dataset(self, dataset_id: str) -> Dataset:
113
139
  data = self._request("GET", f"/datasets/{dataset_id}")
@@ -124,6 +150,7 @@ class EvaluationsClient:
124
150
  python_version: Optional[str] = None,
125
151
  ) -> EvaluationRun:
126
152
  from agentx.version import VERSION
153
+
127
154
  payload = {
128
155
  "datasetId": dataset_id,
129
156
  "evaluationSubject": subject.model_dump(by_alias=True, exclude_none=True),
@@ -138,7 +165,9 @@ class EvaluationsClient:
138
165
  data = self._request("POST", "/runs", json=payload)
139
166
  return EvaluationRun(**data)
140
167
 
141
- def append_results(self, run_id: str, batch_id: str, results: List[EvaluationResult]) -> BatchAppendResponse:
168
+ def append_results(
169
+ self, run_id: str, batch_id: str, results: List[EvaluationResult]
170
+ ) -> BatchAppendResponse:
142
171
  payload = {
143
172
  "batchId": batch_id,
144
173
  "results": [_result_to_payload(r) for r in results],
@@ -147,7 +176,9 @@ class EvaluationsClient:
147
176
  return BatchAppendResponse(**data)
148
177
 
149
178
  def finalize_run(self, run_id: str) -> Dict[str, Any]:
150
- return self._request("POST", f"/runs/{run_id}/finalize", json={"status": "completed"})
179
+ return self._request(
180
+ "POST", f"/runs/{run_id}/finalize", json={"status": "completed"}
181
+ )
151
182
 
152
183
  def analyze_run(self, run_id: str) -> Dict[str, Any]:
153
184
  return self._request("POST", f"/runs/{run_id}/analyze", json={}, timeout=300)
@@ -168,6 +199,7 @@ class EvaluationsClient:
168
199
  # Helpers
169
200
  # ---------------------------------------------------------------------------
170
201
 
202
+
171
203
  def _result_to_payload(r: EvaluationResult) -> dict:
172
204
  d = r.model_dump(by_alias=True, exclude_none=True)
173
205
  # Rename snake_case fields the model may have stored locally
@@ -180,4 +212,5 @@ def _result_to_payload(r: EvaluationResult) -> dict:
180
212
 
181
213
  def _python_version() -> str:
182
214
  import sys
215
+
183
216
  return f"{sys.version_info.major}.{sys.version_info.minor}"