quraite 0.0.2__py3-none-any.whl → 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- quraite/__init__.py +3 -3
- quraite/adapters/__init__.py +134 -134
- quraite/adapters/agno_adapter.py +159 -159
- quraite/adapters/base.py +123 -123
- quraite/adapters/bedrock_agents_adapter.py +343 -343
- quraite/adapters/flowise_adapter.py +275 -275
- quraite/adapters/google_adk_adapter.py +209 -209
- quraite/adapters/http_adapter.py +239 -239
- quraite/adapters/langflow_adapter.py +192 -192
- quraite/adapters/langgraph_adapter.py +304 -304
- quraite/adapters/langgraph_server_adapter.py +252 -252
- quraite/adapters/n8n_adapter.py +220 -220
- quraite/adapters/openai_agents_adapter.py +269 -269
- quraite/adapters/pydantic_ai_adapter.py +312 -312
- quraite/adapters/smolagents_adapter.py +152 -152
- quraite/logger.py +61 -64
- quraite/schema/message.py +91 -54
- quraite/schema/response.py +16 -16
- quraite/serve/__init__.py +1 -1
- quraite/serve/cloudflared.py +210 -210
- quraite/serve/local_agent.py +360 -360
- quraite/tracing/__init__.py +24 -24
- quraite/tracing/constants.py +16 -16
- quraite/tracing/span_exporter.py +115 -115
- quraite/tracing/span_processor.py +49 -49
- quraite/tracing/tool_extractors.py +290 -290
- quraite/tracing/trace.py +564 -494
- quraite/tracing/types.py +179 -179
- quraite/tracing/utils.py +170 -170
- quraite/utils/json_utils.py +269 -269
- {quraite-0.0.2.dist-info → quraite-0.1.0.dist-info}/METADATA +9 -9
- quraite-0.1.0.dist-info/RECORD +35 -0
- {quraite-0.0.2.dist-info → quraite-0.1.0.dist-info}/WHEEL +1 -1
- quraite/traces/traces_adk_openinference.json +0 -379
- quraite/traces/traces_agno_multi_agent.json +0 -669
- quraite/traces/traces_agno_openinference.json +0 -321
- quraite/traces/traces_crewai_openinference.json +0 -155
- quraite/traces/traces_langgraph_openinference.json +0 -349
- quraite/traces/traces_langgraph_openinference_multi_agent.json +0 -2705
- quraite/traces/traces_langgraph_traceloop.json +0 -510
- quraite/traces/traces_openai_agents_multi_agent_1.json +0 -402
- quraite/traces/traces_openai_agents_openinference.json +0 -341
- quraite/traces/traces_pydantic_openinference.json +0 -286
- quraite/traces/traces_pydantic_openinference_multi_agent_1.json +0 -399
- quraite/traces/traces_pydantic_openinference_multi_agent_2.json +0 -398
- quraite/traces/traces_smol_agents_openinference.json +0 -397
- quraite/traces/traces_smol_agents_tool_calling_openinference.json +0 -704
- quraite-0.0.2.dist-info/RECORD +0 -49
|
@@ -1,192 +1,192 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import json
|
|
3
|
-
import uuid
|
|
4
|
-
from typing import Any, Dict, List, Union
|
|
5
|
-
|
|
6
|
-
import aiohttp
|
|
7
|
-
|
|
8
|
-
from quraite.adapters.base import BaseAdapter
|
|
9
|
-
from quraite.logger import get_logger
|
|
10
|
-
from quraite.schema.message import (
|
|
11
|
-
AgentMessage,
|
|
12
|
-
AssistantMessage,
|
|
13
|
-
MessageContentText,
|
|
14
|
-
ToolCall,
|
|
15
|
-
ToolMessage,
|
|
16
|
-
)
|
|
17
|
-
from quraite.schema.response import AgentInvocationResponse
|
|
18
|
-
|
|
19
|
-
logger = get_logger(__name__)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class LangflowAdapter(BaseAdapter):
|
|
23
|
-
def __init__(self, api_url: str, x_api_key: str, timeout: int = 60):
|
|
24
|
-
self.api_url = api_url
|
|
25
|
-
self.x_api_key = x_api_key
|
|
26
|
-
self.headers = {"Content-Type": "application/json", "x-api-key": self.x_api_key}
|
|
27
|
-
self.timeout = timeout
|
|
28
|
-
logger.info(
|
|
29
|
-
"LangflowAdapter initialized (api_url=%s, timeout=%s)",
|
|
30
|
-
self.api_url,
|
|
31
|
-
timeout,
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
def _convert_api_output_to_messages(
|
|
35
|
-
self,
|
|
36
|
-
response: Dict[str, Any],
|
|
37
|
-
) -> List[AgentMessage]:
|
|
38
|
-
logger.debug(
|
|
39
|
-
"Converting Langflow response (root_keys=%s)",
|
|
40
|
-
list(response.keys()),
|
|
41
|
-
)
|
|
42
|
-
content_blocks = response["outputs"][0]["outputs"][0]["results"]["message"][
|
|
43
|
-
"content_blocks"
|
|
44
|
-
]
|
|
45
|
-
contents = content_blocks[0]["contents"]
|
|
46
|
-
|
|
47
|
-
# Assume everything sequential.
|
|
48
|
-
ai_trajectory: List[AgentMessage] = []
|
|
49
|
-
for step in contents:
|
|
50
|
-
if step["type"] == "text":
|
|
51
|
-
if step["header"]["title"] == "Input":
|
|
52
|
-
continue
|
|
53
|
-
else:
|
|
54
|
-
ai_trajectory.append(
|
|
55
|
-
AssistantMessage(
|
|
56
|
-
content=[
|
|
57
|
-
MessageContentText(type="text", text=step["text"])
|
|
58
|
-
],
|
|
59
|
-
)
|
|
60
|
-
)
|
|
61
|
-
elif step["type"] == "tool_use":
|
|
62
|
-
tool_id = str(uuid.uuid4())
|
|
63
|
-
tool_input = step.get("tool_input", {})
|
|
64
|
-
if not isinstance(tool_input, dict):
|
|
65
|
-
tool_input = {"value": tool_input}
|
|
66
|
-
|
|
67
|
-
# Create AssistantMessage with tool call
|
|
68
|
-
ai_trajectory.append(
|
|
69
|
-
AssistantMessage(
|
|
70
|
-
tool_calls=[
|
|
71
|
-
ToolCall(
|
|
72
|
-
id=tool_id,
|
|
73
|
-
name=step["name"],
|
|
74
|
-
arguments=tool_input,
|
|
75
|
-
)
|
|
76
|
-
],
|
|
77
|
-
)
|
|
78
|
-
)
|
|
79
|
-
# Create ToolMessage with tool result
|
|
80
|
-
tool_output = step.get("output", "")
|
|
81
|
-
ai_trajectory.append(
|
|
82
|
-
ToolMessage(
|
|
83
|
-
tool_name=step["name"],
|
|
84
|
-
tool_call_id=tool_id,
|
|
85
|
-
content=[
|
|
86
|
-
MessageContentText(type="text", text=str(tool_output))
|
|
87
|
-
],
|
|
88
|
-
)
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
logger.info(
|
|
92
|
-
"Converted Langflow response into %d trajectory messages",
|
|
93
|
-
len(ai_trajectory),
|
|
94
|
-
)
|
|
95
|
-
return ai_trajectory
|
|
96
|
-
|
|
97
|
-
def _prepare_input(self, input: List[AgentMessage]) -> str:
|
|
98
|
-
logger.debug("Preparing Langflow input from %d messages", len(input))
|
|
99
|
-
if not input or input[-1].role != "user":
|
|
100
|
-
logger.error("Langflow input missing user message")
|
|
101
|
-
raise ValueError("No user message found in the input")
|
|
102
|
-
|
|
103
|
-
last_user_message = input[-1]
|
|
104
|
-
if not last_user_message.content:
|
|
105
|
-
logger.error("Langflow input user message missing content")
|
|
106
|
-
raise ValueError("User message has no content")
|
|
107
|
-
|
|
108
|
-
text_content = None
|
|
109
|
-
for content_item in last_user_message.content:
|
|
110
|
-
if content_item.type == "text" and content_item.text:
|
|
111
|
-
text_content = content_item.text
|
|
112
|
-
break
|
|
113
|
-
|
|
114
|
-
if not text_content:
|
|
115
|
-
logger.error("Langflow input missing text content")
|
|
116
|
-
raise ValueError("No text content found in user message")
|
|
117
|
-
|
|
118
|
-
logger.debug("Prepared Langflow input (text_length=%d)", len(text_content))
|
|
119
|
-
return text_content
|
|
120
|
-
|
|
121
|
-
async def _aapi_call(
|
|
122
|
-
self,
|
|
123
|
-
query: str,
|
|
124
|
-
sessionId: str,
|
|
125
|
-
) -> Dict[str, Any]:
|
|
126
|
-
payload = {
|
|
127
|
-
"output_type": "chat",
|
|
128
|
-
"input_type": "chat",
|
|
129
|
-
"input_value": query,
|
|
130
|
-
"session_id": sessionId,
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
async with aiohttp.ClientSession() as session:
|
|
134
|
-
try:
|
|
135
|
-
async with session.post(
|
|
136
|
-
self.api_url,
|
|
137
|
-
headers=self.headers,
|
|
138
|
-
json=payload,
|
|
139
|
-
timeout=aiohttp.ClientTimeout(total=self.timeout),
|
|
140
|
-
) as response:
|
|
141
|
-
response.raise_for_status()
|
|
142
|
-
logger.info(
|
|
143
|
-
"Langflow API call succeeded (status=%s)", response.status
|
|
144
|
-
)
|
|
145
|
-
return await response.json()
|
|
146
|
-
|
|
147
|
-
except (aiohttp.ClientError, asyncio.TimeoutError) as e:
|
|
148
|
-
logger.exception("Langflow API request failed")
|
|
149
|
-
raise aiohttp.ClientError(f"Async API request failed: {str(e)}") from e
|
|
150
|
-
|
|
151
|
-
except json.JSONDecodeError as e:
|
|
152
|
-
logger.exception("Langflow API response decoding failed")
|
|
153
|
-
raise ValueError(f"Failed to decode JSON response: {e}") from e
|
|
154
|
-
|
|
155
|
-
async def ainvoke(
|
|
156
|
-
self,
|
|
157
|
-
input: List[AgentMessage],
|
|
158
|
-
session_id: Union[str, None],
|
|
159
|
-
) -> AgentInvocationResponse:
|
|
160
|
-
"""Asynchronous invocation method - invokes the Langflow agent and converts to List[AgentMessage]."""
|
|
161
|
-
logger.info(
|
|
162
|
-
"Langflow ainvoke called (session_id=%s, input_messages=%d)",
|
|
163
|
-
session_id,
|
|
164
|
-
len(input),
|
|
165
|
-
)
|
|
166
|
-
agent_input = self._prepare_input(input)
|
|
167
|
-
|
|
168
|
-
try:
|
|
169
|
-
agent_output = await self._aapi_call(
|
|
170
|
-
query=agent_input,
|
|
171
|
-
sessionId=session_id if session_id else uuid.uuid4(),
|
|
172
|
-
)
|
|
173
|
-
logger.debug(
|
|
174
|
-
"Langflow API returned payload with top-level keys: %s",
|
|
175
|
-
list(agent_output.keys()),
|
|
176
|
-
)
|
|
177
|
-
except Exception as e:
|
|
178
|
-
logger.exception("Error calling Langflow endpoint")
|
|
179
|
-
raise RuntimeError(f"Error calling langflow endpoint: {e}") from e
|
|
180
|
-
|
|
181
|
-
try:
|
|
182
|
-
agent_trajectory = self._convert_api_output_to_messages(agent_output)
|
|
183
|
-
logger.info(
|
|
184
|
-
"Langflow conversion produced %d trajectory messages",
|
|
185
|
-
len(agent_trajectory),
|
|
186
|
-
)
|
|
187
|
-
return AgentInvocationResponse(
|
|
188
|
-
agent_trajectory=agent_trajectory,
|
|
189
|
-
)
|
|
190
|
-
except Exception as e:
|
|
191
|
-
logger.exception("Error processing Langflow response")
|
|
192
|
-
raise RuntimeError(f"Error processing langflow response: {e}") from e
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
import uuid
|
|
4
|
+
from typing import Any, Dict, List, Union
|
|
5
|
+
|
|
6
|
+
import aiohttp
|
|
7
|
+
|
|
8
|
+
from quraite.adapters.base import BaseAdapter
|
|
9
|
+
from quraite.logger import get_logger
|
|
10
|
+
from quraite.schema.message import (
|
|
11
|
+
AgentMessage,
|
|
12
|
+
AssistantMessage,
|
|
13
|
+
MessageContentText,
|
|
14
|
+
ToolCall,
|
|
15
|
+
ToolMessage,
|
|
16
|
+
)
|
|
17
|
+
from quraite.schema.response import AgentInvocationResponse
|
|
18
|
+
|
|
19
|
+
logger = get_logger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class LangflowAdapter(BaseAdapter):
|
|
23
|
+
def __init__(self, api_url: str, x_api_key: str, timeout: int = 60):
|
|
24
|
+
self.api_url = api_url
|
|
25
|
+
self.x_api_key = x_api_key
|
|
26
|
+
self.headers = {"Content-Type": "application/json", "x-api-key": self.x_api_key}
|
|
27
|
+
self.timeout = timeout
|
|
28
|
+
logger.info(
|
|
29
|
+
"LangflowAdapter initialized (api_url=%s, timeout=%s)",
|
|
30
|
+
self.api_url,
|
|
31
|
+
timeout,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
def _convert_api_output_to_messages(
|
|
35
|
+
self,
|
|
36
|
+
response: Dict[str, Any],
|
|
37
|
+
) -> List[AgentMessage]:
|
|
38
|
+
logger.debug(
|
|
39
|
+
"Converting Langflow response (root_keys=%s)",
|
|
40
|
+
list(response.keys()),
|
|
41
|
+
)
|
|
42
|
+
content_blocks = response["outputs"][0]["outputs"][0]["results"]["message"][
|
|
43
|
+
"content_blocks"
|
|
44
|
+
]
|
|
45
|
+
contents = content_blocks[0]["contents"]
|
|
46
|
+
|
|
47
|
+
# Assume everything sequential.
|
|
48
|
+
ai_trajectory: List[AgentMessage] = []
|
|
49
|
+
for step in contents:
|
|
50
|
+
if step["type"] == "text":
|
|
51
|
+
if step["header"]["title"] == "Input":
|
|
52
|
+
continue
|
|
53
|
+
else:
|
|
54
|
+
ai_trajectory.append(
|
|
55
|
+
AssistantMessage(
|
|
56
|
+
content=[
|
|
57
|
+
MessageContentText(type="text", text=step["text"])
|
|
58
|
+
],
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
elif step["type"] == "tool_use":
|
|
62
|
+
tool_id = str(uuid.uuid4())
|
|
63
|
+
tool_input = step.get("tool_input", {})
|
|
64
|
+
if not isinstance(tool_input, dict):
|
|
65
|
+
tool_input = {"value": tool_input}
|
|
66
|
+
|
|
67
|
+
# Create AssistantMessage with tool call
|
|
68
|
+
ai_trajectory.append(
|
|
69
|
+
AssistantMessage(
|
|
70
|
+
tool_calls=[
|
|
71
|
+
ToolCall(
|
|
72
|
+
id=tool_id,
|
|
73
|
+
name=step["name"],
|
|
74
|
+
arguments=tool_input,
|
|
75
|
+
)
|
|
76
|
+
],
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
# Create ToolMessage with tool result
|
|
80
|
+
tool_output = step.get("output", "")
|
|
81
|
+
ai_trajectory.append(
|
|
82
|
+
ToolMessage(
|
|
83
|
+
tool_name=step["name"],
|
|
84
|
+
tool_call_id=tool_id,
|
|
85
|
+
content=[
|
|
86
|
+
MessageContentText(type="text", text=str(tool_output))
|
|
87
|
+
],
|
|
88
|
+
)
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
logger.info(
|
|
92
|
+
"Converted Langflow response into %d trajectory messages",
|
|
93
|
+
len(ai_trajectory),
|
|
94
|
+
)
|
|
95
|
+
return ai_trajectory
|
|
96
|
+
|
|
97
|
+
def _prepare_input(self, input: List[AgentMessage]) -> str:
|
|
98
|
+
logger.debug("Preparing Langflow input from %d messages", len(input))
|
|
99
|
+
if not input or input[-1].role != "user":
|
|
100
|
+
logger.error("Langflow input missing user message")
|
|
101
|
+
raise ValueError("No user message found in the input")
|
|
102
|
+
|
|
103
|
+
last_user_message = input[-1]
|
|
104
|
+
if not last_user_message.content:
|
|
105
|
+
logger.error("Langflow input user message missing content")
|
|
106
|
+
raise ValueError("User message has no content")
|
|
107
|
+
|
|
108
|
+
text_content = None
|
|
109
|
+
for content_item in last_user_message.content:
|
|
110
|
+
if content_item.type == "text" and content_item.text:
|
|
111
|
+
text_content = content_item.text
|
|
112
|
+
break
|
|
113
|
+
|
|
114
|
+
if not text_content:
|
|
115
|
+
logger.error("Langflow input missing text content")
|
|
116
|
+
raise ValueError("No text content found in user message")
|
|
117
|
+
|
|
118
|
+
logger.debug("Prepared Langflow input (text_length=%d)", len(text_content))
|
|
119
|
+
return text_content
|
|
120
|
+
|
|
121
|
+
async def _aapi_call(
|
|
122
|
+
self,
|
|
123
|
+
query: str,
|
|
124
|
+
sessionId: str,
|
|
125
|
+
) -> Dict[str, Any]:
|
|
126
|
+
payload = {
|
|
127
|
+
"output_type": "chat",
|
|
128
|
+
"input_type": "chat",
|
|
129
|
+
"input_value": query,
|
|
130
|
+
"session_id": sessionId,
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async with aiohttp.ClientSession() as session:
|
|
134
|
+
try:
|
|
135
|
+
async with session.post(
|
|
136
|
+
self.api_url,
|
|
137
|
+
headers=self.headers,
|
|
138
|
+
json=payload,
|
|
139
|
+
timeout=aiohttp.ClientTimeout(total=self.timeout),
|
|
140
|
+
) as response:
|
|
141
|
+
response.raise_for_status()
|
|
142
|
+
logger.info(
|
|
143
|
+
"Langflow API call succeeded (status=%s)", response.status
|
|
144
|
+
)
|
|
145
|
+
return await response.json()
|
|
146
|
+
|
|
147
|
+
except (aiohttp.ClientError, asyncio.TimeoutError) as e:
|
|
148
|
+
logger.exception("Langflow API request failed")
|
|
149
|
+
raise aiohttp.ClientError(f"Async API request failed: {str(e)}") from e
|
|
150
|
+
|
|
151
|
+
except json.JSONDecodeError as e:
|
|
152
|
+
logger.exception("Langflow API response decoding failed")
|
|
153
|
+
raise ValueError(f"Failed to decode JSON response: {e}") from e
|
|
154
|
+
|
|
155
|
+
async def ainvoke(
|
|
156
|
+
self,
|
|
157
|
+
input: List[AgentMessage],
|
|
158
|
+
session_id: Union[str, None],
|
|
159
|
+
) -> AgentInvocationResponse:
|
|
160
|
+
"""Asynchronous invocation method - invokes the Langflow agent and converts to List[AgentMessage]."""
|
|
161
|
+
logger.info(
|
|
162
|
+
"Langflow ainvoke called (session_id=%s, input_messages=%d)",
|
|
163
|
+
session_id,
|
|
164
|
+
len(input),
|
|
165
|
+
)
|
|
166
|
+
agent_input = self._prepare_input(input)
|
|
167
|
+
|
|
168
|
+
try:
|
|
169
|
+
agent_output = await self._aapi_call(
|
|
170
|
+
query=agent_input,
|
|
171
|
+
sessionId=session_id if session_id else uuid.uuid4(),
|
|
172
|
+
)
|
|
173
|
+
logger.debug(
|
|
174
|
+
"Langflow API returned payload with top-level keys: %s",
|
|
175
|
+
list(agent_output.keys()),
|
|
176
|
+
)
|
|
177
|
+
except Exception as e:
|
|
178
|
+
logger.exception("Error calling Langflow endpoint")
|
|
179
|
+
raise RuntimeError(f"Error calling langflow endpoint: {e}") from e
|
|
180
|
+
|
|
181
|
+
try:
|
|
182
|
+
agent_trajectory = self._convert_api_output_to_messages(agent_output)
|
|
183
|
+
logger.info(
|
|
184
|
+
"Langflow conversion produced %d trajectory messages",
|
|
185
|
+
len(agent_trajectory),
|
|
186
|
+
)
|
|
187
|
+
return AgentInvocationResponse(
|
|
188
|
+
agent_trajectory=agent_trajectory,
|
|
189
|
+
)
|
|
190
|
+
except Exception as e:
|
|
191
|
+
logger.exception("Error processing Langflow response")
|
|
192
|
+
raise RuntimeError(f"Error processing langflow response: {e}") from e
|