quraite 0.1.0__py3-none-any.whl → 0.1.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- quraite/__init__.py +3 -3
- quraite/adapters/__init__.py +134 -134
- quraite/adapters/agno_adapter.py +157 -159
- quraite/adapters/base.py +123 -123
- quraite/adapters/bedrock_agents_adapter.py +343 -343
- quraite/adapters/flowise_adapter.py +275 -275
- quraite/adapters/google_adk_adapter.py +211 -209
- quraite/adapters/http_adapter.py +255 -239
- quraite/adapters/{langgraph_adapter.py → langchain_adapter.py} +305 -304
- quraite/adapters/{langgraph_server_adapter.py → langchain_server_adapter.py} +252 -252
- quraite/adapters/langflow_adapter.py +192 -192
- quraite/adapters/n8n_adapter.py +220 -220
- quraite/adapters/openai_agents_adapter.py +267 -269
- quraite/adapters/pydantic_ai_adapter.py +307 -312
- quraite/adapters/smolagents_adapter.py +148 -152
- quraite/logger.py +61 -61
- quraite/schema/message.py +91 -91
- quraite/schema/response.py +16 -16
- quraite/serve/__init__.py +1 -1
- quraite/serve/cloudflared.py +210 -210
- quraite/serve/local_agent.py +360 -360
- quraite/traces/traces_adk_openinference.json +379 -0
- quraite/traces/traces_agno_multi_agent.json +669 -0
- quraite/traces/traces_agno_openinference.json +321 -0
- quraite/traces/traces_crewai_openinference.json +155 -0
- quraite/traces/traces_langgraph_openinference.json +349 -0
- quraite/traces/traces_langgraph_openinference_multi_agent.json +2705 -0
- quraite/traces/traces_langgraph_traceloop.json +510 -0
- quraite/traces/traces_openai_agents_multi_agent_1.json +402 -0
- quraite/traces/traces_openai_agents_openinference.json +341 -0
- quraite/traces/traces_pydantic_openinference.json +286 -0
- quraite/traces/traces_pydantic_openinference_multi_agent_1.json +399 -0
- quraite/traces/traces_pydantic_openinference_multi_agent_2.json +398 -0
- quraite/traces/traces_smol_agents_openinference.json +397 -0
- quraite/traces/traces_smol_agents_tool_calling_openinference.json +704 -0
- quraite/tracing/__init__.py +25 -24
- quraite/tracing/constants.py +15 -16
- quraite/tracing/span_exporter.py +101 -115
- quraite/tracing/span_processor.py +47 -49
- quraite/tracing/tool_extractors.py +309 -290
- quraite/tracing/trace.py +564 -564
- quraite/tracing/types.py +179 -179
- quraite/tracing/utils.py +170 -170
- quraite/utils/json_utils.py +269 -269
- quraite-0.1.2.dist-info/METADATA +386 -0
- quraite-0.1.2.dist-info/RECORD +49 -0
- {quraite-0.1.0.dist-info → quraite-0.1.2.dist-info}/WHEEL +1 -1
- quraite-0.1.0.dist-info/METADATA +0 -44
- quraite-0.1.0.dist-info/RECORD +0 -35
quraite/adapters/http_adapter.py
CHANGED
|
@@ -1,239 +1,255 @@
|
|
|
1
|
-
"""
|
|
2
|
-
HTTP Adapter - Connects to remote agent servers via HTTP.
|
|
3
|
-
|
|
4
|
-
This module provides a BaseAdapter implementation that calls remote agent
|
|
5
|
-
servers via HTTP endpoints, enabling distributed agent evaluation.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from typing import Any, Dict, List, Optional, Union
|
|
9
|
-
|
|
10
|
-
import httpx
|
|
11
|
-
|
|
12
|
-
from quraite.adapters.base import BaseAdapter
|
|
13
|
-
from quraite.logger import get_logger
|
|
14
|
-
from quraite.schema.message import AgentMessage
|
|
15
|
-
from quraite.schema.response import AgentInvocationResponse
|
|
16
|
-
|
|
17
|
-
logger = get_logger(__name__)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class HttpAdapter(BaseAdapter):
|
|
21
|
-
"""
|
|
22
|
-
HTTP adapter client that communicates with agent servers via HTTP.
|
|
23
|
-
|
|
24
|
-
This class implements the BaseAdapter interface and forwards adapter
|
|
25
|
-
requests to a HTTP agent server, handling serialization, network errors,
|
|
26
|
-
and retries.
|
|
27
|
-
|
|
28
|
-
Args:
|
|
29
|
-
url: The full URL of the remote agent endpoint (e.g., "http://localhost:8000/v1/agents/completions")
|
|
30
|
-
headers: Optional dictionary of HTTP headers to include in requests
|
|
31
|
-
timeout: Request timeout in seconds (default: 60)
|
|
32
|
-
max_retries: Maximum number of retry attempts (default: 3)
|
|
33
|
-
retry_delay: Initial retry delay in seconds (default: 1)
|
|
34
|
-
|
|
35
|
-
Example:
|
|
36
|
-
```python
|
|
37
|
-
remote_agent = HttpAdapter(
|
|
38
|
-
url="http://localhost:8000/v1/agents/completions",
|
|
39
|
-
headers={"Authorization": "Bearer secret_key"}
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
result = remote_agent.ainvoke(
|
|
43
|
-
input=[UserMessage(...)],
|
|
44
|
-
session_id="conv_123"
|
|
45
|
-
)
|
|
46
|
-
```
|
|
47
|
-
"""
|
|
48
|
-
|
|
49
|
-
def __init__(
|
|
50
|
-
self,
|
|
51
|
-
url: str,
|
|
52
|
-
headers: Optional[Dict[str, str]] = None,
|
|
53
|
-
timeout: float = 60.0,
|
|
54
|
-
max_retries: int = 3,
|
|
55
|
-
retry_delay: float = 1.0,
|
|
56
|
-
):
|
|
57
|
-
"""Initialize the HTTP adapter client."""
|
|
58
|
-
self.url = url.rstrip("/")
|
|
59
|
-
self.headers = headers or {}
|
|
60
|
-
self.timeout = timeout
|
|
61
|
-
self.max_retries = max_retries
|
|
62
|
-
self.retry_delay = retry_delay
|
|
63
|
-
|
|
64
|
-
# Create HTTP client with user-provided headers
|
|
65
|
-
# Always include Content-Type if not provided
|
|
66
|
-
client_headers = {"Content-Type": "application/json"}
|
|
67
|
-
client_headers.update(self.headers)
|
|
68
|
-
|
|
69
|
-
self.async_client = httpx.AsyncClient(
|
|
70
|
-
headers=client_headers,
|
|
71
|
-
timeout=self.timeout,
|
|
72
|
-
)
|
|
73
|
-
logger.info(
|
|
74
|
-
"HttpAdapter initialized (url=%s, timeout=%s, max_retries=%s)",
|
|
75
|
-
self.url,
|
|
76
|
-
self.timeout,
|
|
77
|
-
self.max_retries,
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
def _serialize_request(
|
|
81
|
-
self,
|
|
82
|
-
input: List[AgentMessage],
|
|
83
|
-
session_id: Union[str, None],
|
|
84
|
-
) -> Dict[str, Any]:
|
|
85
|
-
"""
|
|
86
|
-
Serialize invocation request to JSON-compatible dict.
|
|
87
|
-
|
|
88
|
-
Args:
|
|
89
|
-
input: List[AgentMessage] containing user_message
|
|
90
|
-
session_id: Optional conversation ID for maintaining context
|
|
91
|
-
|
|
92
|
-
Returns:
|
|
93
|
-
Dictionary ready for JSON serialization
|
|
94
|
-
"""
|
|
95
|
-
logger.debug(
|
|
96
|
-
"Serializing HTTP request (messages=%d, session_id=%s)",
|
|
97
|
-
len(input),
|
|
98
|
-
session_id,
|
|
99
|
-
)
|
|
100
|
-
return {
|
|
101
|
-
"input": [msg.model_dump(mode="json") for msg in input],
|
|
102
|
-
"session_id": session_id,
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
async def _make_request_with_retry_async(
|
|
106
|
-
self,
|
|
107
|
-
method: str,
|
|
108
|
-
url: str,
|
|
109
|
-
payload: Dict[str, Any],
|
|
110
|
-
) -> Dict[str, Any]:
|
|
111
|
-
"""
|
|
112
|
-
Make HTTP request with retry logic (asynchronous).
|
|
113
|
-
|
|
114
|
-
Args:
|
|
115
|
-
method: HTTP method (POST)
|
|
116
|
-
url: Full URL of the endpoint
|
|
117
|
-
payload: Request payload
|
|
118
|
-
|
|
119
|
-
Returns:
|
|
120
|
-
Response data as dictionary
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
)
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
)
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
)
|
|
220
|
-
logger.debug(
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
return
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
async def
|
|
238
|
-
|
|
239
|
-
|
|
1
|
+
"""
|
|
2
|
+
HTTP Adapter - Connects to remote agent servers via HTTP.
|
|
3
|
+
|
|
4
|
+
This module provides a BaseAdapter implementation that calls remote agent
|
|
5
|
+
servers via HTTP endpoints, enabling distributed agent evaluation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict, List, Optional, Union
|
|
9
|
+
|
|
10
|
+
import httpx
|
|
11
|
+
|
|
12
|
+
from quraite.adapters.base import BaseAdapter
|
|
13
|
+
from quraite.logger import get_logger
|
|
14
|
+
from quraite.schema.message import AgentMessage
|
|
15
|
+
from quraite.schema.response import AgentInvocationResponse
|
|
16
|
+
|
|
17
|
+
logger = get_logger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class HttpAdapter(BaseAdapter):
|
|
21
|
+
"""
|
|
22
|
+
HTTP adapter client that communicates with agent servers via HTTP.
|
|
23
|
+
|
|
24
|
+
This class implements the BaseAdapter interface and forwards adapter
|
|
25
|
+
requests to a HTTP agent server, handling serialization, network errors,
|
|
26
|
+
and retries.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
url: The full URL of the remote agent endpoint (e.g., "http://localhost:8000/v1/agents/completions")
|
|
30
|
+
headers: Optional dictionary of HTTP headers to include in requests
|
|
31
|
+
timeout: Request timeout in seconds (default: 60)
|
|
32
|
+
max_retries: Maximum number of retry attempts (default: 3)
|
|
33
|
+
retry_delay: Initial retry delay in seconds (default: 1)
|
|
34
|
+
|
|
35
|
+
Example:
|
|
36
|
+
```python
|
|
37
|
+
remote_agent = HttpAdapter(
|
|
38
|
+
url="http://localhost:8000/v1/agents/completions",
|
|
39
|
+
headers={"Authorization": "Bearer secret_key"}
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
result = remote_agent.ainvoke(
|
|
43
|
+
input=[UserMessage(...)],
|
|
44
|
+
session_id="conv_123"
|
|
45
|
+
)
|
|
46
|
+
```
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(
|
|
50
|
+
self,
|
|
51
|
+
url: str,
|
|
52
|
+
headers: Optional[Dict[str, str]] = None,
|
|
53
|
+
timeout: float = 60.0,
|
|
54
|
+
max_retries: int = 3,
|
|
55
|
+
retry_delay: float = 1.0,
|
|
56
|
+
):
|
|
57
|
+
"""Initialize the HTTP adapter client."""
|
|
58
|
+
self.url = url.rstrip("/")
|
|
59
|
+
self.headers = headers or {}
|
|
60
|
+
self.timeout = timeout
|
|
61
|
+
self.max_retries = max_retries
|
|
62
|
+
self.retry_delay = retry_delay
|
|
63
|
+
|
|
64
|
+
# Create HTTP client with user-provided headers
|
|
65
|
+
# Always include Content-Type if not provided
|
|
66
|
+
client_headers = {"Content-Type": "application/json"}
|
|
67
|
+
client_headers.update(self.headers)
|
|
68
|
+
|
|
69
|
+
self.async_client = httpx.AsyncClient(
|
|
70
|
+
headers=client_headers,
|
|
71
|
+
timeout=self.timeout,
|
|
72
|
+
)
|
|
73
|
+
logger.info(
|
|
74
|
+
"HttpAdapter initialized (url=%s, timeout=%s, max_retries=%s)",
|
|
75
|
+
self.url,
|
|
76
|
+
self.timeout,
|
|
77
|
+
self.max_retries,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
def _serialize_request(
|
|
81
|
+
self,
|
|
82
|
+
input: List[AgentMessage],
|
|
83
|
+
session_id: Union[str, None],
|
|
84
|
+
) -> Dict[str, Any]:
|
|
85
|
+
"""
|
|
86
|
+
Serialize invocation request to JSON-compatible dict.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
input: List[AgentMessage] containing user_message
|
|
90
|
+
session_id: Optional conversation ID for maintaining context
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
Dictionary ready for JSON serialization
|
|
94
|
+
"""
|
|
95
|
+
logger.debug(
|
|
96
|
+
"Serializing HTTP request (messages=%d, session_id=%s)",
|
|
97
|
+
len(input),
|
|
98
|
+
session_id,
|
|
99
|
+
)
|
|
100
|
+
return {
|
|
101
|
+
"input": [msg.model_dump(mode="json") for msg in input],
|
|
102
|
+
"session_id": session_id,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async def _make_request_with_retry_async(
|
|
106
|
+
self,
|
|
107
|
+
method: str,
|
|
108
|
+
url: str,
|
|
109
|
+
payload: Dict[str, Any],
|
|
110
|
+
) -> Dict[str, Any]:
|
|
111
|
+
"""
|
|
112
|
+
Make HTTP request with retry logic (asynchronous).
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
method: HTTP method (POST)
|
|
116
|
+
url: Full URL of the endpoint
|
|
117
|
+
payload: Request payload
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
Response data as dictionary
|
|
121
|
+
"""
|
|
122
|
+
last_exception = None
|
|
123
|
+
|
|
124
|
+
for attempt in range(self.max_retries):
|
|
125
|
+
try:
|
|
126
|
+
response = await self.async_client.request(
|
|
127
|
+
method=method,
|
|
128
|
+
url=url,
|
|
129
|
+
json=payload,
|
|
130
|
+
)
|
|
131
|
+
response.raise_for_status()
|
|
132
|
+
logger.info(
|
|
133
|
+
"HTTP request succeeded (status=%s, attempt=%d)",
|
|
134
|
+
response.status_code,
|
|
135
|
+
attempt + 1,
|
|
136
|
+
)
|
|
137
|
+
return response.json()
|
|
138
|
+
|
|
139
|
+
except httpx.HTTPStatusError as e:
|
|
140
|
+
error_detail = e.response.text
|
|
141
|
+
logger.error(
|
|
142
|
+
"Failed while invoking the url '%s' with status code '%s' and detail '%s'",
|
|
143
|
+
url,
|
|
144
|
+
e.response.status_code,
|
|
145
|
+
error_detail,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
raise e
|
|
149
|
+
|
|
150
|
+
except (httpx.ConnectError, httpx.TimeoutException) as e:
|
|
151
|
+
# Retry on network errors
|
|
152
|
+
last_exception = e
|
|
153
|
+
if attempt < self.max_retries - 1:
|
|
154
|
+
delay = self.retry_delay * (2**attempt)
|
|
155
|
+
logger.warning(
|
|
156
|
+
"HTTP adapter retrying after network/connection error (retry_in=%.2fs)",
|
|
157
|
+
delay,
|
|
158
|
+
)
|
|
159
|
+
await self._async_sleep(delay)
|
|
160
|
+
else:
|
|
161
|
+
logger.error(
|
|
162
|
+
"Failed while connecting to the url %s after %d attempts. Last error: %s",
|
|
163
|
+
url,
|
|
164
|
+
self.max_retries,
|
|
165
|
+
last_exception,
|
|
166
|
+
)
|
|
167
|
+
raise last_exception
|
|
168
|
+
|
|
169
|
+
logger.error(
|
|
170
|
+
"HTTP adapter failed while invoking the url %s after %d attempts. Last error: %s",
|
|
171
|
+
url,
|
|
172
|
+
self.max_retries,
|
|
173
|
+
last_exception,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
raise last_exception
|
|
177
|
+
|
|
178
|
+
@staticmethod
|
|
179
|
+
async def _async_sleep(seconds: float):
|
|
180
|
+
"""Helper for async sleep."""
|
|
181
|
+
import asyncio
|
|
182
|
+
|
|
183
|
+
await asyncio.sleep(seconds)
|
|
184
|
+
|
|
185
|
+
async def ainvoke(
|
|
186
|
+
self,
|
|
187
|
+
input: List[AgentMessage],
|
|
188
|
+
session_id: Union[str, None],
|
|
189
|
+
) -> AgentInvocationResponse:
|
|
190
|
+
"""
|
|
191
|
+
Asynchronously invoke the HTTP agent.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
input: List[AgentMessage] containing user_message
|
|
195
|
+
session_id: Optional conversation ID for maintaining context
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
AgentInvocationResponse: Response containing agent trace, trajectory, and final response.
|
|
199
|
+
"""
|
|
200
|
+
logger.info(
|
|
201
|
+
"HTTP ainvoke called (session_id=%s, input_messages=%d)",
|
|
202
|
+
session_id,
|
|
203
|
+
len(input),
|
|
204
|
+
)
|
|
205
|
+
payload = self._serialize_request(input, session_id)
|
|
206
|
+
response_data = await self._make_request_with_retry_async(
|
|
207
|
+
method="POST", url=self.url, payload=payload
|
|
208
|
+
)
|
|
209
|
+
logger.debug(
|
|
210
|
+
"HTTP adapter received response keys: %s", list(response_data.keys())
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
return AgentInvocationResponse.model_validate(
|
|
214
|
+
response_data.get("agent_response", {})
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
async def aclose(self):
|
|
218
|
+
"""Close async HTTP client."""
|
|
219
|
+
await self.async_client.aclose()
|
|
220
|
+
logger.debug("HTTP adapter client closed")
|
|
221
|
+
|
|
222
|
+
async def __aenter__(self):
|
|
223
|
+
"""Async context manager entry."""
|
|
224
|
+
return self
|
|
225
|
+
|
|
226
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
227
|
+
"""Async context manager exit."""
|
|
228
|
+
await self.aclose()
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
if __name__ == "__main__":
|
|
232
|
+
import asyncio
|
|
233
|
+
import json
|
|
234
|
+
|
|
235
|
+
from quraite.schema.message import MessageContentText, UserMessage
|
|
236
|
+
|
|
237
|
+
async def test_http_adapter():
|
|
238
|
+
adapter = HttpAdapter(url="http://localhost:8080/v1/agents/completions")
|
|
239
|
+
|
|
240
|
+
try:
|
|
241
|
+
response = await adapter.ainvoke(
|
|
242
|
+
input=[
|
|
243
|
+
UserMessage(
|
|
244
|
+
content=[MessageContentText(text="What is 34354 - 54?")]
|
|
245
|
+
)
|
|
246
|
+
],
|
|
247
|
+
session_id="test",
|
|
248
|
+
)
|
|
249
|
+
print(response.agent_trajectory)
|
|
250
|
+
except httpx.HTTPStatusError as e:
|
|
251
|
+
print(json.loads(e.response.text)["detail"])
|
|
252
|
+
except Exception as e:
|
|
253
|
+
print(e)
|
|
254
|
+
|
|
255
|
+
asyncio.run(test_http_adapter())
|