quraite 0.1.0__py3-none-any.whl → 0.1.2__py3-none-any.whl
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.
- quraite/__init__.py +3 -3
- quraite/adapters/__init__.py +134 -134
- quraite/adapters/agno_adapter.py +157 -159
- quraite/adapters/base.py +123 -123
- quraite/adapters/bedrock_agents_adapter.py +343 -343
- quraite/adapters/flowise_adapter.py +275 -275
- quraite/adapters/google_adk_adapter.py +211 -209
- quraite/adapters/http_adapter.py +255 -239
- quraite/adapters/{langgraph_adapter.py → langchain_adapter.py} +305 -304
- quraite/adapters/{langgraph_server_adapter.py → langchain_server_adapter.py} +252 -252
- quraite/adapters/langflow_adapter.py +192 -192
- quraite/adapters/n8n_adapter.py +220 -220
- quraite/adapters/openai_agents_adapter.py +267 -269
- quraite/adapters/pydantic_ai_adapter.py +307 -312
- quraite/adapters/smolagents_adapter.py +148 -152
- quraite/logger.py +61 -61
- quraite/schema/message.py +91 -91
- quraite/schema/response.py +16 -16
- quraite/serve/__init__.py +1 -1
- quraite/serve/cloudflared.py +210 -210
- quraite/serve/local_agent.py +360 -360
- quraite/traces/traces_adk_openinference.json +379 -0
- quraite/traces/traces_agno_multi_agent.json +669 -0
- quraite/traces/traces_agno_openinference.json +321 -0
- quraite/traces/traces_crewai_openinference.json +155 -0
- quraite/traces/traces_langgraph_openinference.json +349 -0
- quraite/traces/traces_langgraph_openinference_multi_agent.json +2705 -0
- quraite/traces/traces_langgraph_traceloop.json +510 -0
- quraite/traces/traces_openai_agents_multi_agent_1.json +402 -0
- quraite/traces/traces_openai_agents_openinference.json +341 -0
- quraite/traces/traces_pydantic_openinference.json +286 -0
- quraite/traces/traces_pydantic_openinference_multi_agent_1.json +399 -0
- quraite/traces/traces_pydantic_openinference_multi_agent_2.json +398 -0
- quraite/traces/traces_smol_agents_openinference.json +397 -0
- quraite/traces/traces_smol_agents_tool_calling_openinference.json +704 -0
- quraite/tracing/__init__.py +25 -24
- quraite/tracing/constants.py +15 -16
- quraite/tracing/span_exporter.py +101 -115
- quraite/tracing/span_processor.py +47 -49
- quraite/tracing/tool_extractors.py +309 -290
- quraite/tracing/trace.py +564 -564
- quraite/tracing/types.py +179 -179
- quraite/tracing/utils.py +170 -170
- quraite/utils/json_utils.py +269 -269
- quraite-0.1.2.dist-info/METADATA +386 -0
- quraite-0.1.2.dist-info/RECORD +49 -0
- {quraite-0.1.0.dist-info → quraite-0.1.2.dist-info}/WHEEL +1 -1
- quraite-0.1.0.dist-info/METADATA +0 -44
- quraite-0.1.0.dist-info/RECORD +0 -35
|
@@ -1,290 +1,309 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Framework-specific tool extractors for converting span attributes to standardized tool call information.
|
|
3
|
-
|
|
4
|
-
These extractors handle the varying attribute structures across different agent frameworks
|
|
5
|
-
(pydantic,
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
import json
|
|
9
|
-
from typing import Any, Protocol
|
|
10
|
-
|
|
11
|
-
from
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
self.
|
|
27
|
-
self.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
# =============================================================================
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
-
|
|
58
|
-
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
-
|
|
86
|
-
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
-
|
|
154
|
-
-
|
|
155
|
-
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
response = output_value
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
- tool.
|
|
232
|
-
-
|
|
233
|
-
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
"""
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
)
|
|
1
|
+
"""
|
|
2
|
+
Framework-specific tool extractors for converting span attributes to standardized tool call information.
|
|
3
|
+
|
|
4
|
+
These extractors handle the varying attribute structures across different agent frameworks
|
|
5
|
+
(pydantic, langchain, adk, openai_agents, agno, smolagents, etc.)
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from typing import Any, Protocol
|
|
10
|
+
|
|
11
|
+
from openinference.semconv.trace import SpanAttributes
|
|
12
|
+
|
|
13
|
+
from quraite.tracing.constants import Framework
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ToolCallInfo:
|
|
17
|
+
"""Standardized tool call information extracted from a TOOL span."""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
tool_name: str,
|
|
22
|
+
tool_call_id: str | None,
|
|
23
|
+
arguments: str | dict,
|
|
24
|
+
response: Any,
|
|
25
|
+
):
|
|
26
|
+
self.tool_name = tool_name
|
|
27
|
+
self.tool_call_id = tool_call_id
|
|
28
|
+
self.arguments = arguments
|
|
29
|
+
self.response = response
|
|
30
|
+
|
|
31
|
+
def to_dict(self) -> dict[str, Any]:
|
|
32
|
+
return {
|
|
33
|
+
"role": "tool",
|
|
34
|
+
"tool_name": self.tool_name,
|
|
35
|
+
"tool_call_id": self.tool_call_id,
|
|
36
|
+
"arguments": self.arguments,
|
|
37
|
+
"response": self.response,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ToolExtractor(Protocol):
|
|
42
|
+
"""Protocol for framework-specific tool extractors."""
|
|
43
|
+
|
|
44
|
+
def __call__(self, span: dict[str, Any]) -> ToolCallInfo | None: ...
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# =============================================================================
|
|
48
|
+
# Framework-specific tool extractors
|
|
49
|
+
# =============================================================================
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def extract_tool_pydantic(span: dict[str, Any]) -> ToolCallInfo | None:
|
|
53
|
+
"""
|
|
54
|
+
Extract tool info from Pydantic AI tool spans.
|
|
55
|
+
|
|
56
|
+
Attributes:
|
|
57
|
+
- tool.name: "customer_balance"
|
|
58
|
+
- tool_call.id: "call_xxx"
|
|
59
|
+
- tool_arguments: "{\"include_pending\":true}"
|
|
60
|
+
- tool_response: "$123.45"
|
|
61
|
+
"""
|
|
62
|
+
attrs = span.get("attributes", {})
|
|
63
|
+
|
|
64
|
+
tool_name = attrs.get("tool.name") or attrs.get("gen_ai.tool.name")
|
|
65
|
+
if not tool_name:
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
tool_call_id = attrs.get("tool_call.id") or attrs.get("gen_ai.tool.call.id")
|
|
69
|
+
arguments = attrs.get("tool_arguments", "{}")
|
|
70
|
+
response = attrs.get("tool_response", "")
|
|
71
|
+
|
|
72
|
+
return ToolCallInfo(
|
|
73
|
+
tool_name=tool_name,
|
|
74
|
+
tool_call_id=tool_call_id,
|
|
75
|
+
arguments=arguments,
|
|
76
|
+
response=response,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def extract_tool_langchain(span: dict[str, Any]) -> ToolCallInfo | None:
|
|
81
|
+
"""
|
|
82
|
+
Extract tool info from LangChain tool spans.
|
|
83
|
+
|
|
84
|
+
Attributes:
|
|
85
|
+
- tool.name: "add"
|
|
86
|
+
- tool.description: "Add two numbers."
|
|
87
|
+
- input.value: "{'b': 1, 'a': 1}"
|
|
88
|
+
- output.value: JSON with content
|
|
89
|
+
"""
|
|
90
|
+
attrs = span.get("attributes", {})
|
|
91
|
+
|
|
92
|
+
tool_name = attrs.get("tool.name")
|
|
93
|
+
if not tool_name:
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
arguments = attrs.get("input.value", "{}")
|
|
97
|
+
output_value = attrs.get("output.value", "")
|
|
98
|
+
|
|
99
|
+
# Also check for response attribute (some LangChain spans store response here)
|
|
100
|
+
response_value = attrs.get("response", output_value)
|
|
101
|
+
|
|
102
|
+
# Try to parse output to extract content
|
|
103
|
+
response = response_value
|
|
104
|
+
if isinstance(response_value, str):
|
|
105
|
+
try:
|
|
106
|
+
parsed = json.loads(response_value)
|
|
107
|
+
if isinstance(parsed, dict):
|
|
108
|
+
# Check if response field contains JSON string (nested JSON)
|
|
109
|
+
if "response" in parsed and isinstance(parsed["response"], str):
|
|
110
|
+
try:
|
|
111
|
+
inner_parsed = json.loads(parsed["response"])
|
|
112
|
+
if isinstance(inner_parsed, dict) and "update" in inner_parsed:
|
|
113
|
+
parsed = inner_parsed
|
|
114
|
+
except (json.JSONDecodeError, TypeError):
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
# First check for direct content field
|
|
118
|
+
if "content" in parsed:
|
|
119
|
+
response = parsed.get("content", response_value)
|
|
120
|
+
# Check for update.messages structure (LangChain graph updates)
|
|
121
|
+
# this comes when you use supervisor agent with multiple agents
|
|
122
|
+
elif "update" in parsed:
|
|
123
|
+
update = parsed.get("update", {})
|
|
124
|
+
messages = update.get("messages", [])
|
|
125
|
+
# Find the last tool message
|
|
126
|
+
for msg in reversed(messages):
|
|
127
|
+
if isinstance(msg, dict) and msg.get("type") == "tool":
|
|
128
|
+
content = msg.get("content", "")
|
|
129
|
+
if content:
|
|
130
|
+
response = content
|
|
131
|
+
break
|
|
132
|
+
else:
|
|
133
|
+
# No tool message found, keep original response
|
|
134
|
+
response = response_value
|
|
135
|
+
else:
|
|
136
|
+
response = response_value
|
|
137
|
+
except json.JSONDecodeError:
|
|
138
|
+
pass
|
|
139
|
+
|
|
140
|
+
return ToolCallInfo(
|
|
141
|
+
tool_name=tool_name,
|
|
142
|
+
tool_call_id=None, # LangChain doesn't always have call IDs in tool spans
|
|
143
|
+
arguments=arguments,
|
|
144
|
+
response=response,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def extract_tool_adk(span: dict[str, Any]) -> ToolCallInfo | None:
|
|
149
|
+
"""
|
|
150
|
+
Extract tool info from Google ADK tool spans.
|
|
151
|
+
|
|
152
|
+
Attributes:
|
|
153
|
+
- tool.name: "get_weather"
|
|
154
|
+
- tool.parameters: "{\"city\": \"New York\"}"
|
|
155
|
+
- gcp.vertex.agent.tool_call_args: "{\"city\": \"New York\"}"
|
|
156
|
+
- gcp.vertex.agent.tool_response: JSON response
|
|
157
|
+
- output.value: JSON with id, name, response
|
|
158
|
+
"""
|
|
159
|
+
attrs = span.get("attributes", {})
|
|
160
|
+
|
|
161
|
+
tool_name = attrs.get("tool.name") or attrs.get("gen_ai.tool.name")
|
|
162
|
+
if not tool_name:
|
|
163
|
+
return None
|
|
164
|
+
|
|
165
|
+
# Skip merged tool spans
|
|
166
|
+
if tool_name == "(merged tools)":
|
|
167
|
+
return None
|
|
168
|
+
|
|
169
|
+
tool_call_id = attrs.get("gen_ai.tool.call.id")
|
|
170
|
+
arguments = (
|
|
171
|
+
attrs.get("tool.parameters")
|
|
172
|
+
or attrs.get("gcp.vertex.agent.tool_call_args")
|
|
173
|
+
or attrs.get("input.value", "{}")
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
# Get response from various possible locations
|
|
177
|
+
response = attrs.get("gcp.vertex.agent.tool_response", "")
|
|
178
|
+
if not response or response == "<not serializable>":
|
|
179
|
+
output_value = attrs.get("output.value", "")
|
|
180
|
+
if isinstance(output_value, str):
|
|
181
|
+
try:
|
|
182
|
+
parsed = json.loads(output_value)
|
|
183
|
+
if isinstance(parsed, dict) and "response" in parsed:
|
|
184
|
+
response = parsed.get("response", output_value)
|
|
185
|
+
else:
|
|
186
|
+
response = output_value
|
|
187
|
+
except json.JSONDecodeError:
|
|
188
|
+
response = output_value
|
|
189
|
+
else:
|
|
190
|
+
response = output_value
|
|
191
|
+
|
|
192
|
+
return ToolCallInfo(
|
|
193
|
+
tool_name=tool_name,
|
|
194
|
+
tool_call_id=tool_call_id,
|
|
195
|
+
arguments=arguments,
|
|
196
|
+
response=response,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def extract_tool_openai_agents(span: dict[str, Any]) -> ToolCallInfo | None:
|
|
201
|
+
"""
|
|
202
|
+
Extract tool info from OpenAI Agents tool spans.
|
|
203
|
+
|
|
204
|
+
Attributes:
|
|
205
|
+
- tool.name: "multiply"
|
|
206
|
+
- input.value: "{\"a\":10,\"b\":2}"
|
|
207
|
+
- output.value: 20.0
|
|
208
|
+
"""
|
|
209
|
+
attrs = span.get("attributes", {})
|
|
210
|
+
|
|
211
|
+
tool_name = attrs.get("tool.name")
|
|
212
|
+
if not tool_name:
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
arguments = attrs.get("input.value", "{}")
|
|
216
|
+
response = attrs.get("output.value", "")
|
|
217
|
+
|
|
218
|
+
return ToolCallInfo(
|
|
219
|
+
tool_name=tool_name,
|
|
220
|
+
tool_call_id=None, # OpenAI Agents SDK doesn't put call ID in tool span
|
|
221
|
+
arguments=arguments,
|
|
222
|
+
response=response,
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def extract_tool_agno(span: dict[str, Any]) -> ToolCallInfo | None:
|
|
227
|
+
"""
|
|
228
|
+
Extract tool info from Agno tool spans.
|
|
229
|
+
|
|
230
|
+
Attributes:
|
|
231
|
+
- tool.name: "duckduckgo_search"
|
|
232
|
+
- tool.description: "..."
|
|
233
|
+
- tool.parameters: "{\"query\": \"...\", \"max_results\": 5}"
|
|
234
|
+
- input.value: same as parameters
|
|
235
|
+
- output.value: JSON response
|
|
236
|
+
"""
|
|
237
|
+
attrs = span.get("attributes", {})
|
|
238
|
+
|
|
239
|
+
tool_name = attrs.get("tool.name")
|
|
240
|
+
if not tool_name:
|
|
241
|
+
return None
|
|
242
|
+
|
|
243
|
+
arguments = attrs.get("tool.parameters") or attrs.get("input.value", "{}")
|
|
244
|
+
response = attrs.get("output.value", "")
|
|
245
|
+
|
|
246
|
+
return ToolCallInfo(
|
|
247
|
+
tool_name=tool_name,
|
|
248
|
+
tool_call_id=None,
|
|
249
|
+
arguments=arguments,
|
|
250
|
+
response=response,
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def extract_tool_smolagents(span: dict[str, Any]) -> ToolCallInfo | None:
|
|
255
|
+
"""
|
|
256
|
+
Extract tool info from SmolAgents tool spans.
|
|
257
|
+
"""
|
|
258
|
+
attrs = span.get("attributes", {})
|
|
259
|
+
|
|
260
|
+
tool_name = attrs.get("tool.name")
|
|
261
|
+
if not tool_name:
|
|
262
|
+
return None
|
|
263
|
+
|
|
264
|
+
arguments = attrs.get("input.value", "{}")
|
|
265
|
+
response = attrs.get("output.value", "")
|
|
266
|
+
|
|
267
|
+
return ToolCallInfo(
|
|
268
|
+
tool_name=tool_name,
|
|
269
|
+
tool_call_id=None,
|
|
270
|
+
arguments=arguments,
|
|
271
|
+
response=response,
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def extract_default(span: dict[str, Any]) -> ToolCallInfo | None:
|
|
276
|
+
"""
|
|
277
|
+
Extract tool info following openinference semantic conventions.
|
|
278
|
+
"""
|
|
279
|
+
attrs = span.get("attributes", {})
|
|
280
|
+
|
|
281
|
+
tool_name = attrs.get(SpanAttributes.TOOL_NAME, "")
|
|
282
|
+
arguments = attrs.get(SpanAttributes.TOOL_PARAMETERS, "{}")
|
|
283
|
+
response = attrs.get(SpanAttributes.OUTPUT_VALUE, "")
|
|
284
|
+
|
|
285
|
+
return ToolCallInfo(
|
|
286
|
+
tool_name=tool_name,
|
|
287
|
+
tool_call_id=None,
|
|
288
|
+
arguments=arguments,
|
|
289
|
+
response=response,
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
# Registry of framework extractors
|
|
294
|
+
TOOL_EXTRACTORS: dict[Framework, ToolExtractor] = {
|
|
295
|
+
Framework.PYDANTIC_AI: extract_tool_pydantic,
|
|
296
|
+
Framework.LANGCHAIN: extract_tool_langchain,
|
|
297
|
+
Framework.GOOGLE_ADK: extract_tool_adk,
|
|
298
|
+
Framework.OPENAI_AGENTS: extract_tool_openai_agents,
|
|
299
|
+
Framework.AGNO: extract_tool_agno,
|
|
300
|
+
Framework.SMOLAGENTS: extract_tool_smolagents,
|
|
301
|
+
Framework.DEFAULT: extract_default,
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def get_tool_extractor(framework: Framework | str) -> ToolExtractor:
|
|
306
|
+
"""Get the appropriate tool extractor for the given framework."""
|
|
307
|
+
if isinstance(framework, str):
|
|
308
|
+
framework = Framework(framework.lower())
|
|
309
|
+
return TOOL_EXTRACTORS.get(framework, extract_default)
|