Skip to content

Commit 77cfc6d

Browse files
committed
Merge branch 'enhance-instrumentation' of https://github.com/AgentOps-AI/agentops into fix_openai
2 parents 79895cb + b79259e commit 77cfc6d

File tree

130 files changed

+4289
-3241
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

130 files changed

+4289
-3241
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ AgentOps helps developers build, evaluate, and monitor AI agents. From prototype
5858
<div style="display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 30px; margin-bottom: 20px;">
5959
<a href="https://docs.agentops.ai/v1/integrations/openai-agents"><img src="docs/images/external/openai/agents-sdk.svg" height="45" alt="OpenAI Agents SDK"></a>
6060
<a href="https://docs.agentops.ai/v1/integrations/crewai"><img src="docs/v1/img/docs-icons/crew-banner.png" height="45" alt="CrewAI"></a>
61-
<a href="https://docs.ag2.ai/docs/ecosystem/agentops"><img src="docs/images/external/autogen/ag2.svg" height="45" alt="AG2 (AutoGen)"></a>
61+
<a href="https://docs.ag2.ai/docs/ecosystem/agentops"><img src="docs/images/external/ag2/ag2-logo.svg" height="45" alt="AG2 (AutoGen)"></a>
6262
<a href="https://docs.agentops.ai/v1/integrations/microsoft"><img src="docs/images/external/microsoft/microsoft_logo.svg" height="45" alt="Microsoft"></a>
6363
</div>
6464

agentops/instrumentation/README.md

Lines changed: 147 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,164 @@ This package provides OpenTelemetry instrumentation for various LLM providers an
44

55
## Available Instrumentors
66

7-
- OpenAI (`v0.27.0+` and `v1.0.0+`)
7+
- **OpenAI** (`v0.27.0+` and `v1.0.0+`)
8+
- **Anthropic** (`v0.7.0+`)
9+
- **Google GenAI** (`v0.1.0+`)
10+
- **IBM WatsonX AI** (`v0.1.0+`)
11+
- **CrewAI** (`v0.56.0+`)
12+
- **AG2/AutoGen** (`v0.3.2+`)
13+
- **Google ADK** (`v0.1.0+`)
14+
- **Agno** (`v0.0.1+`)
15+
- **Mem0** (`v0.1.0+`)
16+
- **SmolAgents** (`v0.1.0+`)
817

18+
## Common Module Usage
919

10-
## Usage
20+
The `agentops.instrumentation.common` module provides shared utilities for creating instrumentations:
1121

12-
### OpenAI Instrumentation
22+
### Base Instrumentor
23+
24+
Use `BaseAgentOpsInstrumentor` for creating new instrumentations:
25+
26+
```python
27+
from agentops.instrumentation.common import BaseAgentOpsInstrumentor, InstrumentorConfig, WrapConfig
28+
29+
class MyInstrumentor(BaseAgentOpsInstrumentor):
30+
def __init__(self):
31+
config = InstrumentorConfig(
32+
library_name="my-library",
33+
library_version="1.0.0",
34+
wrapped_methods=[
35+
WrapConfig(
36+
trace_name="my.method",
37+
package="my_library.module",
38+
class_name="MyClass",
39+
method_name="my_method",
40+
handler=my_attribute_handler
41+
)
42+
],
43+
dependencies=["my-library >= 1.0.0"]
44+
)
45+
super().__init__(config)
46+
```
47+
48+
### Attribute Handlers
49+
50+
Create attribute handlers to extract data from method calls:
51+
52+
```python
53+
from agentops.instrumentation.common import AttributeMap
54+
55+
def my_attribute_handler(args=None, kwargs=None, return_value=None) -> AttributeMap:
56+
attributes = {}
57+
58+
if kwargs and "model" in kwargs:
59+
attributes["llm.request.model"] = kwargs["model"]
60+
61+
if return_value and hasattr(return_value, "usage"):
62+
attributes["llm.usage.total_tokens"] = return_value.usage.total_tokens
63+
64+
return attributes
65+
```
66+
67+
### Span Management
68+
69+
Use the span management utilities for consistent span creation:
70+
71+
```python
72+
from agentops.instrumentation.common import create_span, SpanAttributeManager
73+
74+
# Create an attribute manager
75+
attr_manager = SpanAttributeManager(service_name="my-service")
76+
77+
# Use the create_span context manager
78+
with create_span(
79+
tracer,
80+
"my.operation",
81+
attributes={"my.attribute": "value"},
82+
attribute_manager=attr_manager
83+
) as span:
84+
# Your operation code here
85+
pass
86+
```
87+
88+
### Token Counting
89+
90+
Use the token counting utilities for consistent token usage extraction:
1391

1492
```python
15-
from opentelemetry.instrumentation.openai import OpenAIInstrumentor
93+
from agentops.instrumentation.common import TokenUsageExtractor, set_token_usage_attributes
94+
95+
# Extract token usage from a response
96+
usage = TokenUsageExtractor.extract_from_response(response)
97+
98+
# Set token usage attributes on a span
99+
set_token_usage_attributes(span, response)
100+
```
101+
102+
### Streaming Support
16103

17-
from agentops.telemetry import get_tracer_provider()
104+
Use streaming utilities for handling streaming responses:
18105

19-
# Initialize and instrument
20-
instrumentor = OpenAIInstrumentor(
21-
enrich_assistant=True, # Include assistant messages in spans
22-
enrich_token_usage=True, # Include token usage in spans
23-
enable_trace_context_propagation=True, # Enable trace context propagation
106+
```python
107+
from agentops.instrumentation.common import create_stream_wrapper_factory, StreamingResponseHandler
108+
109+
# Create a stream wrapper factory
110+
wrapper = create_stream_wrapper_factory(
111+
tracer,
112+
"my.stream",
113+
extract_chunk_content=StreamingResponseHandler.extract_generic_chunk_content,
114+
initial_attributes={"stream.type": "text"}
24115
)
25-
instrumentor.instrument(tracer_provider=tracer_provider) # <-- Uses the global AgentOps TracerProvider
116+
117+
# Apply to streaming methods
118+
wrap_function_wrapper("my_module", "stream_method", wrapper)
26119
```
27120

121+
### Metrics
122+
123+
Use standard metrics for consistency across instrumentations:
124+
125+
```python
126+
from agentops.instrumentation.common import StandardMetrics, MetricsRecorder
127+
128+
# Create standard metrics
129+
metrics = StandardMetrics.create_standard_metrics(meter)
130+
131+
# Use the metrics recorder
132+
recorder = MetricsRecorder(metrics)
133+
recorder.record_token_usage(prompt_tokens=100, completion_tokens=50)
134+
recorder.record_duration(1.5)
135+
```
136+
137+
## Creating a New Instrumentor
138+
139+
1. Create a new directory under `agentops/instrumentation/` for your provider
140+
2. Create an `__init__.py` file with version information
141+
3. Create an `instrumentor.py` file extending `BaseAgentOpsInstrumentor`
142+
4. Create attribute handlers in an `attributes/` subdirectory
143+
5. Add your instrumentor to the main `__init__.py` configuration
144+
145+
Example structure:
146+
```
147+
agentops/instrumentation/
148+
├── my_provider/
149+
│ ├── __init__.py
150+
│ ├── instrumentor.py
151+
│ └── attributes/
152+
│ ├── __init__.py
153+
│ └── handlers.py
154+
```
28155

29-
> To add custom instrumentation, please do so in the `third_party/opentelemetry` directory.
156+
## Best Practices
30157

158+
1. **Use Common Utilities**: Leverage the common module for consistency
159+
2. **Follow Semantic Conventions**: Use attributes from `agentops.semconv`
160+
3. **Handle Errors Gracefully**: Wrap operations in try-except blocks
161+
4. **Support Async**: Provide both sync and async method wrapping
162+
5. **Document Attributes**: Comment on what attributes are captured
163+
6. **Test Thoroughly**: Write unit tests for your instrumentor
31164

165+
## Examples
32166

167+
See the `examples/` directory for usage examples of each instrumentor.

agentops/instrumentation/__init__.py

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
from dataclasses import dataclass
2323
import importlib
2424
import sys
25-
from importlib.metadata import version
2625
from packaging.version import Version, parse
2726
import builtins
2827

@@ -34,6 +33,7 @@
3433

3534
from agentops.logging import logger
3635
from agentops.sdk.core import tracer
36+
from agentops.instrumentation.common import get_library_version
3737

3838

3939
# Define the structure for instrumentor configurations
@@ -47,28 +47,28 @@ class InstrumentorConfig(TypedDict):
4747
# Configuration for supported LLM providers
4848
PROVIDERS: dict[str, InstrumentorConfig] = {
4949
"openai": {
50-
"module_name": "agentops.instrumentation.openai",
50+
"module_name": "agentops.instrumentation.providers.openai",
5151
"class_name": "OpenAIInstrumentor",
5252
"min_version": "1.0.0",
5353
},
5454
"anthropic": {
55-
"module_name": "agentops.instrumentation.anthropic",
55+
"module_name": "agentops.instrumentation.providers.anthropic",
5656
"class_name": "AnthropicInstrumentor",
5757
"min_version": "0.32.0",
5858
},
5959
"ibm_watsonx_ai": {
60-
"module_name": "agentops.instrumentation.ibm_watsonx_ai",
60+
"module_name": "agentops.instrumentation.providers.ibm_watsonx_ai",
6161
"class_name": "IBMWatsonXInstrumentor",
6262
"min_version": "0.1.0",
6363
},
6464
"google.genai": {
65-
"module_name": "agentops.instrumentation.google_genai",
65+
"module_name": "agentops.instrumentation.providers.google_genai",
6666
"class_name": "GoogleGenAIInstrumentor",
6767
"min_version": "0.1.0",
6868
"package_name": "google-genai", # Actual pip package name
6969
},
7070
"mem0": {
71-
"module_name": "agentops.instrumentation.mem0",
71+
"module_name": "agentops.instrumentation.providers.mem0",
7272
"class_name": "Mem0Instrumentor",
7373
"min_version": "0.1.0",
7474
"package_name": "mem0ai",
@@ -78,7 +78,7 @@ class InstrumentorConfig(TypedDict):
7878
# Configuration for utility instrumentors
7979
UTILITY_INSTRUMENTORS: dict[str, InstrumentorConfig] = {
8080
"concurrent.futures": {
81-
"module_name": "agentops.instrumentation.concurrent_futures",
81+
"module_name": "agentops.instrumentation.utilities.concurrent_futures",
8282
"class_name": "ConcurrentFuturesInstrumentor",
8383
"min_version": "3.7.0", # Python 3.7+ (concurrent.futures is stdlib)
8484
"package_name": "python", # Special case for stdlib modules
@@ -88,21 +88,35 @@ class InstrumentorConfig(TypedDict):
8888
# Configuration for supported agentic libraries
8989
AGENTIC_LIBRARIES: dict[str, InstrumentorConfig] = {
9090
"crewai": {
91-
"module_name": "agentops.instrumentation.crewai",
91+
"module_name": "agentops.instrumentation.agentic.crewai",
9292
"class_name": "CrewAIInstrumentor",
9393
"min_version": "0.56.0",
9494
},
95-
"autogen": {"module_name": "agentops.instrumentation.ag2", "class_name": "AG2Instrumentor", "min_version": "0.3.2"},
95+
"autogen": {
96+
"module_name": "agentops.instrumentation.agentic.ag2",
97+
"class_name": "AG2Instrumentor",
98+
"min_version": "0.3.2",
99+
},
96100
"agents": {
97-
"module_name": "agentops.instrumentation.openai_agents",
101+
"module_name": "agentops.instrumentation.agentic.openai_agents",
98102
"class_name": "OpenAIAgentsInstrumentor",
99103
"min_version": "0.0.1",
100104
},
101105
"google.adk": {
102-
"module_name": "agentops.instrumentation.google_adk",
106+
"module_name": "agentops.instrumentation.agentic.google_adk",
103107
"class_name": "GoogleADKInstrumentor",
104108
"min_version": "0.1.0",
105109
},
110+
"agno": {
111+
"module_name": "agentops.instrumentation.agentic.agno",
112+
"class_name": "AgnoInstrumentor",
113+
"min_version": "0.1.0",
114+
},
115+
"smolagents": {
116+
"module_name": "agentops.instrumentation.agentic.smolagents",
117+
"class_name": "SmolAgentsInstrumentor",
118+
"min_version": "1.0.0",
119+
},
106120
}
107121

108122
# Combine all target packages for monitoring
@@ -456,9 +470,11 @@ def should_activate(self) -> bool:
456470
provider_name = self.package_name
457471
else:
458472
provider_name = self.module_name.split(".")[-1]
459-
module_version = version(provider_name)
460-
return module_version is not None and Version(module_version) >= parse(self.min_version)
461-
except ImportError:
473+
474+
# Use common version utility
475+
module_version = get_library_version(provider_name)
476+
return module_version != "unknown" and Version(module_version) >= parse(self.min_version)
477+
except Exception:
462478
return False
463479

464480
def get_instance(self) -> BaseInstrumentor:

agentops/instrumentation/ag2/__init__.py

Lines changed: 0 additions & 28 deletions
This file was deleted.

0 commit comments

Comments
 (0)