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/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)