agentx-python 0.6.23__tar.gz → 0.6.25__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agentx_python-0.6.23 → agentx_python-0.6.25}/PKG-INFO +1 -1
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/evaluations/datasets.py +11 -1
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/evaluations/models.py +5 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/evaluations/results.py +3 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/integrations/_traced_call.py +37 -1
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/integrations/anthropic.py +5 -1
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/integrations/google_genai.py +32 -1
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/integrations/langchain.py +32 -2
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/integrations/litellm.py +6 -1
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/integrations/openai.py +3 -1
- agentx_python-0.6.25/agentx/version.py +1 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx_python.egg-info/PKG-INFO +1 -1
- agentx_python-0.6.23/agentx/version.py +0 -1
- {agentx_python-0.6.23 → agentx_python-0.6.25}/LICENSE +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/README.md +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/__init__.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/agentx.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/cli.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/evaluations/__init__.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/evaluations/_term.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/evaluations/adapters/__init__.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/evaluations/adapters/http_endpoint.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/evaluations/adapters/precomputed.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/evaluations/adapters/raw.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/evaluations/client.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/evaluations/evaluation_settings.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/evaluations/prompts.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/evaluations/redaction.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/evaluations/reporting.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/evaluations/runner.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/evaluations/tracing.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/exceptions.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/feedback.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/integrations/__init__.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/integrations/autogen.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/integrations/crewai.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/integrations/databricks.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/integrations/google_adk.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/integrations/llamaindex.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/integrations/moveworks.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/integrations/openai_agents.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/monitor/__init__.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/monitor/client.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/monitor/models.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/monitor/online_evaluators.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/monitor/patterns.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/monitor/profile.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/monitor/signals.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/outcomes.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/py.typed +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/resources/__init__.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/resources/agent.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/resources/conversation.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/resources/workforce.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/tracing/__init__.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/tracing/ci_types.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/tracing/ingest_client.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/tracing/tracer.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx/util.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx_python.egg-info/SOURCES.txt +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx_python.egg-info/dependency_links.txt +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx_python.egg-info/entry_points.txt +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx_python.egg-info/not-zip-safe +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx_python.egg-info/requires.txt +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/agentx_python.egg-info/top_level.txt +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/setup.cfg +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/setup.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/tests/test_integration.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/tests/test_integrations.py +0 -0
- {agentx_python-0.6.23 → agentx_python-0.6.25}/tests/test_span_tree.py +0 -0
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import csv
|
|
4
4
|
import logging
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import Any, Dict, List, Optional, TYPE_CHECKING
|
|
6
|
+
from typing import Any, Dict, List, Optional, TYPE_CHECKING, Union
|
|
7
7
|
|
|
8
8
|
from agentx.evaluations.models import Dataset
|
|
9
9
|
|
|
@@ -88,6 +88,7 @@ class DatasetBuilder:
|
|
|
88
88
|
smoke_test_guidance: Optional[str] = None,
|
|
89
89
|
expected_tools: Optional[List[str]] = None,
|
|
90
90
|
trajectory_match_mode: str = "strict",
|
|
91
|
+
expected_retrieval_context: Optional[Union[str, List[str]]] = None,
|
|
91
92
|
) -> "DatasetBuilder":
|
|
92
93
|
"""Add a case. `judge_guideline` is optional grading guidance specific to this question.
|
|
93
94
|
|
|
@@ -99,6 +100,13 @@ class DatasetBuilder:
|
|
|
99
100
|
any order), "superset" (all expected present, extras allowed), or "subset" (no unexpected
|
|
100
101
|
calls, missing allowed).
|
|
101
102
|
|
|
103
|
+
`expected_retrieval_context` (string or list of chunk strings) declares what a correct
|
|
104
|
+
retriever should have fetched for this case. When the run's result carries actual
|
|
105
|
+
retrieved context (a `retrieval_context` return value, or a linked trace with retrieval
|
|
106
|
+
spans), the engine compares the two with token-level Jaccard similarity and reports a
|
|
107
|
+
deterministic "Context match (jaccard)" scorer row (0-1) - a cheap retriever regression
|
|
108
|
+
check with no LLM judge call.
|
|
109
|
+
|
|
102
110
|
`smoke_test_count`, when set (1-10), asks this question that many extra ways each
|
|
103
111
|
evaluation run, LLM-paraphrased server-side, to catch agents that are brittle to phrasing
|
|
104
112
|
rather than genuinely wrong. `smoke_test_guidance` optionally steers what kind of variants
|
|
@@ -123,6 +131,8 @@ class DatasetBuilder:
|
|
|
123
131
|
main["smokeTest"]["guidance"] = smoke_test_guidance
|
|
124
132
|
if expected_tools:
|
|
125
133
|
main["expectedTrajectory"] = {"tools": expected_tools, "mode": trajectory_match_mode}
|
|
134
|
+
if expected_retrieval_context:
|
|
135
|
+
main["expectedRetrievalContext"] = expected_retrieval_context
|
|
126
136
|
self._payload["questions"].append(
|
|
127
137
|
{
|
|
128
138
|
"main_question": main,
|
|
@@ -346,6 +346,11 @@ class EvaluationResult(BaseModel):
|
|
|
346
346
|
# the dashboard's "Message Trace Details -> Execution Timeline" render the full execution
|
|
347
347
|
# trace for this case, not just the lightweight observable_trace events above.
|
|
348
348
|
trace_id: Optional[str] = Field(default=None, alias="traceId")
|
|
349
|
+
# What the agent actually retrieved for THIS case (a string, or list of chunk strings) - the
|
|
350
|
+
# {context} the RAG metric pack's judges grade faithfulness/relevancy against. Overrides the
|
|
351
|
+
# question's static retrievalContext; when absent, the engine falls back to the linked
|
|
352
|
+
# trace's recorded retrieval spans (trace_id above), then the case's pinned context.
|
|
353
|
+
retrieval_context: Optional[Any] = Field(default=None, alias="retrievalContext")
|
|
349
354
|
# Smoke test: set by execute() from the originating EvaluationCase, not something you need to
|
|
350
355
|
# set yourself when returning a plain str/dict from your callable.
|
|
351
356
|
is_smoke_test_variant: Optional[bool] = Field(default=None, alias="isSmokeTestVariant")
|
|
@@ -37,6 +37,7 @@ def normalize_result(
|
|
|
37
37
|
output: Optional[dict] = None
|
|
38
38
|
trace = None
|
|
39
39
|
trace_id: Optional[str] = None
|
|
40
|
+
retrieval_context = None
|
|
40
41
|
metadata: Optional[dict] = None
|
|
41
42
|
error: Optional[ResultError] = None
|
|
42
43
|
input_tokens: Optional[int] = None
|
|
@@ -55,6 +56,7 @@ def normalize_result(
|
|
|
55
56
|
trace = build_trace(raw.get("trace") or raw.get("observable_trace"))
|
|
56
57
|
trace_id_raw = raw.get("trace_id") or raw.get("traceId")
|
|
57
58
|
trace_id = str(trace_id_raw) if trace_id_raw else None
|
|
59
|
+
retrieval_context = raw.get("retrieval_context") or raw.get("retrievalContext")
|
|
58
60
|
meta_raw = raw.get("metadata")
|
|
59
61
|
if isinstance(meta_raw, dict):
|
|
60
62
|
metadata = redact_dict(meta_raw)
|
|
@@ -108,6 +110,7 @@ def normalize_result(
|
|
|
108
110
|
),
|
|
109
111
|
metadata=metadata,
|
|
110
112
|
traceId=trace_id,
|
|
113
|
+
retrievalContext=retrieval_context,
|
|
111
114
|
isSmokeTestVariant=case.is_smoke_test_variant,
|
|
112
115
|
smokeTestVariantText=case.smoke_test_variant_text,
|
|
113
116
|
)
|
|
@@ -13,9 +13,10 @@ from __future__ import annotations
|
|
|
13
13
|
|
|
14
14
|
import asyncio
|
|
15
15
|
import inspect
|
|
16
|
+
import json
|
|
16
17
|
from typing import Any, Callable, Dict, Optional
|
|
17
18
|
|
|
18
|
-
from agentx.tracing.tracer import Tracer
|
|
19
|
+
from agentx.tracing.tracer import Tracer, _safe_serialize
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
def call_and_trace(
|
|
@@ -62,6 +63,32 @@ async def _await_and_finish(
|
|
|
62
63
|
return response
|
|
63
64
|
|
|
64
65
|
|
|
66
|
+
# The engine's unregistered-tool surfacing (Tools & MCPs -> Unregistered) reads a trace's
|
|
67
|
+
# metadata "tools" key to show the REAL definition instead of one inferred from observed
|
|
68
|
+
# arguments (see AgentX-trace-eval's toolSchemas.ts draftFromMetadata). Raw-client patches see
|
|
69
|
+
# the request's tools=[...] right in kwargs, so capture it - capped so a huge toolbox never
|
|
70
|
+
# blows up the trace's metadata budget.
|
|
71
|
+
_MAX_TOOL_DEFINITIONS = 20
|
|
72
|
+
_MAX_TOOL_DEFINITIONS_BYTES = 12_000
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def capture_tool_definitions(tools: Any) -> Optional[list]:
|
|
76
|
+
"""Return a metadata-ready copy of a request's ``tools=[...]`` list, or None."""
|
|
77
|
+
if not isinstance(tools, list) or not tools:
|
|
78
|
+
return None
|
|
79
|
+
# A plain JSON round-trip preserves nested schema objects exactly (default=str catches the
|
|
80
|
+
# odd SDK object inside); _safe_serialize would repr-stringify nested dicts, turning a
|
|
81
|
+
# parameters schema into an unusable string.
|
|
82
|
+
try:
|
|
83
|
+
text = json.dumps(tools[:_MAX_TOOL_DEFINITIONS], default=str)
|
|
84
|
+
if len(text) > _MAX_TOOL_DEFINITIONS_BYTES:
|
|
85
|
+
return None
|
|
86
|
+
serialized = json.loads(text)
|
|
87
|
+
except (TypeError, ValueError):
|
|
88
|
+
return None
|
|
89
|
+
return serialized if isinstance(serialized, list) else None
|
|
90
|
+
|
|
91
|
+
|
|
65
92
|
def finish_llm_call(
|
|
66
93
|
tracer: Tracer,
|
|
67
94
|
*,
|
|
@@ -79,6 +106,7 @@ def finish_llm_call(
|
|
|
79
106
|
error: Optional[str],
|
|
80
107
|
cache_read_tokens: Optional[int] = None,
|
|
81
108
|
cache_write_tokens: Optional[int] = None,
|
|
109
|
+
tool_definitions: Optional[list] = None,
|
|
82
110
|
) -> None:
|
|
83
111
|
"""
|
|
84
112
|
Close out one raw-client LLM call - shared by the ``on_finish``/exit
|
|
@@ -96,8 +124,16 @@ def finish_llm_call(
|
|
|
96
124
|
"""
|
|
97
125
|
latency_ms = int((end_t - start_t) * 1000)
|
|
98
126
|
|
|
127
|
+
if tool_definitions:
|
|
128
|
+
metadata = {**(metadata or {}), "tools": tool_definitions}
|
|
129
|
+
|
|
99
130
|
active_span = tracer.current_span
|
|
100
131
|
if active_span is not None:
|
|
132
|
+
# The definitions describe the whole call's toolbox - attach them to the enclosing
|
|
133
|
+
# span's metadata (first capture wins) so the ROOT trace carries them for the
|
|
134
|
+
# unregistered-tool listing, same as the standalone-trace path below.
|
|
135
|
+
if tool_definitions and not (active_span._metadata or {}).get("tools"):
|
|
136
|
+
active_span._metadata = {**(active_span._metadata or {}), "tools": tool_definitions}
|
|
101
137
|
if error is not None:
|
|
102
138
|
active_span.set_error(error)
|
|
103
139
|
active_span._record_llm_call(
|
|
@@ -22,7 +22,7 @@ import time
|
|
|
22
22
|
from typing import Any, Dict, Optional, Tuple
|
|
23
23
|
|
|
24
24
|
from agentx.tracing.tracer import Tracer, _safe_serialize
|
|
25
|
-
from agentx.integrations._traced_call import call_and_trace, finish_llm_call
|
|
25
|
+
from agentx.integrations._traced_call import capture_tool_definitions, call_and_trace, finish_llm_call
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
def _extract_output_text(response: Any) -> Optional[str]:
|
|
@@ -136,6 +136,7 @@ def _patch_create(
|
|
|
136
136
|
kwargs.get("system"),
|
|
137
137
|
)
|
|
138
138
|
model = kwargs.get("model")
|
|
139
|
+
tool_definitions = capture_tool_definitions(kwargs.get("tools"))
|
|
139
140
|
|
|
140
141
|
input_repr = _safe_serialize(input_messages)
|
|
141
142
|
|
|
@@ -171,6 +172,7 @@ def _patch_create(
|
|
|
171
172
|
cache_read_tokens=cache_read_tokens,
|
|
172
173
|
cache_write_tokens=cache_write_tokens,
|
|
173
174
|
error=error,
|
|
175
|
+
tool_definitions=tool_definitions,
|
|
174
176
|
)
|
|
175
177
|
|
|
176
178
|
return call_and_trace(original, args, kwargs, on_finish)
|
|
@@ -199,6 +201,7 @@ def _patch_stream(
|
|
|
199
201
|
ctx = original_stream(*args, **kwargs)
|
|
200
202
|
input_repr = _safe_serialize(_prepend_system(kwargs.get("messages"), kwargs.get("system")))
|
|
201
203
|
model = kwargs.get("model")
|
|
204
|
+
tool_definitions = capture_tool_definitions(kwargs.get("tools"))
|
|
202
205
|
|
|
203
206
|
def build_and_send(end_t: float, error: Optional[str], final_message: Optional[Any]) -> None:
|
|
204
207
|
output = None
|
|
@@ -230,6 +233,7 @@ def _patch_stream(
|
|
|
230
233
|
cache_read_tokens=cache_read_tokens,
|
|
231
234
|
cache_write_tokens=cache_write_tokens,
|
|
232
235
|
error=error,
|
|
236
|
+
tool_definitions=tool_definitions,
|
|
233
237
|
)
|
|
234
238
|
|
|
235
239
|
class _TracedStream:
|
|
@@ -20,7 +20,7 @@ import time
|
|
|
20
20
|
from typing import Any, Dict, List, Optional
|
|
21
21
|
|
|
22
22
|
from agentx.tracing.tracer import Tracer, _safe_serialize
|
|
23
|
-
from agentx.integrations._traced_call import call_and_trace, finish_llm_call
|
|
23
|
+
from agentx.integrations._traced_call import capture_tool_definitions, call_and_trace, finish_llm_call
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
def patch_genai_client(
|
|
@@ -90,6 +90,31 @@ def _extract_response_text(response: Any) -> Optional[str]:
|
|
|
90
90
|
return None
|
|
91
91
|
|
|
92
92
|
|
|
93
|
+
|
|
94
|
+
def _genai_tool_definitions(config: Any) -> Optional[list]:
|
|
95
|
+
"""
|
|
96
|
+
Flatten a GenAI request's config.tools (each a Tool holding function_declarations) into the
|
|
97
|
+
flat [{name, description, parameters}, ...] list the engine's unregistered-tool listing
|
|
98
|
+
reads from trace metadata. Plain dicts and SDK objects both handled.
|
|
99
|
+
"""
|
|
100
|
+
tools = getattr(config, "tools", None) if config is not None and not isinstance(config, dict) else (config or {}).get("tools") if isinstance(config, dict) else None
|
|
101
|
+
if not tools:
|
|
102
|
+
return None
|
|
103
|
+
flat = []
|
|
104
|
+
for tool in tools:
|
|
105
|
+
decls = getattr(tool, "function_declarations", None) if not isinstance(tool, dict) else tool.get("function_declarations")
|
|
106
|
+
for decl in decls or []:
|
|
107
|
+
if isinstance(decl, dict):
|
|
108
|
+
flat.append(decl)
|
|
109
|
+
else:
|
|
110
|
+
flat.append({
|
|
111
|
+
"name": getattr(decl, "name", None),
|
|
112
|
+
"description": getattr(decl, "description", None),
|
|
113
|
+
"parameters": getattr(decl, "parameters", None),
|
|
114
|
+
})
|
|
115
|
+
return capture_tool_definitions(flat)
|
|
116
|
+
|
|
117
|
+
|
|
93
118
|
def _patch_generate_content(
|
|
94
119
|
models: Any,
|
|
95
120
|
tracer: Tracer,
|
|
@@ -106,6 +131,7 @@ def _patch_generate_content(
|
|
|
106
131
|
model = kwargs.get("model") or (args[0] if args else None)
|
|
107
132
|
contents = kwargs.get("contents") or (args[1] if len(args) > 1 else None)
|
|
108
133
|
input_repr = contents if isinstance(contents, str) else _safe_serialize(contents)
|
|
134
|
+
tool_definitions = _genai_tool_definitions(kwargs.get("config"))
|
|
109
135
|
|
|
110
136
|
def on_finish(response: Optional[Any], error: Optional[str]) -> None:
|
|
111
137
|
end_t = time.time()
|
|
@@ -137,6 +163,7 @@ def _patch_generate_content(
|
|
|
137
163
|
output_tokens=output_tokens,
|
|
138
164
|
cache_read_tokens=cache_read_tokens,
|
|
139
165
|
error=error,
|
|
166
|
+
tool_definitions=tool_definitions,
|
|
140
167
|
)
|
|
141
168
|
|
|
142
169
|
return call_and_trace(original, args, kwargs, on_finish)
|
|
@@ -180,6 +207,7 @@ def _patch_sync_generate_content_stream(
|
|
|
180
207
|
def patched_stream(*args, **kwargs):
|
|
181
208
|
start_t = time.time()
|
|
182
209
|
model, input_repr = _stream_input_and_model(args, kwargs)
|
|
210
|
+
tool_definitions = _genai_tool_definitions(kwargs.get("config"))
|
|
183
211
|
accumulated_text: List[str] = []
|
|
184
212
|
last_usage_metadata = None
|
|
185
213
|
error: Optional[str] = None
|
|
@@ -222,6 +250,7 @@ def _patch_sync_generate_content_stream(
|
|
|
222
250
|
output_tokens=output_tokens,
|
|
223
251
|
cache_read_tokens=cache_read_tokens,
|
|
224
252
|
error=error,
|
|
253
|
+
tool_definitions=tool_definitions,
|
|
225
254
|
)
|
|
226
255
|
|
|
227
256
|
patched_stream._agentx_patched = True
|
|
@@ -247,6 +276,7 @@ def _patch_async_generate_content_stream(
|
|
|
247
276
|
# drives it.
|
|
248
277
|
start_t = time.time()
|
|
249
278
|
model, input_repr = _stream_input_and_model(args, kwargs)
|
|
279
|
+
tool_definitions = _genai_tool_definitions(kwargs.get("config"))
|
|
250
280
|
inner = await original_stream(*args, **kwargs)
|
|
251
281
|
|
|
252
282
|
async def traced_agen():
|
|
@@ -290,6 +320,7 @@ def _patch_async_generate_content_stream(
|
|
|
290
320
|
output_tokens=output_tokens,
|
|
291
321
|
cache_read_tokens=cache_read_tokens,
|
|
292
322
|
error=error,
|
|
323
|
+
tool_definitions=tool_definitions,
|
|
293
324
|
)
|
|
294
325
|
|
|
295
326
|
return traced_agen()
|
|
@@ -26,6 +26,7 @@ from typing import Any, Dict, List, Optional, Union
|
|
|
26
26
|
from uuid import UUID
|
|
27
27
|
|
|
28
28
|
from agentx.tracing.tracer import Tracer, _safe_serialize
|
|
29
|
+
from agentx.integrations._traced_call import capture_tool_definitions
|
|
29
30
|
|
|
30
31
|
try:
|
|
31
32
|
from langchain_core.callbacks.base import BaseCallbackHandler
|
|
@@ -375,6 +376,10 @@ class AgentXCallbackHandler(BaseCallbackHandler):
|
|
|
375
376
|
# skipped plumbing runs to the nearest emitted ancestor.
|
|
376
377
|
"node_runs": {},
|
|
377
378
|
"chain_parents": {},
|
|
379
|
+
# The request's tools=[...] as seen on the first LLM call's invocation params -
|
|
380
|
+
# attached to the root trace's metadata so the engine's unregistered-tool
|
|
381
|
+
# listing can surface the REAL definition (not one inferred from arguments).
|
|
382
|
+
"tool_definitions": None,
|
|
378
383
|
}
|
|
379
384
|
else:
|
|
380
385
|
top = self._find_top_ancestor(parent_run_id)
|
|
@@ -522,6 +527,8 @@ class AgentXCallbackHandler(BaseCallbackHandler):
|
|
|
522
527
|
|
|
523
528
|
active_span = self._tracer.current_span
|
|
524
529
|
if active_span is not None:
|
|
530
|
+
if state.get("tool_definitions") and not (active_span._metadata or {}).get("tools"):
|
|
531
|
+
active_span._metadata = {**(active_span._metadata or {}), "tools": state["tool_definitions"]}
|
|
525
532
|
# Part of a `with tracer.trace(...)` block (e.g. an orchestrator
|
|
526
533
|
# spanning several chain/agent/retriever calls) - fold this
|
|
527
534
|
# top-level run into it instead of sending an independent trace.
|
|
@@ -540,7 +547,15 @@ class AgentXCallbackHandler(BaseCallbackHandler):
|
|
|
540
547
|
# Standalone usage (no enclosing `with tracer.trace()`): open a real root span for
|
|
541
548
|
# this chain invocation and let _merge_child_run explode its accumulated
|
|
542
549
|
# execution_steps/tool_calls/retrieval_steps into real child-span rows.
|
|
543
|
-
with self._tracer.trace(
|
|
550
|
+
with self._tracer.trace(
|
|
551
|
+
self._name,
|
|
552
|
+
metadata=(
|
|
553
|
+
{**(self._metadata or {}), "tools": state["tool_definitions"]}
|
|
554
|
+
if state.get("tool_definitions")
|
|
555
|
+
else self._metadata
|
|
556
|
+
),
|
|
557
|
+
session_id=self._session_id,
|
|
558
|
+
) as span:
|
|
544
559
|
# __enter__ just set _start to "now" - overridden to the chain's real start time,
|
|
545
560
|
# see llamaindex.py's _send_trace for the identical fix and full rationale.
|
|
546
561
|
span._start = state["start"]
|
|
@@ -577,6 +592,8 @@ class AgentXCallbackHandler(BaseCallbackHandler):
|
|
|
577
592
|
|
|
578
593
|
active_span = self._tracer.current_span
|
|
579
594
|
if active_span is not None:
|
|
595
|
+
if state.get("tool_definitions") and not (active_span._metadata or {}).get("tools"):
|
|
596
|
+
active_span._metadata = {**(active_span._metadata or {}), "tools": state["tool_definitions"]}
|
|
580
597
|
active_span.set_error(str(error))
|
|
581
598
|
active_span._merge_child_run(
|
|
582
599
|
tool_calls=state["tool_calls"],
|
|
@@ -590,7 +607,15 @@ class AgentXCallbackHandler(BaseCallbackHandler):
|
|
|
590
607
|
self._emit_span_tree(active_span, state, state["tool_calls"])
|
|
591
608
|
else:
|
|
592
609
|
# See on_chain_end's matching branch - same standalone-usage handling.
|
|
593
|
-
with self._tracer.trace(
|
|
610
|
+
with self._tracer.trace(
|
|
611
|
+
self._name,
|
|
612
|
+
metadata=(
|
|
613
|
+
{**(self._metadata or {}), "tools": state["tool_definitions"]}
|
|
614
|
+
if state.get("tool_definitions")
|
|
615
|
+
else self._metadata
|
|
616
|
+
),
|
|
617
|
+
session_id=self._session_id,
|
|
618
|
+
) as span:
|
|
594
619
|
span._start = state["start"]
|
|
595
620
|
span.set_error(str(error))
|
|
596
621
|
span._merge_child_run(
|
|
@@ -629,6 +654,11 @@ class AgentXCallbackHandler(BaseCallbackHandler):
|
|
|
629
654
|
or serialized.get("name")
|
|
630
655
|
)
|
|
631
656
|
model = str(model) if model and model not in ("None", "none") else None
|
|
657
|
+
top_for_tools = self._find_top_ancestor(parent_run_id)
|
|
658
|
+
if top_for_tools and top_for_tools in self._runs and not self._runs[top_for_tools].get("tool_definitions"):
|
|
659
|
+
captured = capture_tool_definitions(kwargs.get("invocation_params", {}).get("tools"))
|
|
660
|
+
if captured:
|
|
661
|
+
self._runs[top_for_tools]["tool_definitions"] = captured
|
|
632
662
|
self._runs[run_id] = {
|
|
633
663
|
"llm_start": time.time(),
|
|
634
664
|
"model": model,
|
|
@@ -18,7 +18,7 @@ from __future__ import annotations
|
|
|
18
18
|
from typing import Any, Dict, Optional, Tuple
|
|
19
19
|
|
|
20
20
|
from agentx.tracing.tracer import Tracer, _safe_serialize
|
|
21
|
-
from agentx.integrations._traced_call import finish_llm_call
|
|
21
|
+
from agentx.integrations._traced_call import capture_tool_definitions, finish_llm_call
|
|
22
22
|
|
|
23
23
|
try:
|
|
24
24
|
from litellm.integrations.custom_logger import CustomLogger
|
|
@@ -98,6 +98,10 @@ class AgentXLiteLLMLogger(CustomLogger):
|
|
|
98
98
|
def _finish(self, kwargs: Dict[str, Any], response_obj: Any, start_time: Any, end_time: Any, error: Optional[str]) -> None:
|
|
99
99
|
model = kwargs.get("model")
|
|
100
100
|
input_repr = _safe_serialize(kwargs.get("messages"))
|
|
101
|
+
# LiteLLM surfaces the request's tools under optional_params (and sometimes top-level).
|
|
102
|
+
tool_definitions = capture_tool_definitions(
|
|
103
|
+
kwargs.get("tools") or (kwargs.get("optional_params") or {}).get("tools")
|
|
104
|
+
)
|
|
101
105
|
output = None
|
|
102
106
|
input_tokens = None
|
|
103
107
|
output_tokens = None
|
|
@@ -118,6 +122,7 @@ class AgentXLiteLLMLogger(CustomLogger):
|
|
|
118
122
|
output=output,
|
|
119
123
|
model=model,
|
|
120
124
|
input_tokens=input_tokens,
|
|
125
|
+
tool_definitions=tool_definitions,
|
|
121
126
|
output_tokens=output_tokens,
|
|
122
127
|
cache_read_tokens=cache_read_tokens,
|
|
123
128
|
error=error,
|
|
@@ -28,7 +28,7 @@ import time
|
|
|
28
28
|
from typing import Any, Dict, Optional, Tuple
|
|
29
29
|
|
|
30
30
|
from agentx.tracing.tracer import Tracer, _safe_serialize
|
|
31
|
-
from agentx.integrations._traced_call import call_and_trace, finish_llm_call
|
|
31
|
+
from agentx.integrations._traced_call import capture_tool_definitions, call_and_trace, finish_llm_call
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
def _extract_output_text(response: Any) -> Optional[str]:
|
|
@@ -128,6 +128,7 @@ def _patch_chat_completions_create(
|
|
|
128
128
|
input_messages = kwargs.get("messages") or (args[0] if args else None)
|
|
129
129
|
model = kwargs.get("model")
|
|
130
130
|
input_repr = _safe_serialize(input_messages)
|
|
131
|
+
tool_definitions = capture_tool_definitions(kwargs.get("tools"))
|
|
131
132
|
|
|
132
133
|
def on_finish(response: Optional[Any], error: Optional[str]) -> None:
|
|
133
134
|
end_t = time.time()
|
|
@@ -159,6 +160,7 @@ def _patch_chat_completions_create(
|
|
|
159
160
|
output_tokens=output_tokens,
|
|
160
161
|
cache_read_tokens=cache_read_tokens,
|
|
161
162
|
error=error,
|
|
163
|
+
tool_definitions=tool_definitions,
|
|
162
164
|
)
|
|
163
165
|
|
|
164
166
|
return call_and_trace(original, args, kwargs, on_finish)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
VERSION = "0.6.25"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
VERSION = "0.6.23"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|