quraite 0.1.3__py3-none-any.whl → 0.1.4__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.
Files changed (49) hide show
  1. quraite/__init__.py +4 -0
  2. quraite/adapters/agno_adapter.py +50 -92
  3. quraite/adapters/base.py +26 -76
  4. quraite/adapters/bedrock_agents_adapter.py +23 -76
  5. quraite/adapters/flowise_adapter.py +31 -72
  6. quraite/adapters/google_adk_adapter.py +28 -94
  7. quraite/adapters/http_adapter.py +28 -44
  8. quraite/adapters/langchain_adapter.py +51 -118
  9. quraite/adapters/langchain_server_adapter.py +37 -89
  10. quraite/adapters/langflow_adapter.py +15 -60
  11. quraite/adapters/n8n_adapter.py +19 -63
  12. quraite/adapters/openai_agents_adapter.py +35 -59
  13. quraite/adapters/pydantic_ai_adapter.py +27 -97
  14. quraite/adapters/smolagents_adapter.py +21 -82
  15. quraite/constants/framework.py +14 -0
  16. quraite/schema/__init__.py +4 -0
  17. quraite/schema/invoke.py +46 -0
  18. quraite/schema/message.py +20 -21
  19. quraite/serve/__init__.py +4 -0
  20. quraite/serve/cloudflared.py +3 -2
  21. quraite/serve/server.py +305 -0
  22. quraite/tracing/__init__.py +8 -5
  23. quraite/tracing/constants.py +0 -14
  24. quraite/tracing/setup.py +129 -0
  25. quraite/tracing/span_exporter.py +6 -6
  26. quraite/tracing/span_processor.py +6 -7
  27. quraite/tracing/tool_extractors.py +1 -1
  28. quraite/tracing/trace.py +36 -24
  29. quraite/utils/json_utils.py +2 -2
  30. {quraite-0.1.3.dist-info → quraite-0.1.4.dist-info}/METADATA +54 -62
  31. quraite-0.1.4.dist-info/RECORD +37 -0
  32. quraite/schema/response.py +0 -16
  33. quraite/serve/local_agent.py +0 -361
  34. quraite/traces/traces_adk_openinference.json +0 -379
  35. quraite/traces/traces_agno_multi_agent.json +0 -669
  36. quraite/traces/traces_agno_openinference.json +0 -321
  37. quraite/traces/traces_crewai_openinference.json +0 -155
  38. quraite/traces/traces_langgraph_openinference.json +0 -349
  39. quraite/traces/traces_langgraph_openinference_multi_agent.json +0 -2705
  40. quraite/traces/traces_langgraph_traceloop.json +0 -510
  41. quraite/traces/traces_openai_agents_multi_agent_1.json +0 -402
  42. quraite/traces/traces_openai_agents_openinference.json +0 -341
  43. quraite/traces/traces_pydantic_openinference.json +0 -286
  44. quraite/traces/traces_pydantic_openinference_multi_agent_1.json +0 -399
  45. quraite/traces/traces_pydantic_openinference_multi_agent_2.json +0 -398
  46. quraite/traces/traces_smol_agents_openinference.json +0 -397
  47. quraite/traces/traces_smol_agents_tool_calling_openinference.json +0 -704
  48. quraite-0.1.3.dist-info/RECORD +0 -49
  49. {quraite-0.1.3.dist-info → quraite-0.1.4.dist-info}/WHEEL +0 -0
@@ -1,12 +1,13 @@
1
1
  import asyncio
2
2
  import json
3
3
  import uuid
4
- from typing import Any, Dict, List, Optional, Union
4
+ from typing import Any
5
5
 
6
6
  import aiohttp
7
7
 
8
8
  from quraite.adapters.base import BaseAdapter
9
9
  from quraite.logger import get_logger
10
+ from quraite.schema.invoke import InvokeInput, InvokeOutput
10
11
  from quraite.schema.message import (
11
12
  AgentMessage,
12
13
  AssistantMessage,
@@ -14,14 +15,13 @@ from quraite.schema.message import (
14
15
  ToolCall,
15
16
  ToolMessage,
16
17
  )
17
- from quraite.schema.response import AgentInvocationResponse
18
18
 
19
19
  logger = get_logger(__name__)
20
20
 
21
21
 
22
22
  class N8nAdapter(BaseAdapter):
23
23
  def __init__(
24
- self, api_url: str, headers: Optional[Dict[str, str]] = None, timeout: int = 60
24
+ self, api_url: str, headers: dict[str, str] | None = None, timeout: int = 60
25
25
  ):
26
26
  self.api_url = api_url
27
27
  self.headers = headers or {}
@@ -35,13 +35,13 @@ class N8nAdapter(BaseAdapter):
35
35
 
36
36
  def _convert_api_output_to_messages(
37
37
  self,
38
- response: Dict[str, Any],
39
- ) -> List[AgentMessage]:
38
+ response: dict[str, Any],
39
+ ) -> list[AgentMessage]:
40
40
  logger.debug(
41
41
  "Converting n8n response (steps=%d)",
42
42
  len(response[0].get("intermediateSteps", [])),
43
43
  )
44
- messages: List[AgentMessage] = []
44
+ messages: list[AgentMessage] = []
45
45
  output = response[0]["output"]
46
46
  intermediateSteps = response[0]["intermediateSteps"]
47
47
 
@@ -52,10 +52,10 @@ class N8nAdapter(BaseAdapter):
52
52
  )
53
53
  ]
54
54
 
55
- def flush_messages(tool_calls_dict: Dict[str, Any]):
55
+ def flush_messages(tool_calls_dict: dict[str, Any]):
56
56
  nonlocal messages
57
- tool_calls_list: List[ToolCall] = []
58
- tool_results: List[ToolMessage] = []
57
+ tool_calls_list: list[ToolCall] = []
58
+ tool_results: list[ToolMessage] = []
59
59
 
60
60
  for tool_call_id, tool_call_dict in tool_calls_dict.items():
61
61
  tool_name = tool_call_dict.get("name", "")
@@ -87,7 +87,7 @@ class N8nAdapter(BaseAdapter):
87
87
 
88
88
  messages.extend(tool_results)
89
89
 
90
- current_step_tool_calls_dict: Dict[str, Any] = {}
90
+ current_step_tool_calls_dict: dict[str, Any] = {}
91
91
  for step in intermediateSteps:
92
92
  message_log = step.get("action", {}).get("messageLog", {})
93
93
  if message_log:
@@ -127,35 +127,11 @@ class N8nAdapter(BaseAdapter):
127
127
  )
128
128
  return messages
129
129
 
130
- def _prepare_input(self, input: List[AgentMessage]) -> str:
131
- logger.debug("Preparing n8n input from %d messages", len(input))
132
- if not input or input[-1].role != "user":
133
- logger.error("n8n input missing user message")
134
- raise ValueError("No user message found in the input")
135
-
136
- last_user_message = input[-1]
137
- if not last_user_message.content:
138
- logger.error("n8n user message missing content")
139
- raise ValueError("User message has no content")
140
-
141
- text_content = None
142
- for content_item in last_user_message.content:
143
- if content_item.type == "text" and content_item.text:
144
- text_content = content_item.text
145
- break
146
-
147
- if not text_content:
148
- logger.error("n8n user message missing text content")
149
- raise ValueError("No text content found in user message")
150
-
151
- logger.debug("Prepared n8n input (text_length=%d)", len(text_content))
152
- return text_content
153
-
154
130
  async def _aapi_call(
155
131
  self,
156
132
  query: str,
157
133
  sessionId: str,
158
- ) -> Dict[str, Any]:
134
+ ) -> dict[str, Any]:
159
135
  payload = {
160
136
  "query": query,
161
137
  "sessionId": sessionId,
@@ -183,38 +159,18 @@ class N8nAdapter(BaseAdapter):
183
159
  logger.exception("n8n API response decoding failed")
184
160
  raise ValueError(f"Failed to decode JSON response: {e}") from e
185
161
 
186
- async def ainvoke(
187
- self,
188
- input: List[AgentMessage],
189
- session_id: Union[str, None],
190
- ) -> AgentInvocationResponse:
191
- logger.info(
192
- "n8n ainvoke called (session_id=%s, input_messages=%d)",
193
- session_id,
194
- len(input),
195
- )
196
- agent_input = self._prepare_input(input)
162
+ async def ainvoke(self, input: InvokeInput) -> InvokeOutput:
163
+ """Invoke n8n agent and return response."""
164
+ logger.info("n8n ainvoke called (session_id=%s)", input.session_id)
197
165
 
198
166
  try:
199
167
  agent_output = await self._aapi_call(
200
- query=agent_input,
201
- sessionId=session_id if session_id else uuid.uuid4(),
202
- )
203
- logger.debug(
204
- "n8n API returned payload keys: %s", list(agent_output[0].keys())
168
+ query=input.user_message_str(),
169
+ sessionId=input.session_id if input.session_id else str(uuid.uuid4()),
205
170
  )
171
+ agent_trajectory = self._convert_api_output_to_messages(agent_output)
172
+ logger.info("n8n produced %d trajectory messages", len(agent_trajectory))
173
+ return InvokeOutput(agent_trajectory=agent_trajectory)
206
174
  except Exception as e:
207
175
  logger.exception("Error calling n8n endpoint")
208
176
  raise RuntimeError(f"Error calling n8n endpoint: {e}") from e
209
-
210
- try:
211
- agent_trajectory = self._convert_api_output_to_messages(agent_output)
212
- logger.info(
213
- "n8n conversion produced %d trajectory messages", len(agent_trajectory)
214
- )
215
- return AgentInvocationResponse(
216
- agent_trajectory=agent_trajectory,
217
- )
218
- except Exception as e:
219
- logger.exception("Error processing n8n response")
220
- raise RuntimeError(f"Error processing n8n response: {e}") from e
@@ -1,5 +1,4 @@
1
1
  import json
2
- from typing import Dict, List, Optional, Union
3
2
 
4
3
  from agents import (
5
4
  Agent,
@@ -16,7 +15,9 @@ from agents.memory import Session
16
15
  from opentelemetry.trace import TracerProvider
17
16
 
18
17
  from quraite.adapters.base import BaseAdapter
18
+ from quraite.constants.framework import Framework
19
19
  from quraite.logger import get_logger
20
+ from quraite.schema.invoke import InvokeInput, InvokeOutput
20
21
  from quraite.schema.message import (
21
22
  AgentMessage,
22
23
  AssistantMessage,
@@ -25,38 +26,46 @@ from quraite.schema.message import (
25
26
  ToolCall,
26
27
  ToolMessage,
27
28
  )
28
- from quraite.schema.response import AgentInvocationResponse
29
- from quraite.tracing.constants import Framework
30
29
  from quraite.tracing.trace import AgentSpan, AgentTrace
31
30
 
32
31
  logger = get_logger(__name__)
33
32
 
34
33
 
35
34
  class OpenaiAgentsAdapter(BaseAdapter):
35
+ """OpenAI Agents adapter for Agent."""
36
+
36
37
  def __init__(
37
38
  self,
38
39
  agent: Agent,
40
+ *,
41
+ tracer_provider: TracerProvider | None = None,
39
42
  agent_name: str = "OpenAI Agents",
40
- tracer_provider: Optional[TracerProvider] = None,
41
43
  ):
44
+ """
45
+ Initialize OpenAI Agents adapter.
46
+
47
+ Args:
48
+ agent: OpenAI Agent instance
49
+ tracer_provider: Optional TracerProvider from setup_tracing()
50
+ agent_name: Agent name for metadata
51
+ """
42
52
  self.agent = agent
43
- self.sessions: Dict[str, Session] = {}
44
- self._init_tracing(tracer_provider, required=False)
53
+ self.sessions: dict[str, Session] = {}
45
54
  self.agent_name = agent_name
55
+ self._init_tracer(tracer_provider)
46
56
  logger.info(
47
- "OpenaiAgentsAdapter initialized (agent_name=%s, tracing_enabled=%s)",
48
- agent_name,
49
- bool(tracer_provider),
57
+ "OpenaiAgentsAdapter initialized (tracing=%s)", bool(tracer_provider)
50
58
  )
51
59
 
52
60
  def _convert_run_items_to_messages(
53
- self, run_items: List[RunItem]
54
- ) -> List[AgentMessage]:
61
+ self, run_items: list[RunItem]
62
+ ) -> list[AgentMessage]:
63
+ """Convert OpenAI run items to Quraite messages."""
55
64
  logger.debug("Converting %d OpenAI run items to messages", len(run_items))
56
- messages: List[AgentMessage] = []
57
- text_content: List[MessageContentText] = []
58
- reasoning_content: List[MessageContentReasoning] = []
59
- tool_calls: List[ToolCall] = []
65
+ messages: list[AgentMessage] = []
66
+ text_content: list[MessageContentText] = []
67
+ reasoning_content: list[MessageContentReasoning] = []
68
+ tool_calls: list[ToolCall] = []
60
69
 
61
70
  def flush_assistant_message():
62
71
  nonlocal text_content, reasoning_content, tool_calls
@@ -141,46 +150,13 @@ class OpenaiAgentsAdapter(BaseAdapter):
141
150
  logger.info("Converted OpenAI agent run into %d messages", len(messages))
142
151
  return messages
143
152
 
144
- def _prepare_input(self, input: List[AgentMessage]) -> str:
145
- logger.debug("Preparing OpenAI input from %d messages", len(input))
146
- if not input or input[-1].role != "user":
147
- logger.error("OpenAI input missing user message")
148
- raise ValueError("No user message found in the input")
149
-
150
- last_user_message = input[-1]
151
- if not last_user_message.content:
152
- logger.error("OpenAI user message missing content")
153
- raise ValueError("User message has no content")
154
-
155
- text_content = None
156
- for content_item in last_user_message.content:
157
- if content_item.type == "text" and content_item.text:
158
- text_content = content_item.text
159
- break
160
-
161
- if not text_content:
162
- logger.error("OpenAI user message missing text content")
163
- raise ValueError("No text content found in user message")
164
-
165
- logger.debug("Prepared OpenAI input (text_length=%d)", len(text_content))
166
- return text_content
167
-
168
- async def ainvoke(
169
- self,
170
- input: List[AgentMessage],
171
- session_id: Union[str, None] = None,
172
- ) -> AgentInvocationResponse:
173
- """Asynchronous invocation method - invokes the OpenAI Agents agent and converts to List[AgentMessage]."""
153
+ async def ainvoke(self, input: InvokeInput) -> InvokeOutput:
154
+ """Invoke OpenAI Agents agent and return response."""
174
155
  try:
175
- logger.info(
176
- "OpenAI ainvoke called (session_id=%s, input_messages=%d)",
177
- session_id,
178
- len(input),
179
- )
180
- agent_input: Union[str, List[TResponseInputItem]] = self._prepare_input(
181
- input
182
- )
156
+ logger.info("OpenAI ainvoke called (session_id=%s)", input.session_id)
157
+ agent_input: str | list[TResponseInputItem] = input.user_message_str()
183
158
 
159
+ session_id = input.session_id or "default"
184
160
  if session_id not in self.sessions:
185
161
  self.sessions[session_id] = SQLiteSession(session_id=session_id)
186
162
  session = self.sessions[session_id]
@@ -195,9 +171,9 @@ class OpenaiAgentsAdapter(BaseAdapter):
195
171
 
196
172
  async def _ainvoke_with_tracing(
197
173
  self,
198
- agent_input: Union[str, List[TResponseInputItem]],
174
+ agent_input: str | list[TResponseInputItem],
199
175
  session: Session,
200
- ) -> AgentInvocationResponse:
176
+ ) -> InvokeOutput:
201
177
  """Execute ainvoke with tracing enabled."""
202
178
  with self.tracer.start_as_current_span("openai_invocation") as span:
203
179
  trace_id = span.get_span_context().trace_id
@@ -234,7 +210,7 @@ class OpenaiAgentsAdapter(BaseAdapter):
234
210
  trace_id,
235
211
  )
236
212
 
237
- return AgentInvocationResponse(
213
+ return InvokeOutput(
238
214
  agent_trace=agent_trace,
239
215
  agent_trajectory=agent_trace.to_agent_trajectory(
240
216
  framework=Framework.OPENAI_AGENTS
@@ -243,9 +219,9 @@ class OpenaiAgentsAdapter(BaseAdapter):
243
219
 
244
220
  async def _ainvoke_without_tracing(
245
221
  self,
246
- agent_input: Union[str, List[TResponseInputItem]],
222
+ agent_input: str | list[TResponseInputItem],
247
223
  session: Session,
248
- ) -> AgentInvocationResponse:
224
+ ) -> InvokeOutput:
249
225
  """Execute ainvoke without tracing."""
250
226
  result = await Runner.run(
251
227
  self.agent,
@@ -259,7 +235,7 @@ class OpenaiAgentsAdapter(BaseAdapter):
259
235
  "OpenAI agent produced %d trajectory messages (no tracing)",
260
236
  len(agent_trajectory),
261
237
  )
262
- return AgentInvocationResponse(
238
+ return InvokeOutput(
263
239
  agent_trajectory=agent_trajectory,
264
240
  )
265
241
  except Exception as exc:
@@ -1,4 +1,4 @@
1
- from typing import Any, List, Union
1
+ from typing import Any
2
2
 
3
3
  from opentelemetry.trace import TracerProvider
4
4
  from pydantic_ai import Agent
@@ -12,7 +12,9 @@ from pydantic_ai.messages import (
12
12
  )
13
13
 
14
14
  from quraite.adapters.base import BaseAdapter
15
+ from quraite.constants.framework import Framework
15
16
  from quraite.logger import get_logger
17
+ from quraite.schema.invoke import InvokeInput, InvokeOutput
16
18
  from quraite.schema.message import (
17
19
  AgentMessage,
18
20
  AssistantMessage,
@@ -20,69 +22,47 @@ from quraite.schema.message import (
20
22
  ToolCall,
21
23
  ToolMessage,
22
24
  )
23
- from quraite.schema.response import AgentInvocationResponse
24
- from quraite.tracing.constants import Framework
25
25
  from quraite.tracing.trace import AgentSpan, AgentTrace
26
26
 
27
27
  logger = get_logger(__name__)
28
28
 
29
29
 
30
30
  class PydanticAIAdapter(BaseAdapter):
31
- """
32
- Pydantic AI adapter wrapper that converts any Pydantic AI agent
33
- to a standardized callable interface (invoke) and converts the output to List[AgentMessage].
34
-
35
- This class wraps any Pydantic AI Agent and provides:
36
- - Synchronous invocation via invoke()
37
- - Asynchronous invocation via ainvoke()
38
- - Automatic conversion to List[AgentMessage] format
39
- - Access to message history and traces
40
- """
31
+ """Pydantic AI adapter for Agent."""
41
32
 
42
33
  def __init__(
43
34
  self,
44
35
  agent: Agent,
45
- agent_name: str = "Pydantic AI Agent",
36
+ *,
46
37
  tracer_provider: TracerProvider | None = None,
38
+ agent_name: str = "Pydantic AI Agent",
47
39
  ):
48
40
  """
49
- Initialize with a pre-configured Pydantic AI agent
41
+ Initialize Pydantic AI adapter.
50
42
 
51
43
  Args:
52
- agent: A Pydantic AI Agent instance
53
- agent_name: Name of the agent for trajectory metadata
44
+ agent: Pydantic AI Agent instance
45
+ tracer_provider: Optional TracerProvider from setup_tracing()
46
+ agent_name: Agent name for metadata
54
47
  """
55
48
  self.agent = agent
56
49
  self.agent_name = agent_name
57
- # Store session state for conversation context
58
50
  self._sessions: dict[str, Any] = {}
59
- self._init_tracing(tracer_provider, required=False)
60
- logger.info(
61
- "PydanticAIAdapter initialized (agent_name=%s, tracing_enabled=%s)",
62
- agent_name,
63
- bool(tracer_provider),
64
- )
51
+ self._init_tracer(tracer_provider)
52
+ logger.info("PydanticAIAdapter initialized (tracing=%s)", bool(tracer_provider))
65
53
 
66
54
  def _convert_pydantic_ai_messages_to_messages(
67
- self, messages: List[Any]
68
- ) -> List[AgentMessage]:
69
- """
70
- Convert Pydantic AI ModelMessage objects (with parts) to SDK Message format.
71
-
72
- Args:
73
- messages: List of Pydantic AI ModelMessage objects
74
-
75
- Returns:
76
- List[AgentMessage]: Converted messages in SDK format
77
- """
78
- converted_messages: List[AgentMessage] = []
55
+ self, messages: list[Any]
56
+ ) -> list[AgentMessage]:
57
+ """Convert Pydantic AI messages to Quraite format."""
58
+ converted_messages: list[AgentMessage] = []
79
59
 
80
60
  for msg in messages:
81
61
  if not hasattr(msg, "parts"):
82
62
  continue
83
63
 
84
- content: List[MessageContentText] = []
85
- tool_calls_list: List[ToolCall] = []
64
+ content: list[MessageContentText] = []
65
+ tool_calls_list: list[ToolCall] = []
86
66
 
87
67
  for part in msg.parts:
88
68
  if isinstance(part, SystemPromptPart) or isinstance(
@@ -147,61 +127,11 @@ class PydanticAIAdapter(BaseAdapter):
147
127
  )
148
128
  return converted_messages
149
129
 
150
- def _prepare_input(self, input: List[AgentMessage]) -> str:
151
- """
152
- Prepare input for Pydantic AI agent from List[AgentMessage].
153
-
154
- Args:
155
- input: List[AgentMessage] containing user_message
156
-
157
- Returns:
158
- str: User message text
159
- """
160
- logger.debug("Preparing Pydantic AI input from %d messages", len(input))
161
- if not input or input[-1].role != "user":
162
- logger.error("Pydantic AI input missing user message")
163
- raise ValueError("No user message found in the input")
164
-
165
- last_user_message = input[-1]
166
- if not last_user_message.content:
167
- logger.error("Pydantic AI user message missing content")
168
- raise ValueError("User message has no content")
169
-
170
- text_content = None
171
- for content_item in last_user_message.content:
172
- if content_item.type == "text" and content_item.text:
173
- text_content = content_item.text
174
- break
175
-
176
- if not text_content:
177
- logger.error("Pydantic AI user message missing text content")
178
- raise ValueError("No text content found in user message")
179
-
180
- logger.debug("Prepared Pydantic AI input (text_length=%d)", len(text_content))
181
- return text_content
182
-
183
- async def ainvoke(
184
- self,
185
- input: List[AgentMessage],
186
- session_id: Union[str, None],
187
- ) -> AgentInvocationResponse:
188
- """
189
- Asynchronous invocation method - invokes the Pydantic AI agent and converts the output to List[AgentMessage]
190
-
191
- Args:
192
- input: List[AgentMessage] containing user_message
193
- session_id: Optional conversation ID for maintaining context
194
-
195
- Returns:
196
- AgentInvocationResponse - response containing agent trace, trajectory, and final response.
197
- """
198
- logger.info(
199
- "Pydantic AI ainvoke called (session_id=%s, input_messages=%d)",
200
- session_id,
201
- len(input),
202
- )
203
- agent_input = self._prepare_input(input)
204
- session_id = session_id or "default"
130
+ async def ainvoke(self, input: InvokeInput) -> InvokeOutput:
131
+ """Invoke Pydantic AI agent and return response."""
132
+ logger.info("Pydantic AI ainvoke called (session_id=%s)", input.session_id)
133
+ agent_input = input.user_message_str()
134
+ session_id = input.session_id or "default"
205
135
 
206
136
  try:
207
137
  # Get message history for this session (for multi-turn conversations)
@@ -226,7 +156,7 @@ class PydanticAIAdapter(BaseAdapter):
226
156
  agent_input: str,
227
157
  session_id: str,
228
158
  message_history: Any | None,
229
- ) -> AgentInvocationResponse:
159
+ ) -> InvokeOutput:
230
160
  """Execute ainvoke with tracing enabled."""
231
161
  with self.tracer.start_as_current_span("pydantic_invocation") as span:
232
162
  trace_id = span.get_span_context().trace_id
@@ -266,7 +196,7 @@ class PydanticAIAdapter(BaseAdapter):
266
196
  else:
267
197
  logger.warning("No spans exported for Pydantic AI trace_id=%s", trace_id)
268
198
 
269
- return AgentInvocationResponse(
199
+ return InvokeOutput(
270
200
  agent_trace=agent_trace,
271
201
  agent_trajectory=agent_trace.to_agent_trajectory(
272
202
  framework=Framework.PYDANTIC_AI
@@ -278,7 +208,7 @@ class PydanticAIAdapter(BaseAdapter):
278
208
  agent_input: str,
279
209
  session_id: str,
280
210
  message_history: Any | None,
281
- ) -> AgentInvocationResponse:
211
+ ) -> InvokeOutput:
282
212
  """Execute ainvoke without tracing."""
283
213
  # Run the agent asynchronously with message history for context
284
214
  if message_history:
@@ -302,6 +232,6 @@ class PydanticAIAdapter(BaseAdapter):
302
232
  len(agent_trajectory),
303
233
  )
304
234
 
305
- return AgentInvocationResponse(
235
+ return InvokeOutput(
306
236
  agent_trajectory=agent_trajectory,
307
237
  )
@@ -1,117 +1,56 @@
1
1
  import asyncio
2
- from typing import List, Optional
3
2
 
4
3
  from opentelemetry.trace import TracerProvider
5
4
  from smolagents import CodeAgent
6
5
 
7
6
  from quraite.adapters.base import BaseAdapter
7
+ from quraite.constants.framework import Framework
8
8
  from quraite.logger import get_logger
9
- from quraite.schema.message import AgentMessage
10
- from quraite.schema.response import AgentInvocationResponse
11
- from quraite.tracing.constants import Framework
9
+ from quraite.schema.invoke import InvokeInput, InvokeOutput
12
10
  from quraite.tracing.trace import AgentSpan, AgentTrace
13
11
 
14
12
  logger = get_logger(__name__)
15
13
 
16
14
 
17
15
  class SmolagentsAdapter(BaseAdapter):
18
- """
19
- Smolagents adapter wrapper that converts any Smolagents CodeAgent
20
- to a standardized callable interface (ainvoke) with tracing support.
21
-
22
- This class wraps any CodeAgent and provides:
23
- - Asynchronous invocation via ainvoke()
24
- - OpenTelemetry tracing integration
25
- - Required tracing (returns AgentTrace containing spans)
26
- """
16
+ """Smolagents adapter for CodeAgent (requires tracing)."""
27
17
 
28
18
  def __init__(
29
19
  self,
30
20
  agent: CodeAgent,
21
+ *,
22
+ tracer_provider: TracerProvider | None = None,
31
23
  agent_name: str = "Smolagents Agent",
32
- tracer_provider: Optional[TracerProvider] = None,
33
24
  ):
34
25
  """
35
- Initialize with a pre-configured Smolagents CodeAgent
26
+ Initialize Smolagents adapter.
36
27
 
37
28
  Args:
38
- agent: A Smolagents CodeAgent instance
39
- agent_name: Name of the agent for trajectory metadata
40
- tracer_provider: TracerProvider for tracing (required)
29
+ agent: Smolagents CodeAgent instance
30
+ tracer_provider: TracerProvider from setup_tracing() (required)
31
+ agent_name: Agent name for metadata
41
32
  """
42
- logger.debug(
43
- "Initializing SmolagentsAdapter with agent_name=%s (tracing_required=True)",
44
- agent_name,
45
- )
46
- self._init_tracing(tracer_provider, required=True)
33
+ if tracer_provider is None:
34
+ raise ValueError(
35
+ "Smolagents adapter requires tracing. Use setup_tracing([Framework.SMOLAGENTS]) first."
36
+ )
47
37
 
48
38
  self.agent = agent
49
39
  self.agent_name = agent_name
50
- logger.info("SmolagentsAdapter initialized successfully")
51
-
52
- def _prepare_input(self, input: List[AgentMessage]) -> str:
53
- """
54
- Prepare input for Smolagents CodeAgent from List[AgentMessage].
55
-
56
- Args:
57
- input: List[AgentMessage] containing user_message
58
-
59
- Returns:
60
- str: User message text
61
- """
62
- logger.debug("Preparing Smolagents input from %d messages", len(input))
63
- if not input or input[-1].role != "user":
64
- logger.error("Smolagents input missing user message")
65
- raise ValueError("No user message found in the input")
66
-
67
- last_user_message = input[-1]
68
- if not last_user_message.content:
69
- logger.error("Smolagents user message missing content")
70
- raise ValueError("User message has no content")
40
+ self._init_tracer(tracer_provider)
41
+ logger.info("SmolagentsAdapter initialized")
71
42
 
72
- text_content = None
73
- for content_item in last_user_message.content:
74
- if content_item.type == "text" and content_item.text:
75
- text_content = content_item.text
76
- break
77
-
78
- if not text_content:
79
- logger.error("Smolagents user message missing text content")
80
- raise ValueError("No text content found in user message")
81
-
82
- logger.debug("Prepared Smolagents input (text_length=%d)", len(text_content))
83
- return text_content
84
-
85
- async def ainvoke(
86
- self,
87
- input: List[AgentMessage],
88
- session_id: Optional[str] = None,
89
- ) -> AgentInvocationResponse:
90
- """
91
- Asynchronous invocation method - invokes the Smolagents CodeAgent with tracing
92
-
93
- Args:
94
- input: List[AgentMessage] containing user_message
95
- session_id: Optional conversation ID (for parity with other adapters)
96
-
97
- Returns:
98
- AgentTrace - trace with spans captured during invocation
99
- """
100
- _ = session_id # Currently unused but kept for interface compatibility
101
- logger.info(
102
- "Smolagents ainvoke called (session_id=%s, input_messages=%d)",
103
- session_id,
104
- len(input),
105
- )
106
- agent_input = self._prepare_input(input)
43
+ async def ainvoke(self, input: InvokeInput) -> InvokeOutput:
44
+ """Invoke Smolagents agent and return response with trace."""
45
+ logger.info("Smolagents ainvoke called (session_id=%s)", input.session_id)
107
46
 
108
47
  try:
109
- return await self._ainvoke_with_tracing(agent_input)
48
+ return await self._ainvoke_with_tracing(input.user_message_str())
110
49
  except Exception as exc:
111
50
  logger.exception("Error invoking Smolagents agent")
112
51
  raise RuntimeError(f"Error invoking Smolagents agent: {exc}") from exc
113
52
 
114
- async def _ainvoke_with_tracing(self, agent_input: str) -> AgentInvocationResponse:
53
+ async def _ainvoke_with_tracing(self, agent_input: str) -> InvokeOutput:
115
54
  """Execute ainvoke with tracing enabled."""
116
55
  with self.tracer.start_as_current_span("smolagents_invocation") as span:
117
56
  trace_id = span.get_span_context().trace_id
@@ -140,7 +79,7 @@ class SmolagentsAdapter(BaseAdapter):
140
79
  else:
141
80
  logger.warning("No spans captured for Smolagents trace_id=%s", trace_id)
142
81
 
143
- return AgentInvocationResponse(
82
+ return InvokeOutput(
144
83
  agent_trace=agent_trace,
145
84
  agent_trajectory=agent_trace.to_agent_trajectory(
146
85
  framework=Framework.SMOLAGENTS
@@ -0,0 +1,14 @@
1
+ from enum import Enum
2
+
3
+
4
+ class Framework(str, Enum):
5
+ """Supported agent frameworks."""
6
+
7
+ DEFAULT = "default"
8
+ PYDANTIC_AI = "pydantic_ai"
9
+ LANGCHAIN = "langchain"
10
+ GOOGLE_ADK = "google_adk"
11
+ OPENAI_AGENTS = "openai_agents"
12
+ AGNO = "agno"
13
+ SMOLAGENTS = "smolagents"
14
+ OPENAI = "openai"