quraite 0.1.1__py3-none-any.whl → 0.1.3__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/adapters/__init__.py +12 -12
- quraite/adapters/agno_adapter.py +12 -14
- quraite/adapters/base.py +2 -2
- quraite/adapters/google_adk_adapter.py +13 -11
- quraite/adapters/http_adapter.py +6 -2
- quraite/adapters/{langgraph_adapter.py → langchain_adapter.py} +22 -21
- quraite/adapters/{langgraph_server_adapter.py → langchain_server_adapter.py} +21 -21
- quraite/adapters/openai_agents_adapter.py +12 -14
- quraite/adapters/pydantic_ai_adapter.py +12 -17
- quraite/adapters/smolagents_adapter.py +10 -14
- quraite/serve/local_agent.py +7 -6
- quraite/tracing/__init__.py +1 -0
- quraite/tracing/constants.py +3 -4
- quraite/tracing/span_exporter.py +5 -19
- quraite/tracing/span_processor.py +1 -3
- quraite/tracing/tool_extractors.py +30 -11
- quraite/tracing/trace.py +6 -6
- {quraite-0.1.1.dist-info → quraite-0.1.3.dist-info}/METADATA +52 -43
- {quraite-0.1.1.dist-info → quraite-0.1.3.dist-info}/RECORD +20 -20
- {quraite-0.1.1.dist-info → quraite-0.1.3.dist-info}/WHEEL +0 -0
quraite/adapters/__init__.py
CHANGED
|
@@ -8,9 +8,9 @@ if TYPE_CHECKING:
|
|
|
8
8
|
from quraite.adapters.bedrock_agents_adapter import BedrockAgentsAdapter
|
|
9
9
|
from quraite.adapters.flowise_adapter import FlowiseAdapter
|
|
10
10
|
from quraite.adapters.google_adk_adapter import GoogleADKAdapter
|
|
11
|
+
from quraite.adapters.langchain_adapter import LangchainAdapter
|
|
12
|
+
from quraite.adapters.langchain_server_adapter import LangchainServerAdapter
|
|
11
13
|
from quraite.adapters.langflow_adapter import LangflowAdapter
|
|
12
|
-
from quraite.adapters.langgraph_adapter import LanggraphAdapter
|
|
13
|
-
from quraite.adapters.langgraph_server_adapter import LanggraphServerAdapter
|
|
14
14
|
from quraite.adapters.n8n_adapter import N8nAdapter
|
|
15
15
|
from quraite.adapters.openai_agents_adapter import OpenaiAgentsAdapter
|
|
16
16
|
from quraite.adapters.pydantic_ai_adapter import PydanticAIAdapter
|
|
@@ -25,8 +25,8 @@ __all__ = [
|
|
|
25
25
|
"FlowiseAdapter",
|
|
26
26
|
"GoogleADKAdapter",
|
|
27
27
|
"LangflowAdapter",
|
|
28
|
-
"
|
|
29
|
-
"
|
|
28
|
+
"LangchainAdapter",
|
|
29
|
+
"LangchainServerAdapter",
|
|
30
30
|
"N8nAdapter",
|
|
31
31
|
"OpenaiAgentsAdapter",
|
|
32
32
|
"PydanticAIAdapter",
|
|
@@ -76,24 +76,24 @@ def __getattr__(name: str):
|
|
|
76
76
|
|
|
77
77
|
return LangflowAdapter
|
|
78
78
|
|
|
79
|
-
elif name == "
|
|
79
|
+
elif name == "LangchainAdapter":
|
|
80
80
|
try:
|
|
81
|
-
from quraite.adapters.
|
|
81
|
+
from quraite.adapters.langchain_adapter import LangchainAdapter
|
|
82
82
|
|
|
83
|
-
return
|
|
83
|
+
return LangchainAdapter
|
|
84
84
|
except ImportError as e:
|
|
85
85
|
raise ImportError(
|
|
86
|
-
f"Failed to import {name}. Please install the '
|
|
86
|
+
f"Failed to import {name}. Please install the 'langchain' optional dependency: pip install 'quraite[langchain]'"
|
|
87
87
|
) from e
|
|
88
88
|
|
|
89
|
-
elif name == "
|
|
89
|
+
elif name == "LangchainServerAdapter":
|
|
90
90
|
try:
|
|
91
|
-
from quraite.adapters.
|
|
91
|
+
from quraite.adapters.langchain_server_adapter import LangchainServerAdapter
|
|
92
92
|
|
|
93
|
-
return
|
|
93
|
+
return LangchainServerAdapter
|
|
94
94
|
except ImportError as e:
|
|
95
95
|
raise ImportError(
|
|
96
|
-
f"Failed to import {name}. Please install the '
|
|
96
|
+
f"Failed to import {name}. Please install the 'langchain' optional dependency: pip install 'quraite[langchain]'"
|
|
97
97
|
) from e
|
|
98
98
|
|
|
99
99
|
elif name == "N8nAdapter":
|
quraite/adapters/agno_adapter.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import uuid
|
|
2
1
|
from typing import List, Union
|
|
3
2
|
|
|
4
3
|
from agno.agent import Agent
|
|
@@ -9,7 +8,7 @@ from quraite.adapters.base import BaseAdapter
|
|
|
9
8
|
from quraite.logger import get_logger
|
|
10
9
|
from quraite.schema.message import AgentMessage
|
|
11
10
|
from quraite.schema.response import AgentInvocationResponse
|
|
12
|
-
from quraite.tracing.constants import
|
|
11
|
+
from quraite.tracing.constants import Framework
|
|
13
12
|
from quraite.tracing.trace import AgentSpan, AgentTrace
|
|
14
13
|
|
|
15
14
|
logger = get_logger(__name__)
|
|
@@ -122,27 +121,26 @@ class AgnoAdapter(BaseAdapter):
|
|
|
122
121
|
session_id: str,
|
|
123
122
|
) -> AgentInvocationResponse:
|
|
124
123
|
"""Execute ainvoke with tracing enabled."""
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
with self.tracer.start_as_current_span(name=adapter_trace_id):
|
|
124
|
+
with self.tracer.start_as_current_span("agno_invocation") as span:
|
|
125
|
+
trace_id = span.get_span_context().trace_id
|
|
126
|
+
logger.debug(
|
|
127
|
+
"Starting traced invocation (session_id=%s) with trace_id=%s",
|
|
128
|
+
session_id,
|
|
129
|
+
trace_id,
|
|
130
|
+
)
|
|
133
131
|
# Run the agent/team
|
|
134
132
|
await self.agent.arun(agent_input, session_id=session_id)
|
|
135
133
|
|
|
136
134
|
# Get trace spans
|
|
137
|
-
trace_readable_spans = self.quraite_span_exporter.
|
|
138
|
-
|
|
135
|
+
trace_readable_spans = self.quraite_span_exporter.get_spans_by_trace_id(
|
|
136
|
+
trace_id
|
|
139
137
|
)
|
|
140
138
|
|
|
141
139
|
if trace_readable_spans:
|
|
142
140
|
logger.info(
|
|
143
141
|
"Retrieved %d spans for trace_id=%s",
|
|
144
142
|
len(trace_readable_spans),
|
|
145
|
-
|
|
143
|
+
trace_id,
|
|
146
144
|
)
|
|
147
145
|
agent_trace = AgentTrace(
|
|
148
146
|
spans=[
|
|
@@ -151,7 +149,7 @@ class AgnoAdapter(BaseAdapter):
|
|
|
151
149
|
],
|
|
152
150
|
)
|
|
153
151
|
else:
|
|
154
|
-
logger.warning("No spans found for trace_id=%s",
|
|
152
|
+
logger.warning("No spans found for trace_id=%s", trace_id)
|
|
155
153
|
|
|
156
154
|
return AgentInvocationResponse(
|
|
157
155
|
agent_trace=agent_trace,
|
quraite/adapters/base.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
from typing import Any, List, Optional, Union
|
|
3
3
|
|
|
4
|
-
from opentelemetry.trace import TracerProvider
|
|
4
|
+
from opentelemetry.trace import Tracer, TracerProvider
|
|
5
5
|
|
|
6
6
|
from quraite.schema.message import AgentMessage, AssistantMessage, MessageContentText
|
|
7
7
|
from quraite.schema.response import AgentInvocationResponse
|
|
@@ -22,7 +22,7 @@ class BaseAdapter(ABC):
|
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
24
|
tracer_provider: Optional[TracerProvider] = None
|
|
25
|
-
tracer: Optional[
|
|
25
|
+
tracer: Optional[Tracer] = None
|
|
26
26
|
quraite_span_exporter: Optional[QuraiteInMemorySpanExporter] = None
|
|
27
27
|
|
|
28
28
|
def _init_tracing(
|
|
@@ -13,7 +13,7 @@ from quraite.adapters.base import BaseAdapter
|
|
|
13
13
|
from quraite.logger import get_logger
|
|
14
14
|
from quraite.schema.message import AgentMessage
|
|
15
15
|
from quraite.schema.response import AgentInvocationResponse
|
|
16
|
-
from quraite.tracing.constants import
|
|
16
|
+
from quraite.tracing.constants import Framework
|
|
17
17
|
from quraite.tracing.trace import AgentSpan, AgentTrace
|
|
18
18
|
|
|
19
19
|
logger = get_logger(__name__)
|
|
@@ -141,14 +141,18 @@ class GoogleADKAdapter(BaseAdapter):
|
|
|
141
141
|
session_id: str,
|
|
142
142
|
) -> AgentInvocationResponse:
|
|
143
143
|
"""Execute ainvoke with tracing enabled."""
|
|
144
|
-
adapter_trace_id = f"{QURAITE_ADAPTER_TRACE_PREFIX}-{uuid.uuid4()}"
|
|
145
144
|
logger.debug(
|
|
146
|
-
"Starting Google ADK traced invocation (
|
|
147
|
-
adapter_trace_id,
|
|
145
|
+
"Starting Google ADK traced invocation (session_id=%s)",
|
|
148
146
|
session_id,
|
|
149
147
|
)
|
|
150
148
|
|
|
151
|
-
with self.tracer.start_as_current_span(
|
|
149
|
+
with self.tracer.start_as_current_span("google_adk_invocation") as span:
|
|
150
|
+
trace_id = span.get_span_context().trace_id
|
|
151
|
+
logger.debug(
|
|
152
|
+
"Starting Google ADK traced invocation (trace_id=%s, session_id=%s)",
|
|
153
|
+
trace_id,
|
|
154
|
+
session_id,
|
|
155
|
+
)
|
|
152
156
|
# Create session if it doesn't exist
|
|
153
157
|
try:
|
|
154
158
|
await self.session_service.create_session(
|
|
@@ -180,8 +184,8 @@ class GoogleADKAdapter(BaseAdapter):
|
|
|
180
184
|
pass # Just consume events, tracing handles capture
|
|
181
185
|
|
|
182
186
|
# Get trace spans
|
|
183
|
-
trace_readable_spans = self.quraite_span_exporter.
|
|
184
|
-
|
|
187
|
+
trace_readable_spans = self.quraite_span_exporter.get_spans_by_trace_id(
|
|
188
|
+
trace_id
|
|
185
189
|
)
|
|
186
190
|
|
|
187
191
|
if trace_readable_spans:
|
|
@@ -194,12 +198,10 @@ class GoogleADKAdapter(BaseAdapter):
|
|
|
194
198
|
logger.info(
|
|
195
199
|
"Google ADK trace collected %d spans for trace_id=%s",
|
|
196
200
|
len(trace_readable_spans),
|
|
197
|
-
|
|
201
|
+
trace_id,
|
|
198
202
|
)
|
|
199
203
|
else:
|
|
200
|
-
logger.warning(
|
|
201
|
-
"No spans exported for Google ADK trace_id=%s", adapter_trace_id
|
|
202
|
-
)
|
|
204
|
+
logger.warning("No spans exported for Google ADK trace_id=%s", trace_id)
|
|
203
205
|
|
|
204
206
|
return AgentInvocationResponse(
|
|
205
207
|
agent_trace=agent_trace,
|
quraite/adapters/http_adapter.py
CHANGED
|
@@ -239,10 +239,14 @@ if __name__ == "__main__":
|
|
|
239
239
|
|
|
240
240
|
try:
|
|
241
241
|
response = await adapter.ainvoke(
|
|
242
|
-
input=[
|
|
242
|
+
input=[
|
|
243
|
+
UserMessage(
|
|
244
|
+
content=[MessageContentText(text="What is 34354 - 54?")]
|
|
245
|
+
)
|
|
246
|
+
],
|
|
243
247
|
session_id="test",
|
|
244
248
|
)
|
|
245
|
-
print(response)
|
|
249
|
+
print(response.agent_trajectory)
|
|
246
250
|
except httpx.HTTPStatusError as e:
|
|
247
251
|
print(json.loads(e.response.text)["detail"])
|
|
248
252
|
except Exception as e:
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import json
|
|
2
|
-
import uuid
|
|
3
2
|
from typing import Optional
|
|
4
3
|
|
|
5
4
|
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage, ToolMessage
|
|
@@ -14,7 +13,7 @@ from quraite.schema.message import ToolCall
|
|
|
14
13
|
from quraite.schema.message import ToolMessage as QuraiteToolMessage
|
|
15
14
|
from quraite.schema.message import UserMessage
|
|
16
15
|
from quraite.schema.response import AgentInvocationResponse
|
|
17
|
-
from quraite.tracing.constants import
|
|
16
|
+
from quraite.tracing.constants import Framework
|
|
18
17
|
from quraite.tracing.trace import AgentSpan, AgentTrace
|
|
19
18
|
|
|
20
19
|
LangchainMessage = HumanMessage | SystemMessage | AIMessage | ToolMessage
|
|
@@ -22,12 +21,12 @@ LangchainMessage = HumanMessage | SystemMessage | AIMessage | ToolMessage
|
|
|
22
21
|
logger = get_logger(__name__)
|
|
23
22
|
|
|
24
23
|
|
|
25
|
-
class
|
|
24
|
+
class LangchainAdapter(BaseAdapter):
|
|
26
25
|
"""
|
|
27
|
-
|
|
26
|
+
LangChain adapter wrapper that converts any LangChain agent
|
|
28
27
|
to a standardized callable interface (invoke) and converts the output to List[AgentMessage].
|
|
29
28
|
|
|
30
|
-
This class wraps any
|
|
29
|
+
This class wraps any LangChain CompiledGraph and provides:
|
|
31
30
|
- Synchronous invocation via invoke()
|
|
32
31
|
- Asynchronous invocation via ainvoke()
|
|
33
32
|
- Automatic conversion to List[AgentMessage] format
|
|
@@ -36,22 +35,22 @@ class LanggraphAdapter(BaseAdapter):
|
|
|
36
35
|
def __init__(
|
|
37
36
|
self,
|
|
38
37
|
agent_graph: CompiledStateGraph,
|
|
39
|
-
agent_name: str = "
|
|
38
|
+
agent_name: str = "LangChain Agent",
|
|
40
39
|
tracer_provider: Optional[TracerProvider] = None,
|
|
41
40
|
):
|
|
42
41
|
"""
|
|
43
|
-
Initialize with a pre-configured
|
|
42
|
+
Initialize with a pre-configured LangChain agent
|
|
44
43
|
|
|
45
44
|
Args:
|
|
46
|
-
agent_graph: Any CompiledGraph from
|
|
45
|
+
agent_graph: Any CompiledGraph from LangChain (must have invoke/ainvoke methods)
|
|
47
46
|
agent_name: Name of the agent for trajectory metadata
|
|
48
47
|
"""
|
|
49
|
-
logger.debug("Initializing
|
|
48
|
+
logger.debug("Initializing LangchainAdapter with agent_name=%s", agent_name)
|
|
50
49
|
self.agent_graph = agent_graph
|
|
51
50
|
self.agent_name = agent_name
|
|
52
51
|
self._init_tracing(tracer_provider, required=False)
|
|
53
52
|
logger.info(
|
|
54
|
-
"
|
|
53
|
+
"LangchainAdapter initialized successfully (tracing_enabled=%s)",
|
|
55
54
|
bool(tracer_provider),
|
|
56
55
|
)
|
|
57
56
|
|
|
@@ -59,7 +58,7 @@ class LanggraphAdapter(BaseAdapter):
|
|
|
59
58
|
self, input: list[AgentMessage]
|
|
60
59
|
) -> dict[str, list[HumanMessage]]:
|
|
61
60
|
"""
|
|
62
|
-
Prepare input for
|
|
61
|
+
Prepare input for LangChain agent from List[Message].
|
|
63
62
|
|
|
64
63
|
Args:
|
|
65
64
|
input: List[AgentMessage] containing user_message
|
|
@@ -201,7 +200,7 @@ class LanggraphAdapter(BaseAdapter):
|
|
|
201
200
|
session_id: str | None,
|
|
202
201
|
) -> AgentInvocationResponse:
|
|
203
202
|
"""
|
|
204
|
-
Asynchronous invocation method - invokes the
|
|
203
|
+
Asynchronous invocation method - invokes the LangChain agent and converts the output to List[AgentMessage]
|
|
205
204
|
|
|
206
205
|
Args:
|
|
207
206
|
input: List[AgentMessage] containing user_message
|
|
@@ -238,14 +237,16 @@ class LanggraphAdapter(BaseAdapter):
|
|
|
238
237
|
config: dict,
|
|
239
238
|
) -> AgentInvocationResponse:
|
|
240
239
|
"""Execute ainvoke with tracing enabled."""
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
240
|
+
with self.tracer.start_as_current_span("langchain_invocation") as span:
|
|
241
|
+
trace_id = span.get_span_context().trace_id
|
|
242
|
+
logger.debug(
|
|
243
|
+
"Starting LangChain traced invocation (trace_id=%s)",
|
|
244
|
+
trace_id,
|
|
245
|
+
)
|
|
246
|
+
_ = await self.agent_graph.ainvoke(agent_input, config=config)
|
|
246
247
|
|
|
247
|
-
trace_readable_spans = self.quraite_span_exporter.
|
|
248
|
-
|
|
248
|
+
trace_readable_spans = self.quraite_span_exporter.get_spans_by_trace_id(
|
|
249
|
+
trace_id
|
|
249
250
|
)
|
|
250
251
|
|
|
251
252
|
if trace_readable_spans:
|
|
@@ -257,7 +258,7 @@ class LanggraphAdapter(BaseAdapter):
|
|
|
257
258
|
]
|
|
258
259
|
)
|
|
259
260
|
|
|
260
|
-
trajectory = agent_trace.to_agent_trajectory(framework=Framework.
|
|
261
|
+
trajectory = agent_trace.to_agent_trajectory(framework=Framework.LANGCHAIN)
|
|
261
262
|
logger.debug("Generated trajectory with %d messages", len(trajectory))
|
|
262
263
|
|
|
263
264
|
return AgentInvocationResponse(
|
|
@@ -265,7 +266,7 @@ class LanggraphAdapter(BaseAdapter):
|
|
|
265
266
|
agent_trajectory=trajectory,
|
|
266
267
|
)
|
|
267
268
|
|
|
268
|
-
logger.warning("No trace spans found for trace_id=%s",
|
|
269
|
+
logger.warning("No trace spans found for trace_id=%s", trace_id)
|
|
269
270
|
return AgentInvocationResponse()
|
|
270
271
|
|
|
271
272
|
async def _ainvoke_without_tracing(
|
|
@@ -22,11 +22,11 @@ LangchainMessage = Annotated[
|
|
|
22
22
|
logger = get_logger(__name__)
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
class
|
|
26
|
-
"""Remote
|
|
25
|
+
class LangchainServerAdapter(BaseAdapter):
|
|
26
|
+
"""Remote LangChain server adapter based on langgraph-sdk.
|
|
27
27
|
|
|
28
28
|
Args:
|
|
29
|
-
base_url: The base URL of the
|
|
29
|
+
base_url: The base URL of the LangChain server
|
|
30
30
|
assistant_id: The ID of the assistant to invoke
|
|
31
31
|
**kwargs: Additional keyword arguments passed directly to
|
|
32
32
|
langgraph_sdk.get_client() and get_sync_client().
|
|
@@ -49,7 +49,7 @@ class LanggraphServerAdapter(BaseAdapter):
|
|
|
49
49
|
self.graph_name = graph_name
|
|
50
50
|
|
|
51
51
|
logger.debug(
|
|
52
|
-
"Initializing
|
|
52
|
+
"Initializing LangchainServerAdapter (base_url=%s, assistant_id=%s, graph_name=%s)",
|
|
53
53
|
base_url,
|
|
54
54
|
assistant_id,
|
|
55
55
|
graph_name,
|
|
@@ -73,17 +73,17 @@ class LanggraphServerAdapter(BaseAdapter):
|
|
|
73
73
|
)
|
|
74
74
|
except Exception as exc:
|
|
75
75
|
raise RuntimeError(
|
|
76
|
-
f"Failed to initialize
|
|
76
|
+
f"Failed to initialize LangChain RemoteGraph for {self.base_url}: {exc}"
|
|
77
77
|
)
|
|
78
78
|
logger.info(
|
|
79
|
-
"
|
|
79
|
+
"LangchainServerAdapter initialized (assistant_id=%s, graph_name=%s)",
|
|
80
80
|
self.assistant_id,
|
|
81
81
|
self.graph_name,
|
|
82
82
|
)
|
|
83
83
|
|
|
84
84
|
def _prepare_input(self, input: List[AgentMessage]) -> Any:
|
|
85
85
|
"""
|
|
86
|
-
Prepare input for
|
|
86
|
+
Prepare input for LangChain agent from List[AgentMessage].
|
|
87
87
|
|
|
88
88
|
Args:
|
|
89
89
|
input: List[AgentMessage] containing user_message
|
|
@@ -91,15 +91,15 @@ class LanggraphServerAdapter(BaseAdapter):
|
|
|
91
91
|
Returns:
|
|
92
92
|
Dict with messages list containing user_message
|
|
93
93
|
"""
|
|
94
|
-
logger.debug("Preparing
|
|
94
|
+
logger.debug("Preparing Langchain server input from %d messages", len(input))
|
|
95
95
|
if not input or input[-1].role != "user":
|
|
96
|
-
logger.error("
|
|
96
|
+
logger.error("Langchain server input missing user message")
|
|
97
97
|
raise ValueError("No user message found in the input")
|
|
98
98
|
|
|
99
99
|
last_user_message = input[-1]
|
|
100
100
|
# Check if content list is not empty and has text
|
|
101
101
|
if not last_user_message.content:
|
|
102
|
-
logger.error("
|
|
102
|
+
logger.error("Langchain server user message missing content")
|
|
103
103
|
raise ValueError("User message has no content")
|
|
104
104
|
|
|
105
105
|
# Find the first text content item
|
|
@@ -110,11 +110,11 @@ class LanggraphServerAdapter(BaseAdapter):
|
|
|
110
110
|
break
|
|
111
111
|
|
|
112
112
|
if not text_content:
|
|
113
|
-
logger.error("
|
|
113
|
+
logger.error("Langchain server user message missing text content")
|
|
114
114
|
raise ValueError("No text content found in user message")
|
|
115
115
|
|
|
116
116
|
logger.debug(
|
|
117
|
-
"Prepared
|
|
117
|
+
"Prepared Langchain server input (text_length=%d)", len(text_content)
|
|
118
118
|
)
|
|
119
119
|
return {"messages": [HumanMessage(content=text_content).model_dump()]}
|
|
120
120
|
|
|
@@ -123,7 +123,7 @@ class LanggraphServerAdapter(BaseAdapter):
|
|
|
123
123
|
messages: List[dict],
|
|
124
124
|
) -> List[AgentMessage]:
|
|
125
125
|
logger.debug(
|
|
126
|
-
"Converting %d
|
|
126
|
+
"Converting %d Langchain server messages to quraite format", len(messages)
|
|
127
127
|
)
|
|
128
128
|
converted_messages: List[AgentMessage] = []
|
|
129
129
|
|
|
@@ -201,7 +201,7 @@ class LanggraphServerAdapter(BaseAdapter):
|
|
|
201
201
|
continue
|
|
202
202
|
|
|
203
203
|
logger.info(
|
|
204
|
-
"
|
|
204
|
+
"Langchain server message conversion produced %d messages",
|
|
205
205
|
len(converted_messages),
|
|
206
206
|
)
|
|
207
207
|
return converted_messages
|
|
@@ -209,7 +209,7 @@ class LanggraphServerAdapter(BaseAdapter):
|
|
|
209
209
|
async def ainvoke(
|
|
210
210
|
self,
|
|
211
211
|
input: List[AgentMessage],
|
|
212
|
-
session_id: Annotated[Union[str, None], "Thread ID used by
|
|
212
|
+
session_id: Annotated[Union[str, None], "Thread ID used by LangChain API"],
|
|
213
213
|
) -> AgentInvocationResponse:
|
|
214
214
|
agent_messages = []
|
|
215
215
|
agent_input = self._prepare_input(input)
|
|
@@ -219,19 +219,19 @@ class LanggraphServerAdapter(BaseAdapter):
|
|
|
219
219
|
config = {}
|
|
220
220
|
|
|
221
221
|
try:
|
|
222
|
-
logger.info("
|
|
222
|
+
logger.info("Langchain server ainvoke called (session_id=%s)", session_id)
|
|
223
223
|
async for event in self.remote_graph.astream(agent_input, config=config):
|
|
224
224
|
for _, result in event.items():
|
|
225
225
|
if result.get("messages"):
|
|
226
226
|
logger.debug(
|
|
227
|
-
"
|
|
227
|
+
"Langchain server received %d messages from stream chunk",
|
|
228
228
|
len(result.get("messages")),
|
|
229
229
|
)
|
|
230
230
|
agent_messages += result.get("messages")
|
|
231
231
|
|
|
232
232
|
except Exception as e:
|
|
233
|
-
logger.exception("Error invoking
|
|
234
|
-
raise RuntimeError(f"Error invoking
|
|
233
|
+
logger.exception("Error invoking Langchain remote graph")
|
|
234
|
+
raise RuntimeError(f"Error invoking LangChain agent: {e}") from e
|
|
235
235
|
|
|
236
236
|
try:
|
|
237
237
|
# Convert to List[AgentMessage]
|
|
@@ -239,7 +239,7 @@ class LanggraphServerAdapter(BaseAdapter):
|
|
|
239
239
|
agent_messages
|
|
240
240
|
)
|
|
241
241
|
logger.info(
|
|
242
|
-
"
|
|
242
|
+
"Langchain server ainvoke produced %d trajectory messages",
|
|
243
243
|
len(agent_trajectory),
|
|
244
244
|
)
|
|
245
245
|
|
|
@@ -248,5 +248,5 @@ class LanggraphServerAdapter(BaseAdapter):
|
|
|
248
248
|
)
|
|
249
249
|
|
|
250
250
|
except ValueError:
|
|
251
|
-
logger.exception("
|
|
251
|
+
logger.exception("Langchain server conversion to AgentMessage failed")
|
|
252
252
|
return AgentInvocationResponse()
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import json
|
|
2
|
-
import uuid
|
|
3
2
|
from typing import Dict, List, Optional, Union
|
|
4
3
|
|
|
5
4
|
from agents import (
|
|
@@ -27,7 +26,7 @@ from quraite.schema.message import (
|
|
|
27
26
|
ToolMessage,
|
|
28
27
|
)
|
|
29
28
|
from quraite.schema.response import AgentInvocationResponse
|
|
30
|
-
from quraite.tracing.constants import
|
|
29
|
+
from quraite.tracing.constants import Framework
|
|
31
30
|
from quraite.tracing.trace import AgentSpan, AgentTrace
|
|
32
31
|
|
|
33
32
|
logger = get_logger(__name__)
|
|
@@ -200,22 +199,21 @@ class OpenaiAgentsAdapter(BaseAdapter):
|
|
|
200
199
|
session: Session,
|
|
201
200
|
) -> AgentInvocationResponse:
|
|
202
201
|
"""Execute ainvoke with tracing enabled."""
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
with self.tracer.start_as_current_span(name=adapter_trace_id):
|
|
202
|
+
with self.tracer.start_as_current_span("openai_invocation") as span:
|
|
203
|
+
trace_id = span.get_span_context().trace_id
|
|
204
|
+
logger.debug(
|
|
205
|
+
"Starting OpenAI traced invocation (trace_id=%s session_id=%s)",
|
|
206
|
+
trace_id,
|
|
207
|
+
session.session_id if session else None,
|
|
208
|
+
)
|
|
211
209
|
await Runner.run(
|
|
212
210
|
self.agent,
|
|
213
211
|
input=agent_input,
|
|
214
212
|
session=session,
|
|
215
213
|
)
|
|
216
214
|
|
|
217
|
-
trace_readable_spans = self.quraite_span_exporter.
|
|
218
|
-
|
|
215
|
+
trace_readable_spans = self.quraite_span_exporter.get_spans_by_trace_id(
|
|
216
|
+
trace_id
|
|
219
217
|
)
|
|
220
218
|
|
|
221
219
|
if trace_readable_spans:
|
|
@@ -228,12 +226,12 @@ class OpenaiAgentsAdapter(BaseAdapter):
|
|
|
228
226
|
logger.info(
|
|
229
227
|
"OpenAI trace collected %d spans for trace_id=%s",
|
|
230
228
|
len(trace_readable_spans),
|
|
231
|
-
|
|
229
|
+
trace_id,
|
|
232
230
|
)
|
|
233
231
|
else:
|
|
234
232
|
logger.warning(
|
|
235
233
|
"No spans exported for OpenAI trace_id=%s",
|
|
236
|
-
|
|
234
|
+
trace_id,
|
|
237
235
|
)
|
|
238
236
|
|
|
239
237
|
return AgentInvocationResponse(
|
|
@@ -21,7 +21,7 @@ from quraite.schema.message import (
|
|
|
21
21
|
ToolMessage,
|
|
22
22
|
)
|
|
23
23
|
from quraite.schema.response import AgentInvocationResponse
|
|
24
|
-
from quraite.tracing.constants import
|
|
24
|
+
from quraite.tracing.constants import Framework
|
|
25
25
|
from quraite.tracing.trace import AgentSpan, AgentTrace
|
|
26
26
|
|
|
27
27
|
logger = get_logger(__name__)
|
|
@@ -228,16 +228,13 @@ class PydanticAIAdapter(BaseAdapter):
|
|
|
228
228
|
message_history: Any | None,
|
|
229
229
|
) -> AgentInvocationResponse:
|
|
230
230
|
"""Execute ainvoke with tracing enabled."""
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
)
|
|
239
|
-
|
|
240
|
-
with self.tracer.start_as_current_span(name=adapter_trace_id):
|
|
231
|
+
with self.tracer.start_as_current_span("pydantic_invocation") as span:
|
|
232
|
+
trace_id = span.get_span_context().trace_id
|
|
233
|
+
logger.debug(
|
|
234
|
+
"Starting Pydantic AI traced invocation (trace_id=%s session_id=%s)",
|
|
235
|
+
trace_id,
|
|
236
|
+
session_id,
|
|
237
|
+
)
|
|
241
238
|
# Run the agent asynchronously with message history for context
|
|
242
239
|
if message_history:
|
|
243
240
|
result = await self.agent.run(
|
|
@@ -250,8 +247,8 @@ class PydanticAIAdapter(BaseAdapter):
|
|
|
250
247
|
# Store the complete updated message history for this session
|
|
251
248
|
self._sessions[session_id] = result.all_messages()
|
|
252
249
|
|
|
253
|
-
trace_readable_spans = self.quraite_span_exporter.
|
|
254
|
-
|
|
250
|
+
trace_readable_spans = self.quraite_span_exporter.get_spans_by_trace_id(
|
|
251
|
+
trace_id
|
|
255
252
|
)
|
|
256
253
|
|
|
257
254
|
if trace_readable_spans:
|
|
@@ -264,12 +261,10 @@ class PydanticAIAdapter(BaseAdapter):
|
|
|
264
261
|
logger.info(
|
|
265
262
|
"Pydantic AI trace collected %d spans for trace_id=%s",
|
|
266
263
|
len(trace_readable_spans),
|
|
267
|
-
|
|
264
|
+
trace_id,
|
|
268
265
|
)
|
|
269
266
|
else:
|
|
270
|
-
logger.warning(
|
|
271
|
-
"No spans exported for Pydantic AI trace_id=%s", adapter_trace_id
|
|
272
|
-
)
|
|
267
|
+
logger.warning("No spans exported for Pydantic AI trace_id=%s", trace_id)
|
|
273
268
|
|
|
274
269
|
return AgentInvocationResponse(
|
|
275
270
|
agent_trace=agent_trace,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import uuid
|
|
3
2
|
from typing import List, Optional
|
|
4
3
|
|
|
5
4
|
from opentelemetry.trace import TracerProvider
|
|
@@ -9,7 +8,7 @@ from quraite.adapters.base import BaseAdapter
|
|
|
9
8
|
from quraite.logger import get_logger
|
|
10
9
|
from quraite.schema.message import AgentMessage
|
|
11
10
|
from quraite.schema.response import AgentInvocationResponse
|
|
12
|
-
from quraite.tracing.constants import
|
|
11
|
+
from quraite.tracing.constants import Framework
|
|
13
12
|
from quraite.tracing.trace import AgentSpan, AgentTrace
|
|
14
13
|
|
|
15
14
|
logger = get_logger(__name__)
|
|
@@ -114,24 +113,23 @@ class SmolagentsAdapter(BaseAdapter):
|
|
|
114
113
|
|
|
115
114
|
async def _ainvoke_with_tracing(self, agent_input: str) -> AgentInvocationResponse:
|
|
116
115
|
"""Execute ainvoke with tracing enabled."""
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
with self.tracer.start_as_current_span(name=adapter_trace_id):
|
|
116
|
+
with self.tracer.start_as_current_span("smolagents_invocation") as span:
|
|
117
|
+
trace_id = span.get_span_context().trace_id
|
|
118
|
+
logger.debug(
|
|
119
|
+
"Starting Smolagents traced invocation (trace_id=%s)", trace_id
|
|
120
|
+
)
|
|
123
121
|
# Run the agent synchronously inside a thread to avoid blocking
|
|
124
122
|
await asyncio.to_thread(self.agent.run, agent_input)
|
|
125
123
|
|
|
126
|
-
trace_readable_spans = self.quraite_span_exporter.
|
|
127
|
-
|
|
124
|
+
trace_readable_spans = self.quraite_span_exporter.get_spans_by_trace_id(
|
|
125
|
+
trace_id
|
|
128
126
|
)
|
|
129
127
|
|
|
130
128
|
if trace_readable_spans:
|
|
131
129
|
logger.info(
|
|
132
130
|
"Smolagents trace collected %d spans for trace_id=%s",
|
|
133
131
|
len(trace_readable_spans),
|
|
134
|
-
|
|
132
|
+
trace_id,
|
|
135
133
|
)
|
|
136
134
|
agent_trace = AgentTrace(
|
|
137
135
|
spans=[
|
|
@@ -140,9 +138,7 @@ class SmolagentsAdapter(BaseAdapter):
|
|
|
140
138
|
],
|
|
141
139
|
)
|
|
142
140
|
else:
|
|
143
|
-
logger.warning(
|
|
144
|
-
"No spans captured for Smolagents trace_id=%s", adapter_trace_id
|
|
145
|
-
)
|
|
141
|
+
logger.warning("No spans captured for Smolagents trace_id=%s", trace_id)
|
|
146
142
|
|
|
147
143
|
return AgentInvocationResponse(
|
|
148
144
|
agent_trace=agent_trace,
|
quraite/serve/local_agent.py
CHANGED
|
@@ -48,9 +48,9 @@ class LocalAgentServer:
|
|
|
48
48
|
Usage:
|
|
49
49
|
```python
|
|
50
50
|
from quraite.serve.local_agent_server import LocalAgentServer
|
|
51
|
-
from quraite.adapters import
|
|
51
|
+
from quraite.adapters import LangChainAdapter
|
|
52
52
|
|
|
53
|
-
sdk = LocalAgentServer(wrapped_agent=
|
|
53
|
+
sdk = LocalAgentServer(wrapped_agent=LangChainAdapter(agent_graph=agent_graph))
|
|
54
54
|
sdk.start(host="0.0.0.0", port=8000, reload=False)
|
|
55
55
|
```
|
|
56
56
|
"""
|
|
@@ -227,15 +227,16 @@ class LocalAgentServer:
|
|
|
227
227
|
|
|
228
228
|
logger.info("Local Agent Server started successfully")
|
|
229
229
|
if self.public_url:
|
|
230
|
-
|
|
230
|
+
path = "/v1/agents/completions"
|
|
231
|
+
logger.info(f"Agent publicly available at {self.public_url}{path}")
|
|
232
|
+
|
|
231
233
|
if not self.agent_id or not self._quraite_endpoint:
|
|
232
234
|
logger.info(
|
|
233
|
-
"Add this URL to your agent in the Quraite platform:
|
|
234
|
-
self.agent_url,
|
|
235
|
+
f"Add this URL to your agent in the Quraite platform: {self.public_url}{path}"
|
|
235
236
|
)
|
|
236
237
|
else:
|
|
237
238
|
logger.info(
|
|
238
|
-
"Agent running locally. Use a tunnel option to make it publicly available."
|
|
239
|
+
f"Agent running locally and available at {self.agent_url}{path}. Use a tunnel option to make it publicly available."
|
|
239
240
|
)
|
|
240
241
|
yield
|
|
241
242
|
|
quraite/tracing/__init__.py
CHANGED
quraite/tracing/constants.py
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
2
|
|
|
3
|
-
QURAITE_ADAPTER_TRACE_PREFIX = "quraite-adapter"
|
|
4
|
-
|
|
5
3
|
QURAITE_TRACER_NAME = "quraite.instrumentation"
|
|
6
4
|
|
|
7
5
|
|
|
8
6
|
class Framework(str, Enum):
|
|
9
7
|
"""Supported agent frameworks."""
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
DEFAULT = "default"
|
|
10
|
+
PYDANTIC_AI = "pydantic_ai"
|
|
11
|
+
LANGCHAIN = "langchain"
|
|
13
12
|
GOOGLE_ADK = "google_adk"
|
|
14
13
|
OPENAI_AGENTS = "openai_agents"
|
|
15
14
|
AGNO = "agno"
|
quraite/tracing/span_exporter.py
CHANGED
|
@@ -12,20 +12,9 @@ class QuraiteInMemorySpanExporter(SpanExporter):
|
|
|
12
12
|
def __init__(self) -> None:
|
|
13
13
|
# self.spans: typing.List[ReadableSpan] = []
|
|
14
14
|
self.traces: typing.Dict[int, typing.List[ReadableSpan]] = defaultdict(list)
|
|
15
|
-
self.testcase_to_trace: typing.Dict[str, int] = {}
|
|
16
|
-
|
|
17
15
|
self._stopped = False
|
|
18
16
|
self._lock = threading.Lock()
|
|
19
17
|
|
|
20
|
-
def handle_testcase_trace(self, span: ReadableSpan) -> None:
|
|
21
|
-
"""Handle a testcase trace."""
|
|
22
|
-
# print(f"🟢 testcase trace received: {span.name}")
|
|
23
|
-
# print(f"🟢 Span: {span.context}")
|
|
24
|
-
formatted_trace_id = format(span.context.trace_id, "032x")[:8]
|
|
25
|
-
# print(f"🟢 testcase formatted trace id: {formatted_trace_id}")
|
|
26
|
-
self.traces[formatted_trace_id] = []
|
|
27
|
-
self.testcase_to_trace[span.name] = formatted_trace_id
|
|
28
|
-
|
|
29
18
|
def export(self, spans: typing.Sequence[ReadableSpan]) -> SpanExportResult:
|
|
30
19
|
"""Stores a list of spans in memory."""
|
|
31
20
|
if self._stopped:
|
|
@@ -33,13 +22,16 @@ class QuraiteInMemorySpanExporter(SpanExporter):
|
|
|
33
22
|
|
|
34
23
|
with self._lock:
|
|
35
24
|
for span in spans:
|
|
36
|
-
formatted_trace_id = format(span.context.trace_id, "032x")[:8]
|
|
37
25
|
# print(f"🟢 span formatted context trace id: {formatted_trace_id}")
|
|
38
|
-
self.traces[
|
|
26
|
+
self.traces[span.context.trace_id].append(span)
|
|
39
27
|
# self.spans.append(span)
|
|
40
28
|
|
|
41
29
|
return SpanExportResult.SUCCESS
|
|
42
30
|
|
|
31
|
+
def get_spans_by_trace_id(self, trace_id: int) -> typing.List[ReadableSpan]:
|
|
32
|
+
"""Get all spans for a specific trace ID"""
|
|
33
|
+
return self.traces.get(trace_id, [])
|
|
34
|
+
|
|
43
35
|
def shutdown(self) -> None:
|
|
44
36
|
"""Shut downs the exporter.
|
|
45
37
|
|
|
@@ -55,11 +47,6 @@ class QuraiteInMemorySpanExporter(SpanExporter):
|
|
|
55
47
|
"""Get all spans grouped by trace ID"""
|
|
56
48
|
return dict(self.traces)
|
|
57
49
|
|
|
58
|
-
def get_trace_by_testcase(self, testcase_name: str) -> typing.List[ReadableSpan]:
|
|
59
|
-
"""Get all spans for a specific testcase"""
|
|
60
|
-
with self._lock:
|
|
61
|
-
return self.traces.get(self.testcase_to_trace.get(testcase_name, None), [])
|
|
62
|
-
|
|
63
50
|
def get_trace(self, trace_id: int) -> typing.List[ReadableSpan]:
|
|
64
51
|
"""Get all spans for a specific trace"""
|
|
65
52
|
return self.traces.get(trace_id, [])
|
|
@@ -75,7 +62,6 @@ class QuraiteInMemorySpanExporter(SpanExporter):
|
|
|
75
62
|
for trace_id, spans in self.traces.items():
|
|
76
63
|
print(f"Trace ID: {trace_id}...")
|
|
77
64
|
print(f"Spans in trace: {len(spans)}")
|
|
78
|
-
print(f"Total Testcases: {self.testcase_to_trace}")
|
|
79
65
|
print(f"TraceIDs: {self.traces.keys()}")
|
|
80
66
|
print(f"{'='*60}\n")
|
|
81
67
|
|
|
@@ -10,7 +10,6 @@ from opentelemetry.context import (
|
|
|
10
10
|
from opentelemetry.sdk.trace.export import ReadableSpan, SpanExporter, SpanProcessor
|
|
11
11
|
from opentelemetry.trace import Span, logger
|
|
12
12
|
|
|
13
|
-
from quraite.tracing.constants import QURAITE_ADAPTER_TRACE_PREFIX
|
|
14
13
|
from quraite.tracing.span_exporter import QuraiteInMemorySpanExporter
|
|
15
14
|
|
|
16
15
|
|
|
@@ -27,8 +26,7 @@ class QuraiteSimpleSpanProcessor(SpanProcessor):
|
|
|
27
26
|
def on_start(
|
|
28
27
|
self, span: Span, parent_context: typing.Optional[Context] = None
|
|
29
28
|
) -> None:
|
|
30
|
-
|
|
31
|
-
self.span_exporter.handle_testcase_trace(span)
|
|
29
|
+
pass
|
|
32
30
|
|
|
33
31
|
def on_end(self, span: ReadableSpan) -> None:
|
|
34
32
|
if not span.context.trace_flags.sampled:
|
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
Framework-specific tool extractors for converting span attributes to standardized tool call information.
|
|
3
3
|
|
|
4
4
|
These extractors handle the varying attribute structures across different agent frameworks
|
|
5
|
-
(pydantic,
|
|
5
|
+
(pydantic, langchain, adk, openai_agents, agno, smolagents, etc.)
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import json
|
|
9
9
|
from typing import Any, Protocol
|
|
10
10
|
|
|
11
|
+
from openinference.semconv.trace import SpanAttributes
|
|
12
|
+
|
|
11
13
|
from quraite.tracing.constants import Framework
|
|
12
14
|
|
|
13
15
|
|
|
@@ -75,9 +77,9 @@ def extract_tool_pydantic(span: dict[str, Any]) -> ToolCallInfo | None:
|
|
|
75
77
|
)
|
|
76
78
|
|
|
77
79
|
|
|
78
|
-
def
|
|
80
|
+
def extract_tool_langchain(span: dict[str, Any]) -> ToolCallInfo | None:
|
|
79
81
|
"""
|
|
80
|
-
Extract tool info from
|
|
82
|
+
Extract tool info from LangChain tool spans.
|
|
81
83
|
|
|
82
84
|
Attributes:
|
|
83
85
|
- tool.name: "add"
|
|
@@ -94,7 +96,7 @@ def extract_tool_langgraph(span: dict[str, Any]) -> ToolCallInfo | None:
|
|
|
94
96
|
arguments = attrs.get("input.value", "{}")
|
|
95
97
|
output_value = attrs.get("output.value", "")
|
|
96
98
|
|
|
97
|
-
# Also check for response attribute (some
|
|
99
|
+
# Also check for response attribute (some LangChain spans store response here)
|
|
98
100
|
response_value = attrs.get("response", output_value)
|
|
99
101
|
|
|
100
102
|
# Try to parse output to extract content
|
|
@@ -115,7 +117,7 @@ def extract_tool_langgraph(span: dict[str, Any]) -> ToolCallInfo | None:
|
|
|
115
117
|
# First check for direct content field
|
|
116
118
|
if "content" in parsed:
|
|
117
119
|
response = parsed.get("content", response_value)
|
|
118
|
-
# Check for update.messages structure (
|
|
120
|
+
# Check for update.messages structure (LangChain graph updates)
|
|
119
121
|
# this comes when you use supervisor agent with multiple agents
|
|
120
122
|
elif "update" in parsed:
|
|
121
123
|
update = parsed.get("update", {})
|
|
@@ -137,7 +139,7 @@ def extract_tool_langgraph(span: dict[str, Any]) -> ToolCallInfo | None:
|
|
|
137
139
|
|
|
138
140
|
return ToolCallInfo(
|
|
139
141
|
tool_name=tool_name,
|
|
140
|
-
tool_call_id=None, #
|
|
142
|
+
tool_call_id=None, # LangChain doesn't always have call IDs in tool spans
|
|
141
143
|
arguments=arguments,
|
|
142
144
|
response=response,
|
|
143
145
|
)
|
|
@@ -270,14 +272,33 @@ def extract_tool_smolagents(span: dict[str, Any]) -> ToolCallInfo | None:
|
|
|
270
272
|
)
|
|
271
273
|
|
|
272
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
|
+
|
|
273
293
|
# Registry of framework extractors
|
|
274
294
|
TOOL_EXTRACTORS: dict[Framework, ToolExtractor] = {
|
|
275
|
-
Framework.
|
|
276
|
-
Framework.
|
|
295
|
+
Framework.PYDANTIC_AI: extract_tool_pydantic,
|
|
296
|
+
Framework.LANGCHAIN: extract_tool_langchain,
|
|
277
297
|
Framework.GOOGLE_ADK: extract_tool_adk,
|
|
278
298
|
Framework.OPENAI_AGENTS: extract_tool_openai_agents,
|
|
279
299
|
Framework.AGNO: extract_tool_agno,
|
|
280
300
|
Framework.SMOLAGENTS: extract_tool_smolagents,
|
|
301
|
+
Framework.DEFAULT: extract_default,
|
|
281
302
|
}
|
|
282
303
|
|
|
283
304
|
|
|
@@ -285,6 +306,4 @@ def get_tool_extractor(framework: Framework | str) -> ToolExtractor:
|
|
|
285
306
|
"""Get the appropriate tool extractor for the given framework."""
|
|
286
307
|
if isinstance(framework, str):
|
|
287
308
|
framework = Framework(framework.lower())
|
|
288
|
-
return TOOL_EXTRACTORS.get(
|
|
289
|
-
framework, extract_tool_langgraph
|
|
290
|
-
) # Default to langgraph
|
|
309
|
+
return TOOL_EXTRACTORS.get(framework, extract_default)
|
quraite/tracing/trace.py
CHANGED
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
|
|
4
4
|
import json
|
|
5
5
|
from functools import cached_property
|
|
6
|
-
from typing import Any, List
|
|
6
|
+
from typing import Any, List, Optional
|
|
7
7
|
|
|
8
8
|
from openinference.semconv.trace import OpenInferenceSpanKindValues, SpanAttributes
|
|
9
9
|
from opentelemetry.sdk.trace import ReadableSpan
|
|
@@ -185,7 +185,7 @@ class AgentSpan(BaseModel):
|
|
|
185
185
|
return cleaned_messages
|
|
186
186
|
|
|
187
187
|
def to_tool_message(
|
|
188
|
-
self, framework: Framework = Framework.
|
|
188
|
+
self, framework: Framework = Framework.LANGCHAIN
|
|
189
189
|
) -> dict[str, Any] | None:
|
|
190
190
|
"""
|
|
191
191
|
Convert tool execution span to a tool message.
|
|
@@ -405,8 +405,8 @@ class AgentTrace(BaseModel):
|
|
|
405
405
|
|
|
406
406
|
def to_agent_trajectory(
|
|
407
407
|
self,
|
|
408
|
-
framework: Framework = Framework.
|
|
409
|
-
only_leaf_llms: bool = True,
|
|
408
|
+
framework: Optional[Framework] = Framework.DEFAULT,
|
|
409
|
+
only_leaf_llms: Optional[bool] = True,
|
|
410
410
|
) -> List[AssistantMessage | ToolMessage]:
|
|
411
411
|
"""
|
|
412
412
|
Convert trace spans to agent trajectory.
|
|
@@ -422,9 +422,9 @@ class AgentTrace(BaseModel):
|
|
|
422
422
|
# - Some frameworks (pydantic_ai, agno) execute tools DURING the LLM span
|
|
423
423
|
# (tools are nested inside LLM), so we sort by end_time to get correct order
|
|
424
424
|
# This means the parent span does not end before the nested span ends.
|
|
425
|
-
# - Other frameworks (
|
|
425
|
+
# - Other frameworks (langchain, openai_agents, etc.) execute tools AFTER the LLM span
|
|
426
426
|
# ends, so start_time sort works fine
|
|
427
|
-
nested_tool_frameworks = (Framework.
|
|
427
|
+
nested_tool_frameworks = (Framework.PYDANTIC_AI, Framework.AGNO)
|
|
428
428
|
|
|
429
429
|
if framework in nested_tool_frameworks:
|
|
430
430
|
# Sort by end_time: LLM output is ready at end_time, then tool results follow
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: quraite
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: This project provides adaptors and methods to integrate with the Quraite platform
|
|
5
5
|
Author: Shiv Mohith
|
|
6
6
|
Author-email: Shiv Mohith <shivmohith8@gmail.com>
|
|
@@ -16,8 +16,8 @@ Requires-Dist: uvicorn>=0.38.0
|
|
|
16
16
|
Requires-Dist: agno>=2.3.4 ; extra == 'agno'
|
|
17
17
|
Requires-Dist: boto3>=1.40.70 ; extra == 'bedrock'
|
|
18
18
|
Requires-Dist: google-adk>=1.18.0 ; extra == 'google-adk'
|
|
19
|
-
Requires-Dist: langchain>=1.0.5 ; extra == '
|
|
20
|
-
Requires-Dist: langgraph>=1.0.3 ; extra == '
|
|
19
|
+
Requires-Dist: langchain>=1.0.5 ; extra == 'langchain'
|
|
20
|
+
Requires-Dist: langgraph>=1.0.3 ; extra == 'langchain'
|
|
21
21
|
Requires-Dist: openai-agents>=0.5.0 ; extra == 'openai-agents'
|
|
22
22
|
Requires-Dist: pydantic-ai>=1.25.0 ; extra == 'pydantic-ai'
|
|
23
23
|
Requires-Dist: pyngrok>=7.5.0 ; extra == 'pyngrok'
|
|
@@ -26,7 +26,7 @@ Requires-Python: >=3.10
|
|
|
26
26
|
Provides-Extra: agno
|
|
27
27
|
Provides-Extra: bedrock
|
|
28
28
|
Provides-Extra: google-adk
|
|
29
|
-
Provides-Extra:
|
|
29
|
+
Provides-Extra: langchain
|
|
30
30
|
Provides-Extra: openai-agents
|
|
31
31
|
Provides-Extra: pydantic-ai
|
|
32
32
|
Provides-Extra: pyngrok
|
|
@@ -37,16 +37,15 @@ Description-Content-Type: text/markdown
|
|
|
37
37
|
|
|
38
38
|
[](https://www.python.org/downloads/)
|
|
39
39
|
[](LICENSE)
|
|
40
|
+
[](https://opentelemetry.io/)
|
|
40
41
|
|
|
41
|
-
The **Quraite Python SDK** provides adapters and methods to integrate AI agent
|
|
42
|
+
The **Quraite Python SDK** provides adapters and methods to integrate AI agent with the [Quraite platform](https://quraite.ai) for evaluation. It offers a unified interface for different agent frameworks, automatic tracing at every turn for agent trajectory evaluation, and easy local server setup with tunneling capabilities.
|
|
42
43
|
|
|
43
44
|
## Features
|
|
44
45
|
|
|
45
|
-
- 🔌 **Framework Adapters**: Support for multiple AI agent frameworks (
|
|
46
|
-
- 📊 **Automatic Tracing**: Built-in OpenTelemetry-based tracing for agent
|
|
47
|
-
- 🚀 **Local Server**: Easy-to-use local server with optional tunneling (Cloudflare/ngrok) for public access
|
|
48
|
-
- 📦 **Standardized Schema**: Unified message and response formats across all frameworks
|
|
49
|
-
- 🔍 **Observability**: Track token usage, costs, latency, and model information for each agent invocation
|
|
46
|
+
- 🔌 **Framework Adapters**: Support for multiple AI agent frameworks (LangChain, Pydantic AI, Agno, Google ADK, OpenAI Agents, Smolagents, AWS Bedrock, Flowise, Langflow, N8n, and more)
|
|
47
|
+
- 📊 **Automatic Tracing**: Built-in OpenInference-based (OpenTelemetry-based tracing support coming soon) tracing for agent trajectory evaluation. Track token usage, costs, latency, and model information for each agent invocation
|
|
48
|
+
- 🚀 **Local Server**: Easy-to-use local server with optional tunneling (Cloudflare/ngrok) for public access and integration with Quraite platform
|
|
50
49
|
|
|
51
50
|
## Installation
|
|
52
51
|
|
|
@@ -61,8 +60,8 @@ pip install quraite
|
|
|
61
60
|
Install with optional dependencies for specific frameworks:
|
|
62
61
|
|
|
63
62
|
```bash
|
|
64
|
-
#
|
|
65
|
-
pip install 'quraite[
|
|
63
|
+
# LangChain
|
|
64
|
+
pip install 'quraite[langchain]'
|
|
66
65
|
|
|
67
66
|
# Pydantic AI
|
|
68
67
|
pip install 'quraite[pydantic-ai]'
|
|
@@ -83,42 +82,45 @@ pip install 'quraite[smolagents]'
|
|
|
83
82
|
pip install 'quraite[bedrock]'
|
|
84
83
|
|
|
85
84
|
# Multiple frameworks
|
|
86
|
-
pip install 'quraite[
|
|
85
|
+
pip install 'quraite[langchain,pydantic-ai,agno]'
|
|
87
86
|
```
|
|
88
87
|
|
|
89
88
|
## Quick Start
|
|
90
89
|
|
|
91
|
-
### Example:
|
|
90
|
+
### Example: LangChain Agent with Local Server
|
|
92
91
|
|
|
93
|
-
Pass your compiled
|
|
92
|
+
Pass your compiled LangChain agent to the adapter and expose it as an HTTP API:
|
|
94
93
|
|
|
95
94
|
```python
|
|
95
|
+
import asyncio
|
|
96
96
|
import uvicorn
|
|
97
97
|
from dotenv import load_dotenv
|
|
98
98
|
from openinference.instrumentation import TracerProvider
|
|
99
99
|
from openinference.instrumentation.langchain import LangChainInstrumentor
|
|
100
100
|
|
|
101
|
-
from quraite.adapters import
|
|
101
|
+
from quraite.adapters import LangChainAdapter
|
|
102
102
|
from quraite.serve.local_agent import LocalAgentServer
|
|
103
103
|
from quraite.tracing.span_exporter import QuraiteInMemorySpanExporter
|
|
104
104
|
from quraite.tracing.span_processor import QuraiteSimpleSpanProcessor
|
|
105
105
|
|
|
106
106
|
load_dotenv()
|
|
107
107
|
|
|
108
|
-
# Set up tracing
|
|
108
|
+
# Set up tracing
|
|
109
|
+
# Use Quraite's in-memory span exporter to capture the agent trajectory
|
|
110
|
+
# and use it for evaluation.
|
|
109
111
|
tracer_provider = TracerProvider()
|
|
110
112
|
quraite_span_exporter = QuraiteInMemorySpanExporter()
|
|
111
113
|
quraite_span_processor = QuraiteSimpleSpanProcessor(quraite_span_exporter)
|
|
112
114
|
tracer_provider.add_span_processor(quraite_span_processor)
|
|
113
115
|
LangChainInstrumentor().instrument(tracer_provider=tracer_provider)
|
|
114
116
|
|
|
115
|
-
# Your compiled
|
|
117
|
+
# Your compiled LangChain agent (created elsewhere)
|
|
116
118
|
# agent = create_agent(...)
|
|
117
119
|
|
|
118
120
|
# Wrap with Quraite adapter
|
|
119
|
-
adapter =
|
|
120
|
-
agent_graph=agent, # Pass your compiled
|
|
121
|
-
tracer_provider=tracer_provider,
|
|
121
|
+
adapter = LangChainAdapter(
|
|
122
|
+
agent_graph=agent, # Pass your compiled LangChain agent here
|
|
123
|
+
tracer_provider=tracer_provider,
|
|
122
124
|
)
|
|
123
125
|
|
|
124
126
|
# Create and start server with Cloudflare tunnel
|
|
@@ -133,14 +135,18 @@ app = server.create_app(
|
|
|
133
135
|
tunnel="cloudflare", # Options: "cloudflare", "ngrok", or "none"
|
|
134
136
|
)
|
|
135
137
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
+
# Option 1: Use the start method to start the server
|
|
139
|
+
asyncio.run(server.start(host="0.0.0.0", port=8080))
|
|
140
|
+
|
|
141
|
+
# Option 2: Use uvicorn to start the server for auto-reload
|
|
142
|
+
# if __name__ == "__main__":
|
|
143
|
+
# uvicorn.run("local_server:app", host="0.0.0.0", port=8080, reload=True)
|
|
138
144
|
```
|
|
139
145
|
|
|
140
146
|
The server exposes:
|
|
141
147
|
|
|
142
148
|
- `GET /` - Health check endpoint
|
|
143
|
-
- `POST /v1/agents/completions` - Agent invocation endpoint
|
|
149
|
+
- `POST /v1/agents/completions` - Agent invocation endpoint. This is the endpoint that Quraite will use to invoke your agent.
|
|
144
150
|
|
|
145
151
|
When using `tunnel="cloudflare"` or `tunnel="ngrok"`, your agent will be publicly accessible via the generated URL.
|
|
146
152
|
|
|
@@ -148,7 +154,7 @@ When using `tunnel="cloudflare"` or `tunnel="ngrok"`, your agent will be publicl
|
|
|
148
154
|
|
|
149
155
|
| Framework | Adapter | Installation |
|
|
150
156
|
| -------------------- | ------------------------ | -------------------------------------- |
|
|
151
|
-
| **
|
|
157
|
+
| **LangChain** | `LangChainAdapter` | `pip install 'quraite[langchain]'` |
|
|
152
158
|
| **Pydantic AI** | `PydanticAIAdapter` | `pip install 'quraite[pydantic-ai]'` |
|
|
153
159
|
| **Agno** | `AgnoAdapter` | `pip install 'quraite[agno]'` |
|
|
154
160
|
| **Google ADK** | `GoogleADKAdapter` | `pip install 'quraite[google-adk]'` |
|
|
@@ -159,27 +165,28 @@ When using `tunnel="cloudflare"` or `tunnel="ngrok"`, your agent will be publicl
|
|
|
159
165
|
| **Langflow** | `LangflowAdapter` | Included in base package |
|
|
160
166
|
| **N8n** | `N8nAdapter` | Included in base package |
|
|
161
167
|
| **HTTP** | `HttpAdapter` | Included in base package |
|
|
162
|
-
| **
|
|
168
|
+
| **LangChain Server** | `LangChainServerAdapter` | `pip install 'quraite[langchain]'` |
|
|
163
169
|
|
|
164
170
|
## Core Concepts
|
|
165
171
|
|
|
166
172
|
### Adapters
|
|
167
173
|
|
|
168
|
-
Adapters provide a unified interface (`BaseAdapter`) for different agent frameworks. Each adapter
|
|
174
|
+
Adapters provide a unified interface (`BaseAdapter`) for different agent frameworks. Each adapter converts framework-specific agent response formats to the Quraite agent message format.
|
|
175
|
+
|
|
176
|
+
If you are building your own agent framework, you can create a custom adapter by extending the `BaseAdapter` class and implementing the `ainvoke` method.
|
|
177
|
+
|
|
178
|
+
### Tracing for Agent Trajectory Evaluation
|
|
169
179
|
|
|
170
|
-
|
|
171
|
-
- Handles message format conversion
|
|
172
|
-
- Supports optional tracing integration
|
|
173
|
-
- Provides async invocation via `ainvoke()`
|
|
180
|
+
**Capture agent trajectories without modifying your code.** Get comprehensive trace data including token usage, costs, and latency for every agent step.
|
|
174
181
|
|
|
175
|
-
|
|
182
|
+
Most agent frameworks return agent steps, but lack critical observability data. We solve this with **OpenInference instrumentation** (OpenTelemetry instrumentation support coming soon) that automatically captures:
|
|
176
183
|
|
|
177
|
-
|
|
184
|
+
- Complete agent trajectories
|
|
185
|
+
- Token usage and costs
|
|
186
|
+
- Step-by-step latency
|
|
187
|
+
- Full execution context
|
|
178
188
|
|
|
179
|
-
-
|
|
180
|
-
- **Tool Calls**: Tool invocations with inputs and outputs
|
|
181
|
-
- **Performance Metrics**: Token usage, costs, latency
|
|
182
|
-
- **Model Information**: Model name and provider details
|
|
189
|
+
**Works with your existing setup.** We provide OpenInference-compatible span exporters and processors that integrate seamlessly with your current observability platform - no vendor lock-in required.
|
|
183
190
|
|
|
184
191
|
To enable tracing:
|
|
185
192
|
|
|
@@ -189,11 +196,13 @@ from quraite.tracing.span_exporter import QuraiteInMemorySpanExporter
|
|
|
189
196
|
from quraite.tracing.span_processor import QuraiteSimpleSpanProcessor
|
|
190
197
|
|
|
191
198
|
tracer_provider = TracerProvider()
|
|
199
|
+
|
|
200
|
+
# Add Quraite span exporter and processor to the tracer provider
|
|
192
201
|
quraite_span_exporter = QuraiteInMemorySpanExporter()
|
|
193
202
|
quraite_span_processor = QuraiteSimpleSpanProcessor(quraite_span_exporter)
|
|
194
203
|
tracer_provider.add_span_processor(quraite_span_processor)
|
|
195
204
|
|
|
196
|
-
# Instrument your framework
|
|
205
|
+
# Instrument your framework with OpenInference
|
|
197
206
|
from openinference.instrumentation.langchain import LangChainInstrumentor
|
|
198
207
|
LangChainInstrumentor().instrument(tracer_provider=tracer_provider)
|
|
199
208
|
```
|
|
@@ -251,18 +260,15 @@ response: AgentInvocationResponse = await adapter.ainvoke(
|
|
|
251
260
|
# Access trajectory (list of messages)
|
|
252
261
|
trajectory = response.agent_trajectory
|
|
253
262
|
|
|
254
|
-
# Access trace
|
|
263
|
+
# Access trace
|
|
255
264
|
trace = response.agent_trace
|
|
256
|
-
|
|
257
|
-
# Access final response text
|
|
258
|
-
final_response = response.agent_final_response
|
|
259
265
|
```
|
|
260
266
|
|
|
261
267
|
## Examples
|
|
262
268
|
|
|
263
269
|
The repository includes comprehensive examples for each supported framework:
|
|
264
270
|
|
|
265
|
-
- [`
|
|
271
|
+
- [`langchain_calculator_agent`](examples/langchain_calculator_agent/) - LangChain calculator agent
|
|
266
272
|
- [`pydantic_calculator_agent`](examples/pydantic_calculator_agent/) - Pydantic AI calculator agent
|
|
267
273
|
- [`agno_calculator_agent`](examples/agno_calculator_agent/) - Agno calculator agent
|
|
268
274
|
- [`google_adk_weather_agent`](examples/google_adk_weather_agent/) - Google ADK weather agent
|
|
@@ -302,6 +308,7 @@ class MyAdapter(BaseAdapter):
|
|
|
302
308
|
Create a local HTTP server for your agent:
|
|
303
309
|
|
|
304
310
|
```python
|
|
311
|
+
import asyncio
|
|
305
312
|
from quraite.serve.local_agent import LocalAgentServer
|
|
306
313
|
|
|
307
314
|
server = LocalAgentServer(
|
|
@@ -314,6 +321,8 @@ app = server.create_app(
|
|
|
314
321
|
host="0.0.0.0",
|
|
315
322
|
tunnel="cloudflare", # or "ngrok" or "none"
|
|
316
323
|
)
|
|
324
|
+
|
|
325
|
+
asyncio.run(server.start(host="0.0.0.0", port=8080))
|
|
317
326
|
```
|
|
318
327
|
|
|
319
328
|
## Development
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
quraite/__init__.py,sha256=vxU3HsbCj6AT_rl5bZJA-e3R_bzqLhMGCe96_KM4Biw,48
|
|
2
|
-
quraite/adapters/__init__.py,sha256
|
|
3
|
-
quraite/adapters/agno_adapter.py,sha256=
|
|
4
|
-
quraite/adapters/base.py,sha256=
|
|
2
|
+
quraite/adapters/__init__.py,sha256=-2FPY_cUHtIKVlB7O3SUkgrEp1iybJ1z3VM52SjYk3I,4842
|
|
3
|
+
quraite/adapters/agno_adapter.py,sha256=mZukC0ItxhhreHQB4V2xPDXJ7JUXpEG60rtj25_iRdg,5572
|
|
4
|
+
quraite/adapters/base.py,sha256=7wRwmcjLg73cec_VKk2jwwPLyDRiutgtCuREnpgyVYw,3928
|
|
5
5
|
quraite/adapters/bedrock_agents_adapter.py,sha256=2G-zUXqhNSzpyxut2UQgqTfe01MWcdjt1ajZIb43CUM,14136
|
|
6
6
|
quraite/adapters/flowise_adapter.py,sha256=yLJ0tjDWMtTzm0bRYbhjfRm6wd5A6DqbHhqemy_FW48,10349
|
|
7
|
-
quraite/adapters/google_adk_adapter.py,sha256=
|
|
8
|
-
quraite/adapters/http_adapter.py,sha256=
|
|
7
|
+
quraite/adapters/google_adk_adapter.py,sha256=ObVojr9sChciayYaZzrCwXUk4kjqdEMMRm4ObYqPmcE,7433
|
|
8
|
+
quraite/adapters/http_adapter.py,sha256=HY9qRj_enxED73TaEIPN06l9ooqkLjBiPHssBdMPLJM,7990
|
|
9
|
+
quraite/adapters/langchain_adapter.py,sha256=PnJUMUVMJptoOE13gIMYkbq3h3ZF1ihEEYroWZQzuSs,11581
|
|
10
|
+
quraite/adapters/langchain_server_adapter.py,sha256=t5wUZrpIRtPN-CbRK3-I0yFfdTpvsbqFzZgrrvkleLQ,9694
|
|
9
11
|
quraite/adapters/langflow_adapter.py,sha256=zGbDSd7Q_AZZ0ICByedsBIl5yNJ0hN6tQZgpUyN41-E,6946
|
|
10
|
-
quraite/adapters/langgraph_adapter.py,sha256=WqmbP6Zw1lwfSCBf_O7oKNoGEb_1-zw_PMS1v71i3-w,11603
|
|
11
|
-
quraite/adapters/langgraph_server_adapter.py,sha256=OhMx370Uzrvbp4yMOtgKkjQFI5u1xboKq0ybQRsBD90,9694
|
|
12
12
|
quraite/adapters/n8n_adapter.py,sha256=CCIOcZNxQw-69xdf9BVX9Jyt8i666AlsE2DJHsY8lUc,7967
|
|
13
|
-
quraite/adapters/openai_agents_adapter.py,sha256=
|
|
14
|
-
quraite/adapters/pydantic_ai_adapter.py,sha256=
|
|
15
|
-
quraite/adapters/smolagents_adapter.py,sha256=
|
|
13
|
+
quraite/adapters/openai_agents_adapter.py,sha256=J404wIy9OyUOB0wTtEy6RIiKijMfQk1oLxu1vC7Je5U,9595
|
|
14
|
+
quraite/adapters/pydantic_ai_adapter.py,sha256=BeP0En6gHxszrwwnpM0NJJ7dtkJTB3YNYG4EktJAL0w,10698
|
|
15
|
+
quraite/adapters/smolagents_adapter.py,sha256=rFnHkF4oHOWQRq2nfIluwWEQwxFk2xbFfJupiu90L-k,5345
|
|
16
16
|
quraite/logger.py,sha256=Py5GRQfD_s5FVb2Ziz5JxJk9AT4ve6w_iePbOSQkwH4,1601
|
|
17
17
|
quraite/schema/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
18
|
quraite/schema/message.py,sha256=D6HWaZJOcoKObzDD-vo4Qg-chJDyOSwC-9XiNv_0xFc,2416
|
|
19
19
|
quraite/schema/response.py,sha256=HUD_TyU6nibfXgRzdpD9LGoRtptD663oq7INy1sqaS4,412
|
|
20
20
|
quraite/serve/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
21
21
|
quraite/serve/cloudflared.py,sha256=7XR1tPl0E3Feawo-6VCM8SXfCAie4cw9T1mx-ZNVJKw,6482
|
|
22
|
-
quraite/serve/local_agent.py,sha256=
|
|
22
|
+
quraite/serve/local_agent.py,sha256=G398f-kqzPjFEaaFuh4i-Y0qtrimP0SLSO7kyAPxcqU,12921
|
|
23
23
|
quraite/traces/traces_adk_openinference.json,sha256=QjXAxMUEdSBcA5pCMOPiDU2_0NDsctEP6IFB6BIps9U,35957
|
|
24
24
|
quraite/traces/traces_agno_multi_agent.json,sha256=FEW_zkSCKYizKG40KNvfXuouXsmOTdsPC7nnXWaKLfE,65098
|
|
25
25
|
quraite/traces/traces_agno_openinference.json,sha256=p_tyBAp9KaT23PqGBU1cSMli7By0vss9Vpm3tL4pTMM,36871
|
|
@@ -34,16 +34,16 @@ quraite/traces/traces_pydantic_openinference_multi_agent_1.json,sha256=FjiPOvbhz
|
|
|
34
34
|
quraite/traces/traces_pydantic_openinference_multi_agent_2.json,sha256=bV3z56JmzoLmxdDvL4RE9opgq3kFnMxNJFD5D4CbDtw,24751
|
|
35
35
|
quraite/traces/traces_smol_agents_openinference.json,sha256=JVgRniA-VCuQ0oKWSqW0d09K4RcRFoOq5pcbejBigAc,83161
|
|
36
36
|
quraite/traces/traces_smol_agents_tool_calling_openinference.json,sha256=QnKnCBukoNyR8ufTPLbjpWBFSguBRSUyd0-zLMNL2sw,217626
|
|
37
|
-
quraite/tracing/__init__.py,sha256=
|
|
38
|
-
quraite/tracing/constants.py,sha256=
|
|
39
|
-
quraite/tracing/span_exporter.py,sha256=
|
|
40
|
-
quraite/tracing/span_processor.py,sha256=
|
|
41
|
-
quraite/tracing/tool_extractors.py,sha256=
|
|
42
|
-
quraite/tracing/trace.py,sha256=
|
|
37
|
+
quraite/tracing/__init__.py,sha256=uDML55hjgQ8W2thaAcZFFooy4aEIExxKzeBFXJGVPm4,763
|
|
38
|
+
quraite/tracing/constants.py,sha256=W2z9RsDMvXfCpyhhlm_5E0EVjw-a9GGNq6wHliM3A_g,338
|
|
39
|
+
quraite/tracing/span_exporter.py,sha256=Y5bJDwNeV62s3UIAykOSvTC3eOSuwYH9j0L4sxhZK-I,3940
|
|
40
|
+
quraite/tracing/span_processor.py,sha256=GKxZGL5D71YVGukns2-tpnng8n1Ds8M1w3D7FIBETZg,1436
|
|
41
|
+
quraite/tracing/tool_extractors.py,sha256=6YuCcb_aL_lyRJAp_A5-kZVK29ARU73mqjmiJnjtbGU,9800
|
|
42
|
+
quraite/tracing/trace.py,sha256=wumQwANCw9KY_pMDjS-10EO8lyO8qHxKZ02Dnq74WgI,21677
|
|
43
43
|
quraite/tracing/types.py,sha256=sWyp-7Qc8zsWN6ltem8OSTRIgKWbqnQSfsTKX6RrrB8,4948
|
|
44
44
|
quraite/tracing/utils.py,sha256=Vm5q_iN_K1f_nEMpkQzv6jiUPpS2Yzo_VP5ZVmJZdBQ,5673
|
|
45
45
|
quraite/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
46
46
|
quraite/utils/json_utils.py,sha256=RewoObREfHnLBVI6MBCIEov6CaIxs8d67jbKEH79mQ0,18686
|
|
47
|
-
quraite-0.1.
|
|
48
|
-
quraite-0.1.
|
|
49
|
-
quraite-0.1.
|
|
47
|
+
quraite-0.1.3.dist-info/WHEEL,sha256=5w2T7AS2mz1-rW9CNagNYWRCaB0iQqBMYLwKdlgiR4Q,78
|
|
48
|
+
quraite-0.1.3.dist-info/METADATA,sha256=82tXqkmPtK4XJcZFNI_fZuBIZjLFWJHoCX2kFJB02TQ,12063
|
|
49
|
+
quraite-0.1.3.dist-info/RECORD,,
|
|
File without changes
|