mseep-agentops 0.4.18__py3-none-any.whl → 0.4.22__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.22.dist-info}/METADATA +30 -40
- mseep_agentops-0.4.22.dist-info/RECORD +178 -0
- {mseep_agentops-0.4.18.dist-info → mseep_agentops-0.4.22.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.22.dist-info}/licenses/LICENSE +0 -0
@@ -1,397 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Unit tests for URL logging functionality and InternalSpanProcessor.
|
3
|
-
"""
|
4
|
-
|
5
|
-
import unittest
|
6
|
-
from unittest.mock import patch, MagicMock
|
7
|
-
|
8
|
-
from opentelemetry.sdk.trace import Span, ReadableSpan
|
9
|
-
|
10
|
-
from agentops.sdk.processors import InternalSpanProcessor
|
11
|
-
from agentops.sdk.core import TraceContext, tracer
|
12
|
-
|
13
|
-
|
14
|
-
class TestURLLogging(unittest.TestCase):
|
15
|
-
"""Tests for URL logging functionality in global tracer."""
|
16
|
-
|
17
|
-
def setUp(self):
|
18
|
-
self.tracing_core = tracer
|
19
|
-
# Mock the initialization to avoid actual setup
|
20
|
-
self.tracing_core._initialized = True
|
21
|
-
self.tracing_core._config = {"project_id": "test_project"}
|
22
|
-
|
23
|
-
@patch("agentops.sdk.core.log_trace_url")
|
24
|
-
@patch("agentops.sdk.core.tracer.make_span")
|
25
|
-
def test_start_trace_logs_url(self, mock_make_span, mock_log_trace_url):
|
26
|
-
"""Test that start_trace logs the trace URL."""
|
27
|
-
# Create a mock span
|
28
|
-
mock_span = MagicMock(spec=Span)
|
29
|
-
mock_context = MagicMock()
|
30
|
-
mock_token = MagicMock()
|
31
|
-
mock_span.get_span_context.return_value.span_id = 12345
|
32
|
-
mock_make_span.return_value = (mock_span, mock_context, mock_token)
|
33
|
-
|
34
|
-
# Call start_trace
|
35
|
-
trace_context = self.tracing_core.start_trace(trace_name="test_trace")
|
36
|
-
|
37
|
-
# Assert that log_trace_url was called with the span and title
|
38
|
-
mock_log_trace_url.assert_called_once_with(mock_span, title="test_trace")
|
39
|
-
self.assertIsInstance(trace_context, TraceContext)
|
40
|
-
self.assertEqual(trace_context.span, mock_span)
|
41
|
-
|
42
|
-
@patch("agentops.sdk.core.log_trace_url")
|
43
|
-
@patch("agentops.sdk.core.tracer.finalize_span")
|
44
|
-
def test_end_trace_logs_url(self, mock_finalize_span, mock_log_trace_url):
|
45
|
-
"""Test that end_trace logs the trace URL."""
|
46
|
-
# Create a mock trace context
|
47
|
-
mock_span = MagicMock(spec=Span)
|
48
|
-
mock_span.name = "test_trace"
|
49
|
-
mock_span.get_span_context.return_value.span_id = 12345
|
50
|
-
mock_token = MagicMock()
|
51
|
-
trace_context = TraceContext(mock_span, mock_token)
|
52
|
-
|
53
|
-
# Call end_trace
|
54
|
-
self.tracing_core.end_trace(trace_context, "Success")
|
55
|
-
|
56
|
-
# Assert that log_trace_url was called with the span and title
|
57
|
-
mock_log_trace_url.assert_called_once_with(mock_span, title="test_trace")
|
58
|
-
|
59
|
-
@patch("agentops.sdk.core.log_trace_url")
|
60
|
-
@patch("agentops.sdk.core.tracer.make_span")
|
61
|
-
def test_start_trace_url_logging_failure_does_not_break_trace(self, mock_make_span, mock_log_trace_url):
|
62
|
-
"""Test that URL logging failure doesn't break trace creation."""
|
63
|
-
# Create a mock span
|
64
|
-
mock_span = MagicMock(spec=Span)
|
65
|
-
mock_context = MagicMock()
|
66
|
-
mock_token = MagicMock()
|
67
|
-
mock_span.get_span_context.return_value.span_id = 12345
|
68
|
-
mock_make_span.return_value = (mock_span, mock_context, mock_token)
|
69
|
-
|
70
|
-
# Make log_trace_url raise an exception
|
71
|
-
mock_log_trace_url.side_effect = Exception("URL logging failed")
|
72
|
-
|
73
|
-
# Call start_trace - should not raise exception
|
74
|
-
trace_context = self.tracing_core.start_trace(trace_name="test_trace")
|
75
|
-
|
76
|
-
# Assert that trace was still created successfully
|
77
|
-
self.assertIsInstance(trace_context, TraceContext)
|
78
|
-
self.assertEqual(trace_context.span, mock_span)
|
79
|
-
mock_log_trace_url.assert_called_once_with(mock_span, title="test_trace")
|
80
|
-
|
81
|
-
@patch("agentops.sdk.core.log_trace_url")
|
82
|
-
@patch("agentops.sdk.core.tracer.finalize_span")
|
83
|
-
def test_end_trace_url_logging_failure_does_not_break_trace(self, mock_finalize_span, mock_log_trace_url):
|
84
|
-
"""Test that URL logging failure doesn't break trace ending."""
|
85
|
-
# Create a mock trace context
|
86
|
-
mock_span = MagicMock(spec=Span)
|
87
|
-
mock_span.name = "test_trace"
|
88
|
-
mock_span.get_span_context.return_value.span_id = 12345
|
89
|
-
mock_token = MagicMock()
|
90
|
-
trace_context = TraceContext(mock_span, mock_token)
|
91
|
-
|
92
|
-
# Make log_trace_url raise an exception
|
93
|
-
mock_log_trace_url.side_effect = Exception("URL logging failed")
|
94
|
-
|
95
|
-
# Call end_trace - should not raise exception
|
96
|
-
self.tracing_core.end_trace(trace_context, "Success")
|
97
|
-
|
98
|
-
# Assert that finalize_span was still called
|
99
|
-
mock_finalize_span.assert_called_once()
|
100
|
-
mock_log_trace_url.assert_called_once_with(mock_span, title="test_trace")
|
101
|
-
|
102
|
-
@patch("agentops.sdk.core.log_trace_url")
|
103
|
-
@patch("agentops.sdk.core.tracer.make_span")
|
104
|
-
def test_start_trace_with_tags_logs_url(self, mock_make_span, mock_log_trace_url):
|
105
|
-
"""Test that start_trace with tags logs the trace URL."""
|
106
|
-
# Create a mock span
|
107
|
-
mock_span = MagicMock(spec=Span)
|
108
|
-
mock_context = MagicMock()
|
109
|
-
mock_token = MagicMock()
|
110
|
-
mock_span.get_span_context.return_value.span_id = 12345
|
111
|
-
mock_make_span.return_value = (mock_span, mock_context, mock_token)
|
112
|
-
|
113
|
-
# Call start_trace with tags
|
114
|
-
trace_context = self.tracing_core.start_trace(trace_name="tagged_trace", tags=["test", "integration"])
|
115
|
-
|
116
|
-
# Assert that log_trace_url was called with the span and title
|
117
|
-
mock_log_trace_url.assert_called_once_with(mock_span, title="tagged_trace")
|
118
|
-
self.assertIsInstance(trace_context, TraceContext)
|
119
|
-
|
120
|
-
|
121
|
-
class TestSessionDecoratorURLLogging(unittest.TestCase):
|
122
|
-
"""Tests for URL logging functionality in session decorators."""
|
123
|
-
|
124
|
-
def setUp(self):
|
125
|
-
self.tracing_core = tracer
|
126
|
-
# Mock the initialization to avoid actual setup
|
127
|
-
self.tracing_core._initialized = True
|
128
|
-
self.tracing_core._config = {"project_id": "test_project"}
|
129
|
-
|
130
|
-
@patch("agentops.sdk.core.log_trace_url")
|
131
|
-
@patch("agentops.sdk.core.tracer.make_span")
|
132
|
-
@patch("agentops.sdk.core.tracer.finalize_span")
|
133
|
-
def test_session_decorator_logs_url_on_start_and_end(self, mock_finalize_span, mock_make_span, mock_log_trace_url):
|
134
|
-
"""Test that session decorator logs URLs on both start and end."""
|
135
|
-
from agentops.sdk.decorators import session
|
136
|
-
|
137
|
-
# Create a mock span
|
138
|
-
mock_span = MagicMock(spec=Span)
|
139
|
-
mock_span.name = "test_function"
|
140
|
-
mock_context = MagicMock()
|
141
|
-
mock_token = MagicMock()
|
142
|
-
mock_span.get_span_context.return_value.span_id = 12345
|
143
|
-
mock_make_span.return_value = (mock_span, mock_context, mock_token)
|
144
|
-
|
145
|
-
@session(name="test_session")
|
146
|
-
def test_function():
|
147
|
-
return "test_result"
|
148
|
-
|
149
|
-
# Call the decorated function
|
150
|
-
result = test_function()
|
151
|
-
|
152
|
-
# Assert that log_trace_url was called (start and end)
|
153
|
-
# Note: The actual number of calls may vary based on implementation details
|
154
|
-
self.assertGreaterEqual(mock_log_trace_url.call_count, 2)
|
155
|
-
# Verify that the calls include the expected session name
|
156
|
-
call_args_list = [
|
157
|
-
call_args[1]["title"] for call_args in mock_log_trace_url.call_args_list if "title" in call_args[1]
|
158
|
-
]
|
159
|
-
self.assertIn("test_session", call_args_list)
|
160
|
-
self.assertEqual(result, "test_result")
|
161
|
-
|
162
|
-
@patch("agentops.sdk.core.log_trace_url")
|
163
|
-
@patch("agentops.sdk.core.tracer.make_span")
|
164
|
-
@patch("agentops.sdk.core.tracer.finalize_span")
|
165
|
-
def test_session_decorator_with_default_name_logs_url(self, mock_finalize_span, mock_make_span, mock_log_trace_url):
|
166
|
-
"""Test that session decorator with default name logs URLs."""
|
167
|
-
from agentops.sdk.decorators import session
|
168
|
-
|
169
|
-
# Create a mock span
|
170
|
-
mock_span = MagicMock(spec=Span)
|
171
|
-
mock_span.name = "my_function"
|
172
|
-
mock_context = MagicMock()
|
173
|
-
mock_token = MagicMock()
|
174
|
-
mock_span.get_span_context.return_value.span_id = 12345
|
175
|
-
mock_make_span.return_value = (mock_span, mock_context, mock_token)
|
176
|
-
|
177
|
-
@session
|
178
|
-
def my_function():
|
179
|
-
return "result"
|
180
|
-
|
181
|
-
# Call the decorated function
|
182
|
-
result = my_function()
|
183
|
-
|
184
|
-
# Assert that log_trace_url was called with function name as title
|
185
|
-
self.assertGreaterEqual(mock_log_trace_url.call_count, 2)
|
186
|
-
# Verify that the calls include the expected function name
|
187
|
-
call_args_list = [
|
188
|
-
call_args[1]["title"] for call_args in mock_log_trace_url.call_args_list if "title" in call_args[1]
|
189
|
-
]
|
190
|
-
self.assertIn("my_function", call_args_list)
|
191
|
-
self.assertEqual(result, "result")
|
192
|
-
|
193
|
-
@patch("agentops.sdk.core.log_trace_url")
|
194
|
-
@patch("agentops.sdk.core.tracer.make_span")
|
195
|
-
@patch("agentops.sdk.core.tracer.finalize_span")
|
196
|
-
def test_session_decorator_handles_url_logging_failure(
|
197
|
-
self, mock_finalize_span, mock_make_span, mock_log_trace_url
|
198
|
-
):
|
199
|
-
"""Test that session decorator handles URL logging failures gracefully."""
|
200
|
-
from agentops.sdk.decorators import session
|
201
|
-
|
202
|
-
# Create a mock span
|
203
|
-
mock_span = MagicMock(spec=Span)
|
204
|
-
mock_span.name = "test_function"
|
205
|
-
mock_context = MagicMock()
|
206
|
-
mock_token = MagicMock()
|
207
|
-
mock_span.get_span_context.return_value.span_id = 12345
|
208
|
-
mock_make_span.return_value = (mock_span, mock_context, mock_token)
|
209
|
-
|
210
|
-
# Make log_trace_url raise an exception
|
211
|
-
mock_log_trace_url.side_effect = Exception("URL logging failed")
|
212
|
-
|
213
|
-
@session(name="failing_session")
|
214
|
-
def test_function():
|
215
|
-
return "test_result"
|
216
|
-
|
217
|
-
# Call the decorated function - should not raise exception
|
218
|
-
result = test_function()
|
219
|
-
|
220
|
-
# Assert that function still executed successfully
|
221
|
-
self.assertEqual(result, "test_result")
|
222
|
-
# Assert that log_trace_url was called (even though it failed)
|
223
|
-
self.assertGreaterEqual(mock_log_trace_url.call_count, 2)
|
224
|
-
|
225
|
-
|
226
|
-
class TestInternalSpanProcessor(unittest.TestCase):
|
227
|
-
"""Tests for InternalSpanProcessor functionality."""
|
228
|
-
|
229
|
-
def setUp(self):
|
230
|
-
self.processor = InternalSpanProcessor()
|
231
|
-
# Reset the root span ID before each test
|
232
|
-
self.processor._root_span_id = None
|
233
|
-
|
234
|
-
def test_tracks_root_span_on_start(self):
|
235
|
-
"""Test that the processor tracks the first span as root span."""
|
236
|
-
# Create a mock span
|
237
|
-
mock_span = MagicMock(spec=Span)
|
238
|
-
mock_context = MagicMock()
|
239
|
-
mock_context.trace_flags.sampled = True
|
240
|
-
mock_context.span_id = 12345
|
241
|
-
mock_span.context = mock_context
|
242
|
-
|
243
|
-
# Call on_start
|
244
|
-
self.processor.on_start(mock_span)
|
245
|
-
|
246
|
-
# Assert that root span ID was set
|
247
|
-
self.assertEqual(self.processor._root_span_id, 12345)
|
248
|
-
|
249
|
-
def test_ignores_unsampled_spans_on_start(self):
|
250
|
-
"""Test that unsampled spans are ignored on start."""
|
251
|
-
# Create a mock unsampled span
|
252
|
-
mock_span = MagicMock(spec=Span)
|
253
|
-
mock_context = MagicMock()
|
254
|
-
mock_context.trace_flags.sampled = False
|
255
|
-
mock_span.context = mock_context
|
256
|
-
|
257
|
-
# Call on_start
|
258
|
-
self.processor.on_start(mock_span)
|
259
|
-
|
260
|
-
# Assert that root span ID was not set
|
261
|
-
self.assertIsNone(self.processor._root_span_id)
|
262
|
-
|
263
|
-
def test_only_tracks_first_span_as_root(self):
|
264
|
-
"""Test that only the first span is tracked as root span."""
|
265
|
-
# First span
|
266
|
-
mock_span1 = MagicMock(spec=Span)
|
267
|
-
mock_context1 = MagicMock()
|
268
|
-
mock_context1.trace_flags.sampled = True
|
269
|
-
mock_context1.span_id = 12345
|
270
|
-
mock_span1.context = mock_context1
|
271
|
-
|
272
|
-
# Second span
|
273
|
-
mock_span2 = MagicMock(spec=Span)
|
274
|
-
mock_context2 = MagicMock()
|
275
|
-
mock_context2.trace_flags.sampled = True
|
276
|
-
mock_context2.span_id = 67890
|
277
|
-
mock_span2.context = mock_context2
|
278
|
-
|
279
|
-
# Start first span
|
280
|
-
self.processor.on_start(mock_span1)
|
281
|
-
self.assertEqual(self.processor._root_span_id, 12345)
|
282
|
-
|
283
|
-
# Start second span - should not change root span ID
|
284
|
-
self.processor.on_start(mock_span2)
|
285
|
-
self.assertEqual(self.processor._root_span_id, 12345)
|
286
|
-
|
287
|
-
@patch("agentops.sdk.processors.upload_logfile")
|
288
|
-
def test_uploads_logfile_on_root_span_end(self, mock_upload_logfile):
|
289
|
-
"""Test that logfile is uploaded when root span ends."""
|
290
|
-
# Set up root span
|
291
|
-
mock_span = MagicMock(spec=Span)
|
292
|
-
mock_context = MagicMock()
|
293
|
-
mock_context.trace_flags.sampled = True
|
294
|
-
mock_context.span_id = 12345
|
295
|
-
mock_context.trace_id = 98765
|
296
|
-
mock_span.context = mock_context
|
297
|
-
|
298
|
-
# Start the span to set it as root
|
299
|
-
self.processor.on_start(mock_span)
|
300
|
-
|
301
|
-
# Create readable span for end event
|
302
|
-
mock_readable_span = MagicMock(spec=ReadableSpan)
|
303
|
-
mock_readable_span.context = mock_context
|
304
|
-
|
305
|
-
# End the span
|
306
|
-
self.processor.on_end(mock_readable_span)
|
307
|
-
|
308
|
-
# Assert that upload_logfile was called with trace_id
|
309
|
-
mock_upload_logfile.assert_called_once_with(98765)
|
310
|
-
|
311
|
-
@patch("agentops.sdk.processors.upload_logfile")
|
312
|
-
def test_does_not_upload_logfile_for_non_root_span(self, mock_upload_logfile):
|
313
|
-
"""Test that logfile is not uploaded for non-root spans."""
|
314
|
-
# Set up root span
|
315
|
-
root_span = MagicMock(spec=Span)
|
316
|
-
root_context = MagicMock()
|
317
|
-
root_context.trace_flags.sampled = True
|
318
|
-
root_context.span_id = 12345
|
319
|
-
root_span.context = root_context
|
320
|
-
|
321
|
-
# Start root span
|
322
|
-
self.processor.on_start(root_span)
|
323
|
-
|
324
|
-
# Create non-root span
|
325
|
-
non_root_span = MagicMock(spec=ReadableSpan)
|
326
|
-
non_root_context = MagicMock()
|
327
|
-
non_root_context.trace_flags.sampled = True
|
328
|
-
non_root_context.span_id = 67890 # Different from root
|
329
|
-
non_root_span.context = non_root_context
|
330
|
-
|
331
|
-
# End non-root span
|
332
|
-
self.processor.on_end(non_root_span)
|
333
|
-
|
334
|
-
# Assert that upload_logfile was not called
|
335
|
-
mock_upload_logfile.assert_not_called()
|
336
|
-
|
337
|
-
@patch("agentops.sdk.processors.upload_logfile")
|
338
|
-
def test_handles_upload_logfile_error(self, mock_upload_logfile):
|
339
|
-
"""Test that processor handles upload_logfile errors gracefully."""
|
340
|
-
# Set up root span
|
341
|
-
mock_span = MagicMock(spec=Span)
|
342
|
-
mock_context = MagicMock()
|
343
|
-
mock_context.trace_flags.sampled = True
|
344
|
-
mock_context.span_id = 12345
|
345
|
-
mock_context.trace_id = 98765
|
346
|
-
mock_span.context = mock_context
|
347
|
-
|
348
|
-
# Start the span to set it as root
|
349
|
-
self.processor.on_start(mock_span)
|
350
|
-
|
351
|
-
# Make upload_logfile raise an exception
|
352
|
-
mock_upload_logfile.side_effect = Exception("Upload failed")
|
353
|
-
|
354
|
-
# Create readable span for end event
|
355
|
-
mock_readable_span = MagicMock(spec=ReadableSpan)
|
356
|
-
mock_readable_span.context = mock_context
|
357
|
-
|
358
|
-
# End the span - should not raise exception
|
359
|
-
self.processor.on_end(mock_readable_span)
|
360
|
-
|
361
|
-
# Assert that upload_logfile was called
|
362
|
-
mock_upload_logfile.assert_called_once_with(98765)
|
363
|
-
|
364
|
-
def test_ignores_unsampled_spans_on_end(self):
|
365
|
-
"""Test that unsampled spans are ignored on end."""
|
366
|
-
# Create a mock unsampled span
|
367
|
-
mock_span = MagicMock(spec=ReadableSpan)
|
368
|
-
mock_context = MagicMock()
|
369
|
-
mock_context.trace_flags.sampled = False
|
370
|
-
mock_span.context = mock_context
|
371
|
-
|
372
|
-
# Call on_end - should not raise exception
|
373
|
-
self.processor.on_end(mock_span)
|
374
|
-
|
375
|
-
def test_shutdown_resets_root_span_id(self):
|
376
|
-
"""Test that shutdown resets the root span ID."""
|
377
|
-
# Set up root span
|
378
|
-
mock_span = MagicMock(spec=Span)
|
379
|
-
mock_context = MagicMock()
|
380
|
-
mock_context.trace_flags.sampled = True
|
381
|
-
mock_context.span_id = 12345
|
382
|
-
mock_span.context = mock_context
|
383
|
-
|
384
|
-
# Start span to set root span ID
|
385
|
-
self.processor.on_start(mock_span)
|
386
|
-
self.assertEqual(self.processor._root_span_id, 12345)
|
387
|
-
|
388
|
-
# Call shutdown
|
389
|
-
self.processor.shutdown()
|
390
|
-
|
391
|
-
# Verify root span ID was reset
|
392
|
-
self.assertIsNone(self.processor._root_span_id)
|
393
|
-
|
394
|
-
def test_force_flush_returns_true(self):
|
395
|
-
"""Test that force_flush returns True."""
|
396
|
-
result = self.processor.force_flush()
|
397
|
-
self.assertTrue(result)
|
@@ -1,35 +0,0 @@
|
|
1
|
-
from unittest.mock import patch
|
2
|
-
|
3
|
-
|
4
|
-
from agentops.sdk.core import tracer
|
5
|
-
from agentops.sdk.attributes import get_global_resource_attributes
|
6
|
-
from agentops.semconv.resource import ResourceAttributes
|
7
|
-
|
8
|
-
|
9
|
-
@patch("agentops.sdk.attributes.get_imported_libraries", return_value=["agentops"])
|
10
|
-
def test_global_resource_attributes_no_system(mock_libs):
|
11
|
-
attrs = get_global_resource_attributes("svc", project_id="proj")
|
12
|
-
assert attrs[ResourceAttributes.SERVICE_NAME] == "svc"
|
13
|
-
assert attrs[ResourceAttributes.PROJECT_ID] == "proj"
|
14
|
-
assert ResourceAttributes.IMPORTED_LIBRARIES in attrs
|
15
|
-
assert ResourceAttributes.HOST_MACHINE not in attrs
|
16
|
-
assert ResourceAttributes.CPU_COUNT not in attrs
|
17
|
-
|
18
|
-
|
19
|
-
@patch("agentops.sdk.core.get_system_resource_attributes")
|
20
|
-
def test_system_metadata_only_for_session(mock_sys_attrs, instrumentation):
|
21
|
-
mock_sys_attrs.return_value = {ResourceAttributes.HOST_MACHINE: "test"}
|
22
|
-
|
23
|
-
ctx = tracer.start_trace("session")
|
24
|
-
tracer.end_trace(ctx, end_state="Success")
|
25
|
-
spans = instrumentation.get_finished_spans()
|
26
|
-
assert len(spans) == 1
|
27
|
-
assert spans[0].attributes.get(ResourceAttributes.HOST_MACHINE) == "test"
|
28
|
-
|
29
|
-
instrumentation.clear_spans()
|
30
|
-
|
31
|
-
ctx = tracer.start_trace("custom")
|
32
|
-
tracer.end_trace(ctx, end_state="Success")
|
33
|
-
spans = instrumentation.get_finished_spans()
|
34
|
-
assert len(spans) == 1
|
35
|
-
assert ResourceAttributes.HOST_MACHINE not in spans[0].attributes
|
tests/unit/test_config.py
DELETED
@@ -1,82 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
from unittest import mock
|
3
|
-
from uuid import UUID
|
4
|
-
|
5
|
-
import pytest
|
6
|
-
|
7
|
-
from agentops.config import Config
|
8
|
-
|
9
|
-
|
10
|
-
@pytest.fixture(autouse=True)
|
11
|
-
def mock_env():
|
12
|
-
"""Fixture to mock environment variables"""
|
13
|
-
with mock.patch.dict(os.environ, clear=True):
|
14
|
-
# Set up test environment variables
|
15
|
-
env_vars = {
|
16
|
-
"AGENTOPS_API_KEY": "test-api-key",
|
17
|
-
"AGENTOPS_API_ENDPOINT": "https://test.agentops.ai",
|
18
|
-
"AGENTOPS_APP_URL": "https://test-app.agentops.ai",
|
19
|
-
"AGENTOPS_MAX_WAIT_TIME": "1000",
|
20
|
-
"AGENTOPS_MAX_QUEUE_SIZE": "256",
|
21
|
-
"AGENTOPS_DEFAULT_TAGS": "tag1,tag2,tag3",
|
22
|
-
"AGENTOPS_INSTRUMENT_LLM_CALLS": "false",
|
23
|
-
"AGENTOPS_AUTO_START_SESSION": "false",
|
24
|
-
"AGENTOPS_SKIP_AUTO_END_SESSION": "true",
|
25
|
-
"AGENTOPS_ENV_DATA_OPT_OUT": "true",
|
26
|
-
}
|
27
|
-
for key, value in env_vars.items():
|
28
|
-
os.environ[key] = value
|
29
|
-
yield
|
30
|
-
|
31
|
-
|
32
|
-
@pytest.fixture
|
33
|
-
def valid_uuid():
|
34
|
-
"""Return a valid UUID string for testing"""
|
35
|
-
return str(UUID("12345678-1234-5678-1234-567812345678"))
|
36
|
-
|
37
|
-
|
38
|
-
def test_config_from_env(mock_env):
|
39
|
-
"""Test configuration initialization from environment variables"""
|
40
|
-
config = Config()
|
41
|
-
|
42
|
-
assert config.api_key == "test-api-key"
|
43
|
-
assert config.endpoint == "https://test.agentops.ai"
|
44
|
-
assert config.app_url == "https://test-app.agentops.ai"
|
45
|
-
assert config.max_wait_time == 1000
|
46
|
-
assert config.max_queue_size == 256
|
47
|
-
assert config.default_tags == {"tag1", "tag2", "tag3"}
|
48
|
-
assert config.instrument_llm_calls is False
|
49
|
-
assert config.auto_start_session is False
|
50
|
-
assert config.skip_auto_end_session is True
|
51
|
-
assert config.env_data_opt_out is True
|
52
|
-
|
53
|
-
|
54
|
-
def test_config_override_env(mock_env, valid_uuid):
|
55
|
-
"""Test that kwargs override environment variables"""
|
56
|
-
config = Config()
|
57
|
-
|
58
|
-
# Store the original value from environment
|
59
|
-
original_max_queue_size = config.max_queue_size
|
60
|
-
|
61
|
-
config.configure(
|
62
|
-
api_key=valid_uuid,
|
63
|
-
endpoint="https://override.agentops.ai",
|
64
|
-
app_url="https://override-app.agentops.ai",
|
65
|
-
max_wait_time=2000,
|
66
|
-
default_tags=["new-tag"],
|
67
|
-
instrument_llm_calls=True,
|
68
|
-
max_queue_size=original_max_queue_size, # Explicitly pass the original value
|
69
|
-
)
|
70
|
-
|
71
|
-
assert config.api_key == valid_uuid
|
72
|
-
assert config.endpoint == "https://override.agentops.ai"
|
73
|
-
assert config.app_url == "https://override-app.agentops.ai"
|
74
|
-
assert config.max_wait_time == 2000
|
75
|
-
assert config.default_tags == {"new-tag"}
|
76
|
-
assert config.instrument_llm_calls is True
|
77
|
-
# Other values should remain from env
|
78
|
-
assert config.max_queue_size == 256 # Use the value from mock_env
|
79
|
-
|
80
|
-
|
81
|
-
def test_invalid_api_key():
|
82
|
-
"""Test handling of invalid API key raises InvalidApiKeyException"""
|