agentx-python 0.6.3__tar.gz → 0.6.5__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.6.3/agentx_python.egg-info → agentx_python-0.6.5}/PKG-INFO +63 -4
- {agentx_python-0.6.3 → agentx_python-0.6.5}/README.md +62 -3
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/agentx.py +8 -2
- agentx_python-0.6.5/agentx/evaluations/__init__.py +7 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/client.py +65 -6
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/datasets.py +12 -1
- agentx_python-0.6.5/agentx/evaluations/evaluation_settings.py +109 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/models.py +89 -7
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/reporting.py +14 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/results.py +4 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/runner.py +87 -10
- agentx_python-0.6.5/agentx/resources/__init__.py +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/resources/conversation.py +2 -2
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/tracing/ingest_client.py +24 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/tracing/tracer.py +56 -5
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/util.py +2 -1
- agentx_python-0.6.5/agentx/version.py +1 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5/agentx_python.egg-info}/PKG-INFO +63 -4
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx_python.egg-info/SOURCES.txt +3 -0
- agentx_python-0.6.5/agentx_python.egg-info/not-zip-safe +1 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/setup.py +5 -0
- agentx_python-0.6.3/agentx/evaluations/__init__.py +0 -3
- agentx_python-0.6.3/agentx/version.py +0 -1
- {agentx_python-0.6.3 → agentx_python-0.6.5}/LICENSE +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/__init__.py +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/_term.py +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/adapters/__init__.py +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/adapters/http_endpoint.py +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/adapters/precomputed.py +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/adapters/raw.py +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/redaction.py +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/tracing.py +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/exceptions.py +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/integrations/__init__.py +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/integrations/_perf.py +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/integrations/anthropic.py +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/integrations/crewai.py +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/integrations/google_adk.py +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/integrations/google_genai.py +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/integrations/langchain.py +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/integrations/openai_agents.py +0 -0
- /agentx_python-0.6.3/agentx/resources/__init__.py → /agentx_python-0.6.5/agentx/py.typed +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/resources/agent.py +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/resources/workforce.py +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/tracing/__init__.py +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/tracing/ci_types.py +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx_python.egg-info/dependency_links.txt +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx_python.egg-info/requires.txt +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx_python.egg-info/top_level.txt +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/setup.cfg +0 -0
- {agentx_python-0.6.3 → agentx_python-0.6.5}/tests/test_integration.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentx-python
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.5
|
|
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
|
|
@@ -49,8 +49,12 @@ Dynamic: summary
|
|
|
49
49
|

|
|
50
50
|
|
|
51
51
|
[](https://pypi.org/project/agentx-python/)
|
|
52
|
+
[](https://pypi.org/project/agentx-python/)
|
|
53
|
+
[](LICENSE)
|
|
52
54
|
|
|
53
|
-
The official Python SDK for **[AgentX](https://
|
|
55
|
+
The official Python SDK for **[AgentX](https://app.agentx.so/)** — build, chat with, orchestrate, and trace AI agents in a few lines of code.
|
|
56
|
+
|
|
57
|
+
Also see [SDK Developer Docs](https://developers.agentx.so), [API Reference Docs](https://docs.agentx.so/reference)
|
|
54
58
|
|
|
55
59
|
---
|
|
56
60
|
|
|
@@ -64,8 +68,9 @@ The official Python SDK for **[AgentX](https://www.agentx.so/)** — build, chat
|
|
|
64
68
|
- [List agents](#list-agents)
|
|
65
69
|
- [Start a conversation](#start-a-conversation)
|
|
66
70
|
- [Chat (streaming and non-streaming)](#chat-streaming-and-non-streaming)
|
|
67
|
-
- [Workforce (multi-agent orchestration)](#workforce-multi-agent-orchestration)
|
|
68
|
-
- [
|
|
71
|
+
- [Workforce (multi-agent orchestration)](#workforce-multi-agent-orchestration) — teams of agents with a designated manager
|
|
72
|
+
- [Production tracing](#production-tracing) — record live agent runs from any framework
|
|
73
|
+
- [Custom agent evaluations](#custom-agent-evaluations) — LLM-as-a-judge, cosine / Jaccard similarity
|
|
69
74
|
- [Links](#links)
|
|
70
75
|
|
|
71
76
|
---
|
|
@@ -78,6 +83,7 @@ The official Python SDK for **[AgentX](https://www.agentx.so/)** — build, chat
|
|
|
78
83
|
- **Batteries included** — voice (ASR/TTS), image generation, document/CSV/Excel/OCR, RAG with built-in re-ranking.
|
|
79
84
|
- **MCP support** — connect any Model Context Protocol server.
|
|
80
85
|
- **Multi-agent orchestration** — workforces of agents with a designated manager, across LLM vendors.
|
|
86
|
+
- **Production tracing** — one decorator or context manager records every agent run (input, output, latency, tool calls, token usage) into your workspace, for any framework.
|
|
81
87
|
- **Agent Evaluations** — score any agent (LangChain, CrewAI, OpenAI, Anthropic, HTTP, …) with LLM-as-a-judge ratings plus optional cosine and Jaccard similarity metrics.
|
|
82
88
|
- **A2A** — Each agent can be published with agent-to-agent protocol compatible.
|
|
83
89
|
|
|
@@ -187,6 +193,58 @@ for chunk in workforce.chat_stream(conversation.id, "How can you help me with th
|
|
|
187
193
|
|
|
188
194
|
---
|
|
189
195
|
|
|
196
|
+
## Production tracing
|
|
197
|
+
|
|
198
|
+
Record live agent runs into your workspace with a single decorator or context manager — no changes to your agent's logic. Traces appear in the **Live Traces** tab and can be evaluated against your test datasets with [`tracer.evaluate_trace()`](TRACING.md#tracerevaluate_trace).
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
from agentx import AgentX
|
|
202
|
+
|
|
203
|
+
client = AgentX.from_env()
|
|
204
|
+
tracer = client.tracer
|
|
205
|
+
|
|
206
|
+
@tracer.trace("customer-support-agent", framework="langchain", model="gpt-4o")
|
|
207
|
+
def handle_query(query: str) -> str:
|
|
208
|
+
return chain.invoke(query)
|
|
209
|
+
|
|
210
|
+
# Every call is automatically traced: input, output, latency, tool calls, token usage
|
|
211
|
+
handle_query("How do I reset my password?")
|
|
212
|
+
tracer.flush(timeout=10) # ensure delivery before the process exits
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Prefer full control over what gets captured? Use the context manager instead:
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
with tracer.trace("rag-agent", framework="langchain") as span:
|
|
219
|
+
span.input = {"query": query, "user_id": user_id}
|
|
220
|
+
|
|
221
|
+
kb_result = search_knowledge_base(query)
|
|
222
|
+
span.add_tool_call("search_knowledge_base", input=query, output=kb_result, latency_ms=190)
|
|
223
|
+
|
|
224
|
+
span.output = llm.invoke(f"Context: {kb_result}\n\nQuery: {query}")
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Framework integrations
|
|
228
|
+
|
|
229
|
+
Each integration auto-captures LLM calls, tool calls, and token usage — install the matching extra:
|
|
230
|
+
|
|
231
|
+
| Framework | Install | Integration |
|
|
232
|
+
| --------------------- | -------------------------------------------- | ------------------------ |
|
|
233
|
+
| LangChain | `pip install "agentx-python[langchain]"` | `AgentXCallbackHandler` |
|
|
234
|
+
| CrewAI | `pip install "agentx-python[crewai]"` | `AgentXCrewObserver` |
|
|
235
|
+
| OpenAI Agents SDK | `pip install "agentx-python[openai-agents]"` | `AgentXTracingProcessor` |
|
|
236
|
+
| Anthropic | `pip install "agentx-python[anthropic]"` | `patch_anthropic_client` |
|
|
237
|
+
| Google ADK | `pip install "agentx-python[google-adk]"` | `AgentXADKPlugin` |
|
|
238
|
+
| Google GenAI (Gemini) | `pip install "agentx-python[google-genai]"` | `patch_genai_client` |
|
|
239
|
+
|
|
240
|
+
Or plain Python — wrap any function with `@tracer.trace(...)` and it just works, no framework required.
|
|
241
|
+
|
|
242
|
+
Running specialist agents in parallel with a `ThreadPoolExecutor`? Wrap each worker body in `tracer.use_span(span)` so their steps land on the parent trace instead of becoming independent traces — see [TRACING.md](TRACING.md) for the full pattern.
|
|
243
|
+
|
|
244
|
+
See **[TRACING.md](TRACING.md)** for the complete guide — session grouping, error handling, async support, and the full API reference.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
190
248
|
## Custom agent evaluations
|
|
191
249
|
|
|
192
250
|
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.
|
|
@@ -214,4 +272,5 @@ See **[EVALUATIONS.md](EVALUATIONS.md)** for the full guide — dataset builder,
|
|
|
214
272
|
- **Dashboard** — [app.agentx.so](https://app.agentx.so)
|
|
215
273
|
- **Website** — [agentx.so](https://www.agentx.so/)
|
|
216
274
|
- **PyPI** — [agentx-python](https://pypi.org/project/agentx-python/)
|
|
275
|
+
- **Tracing docs** — [TRACING.md](TRACING.md)
|
|
217
276
|
- **Evaluations docs** — [EVALUATIONS.md](EVALUATIONS.md)
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|

|
|
2
2
|
|
|
3
3
|
[](https://pypi.org/project/agentx-python/)
|
|
4
|
+
[](https://pypi.org/project/agentx-python/)
|
|
5
|
+
[](LICENSE)
|
|
4
6
|
|
|
5
|
-
The official Python SDK for **[AgentX](https://
|
|
7
|
+
The official Python SDK for **[AgentX](https://app.agentx.so/)** — build, chat with, orchestrate, and trace AI agents in a few lines of code.
|
|
8
|
+
|
|
9
|
+
Also see [SDK Developer Docs](https://developers.agentx.so), [API Reference Docs](https://docs.agentx.so/reference)
|
|
6
10
|
|
|
7
11
|
---
|
|
8
12
|
|
|
@@ -16,8 +20,9 @@ The official Python SDK for **[AgentX](https://www.agentx.so/)** — build, chat
|
|
|
16
20
|
- [List agents](#list-agents)
|
|
17
21
|
- [Start a conversation](#start-a-conversation)
|
|
18
22
|
- [Chat (streaming and non-streaming)](#chat-streaming-and-non-streaming)
|
|
19
|
-
- [Workforce (multi-agent orchestration)](#workforce-multi-agent-orchestration)
|
|
20
|
-
- [
|
|
23
|
+
- [Workforce (multi-agent orchestration)](#workforce-multi-agent-orchestration) — teams of agents with a designated manager
|
|
24
|
+
- [Production tracing](#production-tracing) — record live agent runs from any framework
|
|
25
|
+
- [Custom agent evaluations](#custom-agent-evaluations) — LLM-as-a-judge, cosine / Jaccard similarity
|
|
21
26
|
- [Links](#links)
|
|
22
27
|
|
|
23
28
|
---
|
|
@@ -30,6 +35,7 @@ The official Python SDK for **[AgentX](https://www.agentx.so/)** — build, chat
|
|
|
30
35
|
- **Batteries included** — voice (ASR/TTS), image generation, document/CSV/Excel/OCR, RAG with built-in re-ranking.
|
|
31
36
|
- **MCP support** — connect any Model Context Protocol server.
|
|
32
37
|
- **Multi-agent orchestration** — workforces of agents with a designated manager, across LLM vendors.
|
|
38
|
+
- **Production tracing** — one decorator or context manager records every agent run (input, output, latency, tool calls, token usage) into your workspace, for any framework.
|
|
33
39
|
- **Agent Evaluations** — score any agent (LangChain, CrewAI, OpenAI, Anthropic, HTTP, …) with LLM-as-a-judge ratings plus optional cosine and Jaccard similarity metrics.
|
|
34
40
|
- **A2A** — Each agent can be published with agent-to-agent protocol compatible.
|
|
35
41
|
|
|
@@ -139,6 +145,58 @@ for chunk in workforce.chat_stream(conversation.id, "How can you help me with th
|
|
|
139
145
|
|
|
140
146
|
---
|
|
141
147
|
|
|
148
|
+
## Production tracing
|
|
149
|
+
|
|
150
|
+
Record live agent runs into your workspace with a single decorator or context manager — no changes to your agent's logic. Traces appear in the **Live Traces** tab and can be evaluated against your test datasets with [`tracer.evaluate_trace()`](TRACING.md#tracerevaluate_trace).
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
from agentx import AgentX
|
|
154
|
+
|
|
155
|
+
client = AgentX.from_env()
|
|
156
|
+
tracer = client.tracer
|
|
157
|
+
|
|
158
|
+
@tracer.trace("customer-support-agent", framework="langchain", model="gpt-4o")
|
|
159
|
+
def handle_query(query: str) -> str:
|
|
160
|
+
return chain.invoke(query)
|
|
161
|
+
|
|
162
|
+
# Every call is automatically traced: input, output, latency, tool calls, token usage
|
|
163
|
+
handle_query("How do I reset my password?")
|
|
164
|
+
tracer.flush(timeout=10) # ensure delivery before the process exits
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Prefer full control over what gets captured? Use the context manager instead:
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
with tracer.trace("rag-agent", framework="langchain") as span:
|
|
171
|
+
span.input = {"query": query, "user_id": user_id}
|
|
172
|
+
|
|
173
|
+
kb_result = search_knowledge_base(query)
|
|
174
|
+
span.add_tool_call("search_knowledge_base", input=query, output=kb_result, latency_ms=190)
|
|
175
|
+
|
|
176
|
+
span.output = llm.invoke(f"Context: {kb_result}\n\nQuery: {query}")
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Framework integrations
|
|
180
|
+
|
|
181
|
+
Each integration auto-captures LLM calls, tool calls, and token usage — install the matching extra:
|
|
182
|
+
|
|
183
|
+
| Framework | Install | Integration |
|
|
184
|
+
| --------------------- | -------------------------------------------- | ------------------------ |
|
|
185
|
+
| LangChain | `pip install "agentx-python[langchain]"` | `AgentXCallbackHandler` |
|
|
186
|
+
| CrewAI | `pip install "agentx-python[crewai]"` | `AgentXCrewObserver` |
|
|
187
|
+
| OpenAI Agents SDK | `pip install "agentx-python[openai-agents]"` | `AgentXTracingProcessor` |
|
|
188
|
+
| Anthropic | `pip install "agentx-python[anthropic]"` | `patch_anthropic_client` |
|
|
189
|
+
| Google ADK | `pip install "agentx-python[google-adk]"` | `AgentXADKPlugin` |
|
|
190
|
+
| Google GenAI (Gemini) | `pip install "agentx-python[google-genai]"` | `patch_genai_client` |
|
|
191
|
+
|
|
192
|
+
Or plain Python — wrap any function with `@tracer.trace(...)` and it just works, no framework required.
|
|
193
|
+
|
|
194
|
+
Running specialist agents in parallel with a `ThreadPoolExecutor`? Wrap each worker body in `tracer.use_span(span)` so their steps land on the parent trace instead of becoming independent traces — see [TRACING.md](TRACING.md) for the full pattern.
|
|
195
|
+
|
|
196
|
+
See **[TRACING.md](TRACING.md)** for the complete guide — session grouping, error handling, async support, and the full API reference.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
142
200
|
## Custom agent evaluations
|
|
143
201
|
|
|
144
202
|
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.
|
|
@@ -166,4 +224,5 @@ See **[EVALUATIONS.md](EVALUATIONS.md)** for the full guide — dataset builder,
|
|
|
166
224
|
- **Dashboard** — [app.agentx.so](https://app.agentx.so)
|
|
167
225
|
- **Website** — [agentx.so](https://www.agentx.so/)
|
|
168
226
|
- **PyPI** — [agentx-python](https://pypi.org/project/agentx-python/)
|
|
227
|
+
- **Tracing docs** — [TRACING.md](TRACING.md)
|
|
169
228
|
- **Evaluations docs** — [EVALUATIONS.md](EVALUATIONS.md)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import List
|
|
1
|
+
from typing import List, Optional
|
|
2
2
|
import requests
|
|
3
3
|
import os
|
|
4
4
|
import logging
|
|
@@ -10,7 +10,12 @@ from agentx.resources.workforce import Workforce
|
|
|
10
10
|
|
|
11
11
|
class AgentX:
|
|
12
12
|
|
|
13
|
-
def __init__(
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
api_key: Optional[str] = None,
|
|
16
|
+
base_url: Optional[str] = None,
|
|
17
|
+
workspace_id: Optional[str] = None,
|
|
18
|
+
):
|
|
14
19
|
self.api_key = api_key or os.getenv("AGENTX_API_KEY")
|
|
15
20
|
if self.api_key and not os.getenv("AGENTX_API_KEY"):
|
|
16
21
|
os.environ["AGENTX_API_KEY"] = self.api_key
|
|
@@ -32,6 +37,7 @@ class AgentX:
|
|
|
32
37
|
api_key=self.api_key,
|
|
33
38
|
sdk_version=VERSION,
|
|
34
39
|
base_url=self.base_url,
|
|
40
|
+
workspace_id=self.workspace_id,
|
|
35
41
|
)
|
|
36
42
|
self.evaluations = EvaluationsRunner(_eval_client)
|
|
37
43
|
|
|
@@ -13,6 +13,7 @@ from agentx.evaluations.models import (
|
|
|
13
13
|
Dataset,
|
|
14
14
|
EvaluationResult,
|
|
15
15
|
EvaluationRun,
|
|
16
|
+
EvaluationSettings,
|
|
16
17
|
EvaluationSubject,
|
|
17
18
|
ModelInfo,
|
|
18
19
|
Report,
|
|
@@ -44,12 +45,20 @@ class AgentXValidationError(AgentXEvaluationsError):
|
|
|
44
45
|
|
|
45
46
|
class EvaluationsClient:
|
|
46
47
|
def __init__(
|
|
47
|
-
self,
|
|
48
|
+
self,
|
|
49
|
+
api_key: str,
|
|
50
|
+
sdk_version: str = "unknown",
|
|
51
|
+
base_url: Optional[str] = None,
|
|
52
|
+
workspace_id: Optional[str] = None,
|
|
48
53
|
):
|
|
49
54
|
if not api_key:
|
|
50
55
|
raise AgentXAuthError("AGENTX_API_KEY is required")
|
|
51
56
|
self._api_key = api_key
|
|
52
57
|
self._sdk_version = sdk_version
|
|
58
|
+
# Falls back to the caller's default workspace server-side when unset — see
|
|
59
|
+
# _with_workspace(). Without this, dataset/settings/run creation silently land in
|
|
60
|
+
# whatever workspace the API key's user defaults to, not the one the caller intended.
|
|
61
|
+
self._workspace_id = workspace_id
|
|
53
62
|
# Priority: constructor arg > env var > SDK default
|
|
54
63
|
# Always append /custom-agent-evaluations so users only need to provide /api/v1
|
|
55
64
|
_api_base = (
|
|
@@ -67,15 +76,29 @@ class EvaluationsClient:
|
|
|
67
76
|
"accept": "*/*",
|
|
68
77
|
}
|
|
69
78
|
)
|
|
70
|
-
# Expose dataset builder
|
|
79
|
+
# Expose dataset / evaluation-settings builder factories
|
|
71
80
|
from agentx.evaluations.datasets import DatasetClient
|
|
81
|
+
from agentx.evaluations.evaluation_settings import EvaluationSettingsClient
|
|
72
82
|
|
|
73
83
|
self.datasets = DatasetClient(self)
|
|
84
|
+
self.settings = EvaluationSettingsClient(self)
|
|
74
85
|
|
|
75
86
|
# ------------------------------------------------------------------
|
|
76
87
|
# Low-level HTTP
|
|
77
88
|
# ------------------------------------------------------------------
|
|
78
89
|
|
|
90
|
+
def _with_workspace(self, payload: dict) -> dict:
|
|
91
|
+
"""Injects the client's workspace_id into a request payload, unless the caller already
|
|
92
|
+
set one explicitly. Without this, requests silently fall back to the API key user's
|
|
93
|
+
default workspace server-side, which may not be the workspace the caller intended."""
|
|
94
|
+
if self._workspace_id and not payload.get("workspaceId"):
|
|
95
|
+
return {**payload, "workspaceId": self._workspace_id}
|
|
96
|
+
return payload
|
|
97
|
+
|
|
98
|
+
def _workspace_params(self) -> Optional[dict]:
|
|
99
|
+
"""Same as _with_workspace(), for GET requests that take workspaceId as a query param."""
|
|
100
|
+
return {"workspaceId": self._workspace_id} if self._workspace_id else None
|
|
101
|
+
|
|
79
102
|
def _request(self, method: str, path: str, timeout: int = 30, **kwargs) -> Any:
|
|
80
103
|
url = f"{self._base_url}{path}"
|
|
81
104
|
last_exc: Optional[Exception] = None
|
|
@@ -125,20 +148,52 @@ class EvaluationsClient:
|
|
|
125
148
|
# ------------------------------------------------------------------
|
|
126
149
|
|
|
127
150
|
def create_dataset(self, payload: dict) -> Dataset:
|
|
128
|
-
data = self._request("POST", "/datasets", json=payload)
|
|
151
|
+
data = self._request("POST", "/datasets", json=self._with_workspace(payload))
|
|
129
152
|
return Dataset(**data)
|
|
130
153
|
|
|
131
154
|
def list_datasets(self) -> List[Dataset]:
|
|
132
|
-
data = self._request("GET", "/datasets")
|
|
155
|
+
data = self._request("GET", "/datasets", params=self._workspace_params())
|
|
133
156
|
return [
|
|
134
157
|
Dataset(**d)
|
|
135
158
|
for d in (data if isinstance(data, list) else data.get("datasets", []))
|
|
136
159
|
]
|
|
137
160
|
|
|
138
161
|
def get_dataset(self, dataset_id: str) -> Dataset:
|
|
139
|
-
data = self._request(
|
|
162
|
+
data = self._request(
|
|
163
|
+
"GET", f"/datasets/{dataset_id}", params=self._workspace_params()
|
|
164
|
+
)
|
|
140
165
|
return Dataset(**data)
|
|
141
166
|
|
|
167
|
+
# ------------------------------------------------------------------
|
|
168
|
+
# Evaluation Settings endpoints — standalone grading config, reusable
|
|
169
|
+
# across datasets.
|
|
170
|
+
# ------------------------------------------------------------------
|
|
171
|
+
|
|
172
|
+
def create_evaluation_settings(self, payload: dict) -> EvaluationSettings:
|
|
173
|
+
data = self._request(
|
|
174
|
+
"POST", "/evaluation-settings", json=self._with_workspace(payload)
|
|
175
|
+
)
|
|
176
|
+
return EvaluationSettings(**data)
|
|
177
|
+
|
|
178
|
+
def list_evaluation_settings(self) -> List[EvaluationSettings]:
|
|
179
|
+
data = self._request(
|
|
180
|
+
"GET", "/evaluation-settings", params=self._workspace_params()
|
|
181
|
+
)
|
|
182
|
+
return [
|
|
183
|
+
EvaluationSettings(**e)
|
|
184
|
+
for e in (
|
|
185
|
+
data if isinstance(data, list) else data.get("evaluationSettings", [])
|
|
186
|
+
)
|
|
187
|
+
]
|
|
188
|
+
|
|
189
|
+
def get_evaluation_settings(self, evaluation_settings_id: str) -> EvaluationSettings:
|
|
190
|
+
data = self._request(
|
|
191
|
+
"GET",
|
|
192
|
+
f"/evaluation-settings/{evaluation_settings_id}",
|
|
193
|
+
params=self._workspace_params(),
|
|
194
|
+
)
|
|
195
|
+
return EvaluationSettings(**data)
|
|
196
|
+
|
|
142
197
|
# ------------------------------------------------------------------
|
|
143
198
|
# Run endpoints
|
|
144
199
|
# ------------------------------------------------------------------
|
|
@@ -148,6 +203,7 @@ class EvaluationsClient:
|
|
|
148
203
|
dataset_id: str,
|
|
149
204
|
subject: EvaluationSubject,
|
|
150
205
|
python_version: Optional[str] = None,
|
|
206
|
+
evaluation_settings_id: Optional[str] = None,
|
|
151
207
|
) -> EvaluationRun:
|
|
152
208
|
from agentx.version import VERSION
|
|
153
209
|
|
|
@@ -162,7 +218,9 @@ class EvaluationsClient:
|
|
|
162
218
|
"pythonVersion": python_version or _python_version(),
|
|
163
219
|
},
|
|
164
220
|
}
|
|
165
|
-
|
|
221
|
+
if evaluation_settings_id:
|
|
222
|
+
payload["evaluationSettingsId"] = evaluation_settings_id
|
|
223
|
+
data = self._request("POST", "/runs", json=self._with_workspace(payload))
|
|
166
224
|
return EvaluationRun(**data)
|
|
167
225
|
|
|
168
226
|
def append_results(
|
|
@@ -207,6 +265,7 @@ def _result_to_payload(r: EvaluationResult) -> dict:
|
|
|
207
265
|
d["questionIndex"] = d.pop("question_index", d.get("questionIndex"))
|
|
208
266
|
d["runNumber"] = d.pop("run_number", d.get("runNumber"))
|
|
209
267
|
d["idempotencyKey"] = d.pop("idempotency_key", d.get("idempotencyKey"))
|
|
268
|
+
d["traceId"] = d.pop("trace_id", d.get("traceId"))
|
|
210
269
|
return {k: v for k, v in d.items() if v is not None}
|
|
211
270
|
|
|
212
271
|
|
|
@@ -29,6 +29,8 @@ class DatasetBuilder:
|
|
|
29
29
|
evaluation_criteria: Optional[str] = None,
|
|
30
30
|
vector_similarity: bool = False,
|
|
31
31
|
jaccard_similarity: bool = False,
|
|
32
|
+
bleu_score: bool = False,
|
|
33
|
+
rouge_score: bool = False,
|
|
32
34
|
similarity_model: Optional[str] = None,
|
|
33
35
|
sovereignty_models: Optional[List[str]] = None,
|
|
34
36
|
):
|
|
@@ -43,7 +45,8 @@ class DatasetBuilder:
|
|
|
43
45
|
"questions": [],
|
|
44
46
|
}
|
|
45
47
|
# Opt-in similarity metrics, surfaced on the report as cosine_similarity /
|
|
46
|
-
# jaccard_similarity (computed against each
|
|
48
|
+
# jaccard_similarity / bleu_score / rouge_score (computed against each
|
|
49
|
+
# case's expected_results).
|
|
47
50
|
if vector_similarity:
|
|
48
51
|
vs: Dict[str, Any] = {"enabled": True}
|
|
49
52
|
if similarity_model:
|
|
@@ -51,6 +54,10 @@ class DatasetBuilder:
|
|
|
51
54
|
self._payload["vectorSimilarity"] = vs
|
|
52
55
|
if jaccard_similarity:
|
|
53
56
|
self._payload["jaccardSimilarity"] = {"enabled": True}
|
|
57
|
+
if bleu_score:
|
|
58
|
+
self._payload["bleuScore"] = {"enabled": True}
|
|
59
|
+
if rouge_score:
|
|
60
|
+
self._payload["rougeScore"] = {"enabled": True}
|
|
54
61
|
# Sovereignty & Portability — the models to compare on this dataset (use
|
|
55
62
|
# client.evaluations.list_models() to discover valid ids).
|
|
56
63
|
if sovereignty_models:
|
|
@@ -212,6 +219,8 @@ class DatasetClient:
|
|
|
212
219
|
evaluation_criteria: Optional[str] = None,
|
|
213
220
|
vector_similarity: bool = False,
|
|
214
221
|
jaccard_similarity: bool = False,
|
|
222
|
+
bleu_score: bool = False,
|
|
223
|
+
rouge_score: bool = False,
|
|
215
224
|
similarity_model: Optional[str] = None,
|
|
216
225
|
sovereignty_models: Optional[List[str]] = None,
|
|
217
226
|
) -> DatasetBuilder:
|
|
@@ -225,6 +234,8 @@ class DatasetClient:
|
|
|
225
234
|
evaluation_criteria=evaluation_criteria,
|
|
226
235
|
vector_similarity=vector_similarity,
|
|
227
236
|
jaccard_similarity=jaccard_similarity,
|
|
237
|
+
bleu_score=bleu_score,
|
|
238
|
+
rouge_score=rouge_score,
|
|
228
239
|
similarity_model=similarity_model,
|
|
229
240
|
sovereignty_models=sovereignty_models,
|
|
230
241
|
)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Any, Dict, List, Optional, TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from agentx.evaluations.models import EvaluationSettings
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from agentx.evaluations.client import EvaluationsClient
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class EvaluationSettingsBuilder:
|
|
15
|
+
"""Fluent builder for creating a standalone, reusable grading config (no
|
|
16
|
+
dataset/questions attached)."""
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
client: "EvaluationsClient",
|
|
21
|
+
name: str,
|
|
22
|
+
description: Optional[str] = None,
|
|
23
|
+
number_of_requests: int = 1,
|
|
24
|
+
acceptance_criteria: Optional[str] = None,
|
|
25
|
+
rejection_criteria: Optional[str] = None,
|
|
26
|
+
evaluation_criteria: Optional[str] = None,
|
|
27
|
+
vector_similarity: bool = False,
|
|
28
|
+
jaccard_similarity: bool = False,
|
|
29
|
+
bleu_score: bool = False,
|
|
30
|
+
rouge_score: bool = False,
|
|
31
|
+
similarity_model: Optional[str] = None,
|
|
32
|
+
sovereignty_models: Optional[List[str]] = None,
|
|
33
|
+
):
|
|
34
|
+
self._client = client
|
|
35
|
+
self._payload: Dict[str, Any] = {
|
|
36
|
+
"name": name,
|
|
37
|
+
"description": description,
|
|
38
|
+
"numberOfRequests": number_of_requests,
|
|
39
|
+
"acceptanceCriteria": acceptance_criteria,
|
|
40
|
+
"rejectionCriteria": rejection_criteria,
|
|
41
|
+
"evaluationCriteria": evaluation_criteria,
|
|
42
|
+
}
|
|
43
|
+
# Opt-in similarity metrics, mirrors DatasetBuilder's config kwargs.
|
|
44
|
+
if vector_similarity:
|
|
45
|
+
vs: Dict[str, Any] = {"enabled": True}
|
|
46
|
+
if similarity_model:
|
|
47
|
+
vs["model"] = similarity_model
|
|
48
|
+
self._payload["vectorSimilarity"] = vs
|
|
49
|
+
if jaccard_similarity:
|
|
50
|
+
self._payload["jaccardSimilarity"] = {"enabled": True}
|
|
51
|
+
if bleu_score:
|
|
52
|
+
self._payload["bleuScore"] = {"enabled": True}
|
|
53
|
+
if rouge_score:
|
|
54
|
+
self._payload["rougeScore"] = {"enabled": True}
|
|
55
|
+
# Sovereignty & Portability — the models to compare when this config runs
|
|
56
|
+
# (use client.evaluations.list_models() to discover valid ids).
|
|
57
|
+
if sovereignty_models:
|
|
58
|
+
self._payload["sovereigntyIndex"] = {
|
|
59
|
+
"enabled": True,
|
|
60
|
+
"models": list(sovereignty_models),
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
def publish(self) -> EvaluationSettings:
|
|
64
|
+
logger.info("Publishing evaluation settings '%s'", self._payload["name"])
|
|
65
|
+
return self._client.create_evaluation_settings(self._payload)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class EvaluationSettingsClient:
|
|
69
|
+
"""Thin wrapper surfaced as client.evaluations.settings."""
|
|
70
|
+
|
|
71
|
+
def __init__(self, client: "EvaluationsClient"):
|
|
72
|
+
self._client = client
|
|
73
|
+
|
|
74
|
+
def builder(
|
|
75
|
+
self,
|
|
76
|
+
name: str,
|
|
77
|
+
description: Optional[str] = None,
|
|
78
|
+
number_of_requests: int = 1,
|
|
79
|
+
acceptance_criteria: Optional[str] = None,
|
|
80
|
+
rejection_criteria: Optional[str] = None,
|
|
81
|
+
evaluation_criteria: Optional[str] = None,
|
|
82
|
+
vector_similarity: bool = False,
|
|
83
|
+
jaccard_similarity: bool = False,
|
|
84
|
+
bleu_score: bool = False,
|
|
85
|
+
rouge_score: bool = False,
|
|
86
|
+
similarity_model: Optional[str] = None,
|
|
87
|
+
sovereignty_models: Optional[List[str]] = None,
|
|
88
|
+
) -> EvaluationSettingsBuilder:
|
|
89
|
+
return EvaluationSettingsBuilder(
|
|
90
|
+
self._client,
|
|
91
|
+
name=name,
|
|
92
|
+
description=description,
|
|
93
|
+
number_of_requests=number_of_requests,
|
|
94
|
+
acceptance_criteria=acceptance_criteria,
|
|
95
|
+
rejection_criteria=rejection_criteria,
|
|
96
|
+
evaluation_criteria=evaluation_criteria,
|
|
97
|
+
vector_similarity=vector_similarity,
|
|
98
|
+
jaccard_similarity=jaccard_similarity,
|
|
99
|
+
bleu_score=bleu_score,
|
|
100
|
+
rouge_score=rouge_score,
|
|
101
|
+
similarity_model=similarity_model,
|
|
102
|
+
sovereignty_models=sovereignty_models,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
def get(self, evaluation_settings_id: str) -> EvaluationSettings:
|
|
106
|
+
return self._client.get_evaluation_settings(evaluation_settings_id)
|
|
107
|
+
|
|
108
|
+
def list(self) -> List[EvaluationSettings]:
|
|
109
|
+
return self._client.list_evaluation_settings()
|