aiqa-client 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.
- {aiqa_client-0.1.1/aiqa_client.egg-info → aiqa_client-0.1.2}/PKG-INFO +95 -4
- aiqa_client-0.1.2/README.md +210 -0
- aiqa_client-0.1.2/aiqa/__init__.py +66 -0
- {aiqa_client-0.1.1 → aiqa_client-0.1.2}/aiqa/aiqa_exporter.py +219 -60
- aiqa_client-0.1.2/aiqa/client.py +170 -0
- aiqa_client-0.1.2/aiqa/experiment_runner.py +336 -0
- aiqa_client-0.1.2/aiqa/object_serialiser.py +361 -0
- aiqa_client-0.1.2/aiqa/test_experiment_runner.py +176 -0
- aiqa_client-0.1.2/aiqa/test_tracing.py +230 -0
- aiqa_client-0.1.2/aiqa/tracing.py +1256 -0
- {aiqa_client-0.1.1 → aiqa_client-0.1.2/aiqa_client.egg-info}/PKG-INFO +95 -4
- {aiqa_client-0.1.1 → aiqa_client-0.1.2}/aiqa_client.egg-info/SOURCES.txt +5 -0
- {aiqa_client-0.1.1 → aiqa_client-0.1.2}/aiqa_client.egg-info/requires.txt +1 -0
- {aiqa_client-0.1.1 → aiqa_client-0.1.2}/pyproject.toml +2 -1
- aiqa_client-0.1.1/README.md +0 -120
- aiqa_client-0.1.1/aiqa/__init__.py +0 -29
- aiqa_client-0.1.1/aiqa/tracing.py +0 -315
- {aiqa_client-0.1.1 → aiqa_client-0.1.2}/LICENSE +0 -0
- {aiqa_client-0.1.1 → aiqa_client-0.1.2}/MANIFEST.in +0 -0
- {aiqa_client-0.1.1 → aiqa_client-0.1.2}/aiqa/py.typed +0 -0
- {aiqa_client-0.1.1 → aiqa_client-0.1.2}/aiqa_client.egg-info/dependency_links.txt +0 -0
- {aiqa_client-0.1.1 → aiqa_client-0.1.2}/aiqa_client.egg-info/top_level.txt +0 -0
- {aiqa_client-0.1.1 → aiqa_client-0.1.2}/setup.cfg +0 -0
- {aiqa_client-0.1.1 → aiqa_client-0.1.2}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aiqa-client
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: OpenTelemetry-based Python client for tracing functions and sending traces to the AIQA server
|
|
5
5
|
Author-email: AIQA <info@aiqa.dev>
|
|
6
6
|
License: MIT
|
|
@@ -27,6 +27,7 @@ Requires-Dist: opentelemetry-api>=1.24.0
|
|
|
27
27
|
Requires-Dist: opentelemetry-sdk>=1.24.0
|
|
28
28
|
Requires-Dist: opentelemetry-semantic-conventions>=0.40b0
|
|
29
29
|
Requires-Dist: aiohttp>=3.9.0
|
|
30
|
+
Requires-Dist: requests>=2.31.0
|
|
30
31
|
Provides-Extra: dev
|
|
31
32
|
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
32
33
|
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
@@ -71,7 +72,15 @@ export AIQA_API_KEY="your-api-key"
|
|
|
71
72
|
### Basic Usage
|
|
72
73
|
|
|
73
74
|
```python
|
|
74
|
-
from
|
|
75
|
+
from dotenv import load_dotenv
|
|
76
|
+
from aiqa import get_aiqa_client, WithTracing
|
|
77
|
+
|
|
78
|
+
# Load environment variables from .env file (if using one)
|
|
79
|
+
load_dotenv()
|
|
80
|
+
|
|
81
|
+
# Initialize client (must be called before using WithTracing)
|
|
82
|
+
# This loads environment variables and initializes the tracing system
|
|
83
|
+
get_aiqa_client()
|
|
75
84
|
|
|
76
85
|
@WithTracing
|
|
77
86
|
def my_function(x, y):
|
|
@@ -107,12 +116,12 @@ def my_function(data):
|
|
|
107
116
|
Spans are automatically flushed every 5 seconds. To flush immediately:
|
|
108
117
|
|
|
109
118
|
```python
|
|
110
|
-
from aiqa import
|
|
119
|
+
from aiqa import flush_tracing
|
|
111
120
|
import asyncio
|
|
112
121
|
|
|
113
122
|
async def main():
|
|
114
123
|
# Your code here
|
|
115
|
-
await
|
|
124
|
+
await flush_tracing()
|
|
116
125
|
|
|
117
126
|
asyncio.run(main())
|
|
118
127
|
```
|
|
@@ -143,6 +152,87 @@ def my_function():
|
|
|
143
152
|
# ... rest of function
|
|
144
153
|
```
|
|
145
154
|
|
|
155
|
+
### Grouping Traces by Conversation
|
|
156
|
+
|
|
157
|
+
To group multiple traces together that are part of the same conversation or session:
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
from aiqa import WithTracing, set_conversation_id
|
|
161
|
+
|
|
162
|
+
@WithTracing
|
|
163
|
+
def handle_user_request(user_id: str, session_id: str):
|
|
164
|
+
# Set conversation ID to group all traces for this user session
|
|
165
|
+
set_conversation_id(f"user_{user_id}_session_{session_id}")
|
|
166
|
+
# All spans created in this function and its children will have this gen_ai.conversation.id
|
|
167
|
+
# ... rest of function
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
The `gen_ai.conversation.id` attribute allows you to filter and group traces in the AIQA server by conversation, making it easier to analyze multi-step interactions or user sessions. See the [OpenTelemetry GenAI Events specification](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-events/) for more details.
|
|
171
|
+
|
|
172
|
+
### Trace ID Propagation Across Services/Agents
|
|
173
|
+
|
|
174
|
+
To link traces across different services or agents, you can extract and propagate trace IDs:
|
|
175
|
+
|
|
176
|
+
#### Getting Current Trace ID
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
from aiqa import get_trace_id, get_span_id
|
|
180
|
+
|
|
181
|
+
# Get the current trace ID and span ID
|
|
182
|
+
trace_id = get_trace_id() # Returns hex string (32 chars) or None
|
|
183
|
+
span_id = get_span_id() # Returns hex string (16 chars) or None
|
|
184
|
+
|
|
185
|
+
# Pass these to another service (e.g., in HTTP headers, message queue, etc.)
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
#### Continuing a Trace in Another Service
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
from aiqa import create_span_from_trace_id
|
|
192
|
+
|
|
193
|
+
# Continue a trace from another service/agent
|
|
194
|
+
# trace_id and parent_span_id come from the other service
|
|
195
|
+
with create_span_from_trace_id(
|
|
196
|
+
trace_id="abc123...",
|
|
197
|
+
parent_span_id="def456...",
|
|
198
|
+
span_name="service_b_operation"
|
|
199
|
+
):
|
|
200
|
+
# Your code here - this span will be linked to the original trace
|
|
201
|
+
pass
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
#### Using OpenTelemetry Context Propagation (Recommended)
|
|
205
|
+
|
|
206
|
+
For HTTP requests, use the built-in context propagation:
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
from aiqa import inject_trace_context, extract_trace_context
|
|
210
|
+
import requests
|
|
211
|
+
from opentelemetry.trace import use_span
|
|
212
|
+
|
|
213
|
+
# In the sending service:
|
|
214
|
+
headers = {}
|
|
215
|
+
inject_trace_context(headers) # Adds trace context to headers
|
|
216
|
+
response = requests.get("http://other-service/api", headers=headers)
|
|
217
|
+
|
|
218
|
+
# In the receiving service:
|
|
219
|
+
# Extract context from incoming request headers
|
|
220
|
+
ctx = extract_trace_context(request.headers)
|
|
221
|
+
|
|
222
|
+
# Use the context to create a span
|
|
223
|
+
from opentelemetry.trace import use_span
|
|
224
|
+
with use_span(ctx):
|
|
225
|
+
# Your code here
|
|
226
|
+
pass
|
|
227
|
+
|
|
228
|
+
# Or create a span with the context
|
|
229
|
+
from opentelemetry import trace
|
|
230
|
+
tracer = trace.get_tracer("aiqa-tracer")
|
|
231
|
+
with tracer.start_as_current_span("operation", context=ctx):
|
|
232
|
+
# Your code here
|
|
233
|
+
pass
|
|
234
|
+
```
|
|
235
|
+
|
|
146
236
|
## Features
|
|
147
237
|
|
|
148
238
|
- Automatic tracing of function calls (sync and async)
|
|
@@ -150,6 +240,7 @@ def my_function():
|
|
|
150
240
|
- Automatic error tracking and exception recording
|
|
151
241
|
- Thread-safe span buffering and auto-flushing
|
|
152
242
|
- OpenTelemetry context propagation for nested spans
|
|
243
|
+
- Trace ID propagation utilities for distributed tracing
|
|
153
244
|
|
|
154
245
|
## Example
|
|
155
246
|
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# A Python client for the AIQA server
|
|
2
|
+
|
|
3
|
+
OpenTelemetry-based client for tracing Python functions and sending traces to the AIQA server.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### From PyPI (recommended)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install aiqa-client
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### From source
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
python -m venv .venv
|
|
17
|
+
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
|
18
|
+
pip install -r requirements.txt
|
|
19
|
+
pip install -e .
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
See [TESTING.md](TESTING.md) for detailed testing instructions.
|
|
23
|
+
|
|
24
|
+
## Setup
|
|
25
|
+
|
|
26
|
+
Set the following environment variables:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
export AIQA_SERVER_URL="http://localhost:3000"
|
|
30
|
+
export AIQA_API_KEY="your-api-key"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
### Basic Usage
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from dotenv import load_dotenv
|
|
39
|
+
from aiqa import get_aiqa_client, WithTracing
|
|
40
|
+
|
|
41
|
+
# Load environment variables from .env file (if using one)
|
|
42
|
+
load_dotenv()
|
|
43
|
+
|
|
44
|
+
# Initialize client (must be called before using WithTracing)
|
|
45
|
+
# This loads environment variables and initializes the tracing system
|
|
46
|
+
get_aiqa_client()
|
|
47
|
+
|
|
48
|
+
@WithTracing
|
|
49
|
+
def my_function(x, y):
|
|
50
|
+
return x + y
|
|
51
|
+
|
|
52
|
+
@WithTracing
|
|
53
|
+
async def my_async_function(x, y):
|
|
54
|
+
await asyncio.sleep(0.1)
|
|
55
|
+
return x * y
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Custom Span Name
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
@WithTracing(name="custom_span_name")
|
|
62
|
+
def my_function():
|
|
63
|
+
pass
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Input/Output Filtering
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
@WithTracing(
|
|
70
|
+
filter_input=lambda x: {"filtered": str(x)},
|
|
71
|
+
filter_output=lambda x: {"result": x}
|
|
72
|
+
)
|
|
73
|
+
def my_function(data):
|
|
74
|
+
return {"processed": data}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Flushing Spans
|
|
78
|
+
|
|
79
|
+
Spans are automatically flushed every 5 seconds. To flush immediately:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from aiqa import flush_tracing
|
|
83
|
+
import asyncio
|
|
84
|
+
|
|
85
|
+
async def main():
|
|
86
|
+
# Your code here
|
|
87
|
+
await flush_tracing()
|
|
88
|
+
|
|
89
|
+
asyncio.run(main())
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Shutting Down
|
|
93
|
+
|
|
94
|
+
To ensure all spans are sent before process exit:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from aiqa import shutdown_tracing
|
|
98
|
+
import asyncio
|
|
99
|
+
|
|
100
|
+
async def main():
|
|
101
|
+
# Your code here
|
|
102
|
+
await shutdown_tracing()
|
|
103
|
+
|
|
104
|
+
asyncio.run(main())
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Setting Span Attributes and Names
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
from aiqa import set_span_attribute, set_span_name
|
|
111
|
+
|
|
112
|
+
def my_function():
|
|
113
|
+
set_span_attribute("custom.attribute", "value")
|
|
114
|
+
set_span_name("custom_span_name")
|
|
115
|
+
# ... rest of function
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Grouping Traces by Conversation
|
|
119
|
+
|
|
120
|
+
To group multiple traces together that are part of the same conversation or session:
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
from aiqa import WithTracing, set_conversation_id
|
|
124
|
+
|
|
125
|
+
@WithTracing
|
|
126
|
+
def handle_user_request(user_id: str, session_id: str):
|
|
127
|
+
# Set conversation ID to group all traces for this user session
|
|
128
|
+
set_conversation_id(f"user_{user_id}_session_{session_id}")
|
|
129
|
+
# All spans created in this function and its children will have this gen_ai.conversation.id
|
|
130
|
+
# ... rest of function
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The `gen_ai.conversation.id` attribute allows you to filter and group traces in the AIQA server by conversation, making it easier to analyze multi-step interactions or user sessions. See the [OpenTelemetry GenAI Events specification](https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-events/) for more details.
|
|
134
|
+
|
|
135
|
+
### Trace ID Propagation Across Services/Agents
|
|
136
|
+
|
|
137
|
+
To link traces across different services or agents, you can extract and propagate trace IDs:
|
|
138
|
+
|
|
139
|
+
#### Getting Current Trace ID
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from aiqa import get_trace_id, get_span_id
|
|
143
|
+
|
|
144
|
+
# Get the current trace ID and span ID
|
|
145
|
+
trace_id = get_trace_id() # Returns hex string (32 chars) or None
|
|
146
|
+
span_id = get_span_id() # Returns hex string (16 chars) or None
|
|
147
|
+
|
|
148
|
+
# Pass these to another service (e.g., in HTTP headers, message queue, etc.)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
#### Continuing a Trace in Another Service
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from aiqa import create_span_from_trace_id
|
|
155
|
+
|
|
156
|
+
# Continue a trace from another service/agent
|
|
157
|
+
# trace_id and parent_span_id come from the other service
|
|
158
|
+
with create_span_from_trace_id(
|
|
159
|
+
trace_id="abc123...",
|
|
160
|
+
parent_span_id="def456...",
|
|
161
|
+
span_name="service_b_operation"
|
|
162
|
+
):
|
|
163
|
+
# Your code here - this span will be linked to the original trace
|
|
164
|
+
pass
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
#### Using OpenTelemetry Context Propagation (Recommended)
|
|
168
|
+
|
|
169
|
+
For HTTP requests, use the built-in context propagation:
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
from aiqa import inject_trace_context, extract_trace_context
|
|
173
|
+
import requests
|
|
174
|
+
from opentelemetry.trace import use_span
|
|
175
|
+
|
|
176
|
+
# In the sending service:
|
|
177
|
+
headers = {}
|
|
178
|
+
inject_trace_context(headers) # Adds trace context to headers
|
|
179
|
+
response = requests.get("http://other-service/api", headers=headers)
|
|
180
|
+
|
|
181
|
+
# In the receiving service:
|
|
182
|
+
# Extract context from incoming request headers
|
|
183
|
+
ctx = extract_trace_context(request.headers)
|
|
184
|
+
|
|
185
|
+
# Use the context to create a span
|
|
186
|
+
from opentelemetry.trace import use_span
|
|
187
|
+
with use_span(ctx):
|
|
188
|
+
# Your code here
|
|
189
|
+
pass
|
|
190
|
+
|
|
191
|
+
# Or create a span with the context
|
|
192
|
+
from opentelemetry import trace
|
|
193
|
+
tracer = trace.get_tracer("aiqa-tracer")
|
|
194
|
+
with tracer.start_as_current_span("operation", context=ctx):
|
|
195
|
+
# Your code here
|
|
196
|
+
pass
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Features
|
|
200
|
+
|
|
201
|
+
- Automatic tracing of function calls (sync and async)
|
|
202
|
+
- Records function inputs and outputs as span attributes
|
|
203
|
+
- Automatic error tracking and exception recording
|
|
204
|
+
- Thread-safe span buffering and auto-flushing
|
|
205
|
+
- OpenTelemetry context propagation for nested spans
|
|
206
|
+
- Trace ID propagation utilities for distributed tracing
|
|
207
|
+
|
|
208
|
+
## Example
|
|
209
|
+
|
|
210
|
+
See `example.py` for a complete working example.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Python client for AIQA server - OpenTelemetry tracing decorators.
|
|
3
|
+
|
|
4
|
+
IMPORTANT: Before using any AIQA functionality, you must call get_aiqa_client() to initialize
|
|
5
|
+
the client and load environment variables (AIQA_SERVER_URL, AIQA_API_KEY, AIQA_COMPONENT_TAG, etc.).
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
from dotenv import load_dotenv
|
|
9
|
+
from aiqa import get_aiqa_client, WithTracing
|
|
10
|
+
|
|
11
|
+
# Load environment variables from .env file (if using one)
|
|
12
|
+
load_dotenv()
|
|
13
|
+
|
|
14
|
+
# Initialize client (must be called before using WithTracing or other functions)
|
|
15
|
+
get_aiqa_client()
|
|
16
|
+
|
|
17
|
+
@WithTracing
|
|
18
|
+
def my_function():
|
|
19
|
+
return "Hello, AIQA!"
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from .tracing import (
|
|
23
|
+
WithTracing,
|
|
24
|
+
flush_tracing,
|
|
25
|
+
shutdown_tracing,
|
|
26
|
+
set_span_attribute,
|
|
27
|
+
set_span_name,
|
|
28
|
+
get_active_span,
|
|
29
|
+
get_provider,
|
|
30
|
+
get_exporter,
|
|
31
|
+
get_trace_id,
|
|
32
|
+
get_span_id,
|
|
33
|
+
create_span_from_trace_id,
|
|
34
|
+
inject_trace_context,
|
|
35
|
+
extract_trace_context,
|
|
36
|
+
set_conversation_id,
|
|
37
|
+
set_component_tag,
|
|
38
|
+
get_span,
|
|
39
|
+
)
|
|
40
|
+
from .client import get_aiqa_client
|
|
41
|
+
from .experiment_runner import ExperimentRunner
|
|
42
|
+
|
|
43
|
+
__version__ = "0.1.2"
|
|
44
|
+
|
|
45
|
+
__all__ = [
|
|
46
|
+
"WithTracing",
|
|
47
|
+
"flush_tracing",
|
|
48
|
+
"shutdown_tracing",
|
|
49
|
+
"set_span_attribute",
|
|
50
|
+
"set_span_name",
|
|
51
|
+
"get_active_span",
|
|
52
|
+
"get_provider",
|
|
53
|
+
"get_exporter",
|
|
54
|
+
"get_aiqa_client",
|
|
55
|
+
"ExperimentRunner",
|
|
56
|
+
"get_trace_id",
|
|
57
|
+
"get_span_id",
|
|
58
|
+
"create_span_from_trace_id",
|
|
59
|
+
"inject_trace_context",
|
|
60
|
+
"extract_trace_context",
|
|
61
|
+
"set_conversation_id",
|
|
62
|
+
"set_component_tag",
|
|
63
|
+
"get_span",
|
|
64
|
+
"__version__",
|
|
65
|
+
]
|
|
66
|
+
|