agentx-python 0.6.10__tar.gz → 0.6.12__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 (68) hide show
  1. {agentx_python-0.6.10 → agentx_python-0.6.12}/PKG-INFO +47 -2
  2. agentx_python-0.6.10/agentx_python.egg-info/PKG-INFO → agentx_python-0.6.12/README.md +32 -49
  3. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/evaluations/client.py +27 -1
  4. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/evaluations/models.py +24 -0
  5. agentx_python-0.6.12/agentx/evaluations/prompts.py +55 -0
  6. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/evaluations/runner.py +1 -0
  7. agentx_python-0.6.12/agentx/integrations/_traced_call.py +141 -0
  8. agentx_python-0.6.12/agentx/integrations/anthropic.py +266 -0
  9. agentx_python-0.6.12/agentx/integrations/autogen.py +199 -0
  10. agentx_python-0.6.12/agentx/integrations/crewai.py +257 -0
  11. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/integrations/google_adk.py +31 -0
  12. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/integrations/google_genai.py +127 -64
  13. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/integrations/langchain.py +69 -7
  14. agentx_python-0.6.12/agentx/integrations/litellm.py +142 -0
  15. agentx_python-0.6.12/agentx/integrations/llamaindex.py +302 -0
  16. agentx_python-0.6.12/agentx/integrations/openai.py +158 -0
  17. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/integrations/openai_agents.py +11 -0
  18. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/monitor/client.py +49 -1
  19. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/monitor/models.py +59 -0
  20. agentx_python-0.6.12/agentx/monitor/online_evaluators.py +120 -0
  21. agentx_python-0.6.12/agentx/version.py +1 -0
  22. agentx_python-0.6.10/README.md → agentx_python-0.6.12/agentx_python.egg-info/PKG-INFO +94 -1
  23. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx_python.egg-info/SOURCES.txt +9 -1
  24. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx_python.egg-info/requires.txt +18 -0
  25. {agentx_python-0.6.10 → agentx_python-0.6.12}/setup.py +9 -0
  26. agentx_python-0.6.12/tests/test_integrations.py +1011 -0
  27. agentx_python-0.6.10/agentx/integrations/anthropic.py +0 -250
  28. agentx_python-0.6.10/agentx/integrations/crewai.py +0 -141
  29. agentx_python-0.6.10/agentx/version.py +0 -1
  30. {agentx_python-0.6.10 → agentx_python-0.6.12}/LICENSE +0 -0
  31. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/__init__.py +0 -0
  32. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/agentx.py +0 -0
  33. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/cli.py +0 -0
  34. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/evaluations/__init__.py +0 -0
  35. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/evaluations/_term.py +0 -0
  36. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/evaluations/adapters/__init__.py +0 -0
  37. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/evaluations/adapters/http_endpoint.py +0 -0
  38. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/evaluations/adapters/precomputed.py +0 -0
  39. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/evaluations/adapters/raw.py +0 -0
  40. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/evaluations/datasets.py +0 -0
  41. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/evaluations/evaluation_settings.py +0 -0
  42. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/evaluations/redaction.py +0 -0
  43. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/evaluations/reporting.py +0 -0
  44. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/evaluations/results.py +0 -0
  45. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/evaluations/tracing.py +0 -0
  46. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/exceptions.py +0 -0
  47. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/integrations/__init__.py +0 -0
  48. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/integrations/_perf.py +0 -0
  49. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/monitor/__init__.py +0 -0
  50. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/monitor/patterns.py +0 -0
  51. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/monitor/profile.py +0 -0
  52. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/monitor/signals.py +0 -0
  53. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/py.typed +0 -0
  54. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/resources/__init__.py +0 -0
  55. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/resources/agent.py +0 -0
  56. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/resources/conversation.py +0 -0
  57. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/resources/workforce.py +0 -0
  58. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/tracing/__init__.py +0 -0
  59. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/tracing/ci_types.py +0 -0
  60. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/tracing/ingest_client.py +0 -0
  61. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/tracing/tracer.py +0 -0
  62. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx/util.py +0 -0
  63. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx_python.egg-info/dependency_links.txt +0 -0
  64. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx_python.egg-info/entry_points.txt +0 -0
  65. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx_python.egg-info/not-zip-safe +0 -0
  66. {agentx_python-0.6.10 → agentx_python-0.6.12}/agentx_python.egg-info/top_level.txt +0 -0
  67. {agentx_python-0.6.10 → agentx_python-0.6.12}/setup.cfg +0 -0
  68. {agentx_python-0.6.10 → agentx_python-0.6.12}/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.10
3
+ Version: 0.6.12
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
@@ -21,19 +21,33 @@ Provides-Extra: crewai
21
21
  Requires-Dist: crewai>=0.80.0; extra == "crewai"
22
22
  Provides-Extra: openai-agents
23
23
  Requires-Dist: openai-agents>=0.0.3; extra == "openai-agents"
24
+ Provides-Extra: openai
25
+ Requires-Dist: openai>=1.0.0; extra == "openai"
24
26
  Provides-Extra: anthropic
25
27
  Requires-Dist: anthropic>=0.25.0; extra == "anthropic"
26
28
  Provides-Extra: google-adk
27
29
  Requires-Dist: google-adk>=1.0.0; extra == "google-adk"
28
30
  Provides-Extra: google-genai
29
31
  Requires-Dist: google-genai>=1.0.0; extra == "google-genai"
32
+ Provides-Extra: litellm
33
+ Requires-Dist: litellm>=1.0.0; extra == "litellm"
34
+ Provides-Extra: llamaindex
35
+ Requires-Dist: llama-index-core>=0.10.0; extra == "llamaindex"
36
+ Provides-Extra: autogen
37
+ Requires-Dist: autogen-agentchat>=0.4.0; extra == "autogen"
38
+ Requires-Dist: autogen-core>=0.4.0; extra == "autogen"
30
39
  Provides-Extra: all
31
40
  Requires-Dist: langchain-core>=0.1.0; extra == "all"
32
41
  Requires-Dist: crewai>=0.80.0; extra == "all"
33
42
  Requires-Dist: openai-agents>=0.0.3; extra == "all"
43
+ Requires-Dist: openai>=1.0.0; extra == "all"
34
44
  Requires-Dist: anthropic>=0.25.0; extra == "all"
35
45
  Requires-Dist: google-adk>=1.0.0; extra == "all"
36
46
  Requires-Dist: google-genai>=1.0.0; extra == "all"
47
+ Requires-Dist: litellm>=1.0.0; extra == "all"
48
+ Requires-Dist: llama-index-core>=0.10.0; extra == "all"
49
+ Requires-Dist: autogen-agentchat>=0.4.0; extra == "all"
50
+ Requires-Dist: autogen-core>=0.4.0; extra == "all"
37
51
  Dynamic: author
38
52
  Dynamic: author-email
39
53
  Dynamic: classifier
@@ -235,9 +249,13 @@ Each integration auto-captures LLM calls, tool calls, and token usage — instal
235
249
  | LangChain | `pip install "agentx-python[langchain]"` | `AgentXCallbackHandler` |
236
250
  | CrewAI | `pip install "agentx-python[crewai]"` | `AgentXCrewObserver` |
237
251
  | OpenAI Agents SDK | `pip install "agentx-python[openai-agents]"` | `AgentXTracingProcessor` |
252
+ | OpenAI (raw client) | `pip install "agentx-python[openai]"` | `patch_openai_client` |
238
253
  | Anthropic | `pip install "agentx-python[anthropic]"` | `patch_anthropic_client` |
239
254
  | Google ADK | `pip install "agentx-python[google-adk]"` | `AgentXADKPlugin` |
240
255
  | Google GenAI (Gemini) | `pip install "agentx-python[google-genai]"` | `patch_genai_client` |
256
+ | LiteLLM | `pip install "agentx-python[litellm]"` | `AgentXLiteLLMLogger` |
257
+ | LlamaIndex | `pip install "agentx-python[llamaindex]"` | `AgentXLlamaIndexHandler`|
258
+ | AutoGen | `pip install "agentx-python[autogen]"` | `AgentXAutoGenObserver` |
241
259
 
242
260
  Or plain Python — wrap any function with `@tracer.trace(...)` and it just works, no framework required.
243
261
 
@@ -280,6 +298,19 @@ Per-agent coverage/threshold settings (sample rate, retention, and threshold ove
280
298
  client.monitor.profile.update("agent_123", threshold_overrides={"latencyMs": 15000})
281
299
  ```
282
300
 
301
+ Self-host also has **online evaluators**: a real LLM judge scoring a sample of live traffic continuously, distinct from a pattern's rule-matching. A score below `alert_threshold` raises a signal the same way a failing pattern does, deduped and triage-ready in `client.monitor.signals`.
302
+
303
+ ```python
304
+ evaluator = client.monitor.online_evaluators.builder(
305
+ name="Helpfulness",
306
+ evaluation_settings_id=settings.id,
307
+ sample_rate=0.1,
308
+ alert_threshold=5,
309
+ ).publish()
310
+
311
+ client.monitor.online_evaluators.ratings(evaluator.id, window="7d")
312
+ ```
313
+
283
314
  See **[TRACING.md](TRACING.md)** for the complete Monitor guide.
284
315
 
285
316
  ---
@@ -309,7 +340,21 @@ print(report.recommendations) # list of prioritized, actionable fixes
309
340
 
310
341
  Ask a case's question several extra ways each run, LLM-paraphrased server-side, to catch agents that break on phrasing rather than substance, and override the judge's prompt/model per config, see [Smoke testing](EVALUATIONS.md#smoke-testing-phrasing-robustness) and [Configuring the judge](EVALUATIONS.md#configuring-the-judge) in the full guide.
311
342
 
312
- See **[EVALUATIONS.md](EVALUATIONS.md)** for the full guidedataset builder, framework adapters, similarity metrics, smoke testing, judge configuration, and the complete API reference.
343
+ Since AgentX doesn't own your agent's code, `client.evaluations.prompts` lets AgentX become your prompt's *source of truth* instead the same problem LangSmith's Prompt Hub and Langfuse's Prompt Management solve. Pull a version at runtime, tag your eval runs (or live traces) with it, and let a judge propose a rewrite from your real worst-rated results — a human always has to approve before it publishes:
344
+
345
+ ```python
346
+ prompt = client.evaluations.prompts.get("support-agent-system-prompt") # or prompt.id
347
+ # use prompt.text as your own agent's system prompt
348
+
349
+ client.evaluations.run(
350
+ dataset_id="evds_…",
351
+ subject={"kind": "custom_agent", "metadata": {"promptName": prompt.name}},
352
+ ).execute(my_agent_fn)
353
+ ```
354
+
355
+ See [Prompt registry](EVALUATIONS.md#prompt-registry) in the full guide, or [self-host's docs](https://docs.agentx.so/self-host#prompt-registry) for the "Suggest improvement" dashboard flow (self-host only — no hosted-SaaS equivalent yet).
356
+
357
+ See **[EVALUATIONS.md](EVALUATIONS.md)** for the full guide — dataset builder, framework adapters, similarity metrics, smoke testing, judge configuration, prompt registry, and the complete API reference.
313
358
 
314
359
  ---
315
360
 
@@ -1,51 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: agentx-python
3
- Version: 0.6.10
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>=2.0.0
18
- Provides-Extra: langchain
19
- Requires-Dist: langchain-core>=0.1.0; extra == "langchain"
20
- Provides-Extra: crewai
21
- Requires-Dist: crewai>=0.80.0; extra == "crewai"
22
- Provides-Extra: openai-agents
23
- Requires-Dist: openai-agents>=0.0.3; extra == "openai-agents"
24
- Provides-Extra: anthropic
25
- Requires-Dist: anthropic>=0.25.0; extra == "anthropic"
26
- Provides-Extra: google-adk
27
- Requires-Dist: google-adk>=1.0.0; extra == "google-adk"
28
- Provides-Extra: google-genai
29
- Requires-Dist: google-genai>=1.0.0; extra == "google-genai"
30
- Provides-Extra: all
31
- Requires-Dist: langchain-core>=0.1.0; extra == "all"
32
- Requires-Dist: crewai>=0.80.0; extra == "all"
33
- Requires-Dist: openai-agents>=0.0.3; extra == "all"
34
- Requires-Dist: anthropic>=0.25.0; extra == "all"
35
- Requires-Dist: google-adk>=1.0.0; extra == "all"
36
- Requires-Dist: google-genai>=1.0.0; extra == "all"
37
- Dynamic: author
38
- Dynamic: author-email
39
- Dynamic: classifier
40
- Dynamic: description
41
- Dynamic: description-content-type
42
- Dynamic: home-page
43
- Dynamic: license-file
44
- Dynamic: provides-extra
45
- Dynamic: requires-dist
46
- Dynamic: requires-python
47
- Dynamic: summary
48
-
49
1
  ![Logo](https://agentx-resources.s3.us-west-1.amazonaws.com/AgentX-logo-387x60.png)
50
2
 
51
3
  [![PyPI version](https://img.shields.io/pypi/v/agentx-python)](https://pypi.org/project/agentx-python/)
@@ -235,9 +187,13 @@ Each integration auto-captures LLM calls, tool calls, and token usage — instal
235
187
  | LangChain | `pip install "agentx-python[langchain]"` | `AgentXCallbackHandler` |
236
188
  | CrewAI | `pip install "agentx-python[crewai]"` | `AgentXCrewObserver` |
237
189
  | OpenAI Agents SDK | `pip install "agentx-python[openai-agents]"` | `AgentXTracingProcessor` |
190
+ | OpenAI (raw client) | `pip install "agentx-python[openai]"` | `patch_openai_client` |
238
191
  | Anthropic | `pip install "agentx-python[anthropic]"` | `patch_anthropic_client` |
239
192
  | Google ADK | `pip install "agentx-python[google-adk]"` | `AgentXADKPlugin` |
240
193
  | Google GenAI (Gemini) | `pip install "agentx-python[google-genai]"` | `patch_genai_client` |
194
+ | LiteLLM | `pip install "agentx-python[litellm]"` | `AgentXLiteLLMLogger` |
195
+ | LlamaIndex | `pip install "agentx-python[llamaindex]"` | `AgentXLlamaIndexHandler`|
196
+ | AutoGen | `pip install "agentx-python[autogen]"` | `AgentXAutoGenObserver` |
241
197
 
242
198
  Or plain Python — wrap any function with `@tracer.trace(...)` and it just works, no framework required.
243
199
 
@@ -280,6 +236,19 @@ Per-agent coverage/threshold settings (sample rate, retention, and threshold ove
280
236
  client.monitor.profile.update("agent_123", threshold_overrides={"latencyMs": 15000})
281
237
  ```
282
238
 
239
+ Self-host also has **online evaluators**: a real LLM judge scoring a sample of live traffic continuously, distinct from a pattern's rule-matching. A score below `alert_threshold` raises a signal the same way a failing pattern does, deduped and triage-ready in `client.monitor.signals`.
240
+
241
+ ```python
242
+ evaluator = client.monitor.online_evaluators.builder(
243
+ name="Helpfulness",
244
+ evaluation_settings_id=settings.id,
245
+ sample_rate=0.1,
246
+ alert_threshold=5,
247
+ ).publish()
248
+
249
+ client.monitor.online_evaluators.ratings(evaluator.id, window="7d")
250
+ ```
251
+
283
252
  See **[TRACING.md](TRACING.md)** for the complete Monitor guide.
284
253
 
285
254
  ---
@@ -309,7 +278,21 @@ print(report.recommendations) # list of prioritized, actionable fixes
309
278
 
310
279
  Ask a case's question several extra ways each run, LLM-paraphrased server-side, to catch agents that break on phrasing rather than substance, and override the judge's prompt/model per config, see [Smoke testing](EVALUATIONS.md#smoke-testing-phrasing-robustness) and [Configuring the judge](EVALUATIONS.md#configuring-the-judge) in the full guide.
311
280
 
312
- See **[EVALUATIONS.md](EVALUATIONS.md)** for the full guidedataset builder, framework adapters, similarity metrics, smoke testing, judge configuration, and the complete API reference.
281
+ Since AgentX doesn't own your agent's code, `client.evaluations.prompts` lets AgentX become your prompt's *source of truth* instead the same problem LangSmith's Prompt Hub and Langfuse's Prompt Management solve. Pull a version at runtime, tag your eval runs (or live traces) with it, and let a judge propose a rewrite from your real worst-rated results — a human always has to approve before it publishes:
282
+
283
+ ```python
284
+ prompt = client.evaluations.prompts.get("support-agent-system-prompt") # or prompt.id
285
+ # use prompt.text as your own agent's system prompt
286
+
287
+ client.evaluations.run(
288
+ dataset_id="evds_…",
289
+ subject={"kind": "custom_agent", "metadata": {"promptName": prompt.name}},
290
+ ).execute(my_agent_fn)
291
+ ```
292
+
293
+ See [Prompt registry](EVALUATIONS.md#prompt-registry) in the full guide, or [self-host's docs](https://docs.agentx.so/self-host#prompt-registry) for the "Suggest improvement" dashboard flow (self-host only — no hosted-SaaS equivalent yet).
294
+
295
+ See **[EVALUATIONS.md](EVALUATIONS.md)** for the full guide — dataset builder, framework adapters, similarity metrics, smoke testing, judge configuration, prompt registry, and the complete API reference.
313
296
 
314
297
  ---
315
298
 
@@ -17,6 +17,7 @@ from agentx.evaluations.models import (
17
17
  EvaluationSettings,
18
18
  EvaluationSubject,
19
19
  ModelInfo,
20
+ Prompt,
20
21
  Report,
21
22
  )
22
23
 
@@ -77,12 +78,14 @@ class EvaluationsClient:
77
78
  "accept": "*/*",
78
79
  }
79
80
  )
80
- # Expose dataset / evaluation-settings builder factories
81
+ # Expose dataset / evaluation-settings / prompt builder factories
81
82
  from agentx.evaluations.datasets import DatasetClient
82
83
  from agentx.evaluations.evaluation_settings import EvaluationSettingsClient
84
+ from agentx.evaluations.prompts import PromptClient
83
85
 
84
86
  self.datasets = DatasetClient(self)
85
87
  self.settings = EvaluationSettingsClient(self)
88
+ self.prompts = PromptClient(self)
86
89
 
87
90
  # ------------------------------------------------------------------
88
91
  # Low-level HTTP
@@ -195,6 +198,29 @@ class EvaluationsClient:
195
198
  )
196
199
  return EvaluationSettings(**data)
197
200
 
201
+ # ------------------------------------------------------------------
202
+ # Prompt registry endpoints — see agentx.evaluations.prompts.PromptClient for the concept
203
+ # (the external-agent analog to native autotune). Deliberately read-mostly: no publish here,
204
+ # a new version only ever comes from the dashboard's human-approved propose/publish flow.
205
+ # ------------------------------------------------------------------
206
+
207
+ def create_prompt(self, payload: dict) -> Prompt:
208
+ data = self._request("POST", "/prompts", json=self._with_workspace(payload))
209
+ return Prompt(**data)
210
+
211
+ def list_prompts(self) -> List[Prompt]:
212
+ data = self._request("GET", "/prompts", params=self._workspace_params())
213
+ return [Prompt(**p) for p in (data if isinstance(data, list) else data.get("prompts", []))]
214
+
215
+ def get_prompt(self, name: str, version: Optional[int] = None) -> Prompt:
216
+ # `name` doubles as an id: the backend route tries a name match first, then falls back to
217
+ # an id match (see engine's getPromptRowByNameOrId), so callers can pass either.
218
+ params = self._workspace_params() or {}
219
+ if version is not None:
220
+ params = {**params, "version": version}
221
+ data = self._request("GET", f"/prompts/{name}", params=params or None)
222
+ return Prompt(**data)
223
+
198
224
  # ------------------------------------------------------------------
199
225
  # Run endpoints
200
226
  # ------------------------------------------------------------------
@@ -128,6 +128,30 @@ class EvaluationSettings(BaseModel):
128
128
  extra = "ignore"
129
129
 
130
130
 
131
+ # ---------------------------------------------------------------------------
132
+ # Prompt registry — the external-agent analog to AgentX's native autotune. AgentX doesn't own
133
+ # your agent's code, so instead of branching/applying a config it becomes the prompt's source of
134
+ # truth (same shape as LangSmith's Prompt Hub / Langfuse's Prompt Management): pull a version at
135
+ # runtime with ``client.evaluations.prompts.get(name_or_id)``, use ``prompt.text`` as your agent's
136
+ # system prompt, and tag your eval runs so the existing version-comparison view can tell you
137
+ # which published version scored higher — see ``client.evaluations.prompts`` docs.
138
+ # ---------------------------------------------------------------------------
139
+
140
+
141
+ class Prompt(BaseModel):
142
+ id: str = Field(alias="_id")
143
+ name: str
144
+ description: Optional[str] = None
145
+ version: int
146
+ text: str
147
+ created_at: Optional[str] = Field(default=None, alias="createdAt")
148
+ updated_at: Optional[str] = Field(default=None, alias="updatedAt")
149
+
150
+ class Config:
151
+ populate_by_name = True
152
+ extra = "ignore"
153
+
154
+
131
155
  # ---------------------------------------------------------------------------
132
156
  # Evaluation subject
133
157
  # ---------------------------------------------------------------------------
@@ -0,0 +1,55 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, List, Optional
4
+
5
+ from agentx.evaluations.models import Prompt
6
+
7
+ if TYPE_CHECKING:
8
+ from agentx.evaluations.client import EvaluationsClient
9
+
10
+
11
+ class PromptClient:
12
+ """Thin wrapper surfaced as ``client.evaluations.prompts`` — the external-agent analog to
13
+ AgentX's native autotune. AgentX doesn't own your agent's code, so instead of branching and
14
+ applying a config, it becomes the prompt's source of truth (the same shape as LangSmith's
15
+ Prompt Hub / Langfuse's Prompt Management): create/pull versions here, use ``prompt.text`` as
16
+ your agent's own system prompt, and tag your eval runs with the pulled version so the
17
+ existing version-comparison view (``client.evaluations`` run comparisons on a dataset) can
18
+ tell you which published version actually scored higher.
19
+
20
+ Deliberately read-mostly from here: there is no ``publish`` on this client. A prompt only
21
+ gets a new version through the dashboard's human-approved propose/publish flow, so a
22
+ rewritten prompt never reaches your running agent without someone explicitly approving it.
23
+
24
+ Example::
25
+
26
+ prompt = client.evaluations.prompts.get("support-agent-system-prompt")
27
+ # ... call your own LLM using prompt.text as the system prompt ...
28
+ client.evaluations.init_run(
29
+ dataset_id=dataset_id,
30
+ subject=EvaluationSubject(
31
+ metadata={
32
+ "promptName": prompt.name,
33
+ "version": f"{prompt.name}@v{prompt.version}",
34
+ }
35
+ ),
36
+ )
37
+
38
+ ``get()`` also accepts a prompt's ``id`` in place of its name, e.g.
39
+ ``client.evaluations.prompts.get(prompt.id)``.
40
+ """
41
+
42
+ def __init__(self, client: "EvaluationsClient"):
43
+ self._client = client
44
+
45
+ def create(self, name: str, text: str, description: Optional[str] = None) -> Prompt:
46
+ return self._client.create_prompt({"name": name, "text": text, "description": description})
47
+
48
+ def get(self, name: str, version: Optional[int] = None) -> Prompt:
49
+ """Accepts either the prompt's name or its ``id`` (e.g. ``prompt.id`` from an earlier
50
+ ``get``/``create`` call), useful for round-tripping an id you already have without a
51
+ second lookup method."""
52
+ return self._client.get_prompt(name, version=version)
53
+
54
+ def list(self) -> List[Prompt]:
55
+ return self._client.list_prompts()
@@ -335,6 +335,7 @@ class EvaluationsRunner:
335
335
  self._client = client
336
336
  self.datasets = client.datasets
337
337
  self.settings = client.settings
338
+ self.prompts = client.prompts
338
339
 
339
340
  def list_models(self, provider: Optional[str] = None) -> List[ModelInfo]:
340
341
  """List the LLM models AgentX supports — the same set selectable for
@@ -0,0 +1,141 @@
1
+ """
2
+ Shared helper for tracing a raw SDK client call that may be sync or async.
3
+
4
+ Anthropic's and Google GenAI's raw clients expose the same method names for
5
+ both their sync and async client variants (``client.messages.create``,
6
+ ``client.models.generate_content``) — the only way to tell them apart is to
7
+ call the method and check whether the result is awaitable.
8
+ ``inspect.iscoroutinefunction`` is unreliable for this: it returns ``False``
9
+ even for ``AsyncAnthropic().messages.create``, since these SDKs don't
10
+ implement the async variant as a plain top-level ``async def``.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import asyncio
15
+ import inspect
16
+ from typing import Any, Callable, Dict, Optional
17
+
18
+ from agentx.tracing.tracer import Tracer
19
+ from agentx.integrations._perf import build_performance_summary
20
+
21
+
22
+ def call_and_trace(
23
+ original: Callable[..., Any],
24
+ args: tuple,
25
+ kwargs: dict,
26
+ on_finish: Callable[[Optional[Any], Optional[str]], None],
27
+ ) -> Any:
28
+ """
29
+ Call ``original(*args, **kwargs)``.
30
+
31
+ If the result is awaitable (async client), return a coroutine that awaits
32
+ it and calls ``on_finish(response, error)`` only after the real await
33
+ completes, so timing/output/tokens reflect the actual call rather than
34
+ the moment the coroutine object was constructed. If the result is a
35
+ normal value (sync client), call ``on_finish`` immediately.
36
+
37
+ Either way, the original call's own return value / exception behavior is
38
+ unchanged for the caller — this only affects when/how the trace is built.
39
+ """
40
+ try:
41
+ result = original(*args, **kwargs)
42
+ except Exception as exc:
43
+ on_finish(None, str(exc))
44
+ raise
45
+
46
+ if asyncio.iscoroutine(result) or inspect.isawaitable(result):
47
+ return _await_and_finish(result, on_finish)
48
+
49
+ on_finish(result, None)
50
+ return result
51
+
52
+
53
+ async def _await_and_finish(
54
+ awaitable: Any,
55
+ on_finish: Callable[[Optional[Any], Optional[str]], None],
56
+ ) -> Any:
57
+ try:
58
+ response = await awaitable
59
+ except Exception as exc:
60
+ on_finish(None, str(exc))
61
+ raise
62
+ on_finish(response, None)
63
+ return response
64
+
65
+
66
+ def finish_llm_call(
67
+ tracer: Tracer,
68
+ *,
69
+ name: str,
70
+ framework: str,
71
+ metadata: Optional[Dict[str, Any]],
72
+ session_id: Optional[str],
73
+ start_t: float,
74
+ end_t: float,
75
+ input_repr: Any,
76
+ output: Optional[str],
77
+ model: Optional[str],
78
+ input_tokens: Optional[int],
79
+ output_tokens: Optional[int],
80
+ error: Optional[str],
81
+ ) -> None:
82
+ """
83
+ Close out one raw-client LLM call — shared by the ``on_finish``/exit
84
+ callbacks of every integration that patches a raw provider client
85
+ (``anthropic.py``, ``google_genai.py``, ``openai.py``) rather than a
86
+ framework-level callback/plugin system.
87
+
88
+ If the call happened inside a ``with tracer.trace(...)`` block, attach it
89
+ as one LLM-call step on that span instead of sending an independent
90
+ trace — the same "part of a multi-call agentic loop" behavior
91
+ ``anthropic.py`` already had; folded in here so every raw-client
92
+ integration gets it instead of each having to remember to check
93
+ ``tracer.current_span`` itself.
94
+ """
95
+ latency_ms = int((end_t - start_t) * 1000)
96
+
97
+ active_span = tracer.current_span
98
+ if active_span is not None:
99
+ if error is not None:
100
+ active_span.set_error(error)
101
+ active_span._record_llm_call(
102
+ duration_ms=latency_ms,
103
+ start_time=start_t,
104
+ end_time=end_t,
105
+ input=input_repr,
106
+ output=output,
107
+ model=model,
108
+ input_tokens=input_tokens,
109
+ output_tokens=output_tokens,
110
+ )
111
+ return
112
+
113
+ perf = build_performance_summary(
114
+ total_duration_ms=latency_ms,
115
+ execution_steps=[{
116
+ "name": "LLM Call 1",
117
+ "duration_ms": latency_ms,
118
+ "start_time": start_t,
119
+ "end_time": end_t,
120
+ "model": model,
121
+ "input": input_repr,
122
+ "output": output,
123
+ "inputTokenSize": input_tokens,
124
+ "outputTokenSize": output_tokens,
125
+ }],
126
+ has_errors=error is not None,
127
+ )
128
+ tracer._send(
129
+ name=name,
130
+ input=input_repr,
131
+ output=output,
132
+ latency_ms=latency_ms,
133
+ error=error,
134
+ framework=framework,
135
+ model=model,
136
+ metadata=metadata,
137
+ session_id=session_id,
138
+ performance_summary=perf,
139
+ input_tokens=input_tokens,
140
+ output_tokens=output_tokens,
141
+ )