quraite 0.0.2__tar.gz → 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. {quraite-0.0.2 → quraite-0.1.0}/PKG-INFO +9 -9
  2. {quraite-0.0.2 → quraite-0.1.0}/README.md +8 -8
  3. {quraite-0.0.2 → quraite-0.1.0}/pyproject.toml +1 -1
  4. {quraite-0.0.2 → quraite-0.1.0}/quraite/__init__.py +3 -3
  5. {quraite-0.0.2 → quraite-0.1.0}/quraite/adapters/__init__.py +134 -134
  6. {quraite-0.0.2 → quraite-0.1.0}/quraite/adapters/agno_adapter.py +159 -159
  7. {quraite-0.0.2 → quraite-0.1.0}/quraite/adapters/base.py +123 -123
  8. {quraite-0.0.2 → quraite-0.1.0}/quraite/adapters/bedrock_agents_adapter.py +343 -343
  9. {quraite-0.0.2 → quraite-0.1.0}/quraite/adapters/flowise_adapter.py +275 -275
  10. {quraite-0.0.2 → quraite-0.1.0}/quraite/adapters/google_adk_adapter.py +209 -209
  11. {quraite-0.0.2 → quraite-0.1.0}/quraite/adapters/http_adapter.py +239 -239
  12. {quraite-0.0.2 → quraite-0.1.0}/quraite/adapters/langflow_adapter.py +192 -192
  13. {quraite-0.0.2 → quraite-0.1.0}/quraite/adapters/langgraph_adapter.py +304 -304
  14. {quraite-0.0.2 → quraite-0.1.0}/quraite/adapters/langgraph_server_adapter.py +252 -252
  15. {quraite-0.0.2 → quraite-0.1.0}/quraite/adapters/n8n_adapter.py +220 -220
  16. {quraite-0.0.2 → quraite-0.1.0}/quraite/adapters/openai_agents_adapter.py +269 -269
  17. {quraite-0.0.2 → quraite-0.1.0}/quraite/adapters/pydantic_ai_adapter.py +312 -312
  18. {quraite-0.0.2 → quraite-0.1.0}/quraite/adapters/smolagents_adapter.py +152 -152
  19. {quraite-0.0.2 → quraite-0.1.0}/quraite/logger.py +61 -64
  20. {quraite-0.0.2 → quraite-0.1.0}/quraite/schema/message.py +91 -54
  21. {quraite-0.0.2 → quraite-0.1.0}/quraite/schema/response.py +16 -16
  22. {quraite-0.0.2 → quraite-0.1.0}/quraite/serve/__init__.py +1 -1
  23. {quraite-0.0.2 → quraite-0.1.0}/quraite/serve/cloudflared.py +210 -210
  24. {quraite-0.0.2 → quraite-0.1.0}/quraite/serve/local_agent.py +360 -360
  25. {quraite-0.0.2 → quraite-0.1.0}/quraite/tracing/__init__.py +24 -24
  26. {quraite-0.0.2 → quraite-0.1.0}/quraite/tracing/constants.py +16 -16
  27. {quraite-0.0.2 → quraite-0.1.0}/quraite/tracing/span_exporter.py +115 -115
  28. {quraite-0.0.2 → quraite-0.1.0}/quraite/tracing/span_processor.py +49 -49
  29. {quraite-0.0.2 → quraite-0.1.0}/quraite/tracing/tool_extractors.py +290 -290
  30. {quraite-0.0.2 → quraite-0.1.0}/quraite/tracing/trace.py +564 -494
  31. {quraite-0.0.2 → quraite-0.1.0}/quraite/tracing/types.py +179 -179
  32. {quraite-0.0.2 → quraite-0.1.0}/quraite/tracing/utils.py +170 -170
  33. {quraite-0.0.2 → quraite-0.1.0}/quraite/utils/json_utils.py +269 -269
  34. quraite-0.0.2/quraite/traces/traces_adk_openinference.json +0 -379
  35. quraite-0.0.2/quraite/traces/traces_agno_multi_agent.json +0 -669
  36. quraite-0.0.2/quraite/traces/traces_agno_openinference.json +0 -321
  37. quraite-0.0.2/quraite/traces/traces_crewai_openinference.json +0 -155
  38. quraite-0.0.2/quraite/traces/traces_langgraph_openinference.json +0 -349
  39. quraite-0.0.2/quraite/traces/traces_langgraph_openinference_multi_agent.json +0 -2705
  40. quraite-0.0.2/quraite/traces/traces_langgraph_traceloop.json +0 -510
  41. quraite-0.0.2/quraite/traces/traces_openai_agents_multi_agent_1.json +0 -402
  42. quraite-0.0.2/quraite/traces/traces_openai_agents_openinference.json +0 -341
  43. quraite-0.0.2/quraite/traces/traces_pydantic_openinference.json +0 -286
  44. quraite-0.0.2/quraite/traces/traces_pydantic_openinference_multi_agent_1.json +0 -399
  45. quraite-0.0.2/quraite/traces/traces_pydantic_openinference_multi_agent_2.json +0 -398
  46. quraite-0.0.2/quraite/traces/traces_smol_agents_openinference.json +0 -397
  47. quraite-0.0.2/quraite/traces/traces_smol_agents_tool_calling_openinference.json +0 -704
  48. {quraite-0.0.2 → quraite-0.1.0}/quraite/schema/__init__.py +0 -0
  49. {quraite-0.0.2 → quraite-0.1.0}/quraite/utils/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: quraite
3
- Version: 0.0.2
3
+ Version: 0.1.0
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>
@@ -33,12 +33,12 @@ Provides-Extra: pyngrok
33
33
  Provides-Extra: smolagents
34
34
  Description-Content-Type: text/markdown
35
35
 
36
- ## Quraite Python SDK
37
-
38
- ### Publishing to Test PyPI
39
-
40
- ```bash
41
- make update-version v=0.4.0
42
- make build
43
- make publish-test-pypi # enter user name as "__token__" and then enter the key
36
+ ## Quraite Python SDK
37
+
38
+ ### Publishing to Test PyPI
39
+
40
+ ```bash
41
+ make update-version v=0.4.0
42
+ make build
43
+ make publish-test-pypi # enter user name as "__token__" and then enter the key
44
44
  ```
@@ -1,9 +1,9 @@
1
- ## Quraite Python SDK
2
-
3
- ### Publishing to Test PyPI
4
-
5
- ```bash
6
- make update-version v=0.4.0
7
- make build
8
- make publish-test-pypi # enter user name as "__token__" and then enter the key
1
+ ## Quraite Python SDK
2
+
3
+ ### Publishing to Test PyPI
4
+
5
+ ```bash
6
+ make update-version v=0.4.0
7
+ make build
8
+ make publish-test-pypi # enter user name as "__token__" and then enter the key
9
9
  ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "quraite"
3
- version = "0.0.2"
3
+ version = "0.1.0"
4
4
  description = "This project provides adaptors and methods to integrate with the Quraite platform"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -1,3 +1,3 @@
1
- """Quraite Python SDK"""
2
-
3
- __version__ = "0.6.0"
1
+ """Quraite Python SDK"""
2
+
3
+ __version__ = "0.6.0"
@@ -1,134 +1,134 @@
1
- from typing import TYPE_CHECKING
2
-
3
- from quraite.adapters.base import BaseAdapter, DummyAdapter
4
- from quraite.adapters.http_adapter import HttpAdapter
5
-
6
- if TYPE_CHECKING:
7
- from quraite.adapters.agno_adapter import AgnoAdapter
8
- from quraite.adapters.bedrock_agents_adapter import BedrockAgentsAdapter
9
- from quraite.adapters.flowise_adapter import FlowiseAdapter
10
- from quraite.adapters.google_adk_adapter import GoogleADKAdapter
11
- 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
- from quraite.adapters.n8n_adapter import N8nAdapter
15
- from quraite.adapters.openai_agents_adapter import OpenaiAgentsAdapter
16
- from quraite.adapters.pydantic_ai_adapter import PydanticAIAdapter
17
- from quraite.adapters.smolagents_adapter import SmolagentsAdapter
18
-
19
-
20
- __all__ = [
21
- "AgnoAdapter",
22
- "BaseAdapter",
23
- "BedrockAgentsAdapter",
24
- "DummyAdapter",
25
- "FlowiseAdapter",
26
- "GoogleADKAdapter",
27
- "LangflowAdapter",
28
- "LanggraphAdapter",
29
- "LanggraphServerAdapter",
30
- "N8nAdapter",
31
- "OpenaiAgentsAdapter",
32
- "PydanticAIAdapter",
33
- "HttpAdapter",
34
- "SmolagentsAdapter",
35
- ]
36
-
37
-
38
- def __getattr__(name: str):
39
- if name == "AgnoAdapter":
40
- try:
41
- from quraite.adapters.agno_adapter import AgnoAdapter
42
-
43
- return AgnoAdapter
44
- except ImportError as e:
45
- raise ImportError(
46
- f"Failed to import {name}. Please install the 'agno' optional dependency: pip install 'quraite[agno]'"
47
- ) from e
48
-
49
- elif name == "BedrockAgentsAdapter":
50
- try:
51
- from quraite.adapters.bedrock_agents_adapter import BedrockAgentsAdapter
52
-
53
- return BedrockAgentsAdapter
54
- except ImportError as e:
55
- raise ImportError(
56
- f"Failed to import {name}. Please install the 'bedrock-agents' optional dependency: pip install 'quraite[bedrock-agents]'"
57
- ) from e
58
-
59
- elif name == "FlowiseAdapter":
60
- from quraite.adapters.flowise_adapter import FlowiseAdapter
61
-
62
- return FlowiseAdapter
63
-
64
- elif name == "GoogleADKAdapter":
65
- try:
66
- from quraite.adapters.google_adk_adapter import GoogleADKAdapter
67
-
68
- return GoogleADKAdapter
69
- except ImportError as e:
70
- raise ImportError(
71
- f"Failed to import {name}. Please install the 'google-adk' optional dependency: pip install 'quraite[google-adk]'"
72
- ) from e
73
-
74
- elif name == "LangflowAdapter":
75
- from quraite.adapters.langflow_adapter import LangflowAdapter
76
-
77
- return LangflowAdapter
78
-
79
- elif name == "LanggraphAdapter":
80
- try:
81
- from quraite.adapters.langgraph_adapter import LanggraphAdapter
82
-
83
- return LanggraphAdapter
84
- except ImportError as e:
85
- raise ImportError(
86
- f"Failed to import {name}. Please install the 'langgraph' optional dependency: pip install 'quraite[langgraph]'"
87
- ) from e
88
-
89
- elif name == "LanggraphServerAdapter":
90
- try:
91
- from quraite.adapters.langgraph_server_adapter import LanggraphServerAdapter
92
-
93
- return LanggraphServerAdapter
94
- except ImportError as e:
95
- raise ImportError(
96
- f"Failed to import {name}. Please install the 'langgraph' optional dependency: pip install 'quraite[langgraph]'"
97
- ) from e
98
-
99
- elif name == "N8nAdapter":
100
- from quraite.adapters.n8n_adapter import N8nAdapter
101
-
102
- return N8nAdapter
103
-
104
- elif name == "OpenaiAgentsAdapter":
105
- try:
106
- from quraite.adapters.openai_agents_adapter import OpenaiAgentsAdapter
107
-
108
- return OpenaiAgentsAdapter
109
- except ImportError as e:
110
- raise ImportError(
111
- f"Failed to import {name}. Please install the 'openai-agents' optional dependency: pip install 'quraite[openai-agents]'"
112
- ) from e
113
-
114
- elif name == "PydanticAIAdapter":
115
- try:
116
- from quraite.adapters.pydantic_ai_adapter import PydanticAIAdapter
117
-
118
- return PydanticAIAdapter
119
- except ImportError as e:
120
- raise ImportError(
121
- f"Failed to import {name}. Please install the 'pydantic-ai' optional dependency: pip install 'quraite[pydantic-ai]'"
122
- ) from e
123
-
124
- elif name == "SmolagentsAdapter":
125
- try:
126
- from quraite.adapters.smolagents_adapter import SmolagentsAdapter
127
-
128
- return SmolagentsAdapter
129
- except ImportError as e:
130
- raise ImportError(
131
- f"Failed to import {name}. Please install the 'smolagents' optional dependency: pip install 'quraite[smolagents]'"
132
- ) from e
133
-
134
- raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
1
+ from typing import TYPE_CHECKING
2
+
3
+ from quraite.adapters.base import BaseAdapter, DummyAdapter
4
+ from quraite.adapters.http_adapter import HttpAdapter
5
+
6
+ if TYPE_CHECKING:
7
+ from quraite.adapters.agno_adapter import AgnoAdapter
8
+ from quraite.adapters.bedrock_agents_adapter import BedrockAgentsAdapter
9
+ from quraite.adapters.flowise_adapter import FlowiseAdapter
10
+ from quraite.adapters.google_adk_adapter import GoogleADKAdapter
11
+ 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
+ from quraite.adapters.n8n_adapter import N8nAdapter
15
+ from quraite.adapters.openai_agents_adapter import OpenaiAgentsAdapter
16
+ from quraite.adapters.pydantic_ai_adapter import PydanticAIAdapter
17
+ from quraite.adapters.smolagents_adapter import SmolagentsAdapter
18
+
19
+
20
+ __all__ = [
21
+ "AgnoAdapter",
22
+ "BaseAdapter",
23
+ "BedrockAgentsAdapter",
24
+ "DummyAdapter",
25
+ "FlowiseAdapter",
26
+ "GoogleADKAdapter",
27
+ "LangflowAdapter",
28
+ "LanggraphAdapter",
29
+ "LanggraphServerAdapter",
30
+ "N8nAdapter",
31
+ "OpenaiAgentsAdapter",
32
+ "PydanticAIAdapter",
33
+ "HttpAdapter",
34
+ "SmolagentsAdapter",
35
+ ]
36
+
37
+
38
+ def __getattr__(name: str):
39
+ if name == "AgnoAdapter":
40
+ try:
41
+ from quraite.adapters.agno_adapter import AgnoAdapter
42
+
43
+ return AgnoAdapter
44
+ except ImportError as e:
45
+ raise ImportError(
46
+ f"Failed to import {name}. Please install the 'agno' optional dependency: pip install 'quraite[agno]'"
47
+ ) from e
48
+
49
+ elif name == "BedrockAgentsAdapter":
50
+ try:
51
+ from quraite.adapters.bedrock_agents_adapter import BedrockAgentsAdapter
52
+
53
+ return BedrockAgentsAdapter
54
+ except ImportError as e:
55
+ raise ImportError(
56
+ f"Failed to import {name}. Please install the 'bedrock-agents' optional dependency: pip install 'quraite[bedrock-agents]'"
57
+ ) from e
58
+
59
+ elif name == "FlowiseAdapter":
60
+ from quraite.adapters.flowise_adapter import FlowiseAdapter
61
+
62
+ return FlowiseAdapter
63
+
64
+ elif name == "GoogleADKAdapter":
65
+ try:
66
+ from quraite.adapters.google_adk_adapter import GoogleADKAdapter
67
+
68
+ return GoogleADKAdapter
69
+ except ImportError as e:
70
+ raise ImportError(
71
+ f"Failed to import {name}. Please install the 'google-adk' optional dependency: pip install 'quraite[google-adk]'"
72
+ ) from e
73
+
74
+ elif name == "LangflowAdapter":
75
+ from quraite.adapters.langflow_adapter import LangflowAdapter
76
+
77
+ return LangflowAdapter
78
+
79
+ elif name == "LanggraphAdapter":
80
+ try:
81
+ from quraite.adapters.langgraph_adapter import LanggraphAdapter
82
+
83
+ return LanggraphAdapter
84
+ except ImportError as e:
85
+ raise ImportError(
86
+ f"Failed to import {name}. Please install the 'langgraph' optional dependency: pip install 'quraite[langgraph]'"
87
+ ) from e
88
+
89
+ elif name == "LanggraphServerAdapter":
90
+ try:
91
+ from quraite.adapters.langgraph_server_adapter import LanggraphServerAdapter
92
+
93
+ return LanggraphServerAdapter
94
+ except ImportError as e:
95
+ raise ImportError(
96
+ f"Failed to import {name}. Please install the 'langgraph' optional dependency: pip install 'quraite[langgraph]'"
97
+ ) from e
98
+
99
+ elif name == "N8nAdapter":
100
+ from quraite.adapters.n8n_adapter import N8nAdapter
101
+
102
+ return N8nAdapter
103
+
104
+ elif name == "OpenaiAgentsAdapter":
105
+ try:
106
+ from quraite.adapters.openai_agents_adapter import OpenaiAgentsAdapter
107
+
108
+ return OpenaiAgentsAdapter
109
+ except ImportError as e:
110
+ raise ImportError(
111
+ f"Failed to import {name}. Please install the 'openai-agents' optional dependency: pip install 'quraite[openai-agents]'"
112
+ ) from e
113
+
114
+ elif name == "PydanticAIAdapter":
115
+ try:
116
+ from quraite.adapters.pydantic_ai_adapter import PydanticAIAdapter
117
+
118
+ return PydanticAIAdapter
119
+ except ImportError as e:
120
+ raise ImportError(
121
+ f"Failed to import {name}. Please install the 'pydantic-ai' optional dependency: pip install 'quraite[pydantic-ai]'"
122
+ ) from e
123
+
124
+ elif name == "SmolagentsAdapter":
125
+ try:
126
+ from quraite.adapters.smolagents_adapter import SmolagentsAdapter
127
+
128
+ return SmolagentsAdapter
129
+ except ImportError as e:
130
+ raise ImportError(
131
+ f"Failed to import {name}. Please install the 'smolagents' optional dependency: pip install 'quraite[smolagents]'"
132
+ ) from e
133
+
134
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -1,159 +1,159 @@
1
- import uuid
2
- from typing import List, Union
3
-
4
- from agno.agent import Agent
5
- from agno.team import Team
6
- from opentelemetry.trace import TracerProvider
7
-
8
- from quraite.adapters.base import BaseAdapter
9
- from quraite.logger import get_logger
10
- from quraite.schema.message import AgentMessage
11
- from quraite.schema.response import AgentInvocationResponse
12
- from quraite.tracing.constants import QURAITE_ADAPTER_TRACE_PREFIX, Framework
13
- from quraite.tracing.trace import AgentSpan, AgentTrace
14
-
15
- logger = get_logger(__name__)
16
-
17
-
18
- class AgnoAdapter(BaseAdapter):
19
- """
20
- Agno adapter wrapper that converts any Agno agent or team
21
- to a standardized callable interface (invoke) and converts the output to List[AgentMessage].
22
-
23
- This class wraps any Agno Agent or Team and provides:
24
- - Asynchronous invocation via ainvoke()
25
- - Automatic conversion to List[AgentMessage] format
26
- - Access to message history and traces
27
- - Support for both single agents and multi-agent teams
28
- """
29
-
30
- def __init__(
31
- self,
32
- agent: Union[Agent, Team],
33
- agent_name: str = "Agno Agent",
34
- tracer_provider: TracerProvider = None,
35
- ):
36
- """
37
- Initialize with a pre-configured Agno agent or team
38
-
39
- Args:
40
- agent: An Agno Agent or Team instance
41
- agent_name: Name of the agent for trajectory metadata
42
- tracer_provider: TracerProvider for tracing (required)
43
- """
44
- logger.debug("Initializing AgnoAdapter with agent_name=%s", agent_name)
45
- self._init_tracing(tracer_provider, required=True)
46
-
47
- self.agent: Union[Agent, Team] = agent
48
- self.agent_name = agent_name
49
- logger.info(
50
- "AgnoAdapter initialized successfully (tracing_enabled=%s)",
51
- bool(tracer_provider),
52
- )
53
-
54
- def _prepare_input(self, input: List[AgentMessage]) -> str:
55
- """
56
- Prepare input for Agno agent from List[AgentMessage].
57
-
58
- Args:
59
- input: List[AgentMessage] containing user_message
60
-
61
- Returns:
62
- str: User message text
63
- """
64
- logger.debug("Preparing input from %d messages", len(input))
65
- if not input or input[-1].role != "user":
66
- logger.error("Invalid input: no user message found")
67
- raise ValueError("No user message found in the input")
68
-
69
- last_user_message = input[-1]
70
- if not last_user_message.content:
71
- logger.error("User message has no content")
72
- raise ValueError("User message has no content")
73
-
74
- text_content = None
75
- for content_item in last_user_message.content:
76
- if content_item.type == "text" and content_item.text:
77
- text_content = content_item.text
78
- break
79
-
80
- if not text_content:
81
- logger.error("No text content found in user message")
82
- raise ValueError("No text content found in user message")
83
-
84
- logger.debug("Prepared input with text_content length=%d", len(text_content))
85
- return text_content
86
-
87
- async def ainvoke(
88
- self,
89
- input: List[AgentMessage],
90
- session_id: Union[str, None] = None,
91
- ) -> AgentInvocationResponse:
92
- """
93
- Asynchronous invocation method - invokes the Agno agent/team with tracing
94
-
95
- Args:
96
- input: List[AgentMessage] containing user_message
97
- session_id: Optional conversation ID for maintaining context
98
-
99
- Returns:
100
- AgentInvocationResponse - response containing agent trace, trajectory, and final response.
101
- """
102
- logger.info(
103
- "ainvoke called (session_id=%s, input_messages=%d)", session_id, len(input)
104
- )
105
- agent_input = self._prepare_input(input)
106
- session_id = session_id or "default"
107
-
108
- try:
109
- logger.debug("Invoking Agno agent with tracing (session_id=%s)", session_id)
110
- return await self._ainvoke_with_tracing(agent_input, session_id)
111
-
112
- except ValueError:
113
- logger.exception("Invalid input during ainvoke")
114
- raise
115
- except Exception as e:
116
- logger.exception("Unexpected error invoking Agno agent")
117
- raise RuntimeError(f"Error invoking Agno agent: {e}") from e
118
-
119
- async def _ainvoke_with_tracing(
120
- self,
121
- agent_input: str,
122
- session_id: str,
123
- ) -> AgentInvocationResponse:
124
- """Execute ainvoke with tracing enabled."""
125
- adapter_trace_id = f"{QURAITE_ADAPTER_TRACE_PREFIX}-{uuid.uuid4()}"
126
- logger.debug(
127
- "Starting traced invocation (trace_id=%s, session_id=%s)",
128
- adapter_trace_id,
129
- session_id,
130
- )
131
-
132
- with self.tracer.start_as_current_span(name=adapter_trace_id):
133
- # Run the agent/team
134
- await self.agent.arun(agent_input, session_id=session_id)
135
-
136
- # Get trace spans
137
- trace_readable_spans = self.quraite_span_exporter.get_trace_by_testcase(
138
- adapter_trace_id
139
- )
140
-
141
- if trace_readable_spans:
142
- logger.info(
143
- "Retrieved %d spans for trace_id=%s",
144
- len(trace_readable_spans),
145
- adapter_trace_id,
146
- )
147
- agent_trace = AgentTrace(
148
- spans=[
149
- AgentSpan.from_readable_oi_span(span)
150
- for span in trace_readable_spans
151
- ],
152
- )
153
- else:
154
- logger.warning("No spans found for trace_id=%s", adapter_trace_id)
155
-
156
- return AgentInvocationResponse(
157
- agent_trace=agent_trace,
158
- agent_trajectory=agent_trace.to_agent_trajectory(framework=Framework.AGNO),
159
- )
1
+ import uuid
2
+ from typing import List, Union
3
+
4
+ from agno.agent import Agent
5
+ from agno.team import Team
6
+ from opentelemetry.trace import TracerProvider
7
+
8
+ from quraite.adapters.base import BaseAdapter
9
+ from quraite.logger import get_logger
10
+ from quraite.schema.message import AgentMessage
11
+ from quraite.schema.response import AgentInvocationResponse
12
+ from quraite.tracing.constants import QURAITE_ADAPTER_TRACE_PREFIX, Framework
13
+ from quraite.tracing.trace import AgentSpan, AgentTrace
14
+
15
+ logger = get_logger(__name__)
16
+
17
+
18
+ class AgnoAdapter(BaseAdapter):
19
+ """
20
+ Agno adapter wrapper that converts any Agno agent or team
21
+ to a standardized callable interface (invoke) and converts the output to List[AgentMessage].
22
+
23
+ This class wraps any Agno Agent or Team and provides:
24
+ - Asynchronous invocation via ainvoke()
25
+ - Automatic conversion to List[AgentMessage] format
26
+ - Access to message history and traces
27
+ - Support for both single agents and multi-agent teams
28
+ """
29
+
30
+ def __init__(
31
+ self,
32
+ agent: Union[Agent, Team],
33
+ agent_name: str = "Agno Agent",
34
+ tracer_provider: TracerProvider = None,
35
+ ):
36
+ """
37
+ Initialize with a pre-configured Agno agent or team
38
+
39
+ Args:
40
+ agent: An Agno Agent or Team instance
41
+ agent_name: Name of the agent for trajectory metadata
42
+ tracer_provider: TracerProvider for tracing (required)
43
+ """
44
+ logger.debug("Initializing AgnoAdapter with agent_name=%s", agent_name)
45
+ self._init_tracing(tracer_provider, required=True)
46
+
47
+ self.agent: Union[Agent, Team] = agent
48
+ self.agent_name = agent_name
49
+ logger.info(
50
+ "AgnoAdapter initialized successfully (tracing_enabled=%s)",
51
+ bool(tracer_provider),
52
+ )
53
+
54
+ def _prepare_input(self, input: List[AgentMessage]) -> str:
55
+ """
56
+ Prepare input for Agno agent from List[AgentMessage].
57
+
58
+ Args:
59
+ input: List[AgentMessage] containing user_message
60
+
61
+ Returns:
62
+ str: User message text
63
+ """
64
+ logger.debug("Preparing input from %d messages", len(input))
65
+ if not input or input[-1].role != "user":
66
+ logger.error("Invalid input: no user message found")
67
+ raise ValueError("No user message found in the input")
68
+
69
+ last_user_message = input[-1]
70
+ if not last_user_message.content:
71
+ logger.error("User message has no content")
72
+ raise ValueError("User message has no content")
73
+
74
+ text_content = None
75
+ for content_item in last_user_message.content:
76
+ if content_item.type == "text" and content_item.text:
77
+ text_content = content_item.text
78
+ break
79
+
80
+ if not text_content:
81
+ logger.error("No text content found in user message")
82
+ raise ValueError("No text content found in user message")
83
+
84
+ logger.debug("Prepared input with text_content length=%d", len(text_content))
85
+ return text_content
86
+
87
+ async def ainvoke(
88
+ self,
89
+ input: List[AgentMessage],
90
+ session_id: Union[str, None] = None,
91
+ ) -> AgentInvocationResponse:
92
+ """
93
+ Asynchronous invocation method - invokes the Agno agent/team with tracing
94
+
95
+ Args:
96
+ input: List[AgentMessage] containing user_message
97
+ session_id: Optional conversation ID for maintaining context
98
+
99
+ Returns:
100
+ AgentInvocationResponse - response containing agent trace, trajectory, and final response.
101
+ """
102
+ logger.info(
103
+ "ainvoke called (session_id=%s, input_messages=%d)", session_id, len(input)
104
+ )
105
+ agent_input = self._prepare_input(input)
106
+ session_id = session_id or "default"
107
+
108
+ try:
109
+ logger.debug("Invoking Agno agent with tracing (session_id=%s)", session_id)
110
+ return await self._ainvoke_with_tracing(agent_input, session_id)
111
+
112
+ except ValueError:
113
+ logger.exception("Invalid input during ainvoke")
114
+ raise
115
+ except Exception as e:
116
+ logger.exception("Unexpected error invoking Agno agent")
117
+ raise RuntimeError(f"Error invoking Agno agent: {e}") from e
118
+
119
+ async def _ainvoke_with_tracing(
120
+ self,
121
+ agent_input: str,
122
+ session_id: str,
123
+ ) -> AgentInvocationResponse:
124
+ """Execute ainvoke with tracing enabled."""
125
+ adapter_trace_id = f"{QURAITE_ADAPTER_TRACE_PREFIX}-{uuid.uuid4()}"
126
+ logger.debug(
127
+ "Starting traced invocation (trace_id=%s, session_id=%s)",
128
+ adapter_trace_id,
129
+ session_id,
130
+ )
131
+
132
+ with self.tracer.start_as_current_span(name=adapter_trace_id):
133
+ # Run the agent/team
134
+ await self.agent.arun(agent_input, session_id=session_id)
135
+
136
+ # Get trace spans
137
+ trace_readable_spans = self.quraite_span_exporter.get_trace_by_testcase(
138
+ adapter_trace_id
139
+ )
140
+
141
+ if trace_readable_spans:
142
+ logger.info(
143
+ "Retrieved %d spans for trace_id=%s",
144
+ len(trace_readable_spans),
145
+ adapter_trace_id,
146
+ )
147
+ agent_trace = AgentTrace(
148
+ spans=[
149
+ AgentSpan.from_readable_oi_span(span)
150
+ for span in trace_readable_spans
151
+ ],
152
+ )
153
+ else:
154
+ logger.warning("No spans found for trace_id=%s", adapter_trace_id)
155
+
156
+ return AgentInvocationResponse(
157
+ agent_trace=agent_trace,
158
+ agent_trajectory=agent_trace.to_agent_trajectory(framework=Framework.AGNO),
159
+ )