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.
Files changed (51) hide show
  1. {agentx_python-0.6.3/agentx_python.egg-info → agentx_python-0.6.5}/PKG-INFO +63 -4
  2. {agentx_python-0.6.3 → agentx_python-0.6.5}/README.md +62 -3
  3. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/agentx.py +8 -2
  4. agentx_python-0.6.5/agentx/evaluations/__init__.py +7 -0
  5. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/client.py +65 -6
  6. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/datasets.py +12 -1
  7. agentx_python-0.6.5/agentx/evaluations/evaluation_settings.py +109 -0
  8. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/models.py +89 -7
  9. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/reporting.py +14 -0
  10. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/results.py +4 -0
  11. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/runner.py +87 -10
  12. agentx_python-0.6.5/agentx/resources/__init__.py +0 -0
  13. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/resources/conversation.py +2 -2
  14. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/tracing/ingest_client.py +24 -0
  15. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/tracing/tracer.py +56 -5
  16. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/util.py +2 -1
  17. agentx_python-0.6.5/agentx/version.py +1 -0
  18. {agentx_python-0.6.3 → agentx_python-0.6.5/agentx_python.egg-info}/PKG-INFO +63 -4
  19. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx_python.egg-info/SOURCES.txt +3 -0
  20. agentx_python-0.6.5/agentx_python.egg-info/not-zip-safe +1 -0
  21. {agentx_python-0.6.3 → agentx_python-0.6.5}/setup.py +5 -0
  22. agentx_python-0.6.3/agentx/evaluations/__init__.py +0 -3
  23. agentx_python-0.6.3/agentx/version.py +0 -1
  24. {agentx_python-0.6.3 → agentx_python-0.6.5}/LICENSE +0 -0
  25. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/__init__.py +0 -0
  26. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/_term.py +0 -0
  27. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/adapters/__init__.py +0 -0
  28. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/adapters/http_endpoint.py +0 -0
  29. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/adapters/precomputed.py +0 -0
  30. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/adapters/raw.py +0 -0
  31. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/redaction.py +0 -0
  32. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/evaluations/tracing.py +0 -0
  33. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/exceptions.py +0 -0
  34. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/integrations/__init__.py +0 -0
  35. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/integrations/_perf.py +0 -0
  36. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/integrations/anthropic.py +0 -0
  37. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/integrations/crewai.py +0 -0
  38. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/integrations/google_adk.py +0 -0
  39. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/integrations/google_genai.py +0 -0
  40. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/integrations/langchain.py +0 -0
  41. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/integrations/openai_agents.py +0 -0
  42. /agentx_python-0.6.3/agentx/resources/__init__.py → /agentx_python-0.6.5/agentx/py.typed +0 -0
  43. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/resources/agent.py +0 -0
  44. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/resources/workforce.py +0 -0
  45. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/tracing/__init__.py +0 -0
  46. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx/tracing/ci_types.py +0 -0
  47. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx_python.egg-info/dependency_links.txt +0 -0
  48. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx_python.egg-info/requires.txt +0 -0
  49. {agentx_python-0.6.3 → agentx_python-0.6.5}/agentx_python.egg-info/top_level.txt +0 -0
  50. {agentx_python-0.6.3 → agentx_python-0.6.5}/setup.cfg +0 -0
  51. {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
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
  ![Logo](https://agentx-resources.s3.us-west-1.amazonaws.com/AgentX-logo-387x60.png)
50
50
 
51
51
  [![PyPI version](https://img.shields.io/pypi/v/agentx-python)](https://pypi.org/project/agentx-python/)
52
+ [![Python versions](https://img.shields.io/pypi/pyversions/agentx-python)](https://pypi.org/project/agentx-python/)
53
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
52
54
 
53
- The official Python SDK for **[AgentX](https://www.agentx.so/)** — build, chat with, and orchestrate AI agents in a few lines of code.
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
- - [Agent Evaluations](#custom-agent-evaluations) — LLM-as-a-judge, cosine / Jaccard similarity
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
  ![Logo](https://agentx-resources.s3.us-west-1.amazonaws.com/AgentX-logo-387x60.png)
2
2
 
3
3
  [![PyPI version](https://img.shields.io/pypi/v/agentx-python)](https://pypi.org/project/agentx-python/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/agentx-python)](https://pypi.org/project/agentx-python/)
5
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
4
6
 
5
- The official Python SDK for **[AgentX](https://www.agentx.so/)** — build, chat with, and orchestrate AI agents in a few lines of code.
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
- - [Agent Evaluations](#custom-agent-evaluations) — LLM-as-a-judge, cosine / Jaccard similarity
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__(self, api_key: str = None, base_url: str = None, workspace_id: str = None):
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
 
@@ -0,0 +1,7 @@
1
+ from agentx.evaluations.client import EvaluationsClient
2
+ from agentx.evaluations.evaluation_settings import (
3
+ EvaluationSettingsBuilder,
4
+ EvaluationSettingsClient,
5
+ )
6
+
7
+ __all__ = ["EvaluationsClient", "EvaluationSettingsBuilder", "EvaluationSettingsClient"]
@@ -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, api_key: str, sdk_version: str = "unknown", base_url: str = None
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 factory
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("GET", f"/datasets/{dataset_id}")
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
- data = self._request("POST", "/runs", json=payload)
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 case's expected_results).
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()