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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aiqa-client
3
- Version: 0.1.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 aiqa import WithTracing
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 flush_spans
119
+ from aiqa import flush_tracing
111
120
  import asyncio
112
121
 
113
122
  async def main():
114
123
  # Your code here
115
- await flush_spans()
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
+