genai-otel-instrument 0.1.2.dev0__py3-none-any.whl → 0.1.7.dev0__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.
Potentially problematic release.
This version of genai-otel-instrument might be problematic. Click here for more details.
- genai_otel/__version__.py +2 -2
- genai_otel/auto_instrument.py +18 -1
- genai_otel/config.py +22 -1
- genai_otel/cost_calculator.py +204 -13
- genai_otel/cost_enrichment_processor.py +175 -0
- genai_otel/gpu_metrics.py +50 -0
- genai_otel/instrumentors/base.py +300 -44
- genai_otel/instrumentors/cohere_instrumentor.py +140 -76
- genai_otel/instrumentors/huggingface_instrumentor.py +142 -13
- genai_otel/instrumentors/langchain_instrumentor.py +75 -75
- genai_otel/instrumentors/mistralai_instrumentor.py +234 -38
- genai_otel/instrumentors/ollama_instrumentor.py +104 -35
- genai_otel/instrumentors/replicate_instrumentor.py +59 -14
- genai_otel/instrumentors/togetherai_instrumentor.py +120 -16
- genai_otel/instrumentors/vertexai_instrumentor.py +79 -15
- genai_otel/llm_pricing.json +869 -589
- genai_otel/logging_config.py +45 -45
- genai_otel/py.typed +2 -2
- {genai_otel_instrument-0.1.2.dev0.dist-info → genai_otel_instrument-0.1.7.dev0.dist-info}/METADATA +294 -33
- {genai_otel_instrument-0.1.2.dev0.dist-info → genai_otel_instrument-0.1.7.dev0.dist-info}/RECORD +24 -23
- {genai_otel_instrument-0.1.2.dev0.dist-info → genai_otel_instrument-0.1.7.dev0.dist-info}/WHEEL +0 -0
- {genai_otel_instrument-0.1.2.dev0.dist-info → genai_otel_instrument-0.1.7.dev0.dist-info}/entry_points.txt +0 -0
- {genai_otel_instrument-0.1.2.dev0.dist-info → genai_otel_instrument-0.1.7.dev0.dist-info}/licenses/LICENSE +0 -0
- {genai_otel_instrument-0.1.2.dev0.dist-info → genai_otel_instrument-0.1.7.dev0.dist-info}/top_level.txt +0 -0
genai_otel/logging_config.py
CHANGED
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
"""Centralized logging configuration"""
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
import os
|
|
5
|
-
import sys
|
|
6
|
-
from logging.handlers import RotatingFileHandler
|
|
7
|
-
from typing import Optional
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def setup_logging(
|
|
11
|
-
level: Optional[str] = None, log_dir: str = "logs", log_file_name: str = "genai_otel.log"
|
|
12
|
-
):
|
|
13
|
-
"""Configure logging for the library with configurable log level via environment variable
|
|
14
|
-
and log rotation.
|
|
15
|
-
"""
|
|
16
|
-
# Determine log level from environment variable or default to INFO
|
|
17
|
-
env_log_level = os.environ.get("GENAI_OTEL_LOG_LEVEL")
|
|
18
|
-
log_level_str = level or env_log_level or "INFO"
|
|
19
|
-
log_level = getattr(logging, log_level_str.upper(), logging.INFO)
|
|
20
|
-
|
|
21
|
-
# Create logs directory if it doesn't exist
|
|
22
|
-
os.makedirs(log_dir, exist_ok=True)
|
|
23
|
-
log_file_path = os.path.join(log_dir, log_file_name)
|
|
24
|
-
|
|
25
|
-
# Setup handlers
|
|
26
|
-
handlers = [logging.StreamHandler(sys.stdout)]
|
|
27
|
-
|
|
28
|
-
# Add rotating file handler
|
|
29
|
-
file_handler = RotatingFileHandler(log_file_path, maxBytes=10 * 1024 * 1024, backupCount=10)
|
|
30
|
-
handlers.append(file_handler)
|
|
31
|
-
|
|
32
|
-
# Set library logger
|
|
33
|
-
logger = logging.getLogger("genai_otel")
|
|
34
|
-
logger.setLevel(log_level)
|
|
35
|
-
|
|
36
|
-
# Clear existing handlers to prevent duplicates in case of multiple calls
|
|
37
|
-
if logger.handlers:
|
|
38
|
-
for handler in logger.handlers:
|
|
39
|
-
handler.close()
|
|
40
|
-
logger.handlers = []
|
|
41
|
-
|
|
42
|
-
for handler in handlers:
|
|
43
|
-
logger.addHandler(handler)
|
|
44
|
-
|
|
45
|
-
return logger
|
|
1
|
+
"""Centralized logging configuration"""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
from logging.handlers import RotatingFileHandler
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def setup_logging(
|
|
11
|
+
level: Optional[str] = None, log_dir: str = "logs", log_file_name: str = "genai_otel.log"
|
|
12
|
+
):
|
|
13
|
+
"""Configure logging for the library with configurable log level via environment variable
|
|
14
|
+
and log rotation.
|
|
15
|
+
"""
|
|
16
|
+
# Determine log level from environment variable or default to INFO
|
|
17
|
+
env_log_level = os.environ.get("GENAI_OTEL_LOG_LEVEL")
|
|
18
|
+
log_level_str = level or env_log_level or "INFO"
|
|
19
|
+
log_level = getattr(logging, log_level_str.upper(), logging.INFO)
|
|
20
|
+
|
|
21
|
+
# Create logs directory if it doesn't exist
|
|
22
|
+
os.makedirs(log_dir, exist_ok=True)
|
|
23
|
+
log_file_path = os.path.join(log_dir, log_file_name)
|
|
24
|
+
|
|
25
|
+
# Setup handlers
|
|
26
|
+
handlers = [logging.StreamHandler(sys.stdout)]
|
|
27
|
+
|
|
28
|
+
# Add rotating file handler
|
|
29
|
+
file_handler = RotatingFileHandler(log_file_path, maxBytes=10 * 1024 * 1024, backupCount=10)
|
|
30
|
+
handlers.append(file_handler)
|
|
31
|
+
|
|
32
|
+
# Set library logger
|
|
33
|
+
logger = logging.getLogger("genai_otel")
|
|
34
|
+
logger.setLevel(log_level)
|
|
35
|
+
|
|
36
|
+
# Clear existing handlers to prevent duplicates in case of multiple calls
|
|
37
|
+
if logger.handlers:
|
|
38
|
+
for handler in logger.handlers:
|
|
39
|
+
handler.close()
|
|
40
|
+
logger.handlers = []
|
|
41
|
+
|
|
42
|
+
for handler in handlers:
|
|
43
|
+
logger.addHandler(handler)
|
|
44
|
+
|
|
45
|
+
return logger
|
genai_otel/py.typed
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
# Marker file for PEP 561
|
|
2
|
-
# This file indicates that the package supports type checking
|
|
1
|
+
# Marker file for PEP 561
|
|
2
|
+
# This file indicates that the package supports type checking
|
{genai_otel_instrument-0.1.2.dev0.dist-info → genai_otel_instrument-0.1.7.dev0.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: genai-otel-instrument
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.7.dev0
|
|
4
4
|
Summary: Comprehensive OpenTelemetry auto-instrumentation for LLM/GenAI applications
|
|
5
5
|
Author-email: Kshitij Thakkar <kshitijthakkar@rocketmail.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -191,7 +191,7 @@ Dynamic: license-file
|
|
|
191
191
|
[](https://github.com/Mandark-droid/genai_otel_instrument/issues)
|
|
192
192
|
[](https://github.com/Mandark-droid/genai_otel_instrument/pulls)
|
|
193
193
|
|
|
194
|
-
[](https://github.com/Mandark-droid/genai_otel_instrument)
|
|
195
195
|
[](https://github.com/psf/black)
|
|
196
196
|
[](https://pycqa.github.io/isort/)
|
|
197
197
|
[](http://mypy-lang.org/)
|
|
@@ -207,8 +207,9 @@ Production-ready OpenTelemetry instrumentation for GenAI/LLM applications with z
|
|
|
207
207
|
🚀 **Zero-Code Instrumentation** - Just install and set env vars
|
|
208
208
|
🤖 **15+ LLM Providers** - OpenAI, Anthropic, Google, AWS, Azure, and more
|
|
209
209
|
🔧 **MCP Tool Support** - Auto-instrument databases, APIs, caches, vector DBs
|
|
210
|
-
💰 **Cost Tracking** - Automatic cost calculation
|
|
211
|
-
|
|
210
|
+
💰 **Cost Tracking** - Automatic cost calculation for both streaming and non-streaming requests
|
|
211
|
+
⚡ **Streaming Support** - Full observability for streaming responses with TTFT/TBT metrics and cost tracking
|
|
212
|
+
🎮 **GPU Metrics** - Real-time GPU utilization, memory, temperature, power, and electricity cost tracking
|
|
212
213
|
📊 **Complete Observability** - Traces, metrics, and rich span attributes
|
|
213
214
|
➕ **Service Instance ID & Environment** - Identify your services and environments
|
|
214
215
|
⏱️ **Configurable Exporter Timeout** - Set timeout for OTLP exporter
|
|
@@ -255,9 +256,9 @@ For a more comprehensive demonstration of various LLM providers and MCP tools, r
|
|
|
255
256
|
## What Gets Instrumented?
|
|
256
257
|
|
|
257
258
|
### LLM Providers (Auto-detected)
|
|
258
|
-
- OpenAI, Anthropic, Google AI, AWS Bedrock, Azure OpenAI
|
|
259
|
-
-
|
|
260
|
-
-
|
|
259
|
+
- **With Full Cost Tracking**: OpenAI, Anthropic, Google AI, AWS Bedrock, Azure OpenAI, Cohere, Mistral AI, Together AI, Groq, Ollama, Vertex AI
|
|
260
|
+
- **Hardware/Local Pricing**: Replicate (hardware-based $/second), HuggingFace (local execution, free)
|
|
261
|
+
- **Other Providers**: Anyscale
|
|
261
262
|
|
|
262
263
|
### Frameworks
|
|
263
264
|
- LangChain (chains, agents, tools)
|
|
@@ -271,15 +272,86 @@ For a more comprehensive demonstration of various LLM providers and MCP tools, r
|
|
|
271
272
|
- **APIs**: HTTP/REST requests (requests, httpx)
|
|
272
273
|
|
|
273
274
|
### OpenInference (Optional - Python 3.10+ only)
|
|
274
|
-
- Smolagents
|
|
275
|
-
- MCP
|
|
276
|
-
- LiteLLM
|
|
275
|
+
- Smolagents - HuggingFace smolagents framework tracing
|
|
276
|
+
- MCP - Model Context Protocol instrumentation
|
|
277
|
+
- LiteLLM - Multi-provider LLM proxy
|
|
278
|
+
|
|
279
|
+
**Cost Enrichment:** OpenInference instrumentors are automatically enriched with cost tracking! When cost tracking is enabled (`GENAI_ENABLE_COST_TRACKING=true`), a custom `CostEnrichmentSpanProcessor` extracts model and token usage from OpenInference spans and adds cost attributes (`gen_ai.usage.cost.total`, `gen_ai.usage.cost.prompt`, `gen_ai.usage.cost.completion`) using our comprehensive pricing database of 145+ models.
|
|
280
|
+
|
|
281
|
+
The processor supports OpenInference semantic conventions:
|
|
282
|
+
- Model: `llm.model_name`, `embedding.model_name`
|
|
283
|
+
- Tokens: `llm.token_count.prompt`, `llm.token_count.completion`
|
|
284
|
+
- Operations: `openinference.span.kind` (LLM, EMBEDDING, CHAIN, RETRIEVER, etc.)
|
|
277
285
|
|
|
278
286
|
**Note:** OpenInference instrumentors require Python >= 3.10. Install with:
|
|
279
287
|
```bash
|
|
280
288
|
pip install genai-otel-instrument[openinference]
|
|
281
289
|
```
|
|
282
290
|
|
|
291
|
+
## Cost Tracking Coverage
|
|
292
|
+
|
|
293
|
+
The library includes comprehensive cost tracking with pricing data for **145+ models** across **11 providers**:
|
|
294
|
+
|
|
295
|
+
### Providers with Full Token-Based Cost Tracking
|
|
296
|
+
- **OpenAI**: GPT-4o, GPT-4 Turbo, GPT-3.5 Turbo, o1/o3 series, embeddings, audio, vision (35+ models)
|
|
297
|
+
- **Anthropic**: Claude 3.5 Sonnet/Opus/Haiku, Claude 3 series (10+ models)
|
|
298
|
+
- **Google AI**: Gemini 1.5/2.0 Pro/Flash, PaLM 2 (12+ models)
|
|
299
|
+
- **AWS Bedrock**: Amazon Titan, Claude, Llama, Mistral models (20+ models)
|
|
300
|
+
- **Azure OpenAI**: Same as OpenAI with Azure-specific pricing
|
|
301
|
+
- **Cohere**: Command R/R+, Command Light, Embed v3/v2 (8+ models)
|
|
302
|
+
- **Mistral AI**: Mistral Large/Medium/Small, Mixtral, embeddings (8+ models)
|
|
303
|
+
- **Together AI**: DeepSeek-R1, Llama 3.x, Qwen, Mixtral (25+ models)
|
|
304
|
+
- **Groq**: Llama 3.x series, Mixtral, Gemma models (15+ models)
|
|
305
|
+
- **Ollama**: Local models with token tracking (pricing via cost estimation)
|
|
306
|
+
- **Vertex AI**: Gemini models via Google Cloud with usage metadata extraction
|
|
307
|
+
|
|
308
|
+
### Special Pricing Models
|
|
309
|
+
- **Replicate**: Hardware-based pricing ($/second of GPU/CPU time) - not token-based
|
|
310
|
+
- **HuggingFace Transformers**: Local execution - no API costs
|
|
311
|
+
|
|
312
|
+
### Pricing Features
|
|
313
|
+
- **Differential Pricing**: Separate rates for prompt tokens vs. completion tokens
|
|
314
|
+
- **Reasoning Tokens**: Special pricing for OpenAI o1/o3 reasoning tokens
|
|
315
|
+
- **Cache Pricing**: Anthropic prompt caching costs (read/write)
|
|
316
|
+
- **Granular Cost Metrics**: Per-request cost breakdown by token type
|
|
317
|
+
- **Auto-Updated Pricing**: Pricing data maintained in `llm_pricing.json`
|
|
318
|
+
- **Custom Pricing**: Add pricing for custom/proprietary models via environment variable
|
|
319
|
+
|
|
320
|
+
### Adding Custom Model Pricing
|
|
321
|
+
|
|
322
|
+
For custom or proprietary models not in `llm_pricing.json`, you can provide custom pricing via the `GENAI_CUSTOM_PRICING_JSON` environment variable:
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
# For chat models
|
|
326
|
+
export GENAI_CUSTOM_PRICING_JSON='{"chat":{"my-custom-model":{"promptPrice":0.001,"completionPrice":0.002}}}'
|
|
327
|
+
|
|
328
|
+
# For embeddings
|
|
329
|
+
export GENAI_CUSTOM_PRICING_JSON='{"embeddings":{"my-custom-embeddings":0.00005}}'
|
|
330
|
+
|
|
331
|
+
# For multiple categories
|
|
332
|
+
export GENAI_CUSTOM_PRICING_JSON='{
|
|
333
|
+
"chat": {
|
|
334
|
+
"my-custom-chat": {"promptPrice": 0.001, "completionPrice": 0.002}
|
|
335
|
+
},
|
|
336
|
+
"embeddings": {
|
|
337
|
+
"my-custom-embed": 0.00005
|
|
338
|
+
},
|
|
339
|
+
"audio": {
|
|
340
|
+
"my-custom-tts": 0.02
|
|
341
|
+
}
|
|
342
|
+
}'
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
**Pricing Format:**
|
|
346
|
+
- **Chat models**: `{"promptPrice": <$/1k tokens>, "completionPrice": <$/1k tokens>}`
|
|
347
|
+
- **Embeddings**: Single number for price per 1k tokens
|
|
348
|
+
- **Audio**: Price per 1k characters (TTS) or per second (STT)
|
|
349
|
+
- **Images**: Nested structure with quality/size pricing (see `llm_pricing.json` for examples)
|
|
350
|
+
|
|
351
|
+
**Hybrid Pricing:** Custom prices are merged with default pricing from `llm_pricing.json`. If you provide custom pricing for an existing model, the custom price overrides the default.
|
|
352
|
+
|
|
353
|
+
**Coverage Statistics**: As of v0.1.3, 89% test coverage with 415 passing tests, including comprehensive cost calculation validation and cost enrichment processor tests (supporting both GenAI and OpenInference semantic conventions).
|
|
354
|
+
|
|
283
355
|
## Collected Telemetry
|
|
284
356
|
|
|
285
357
|
### Traces
|
|
@@ -299,7 +371,8 @@ Every LLM call, database query, API request, and vector search is traced with fu
|
|
|
299
371
|
- `gen_ai.usage.cost.cache_write` - Cache write cost (Anthropic)
|
|
300
372
|
- `gen_ai.client.errors` - Error counts by operation and type
|
|
301
373
|
- `gen_ai.gpu.*` - GPU utilization, memory, temperature, power (ObservableGauges)
|
|
302
|
-
- `gen_ai.co2.emissions` - CO2 emissions tracking (opt-in)
|
|
374
|
+
- `gen_ai.co2.emissions` - CO2 emissions tracking (opt-in via `GENAI_ENABLE_CO2_TRACKING`)
|
|
375
|
+
- `gen_ai.power.cost` - Cumulative electricity cost in USD based on GPU power consumption (configurable via `GENAI_POWER_COST_PER_KWH`)
|
|
303
376
|
- `gen_ai.server.ttft` - Time to First Token for streaming responses (histogram, 1ms-10s buckets)
|
|
304
377
|
- `gen_ai.server.tbt` - Time Between Tokens for streaming responses (histogram, 10ms-2.5s buckets)
|
|
305
378
|
|
|
@@ -346,7 +419,14 @@ Every LLM call, database query, API request, and vector search is traced with fu
|
|
|
346
419
|
|
|
347
420
|
**Streaming Attributes:**
|
|
348
421
|
- `gen_ai.server.ttft` - Time to First Token (seconds) for streaming responses
|
|
349
|
-
- `gen_ai.streaming.token_count` - Total number of chunks
|
|
422
|
+
- `gen_ai.streaming.token_count` - Total number of chunks in streaming response
|
|
423
|
+
- `gen_ai.usage.prompt_tokens` - Actual prompt tokens (extracted from final chunk)
|
|
424
|
+
- `gen_ai.usage.completion_tokens` - Actual completion tokens (extracted from final chunk)
|
|
425
|
+
- `gen_ai.usage.total_tokens` - Total tokens (extracted from final chunk)
|
|
426
|
+
- `gen_ai.usage.cost.total` - Total cost for streaming request
|
|
427
|
+
- `gen_ai.usage.cost.prompt` - Prompt tokens cost for streaming request
|
|
428
|
+
- `gen_ai.usage.cost.completion` - Completion tokens cost for streaming request
|
|
429
|
+
- All granular cost attributes (reasoning, cache_read, cache_write) also available for streaming
|
|
350
430
|
|
|
351
431
|
**Content Events (opt-in):**
|
|
352
432
|
- `gen_ai.prompt.{index}` events with role and content
|
|
@@ -403,6 +483,181 @@ genai_otel.instrument(
|
|
|
403
483
|
|
|
404
484
|
A `sample.env` file has been generated in the project root directory. This file contains commented-out examples of all supported environment variables, along with their default values or expected formats. You can copy this file to `.env` and uncomment/modify the variables to configure the instrumentation for your specific needs.
|
|
405
485
|
|
|
486
|
+
## Advanced Features
|
|
487
|
+
|
|
488
|
+
### Session and User Tracking
|
|
489
|
+
|
|
490
|
+
Track user sessions and identify users across multiple LLM requests for better analytics, debugging, and cost attribution.
|
|
491
|
+
|
|
492
|
+
**Configuration:**
|
|
493
|
+
|
|
494
|
+
```python
|
|
495
|
+
import genai_otel
|
|
496
|
+
from genai_otel import OTelConfig
|
|
497
|
+
|
|
498
|
+
# Define extractor functions
|
|
499
|
+
def extract_session_id(instance, args, kwargs):
|
|
500
|
+
"""Extract session ID from request metadata."""
|
|
501
|
+
# Option 1: From kwargs metadata
|
|
502
|
+
metadata = kwargs.get("metadata", {})
|
|
503
|
+
return metadata.get("session_id")
|
|
504
|
+
|
|
505
|
+
# Option 2: From custom headers
|
|
506
|
+
# headers = kwargs.get("headers", {})
|
|
507
|
+
# return headers.get("X-Session-ID")
|
|
508
|
+
|
|
509
|
+
# Option 3: From thread-local storage
|
|
510
|
+
# import threading
|
|
511
|
+
# return getattr(threading.current_thread(), "session_id", None)
|
|
512
|
+
|
|
513
|
+
def extract_user_id(instance, args, kwargs):
|
|
514
|
+
"""Extract user ID from request metadata."""
|
|
515
|
+
metadata = kwargs.get("metadata", {})
|
|
516
|
+
return metadata.get("user_id")
|
|
517
|
+
|
|
518
|
+
# Configure with extractors
|
|
519
|
+
config = OTelConfig(
|
|
520
|
+
service_name="my-rag-app",
|
|
521
|
+
endpoint="http://localhost:4318",
|
|
522
|
+
session_id_extractor=extract_session_id,
|
|
523
|
+
user_id_extractor=extract_user_id,
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
genai_otel.instrument(config)
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
**Usage:**
|
|
530
|
+
|
|
531
|
+
```python
|
|
532
|
+
from openai import OpenAI
|
|
533
|
+
|
|
534
|
+
client = OpenAI()
|
|
535
|
+
|
|
536
|
+
# Pass session and user info via metadata
|
|
537
|
+
response = client.chat.completions.create(
|
|
538
|
+
model="gpt-3.5-turbo",
|
|
539
|
+
messages=[{"role": "user", "content": "What is OpenTelemetry?"}],
|
|
540
|
+
extra_body={"metadata": {"session_id": "sess_12345", "user_id": "user_alice"}}
|
|
541
|
+
)
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
**Span Attributes Added:**
|
|
545
|
+
- `session.id` - Unique session identifier for tracking conversations
|
|
546
|
+
- `user.id` - User identifier for per-user analytics and cost tracking
|
|
547
|
+
|
|
548
|
+
**Use Cases:**
|
|
549
|
+
- Track multi-turn conversations across requests
|
|
550
|
+
- Analyze usage patterns per user
|
|
551
|
+
- Debug session-specific issues
|
|
552
|
+
- Calculate per-user costs and quotas
|
|
553
|
+
- Build user-specific dashboards
|
|
554
|
+
|
|
555
|
+
### RAG and Embedding Attributes
|
|
556
|
+
|
|
557
|
+
Enhanced observability for Retrieval-Augmented Generation (RAG) workflows, including embedding generation and document retrieval.
|
|
558
|
+
|
|
559
|
+
**Helper Methods:**
|
|
560
|
+
|
|
561
|
+
The `BaseInstrumentor` provides helper methods to add RAG-specific attributes to your spans:
|
|
562
|
+
|
|
563
|
+
```python
|
|
564
|
+
from opentelemetry import trace
|
|
565
|
+
from genai_otel.instrumentors.base import BaseInstrumentor
|
|
566
|
+
|
|
567
|
+
# Get your instrumentor instance (or create spans manually)
|
|
568
|
+
tracer = trace.get_tracer(__name__)
|
|
569
|
+
|
|
570
|
+
# 1. Embedding Attributes
|
|
571
|
+
with tracer.start_as_current_span("embedding.create") as span:
|
|
572
|
+
# Your embedding logic
|
|
573
|
+
embedding_response = client.embeddings.create(
|
|
574
|
+
model="text-embedding-3-small",
|
|
575
|
+
input="OpenTelemetry provides observability"
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
# Add embedding attributes (if using BaseInstrumentor)
|
|
579
|
+
# instrumentor.add_embedding_attributes(
|
|
580
|
+
# span,
|
|
581
|
+
# model="text-embedding-3-small",
|
|
582
|
+
# input_text="OpenTelemetry provides observability",
|
|
583
|
+
# vector=embedding_response.data[0].embedding
|
|
584
|
+
# )
|
|
585
|
+
|
|
586
|
+
# Or manually set attributes
|
|
587
|
+
span.set_attribute("embedding.model_name", "text-embedding-3-small")
|
|
588
|
+
span.set_attribute("embedding.text", "OpenTelemetry provides observability"[:500])
|
|
589
|
+
span.set_attribute("embedding.vector.dimension", len(embedding_response.data[0].embedding))
|
|
590
|
+
|
|
591
|
+
# 2. Retrieval Attributes
|
|
592
|
+
with tracer.start_as_current_span("retrieval.search") as span:
|
|
593
|
+
# Your retrieval logic
|
|
594
|
+
retrieved_docs = [
|
|
595
|
+
{
|
|
596
|
+
"id": "doc_001",
|
|
597
|
+
"score": 0.95,
|
|
598
|
+
"content": "OpenTelemetry is an observability framework...",
|
|
599
|
+
"metadata": {"source": "docs.opentelemetry.io", "category": "intro"}
|
|
600
|
+
},
|
|
601
|
+
# ... more documents
|
|
602
|
+
]
|
|
603
|
+
|
|
604
|
+
# Add retrieval attributes (if using BaseInstrumentor)
|
|
605
|
+
# instrumentor.add_retrieval_attributes(
|
|
606
|
+
# span,
|
|
607
|
+
# documents=retrieved_docs,
|
|
608
|
+
# query="What is OpenTelemetry?",
|
|
609
|
+
# max_docs=5
|
|
610
|
+
# )
|
|
611
|
+
|
|
612
|
+
# Or manually set attributes
|
|
613
|
+
span.set_attribute("retrieval.query", "What is OpenTelemetry?"[:500])
|
|
614
|
+
span.set_attribute("retrieval.document_count", len(retrieved_docs))
|
|
615
|
+
|
|
616
|
+
for i, doc in enumerate(retrieved_docs[:5]): # Limit to 5 docs
|
|
617
|
+
prefix = f"retrieval.documents.{i}.document"
|
|
618
|
+
span.set_attribute(f"{prefix}.id", doc["id"])
|
|
619
|
+
span.set_attribute(f"{prefix}.score", doc["score"])
|
|
620
|
+
span.set_attribute(f"{prefix}.content", doc["content"][:500])
|
|
621
|
+
|
|
622
|
+
# Add metadata
|
|
623
|
+
for key, value in doc.get("metadata", {}).items():
|
|
624
|
+
span.set_attribute(f"{prefix}.metadata.{key}", str(value))
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
**Embedding Attributes:**
|
|
628
|
+
- `embedding.model_name` - Embedding model used
|
|
629
|
+
- `embedding.text` - Input text (truncated to 500 chars)
|
|
630
|
+
- `embedding.vector` - Embedding vector (optional, if configured)
|
|
631
|
+
- `embedding.vector.dimension` - Vector dimensions
|
|
632
|
+
|
|
633
|
+
**Retrieval Attributes:**
|
|
634
|
+
- `retrieval.query` - Search query (truncated to 500 chars)
|
|
635
|
+
- `retrieval.document_count` - Number of documents retrieved
|
|
636
|
+
- `retrieval.documents.{i}.document.id` - Document ID
|
|
637
|
+
- `retrieval.documents.{i}.document.score` - Relevance score
|
|
638
|
+
- `retrieval.documents.{i}.document.content` - Document content (truncated to 500 chars)
|
|
639
|
+
- `retrieval.documents.{i}.document.metadata.*` - Custom metadata fields
|
|
640
|
+
|
|
641
|
+
**Safeguards:**
|
|
642
|
+
- Text content truncated to 500 characters to avoid span size explosion
|
|
643
|
+
- Document count limited to 5 by default (configurable via `max_docs`)
|
|
644
|
+
- Metadata values truncated to prevent excessive attribute counts
|
|
645
|
+
|
|
646
|
+
**Complete RAG Workflow Example:**
|
|
647
|
+
|
|
648
|
+
See `examples/phase4_session_rag_tracking.py` for a comprehensive demonstration of:
|
|
649
|
+
- Session and user tracking across RAG pipeline
|
|
650
|
+
- Embedding attribute capture
|
|
651
|
+
- Retrieval attribute capture
|
|
652
|
+
- End-to-end RAG workflow with full observability
|
|
653
|
+
|
|
654
|
+
**Use Cases:**
|
|
655
|
+
- Monitor retrieval quality and relevance scores
|
|
656
|
+
- Debug RAG pipeline performance
|
|
657
|
+
- Track embedding model usage
|
|
658
|
+
- Analyze document retrieval patterns
|
|
659
|
+
- Optimize vector search configurations
|
|
660
|
+
|
|
406
661
|
## Example: Full-Stack GenAI App
|
|
407
662
|
|
|
408
663
|
```python
|
|
@@ -585,27 +840,33 @@ genai_otel.instrument(
|
|
|
585
840
|
|
|
586
841
|
Completing remaining items from [OTEL_SEMANTIC_GAP_ANALYSIS_AND_IMPLEMENTATION_PLAN.md](OTEL_SEMANTIC_GAP_ANALYSIS_AND_IMPLEMENTATION_PLAN.md):
|
|
587
842
|
|
|
588
|
-
**Phase 4: Optional Enhancements**
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
- ✅ RAG/Embedding Attributes - Enhanced observability for retrieval-augmented generation
|
|
598
|
-
- `
|
|
599
|
-
- `embedding.
|
|
600
|
-
- `retrieval.documents.{i}.document
|
|
601
|
-
-
|
|
602
|
-
- `
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
843
|
+
**Phase 4: Optional Enhancements (✅ COMPLETED)**
|
|
844
|
+
|
|
845
|
+
All Phase 4 features are now available! See the [Advanced Features](#advanced-features) section for detailed documentation.
|
|
846
|
+
|
|
847
|
+
- ✅ **Session & User Tracking** - Track sessions and users across requests with custom extractor functions
|
|
848
|
+
- Configurable via `session_id_extractor` and `user_id_extractor` in `OTelConfig`
|
|
849
|
+
- Automatically adds `session.id` and `user.id` span attributes
|
|
850
|
+
- See [Session and User Tracking](#session-and-user-tracking) for usage examples
|
|
851
|
+
|
|
852
|
+
- ✅ **RAG/Embedding Attributes** - Enhanced observability for retrieval-augmented generation
|
|
853
|
+
- Helper methods: `add_embedding_attributes()` and `add_retrieval_attributes()`
|
|
854
|
+
- Embedding attributes: `embedding.model_name`, `embedding.text`, `embedding.vector.dimension`
|
|
855
|
+
- Retrieval attributes: `retrieval.query`, `retrieval.document_count`, `retrieval.documents.{i}.document.*`
|
|
856
|
+
- See [RAG and Embedding Attributes](#rag-and-embedding-attributes) for usage examples
|
|
857
|
+
- Complete example: `examples/phase4_session_rag_tracking.py`
|
|
858
|
+
|
|
859
|
+
**Note on Agent Workflow Tracking:**
|
|
860
|
+
|
|
861
|
+
Agent workflow observability is already provided by the OpenInference Smolagents instrumentor (included when `smolagents` is in `enabled_instrumentors`). This is not a new Phase 4 feature, but an existing capability:
|
|
862
|
+
|
|
863
|
+
- `openinference.span.kind: "AGENT"` - Identifies agent spans
|
|
864
|
+
- `agent.name` - Agent identifier (via OpenInference)
|
|
865
|
+
- `agent.iteration` - Current iteration number (via OpenInference)
|
|
866
|
+
- `agent.action` - Action taken (via OpenInference)
|
|
867
|
+
- `agent.observation` - Observation received (via OpenInference)
|
|
868
|
+
|
|
869
|
+
Agent tracking requires Python >= 3.10 and the `smolagents` library. See [OpenInference Integration](#openinference-optional---python-310-only) for details.
|
|
609
870
|
|
|
610
871
|
#### 🔄 Migration Support
|
|
611
872
|
|
{genai_otel_instrument-0.1.2.dev0.dist-info → genai_otel_instrument-0.1.7.dev0.dist-info}/RECORD
RENAMED
|
@@ -1,33 +1,34 @@
|
|
|
1
1
|
genai_otel/__init__.py,sha256=OWgm1dihRkwBQU8fUPnVhE5XCZeF5f15UyH4w6LqGZU,4469
|
|
2
|
-
genai_otel/__version__.py,sha256=
|
|
3
|
-
genai_otel/auto_instrument.py,sha256=
|
|
2
|
+
genai_otel/__version__.py,sha256=amWIeki4bm5YbDNKNBNcQKLS8IoAVLPuipySazUun7Y,751
|
|
3
|
+
genai_otel/auto_instrument.py,sha256=NF0Bo_sFMynSmXNh5KFxdsJQPKuPE2NI_bel1i-CtxU,16260
|
|
4
4
|
genai_otel/cli.py,sha256=mbhaTU0WIAkvPKdIing-guIxPDjEKQftChWQUtPFzkY,3170
|
|
5
|
-
genai_otel/config.py,sha256=
|
|
6
|
-
genai_otel/cost_calculator.py,sha256=
|
|
5
|
+
genai_otel/config.py,sha256=2CIbZH8WKkVzr73y9AOWmscvEW-kUwMLSAyOy9BFqGI,7871
|
|
6
|
+
genai_otel/cost_calculator.py,sha256=BOW-TC41lJ1GcL4hIGZ4NySyV8aro4_juMOe2IqtJ-A,18115
|
|
7
|
+
genai_otel/cost_enrichment_processor.py,sha256=07T7q2gBbB7b-L0Dt5VDsVuxB3XSfnpbtFjcV3ZTpk0,7099
|
|
7
8
|
genai_otel/exceptions.py,sha256=gIRvbI7c4V-M-PG9jS0o4ESRwHUWCm6DVihjfyJI1yg,429
|
|
8
|
-
genai_otel/gpu_metrics.py,sha256=
|
|
9
|
-
genai_otel/llm_pricing.json,sha256=
|
|
10
|
-
genai_otel/logging_config.py,sha256=
|
|
9
|
+
genai_otel/gpu_metrics.py,sha256=hBawkm-NErviwiLzb7z92INstFHec2pREn945rYgrT4,13408
|
|
10
|
+
genai_otel/llm_pricing.json,sha256=ZQ1uILEdQ_yNzenvlPpKazo9NnYqEZgbL_tzQ6Mw2oc,20825
|
|
11
|
+
genai_otel/logging_config.py,sha256=S8apGf93nBjoi_Bhce-LxwTwGTaJUeduPXKiWZ5SIa8,1418
|
|
11
12
|
genai_otel/metrics.py,sha256=Vngwtc1MAMAE7JVpbT_KfiCQ5TdIAKIs_0oztjJdDTg,2671
|
|
12
|
-
genai_otel/py.typed,sha256=
|
|
13
|
+
genai_otel/py.typed,sha256=WJtVGe64tcQSssSo4RD7zCf_3u7X2BmFCWDCroWOcaQ,88
|
|
13
14
|
genai_otel/instrumentors/__init__.py,sha256=39CQZZS-AcHNn37pdt1rCOVBvX9t5RGMPY3sEulQzMA,1821
|
|
14
15
|
genai_otel/instrumentors/anthropic_instrumentor.py,sha256=3koeXSJccALdZiRibavwQt8_lrXYwEhdWfA3dnCo_ng,4837
|
|
15
16
|
genai_otel/instrumentors/anyscale_instrumentor.py,sha256=WUcQVDK8W76gkrAT_TLgzFd7NY42Rn6x0irT7VV2bbI,774
|
|
16
17
|
genai_otel/instrumentors/aws_bedrock_instrumentor.py,sha256=W469XxNVsb6eDjerk3SkjZFZEOIzH_HkBkmqqSCRBhU,3670
|
|
17
18
|
genai_otel/instrumentors/azure_openai_instrumentor.py,sha256=HumsAAtW9YzbcyBCrIGhE5KvZ6-mxSbsEoI_W0JU7xg,2428
|
|
18
|
-
genai_otel/instrumentors/base.py,sha256=
|
|
19
|
-
genai_otel/instrumentors/cohere_instrumentor.py,sha256=
|
|
19
|
+
genai_otel/instrumentors/base.py,sha256=5N0eMDoPT49PedhoDM0EGu8NE9UvseaiWhqfb9UHTIw,38210
|
|
20
|
+
genai_otel/instrumentors/cohere_instrumentor.py,sha256=fsKvHaWvMRAGRbOtybVJVVz-FS_-wmgTJo3Q_F86BOY,5074
|
|
20
21
|
genai_otel/instrumentors/google_ai_instrumentor.py,sha256=ExNo0_OxfCxaRpuUXYU8UZ-ClQRHRLUvf7-kMC6zdc8,2984
|
|
21
22
|
genai_otel/instrumentors/groq_instrumentor.py,sha256=bCm7IDmDyvg0-XuzcCSO5xf9QvDlQGwb7bdQ_ooS6QI,3398
|
|
22
|
-
genai_otel/instrumentors/huggingface_instrumentor.py,sha256=
|
|
23
|
-
genai_otel/instrumentors/langchain_instrumentor.py,sha256=
|
|
23
|
+
genai_otel/instrumentors/huggingface_instrumentor.py,sha256=XlSuHEkxEWu0dAXtw1pAFE3n-M8WFRfKUsgbUSV_Arw,9204
|
|
24
|
+
genai_otel/instrumentors/langchain_instrumentor.py,sha256=002ZrKP04l7VaYxo7nAAwl-uvMVwpzVehO2oS23ed-o,2685
|
|
24
25
|
genai_otel/instrumentors/llamaindex_instrumentor.py,sha256=zZ1J7W4yQo1Ur6Y5y0UXpDdEx9oDnmsqNIin5Jrv9os,1206
|
|
25
|
-
genai_otel/instrumentors/mistralai_instrumentor.py,sha256=
|
|
26
|
-
genai_otel/instrumentors/ollama_instrumentor.py,sha256=
|
|
26
|
+
genai_otel/instrumentors/mistralai_instrumentor.py,sha256=Blo8X4WV-xQe-xF-jhkaGPavkgayANf1F3zCTzuhuL0,12478
|
|
27
|
+
genai_otel/instrumentors/ollama_instrumentor.py,sha256=lv45qf8Cqe_HmF7BIMojZcBFK8AA13uUrCVOKAFhN0k,5286
|
|
27
28
|
genai_otel/instrumentors/openai_instrumentor.py,sha256=0q2vml2oWnTRzfVTEP0_njfxqZS8b3Qek-apeecXvvs,9263
|
|
28
|
-
genai_otel/instrumentors/replicate_instrumentor.py,sha256
|
|
29
|
-
genai_otel/instrumentors/togetherai_instrumentor.py,sha256
|
|
30
|
-
genai_otel/instrumentors/vertexai_instrumentor.py,sha256=
|
|
29
|
+
genai_otel/instrumentors/replicate_instrumentor.py,sha256=-G_Tj0VkAfg-cOKvnk4G56eJiADjyIgv6xEgyAWlFdw,3028
|
|
30
|
+
genai_otel/instrumentors/togetherai_instrumentor.py,sha256=--r0YhCBIrpgyFiUwwc1LFTPoxAAXXV4lOAD66laqEY,5414
|
|
31
|
+
genai_otel/instrumentors/vertexai_instrumentor.py,sha256=F0-z0YXANHqccTT3SHbr7ggj3l4ebI_WwubIkPYPa68,4058
|
|
31
32
|
genai_otel/mcp_instrumentors/__init__.py,sha256=0yVx5bwFxCmI72BORDKLgkn0t5h_xRXvtjUskVE60eo,512
|
|
32
33
|
genai_otel/mcp_instrumentors/api_instrumentor.py,sha256=tAogtajeq4zjaM4-05XYSOzD3ig2mXX9_xFyGHL4auQ,5690
|
|
33
34
|
genai_otel/mcp_instrumentors/base.py,sha256=oF_GyGZPEDS-NapIYzM_-TV1KYJeVlXXFzxzs9sMsqU,4044
|
|
@@ -36,9 +37,9 @@ genai_otel/mcp_instrumentors/kafka_instrumentor.py,sha256=QJYJC1rvo_zZAIaw-cp_Ic
|
|
|
36
37
|
genai_otel/mcp_instrumentors/manager.py,sha256=1Pj5lkEOL8Yq1Oeud4ZExN6k6NLIVtTzKnFLNiFdJvw,5895
|
|
37
38
|
genai_otel/mcp_instrumentors/redis_instrumentor.py,sha256=KUbs0dMyfMzU4T0SS8u43I5fvr09lcBBM92I3KCsYUw,943
|
|
38
39
|
genai_otel/mcp_instrumentors/vector_db_instrumentor.py,sha256=2vhnk4PGpfYKr-XlRbnCIOap4BPKHOn--fh-ai2YXlM,9994
|
|
39
|
-
genai_otel_instrument-0.1.
|
|
40
|
-
genai_otel_instrument-0.1.
|
|
41
|
-
genai_otel_instrument-0.1.
|
|
42
|
-
genai_otel_instrument-0.1.
|
|
43
|
-
genai_otel_instrument-0.1.
|
|
44
|
-
genai_otel_instrument-0.1.
|
|
40
|
+
genai_otel_instrument-0.1.7.dev0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
41
|
+
genai_otel_instrument-0.1.7.dev0.dist-info/METADATA,sha256=ogiTaS3MknTz1tAq51JbpZzpxozuLnkD-f2Z4iu57vk,39184
|
|
42
|
+
genai_otel_instrument-0.1.7.dev0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
43
|
+
genai_otel_instrument-0.1.7.dev0.dist-info/entry_points.txt,sha256=E9UqoHA_fq69yNGAY3SRYf5HH94sZT5DiDueiU1v0KM,57
|
|
44
|
+
genai_otel_instrument-0.1.7.dev0.dist-info/top_level.txt,sha256=cvCm8PUwvYUSQKruk-x6S-_YuDyhOBk8gD910XICcbg,11
|
|
45
|
+
genai_otel_instrument-0.1.7.dev0.dist-info/RECORD,,
|
{genai_otel_instrument-0.1.2.dev0.dist-info → genai_otel_instrument-0.1.7.dev0.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|