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.
- agentx_python-0.4.11/PKG-INFO +198 -0
- agentx_python-0.4.11/README.md +169 -0
- {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/agentx.py +1 -0
- {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/_term.py +36 -14
- {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/adapters/precomputed.py +3 -1
- {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/adapters/raw.py +4 -1
- {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/client.py +45 -12
- {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/datasets.py +52 -9
- {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/models.py +188 -14
- {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/redaction.py +17 -5
- {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/reporting.py +34 -8
- {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/results.py +21 -5
- {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/runner.py +56 -15
- {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/resources/workforce.py +3 -1
- agentx_python-0.4.11/agentx/version.py +1 -0
- agentx_python-0.4.11/agentx_python.egg-info/PKG-INFO +198 -0
- {agentx_python-0.4.9 → agentx_python-0.4.11}/tests/test_integration.py +7 -0
- agentx_python-0.4.9/PKG-INFO +0 -141
- agentx_python-0.4.9/README.md +0 -112
- agentx_python-0.4.9/agentx/version.py +0 -1
- agentx_python-0.4.9/agentx_python.egg-info/PKG-INFO +0 -141
- {agentx_python-0.4.9 → agentx_python-0.4.11}/LICENSE +0 -0
- {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/__init__.py +0 -0
- {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/__init__.py +0 -0
- {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/adapters/__init__.py +0 -0
- {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/adapters/http_endpoint.py +0 -0
- {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/evaluations/tracing.py +0 -0
- {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/resources/__init__.py +0 -0
- {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/resources/agent.py +0 -0
- {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/resources/conversation.py +0 -0
- {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx/util.py +0 -0
- {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx_python.egg-info/SOURCES.txt +0 -0
- {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx_python.egg-info/dependency_links.txt +0 -0
- {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx_python.egg-info/requires.txt +0 -0
- {agentx_python-0.4.9 → agentx_python-0.4.11}/agentx_python.egg-info/top_level.txt +0 -0
- {agentx_python-0.4.9 → agentx_python-0.4.11}/setup.cfg +0 -0
- {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
|
+

|
|
31
|
+
|
|
32
|
+
[](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
|
+

|
|
2
|
+
|
|
3
|
+
[](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)
|
|
@@ -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
|
|
17
|
-
BOLD
|
|
18
|
-
DIM
|
|
19
|
-
GREEN
|
|
17
|
+
RESET = _c("0")
|
|
18
|
+
BOLD = _c("1")
|
|
19
|
+
DIM = _c("2")
|
|
20
|
+
GREEN = _c("32")
|
|
20
21
|
YELLOW = _c("33")
|
|
21
|
-
RED
|
|
22
|
-
CYAN
|
|
22
|
+
RED = _c("31")
|
|
23
|
+
CYAN = _c("36")
|
|
23
24
|
MAGENTA = _c("35")
|
|
24
25
|
|
|
25
26
|
|
|
26
|
-
def green(s: str) -> str:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def
|
|
31
|
-
|
|
32
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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__(
|
|
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 = (
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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(
|
|
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 [
|
|
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(
|
|
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(
|
|
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}"
|