quraite 0.1.1__tar.gz → 0.1.2__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.
- {quraite-0.1.1 → quraite-0.1.2}/PKG-INFO +52 -43
- {quraite-0.1.1 → quraite-0.1.2}/README.md +48 -39
- {quraite-0.1.1 → quraite-0.1.2}/pyproject.toml +2 -2
- {quraite-0.1.1 → quraite-0.1.2}/quraite/adapters/__init__.py +12 -12
- {quraite-0.1.1 → quraite-0.1.2}/quraite/adapters/agno_adapter.py +12 -14
- {quraite-0.1.1 → quraite-0.1.2}/quraite/adapters/base.py +2 -2
- {quraite-0.1.1 → quraite-0.1.2}/quraite/adapters/google_adk_adapter.py +13 -11
- {quraite-0.1.1 → quraite-0.1.2}/quraite/adapters/http_adapter.py +6 -2
- quraite-0.1.1/quraite/adapters/langgraph_adapter.py → quraite-0.1.2/quraite/adapters/langchain_adapter.py +22 -21
- quraite-0.1.1/quraite/adapters/langgraph_server_adapter.py → quraite-0.1.2/quraite/adapters/langchain_server_adapter.py +21 -21
- {quraite-0.1.1 → quraite-0.1.2}/quraite/adapters/openai_agents_adapter.py +12 -14
- {quraite-0.1.1 → quraite-0.1.2}/quraite/adapters/pydantic_ai_adapter.py +12 -17
- {quraite-0.1.1 → quraite-0.1.2}/quraite/adapters/smolagents_adapter.py +10 -14
- {quraite-0.1.1 → quraite-0.1.2}/quraite/serve/local_agent.py +3 -3
- {quraite-0.1.1 → quraite-0.1.2}/quraite/tracing/__init__.py +1 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/tracing/constants.py +3 -4
- {quraite-0.1.1 → quraite-0.1.2}/quraite/tracing/span_exporter.py +5 -19
- {quraite-0.1.1 → quraite-0.1.2}/quraite/tracing/span_processor.py +1 -3
- {quraite-0.1.1 → quraite-0.1.2}/quraite/tracing/tool_extractors.py +30 -11
- {quraite-0.1.1 → quraite-0.1.2}/quraite/tracing/trace.py +6 -6
- {quraite-0.1.1 → quraite-0.1.2}/quraite/__init__.py +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/adapters/bedrock_agents_adapter.py +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/adapters/flowise_adapter.py +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/adapters/langflow_adapter.py +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/adapters/n8n_adapter.py +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/logger.py +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/schema/__init__.py +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/schema/message.py +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/schema/response.py +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/serve/__init__.py +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/serve/cloudflared.py +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/traces/traces_adk_openinference.json +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/traces/traces_agno_multi_agent.json +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/traces/traces_agno_openinference.json +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/traces/traces_crewai_openinference.json +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/traces/traces_langgraph_openinference.json +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/traces/traces_langgraph_openinference_multi_agent.json +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/traces/traces_langgraph_traceloop.json +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/traces/traces_openai_agents_multi_agent_1.json +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/traces/traces_openai_agents_openinference.json +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/traces/traces_pydantic_openinference.json +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/traces/traces_pydantic_openinference_multi_agent_1.json +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/traces/traces_pydantic_openinference_multi_agent_2.json +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/traces/traces_smol_agents_openinference.json +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/traces/traces_smol_agents_tool_calling_openinference.json +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/tracing/types.py +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/tracing/utils.py +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/utils/__init__.py +0 -0
- {quraite-0.1.1 → quraite-0.1.2}/quraite/utils/json_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: quraite
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
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>
|
|
@@ -16,8 +16,8 @@ Requires-Dist: uvicorn>=0.38.0
|
|
|
16
16
|
Requires-Dist: agno>=2.3.4 ; extra == 'agno'
|
|
17
17
|
Requires-Dist: boto3>=1.40.70 ; extra == 'bedrock'
|
|
18
18
|
Requires-Dist: google-adk>=1.18.0 ; extra == 'google-adk'
|
|
19
|
-
Requires-Dist: langchain>=1.0.5 ; extra == '
|
|
20
|
-
Requires-Dist: langgraph>=1.0.3 ; extra == '
|
|
19
|
+
Requires-Dist: langchain>=1.0.5 ; extra == 'langchain'
|
|
20
|
+
Requires-Dist: langgraph>=1.0.3 ; extra == 'langchain'
|
|
21
21
|
Requires-Dist: openai-agents>=0.5.0 ; extra == 'openai-agents'
|
|
22
22
|
Requires-Dist: pydantic-ai>=1.25.0 ; extra == 'pydantic-ai'
|
|
23
23
|
Requires-Dist: pyngrok>=7.5.0 ; extra == 'pyngrok'
|
|
@@ -26,7 +26,7 @@ Requires-Python: >=3.10
|
|
|
26
26
|
Provides-Extra: agno
|
|
27
27
|
Provides-Extra: bedrock
|
|
28
28
|
Provides-Extra: google-adk
|
|
29
|
-
Provides-Extra:
|
|
29
|
+
Provides-Extra: langchain
|
|
30
30
|
Provides-Extra: openai-agents
|
|
31
31
|
Provides-Extra: pydantic-ai
|
|
32
32
|
Provides-Extra: pyngrok
|
|
@@ -37,16 +37,15 @@ Description-Content-Type: text/markdown
|
|
|
37
37
|
|
|
38
38
|
[](https://www.python.org/downloads/)
|
|
39
39
|
[](LICENSE)
|
|
40
|
+
[](https://opentelemetry.io/)
|
|
40
41
|
|
|
41
|
-
The **Quraite Python SDK** provides adapters and methods to integrate AI agent
|
|
42
|
+
The **Quraite Python SDK** provides adapters and methods to integrate AI agent with the [Quraite platform](https://quraite.ai) for evaluation. It offers a unified interface for different agent frameworks, automatic tracing at every turn for agent trajectory evaluation, and easy local server setup with tunneling capabilities.
|
|
42
43
|
|
|
43
44
|
## Features
|
|
44
45
|
|
|
45
|
-
- 🔌 **Framework Adapters**: Support for multiple AI agent frameworks (
|
|
46
|
-
- 📊 **Automatic Tracing**: Built-in OpenTelemetry-based tracing for agent
|
|
47
|
-
- 🚀 **Local Server**: Easy-to-use local server with optional tunneling (Cloudflare/ngrok) for public access
|
|
48
|
-
- 📦 **Standardized Schema**: Unified message and response formats across all frameworks
|
|
49
|
-
- 🔍 **Observability**: Track token usage, costs, latency, and model information for each agent invocation
|
|
46
|
+
- 🔌 **Framework Adapters**: Support for multiple AI agent frameworks (LangChain, Pydantic AI, Agno, Google ADK, OpenAI Agents, Smolagents, AWS Bedrock, Flowise, Langflow, N8n, and more)
|
|
47
|
+
- 📊 **Automatic Tracing**: Built-in OpenInference-based (OpenTelemetry-based tracing support coming soon) tracing for agent trajectory evaluation. Track token usage, costs, latency, and model information for each agent invocation
|
|
48
|
+
- 🚀 **Local Server**: Easy-to-use local server with optional tunneling (Cloudflare/ngrok) for public access and integration with Quraite platform
|
|
50
49
|
|
|
51
50
|
## Installation
|
|
52
51
|
|
|
@@ -61,8 +60,8 @@ pip install quraite
|
|
|
61
60
|
Install with optional dependencies for specific frameworks:
|
|
62
61
|
|
|
63
62
|
```bash
|
|
64
|
-
#
|
|
65
|
-
pip install 'quraite[
|
|
63
|
+
# LangChain
|
|
64
|
+
pip install 'quraite[langchain]'
|
|
66
65
|
|
|
67
66
|
# Pydantic AI
|
|
68
67
|
pip install 'quraite[pydantic-ai]'
|
|
@@ -83,42 +82,45 @@ pip install 'quraite[smolagents]'
|
|
|
83
82
|
pip install 'quraite[bedrock]'
|
|
84
83
|
|
|
85
84
|
# Multiple frameworks
|
|
86
|
-
pip install 'quraite[
|
|
85
|
+
pip install 'quraite[langchain,pydantic-ai,agno]'
|
|
87
86
|
```
|
|
88
87
|
|
|
89
88
|
## Quick Start
|
|
90
89
|
|
|
91
|
-
### Example:
|
|
90
|
+
### Example: LangChain Agent with Local Server
|
|
92
91
|
|
|
93
|
-
Pass your compiled
|
|
92
|
+
Pass your compiled LangChain agent to the adapter and expose it as an HTTP API:
|
|
94
93
|
|
|
95
94
|
```python
|
|
95
|
+
import asyncio
|
|
96
96
|
import uvicorn
|
|
97
97
|
from dotenv import load_dotenv
|
|
98
98
|
from openinference.instrumentation import TracerProvider
|
|
99
99
|
from openinference.instrumentation.langchain import LangChainInstrumentor
|
|
100
100
|
|
|
101
|
-
from quraite.adapters import
|
|
101
|
+
from quraite.adapters import LangChainAdapter
|
|
102
102
|
from quraite.serve.local_agent import LocalAgentServer
|
|
103
103
|
from quraite.tracing.span_exporter import QuraiteInMemorySpanExporter
|
|
104
104
|
from quraite.tracing.span_processor import QuraiteSimpleSpanProcessor
|
|
105
105
|
|
|
106
106
|
load_dotenv()
|
|
107
107
|
|
|
108
|
-
# Set up tracing
|
|
108
|
+
# Set up tracing
|
|
109
|
+
# Use Quraite's in-memory span exporter to capture the agent trajectory
|
|
110
|
+
# and use it for evaluation.
|
|
109
111
|
tracer_provider = TracerProvider()
|
|
110
112
|
quraite_span_exporter = QuraiteInMemorySpanExporter()
|
|
111
113
|
quraite_span_processor = QuraiteSimpleSpanProcessor(quraite_span_exporter)
|
|
112
114
|
tracer_provider.add_span_processor(quraite_span_processor)
|
|
113
115
|
LangChainInstrumentor().instrument(tracer_provider=tracer_provider)
|
|
114
116
|
|
|
115
|
-
# Your compiled
|
|
117
|
+
# Your compiled LangChain agent (created elsewhere)
|
|
116
118
|
# agent = create_agent(...)
|
|
117
119
|
|
|
118
120
|
# Wrap with Quraite adapter
|
|
119
|
-
adapter =
|
|
120
|
-
agent_graph=agent, # Pass your compiled
|
|
121
|
-
tracer_provider=tracer_provider,
|
|
121
|
+
adapter = LangChainAdapter(
|
|
122
|
+
agent_graph=agent, # Pass your compiled LangChain agent here
|
|
123
|
+
tracer_provider=tracer_provider,
|
|
122
124
|
)
|
|
123
125
|
|
|
124
126
|
# Create and start server with Cloudflare tunnel
|
|
@@ -133,14 +135,18 @@ app = server.create_app(
|
|
|
133
135
|
tunnel="cloudflare", # Options: "cloudflare", "ngrok", or "none"
|
|
134
136
|
)
|
|
135
137
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
+
# Option 1: Use the start method to start the server
|
|
139
|
+
asyncio.run(server.start(host="0.0.0.0", port=8080))
|
|
140
|
+
|
|
141
|
+
# Option 2: Use uvicorn to start the server for auto-reload
|
|
142
|
+
# if __name__ == "__main__":
|
|
143
|
+
# uvicorn.run("local_server:app", host="0.0.0.0", port=8080, reload=True)
|
|
138
144
|
```
|
|
139
145
|
|
|
140
146
|
The server exposes:
|
|
141
147
|
|
|
142
148
|
- `GET /` - Health check endpoint
|
|
143
|
-
- `POST /v1/agents/completions` - Agent invocation endpoint
|
|
149
|
+
- `POST /v1/agents/completions` - Agent invocation endpoint. This is the endpoint that Quraite will use to invoke your agent.
|
|
144
150
|
|
|
145
151
|
When using `tunnel="cloudflare"` or `tunnel="ngrok"`, your agent will be publicly accessible via the generated URL.
|
|
146
152
|
|
|
@@ -148,7 +154,7 @@ When using `tunnel="cloudflare"` or `tunnel="ngrok"`, your agent will be publicl
|
|
|
148
154
|
|
|
149
155
|
| Framework | Adapter | Installation |
|
|
150
156
|
| -------------------- | ------------------------ | -------------------------------------- |
|
|
151
|
-
| **
|
|
157
|
+
| **LangChain** | `LangChainAdapter` | `pip install 'quraite[langchain]'` |
|
|
152
158
|
| **Pydantic AI** | `PydanticAIAdapter` | `pip install 'quraite[pydantic-ai]'` |
|
|
153
159
|
| **Agno** | `AgnoAdapter` | `pip install 'quraite[agno]'` |
|
|
154
160
|
| **Google ADK** | `GoogleADKAdapter` | `pip install 'quraite[google-adk]'` |
|
|
@@ -159,27 +165,28 @@ When using `tunnel="cloudflare"` or `tunnel="ngrok"`, your agent will be publicl
|
|
|
159
165
|
| **Langflow** | `LangflowAdapter` | Included in base package |
|
|
160
166
|
| **N8n** | `N8nAdapter` | Included in base package |
|
|
161
167
|
| **HTTP** | `HttpAdapter` | Included in base package |
|
|
162
|
-
| **
|
|
168
|
+
| **LangChain Server** | `LangChainServerAdapter` | `pip install 'quraite[langchain]'` |
|
|
163
169
|
|
|
164
170
|
## Core Concepts
|
|
165
171
|
|
|
166
172
|
### Adapters
|
|
167
173
|
|
|
168
|
-
Adapters provide a unified interface (`BaseAdapter`) for different agent frameworks. Each adapter
|
|
174
|
+
Adapters provide a unified interface (`BaseAdapter`) for different agent frameworks. Each adapter converts framework-specific agent response formats to the Quraite agent message format.
|
|
175
|
+
|
|
176
|
+
If you are building your own agent framework, you can create a custom adapter by extending the `BaseAdapter` class and implementing the `ainvoke` method.
|
|
177
|
+
|
|
178
|
+
### Tracing for Agent Trajectory Evaluation
|
|
169
179
|
|
|
170
|
-
|
|
171
|
-
- Handles message format conversion
|
|
172
|
-
- Supports optional tracing integration
|
|
173
|
-
- Provides async invocation via `ainvoke()`
|
|
180
|
+
**Capture agent trajectories without modifying your code.** Get comprehensive trace data including token usage, costs, and latency for every agent step.
|
|
174
181
|
|
|
175
|
-
|
|
182
|
+
Most agent frameworks return agent steps, but lack critical observability data. We solve this with **OpenInference instrumentation** (OpenTelemetry instrumentation support coming soon) that automatically captures:
|
|
176
183
|
|
|
177
|
-
|
|
184
|
+
- Complete agent trajectories
|
|
185
|
+
- Token usage and costs
|
|
186
|
+
- Step-by-step latency
|
|
187
|
+
- Full execution context
|
|
178
188
|
|
|
179
|
-
-
|
|
180
|
-
- **Tool Calls**: Tool invocations with inputs and outputs
|
|
181
|
-
- **Performance Metrics**: Token usage, costs, latency
|
|
182
|
-
- **Model Information**: Model name and provider details
|
|
189
|
+
**Works with your existing setup.** We provide OpenInference-compatible span exporters and processors that integrate seamlessly with your current observability platform - no vendor lock-in required.
|
|
183
190
|
|
|
184
191
|
To enable tracing:
|
|
185
192
|
|
|
@@ -189,11 +196,13 @@ from quraite.tracing.span_exporter import QuraiteInMemorySpanExporter
|
|
|
189
196
|
from quraite.tracing.span_processor import QuraiteSimpleSpanProcessor
|
|
190
197
|
|
|
191
198
|
tracer_provider = TracerProvider()
|
|
199
|
+
|
|
200
|
+
# Add Quraite span exporter and processor to the tracer provider
|
|
192
201
|
quraite_span_exporter = QuraiteInMemorySpanExporter()
|
|
193
202
|
quraite_span_processor = QuraiteSimpleSpanProcessor(quraite_span_exporter)
|
|
194
203
|
tracer_provider.add_span_processor(quraite_span_processor)
|
|
195
204
|
|
|
196
|
-
# Instrument your framework
|
|
205
|
+
# Instrument your framework with OpenInference
|
|
197
206
|
from openinference.instrumentation.langchain import LangChainInstrumentor
|
|
198
207
|
LangChainInstrumentor().instrument(tracer_provider=tracer_provider)
|
|
199
208
|
```
|
|
@@ -251,18 +260,15 @@ response: AgentInvocationResponse = await adapter.ainvoke(
|
|
|
251
260
|
# Access trajectory (list of messages)
|
|
252
261
|
trajectory = response.agent_trajectory
|
|
253
262
|
|
|
254
|
-
# Access trace
|
|
263
|
+
# Access trace
|
|
255
264
|
trace = response.agent_trace
|
|
256
|
-
|
|
257
|
-
# Access final response text
|
|
258
|
-
final_response = response.agent_final_response
|
|
259
265
|
```
|
|
260
266
|
|
|
261
267
|
## Examples
|
|
262
268
|
|
|
263
269
|
The repository includes comprehensive examples for each supported framework:
|
|
264
270
|
|
|
265
|
-
- [`
|
|
271
|
+
- [`langchain_calculator_agent`](examples/langchain_calculator_agent/) - LangChain calculator agent
|
|
266
272
|
- [`pydantic_calculator_agent`](examples/pydantic_calculator_agent/) - Pydantic AI calculator agent
|
|
267
273
|
- [`agno_calculator_agent`](examples/agno_calculator_agent/) - Agno calculator agent
|
|
268
274
|
- [`google_adk_weather_agent`](examples/google_adk_weather_agent/) - Google ADK weather agent
|
|
@@ -302,6 +308,7 @@ class MyAdapter(BaseAdapter):
|
|
|
302
308
|
Create a local HTTP server for your agent:
|
|
303
309
|
|
|
304
310
|
```python
|
|
311
|
+
import asyncio
|
|
305
312
|
from quraite.serve.local_agent import LocalAgentServer
|
|
306
313
|
|
|
307
314
|
server = LocalAgentServer(
|
|
@@ -314,6 +321,8 @@ app = server.create_app(
|
|
|
314
321
|
host="0.0.0.0",
|
|
315
322
|
tunnel="cloudflare", # or "ngrok" or "none"
|
|
316
323
|
)
|
|
324
|
+
|
|
325
|
+
asyncio.run(server.start(host="0.0.0.0", port=8080))
|
|
317
326
|
```
|
|
318
327
|
|
|
319
328
|
## Development
|
|
@@ -2,16 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.python.org/downloads/)
|
|
4
4
|
[](LICENSE)
|
|
5
|
+
[](https://opentelemetry.io/)
|
|
5
6
|
|
|
6
|
-
The **Quraite Python SDK** provides adapters and methods to integrate AI agent
|
|
7
|
+
The **Quraite Python SDK** provides adapters and methods to integrate AI agent with the [Quraite platform](https://quraite.ai) for evaluation. It offers a unified interface for different agent frameworks, automatic tracing at every turn for agent trajectory evaluation, and easy local server setup with tunneling capabilities.
|
|
7
8
|
|
|
8
9
|
## Features
|
|
9
10
|
|
|
10
|
-
- 🔌 **Framework Adapters**: Support for multiple AI agent frameworks (
|
|
11
|
-
- 📊 **Automatic Tracing**: Built-in OpenTelemetry-based tracing for agent
|
|
12
|
-
- 🚀 **Local Server**: Easy-to-use local server with optional tunneling (Cloudflare/ngrok) for public access
|
|
13
|
-
- 📦 **Standardized Schema**: Unified message and response formats across all frameworks
|
|
14
|
-
- 🔍 **Observability**: Track token usage, costs, latency, and model information for each agent invocation
|
|
11
|
+
- 🔌 **Framework Adapters**: Support for multiple AI agent frameworks (LangChain, Pydantic AI, Agno, Google ADK, OpenAI Agents, Smolagents, AWS Bedrock, Flowise, Langflow, N8n, and more)
|
|
12
|
+
- 📊 **Automatic Tracing**: Built-in OpenInference-based (OpenTelemetry-based tracing support coming soon) tracing for agent trajectory evaluation. Track token usage, costs, latency, and model information for each agent invocation
|
|
13
|
+
- 🚀 **Local Server**: Easy-to-use local server with optional tunneling (Cloudflare/ngrok) for public access and integration with Quraite platform
|
|
15
14
|
|
|
16
15
|
## Installation
|
|
17
16
|
|
|
@@ -26,8 +25,8 @@ pip install quraite
|
|
|
26
25
|
Install with optional dependencies for specific frameworks:
|
|
27
26
|
|
|
28
27
|
```bash
|
|
29
|
-
#
|
|
30
|
-
pip install 'quraite[
|
|
28
|
+
# LangChain
|
|
29
|
+
pip install 'quraite[langchain]'
|
|
31
30
|
|
|
32
31
|
# Pydantic AI
|
|
33
32
|
pip install 'quraite[pydantic-ai]'
|
|
@@ -48,42 +47,45 @@ pip install 'quraite[smolagents]'
|
|
|
48
47
|
pip install 'quraite[bedrock]'
|
|
49
48
|
|
|
50
49
|
# Multiple frameworks
|
|
51
|
-
pip install 'quraite[
|
|
50
|
+
pip install 'quraite[langchain,pydantic-ai,agno]'
|
|
52
51
|
```
|
|
53
52
|
|
|
54
53
|
## Quick Start
|
|
55
54
|
|
|
56
|
-
### Example:
|
|
55
|
+
### Example: LangChain Agent with Local Server
|
|
57
56
|
|
|
58
|
-
Pass your compiled
|
|
57
|
+
Pass your compiled LangChain agent to the adapter and expose it as an HTTP API:
|
|
59
58
|
|
|
60
59
|
```python
|
|
60
|
+
import asyncio
|
|
61
61
|
import uvicorn
|
|
62
62
|
from dotenv import load_dotenv
|
|
63
63
|
from openinference.instrumentation import TracerProvider
|
|
64
64
|
from openinference.instrumentation.langchain import LangChainInstrumentor
|
|
65
65
|
|
|
66
|
-
from quraite.adapters import
|
|
66
|
+
from quraite.adapters import LangChainAdapter
|
|
67
67
|
from quraite.serve.local_agent import LocalAgentServer
|
|
68
68
|
from quraite.tracing.span_exporter import QuraiteInMemorySpanExporter
|
|
69
69
|
from quraite.tracing.span_processor import QuraiteSimpleSpanProcessor
|
|
70
70
|
|
|
71
71
|
load_dotenv()
|
|
72
72
|
|
|
73
|
-
# Set up tracing
|
|
73
|
+
# Set up tracing
|
|
74
|
+
# Use Quraite's in-memory span exporter to capture the agent trajectory
|
|
75
|
+
# and use it for evaluation.
|
|
74
76
|
tracer_provider = TracerProvider()
|
|
75
77
|
quraite_span_exporter = QuraiteInMemorySpanExporter()
|
|
76
78
|
quraite_span_processor = QuraiteSimpleSpanProcessor(quraite_span_exporter)
|
|
77
79
|
tracer_provider.add_span_processor(quraite_span_processor)
|
|
78
80
|
LangChainInstrumentor().instrument(tracer_provider=tracer_provider)
|
|
79
81
|
|
|
80
|
-
# Your compiled
|
|
82
|
+
# Your compiled LangChain agent (created elsewhere)
|
|
81
83
|
# agent = create_agent(...)
|
|
82
84
|
|
|
83
85
|
# Wrap with Quraite adapter
|
|
84
|
-
adapter =
|
|
85
|
-
agent_graph=agent, # Pass your compiled
|
|
86
|
-
tracer_provider=tracer_provider,
|
|
86
|
+
adapter = LangChainAdapter(
|
|
87
|
+
agent_graph=agent, # Pass your compiled LangChain agent here
|
|
88
|
+
tracer_provider=tracer_provider,
|
|
87
89
|
)
|
|
88
90
|
|
|
89
91
|
# Create and start server with Cloudflare tunnel
|
|
@@ -98,14 +100,18 @@ app = server.create_app(
|
|
|
98
100
|
tunnel="cloudflare", # Options: "cloudflare", "ngrok", or "none"
|
|
99
101
|
)
|
|
100
102
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
+
# Option 1: Use the start method to start the server
|
|
104
|
+
asyncio.run(server.start(host="0.0.0.0", port=8080))
|
|
105
|
+
|
|
106
|
+
# Option 2: Use uvicorn to start the server for auto-reload
|
|
107
|
+
# if __name__ == "__main__":
|
|
108
|
+
# uvicorn.run("local_server:app", host="0.0.0.0", port=8080, reload=True)
|
|
103
109
|
```
|
|
104
110
|
|
|
105
111
|
The server exposes:
|
|
106
112
|
|
|
107
113
|
- `GET /` - Health check endpoint
|
|
108
|
-
- `POST /v1/agents/completions` - Agent invocation endpoint
|
|
114
|
+
- `POST /v1/agents/completions` - Agent invocation endpoint. This is the endpoint that Quraite will use to invoke your agent.
|
|
109
115
|
|
|
110
116
|
When using `tunnel="cloudflare"` or `tunnel="ngrok"`, your agent will be publicly accessible via the generated URL.
|
|
111
117
|
|
|
@@ -113,7 +119,7 @@ When using `tunnel="cloudflare"` or `tunnel="ngrok"`, your agent will be publicl
|
|
|
113
119
|
|
|
114
120
|
| Framework | Adapter | Installation |
|
|
115
121
|
| -------------------- | ------------------------ | -------------------------------------- |
|
|
116
|
-
| **
|
|
122
|
+
| **LangChain** | `LangChainAdapter` | `pip install 'quraite[langchain]'` |
|
|
117
123
|
| **Pydantic AI** | `PydanticAIAdapter` | `pip install 'quraite[pydantic-ai]'` |
|
|
118
124
|
| **Agno** | `AgnoAdapter` | `pip install 'quraite[agno]'` |
|
|
119
125
|
| **Google ADK** | `GoogleADKAdapter` | `pip install 'quraite[google-adk]'` |
|
|
@@ -124,27 +130,28 @@ When using `tunnel="cloudflare"` or `tunnel="ngrok"`, your agent will be publicl
|
|
|
124
130
|
| **Langflow** | `LangflowAdapter` | Included in base package |
|
|
125
131
|
| **N8n** | `N8nAdapter` | Included in base package |
|
|
126
132
|
| **HTTP** | `HttpAdapter` | Included in base package |
|
|
127
|
-
| **
|
|
133
|
+
| **LangChain Server** | `LangChainServerAdapter` | `pip install 'quraite[langchain]'` |
|
|
128
134
|
|
|
129
135
|
## Core Concepts
|
|
130
136
|
|
|
131
137
|
### Adapters
|
|
132
138
|
|
|
133
|
-
Adapters provide a unified interface (`BaseAdapter`) for different agent frameworks. Each adapter
|
|
139
|
+
Adapters provide a unified interface (`BaseAdapter`) for different agent frameworks. Each adapter converts framework-specific agent response formats to the Quraite agent message format.
|
|
140
|
+
|
|
141
|
+
If you are building your own agent framework, you can create a custom adapter by extending the `BaseAdapter` class and implementing the `ainvoke` method.
|
|
142
|
+
|
|
143
|
+
### Tracing for Agent Trajectory Evaluation
|
|
134
144
|
|
|
135
|
-
|
|
136
|
-
- Handles message format conversion
|
|
137
|
-
- Supports optional tracing integration
|
|
138
|
-
- Provides async invocation via `ainvoke()`
|
|
145
|
+
**Capture agent trajectories without modifying your code.** Get comprehensive trace data including token usage, costs, and latency for every agent step.
|
|
139
146
|
|
|
140
|
-
|
|
147
|
+
Most agent frameworks return agent steps, but lack critical observability data. We solve this with **OpenInference instrumentation** (OpenTelemetry instrumentation support coming soon) that automatically captures:
|
|
141
148
|
|
|
142
|
-
|
|
149
|
+
- Complete agent trajectories
|
|
150
|
+
- Token usage and costs
|
|
151
|
+
- Step-by-step latency
|
|
152
|
+
- Full execution context
|
|
143
153
|
|
|
144
|
-
-
|
|
145
|
-
- **Tool Calls**: Tool invocations with inputs and outputs
|
|
146
|
-
- **Performance Metrics**: Token usage, costs, latency
|
|
147
|
-
- **Model Information**: Model name and provider details
|
|
154
|
+
**Works with your existing setup.** We provide OpenInference-compatible span exporters and processors that integrate seamlessly with your current observability platform - no vendor lock-in required.
|
|
148
155
|
|
|
149
156
|
To enable tracing:
|
|
150
157
|
|
|
@@ -154,11 +161,13 @@ from quraite.tracing.span_exporter import QuraiteInMemorySpanExporter
|
|
|
154
161
|
from quraite.tracing.span_processor import QuraiteSimpleSpanProcessor
|
|
155
162
|
|
|
156
163
|
tracer_provider = TracerProvider()
|
|
164
|
+
|
|
165
|
+
# Add Quraite span exporter and processor to the tracer provider
|
|
157
166
|
quraite_span_exporter = QuraiteInMemorySpanExporter()
|
|
158
167
|
quraite_span_processor = QuraiteSimpleSpanProcessor(quraite_span_exporter)
|
|
159
168
|
tracer_provider.add_span_processor(quraite_span_processor)
|
|
160
169
|
|
|
161
|
-
# Instrument your framework
|
|
170
|
+
# Instrument your framework with OpenInference
|
|
162
171
|
from openinference.instrumentation.langchain import LangChainInstrumentor
|
|
163
172
|
LangChainInstrumentor().instrument(tracer_provider=tracer_provider)
|
|
164
173
|
```
|
|
@@ -216,18 +225,15 @@ response: AgentInvocationResponse = await adapter.ainvoke(
|
|
|
216
225
|
# Access trajectory (list of messages)
|
|
217
226
|
trajectory = response.agent_trajectory
|
|
218
227
|
|
|
219
|
-
# Access trace
|
|
228
|
+
# Access trace
|
|
220
229
|
trace = response.agent_trace
|
|
221
|
-
|
|
222
|
-
# Access final response text
|
|
223
|
-
final_response = response.agent_final_response
|
|
224
230
|
```
|
|
225
231
|
|
|
226
232
|
## Examples
|
|
227
233
|
|
|
228
234
|
The repository includes comprehensive examples for each supported framework:
|
|
229
235
|
|
|
230
|
-
- [`
|
|
236
|
+
- [`langchain_calculator_agent`](examples/langchain_calculator_agent/) - LangChain calculator agent
|
|
231
237
|
- [`pydantic_calculator_agent`](examples/pydantic_calculator_agent/) - Pydantic AI calculator agent
|
|
232
238
|
- [`agno_calculator_agent`](examples/agno_calculator_agent/) - Agno calculator agent
|
|
233
239
|
- [`google_adk_weather_agent`](examples/google_adk_weather_agent/) - Google ADK weather agent
|
|
@@ -267,6 +273,7 @@ class MyAdapter(BaseAdapter):
|
|
|
267
273
|
Create a local HTTP server for your agent:
|
|
268
274
|
|
|
269
275
|
```python
|
|
276
|
+
import asyncio
|
|
270
277
|
from quraite.serve.local_agent import LocalAgentServer
|
|
271
278
|
|
|
272
279
|
server = LocalAgentServer(
|
|
@@ -279,6 +286,8 @@ app = server.create_app(
|
|
|
279
286
|
host="0.0.0.0",
|
|
280
287
|
tunnel="cloudflare", # or "ngrok" or "none"
|
|
281
288
|
)
|
|
289
|
+
|
|
290
|
+
asyncio.run(server.start(host="0.0.0.0", port=8080))
|
|
282
291
|
```
|
|
283
292
|
|
|
284
293
|
## Development
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "quraite"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.2"
|
|
4
4
|
description = "This project provides adaptors and methods to integrate with the Quraite platform"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -24,7 +24,7 @@ agno = ["agno>=2.3.4"]
|
|
|
24
24
|
|
|
25
25
|
google-adk = ["google-adk>=1.18.0"]
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
langchain = [
|
|
28
28
|
"langchain>=1.0.5",
|
|
29
29
|
"langgraph>=1.0.3"
|
|
30
30
|
]
|
|
@@ -9,8 +9,8 @@ if TYPE_CHECKING:
|
|
|
9
9
|
from quraite.adapters.flowise_adapter import FlowiseAdapter
|
|
10
10
|
from quraite.adapters.google_adk_adapter import GoogleADKAdapter
|
|
11
11
|
from quraite.adapters.langflow_adapter import LangflowAdapter
|
|
12
|
-
from quraite.adapters.
|
|
13
|
-
from quraite.adapters.
|
|
12
|
+
from quraite.adapters.langchain_adapter import LangchainAdapter
|
|
13
|
+
from quraite.adapters.langchain_server_adapter import LangchainServerAdapter
|
|
14
14
|
from quraite.adapters.n8n_adapter import N8nAdapter
|
|
15
15
|
from quraite.adapters.openai_agents_adapter import OpenaiAgentsAdapter
|
|
16
16
|
from quraite.adapters.pydantic_ai_adapter import PydanticAIAdapter
|
|
@@ -25,8 +25,8 @@ __all__ = [
|
|
|
25
25
|
"FlowiseAdapter",
|
|
26
26
|
"GoogleADKAdapter",
|
|
27
27
|
"LangflowAdapter",
|
|
28
|
-
"
|
|
29
|
-
"
|
|
28
|
+
"LangchainAdapter",
|
|
29
|
+
"LangchainServerAdapter",
|
|
30
30
|
"N8nAdapter",
|
|
31
31
|
"OpenaiAgentsAdapter",
|
|
32
32
|
"PydanticAIAdapter",
|
|
@@ -76,24 +76,24 @@ def __getattr__(name: str):
|
|
|
76
76
|
|
|
77
77
|
return LangflowAdapter
|
|
78
78
|
|
|
79
|
-
elif name == "
|
|
79
|
+
elif name == "LangchainAdapter":
|
|
80
80
|
try:
|
|
81
|
-
from quraite.adapters.
|
|
81
|
+
from quraite.adapters.langchain_adapter import LangchainAdapter
|
|
82
82
|
|
|
83
|
-
return
|
|
83
|
+
return LangchainAdapter
|
|
84
84
|
except ImportError as e:
|
|
85
85
|
raise ImportError(
|
|
86
|
-
f"Failed to import {name}. Please install the '
|
|
86
|
+
f"Failed to import {name}. Please install the 'langchain' optional dependency: pip install 'quraite[langchain]'"
|
|
87
87
|
) from e
|
|
88
88
|
|
|
89
|
-
elif name == "
|
|
89
|
+
elif name == "LangchainServerAdapter":
|
|
90
90
|
try:
|
|
91
|
-
from quraite.adapters.
|
|
91
|
+
from quraite.adapters.langchain_server_adapter import LangchainServerAdapter
|
|
92
92
|
|
|
93
|
-
return
|
|
93
|
+
return LangchainServerAdapter
|
|
94
94
|
except ImportError as e:
|
|
95
95
|
raise ImportError(
|
|
96
|
-
f"Failed to import {name}. Please install the '
|
|
96
|
+
f"Failed to import {name}. Please install the 'langchain' optional dependency: pip install 'quraite[langchain]'"
|
|
97
97
|
) from e
|
|
98
98
|
|
|
99
99
|
elif name == "N8nAdapter":
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import uuid
|
|
2
1
|
from typing import List, Union
|
|
3
2
|
|
|
4
3
|
from agno.agent import Agent
|
|
@@ -9,7 +8,7 @@ from quraite.adapters.base import BaseAdapter
|
|
|
9
8
|
from quraite.logger import get_logger
|
|
10
9
|
from quraite.schema.message import AgentMessage
|
|
11
10
|
from quraite.schema.response import AgentInvocationResponse
|
|
12
|
-
from quraite.tracing.constants import
|
|
11
|
+
from quraite.tracing.constants import Framework
|
|
13
12
|
from quraite.tracing.trace import AgentSpan, AgentTrace
|
|
14
13
|
|
|
15
14
|
logger = get_logger(__name__)
|
|
@@ -122,27 +121,26 @@ class AgnoAdapter(BaseAdapter):
|
|
|
122
121
|
session_id: str,
|
|
123
122
|
) -> AgentInvocationResponse:
|
|
124
123
|
"""Execute ainvoke with tracing enabled."""
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
with self.tracer.start_as_current_span(name=adapter_trace_id):
|
|
124
|
+
with self.tracer.start_as_current_span("agno_invocation") as span:
|
|
125
|
+
trace_id = span.get_span_context().trace_id
|
|
126
|
+
logger.debug(
|
|
127
|
+
"Starting traced invocation (session_id=%s) with trace_id=%s",
|
|
128
|
+
session_id,
|
|
129
|
+
trace_id,
|
|
130
|
+
)
|
|
133
131
|
# Run the agent/team
|
|
134
132
|
await self.agent.arun(agent_input, session_id=session_id)
|
|
135
133
|
|
|
136
134
|
# Get trace spans
|
|
137
|
-
trace_readable_spans = self.quraite_span_exporter.
|
|
138
|
-
|
|
135
|
+
trace_readable_spans = self.quraite_span_exporter.get_spans_by_trace_id(
|
|
136
|
+
trace_id
|
|
139
137
|
)
|
|
140
138
|
|
|
141
139
|
if trace_readable_spans:
|
|
142
140
|
logger.info(
|
|
143
141
|
"Retrieved %d spans for trace_id=%s",
|
|
144
142
|
len(trace_readable_spans),
|
|
145
|
-
|
|
143
|
+
trace_id,
|
|
146
144
|
)
|
|
147
145
|
agent_trace = AgentTrace(
|
|
148
146
|
spans=[
|
|
@@ -151,7 +149,7 @@ class AgnoAdapter(BaseAdapter):
|
|
|
151
149
|
],
|
|
152
150
|
)
|
|
153
151
|
else:
|
|
154
|
-
logger.warning("No spans found for trace_id=%s",
|
|
152
|
+
logger.warning("No spans found for trace_id=%s", trace_id)
|
|
155
153
|
|
|
156
154
|
return AgentInvocationResponse(
|
|
157
155
|
agent_trace=agent_trace,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
from typing import Any, List, Optional, Union
|
|
3
3
|
|
|
4
|
-
from opentelemetry.trace import TracerProvider
|
|
4
|
+
from opentelemetry.trace import Tracer, TracerProvider
|
|
5
5
|
|
|
6
6
|
from quraite.schema.message import AgentMessage, AssistantMessage, MessageContentText
|
|
7
7
|
from quraite.schema.response import AgentInvocationResponse
|
|
@@ -22,7 +22,7 @@ class BaseAdapter(ABC):
|
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
24
|
tracer_provider: Optional[TracerProvider] = None
|
|
25
|
-
tracer: Optional[
|
|
25
|
+
tracer: Optional[Tracer] = None
|
|
26
26
|
quraite_span_exporter: Optional[QuraiteInMemorySpanExporter] = None
|
|
27
27
|
|
|
28
28
|
def _init_tracing(
|
|
@@ -13,7 +13,7 @@ from quraite.adapters.base import BaseAdapter
|
|
|
13
13
|
from quraite.logger import get_logger
|
|
14
14
|
from quraite.schema.message import AgentMessage
|
|
15
15
|
from quraite.schema.response import AgentInvocationResponse
|
|
16
|
-
from quraite.tracing.constants import
|
|
16
|
+
from quraite.tracing.constants import Framework
|
|
17
17
|
from quraite.tracing.trace import AgentSpan, AgentTrace
|
|
18
18
|
|
|
19
19
|
logger = get_logger(__name__)
|
|
@@ -141,14 +141,18 @@ class GoogleADKAdapter(BaseAdapter):
|
|
|
141
141
|
session_id: str,
|
|
142
142
|
) -> AgentInvocationResponse:
|
|
143
143
|
"""Execute ainvoke with tracing enabled."""
|
|
144
|
-
adapter_trace_id = f"{QURAITE_ADAPTER_TRACE_PREFIX}-{uuid.uuid4()}"
|
|
145
144
|
logger.debug(
|
|
146
|
-
"Starting Google ADK traced invocation (
|
|
147
|
-
adapter_trace_id,
|
|
145
|
+
"Starting Google ADK traced invocation (session_id=%s)",
|
|
148
146
|
session_id,
|
|
149
147
|
)
|
|
150
148
|
|
|
151
|
-
with self.tracer.start_as_current_span(
|
|
149
|
+
with self.tracer.start_as_current_span("google_adk_invocation") as span:
|
|
150
|
+
trace_id = span.get_span_context().trace_id
|
|
151
|
+
logger.debug(
|
|
152
|
+
"Starting Google ADK traced invocation (trace_id=%s, session_id=%s)",
|
|
153
|
+
trace_id,
|
|
154
|
+
session_id,
|
|
155
|
+
)
|
|
152
156
|
# Create session if it doesn't exist
|
|
153
157
|
try:
|
|
154
158
|
await self.session_service.create_session(
|
|
@@ -180,8 +184,8 @@ class GoogleADKAdapter(BaseAdapter):
|
|
|
180
184
|
pass # Just consume events, tracing handles capture
|
|
181
185
|
|
|
182
186
|
# Get trace spans
|
|
183
|
-
trace_readable_spans = self.quraite_span_exporter.
|
|
184
|
-
|
|
187
|
+
trace_readable_spans = self.quraite_span_exporter.get_spans_by_trace_id(
|
|
188
|
+
trace_id
|
|
185
189
|
)
|
|
186
190
|
|
|
187
191
|
if trace_readable_spans:
|
|
@@ -194,12 +198,10 @@ class GoogleADKAdapter(BaseAdapter):
|
|
|
194
198
|
logger.info(
|
|
195
199
|
"Google ADK trace collected %d spans for trace_id=%s",
|
|
196
200
|
len(trace_readable_spans),
|
|
197
|
-
|
|
201
|
+
trace_id,
|
|
198
202
|
)
|
|
199
203
|
else:
|
|
200
|
-
logger.warning(
|
|
201
|
-
"No spans exported for Google ADK trace_id=%s", adapter_trace_id
|
|
202
|
-
)
|
|
204
|
+
logger.warning("No spans exported for Google ADK trace_id=%s", trace_id)
|
|
203
205
|
|
|
204
206
|
return AgentInvocationResponse(
|
|
205
207
|
agent_trace=agent_trace,
|