mseep-agentops 0.4.18__py3-none-any.whl → 0.4.23__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.
- agentops/__init__.py +0 -0
- agentops/client/api/base.py +28 -30
- agentops/client/api/versions/v3.py +29 -25
- agentops/client/api/versions/v4.py +87 -46
- agentops/client/client.py +98 -29
- agentops/client/http/README.md +87 -0
- agentops/client/http/http_client.py +126 -172
- agentops/config.py +8 -2
- agentops/instrumentation/OpenTelemetry.md +133 -0
- agentops/instrumentation/README.md +167 -0
- agentops/instrumentation/__init__.py +13 -1
- agentops/instrumentation/agentic/ag2/__init__.py +18 -0
- agentops/instrumentation/agentic/ag2/instrumentor.py +922 -0
- agentops/instrumentation/agentic/agno/__init__.py +19 -0
- agentops/instrumentation/agentic/agno/attributes/__init__.py +20 -0
- agentops/instrumentation/agentic/agno/attributes/agent.py +250 -0
- agentops/instrumentation/agentic/agno/attributes/metrics.py +214 -0
- agentops/instrumentation/agentic/agno/attributes/storage.py +158 -0
- agentops/instrumentation/agentic/agno/attributes/team.py +195 -0
- agentops/instrumentation/agentic/agno/attributes/tool.py +210 -0
- agentops/instrumentation/agentic/agno/attributes/workflow.py +254 -0
- agentops/instrumentation/agentic/agno/instrumentor.py +1313 -0
- agentops/instrumentation/agentic/crewai/LICENSE +201 -0
- agentops/instrumentation/agentic/crewai/NOTICE.md +10 -0
- agentops/instrumentation/agentic/crewai/__init__.py +6 -0
- agentops/instrumentation/agentic/crewai/crewai_span_attributes.py +335 -0
- agentops/instrumentation/agentic/crewai/instrumentation.py +535 -0
- agentops/instrumentation/agentic/crewai/version.py +1 -0
- agentops/instrumentation/agentic/google_adk/__init__.py +19 -0
- agentops/instrumentation/agentic/google_adk/instrumentor.py +68 -0
- agentops/instrumentation/agentic/google_adk/patch.py +767 -0
- agentops/instrumentation/agentic/haystack/__init__.py +1 -0
- agentops/instrumentation/agentic/haystack/instrumentor.py +186 -0
- agentops/instrumentation/agentic/langgraph/__init__.py +3 -0
- agentops/instrumentation/agentic/langgraph/attributes.py +54 -0
- agentops/instrumentation/agentic/langgraph/instrumentation.py +598 -0
- agentops/instrumentation/agentic/langgraph/version.py +1 -0
- agentops/instrumentation/agentic/openai_agents/README.md +156 -0
- agentops/instrumentation/agentic/openai_agents/SPANS.md +145 -0
- agentops/instrumentation/agentic/openai_agents/TRACING_API.md +144 -0
- agentops/instrumentation/agentic/openai_agents/__init__.py +30 -0
- agentops/instrumentation/agentic/openai_agents/attributes/common.py +549 -0
- agentops/instrumentation/agentic/openai_agents/attributes/completion.py +172 -0
- agentops/instrumentation/agentic/openai_agents/attributes/model.py +58 -0
- agentops/instrumentation/agentic/openai_agents/attributes/tokens.py +275 -0
- agentops/instrumentation/agentic/openai_agents/exporter.py +469 -0
- agentops/instrumentation/agentic/openai_agents/instrumentor.py +107 -0
- agentops/instrumentation/agentic/openai_agents/processor.py +58 -0
- agentops/instrumentation/agentic/smolagents/README.md +88 -0
- agentops/instrumentation/agentic/smolagents/__init__.py +12 -0
- agentops/instrumentation/agentic/smolagents/attributes/agent.py +354 -0
- agentops/instrumentation/agentic/smolagents/attributes/model.py +205 -0
- agentops/instrumentation/agentic/smolagents/instrumentor.py +286 -0
- agentops/instrumentation/agentic/smolagents/stream_wrapper.py +258 -0
- agentops/instrumentation/agentic/xpander/__init__.py +15 -0
- agentops/instrumentation/agentic/xpander/context.py +112 -0
- agentops/instrumentation/agentic/xpander/instrumentor.py +877 -0
- agentops/instrumentation/agentic/xpander/trace_probe.py +86 -0
- agentops/instrumentation/agentic/xpander/version.py +3 -0
- agentops/instrumentation/common/README.md +65 -0
- agentops/instrumentation/common/attributes.py +1 -2
- agentops/instrumentation/providers/anthropic/__init__.py +24 -0
- agentops/instrumentation/providers/anthropic/attributes/__init__.py +23 -0
- agentops/instrumentation/providers/anthropic/attributes/common.py +64 -0
- agentops/instrumentation/providers/anthropic/attributes/message.py +541 -0
- agentops/instrumentation/providers/anthropic/attributes/tools.py +231 -0
- agentops/instrumentation/providers/anthropic/event_handler_wrapper.py +90 -0
- agentops/instrumentation/providers/anthropic/instrumentor.py +146 -0
- agentops/instrumentation/providers/anthropic/stream_wrapper.py +436 -0
- agentops/instrumentation/providers/google_genai/README.md +33 -0
- agentops/instrumentation/providers/google_genai/__init__.py +24 -0
- agentops/instrumentation/providers/google_genai/attributes/__init__.py +25 -0
- agentops/instrumentation/providers/google_genai/attributes/chat.py +125 -0
- agentops/instrumentation/providers/google_genai/attributes/common.py +88 -0
- agentops/instrumentation/providers/google_genai/attributes/model.py +284 -0
- agentops/instrumentation/providers/google_genai/instrumentor.py +170 -0
- agentops/instrumentation/providers/google_genai/stream_wrapper.py +238 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/__init__.py +28 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/attributes/__init__.py +27 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/attributes/attributes.py +277 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/attributes/common.py +104 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/instrumentor.py +162 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/stream_wrapper.py +302 -0
- agentops/instrumentation/providers/mem0/__init__.py +45 -0
- agentops/instrumentation/providers/mem0/common.py +377 -0
- agentops/instrumentation/providers/mem0/instrumentor.py +270 -0
- agentops/instrumentation/providers/mem0/memory.py +430 -0
- agentops/instrumentation/providers/openai/__init__.py +21 -0
- agentops/instrumentation/providers/openai/attributes/__init__.py +7 -0
- agentops/instrumentation/providers/openai/attributes/common.py +55 -0
- agentops/instrumentation/providers/openai/attributes/response.py +607 -0
- agentops/instrumentation/providers/openai/config.py +36 -0
- agentops/instrumentation/providers/openai/instrumentor.py +312 -0
- agentops/instrumentation/providers/openai/stream_wrapper.py +941 -0
- agentops/instrumentation/providers/openai/utils.py +44 -0
- agentops/instrumentation/providers/openai/v0.py +176 -0
- agentops/instrumentation/providers/openai/v0_wrappers.py +483 -0
- agentops/instrumentation/providers/openai/wrappers/__init__.py +30 -0
- agentops/instrumentation/providers/openai/wrappers/assistant.py +277 -0
- agentops/instrumentation/providers/openai/wrappers/chat.py +259 -0
- agentops/instrumentation/providers/openai/wrappers/completion.py +109 -0
- agentops/instrumentation/providers/openai/wrappers/embeddings.py +94 -0
- agentops/instrumentation/providers/openai/wrappers/image_gen.py +75 -0
- agentops/instrumentation/providers/openai/wrappers/responses.py +191 -0
- agentops/instrumentation/providers/openai/wrappers/shared.py +81 -0
- agentops/instrumentation/utilities/concurrent_futures/__init__.py +10 -0
- agentops/instrumentation/utilities/concurrent_futures/instrumentation.py +206 -0
- agentops/integration/callbacks/dspy/__init__.py +11 -0
- agentops/integration/callbacks/dspy/callback.py +471 -0
- agentops/integration/callbacks/langchain/README.md +59 -0
- agentops/integration/callbacks/langchain/__init__.py +15 -0
- agentops/integration/callbacks/langchain/callback.py +791 -0
- agentops/integration/callbacks/langchain/utils.py +54 -0
- agentops/legacy/crewai.md +121 -0
- agentops/logging/instrument_logging.py +4 -0
- agentops/sdk/README.md +220 -0
- agentops/sdk/core.py +75 -32
- agentops/sdk/descriptors/classproperty.py +28 -0
- agentops/sdk/exporters.py +152 -33
- agentops/semconv/README.md +125 -0
- agentops/semconv/span_kinds.py +0 -2
- agentops/validation.py +102 -63
- {mseep_agentops-0.4.18.dist-info → mseep_agentops-0.4.23.dist-info}/METADATA +30 -40
- mseep_agentops-0.4.23.dist-info/RECORD +178 -0
- {mseep_agentops-0.4.18.dist-info → mseep_agentops-0.4.23.dist-info}/WHEEL +1 -2
- mseep_agentops-0.4.18.dist-info/RECORD +0 -94
- mseep_agentops-0.4.18.dist-info/top_level.txt +0 -2
- tests/conftest.py +0 -10
- tests/unit/client/__init__.py +0 -1
- tests/unit/client/test_http_adapter.py +0 -221
- tests/unit/client/test_http_client.py +0 -206
- tests/unit/conftest.py +0 -54
- tests/unit/sdk/__init__.py +0 -1
- tests/unit/sdk/instrumentation_tester.py +0 -207
- tests/unit/sdk/test_attributes.py +0 -392
- tests/unit/sdk/test_concurrent_instrumentation.py +0 -468
- tests/unit/sdk/test_decorators.py +0 -763
- tests/unit/sdk/test_exporters.py +0 -241
- tests/unit/sdk/test_factory.py +0 -1188
- tests/unit/sdk/test_internal_span_processor.py +0 -397
- tests/unit/sdk/test_resource_attributes.py +0 -35
- tests/unit/test_config.py +0 -82
- tests/unit/test_context_manager.py +0 -777
- tests/unit/test_events.py +0 -27
- tests/unit/test_host_env.py +0 -54
- tests/unit/test_init_py.py +0 -501
- tests/unit/test_serialization.py +0 -433
- tests/unit/test_session.py +0 -676
- tests/unit/test_user_agent.py +0 -34
- tests/unit/test_validation.py +0 -405
- {tests → agentops/instrumentation/agentic/openai_agents/attributes}/__init__.py +0 -0
- /tests/unit/__init__.py → /agentops/instrumentation/providers/openai/attributes/tools.py +0 -0
- {mseep_agentops-0.4.18.dist-info → mseep_agentops-0.4.23.dist-info}/licenses/LICENSE +0 -0
@@ -1,777 +0,0 @@
|
|
1
|
-
import unittest
|
2
|
-
from unittest.mock import Mock, patch
|
3
|
-
import threading
|
4
|
-
import time
|
5
|
-
import asyncio
|
6
|
-
|
7
|
-
from agentops import start_trace
|
8
|
-
from agentops.sdk.core import TraceContext
|
9
|
-
from opentelemetry.trace import StatusCode
|
10
|
-
|
11
|
-
|
12
|
-
class TestContextManager(unittest.TestCase):
|
13
|
-
"""Test the context manager functionality of TraceContext"""
|
14
|
-
|
15
|
-
def test_trace_context_has_context_manager_methods(self):
|
16
|
-
"""Test that TraceContext has __enter__ and __exit__ methods"""
|
17
|
-
# TraceContext should have context manager protocol methods
|
18
|
-
assert hasattr(TraceContext, "__enter__")
|
19
|
-
assert hasattr(TraceContext, "__exit__")
|
20
|
-
|
21
|
-
@patch("agentops.sdk.core.tracer")
|
22
|
-
def test_trace_context_enter_returns_self(self, mock_tracer):
|
23
|
-
"""Test that __enter__ returns the TraceContext instance"""
|
24
|
-
mock_span = Mock()
|
25
|
-
mock_token = Mock()
|
26
|
-
trace_context = TraceContext(span=mock_span, token=mock_token)
|
27
|
-
|
28
|
-
# __enter__ should return self
|
29
|
-
result = trace_context.__enter__()
|
30
|
-
assert result is trace_context
|
31
|
-
|
32
|
-
@patch("agentops.sdk.core.tracer")
|
33
|
-
def test_trace_context_exit_calls_end_trace(self, mock_tracer):
|
34
|
-
"""Test that __exit__ calls end_trace with appropriate state"""
|
35
|
-
mock_span = Mock()
|
36
|
-
mock_token = Mock()
|
37
|
-
trace_context = TraceContext(span=mock_span, token=mock_token)
|
38
|
-
|
39
|
-
# Test normal exit (no exception)
|
40
|
-
trace_context.__exit__(None, None, None)
|
41
|
-
mock_tracer.end_trace.assert_called_once_with(trace_context, StatusCode.OK)
|
42
|
-
|
43
|
-
@patch("agentops.sdk.core.tracer")
|
44
|
-
def test_trace_context_exit_with_exception_sets_error_state(self, mock_tracer):
|
45
|
-
"""Test that __exit__ sets ERROR state when exception occurs"""
|
46
|
-
mock_span = Mock()
|
47
|
-
mock_token = Mock()
|
48
|
-
trace_context = TraceContext(span=mock_span, token=mock_token)
|
49
|
-
|
50
|
-
# Test exit with exception
|
51
|
-
mock_tracer.reset_mock()
|
52
|
-
exc_type = ValueError
|
53
|
-
exc_val = ValueError("test error")
|
54
|
-
exc_tb = None
|
55
|
-
|
56
|
-
trace_context.__exit__(exc_type, exc_val, exc_tb)
|
57
|
-
mock_tracer.end_trace.assert_called_once_with(trace_context, StatusCode.ERROR)
|
58
|
-
|
59
|
-
@patch("agentops.sdk.core.tracer")
|
60
|
-
@patch("agentops.tracer")
|
61
|
-
def test_context_manager_usage_pattern(self, mock_agentops_tracer, mock_core_tracer):
|
62
|
-
"""Test using start_trace as a context manager"""
|
63
|
-
# Create a mock TraceContext
|
64
|
-
mock_span = Mock()
|
65
|
-
mock_token = Mock()
|
66
|
-
mock_trace_context = TraceContext(span=mock_span, token=mock_token)
|
67
|
-
|
68
|
-
# Mock the tracer's start_trace method to return our TraceContext
|
69
|
-
mock_agentops_tracer.initialized = True
|
70
|
-
mock_agentops_tracer.start_trace.return_value = mock_trace_context
|
71
|
-
|
72
|
-
# Use as context manager
|
73
|
-
with start_trace("test_trace") as trace:
|
74
|
-
assert trace is mock_trace_context
|
75
|
-
assert trace.span is mock_span
|
76
|
-
assert trace.token is mock_token
|
77
|
-
|
78
|
-
# Verify start_trace was called
|
79
|
-
mock_agentops_tracer.start_trace.assert_called_once_with(trace_name="test_trace", tags=None)
|
80
|
-
# Verify end_trace was called
|
81
|
-
mock_core_tracer.end_trace.assert_called_once_with(mock_trace_context, StatusCode.OK)
|
82
|
-
|
83
|
-
@patch("agentops.sdk.core.tracer")
|
84
|
-
@patch("agentops.tracer")
|
85
|
-
def test_context_manager_with_exception(self, mock_agentops_tracer, mock_core_tracer):
|
86
|
-
"""Test context manager handles exceptions properly"""
|
87
|
-
# Create a mock TraceContext
|
88
|
-
mock_span = Mock()
|
89
|
-
mock_token = Mock()
|
90
|
-
mock_trace_context = TraceContext(span=mock_span, token=mock_token)
|
91
|
-
|
92
|
-
# Mock the tracer's start_trace method
|
93
|
-
mock_agentops_tracer.initialized = True
|
94
|
-
mock_agentops_tracer.start_trace.return_value = mock_trace_context
|
95
|
-
|
96
|
-
# Test exception handling
|
97
|
-
with self.assertRaises(ValueError):
|
98
|
-
with start_trace("test_trace"):
|
99
|
-
raise ValueError("Test exception")
|
100
|
-
|
101
|
-
# Verify end_trace was called with ERROR state
|
102
|
-
mock_core_tracer.end_trace.assert_called_once_with(mock_trace_context, StatusCode.ERROR)
|
103
|
-
|
104
|
-
@patch("agentops.sdk.core.tracer")
|
105
|
-
@patch("agentops.tracer")
|
106
|
-
@patch("agentops.init")
|
107
|
-
def test_start_trace_auto_initializes_if_needed(self, mock_init, mock_agentops_tracer, mock_core_tracer):
|
108
|
-
"""Test that start_trace attempts to initialize SDK if not initialized"""
|
109
|
-
# First call: SDK not initialized
|
110
|
-
mock_agentops_tracer.initialized = False
|
111
|
-
|
112
|
-
# After init() is called, set initialized to True
|
113
|
-
def set_initialized():
|
114
|
-
mock_agentops_tracer.initialized = True
|
115
|
-
|
116
|
-
mock_init.side_effect = set_initialized
|
117
|
-
|
118
|
-
# Create a mock TraceContext for when start_trace is called after init
|
119
|
-
mock_span = Mock()
|
120
|
-
mock_token = Mock()
|
121
|
-
mock_trace_context = TraceContext(span=mock_span, token=mock_token)
|
122
|
-
mock_agentops_tracer.start_trace.return_value = mock_trace_context
|
123
|
-
|
124
|
-
# Call start_trace
|
125
|
-
result = start_trace("test_trace")
|
126
|
-
|
127
|
-
# Verify init was called
|
128
|
-
mock_init.assert_called_once()
|
129
|
-
# Verify start_trace was called on tracer
|
130
|
-
mock_agentops_tracer.start_trace.assert_called_once_with(trace_name="test_trace", tags=None)
|
131
|
-
assert result is mock_trace_context
|
132
|
-
|
133
|
-
def test_no_wrapper_classes_needed(self):
|
134
|
-
"""Test that we don't need wrapper classes - TraceContext is the context manager"""
|
135
|
-
# TraceContext itself implements the context manager protocol
|
136
|
-
# No need for TraceContextManager wrapper
|
137
|
-
mock_span = Mock()
|
138
|
-
mock_token = Mock()
|
139
|
-
trace_context = TraceContext(span=mock_span, token=mock_token)
|
140
|
-
|
141
|
-
# Can use directly as context manager
|
142
|
-
assert hasattr(trace_context, "__enter__")
|
143
|
-
assert hasattr(trace_context, "__exit__")
|
144
|
-
assert callable(trace_context.__enter__)
|
145
|
-
assert callable(trace_context.__exit__)
|
146
|
-
|
147
|
-
@patch("agentops.sdk.core.tracer")
|
148
|
-
@patch("agentops.tracer")
|
149
|
-
def test_parallel_traces_independence(self, mock_agentops_tracer, mock_core_tracer):
|
150
|
-
"""Test that multiple traces can run in parallel independently"""
|
151
|
-
# Create mock TraceContexts
|
152
|
-
mock_trace1 = TraceContext(span=Mock(), token=Mock())
|
153
|
-
mock_trace2 = TraceContext(span=Mock(), token=Mock())
|
154
|
-
|
155
|
-
# Mock the tracer to return different traces
|
156
|
-
mock_agentops_tracer.initialized = True
|
157
|
-
mock_agentops_tracer.start_trace.side_effect = [mock_trace1, mock_trace2]
|
158
|
-
|
159
|
-
# Start two traces
|
160
|
-
trace1 = start_trace("trace1")
|
161
|
-
trace2 = start_trace("trace2")
|
162
|
-
|
163
|
-
# They should be different instances
|
164
|
-
assert trace1 is not trace2
|
165
|
-
assert trace1 is mock_trace1
|
166
|
-
assert trace2 is mock_trace2
|
167
|
-
|
168
|
-
# End them independently using context manager protocol
|
169
|
-
trace1.__exit__(None, None, None)
|
170
|
-
trace2.__exit__(None, None, None)
|
171
|
-
|
172
|
-
# Verify both were ended
|
173
|
-
assert mock_core_tracer.end_trace.call_count == 2
|
174
|
-
|
175
|
-
@patch("agentops.sdk.core.tracer")
|
176
|
-
@patch("agentops.tracer")
|
177
|
-
def test_nested_context_managers_create_parallel_traces(self, mock_agentops_tracer, mock_core_tracer):
|
178
|
-
"""Test that nested context managers create parallel traces, not parent-child"""
|
179
|
-
# Create mock TraceContexts
|
180
|
-
mock_outer = TraceContext(span=Mock(), token=Mock())
|
181
|
-
mock_inner = TraceContext(span=Mock(), token=Mock())
|
182
|
-
|
183
|
-
# Mock the tracer
|
184
|
-
mock_agentops_tracer.initialized = True
|
185
|
-
mock_agentops_tracer.start_trace.side_effect = [mock_outer, mock_inner]
|
186
|
-
|
187
|
-
# Use nested context managers
|
188
|
-
with start_trace("outer_trace") as outer:
|
189
|
-
assert outer is mock_outer
|
190
|
-
with start_trace("inner_trace") as inner:
|
191
|
-
assert inner is mock_inner
|
192
|
-
assert inner is not outer
|
193
|
-
# Both traces are active
|
194
|
-
assert mock_agentops_tracer.start_trace.call_count == 2
|
195
|
-
|
196
|
-
# Verify both were ended
|
197
|
-
assert mock_core_tracer.end_trace.call_count == 2
|
198
|
-
# Inner trace ended first, then outer
|
199
|
-
calls = mock_core_tracer.end_trace.call_args_list
|
200
|
-
assert calls[0][0][0] is mock_inner
|
201
|
-
assert calls[1][0][0] is mock_outer
|
202
|
-
|
203
|
-
@patch("agentops.sdk.core.tracer")
|
204
|
-
@patch("agentops.tracer")
|
205
|
-
def test_exception_in_nested_traces(self, mock_agentops_tracer, mock_core_tracer):
|
206
|
-
"""Test exception handling in nested traces"""
|
207
|
-
# Create mock TraceContexts
|
208
|
-
mock_outer = TraceContext(span=Mock(), token=Mock())
|
209
|
-
mock_inner = TraceContext(span=Mock(), token=Mock())
|
210
|
-
|
211
|
-
# Mock the tracer
|
212
|
-
mock_agentops_tracer.initialized = True
|
213
|
-
mock_agentops_tracer.start_trace.side_effect = [mock_outer, mock_inner]
|
214
|
-
|
215
|
-
# Test exception in inner trace
|
216
|
-
with self.assertRaises(ValueError):
|
217
|
-
with start_trace("outer_trace"):
|
218
|
-
with start_trace("inner_trace"):
|
219
|
-
raise ValueError("Inner exception")
|
220
|
-
|
221
|
-
# Both traces should be ended with ERROR state
|
222
|
-
assert mock_core_tracer.end_trace.call_count == 2
|
223
|
-
calls = mock_core_tracer.end_trace.call_args_list
|
224
|
-
# Inner trace ended with ERROR
|
225
|
-
assert calls[0][0][0] is mock_inner
|
226
|
-
assert calls[0][0][1] == StatusCode.ERROR
|
227
|
-
# Outer trace also ended with ERROR (exception propagated)
|
228
|
-
assert calls[1][0][0] is mock_outer
|
229
|
-
assert calls[1][0][1] == StatusCode.ERROR
|
230
|
-
|
231
|
-
@patch("agentops.sdk.core.tracer")
|
232
|
-
def test_trace_context_attributes_access(self, mock_tracer):
|
233
|
-
"""Test accessing span and token attributes of TraceContext"""
|
234
|
-
mock_span = Mock()
|
235
|
-
mock_token = Mock()
|
236
|
-
trace_context = TraceContext(span=mock_span, token=mock_token)
|
237
|
-
|
238
|
-
# Direct attribute access
|
239
|
-
assert trace_context.span is mock_span
|
240
|
-
assert trace_context.token is mock_token
|
241
|
-
|
242
|
-
@patch("agentops.sdk.core.tracer")
|
243
|
-
@patch("agentops.tracer")
|
244
|
-
def test_multiple_exceptions_in_sequence(self, mock_agentops_tracer, mock_core_tracer):
|
245
|
-
"""Test handling multiple exceptions in sequence"""
|
246
|
-
# Mock the tracer
|
247
|
-
mock_agentops_tracer.initialized = True
|
248
|
-
|
249
|
-
# Create different mock traces for each attempt
|
250
|
-
mock_traces = [TraceContext(span=Mock(), token=Mock()) for _ in range(3)]
|
251
|
-
mock_agentops_tracer.start_trace.side_effect = mock_traces
|
252
|
-
|
253
|
-
# Multiple traces with exceptions
|
254
|
-
for i in range(3):
|
255
|
-
with self.assertRaises(RuntimeError):
|
256
|
-
with start_trace(f"trace_{i}"):
|
257
|
-
raise RuntimeError(f"Error {i}")
|
258
|
-
|
259
|
-
# All should be ended with ERROR state
|
260
|
-
assert mock_core_tracer.end_trace.call_count == 3
|
261
|
-
for i, call in enumerate(mock_core_tracer.end_trace.call_args_list):
|
262
|
-
assert call[0][0] is mock_traces[i]
|
263
|
-
assert call[0][1] == StatusCode.ERROR
|
264
|
-
|
265
|
-
@patch("agentops.sdk.core.tracer")
|
266
|
-
@patch("agentops.tracer")
|
267
|
-
def test_trace_with_tags_dict(self, mock_agentops_tracer, mock_core_tracer):
|
268
|
-
"""Test starting trace with tags as dictionary"""
|
269
|
-
# Create a mock TraceContext
|
270
|
-
mock_trace = TraceContext(span=Mock(), token=Mock())
|
271
|
-
mock_agentops_tracer.initialized = True
|
272
|
-
mock_agentops_tracer.start_trace.return_value = mock_trace
|
273
|
-
|
274
|
-
tags = {"environment": "test", "version": "1.0"}
|
275
|
-
with start_trace("tagged_trace", tags=tags) as trace:
|
276
|
-
assert trace is mock_trace
|
277
|
-
|
278
|
-
# Verify tags were passed
|
279
|
-
mock_agentops_tracer.start_trace.assert_called_once_with(trace_name="tagged_trace", tags=tags)
|
280
|
-
|
281
|
-
@patch("agentops.sdk.core.tracer")
|
282
|
-
@patch("agentops.tracer")
|
283
|
-
def test_trace_with_tags_list(self, mock_agentops_tracer, mock_core_tracer):
|
284
|
-
"""Test starting trace with tags as list"""
|
285
|
-
# Create a mock TraceContext
|
286
|
-
mock_trace = TraceContext(span=Mock(), token=Mock())
|
287
|
-
mock_agentops_tracer.initialized = True
|
288
|
-
mock_agentops_tracer.start_trace.return_value = mock_trace
|
289
|
-
|
290
|
-
tags = ["test", "v1.0", "experimental"]
|
291
|
-
with start_trace("tagged_trace", tags=tags) as trace:
|
292
|
-
assert trace is mock_trace
|
293
|
-
|
294
|
-
# Verify tags were passed
|
295
|
-
mock_agentops_tracer.start_trace.assert_called_once_with(trace_name="tagged_trace", tags=tags)
|
296
|
-
|
297
|
-
@patch("agentops.sdk.core.tracer")
|
298
|
-
@patch("agentops.tracer")
|
299
|
-
def test_trace_context_manager_thread_safety(self, mock_agentops_tracer, mock_core_tracer):
|
300
|
-
"""Test that context managers work correctly in multi-threaded environment"""
|
301
|
-
# Mock the tracer
|
302
|
-
mock_agentops_tracer.initialized = True
|
303
|
-
|
304
|
-
# Create unique traces for each thread
|
305
|
-
thread_traces = {}
|
306
|
-
trace_lock = threading.Lock()
|
307
|
-
|
308
|
-
def create_trace(trace_name=None, tags=None, **kwargs):
|
309
|
-
trace = TraceContext(span=Mock(), token=Mock())
|
310
|
-
with trace_lock:
|
311
|
-
thread_traces[threading.current_thread().ident] = trace
|
312
|
-
return trace
|
313
|
-
|
314
|
-
mock_agentops_tracer.start_trace.side_effect = create_trace
|
315
|
-
|
316
|
-
results = []
|
317
|
-
errors = []
|
318
|
-
|
319
|
-
def worker(thread_id):
|
320
|
-
try:
|
321
|
-
with start_trace(f"thread_{thread_id}_trace") as trace:
|
322
|
-
# Each thread should get its own trace
|
323
|
-
results.append((thread_id, trace))
|
324
|
-
time.sleep(0.01) # Simulate some work
|
325
|
-
except Exception as e:
|
326
|
-
errors.append((thread_id, str(e)))
|
327
|
-
|
328
|
-
# Start multiple threads
|
329
|
-
threads = []
|
330
|
-
for i in range(5):
|
331
|
-
t = threading.Thread(target=worker, args=(i,))
|
332
|
-
threads.append(t)
|
333
|
-
t.start()
|
334
|
-
|
335
|
-
# Wait for all threads
|
336
|
-
for t in threads:
|
337
|
-
t.join()
|
338
|
-
|
339
|
-
# Check results
|
340
|
-
assert len(errors) == 0, f"Errors in threads: {errors}"
|
341
|
-
assert len(results) == 5
|
342
|
-
|
343
|
-
# Each thread should have gotten a unique trace
|
344
|
-
traces = [r[1] for r in results]
|
345
|
-
assert len(set(id(t) for t in traces)) == 5 # All unique
|
346
|
-
|
347
|
-
@patch("agentops.sdk.core.tracer")
|
348
|
-
@patch("agentops.tracer")
|
349
|
-
def test_context_manager_with_early_return(self, mock_agentops_tracer, mock_core_tracer):
|
350
|
-
"""Test that context manager properly cleans up with early return"""
|
351
|
-
# Create a mock TraceContext
|
352
|
-
mock_trace = TraceContext(span=Mock(), token=Mock())
|
353
|
-
mock_agentops_tracer.initialized = True
|
354
|
-
mock_agentops_tracer.start_trace.return_value = mock_trace
|
355
|
-
|
356
|
-
def function_with_early_return():
|
357
|
-
with start_trace("early_return_trace"):
|
358
|
-
if True: # Early return condition
|
359
|
-
return "early"
|
360
|
-
return "normal"
|
361
|
-
|
362
|
-
result = function_with_early_return()
|
363
|
-
assert result == "early"
|
364
|
-
|
365
|
-
# Verify trace was still ended properly
|
366
|
-
mock_core_tracer.end_trace.assert_called_once_with(mock_trace, StatusCode.OK)
|
367
|
-
|
368
|
-
@patch("agentops.sdk.core.tracer")
|
369
|
-
@patch("agentops.tracer")
|
370
|
-
def test_context_manager_with_finally_block(self, mock_agentops_tracer, mock_core_tracer):
|
371
|
-
"""Test context manager with try-finally block"""
|
372
|
-
# Create a mock TraceContext
|
373
|
-
mock_trace = TraceContext(span=Mock(), token=Mock())
|
374
|
-
mock_agentops_tracer.initialized = True
|
375
|
-
mock_agentops_tracer.start_trace.return_value = mock_trace
|
376
|
-
|
377
|
-
finally_executed = False
|
378
|
-
|
379
|
-
try:
|
380
|
-
with start_trace("finally_trace"):
|
381
|
-
try:
|
382
|
-
raise ValueError("Test")
|
383
|
-
finally:
|
384
|
-
finally_executed = True
|
385
|
-
except ValueError:
|
386
|
-
pass
|
387
|
-
|
388
|
-
assert finally_executed
|
389
|
-
# Trace should be ended with ERROR due to exception
|
390
|
-
mock_core_tracer.end_trace.assert_called_once_with(mock_trace, StatusCode.ERROR)
|
391
|
-
|
392
|
-
@patch("agentops.sdk.core.tracer")
|
393
|
-
@patch("agentops.tracer")
|
394
|
-
def test_backwards_compatibility_existing_patterns(self, mock_agentops_tracer, mock_core_tracer):
|
395
|
-
"""Test that existing usage patterns continue to work"""
|
396
|
-
# Create mock traces
|
397
|
-
mock_traces = [TraceContext(span=Mock(), token=Mock()) for _ in range(3)]
|
398
|
-
mock_agentops_tracer.initialized = True
|
399
|
-
mock_agentops_tracer.start_trace.side_effect = mock_traces
|
400
|
-
|
401
|
-
# Pattern 1: Basic context manager
|
402
|
-
with start_trace("basic") as trace:
|
403
|
-
assert trace is mock_traces[0]
|
404
|
-
|
405
|
-
# Pattern 2: Manual start/end using context manager protocol
|
406
|
-
trace = start_trace("manual")
|
407
|
-
assert trace is mock_traces[1]
|
408
|
-
trace.__exit__(None, None, None) # Use context manager exit instead of end_trace
|
409
|
-
|
410
|
-
# Pattern 3: With tags
|
411
|
-
with start_trace("tagged", tags=["production", "v2"]) as trace:
|
412
|
-
assert trace is mock_traces[2]
|
413
|
-
|
414
|
-
# All patterns should work
|
415
|
-
assert mock_agentops_tracer.start_trace.call_count == 3
|
416
|
-
assert mock_core_tracer.end_trace.call_count == 3
|
417
|
-
|
418
|
-
@patch("agentops.sdk.core.tracer")
|
419
|
-
@patch("agentops.tracer")
|
420
|
-
def test_edge_case_none_trace_context(self, mock_agentops_tracer, mock_core_tracer):
|
421
|
-
"""Test handling when start_trace returns None"""
|
422
|
-
# Mock SDK not initialized and init fails
|
423
|
-
mock_agentops_tracer.initialized = False
|
424
|
-
|
425
|
-
# When start_trace is called on uninitialized tracer, it returns None
|
426
|
-
with patch("agentops.init") as mock_init:
|
427
|
-
mock_init.side_effect = Exception("Init failed")
|
428
|
-
|
429
|
-
result = start_trace("test_trace")
|
430
|
-
assert result is None
|
431
|
-
|
432
|
-
# Verify start_trace was not called on tracer (since init failed)
|
433
|
-
mock_agentops_tracer.start_trace.assert_not_called()
|
434
|
-
|
435
|
-
@patch("agentops.sdk.core.tracer")
|
436
|
-
@patch("agentops.tracer")
|
437
|
-
def test_edge_case_tracing_core_not_initialized(self, mock_agentops_tracer, mock_core_tracer):
|
438
|
-
"""Test behavior when global tracer is not initialized"""
|
439
|
-
mock_agentops_tracer.initialized = False
|
440
|
-
|
441
|
-
# Mock init to succeed but tracer still not initialized
|
442
|
-
with patch("agentops.init") as mock_init:
|
443
|
-
mock_init.return_value = None # init succeeds but doesn't set initialized
|
444
|
-
|
445
|
-
result = start_trace("test")
|
446
|
-
assert result is None
|
447
|
-
|
448
|
-
@patch("agentops.sdk.core.tracer")
|
449
|
-
@patch("agentops.tracer")
|
450
|
-
def test_edge_case_exception_in_exit_method(self, mock_agentops_tracer, mock_core_tracer):
|
451
|
-
"""Test handling when exception occurs in __exit__ method"""
|
452
|
-
# Create a mock TraceContext
|
453
|
-
mock_trace = TraceContext(span=Mock(), token=Mock())
|
454
|
-
mock_agentops_tracer.initialized = True
|
455
|
-
mock_agentops_tracer.start_trace.return_value = mock_trace
|
456
|
-
|
457
|
-
# Make end_trace raise an exception
|
458
|
-
mock_core_tracer.end_trace.side_effect = RuntimeError("End trace failed")
|
459
|
-
|
460
|
-
# The exception in __exit__ should be suppressed
|
461
|
-
with start_trace("exception_in_exit"):
|
462
|
-
pass # Should not raise
|
463
|
-
|
464
|
-
# Verify end_trace was attempted
|
465
|
-
mock_core_tracer.end_trace.assert_called_once()
|
466
|
-
|
467
|
-
@patch("agentops.sdk.core.tracer")
|
468
|
-
@patch("agentops.tracer")
|
469
|
-
def test_performance_many_sequential_traces(self, mock_agentops_tracer, mock_core_tracer):
|
470
|
-
"""Test performance with many sequential traces"""
|
471
|
-
# Mock the tracer
|
472
|
-
mock_agentops_tracer.initialized = True
|
473
|
-
|
474
|
-
# Create traces on demand
|
475
|
-
def create_trace(trace_name=None, tags=None, **kwargs):
|
476
|
-
return TraceContext(span=Mock(), token=Mock())
|
477
|
-
|
478
|
-
mock_agentops_tracer.start_trace.side_effect = create_trace
|
479
|
-
|
480
|
-
# Create many traces sequentially
|
481
|
-
start_time = time.time()
|
482
|
-
for i in range(100):
|
483
|
-
with start_trace(f"trace_{i}") as trace:
|
484
|
-
assert trace is not None
|
485
|
-
assert trace.span is not None
|
486
|
-
|
487
|
-
elapsed = time.time() - start_time
|
488
|
-
|
489
|
-
# Should complete reasonably quickly (< 1 second for 100 traces)
|
490
|
-
assert elapsed < 1.0, f"Too slow: {elapsed:.2f}s for 100 traces"
|
491
|
-
|
492
|
-
# Verify all traces were started and ended
|
493
|
-
assert mock_agentops_tracer.start_trace.call_count == 100
|
494
|
-
assert mock_core_tracer.end_trace.call_count == 100
|
495
|
-
|
496
|
-
@patch("agentops.sdk.core.tracer")
|
497
|
-
def test_trace_context_state_management(self, mock_tracer):
|
498
|
-
"""Test that TraceContext properly manages its internal state"""
|
499
|
-
mock_span = Mock()
|
500
|
-
mock_token = Mock()
|
501
|
-
trace_context = TraceContext(span=mock_span, token=mock_token)
|
502
|
-
|
503
|
-
# Initial state
|
504
|
-
assert trace_context.span is mock_span
|
505
|
-
assert trace_context.token is mock_token
|
506
|
-
|
507
|
-
# Enter context
|
508
|
-
result = trace_context.__enter__()
|
509
|
-
assert result is trace_context
|
510
|
-
|
511
|
-
# Exit context normally
|
512
|
-
trace_context.__exit__(None, None, None)
|
513
|
-
mock_tracer.end_trace.assert_called_once_with(trace_context, StatusCode.OK)
|
514
|
-
|
515
|
-
# State should remain accessible after exit
|
516
|
-
assert trace_context.span is mock_span
|
517
|
-
assert trace_context.token is mock_token
|
518
|
-
|
519
|
-
@patch("agentops.sdk.core.tracer")
|
520
|
-
@patch("agentops.tracer")
|
521
|
-
def test_context_manager_with_async_context(self, mock_agentops_tracer, mock_core_tracer):
|
522
|
-
"""Test context manager works in async context"""
|
523
|
-
# Create a mock TraceContext
|
524
|
-
mock_trace = TraceContext(span=Mock(), token=Mock())
|
525
|
-
mock_agentops_tracer.initialized = True
|
526
|
-
mock_agentops_tracer.start_trace.return_value = mock_trace
|
527
|
-
|
528
|
-
async def async_function():
|
529
|
-
with start_trace("async_context") as trace:
|
530
|
-
assert trace is mock_trace
|
531
|
-
await asyncio.sleep(0.01)
|
532
|
-
return "done"
|
533
|
-
|
534
|
-
# Run async function
|
535
|
-
result = asyncio.run(async_function())
|
536
|
-
assert result == "done"
|
537
|
-
|
538
|
-
# Verify trace was properly managed
|
539
|
-
mock_agentops_tracer.start_trace.assert_called_once_with(trace_name="async_context", tags=None)
|
540
|
-
mock_core_tracer.end_trace.assert_called_once_with(mock_trace, StatusCode.OK)
|
541
|
-
|
542
|
-
|
543
|
-
class TestContextManagerBackwardCompatibility(unittest.TestCase):
|
544
|
-
"""Test backward compatibility for context manager usage"""
|
545
|
-
|
546
|
-
@patch("agentops.sdk.core.tracer")
|
547
|
-
@patch("agentops.tracer")
|
548
|
-
def test_existing_code_patterns_still_work(self, mock_agentops_tracer, mock_core_tracer):
|
549
|
-
"""Test that code using the old patterns still works"""
|
550
|
-
# Create mock traces - need more than 3 for this test
|
551
|
-
mock_traces = [TraceContext(span=Mock(), token=Mock()) for _ in range(5)]
|
552
|
-
mock_agentops_tracer.initialized = True
|
553
|
-
mock_agentops_tracer.start_trace.side_effect = mock_traces
|
554
|
-
|
555
|
-
# Old pattern 1: Simple context manager
|
556
|
-
with start_trace("basic") as trace:
|
557
|
-
# Should work without changes
|
558
|
-
assert trace.span is not None
|
559
|
-
|
560
|
-
# Old pattern 2: Context manager with exception handling
|
561
|
-
try:
|
562
|
-
with start_trace("with_error") as trace:
|
563
|
-
raise ValueError("test")
|
564
|
-
except ValueError:
|
565
|
-
pass
|
566
|
-
|
567
|
-
# Old pattern 3: Nested traces
|
568
|
-
with start_trace("outer") as outer:
|
569
|
-
with start_trace("inner") as inner:
|
570
|
-
assert outer is not inner
|
571
|
-
|
572
|
-
# All should work - 4 calls total (basic, with_error, outer, inner)
|
573
|
-
assert mock_agentops_tracer.start_trace.call_count == 4
|
574
|
-
assert mock_core_tracer.end_trace.call_count == 4
|
575
|
-
|
576
|
-
@patch("agentops.sdk.core.tracer")
|
577
|
-
@patch("agentops.tracer")
|
578
|
-
def test_api_compatibility(self, mock_agentops_tracer, mock_core_tracer):
|
579
|
-
"""Test that the API remains compatible"""
|
580
|
-
# Create mock TraceContexts for each call
|
581
|
-
mock_traces = [TraceContext(span=Mock(), token=Mock()) for _ in range(3)]
|
582
|
-
mock_agentops_tracer.initialized = True
|
583
|
-
mock_agentops_tracer.start_trace.side_effect = mock_traces
|
584
|
-
|
585
|
-
# Test function signatures
|
586
|
-
# start_trace(trace_name, tags=None)
|
587
|
-
trace1 = start_trace("test1")
|
588
|
-
assert trace1 is mock_traces[0]
|
589
|
-
|
590
|
-
trace2 = start_trace("test2", tags=["tag1", "tag2"])
|
591
|
-
assert trace2 is mock_traces[1]
|
592
|
-
|
593
|
-
trace3 = start_trace("test3", tags={"key": "value"})
|
594
|
-
assert trace3 is mock_traces[2]
|
595
|
-
|
596
|
-
# Use context manager protocol to end traces
|
597
|
-
trace1.__exit__(None, None, None)
|
598
|
-
trace2.__exit__(ValueError, ValueError("test"), None)
|
599
|
-
trace3.__exit__(None, None, None)
|
600
|
-
|
601
|
-
# All calls should work
|
602
|
-
assert mock_agentops_tracer.start_trace.call_count == 3
|
603
|
-
assert mock_core_tracer.end_trace.call_count == 3
|
604
|
-
|
605
|
-
@patch("agentops.sdk.core.tracer")
|
606
|
-
@patch("agentops.tracer")
|
607
|
-
def test_return_type_compatibility(self, mock_agentops_tracer, mock_core_tracer):
|
608
|
-
"""Test that return types are compatible with existing code"""
|
609
|
-
mock_span = Mock()
|
610
|
-
mock_token = Mock()
|
611
|
-
mock_trace = TraceContext(span=mock_span, token=mock_token)
|
612
|
-
mock_agentops_tracer.initialized = True
|
613
|
-
mock_agentops_tracer.start_trace.return_value = mock_trace
|
614
|
-
|
615
|
-
# start_trace returns TraceContext (or None)
|
616
|
-
trace = start_trace("test")
|
617
|
-
assert isinstance(trace, TraceContext)
|
618
|
-
assert hasattr(trace, "span")
|
619
|
-
assert hasattr(trace, "token")
|
620
|
-
assert hasattr(trace, "__enter__")
|
621
|
-
assert hasattr(trace, "__exit__")
|
622
|
-
|
623
|
-
# Can be used as context manager
|
624
|
-
with trace:
|
625
|
-
pass
|
626
|
-
|
627
|
-
@patch("agentops.sdk.core.tracer")
|
628
|
-
@patch("agentops.tracer")
|
629
|
-
def test_context_manager_with_keyboard_interrupt(self, mock_agentops_tracer, mock_core_tracer):
|
630
|
-
"""Test context manager handles KeyboardInterrupt properly"""
|
631
|
-
# Create a mock TraceContext
|
632
|
-
mock_trace = TraceContext(span=Mock(), token=Mock())
|
633
|
-
mock_agentops_tracer.initialized = True
|
634
|
-
mock_agentops_tracer.start_trace.return_value = mock_trace
|
635
|
-
|
636
|
-
# Test KeyboardInterrupt handling
|
637
|
-
with self.assertRaises(KeyboardInterrupt):
|
638
|
-
with start_trace("keyboard_interrupt_trace"):
|
639
|
-
raise KeyboardInterrupt()
|
640
|
-
|
641
|
-
# Verify end_trace was called with ERROR state
|
642
|
-
mock_core_tracer.end_trace.assert_called_once_with(mock_trace, StatusCode.ERROR)
|
643
|
-
|
644
|
-
@patch("agentops.sdk.core.tracer")
|
645
|
-
@patch("agentops.tracer")
|
646
|
-
def test_context_manager_with_system_exit(self, mock_agentops_tracer, mock_core_tracer):
|
647
|
-
"""Test context manager handles SystemExit properly"""
|
648
|
-
# Create a mock TraceContext
|
649
|
-
mock_trace = TraceContext(span=Mock(), token=Mock())
|
650
|
-
mock_agentops_tracer.initialized = True
|
651
|
-
mock_agentops_tracer.start_trace.return_value = mock_trace
|
652
|
-
|
653
|
-
# Test SystemExit handling
|
654
|
-
with self.assertRaises(SystemExit):
|
655
|
-
with start_trace("system_exit_trace"):
|
656
|
-
raise SystemExit(1)
|
657
|
-
|
658
|
-
# Verify end_trace was called with ERROR state
|
659
|
-
mock_core_tracer.end_trace.assert_called_once_with(mock_trace, StatusCode.ERROR)
|
660
|
-
|
661
|
-
@patch("agentops.sdk.core.tracer")
|
662
|
-
@patch("agentops.tracer")
|
663
|
-
def test_context_manager_in_generator_function(self, mock_agentops_tracer, mock_core_tracer):
|
664
|
-
"""Test context manager works correctly in generator functions"""
|
665
|
-
# Create mock traces
|
666
|
-
mock_traces = [TraceContext(span=Mock(), token=Mock()) for _ in range(3)]
|
667
|
-
mock_agentops_tracer.initialized = True
|
668
|
-
mock_agentops_tracer.start_trace.side_effect = mock_traces
|
669
|
-
|
670
|
-
def trace_generator():
|
671
|
-
with start_trace("generator_trace"):
|
672
|
-
yield 1
|
673
|
-
yield 2
|
674
|
-
yield 3
|
675
|
-
|
676
|
-
# Consume the generator
|
677
|
-
results = list(trace_generator())
|
678
|
-
assert results == [1, 2, 3]
|
679
|
-
|
680
|
-
# Verify trace was properly managed
|
681
|
-
mock_agentops_tracer.start_trace.assert_called_once()
|
682
|
-
mock_core_tracer.end_trace.assert_called_once()
|
683
|
-
|
684
|
-
@patch("agentops.sdk.core.tracer")
|
685
|
-
def test_context_manager_exit_return_value(self, mock_tracer):
|
686
|
-
"""Test that __exit__ returns None (doesn't suppress exceptions)"""
|
687
|
-
mock_span = Mock()
|
688
|
-
mock_token = Mock()
|
689
|
-
trace_context = TraceContext(span=mock_span, token=mock_token)
|
690
|
-
|
691
|
-
# __exit__ should return None (or falsy) to not suppress exceptions
|
692
|
-
result = trace_context.__exit__(None, None, None)
|
693
|
-
assert result is None or not result
|
694
|
-
|
695
|
-
# Also with exception
|
696
|
-
result = trace_context.__exit__(ValueError, ValueError("test"), None)
|
697
|
-
assert result is None or not result
|
698
|
-
|
699
|
-
@patch("agentops.sdk.core.tracer")
|
700
|
-
@patch("agentops.tracer")
|
701
|
-
def test_context_manager_with_very_large_data(self, mock_agentops_tracer, mock_core_tracer):
|
702
|
-
"""Test context manager with very large trace names and tags"""
|
703
|
-
# Create a mock TraceContext
|
704
|
-
mock_trace = TraceContext(span=Mock(), token=Mock())
|
705
|
-
mock_agentops_tracer.initialized = True
|
706
|
-
mock_agentops_tracer.start_trace.return_value = mock_trace
|
707
|
-
|
708
|
-
# Very large trace name and tags
|
709
|
-
large_trace_name = "x" * 10000
|
710
|
-
large_tags = {f"key_{i}": f"value_{i}" * 100 for i in range(100)}
|
711
|
-
|
712
|
-
with start_trace(large_trace_name, tags=large_tags) as trace:
|
713
|
-
assert trace is mock_trace
|
714
|
-
|
715
|
-
# Should handle large data without issues
|
716
|
-
mock_agentops_tracer.start_trace.assert_called_once()
|
717
|
-
args, kwargs = mock_agentops_tracer.start_trace.call_args
|
718
|
-
assert kwargs["trace_name"] == large_trace_name
|
719
|
-
assert kwargs["tags"] == large_tags
|
720
|
-
|
721
|
-
@patch("agentops.sdk.core.tracer")
|
722
|
-
@patch("agentops.tracer")
|
723
|
-
def test_context_manager_with_asyncio_tasks(self, mock_agentops_tracer, mock_core_tracer):
|
724
|
-
"""Test context manager with multiple asyncio tasks"""
|
725
|
-
# Mock the tracer
|
726
|
-
mock_agentops_tracer.initialized = True
|
727
|
-
|
728
|
-
# Create traces for each task
|
729
|
-
trace_count = 0
|
730
|
-
|
731
|
-
def create_trace(trace_name=None, tags=None, **kwargs):
|
732
|
-
nonlocal trace_count
|
733
|
-
trace_count += 1
|
734
|
-
return TraceContext(span=Mock(name=f"span_{trace_count}"), token=Mock())
|
735
|
-
|
736
|
-
mock_agentops_tracer.start_trace.side_effect = create_trace
|
737
|
-
|
738
|
-
async def task_with_trace(task_id):
|
739
|
-
with start_trace(f"async_task_{task_id}"):
|
740
|
-
await asyncio.sleep(0.01)
|
741
|
-
return task_id
|
742
|
-
|
743
|
-
async def run_concurrent_tasks():
|
744
|
-
tasks = [task_with_trace(i) for i in range(5)]
|
745
|
-
results = await asyncio.gather(*tasks)
|
746
|
-
return results
|
747
|
-
|
748
|
-
# Run async tasks
|
749
|
-
results = asyncio.run(run_concurrent_tasks())
|
750
|
-
assert results == [0, 1, 2, 3, 4]
|
751
|
-
|
752
|
-
# All traces should be started and ended
|
753
|
-
assert mock_agentops_tracer.start_trace.call_count == 5
|
754
|
-
assert mock_core_tracer.end_trace.call_count == 5
|
755
|
-
|
756
|
-
@patch("agentops.sdk.core.tracer")
|
757
|
-
@patch("agentops.tracer")
|
758
|
-
def test_context_manager_resource_cleanup_on_exit_failure(self, mock_agentops_tracer, mock_core_tracer):
|
759
|
-
"""Test that resources are cleaned up even if __exit__ fails"""
|
760
|
-
# Create a mock TraceContext
|
761
|
-
mock_trace = TraceContext(span=Mock(), token=Mock())
|
762
|
-
mock_agentops_tracer.initialized = True
|
763
|
-
mock_agentops_tracer.start_trace.return_value = mock_trace
|
764
|
-
|
765
|
-
# Make end_trace fail
|
766
|
-
mock_core_tracer.end_trace.side_effect = Exception("Cleanup failed")
|
767
|
-
|
768
|
-
# Should not raise exception from __exit__
|
769
|
-
with start_trace("cleanup_test") as trace:
|
770
|
-
assert trace is mock_trace
|
771
|
-
|
772
|
-
# end_trace was attempted despite failure
|
773
|
-
mock_core_tracer.end_trace.assert_called_once()
|
774
|
-
|
775
|
-
|
776
|
-
if __name__ == "__main__":
|
777
|
-
unittest.main()
|