aiqa-client 0.3.4__tar.gz → 0.3.6__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.3.4/aiqa_client.egg-info → aiqa_client-0.3.6}/PKG-INFO +30 -3
- {aiqa_client-0.3.4 → aiqa_client-0.3.6}/README.md +29 -2
- {aiqa_client-0.3.4 → aiqa_client-0.3.6}/aiqa/__init__.py +5 -4
- {aiqa_client-0.3.4 → aiqa_client-0.3.6}/aiqa/aiqa_exporter.py +12 -0
- {aiqa_client-0.3.4 → aiqa_client-0.3.6}/aiqa/client.py +126 -12
- {aiqa_client-0.3.4 → aiqa_client-0.3.6}/aiqa/tracing.py +31 -18
- {aiqa_client-0.3.4 → aiqa_client-0.3.6/aiqa_client.egg-info}/PKG-INFO +30 -3
- {aiqa_client-0.3.4 → aiqa_client-0.3.6}/pyproject.toml +1 -1
- {aiqa_client-0.3.4 → aiqa_client-0.3.6}/LICENSE +0 -0
- {aiqa_client-0.3.4 → aiqa_client-0.3.6}/MANIFEST.in +0 -0
- {aiqa_client-0.3.4 → aiqa_client-0.3.6}/aiqa/experiment_runner.py +0 -0
- {aiqa_client-0.3.4 → aiqa_client-0.3.6}/aiqa/object_serialiser.py +0 -0
- {aiqa_client-0.3.4 → aiqa_client-0.3.6}/aiqa/py.typed +0 -0
- {aiqa_client-0.3.4 → aiqa_client-0.3.6}/aiqa/test_experiment_runner.py +0 -0
- {aiqa_client-0.3.4 → aiqa_client-0.3.6}/aiqa/test_tracing.py +0 -0
- {aiqa_client-0.3.4 → aiqa_client-0.3.6}/aiqa_client.egg-info/SOURCES.txt +0 -0
- {aiqa_client-0.3.4 → aiqa_client-0.3.6}/aiqa_client.egg-info/dependency_links.txt +0 -0
- {aiqa_client-0.3.4 → aiqa_client-0.3.6}/aiqa_client.egg-info/requires.txt +0 -0
- {aiqa_client-0.3.4 → aiqa_client-0.3.6}/aiqa_client.egg-info/top_level.txt +0 -0
- {aiqa_client-0.3.4 → aiqa_client-0.3.6}/setup.cfg +0 -0
- {aiqa_client-0.3.4 → aiqa_client-0.3.6}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aiqa-client
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
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
|
|
@@ -67,6 +67,9 @@ export AIQA_SERVER_URL="http://localhost:3000"
|
|
|
67
67
|
export AIQA_API_KEY="your-api-key"
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
+
**Note:** If `AIQA_SERVER_URL` or `AIQA_API_KEY` are not set, tracing will be automatically disabled. You'll see one warning message at the start, and your application will continue to run without tracing.
|
|
71
|
+
You can check if tracing is enabled via `get_aiqa_client().enabled`.
|
|
72
|
+
|
|
70
73
|
## Usage
|
|
71
74
|
|
|
72
75
|
### Basic Usage
|
|
@@ -141,6 +144,30 @@ async def main():
|
|
|
141
144
|
asyncio.run(main())
|
|
142
145
|
```
|
|
143
146
|
|
|
147
|
+
### Enabling/Disabling Tracing
|
|
148
|
+
|
|
149
|
+
You can programmatically enable or disable tracing:
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from aiqa import get_aiqa_client
|
|
153
|
+
|
|
154
|
+
client = get_aiqa_client()
|
|
155
|
+
|
|
156
|
+
# Disable tracing (spans won't be created or exported)
|
|
157
|
+
client.set_enabled(False)
|
|
158
|
+
|
|
159
|
+
# Re-enable tracing
|
|
160
|
+
client.set_enabled(True)
|
|
161
|
+
|
|
162
|
+
# Check if tracing is enabled
|
|
163
|
+
if client.enabled:
|
|
164
|
+
print("Tracing is enabled")
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
When tracing is disabled:
|
|
168
|
+
- Spans are not created (functions execute normally without tracing overhead)
|
|
169
|
+
- Spans are not exported to the server
|
|
170
|
+
|
|
144
171
|
### Setting Span Attributes and Names
|
|
145
172
|
|
|
146
173
|
```python
|
|
@@ -176,10 +203,10 @@ To link traces across different services or agents, you can extract and propagat
|
|
|
176
203
|
#### Getting Current Trace ID
|
|
177
204
|
|
|
178
205
|
```python
|
|
179
|
-
from aiqa import
|
|
206
|
+
from aiqa import get_active_trace_id, get_span_id
|
|
180
207
|
|
|
181
208
|
# Get the current trace ID and span ID
|
|
182
|
-
trace_id =
|
|
209
|
+
trace_id = get_active_trace_id() # Returns hex string (32 chars) or None
|
|
183
210
|
span_id = get_span_id() # Returns hex string (16 chars) or None
|
|
184
211
|
|
|
185
212
|
# Pass these to another service (e.g., in HTTP headers, message queue, etc.)
|
|
@@ -30,6 +30,9 @@ export AIQA_SERVER_URL="http://localhost:3000"
|
|
|
30
30
|
export AIQA_API_KEY="your-api-key"
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
**Note:** If `AIQA_SERVER_URL` or `AIQA_API_KEY` are not set, tracing will be automatically disabled. You'll see one warning message at the start, and your application will continue to run without tracing.
|
|
34
|
+
You can check if tracing is enabled via `get_aiqa_client().enabled`.
|
|
35
|
+
|
|
33
36
|
## Usage
|
|
34
37
|
|
|
35
38
|
### Basic Usage
|
|
@@ -104,6 +107,30 @@ async def main():
|
|
|
104
107
|
asyncio.run(main())
|
|
105
108
|
```
|
|
106
109
|
|
|
110
|
+
### Enabling/Disabling Tracing
|
|
111
|
+
|
|
112
|
+
You can programmatically enable or disable tracing:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from aiqa import get_aiqa_client
|
|
116
|
+
|
|
117
|
+
client = get_aiqa_client()
|
|
118
|
+
|
|
119
|
+
# Disable tracing (spans won't be created or exported)
|
|
120
|
+
client.set_enabled(False)
|
|
121
|
+
|
|
122
|
+
# Re-enable tracing
|
|
123
|
+
client.set_enabled(True)
|
|
124
|
+
|
|
125
|
+
# Check if tracing is enabled
|
|
126
|
+
if client.enabled:
|
|
127
|
+
print("Tracing is enabled")
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
When tracing is disabled:
|
|
131
|
+
- Spans are not created (functions execute normally without tracing overhead)
|
|
132
|
+
- Spans are not exported to the server
|
|
133
|
+
|
|
107
134
|
### Setting Span Attributes and Names
|
|
108
135
|
|
|
109
136
|
```python
|
|
@@ -139,10 +166,10 @@ To link traces across different services or agents, you can extract and propagat
|
|
|
139
166
|
#### Getting Current Trace ID
|
|
140
167
|
|
|
141
168
|
```python
|
|
142
|
-
from aiqa import
|
|
169
|
+
from aiqa import get_active_trace_id, get_span_id
|
|
143
170
|
|
|
144
171
|
# Get the current trace ID and span ID
|
|
145
|
-
trace_id =
|
|
172
|
+
trace_id = get_active_trace_id() # Returns hex string (32 chars) or None
|
|
146
173
|
span_id = get_span_id() # Returns hex string (16 chars) or None
|
|
147
174
|
|
|
148
175
|
# Pass these to another service (e.g., in HTTP headers, message queue, etc.)
|
|
@@ -28,7 +28,7 @@ from .tracing import (
|
|
|
28
28
|
get_active_span,
|
|
29
29
|
get_provider,
|
|
30
30
|
get_exporter,
|
|
31
|
-
|
|
31
|
+
get_active_trace_id,
|
|
32
32
|
get_span_id,
|
|
33
33
|
create_span_from_trace_id,
|
|
34
34
|
inject_trace_context,
|
|
@@ -37,10 +37,10 @@ from .tracing import (
|
|
|
37
37
|
set_component_tag,
|
|
38
38
|
get_span,
|
|
39
39
|
)
|
|
40
|
-
from .client import get_aiqa_client
|
|
40
|
+
from .client import get_aiqa_client, set_enabled
|
|
41
41
|
from .experiment_runner import ExperimentRunner
|
|
42
42
|
|
|
43
|
-
__version__ = "0.3.
|
|
43
|
+
__version__ = "0.3.6"
|
|
44
44
|
|
|
45
45
|
__all__ = [
|
|
46
46
|
"WithTracing",
|
|
@@ -52,8 +52,9 @@ __all__ = [
|
|
|
52
52
|
"get_provider",
|
|
53
53
|
"get_exporter",
|
|
54
54
|
"get_aiqa_client",
|
|
55
|
+
"set_enabled",
|
|
55
56
|
"ExperimentRunner",
|
|
56
|
-
"
|
|
57
|
+
"get_active_trace_id",
|
|
57
58
|
"get_span_id",
|
|
58
59
|
"create_span_from_trace_id",
|
|
59
60
|
"inject_trace_context",
|
|
@@ -71,6 +71,18 @@ class AIQASpanExporter(SpanExporter):
|
|
|
71
71
|
if not spans:
|
|
72
72
|
logger.debug("export() called with empty spans list")
|
|
73
73
|
return SpanExportResult.SUCCESS
|
|
74
|
+
|
|
75
|
+
# Check if AIQA tracing is enabled
|
|
76
|
+
try:
|
|
77
|
+
from .client import get_aiqa_client
|
|
78
|
+
client = get_aiqa_client()
|
|
79
|
+
if not client.enabled:
|
|
80
|
+
logger.debug(f"AIQA export() skipped: tracing is disabled")
|
|
81
|
+
return SpanExportResult.SUCCESS
|
|
82
|
+
except Exception:
|
|
83
|
+
# If we can't check enabled status, proceed (fail open)
|
|
84
|
+
pass
|
|
85
|
+
|
|
74
86
|
logger.debug(f"AIQA export() called with {len(spans)} spans")
|
|
75
87
|
# Serialize and add to buffer, deduplicating by (traceId, spanId)
|
|
76
88
|
with self.buffer_lock:
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import os
|
|
3
3
|
import logging
|
|
4
4
|
from functools import lru_cache
|
|
5
|
+
from typing import Optional
|
|
5
6
|
from opentelemetry import trace
|
|
6
7
|
from opentelemetry.sdk.trace import TracerProvider
|
|
7
8
|
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
@@ -31,10 +32,79 @@ from .aiqa_exporter import AIQASpanExporter
|
|
|
31
32
|
|
|
32
33
|
AIQA_TRACER_NAME = "aiqa-tracer"
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"
|
|
37
|
-
|
|
35
|
+
|
|
36
|
+
class AIQAClient:
|
|
37
|
+
"""
|
|
38
|
+
Singleton client for AIQA tracing.
|
|
39
|
+
|
|
40
|
+
This class manages the tracing provider, exporter, and enabled state.
|
|
41
|
+
Access via get_aiqa_client() which returns the singleton instance.
|
|
42
|
+
"""
|
|
43
|
+
_instance: Optional['AIQAClient'] = None
|
|
44
|
+
|
|
45
|
+
def __new__(cls):
|
|
46
|
+
if cls._instance is None:
|
|
47
|
+
cls._instance = super().__new__(cls)
|
|
48
|
+
cls._instance._provider: Optional[TracerProvider] = None
|
|
49
|
+
cls._instance._exporter: Optional[AIQASpanExporter] = None
|
|
50
|
+
cls._instance._enabled: bool = True
|
|
51
|
+
cls._instance._initialized: bool = False
|
|
52
|
+
return cls._instance
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def provider(self) -> Optional[TracerProvider]:
|
|
56
|
+
"""Get the tracer provider."""
|
|
57
|
+
return self._provider
|
|
58
|
+
|
|
59
|
+
@provider.setter
|
|
60
|
+
def provider(self, value: Optional[TracerProvider]) -> None:
|
|
61
|
+
"""Set the tracer provider."""
|
|
62
|
+
self._provider = value
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def exporter(self) -> Optional[AIQASpanExporter]:
|
|
66
|
+
"""Get the span exporter."""
|
|
67
|
+
return self._exporter
|
|
68
|
+
|
|
69
|
+
@exporter.setter
|
|
70
|
+
def exporter(self, value: Optional[AIQASpanExporter]) -> None:
|
|
71
|
+
"""Set the span exporter."""
|
|
72
|
+
self._exporter = value
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def enabled(self) -> bool:
|
|
76
|
+
"""Check if tracing is enabled."""
|
|
77
|
+
return self._enabled
|
|
78
|
+
|
|
79
|
+
@enabled.setter
|
|
80
|
+
def enabled(self, value: bool) -> None:
|
|
81
|
+
"""Set the enabled state."""
|
|
82
|
+
self._enabled = value
|
|
83
|
+
|
|
84
|
+
def set_enabled(self, enabled: bool) -> None:
|
|
85
|
+
"""
|
|
86
|
+
Enable or disable AIQA tracing.
|
|
87
|
+
|
|
88
|
+
When disabled:
|
|
89
|
+
- Tracing does not create spans
|
|
90
|
+
- Export does not send spans
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
enabled: True to enable tracing, False to disable
|
|
94
|
+
"""
|
|
95
|
+
self._enabled = enabled
|
|
96
|
+
if enabled:
|
|
97
|
+
logger.info("AIQA tracing enabled")
|
|
98
|
+
else:
|
|
99
|
+
logger.info("AIQA tracing disabled")
|
|
100
|
+
|
|
101
|
+
def is_enabled(self) -> bool:
|
|
102
|
+
"""Check if tracing is enabled."""
|
|
103
|
+
return self._enabled
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
# Global singleton instance (for backward compatibility with direct access)
|
|
107
|
+
client: AIQAClient = AIQAClient()
|
|
38
108
|
|
|
39
109
|
# Component tag to add to all spans (can be set via AIQA_COMPONENT_TAG env var or programmatically)
|
|
40
110
|
_component_tag: str = ""
|
|
@@ -52,14 +122,17 @@ def set_component_tag(tag: str | None) -> None:
|
|
|
52
122
|
|
|
53
123
|
|
|
54
124
|
@lru_cache(maxsize=1)
|
|
55
|
-
def get_aiqa_client():
|
|
125
|
+
def get_aiqa_client() -> AIQAClient:
|
|
56
126
|
"""
|
|
57
|
-
Initialize and return the AIQA client.
|
|
127
|
+
Initialize and return the AIQA client singleton.
|
|
58
128
|
|
|
59
129
|
This function must be called before using any AIQA tracing functionality to ensure
|
|
60
130
|
that environment variables (such as AIQA_SERVER_URL, AIQA_API_KEY, AIQA_COMPONENT_TAG)
|
|
61
131
|
are properly loaded and the tracing system is initialized.
|
|
62
132
|
|
|
133
|
+
The client object manages the tracing system state. Tracing is done by the WithTracing
|
|
134
|
+
decorator. Experiments are run by the ExperimentRunner class.
|
|
135
|
+
|
|
63
136
|
The function is idempotent - calling it multiple times is safe and will only
|
|
64
137
|
initialize once.
|
|
65
138
|
|
|
@@ -67,7 +140,7 @@ def get_aiqa_client():
|
|
|
67
140
|
from aiqa import get_aiqa_client, WithTracing
|
|
68
141
|
|
|
69
142
|
# Initialize client (loads env vars)
|
|
70
|
-
get_aiqa_client()
|
|
143
|
+
client = get_aiqa_client()
|
|
71
144
|
|
|
72
145
|
@WithTracing
|
|
73
146
|
def my_function():
|
|
@@ -79,12 +152,32 @@ def get_aiqa_client():
|
|
|
79
152
|
except Exception as e:
|
|
80
153
|
logger.error(f"Failed to initialize AIQA tracing: {e}")
|
|
81
154
|
logger.warning("AIQA tracing is disabled. Your application will continue to run without tracing.")
|
|
82
|
-
# optionally return a richer client object; for now you just need init
|
|
83
155
|
return client
|
|
84
156
|
|
|
85
157
|
def _init_tracing():
|
|
86
158
|
"""Initialize tracing system and load configuration from environment variables."""
|
|
159
|
+
global client
|
|
160
|
+
if client._initialized:
|
|
161
|
+
return
|
|
162
|
+
|
|
87
163
|
try:
|
|
164
|
+
# Check for required environment variables
|
|
165
|
+
server_url = os.getenv("AIQA_SERVER_URL")
|
|
166
|
+
api_key = os.getenv("AIQA_API_KEY")
|
|
167
|
+
|
|
168
|
+
if not server_url or not api_key:
|
|
169
|
+
client.enabled = False
|
|
170
|
+
missing_vars = []
|
|
171
|
+
if not server_url:
|
|
172
|
+
missing_vars.append("AIQA_SERVER_URL")
|
|
173
|
+
if not api_key:
|
|
174
|
+
missing_vars.append("AIQA_API_KEY")
|
|
175
|
+
logger.warning(
|
|
176
|
+
f"AIQA tracing is disabled: missing required environment variables: {', '.join(missing_vars)}"
|
|
177
|
+
)
|
|
178
|
+
client._initialized = True
|
|
179
|
+
return
|
|
180
|
+
|
|
88
181
|
# Initialize component tag from environment variable
|
|
89
182
|
set_component_tag(os.getenv("AIQA_COMPONENT_TAG", None))
|
|
90
183
|
|
|
@@ -114,15 +207,15 @@ def _init_tracing():
|
|
|
114
207
|
|
|
115
208
|
# Idempotently add your processor
|
|
116
209
|
_attach_aiqa_processor(provider)
|
|
117
|
-
|
|
118
|
-
client["provider"] = provider
|
|
210
|
+
client.provider = provider
|
|
119
211
|
|
|
120
212
|
# Log successful initialization
|
|
121
|
-
server_url = os.getenv("AIQA_SERVER_URL", "not configured")
|
|
122
213
|
logger.info(f"AIQA initialized and tracing (sampling rate: {sampling_rate:.2f}, server: {server_url})")
|
|
214
|
+
client._initialized = True
|
|
123
215
|
|
|
124
216
|
except Exception as e:
|
|
125
217
|
logger.error(f"Error initializing AIQA tracing: {e}")
|
|
218
|
+
client._initialized = True # Mark as initialized even on error to prevent retry loops
|
|
126
219
|
raise
|
|
127
220
|
|
|
128
221
|
def _attach_aiqa_processor(provider: TracerProvider):
|
|
@@ -140,7 +233,7 @@ def _attach_aiqa_processor(provider: TracerProvider):
|
|
|
140
233
|
)
|
|
141
234
|
provider.add_span_processor(BatchSpanProcessor(exporter))
|
|
142
235
|
global client
|
|
143
|
-
client
|
|
236
|
+
client.exporter = exporter
|
|
144
237
|
logger.debug("AIQA span processor attached successfully")
|
|
145
238
|
except Exception as e:
|
|
146
239
|
logger.error(f"Error attaching AIQA span processor: {e}")
|
|
@@ -148,6 +241,27 @@ def _attach_aiqa_processor(provider: TracerProvider):
|
|
|
148
241
|
raise
|
|
149
242
|
|
|
150
243
|
|
|
244
|
+
def set_enabled(enabled: bool) -> None:
|
|
245
|
+
"""
|
|
246
|
+
Enable or disable AIQA tracing.
|
|
247
|
+
|
|
248
|
+
When disabled:
|
|
249
|
+
- Tracing does not create spans
|
|
250
|
+
- Export does not send spans
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
enabled: True to enable tracing, False to disable
|
|
254
|
+
|
|
255
|
+
Example:
|
|
256
|
+
from aiqa import get_aiqa_client
|
|
257
|
+
|
|
258
|
+
client = get_aiqa_client()
|
|
259
|
+
client.set_enabled(False) # Disable tracing
|
|
260
|
+
"""
|
|
261
|
+
client = get_aiqa_client()
|
|
262
|
+
client.set_enabled(enabled)
|
|
263
|
+
|
|
264
|
+
|
|
151
265
|
def get_aiqa_tracer():
|
|
152
266
|
"""
|
|
153
267
|
Get the AIQA tracer with version from __init__.py __version__.
|
|
@@ -29,10 +29,10 @@ async def flush_tracing() -> None:
|
|
|
29
29
|
This flushes both the BatchSpanProcessor and the exporter buffer.
|
|
30
30
|
"""
|
|
31
31
|
client = get_aiqa_client()
|
|
32
|
-
if client.
|
|
33
|
-
client
|
|
34
|
-
if client.
|
|
35
|
-
await client
|
|
32
|
+
if client.provider:
|
|
33
|
+
client.provider.force_flush() # Synchronous method
|
|
34
|
+
if client.exporter:
|
|
35
|
+
await client.exporter.flush()
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
async def shutdown_tracing() -> None:
|
|
@@ -42,10 +42,10 @@ async def shutdown_tracing() -> None:
|
|
|
42
42
|
"""
|
|
43
43
|
try:
|
|
44
44
|
client = get_aiqa_client()
|
|
45
|
-
if client.
|
|
46
|
-
client
|
|
47
|
-
if client.
|
|
48
|
-
client
|
|
45
|
+
if client.provider:
|
|
46
|
+
client.provider.shutdown() # Synchronous method
|
|
47
|
+
if client.exporter:
|
|
48
|
+
client.exporter.shutdown() # Synchronous method
|
|
49
49
|
except Exception as e:
|
|
50
50
|
logger.error(f"Error shutting down tracing: {e}")
|
|
51
51
|
|
|
@@ -55,7 +55,7 @@ async def shutdown_tracing() -> None:
|
|
|
55
55
|
__all__ = [
|
|
56
56
|
"get_provider", "get_exporter", "flush_tracing", "shutdown_tracing", "WithTracing",
|
|
57
57
|
"set_span_attribute", "set_span_name", "get_active_span",
|
|
58
|
-
"
|
|
58
|
+
"get_active_trace_id", "get_span_id", "create_span_from_trace_id", "inject_trace_context", "extract_trace_context",
|
|
59
59
|
"set_conversation_id", "set_component_tag", "set_token_usage", "set_provider_and_model", "get_span", "submit_feedback"
|
|
60
60
|
]
|
|
61
61
|
|
|
@@ -640,7 +640,10 @@ def WithTracing(
|
|
|
640
640
|
def _execute_with_span_sync(executor: Callable[[], Any], input_data: Any) -> Any:
|
|
641
641
|
"""Execute sync function within span context, handling input/output and exceptions."""
|
|
642
642
|
# Ensure tracer provider is initialized before creating spans
|
|
643
|
-
get_aiqa_client()
|
|
643
|
+
client = get_aiqa_client()
|
|
644
|
+
if not client.enabled:
|
|
645
|
+
return executor()
|
|
646
|
+
|
|
644
647
|
with tracer.start_as_current_span(fn_name) as span:
|
|
645
648
|
if not _setup_span(span, input_data):
|
|
646
649
|
return executor()
|
|
@@ -656,7 +659,10 @@ def WithTracing(
|
|
|
656
659
|
async def _execute_with_span_async(executor: Callable[[], Any], input_data: Any) -> Any:
|
|
657
660
|
"""Execute async function within span context, handling input/output and exceptions."""
|
|
658
661
|
# Ensure tracer provider is initialized before creating spans
|
|
659
|
-
get_aiqa_client()
|
|
662
|
+
client = get_aiqa_client()
|
|
663
|
+
if not client.enabled:
|
|
664
|
+
return await executor()
|
|
665
|
+
|
|
660
666
|
with tracer.start_as_current_span(fn_name) as span:
|
|
661
667
|
if not _setup_span(span, input_data):
|
|
662
668
|
return await executor()
|
|
@@ -675,7 +681,10 @@ def WithTracing(
|
|
|
675
681
|
def _execute_generator_sync(executor: Callable[[], Any], input_data: Any) -> Any:
|
|
676
682
|
"""Execute sync generator function, returning a traced generator."""
|
|
677
683
|
# Ensure tracer provider is initialized before creating spans
|
|
678
|
-
get_aiqa_client()
|
|
684
|
+
client = get_aiqa_client()
|
|
685
|
+
if not client.enabled:
|
|
686
|
+
return executor()
|
|
687
|
+
|
|
679
688
|
# Create span but don't use 'with' - span will be closed by TracedGenerator
|
|
680
689
|
span = tracer.start_span(fn_name)
|
|
681
690
|
token = trace.context_api.attach(trace.context_api.set_span_in_context(span))
|
|
@@ -698,7 +707,10 @@ def WithTracing(
|
|
|
698
707
|
async def _execute_generator_async(executor: Callable[[], Any], input_data: Any) -> Any:
|
|
699
708
|
"""Execute async generator function, returning a traced async generator."""
|
|
700
709
|
# Ensure tracer provider is initialized before creating spans
|
|
701
|
-
get_aiqa_client()
|
|
710
|
+
client = get_aiqa_client()
|
|
711
|
+
if not client.enabled:
|
|
712
|
+
return await executor()
|
|
713
|
+
|
|
702
714
|
# Create span but don't use 'with' - span will be closed by TracedAsyncGenerator
|
|
703
715
|
span = tracer.start_span(fn_name)
|
|
704
716
|
token = trace.context_api.attach(trace.context_api.set_span_in_context(span))
|
|
@@ -960,15 +972,15 @@ def set_component_tag(tag: str) -> None:
|
|
|
960
972
|
def get_provider() -> Optional[TracerProvider]:
|
|
961
973
|
"""Get the tracer provider for advanced usage."""
|
|
962
974
|
client = get_aiqa_client()
|
|
963
|
-
return client.
|
|
975
|
+
return client.provider
|
|
964
976
|
|
|
965
977
|
def get_exporter() -> Optional[AIQASpanExporter]:
|
|
966
978
|
"""Get the exporter for advanced usage."""
|
|
967
979
|
client = get_aiqa_client()
|
|
968
|
-
return client.
|
|
980
|
+
return client.exporter
|
|
969
981
|
|
|
970
982
|
|
|
971
|
-
def
|
|
983
|
+
def get_active_trace_id() -> Optional[str]:
|
|
972
984
|
"""
|
|
973
985
|
Get the current trace ID as a hexadecimal string (32 characters).
|
|
974
986
|
|
|
@@ -976,9 +988,10 @@ def get_trace_id() -> Optional[str]:
|
|
|
976
988
|
The trace ID as a hex string, or None if no active span exists.
|
|
977
989
|
|
|
978
990
|
Example:
|
|
979
|
-
trace_id =
|
|
991
|
+
trace_id = get_active_trace_id()
|
|
980
992
|
# Pass trace_id to another service/agent
|
|
981
993
|
# e.g., include in HTTP headers, message queue metadata, etc.
|
|
994
|
+
# Within a single thread, OpenTelemetry normally does this for you.
|
|
982
995
|
"""
|
|
983
996
|
span = trace.get_current_span()
|
|
984
997
|
if span and span.get_span_context().is_valid:
|
|
@@ -1023,7 +1036,7 @@ def create_span_from_trace_id(
|
|
|
1023
1036
|
|
|
1024
1037
|
Example:
|
|
1025
1038
|
# In service A: get trace ID
|
|
1026
|
-
trace_id =
|
|
1039
|
+
trace_id = get_active_trace_id()
|
|
1027
1040
|
span_id = get_span_id()
|
|
1028
1041
|
|
|
1029
1042
|
# Send to service B (e.g., via HTTP, message queue, etc.)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aiqa-client
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.6
|
|
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
|
|
@@ -67,6 +67,9 @@ export AIQA_SERVER_URL="http://localhost:3000"
|
|
|
67
67
|
export AIQA_API_KEY="your-api-key"
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
+
**Note:** If `AIQA_SERVER_URL` or `AIQA_API_KEY` are not set, tracing will be automatically disabled. You'll see one warning message at the start, and your application will continue to run without tracing.
|
|
71
|
+
You can check if tracing is enabled via `get_aiqa_client().enabled`.
|
|
72
|
+
|
|
70
73
|
## Usage
|
|
71
74
|
|
|
72
75
|
### Basic Usage
|
|
@@ -141,6 +144,30 @@ async def main():
|
|
|
141
144
|
asyncio.run(main())
|
|
142
145
|
```
|
|
143
146
|
|
|
147
|
+
### Enabling/Disabling Tracing
|
|
148
|
+
|
|
149
|
+
You can programmatically enable or disable tracing:
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from aiqa import get_aiqa_client
|
|
153
|
+
|
|
154
|
+
client = get_aiqa_client()
|
|
155
|
+
|
|
156
|
+
# Disable tracing (spans won't be created or exported)
|
|
157
|
+
client.set_enabled(False)
|
|
158
|
+
|
|
159
|
+
# Re-enable tracing
|
|
160
|
+
client.set_enabled(True)
|
|
161
|
+
|
|
162
|
+
# Check if tracing is enabled
|
|
163
|
+
if client.enabled:
|
|
164
|
+
print("Tracing is enabled")
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
When tracing is disabled:
|
|
168
|
+
- Spans are not created (functions execute normally without tracing overhead)
|
|
169
|
+
- Spans are not exported to the server
|
|
170
|
+
|
|
144
171
|
### Setting Span Attributes and Names
|
|
145
172
|
|
|
146
173
|
```python
|
|
@@ -176,10 +203,10 @@ To link traces across different services or agents, you can extract and propagat
|
|
|
176
203
|
#### Getting Current Trace ID
|
|
177
204
|
|
|
178
205
|
```python
|
|
179
|
-
from aiqa import
|
|
206
|
+
from aiqa import get_active_trace_id, get_span_id
|
|
180
207
|
|
|
181
208
|
# Get the current trace ID and span ID
|
|
182
|
-
trace_id =
|
|
209
|
+
trace_id = get_active_trace_id() # Returns hex string (32 chars) or None
|
|
183
210
|
span_id = get_span_id() # Returns hex string (16 chars) or None
|
|
184
211
|
|
|
185
212
|
# Pass these to another service (e.g., in HTTP headers, message queue, etc.)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "aiqa-client"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.6"
|
|
8
8
|
description = "OpenTelemetry-based Python client for tracing functions and sending traces to the AIQA server"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|