quraite 0.1.0__py3-none-any.whl → 0.1.1__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 +3 -3
  2. quraite/adapters/__init__.py +134 -134
  3. quraite/adapters/agno_adapter.py +159 -159
  4. quraite/adapters/base.py +123 -123
  5. quraite/adapters/bedrock_agents_adapter.py +343 -343
  6. quraite/adapters/flowise_adapter.py +275 -275
  7. quraite/adapters/google_adk_adapter.py +209 -209
  8. quraite/adapters/http_adapter.py +251 -239
  9. quraite/adapters/langflow_adapter.py +192 -192
  10. quraite/adapters/langgraph_adapter.py +304 -304
  11. quraite/adapters/langgraph_server_adapter.py +252 -252
  12. quraite/adapters/n8n_adapter.py +220 -220
  13. quraite/adapters/openai_agents_adapter.py +269 -269
  14. quraite/adapters/pydantic_ai_adapter.py +312 -312
  15. quraite/adapters/smolagents_adapter.py +152 -152
  16. quraite/logger.py +61 -61
  17. quraite/schema/message.py +91 -91
  18. quraite/schema/response.py +16 -16
  19. quraite/serve/__init__.py +1 -1
  20. quraite/serve/cloudflared.py +210 -210
  21. quraite/serve/local_agent.py +360 -360
  22. quraite/traces/traces_adk_openinference.json +379 -0
  23. quraite/traces/traces_agno_multi_agent.json +669 -0
  24. quraite/traces/traces_agno_openinference.json +321 -0
  25. quraite/traces/traces_crewai_openinference.json +155 -0
  26. quraite/traces/traces_langgraph_openinference.json +349 -0
  27. quraite/traces/traces_langgraph_openinference_multi_agent.json +2705 -0
  28. quraite/traces/traces_langgraph_traceloop.json +510 -0
  29. quraite/traces/traces_openai_agents_multi_agent_1.json +402 -0
  30. quraite/traces/traces_openai_agents_openinference.json +341 -0
  31. quraite/traces/traces_pydantic_openinference.json +286 -0
  32. quraite/traces/traces_pydantic_openinference_multi_agent_1.json +399 -0
  33. quraite/traces/traces_pydantic_openinference_multi_agent_2.json +398 -0
  34. quraite/traces/traces_smol_agents_openinference.json +397 -0
  35. quraite/traces/traces_smol_agents_tool_calling_openinference.json +704 -0
  36. quraite/tracing/__init__.py +24 -24
  37. quraite/tracing/constants.py +16 -16
  38. quraite/tracing/span_exporter.py +115 -115
  39. quraite/tracing/span_processor.py +49 -49
  40. quraite/tracing/tool_extractors.py +290 -290
  41. quraite/tracing/trace.py +564 -564
  42. quraite/tracing/types.py +179 -179
  43. quraite/tracing/utils.py +170 -170
  44. quraite/utils/json_utils.py +269 -269
  45. quraite-0.1.1.dist-info/METADATA +377 -0
  46. quraite-0.1.1.dist-info/RECORD +49 -0
  47. {quraite-0.1.0.dist-info → quraite-0.1.1.dist-info}/WHEEL +1 -1
  48. quraite-0.1.0.dist-info/METADATA +0 -44
  49. quraite-0.1.0.dist-info/RECORD +0 -35
@@ -1,239 +1,251 @@
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
- Raises:
123
- httpx.HTTPError: If all retries fail
124
- ValueError: If response format is invalid
125
- """
126
- last_exception = None
127
-
128
- for attempt in range(self.max_retries):
129
- try:
130
- response = await self.async_client.request(
131
- method=method,
132
- url=url,
133
- json=payload,
134
- )
135
- response.raise_for_status()
136
- logger.info(
137
- "HTTP request succeeded (status=%s, attempt=%d)",
138
- response.status_code,
139
- attempt + 1,
140
- )
141
- return response.json()
142
-
143
- except httpx.HTTPStatusError as e:
144
- # Don't retry on 4xx errors (client errors)
145
- if 400 <= e.response.status_code < 500:
146
- error_detail = e.response.text
147
- logger.error(
148
- "HTTP adapter received 4xx response (status=%s, detail=%s)",
149
- e.response.status_code,
150
- error_detail,
151
- )
152
- raise ValueError(
153
- f"Agent server error ({e.response.status_code}): {error_detail}"
154
- ) from e
155
-
156
- # Retry on 5xx errors
157
- last_exception = e
158
- if attempt < self.max_retries - 1:
159
- delay = self.retry_delay * (2**attempt)
160
- logger.warning(
161
- "HTTP adapter retrying after server error (status=%s, retry_in=%.2fs)",
162
- e.response.status_code,
163
- delay,
164
- )
165
- await self._async_sleep(delay)
166
-
167
- except (httpx.ConnectError, httpx.TimeoutException) as e:
168
- # Retry on network errors
169
- last_exception = e
170
- if attempt < self.max_retries - 1:
171
- delay = self.retry_delay * (2**attempt)
172
- logger.warning(
173
- "HTTP adapter retrying after network error (retry_in=%.2fs)",
174
- delay,
175
- )
176
- await self._async_sleep(delay)
177
-
178
- # All retries failed
179
- logger.exception(
180
- "HTTP adapter failed after %d attempts (url=%s)",
181
- self.max_retries,
182
- self.url,
183
- )
184
- raise RuntimeError(
185
- f"Failed to connect to agent server at {self.url} after "
186
- f"{self.max_retries} attempts. Last error: {last_exception}"
187
- ) from last_exception
188
-
189
- @staticmethod
190
- async def _async_sleep(seconds: float):
191
- """Helper for async sleep."""
192
- import asyncio
193
-
194
- await asyncio.sleep(seconds)
195
-
196
- async def ainvoke(
197
- self,
198
- input: List[AgentMessage],
199
- session_id: Union[str, None],
200
- ) -> AgentInvocationResponse:
201
- """
202
- Asynchronously invoke the HTTP agent.
203
-
204
- Args:
205
- input: List[AgentMessage] containing user_message
206
- session_id: Optional conversation ID for maintaining context
207
-
208
- Returns:
209
- AgentInvocationResponse: Response containing agent trace, trajectory, and final response.
210
- """
211
- logger.info(
212
- "HTTP ainvoke called (session_id=%s, input_messages=%d)",
213
- session_id,
214
- len(input),
215
- )
216
- payload = self._serialize_request(input, session_id)
217
- response_data = await self._make_request_with_retry_async(
218
- method="POST", url=self.url, payload=payload
219
- )
220
- logger.debug(
221
- "HTTP adapter received response keys: %s", list(response_data.keys())
222
- )
223
-
224
- return AgentInvocationResponse.model_validate(
225
- response_data.get("agent_response", {})
226
- )
227
-
228
- async def aclose(self):
229
- """Close async HTTP client."""
230
- await self.async_client.aclose()
231
- logger.debug("HTTP adapter client closed")
232
-
233
- async def __aenter__(self):
234
- """Async context manager entry."""
235
- return self
236
-
237
- async def __aexit__(self, exc_type, exc_val, exc_tb):
238
- """Async context manager exit."""
239
- await self.aclose()
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=[UserMessage(content=[MessageContentText(text="What is 1 + 1")])],
243
+ session_id="test",
244
+ )
245
+ print(response)
246
+ except httpx.HTTPStatusError as e:
247
+ print(json.loads(e.response.text)["detail"])
248
+ except Exception as e:
249
+ print(e)
250
+
251
+ asyncio.run(test_http_adapter())