agentx-python 0.6.2__tar.gz → 0.6.4__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 (47) hide show
  1. {agentx_python-0.6.2/agentx_python.egg-info → agentx_python-0.6.4}/PKG-INFO +63 -4
  2. {agentx_python-0.6.2 → agentx_python-0.6.4}/README.md +62 -3
  3. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/integrations/anthropic.py +30 -5
  4. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/integrations/google_adk.py +17 -2
  5. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/integrations/google_genai.py +14 -2
  6. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/integrations/langchain.py +33 -1
  7. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/tracing/tracer.py +19 -0
  8. agentx_python-0.6.4/agentx/version.py +1 -0
  9. {agentx_python-0.6.2 → agentx_python-0.6.4/agentx_python.egg-info}/PKG-INFO +63 -4
  10. agentx_python-0.6.2/agentx/version.py +0 -1
  11. {agentx_python-0.6.2 → agentx_python-0.6.4}/LICENSE +0 -0
  12. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/__init__.py +0 -0
  13. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/agentx.py +0 -0
  14. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/evaluations/__init__.py +0 -0
  15. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/evaluations/_term.py +0 -0
  16. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/evaluations/adapters/__init__.py +0 -0
  17. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/evaluations/adapters/http_endpoint.py +0 -0
  18. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/evaluations/adapters/precomputed.py +0 -0
  19. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/evaluations/adapters/raw.py +0 -0
  20. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/evaluations/client.py +0 -0
  21. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/evaluations/datasets.py +0 -0
  22. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/evaluations/models.py +0 -0
  23. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/evaluations/redaction.py +0 -0
  24. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/evaluations/reporting.py +0 -0
  25. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/evaluations/results.py +0 -0
  26. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/evaluations/runner.py +0 -0
  27. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/evaluations/tracing.py +0 -0
  28. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/exceptions.py +0 -0
  29. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/integrations/__init__.py +0 -0
  30. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/integrations/_perf.py +0 -0
  31. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/integrations/crewai.py +0 -0
  32. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/integrations/openai_agents.py +0 -0
  33. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/resources/__init__.py +0 -0
  34. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/resources/agent.py +0 -0
  35. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/resources/conversation.py +0 -0
  36. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/resources/workforce.py +0 -0
  37. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/tracing/__init__.py +0 -0
  38. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/tracing/ci_types.py +0 -0
  39. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/tracing/ingest_client.py +0 -0
  40. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx/util.py +0 -0
  41. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx_python.egg-info/SOURCES.txt +0 -0
  42. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx_python.egg-info/dependency_links.txt +0 -0
  43. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx_python.egg-info/requires.txt +0 -0
  44. {agentx_python-0.6.2 → agentx_python-0.6.4}/agentx_python.egg-info/top_level.txt +0 -0
  45. {agentx_python-0.6.2 → agentx_python-0.6.4}/setup.cfg +0 -0
  46. {agentx_python-0.6.2 → agentx_python-0.6.4}/setup.py +0 -0
  47. {agentx_python-0.6.2 → agentx_python-0.6.4}/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.2
3
+ Version: 0.6.4
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)
@@ -22,6 +22,34 @@ from agentx.tracing.tracer import Tracer, _safe_serialize
22
22
  from agentx.integrations._perf import build_performance_summary
23
23
 
24
24
 
25
+ def _extract_output_text(response: Any) -> Optional[str]:
26
+ """
27
+ Extract the assistant's text reply from a Messages API response, falling
28
+ back to a description of any tool_use blocks when the response is a pure
29
+ tool call with no accompanying text.
30
+ """
31
+ content = getattr(response, "content", None) if response is not None else None
32
+ if not content:
33
+ return None
34
+ texts = []
35
+ tool_calls = []
36
+ for block in content:
37
+ block_type = getattr(block, "type", None)
38
+ if block_type == "text":
39
+ text = getattr(block, "text", None)
40
+ if text:
41
+ texts.append(text)
42
+ elif block_type == "tool_use":
43
+ name = getattr(block, "name", "unknown")
44
+ tool_input = getattr(block, "input", None)
45
+ tool_calls.append(f"{name}({tool_input})")
46
+ if texts:
47
+ return "\n".join(texts)
48
+ if tool_calls:
49
+ return "[tool call] " + ", ".join(tool_calls)
50
+ return None
51
+
52
+
25
53
  def patch_anthropic_client(
26
54
  client: Any,
27
55
  tracer: Tracer,
@@ -77,10 +105,7 @@ def _patch_create(
77
105
  input_tokens = None
78
106
  output_tokens = None
79
107
  if response is not None:
80
- try:
81
- output = response.content[0].text if response.content else None
82
- except Exception:
83
- output = str(response)
108
+ output = _extract_output_text(response)
84
109
  try:
85
110
  usage = getattr(response, "usage", None)
86
111
  if usage is not None:
@@ -172,7 +197,7 @@ def _patch_stream(
172
197
  output_tokens = None
173
198
  try:
174
199
  final = ctx.get_final_message()
175
- output = final.content[0].text if final.content else None
200
+ output = _extract_output_text(final)
176
201
  usage = getattr(final, "usage", None)
177
202
  if usage is not None:
178
203
  input_tokens = getattr(usage, "input_tokens", None)
@@ -36,7 +36,11 @@ except ImportError as exc: # pragma: no cover
36
36
 
37
37
 
38
38
  def _content_to_text(content: Any) -> Optional[str]:
39
- """Extract plain text from a google.genai types.Content object."""
39
+ """
40
+ Extract plain text from a google.genai types.Content object, falling back
41
+ to a description of any function_call parts when there's no text (Gemini
42
+ function calling — the model responded with a pure tool call).
43
+ """
40
44
  if content is None:
41
45
  return None
42
46
  if isinstance(content, str):
@@ -45,11 +49,22 @@ def _content_to_text(content: Any) -> Optional[str]:
45
49
  if not parts:
46
50
  return None
47
51
  texts = []
52
+ function_calls = []
48
53
  for part in parts:
49
54
  text = getattr(part, "text", None)
50
55
  if text and isinstance(text, str):
51
56
  texts.append(text)
52
- return " ".join(texts) if texts else None
57
+ continue
58
+ fc = getattr(part, "function_call", None)
59
+ if fc is not None:
60
+ name = getattr(fc, "name", "unknown")
61
+ args = getattr(fc, "args", None)
62
+ function_calls.append(f"{name}({args})")
63
+ if texts:
64
+ return " ".join(texts)
65
+ if function_calls:
66
+ return "[tool call] " + ", ".join(function_calls)
67
+ return None
53
68
 
54
69
 
55
70
  def _contents_to_text(contents: Any) -> Optional[str]:
@@ -16,7 +16,7 @@ Requires: ``pip install "agentx-python[google-genai]"``
16
16
  from __future__ import annotations
17
17
 
18
18
  import time
19
- from typing import Any, Dict, Optional
19
+ from typing import Any, Dict, List, Optional
20
20
 
21
21
  from agentx.tracing.tracer import Tracer, _safe_serialize
22
22
  from agentx.integrations._perf import build_performance_summary
@@ -49,13 +49,18 @@ def patch_genai_client(
49
49
 
50
50
 
51
51
  def _extract_response_text(response: Any) -> Optional[str]:
52
- """Pull the generated text out of a GenerateContentResponse."""
52
+ """
53
+ Pull the generated text out of a GenerateContentResponse, falling back to
54
+ a description of any function_call parts when the response is a pure
55
+ tool call with no text (Gemini function calling).
56
+ """
53
57
  # Convenience .text property (available on non-streaming responses)
54
58
  text = getattr(response, "text", None)
55
59
  if text and isinstance(text, str):
56
60
  return text
57
61
  # Fallback: walk candidates → content → parts
58
62
  candidates = getattr(response, "candidates", None) or []
63
+ function_calls: List[str] = []
59
64
  for candidate in candidates:
60
65
  content = getattr(candidate, "content", None)
61
66
  parts = getattr(content, "parts", None) or []
@@ -63,6 +68,13 @@ def _extract_response_text(response: Any) -> Optional[str]:
63
68
  t = getattr(part, "text", None)
64
69
  if t and isinstance(t, str):
65
70
  return t
71
+ fc = getattr(part, "function_call", None)
72
+ if fc is not None:
73
+ name = getattr(fc, "name", "unknown")
74
+ args = getattr(fc, "args", None)
75
+ function_calls.append(f"{name}({args})")
76
+ if function_calls:
77
+ return "[tool call] " + ", ".join(function_calls)
66
78
  return None
67
79
 
68
80
 
@@ -177,9 +177,30 @@ def _extract_llm_input(
177
177
  return None
178
178
 
179
179
 
180
+ def _describe_tool_calls(message: Any) -> Optional[str]:
181
+ """
182
+ Format an AIMessage's ``tool_calls`` as a readable fallback for ``output``
183
+ when the model responded with a pure tool call and no text content.
184
+ """
185
+ tool_calls = getattr(message, "tool_calls", None) if message is not None else None
186
+ if not tool_calls:
187
+ return None
188
+ parts = []
189
+ for tc in tool_calls:
190
+ if isinstance(tc, dict):
191
+ name = tc.get("name") or "unknown"
192
+ args = tc.get("args")
193
+ else:
194
+ name = getattr(tc, "name", None) or "unknown"
195
+ args = getattr(tc, "args", None)
196
+ parts.append(f"{name}({args})" if args is not None else f"{name}()")
197
+ return "[tool call] " + ", ".join(parts)
198
+
199
+
180
200
  def _extract_llm_output(response: "LLMResult") -> Optional[str]:
181
201
  """Flatten on_llm_end's ``LLMResult`` (chat or completion generations) into one string."""
182
202
  texts: List[str] = []
203
+ tool_call_fallbacks: List[str] = []
183
204
  for gen_list in getattr(response, "generations", None) or []:
184
205
  for gen in gen_list or []:
185
206
  message = getattr(gen, "message", None)
@@ -196,7 +217,18 @@ def _extract_llm_output(response: "LLMResult") -> Optional[str]:
196
217
  text = getattr(gen, "text", None)
197
218
  if text:
198
219
  texts.append(text)
199
- return "\n".join(texts) if texts else None
220
+ continue
221
+ # No text content — the model likely responded with a pure tool
222
+ # call instead of commentary. Fall back to describing it so the
223
+ # step's output isn't silently omitted.
224
+ described = _describe_tool_calls(message)
225
+ if described:
226
+ tool_call_fallbacks.append(described)
227
+ if texts:
228
+ return "\n".join(texts)
229
+ if tool_call_fallbacks:
230
+ return "\n".join(tool_call_fallbacks)
231
+ return None
200
232
 
201
233
 
202
234
  class AgentXCallbackHandler(BaseCallbackHandler):
@@ -4,6 +4,7 @@ import asyncio
4
4
  import concurrent.futures
5
5
  import functools
6
6
  import inspect
7
+ import re
7
8
  import threading
8
9
  import time
9
10
  from contextlib import contextmanager
@@ -20,6 +21,11 @@ from agentx.integrations._perf import (
20
21
 
21
22
  F = TypeVar("F", bound=Callable[..., Any])
22
23
 
24
+ # Matches the generic "LLM Call N" step names integrations generate (e.g.
25
+ # langchain.py numbers steps within one top-level chain run). Used by
26
+ # _TraceSpan._merge_child_run to renumber them on merge — see there.
27
+ _LLM_CALL_NAME_RE = re.compile(r"^LLM Call \d+$")
28
+
23
29
 
24
30
  def _safe_serialize(value: Any, depth: int = 0) -> Any:
25
31
  """Best-effort conversion to a JSON-safe structure, truncated to avoid huge payloads."""
@@ -208,6 +214,19 @@ class _TraceSpan:
208
214
  becoming its own independent trace.
209
215
  """
210
216
  with self._merge_lock:
217
+ if execution_steps:
218
+ # Step names like "LLM Call N" are numbered locally within
219
+ # whatever sub-run produced them (e.g. one LangChain chain
220
+ # invocation numbers its own calls 1, 2, 3...). When several
221
+ # sub-runs merge into this span — e.g. a sequence of
222
+ # single-call specialist chains all folded into one
223
+ # orchestrator span via use_span() — renumber so the merged
224
+ # trace doesn't end up with several "LLM Call 1" entries.
225
+ next_n = len(self._execution_steps) + 1
226
+ for offset, step in enumerate(execution_steps):
227
+ name = step.get("name")
228
+ if isinstance(name, str) and _LLM_CALL_NAME_RE.match(name):
229
+ step["name"] = f"LLM Call {next_n + offset}"
211
230
  self._execution_steps.extend(execution_steps or [])
212
231
  self.tool_calls.extend(tool_calls or [])
213
232
  self._retrieval_steps.extend(retrieval_steps or [])
@@ -0,0 +1 @@
1
+ VERSION = "0.6.4"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentx-python
3
- Version: 0.6.2
3
+ Version: 0.6.4
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 +0,0 @@
1
- VERSION = "0.6.2"
File without changes
File without changes
File without changes