agentreplay 0.1.2__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.
- agentreplay/__init__.py +81 -0
- agentreplay/auto_instrument/__init__.py +237 -0
- agentreplay/auto_instrument/openai.py +431 -0
- agentreplay/batching.py +270 -0
- agentreplay/bootstrap.py +202 -0
- agentreplay/circuit_breaker.py +300 -0
- agentreplay/client.py +1560 -0
- agentreplay/config.py +215 -0
- agentreplay/context.py +168 -0
- agentreplay/env_config.py +327 -0
- agentreplay/env_init.py +128 -0
- agentreplay/exceptions.py +92 -0
- agentreplay/genai.py +510 -0
- agentreplay/genai_conventions.py +502 -0
- agentreplay/install_pth.py +159 -0
- agentreplay/langchain_tracer.py +385 -0
- agentreplay/models.py +120 -0
- agentreplay/otel_bridge.py +281 -0
- agentreplay/patch.py +308 -0
- agentreplay/propagation.py +328 -0
- agentreplay/py.typed +3 -0
- agentreplay/retry.py +151 -0
- agentreplay/sampling.py +298 -0
- agentreplay/session.py +164 -0
- agentreplay/sitecustomize.py +73 -0
- agentreplay/span.py +270 -0
- agentreplay/unified.py +465 -0
- agentreplay-0.1.2.dist-info/METADATA +285 -0
- agentreplay-0.1.2.dist-info/RECORD +33 -0
- agentreplay-0.1.2.dist-info/WHEEL +5 -0
- agentreplay-0.1.2.dist-info/entry_points.txt +2 -0
- agentreplay-0.1.2.dist-info/licenses/LICENSE +190 -0
- agentreplay-0.1.2.dist-info/top_level.txt +1 -0
agentreplay/config.py
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# Copyright 2025 Sushanth (https://github.com/sushanthpy)
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""Configuration management for Agentreplay SDK.
|
|
16
|
+
|
|
17
|
+
Supports environment variable-based configuration following industry best practices.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import os
|
|
21
|
+
from typing import Optional
|
|
22
|
+
from dataclasses import dataclass, field
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class AgentreplayConfig:
|
|
27
|
+
"""Configuration for Agentreplay observability.
|
|
28
|
+
|
|
29
|
+
All settings can be configured via environment variables for zero-code setup.
|
|
30
|
+
|
|
31
|
+
Environment Variables:
|
|
32
|
+
AGENTREPLAY_ENABLED: Enable/disable observability (default: true)
|
|
33
|
+
AGENTREPLAY_API_KEY: API key for authentication
|
|
34
|
+
AGENTREPLAY_ENDPOINT: Agentreplay server endpoint (default: http://localhost:47100)
|
|
35
|
+
AGENTREPLAY_PROJECT: Project identifier
|
|
36
|
+
AGENTREPLAY_SERVICE_NAME: Service name for identification
|
|
37
|
+
AGENTREPLAY_ENVIRONMENT: Environment (production/staging/development)
|
|
38
|
+
AGENTREPLAY_BATCH_SIZE: Number of spans to batch before sending (default: 100)
|
|
39
|
+
AGENTREPLAY_BATCH_TIMEOUT: Max seconds to wait before sending batch (default: 1.0)
|
|
40
|
+
OTEL_EXPORTER_OTLP_ENDPOINT: Alternative OTLP endpoint specification
|
|
41
|
+
|
|
42
|
+
Example:
|
|
43
|
+
```python
|
|
44
|
+
# Automatic configuration from environment
|
|
45
|
+
config = AgentreplayConfig.from_env()
|
|
46
|
+
|
|
47
|
+
# Manual configuration
|
|
48
|
+
config = AgentreplayConfig(
|
|
49
|
+
api_key="cl_abc123",
|
|
50
|
+
endpoint="https://api.agentreplay.io",
|
|
51
|
+
project="my-agent-project"
|
|
52
|
+
)
|
|
53
|
+
```
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
# Core settings
|
|
57
|
+
enabled: bool = True
|
|
58
|
+
api_key: Optional[str] = None
|
|
59
|
+
endpoint: str = "http://localhost:47100"
|
|
60
|
+
project: Optional[str] = None
|
|
61
|
+
|
|
62
|
+
# Service identification
|
|
63
|
+
service_name: str = "agentreplay-app"
|
|
64
|
+
environment: str = "development"
|
|
65
|
+
version: str = "0.1.0"
|
|
66
|
+
|
|
67
|
+
# Batching configuration for performance
|
|
68
|
+
batch_size: int = 100
|
|
69
|
+
batch_timeout: float = 1.0 # seconds
|
|
70
|
+
|
|
71
|
+
# Advanced settings
|
|
72
|
+
max_retries: int = 3
|
|
73
|
+
timeout: float = 5.0 # seconds
|
|
74
|
+
verify_ssl: bool = True
|
|
75
|
+
|
|
76
|
+
@classmethod
|
|
77
|
+
def from_env(cls) -> "AgentreplayConfig":
|
|
78
|
+
"""Create configuration from environment variables.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
AgentreplayConfig: Configuration instance with values from environment.
|
|
82
|
+
"""
|
|
83
|
+
return cls(
|
|
84
|
+
# Core settings
|
|
85
|
+
enabled=cls._get_bool_env("AGENTREPLAY_ENABLED", True),
|
|
86
|
+
api_key=os.getenv("AGENTREPLAY_API_KEY"),
|
|
87
|
+
endpoint=cls._get_endpoint(),
|
|
88
|
+
project=os.getenv("AGENTREPLAY_PROJECT"),
|
|
89
|
+
|
|
90
|
+
# Service identification
|
|
91
|
+
service_name=os.getenv("AGENTREPLAY_SERVICE_NAME", "agentreplay-app"),
|
|
92
|
+
environment=os.getenv("AGENTREPLAY_ENVIRONMENT", "development"),
|
|
93
|
+
version=os.getenv("AGENTREPLAY_VERSION", "0.1.0"),
|
|
94
|
+
|
|
95
|
+
# Batching
|
|
96
|
+
batch_size=cls._get_int_env("AGENTREPLAY_BATCH_SIZE", 100),
|
|
97
|
+
batch_timeout=cls._get_float_env("AGENTREPLAY_BATCH_TIMEOUT", 1.0),
|
|
98
|
+
|
|
99
|
+
# Advanced
|
|
100
|
+
max_retries=cls._get_int_env("AGENTREPLAY_MAX_RETRIES", 3),
|
|
101
|
+
timeout=cls._get_float_env("AGENTREPLAY_TIMEOUT", 5.0),
|
|
102
|
+
verify_ssl=cls._get_bool_env("AGENTREPLAY_VERIFY_SSL", True),
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
@staticmethod
|
|
106
|
+
def _get_endpoint() -> str:
|
|
107
|
+
"""Get endpoint from multiple possible environment variables."""
|
|
108
|
+
# Check Agentreplay-specific first
|
|
109
|
+
if endpoint := os.getenv("AGENTREPLAY_ENDPOINT"):
|
|
110
|
+
return endpoint
|
|
111
|
+
|
|
112
|
+
# Fall back to OTEL standard
|
|
113
|
+
if endpoint := os.getenv("OTEL_EXPORTER_OTLP_ENDPOINT"):
|
|
114
|
+
return endpoint
|
|
115
|
+
|
|
116
|
+
# Default
|
|
117
|
+
return "http://localhost:47100"
|
|
118
|
+
|
|
119
|
+
@staticmethod
|
|
120
|
+
def _get_bool_env(key: str, default: bool) -> bool:
|
|
121
|
+
"""Get boolean environment variable."""
|
|
122
|
+
value = os.getenv(key)
|
|
123
|
+
if value is None:
|
|
124
|
+
return default
|
|
125
|
+
return value.lower() in ("true", "1", "yes", "on")
|
|
126
|
+
|
|
127
|
+
@staticmethod
|
|
128
|
+
def _get_int_env(key: str, default: int) -> int:
|
|
129
|
+
"""Get integer environment variable."""
|
|
130
|
+
value = os.getenv(key)
|
|
131
|
+
if value is None:
|
|
132
|
+
return default
|
|
133
|
+
try:
|
|
134
|
+
return int(value)
|
|
135
|
+
except ValueError:
|
|
136
|
+
return default
|
|
137
|
+
|
|
138
|
+
@staticmethod
|
|
139
|
+
def _get_float_env(key: str, default: float) -> float:
|
|
140
|
+
"""Get float environment variable."""
|
|
141
|
+
value = os.getenv(key)
|
|
142
|
+
if value is None:
|
|
143
|
+
return default
|
|
144
|
+
try:
|
|
145
|
+
return float(value)
|
|
146
|
+
except ValueError:
|
|
147
|
+
return default
|
|
148
|
+
|
|
149
|
+
def is_enabled(self) -> bool:
|
|
150
|
+
"""Check if observability is enabled."""
|
|
151
|
+
return self.enabled
|
|
152
|
+
|
|
153
|
+
def get_headers(self) -> dict:
|
|
154
|
+
"""Get HTTP headers for API requests."""
|
|
155
|
+
headers = {
|
|
156
|
+
"Content-Type": "application/json",
|
|
157
|
+
"User-Agent": f"agentreplay-python-sdk/{self.version}",
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if self.api_key:
|
|
161
|
+
headers["Authorization"] = f"Bearer {self.api_key}"
|
|
162
|
+
|
|
163
|
+
if self.project:
|
|
164
|
+
headers["X-Agentreplay-Project"] = self.project
|
|
165
|
+
|
|
166
|
+
return headers
|
|
167
|
+
|
|
168
|
+
def __repr__(self) -> str:
|
|
169
|
+
"""String representation with sensitive data masked."""
|
|
170
|
+
api_key_masked = f"{self.api_key[:8]}..." if self.api_key else None
|
|
171
|
+
return (
|
|
172
|
+
f"AgentreplayConfig("
|
|
173
|
+
f"enabled={self.enabled}, "
|
|
174
|
+
f"endpoint={self.endpoint}, "
|
|
175
|
+
f"project={self.project}, "
|
|
176
|
+
f"service_name={self.service_name}, "
|
|
177
|
+
f"api_key={'***' if self.api_key else None})"
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
# Global configuration instance
|
|
182
|
+
_global_config: Optional[AgentreplayConfig] = None
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def get_config() -> AgentreplayConfig:
|
|
186
|
+
"""Get the global configuration instance.
|
|
187
|
+
|
|
188
|
+
Creates configuration from environment variables if not already initialized.
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
AgentreplayConfig: Global configuration instance.
|
|
192
|
+
"""
|
|
193
|
+
global _global_config
|
|
194
|
+
if _global_config is None:
|
|
195
|
+
_global_config = AgentreplayConfig.from_env()
|
|
196
|
+
return _global_config
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def set_config(config: AgentreplayConfig) -> None:
|
|
200
|
+
"""Set the global configuration instance.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
config: Configuration to set as global.
|
|
204
|
+
"""
|
|
205
|
+
global _global_config
|
|
206
|
+
_global_config = config
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def reset_config() -> None:
|
|
210
|
+
"""Reset the global configuration to None.
|
|
211
|
+
|
|
212
|
+
Next call to get_config() will recreate from environment variables.
|
|
213
|
+
"""
|
|
214
|
+
global _global_config
|
|
215
|
+
_global_config = None
|
agentreplay/context.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Copyright 2025 Sushanth (https://github.com/sushanthpy)
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""Agent Context Tracking for Multi-Agent Systems.
|
|
16
|
+
|
|
17
|
+
This module provides contextvars-based context propagation for tracking
|
|
18
|
+
agent execution in multi-agent systems like CrewAI, AutoGen, and LangGraph.
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
>>> from agentreplay.context import AgentContext
|
|
22
|
+
>>>
|
|
23
|
+
>>> with AgentContext(agent_id="researcher", session_id="sess-123"):
|
|
24
|
+
... # All LLM calls here get tagged with agent_id="researcher"
|
|
25
|
+
... response = client.chat.completions.create(...)
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from contextvars import ContextVar
|
|
29
|
+
from typing import Optional
|
|
30
|
+
|
|
31
|
+
# Define context variables
|
|
32
|
+
_agent_id: ContextVar[Optional[str]] = ContextVar('agent_id', default=None)
|
|
33
|
+
_session_id: ContextVar[Optional[str]] = ContextVar('session_id', default=None)
|
|
34
|
+
_workflow_id: ContextVar[Optional[str]] = ContextVar('workflow_id', default=None)
|
|
35
|
+
_user_id: ContextVar[Optional[str]] = ContextVar('user_id', default=None)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class AgentContext:
|
|
39
|
+
"""Context manager for tracking agent execution.
|
|
40
|
+
|
|
41
|
+
This context manager sets context variables that are automatically
|
|
42
|
+
propagated to all LLM calls made within the context. It works with
|
|
43
|
+
both synchronous and asynchronous code.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
agent_id: Unique identifier for the agent
|
|
47
|
+
session_id: Session identifier (optional)
|
|
48
|
+
workflow_id: Workflow identifier (optional)
|
|
49
|
+
user_id: User identifier (optional)
|
|
50
|
+
|
|
51
|
+
Example:
|
|
52
|
+
>>> with AgentContext(agent_id="researcher", session_id="sess-123"):
|
|
53
|
+
... # All LLM calls here are automatically tagged
|
|
54
|
+
... response = openai_client.chat.completions.create(...)
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def __init__(
|
|
58
|
+
self,
|
|
59
|
+
agent_id: str,
|
|
60
|
+
session_id: Optional[str] = None,
|
|
61
|
+
workflow_id: Optional[str] = None,
|
|
62
|
+
user_id: Optional[str] = None
|
|
63
|
+
):
|
|
64
|
+
self.agent_id = agent_id
|
|
65
|
+
self.session_id = session_id
|
|
66
|
+
self.workflow_id = workflow_id
|
|
67
|
+
self.user_id = user_id
|
|
68
|
+
self.tokens = []
|
|
69
|
+
|
|
70
|
+
def __enter__(self):
|
|
71
|
+
# Set context variables, store tokens for cleanup
|
|
72
|
+
self.tokens.append(_agent_id.set(self.agent_id))
|
|
73
|
+
if self.session_id:
|
|
74
|
+
self.tokens.append(_session_id.set(self.session_id))
|
|
75
|
+
if self.workflow_id:
|
|
76
|
+
self.tokens.append(_workflow_id.set(self.workflow_id))
|
|
77
|
+
if self.user_id:
|
|
78
|
+
self.tokens.append(_user_id.set(self.user_id))
|
|
79
|
+
return self
|
|
80
|
+
|
|
81
|
+
def __exit__(self, *args):
|
|
82
|
+
# Reset context variables in reverse order
|
|
83
|
+
for token in reversed(self.tokens):
|
|
84
|
+
token.var.reset(token)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def get_current_agent_id() -> Optional[str]:
|
|
88
|
+
"""Get the current agent ID from context.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Agent ID if set, None otherwise
|
|
92
|
+
"""
|
|
93
|
+
return _agent_id.get()
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def get_current_session_id() -> Optional[str]:
|
|
97
|
+
"""Get the current session ID from context.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
Session ID if set, None otherwise
|
|
101
|
+
"""
|
|
102
|
+
return _session_id.get()
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def get_current_workflow_id() -> Optional[str]:
|
|
106
|
+
"""Get the current workflow ID from context.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
Workflow ID if set, None otherwise
|
|
110
|
+
"""
|
|
111
|
+
return _workflow_id.get()
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def get_current_user_id() -> Optional[str]:
|
|
115
|
+
"""Get the current user ID from context.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
User ID if set, None otherwise
|
|
119
|
+
"""
|
|
120
|
+
return _user_id.get()
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def set_agent_id(agent_id: str):
|
|
124
|
+
"""Set the agent ID in the current context.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
agent_id: Agent identifier
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
Token that can be used to reset the context
|
|
131
|
+
"""
|
|
132
|
+
return _agent_id.set(agent_id)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def set_session_id(session_id: str):
|
|
136
|
+
"""Set the session ID in the current context.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
session_id: Session identifier
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
Token that can be used to reset the context
|
|
143
|
+
"""
|
|
144
|
+
return _session_id.set(session_id)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def set_workflow_id(workflow_id: str):
|
|
148
|
+
"""Set the workflow ID in the current context.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
workflow_id: Workflow identifier
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
Token that can be used to reset the context
|
|
155
|
+
"""
|
|
156
|
+
return _workflow_id.set(workflow_id)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def set_user_id(user_id: str):
|
|
160
|
+
"""Set the user ID in the current context.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
user_id: User identifier
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
Token that can be used to reset the context
|
|
167
|
+
"""
|
|
168
|
+
return _user_id.set(user_id)
|