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.
Files changed (153) hide show
  1. agentops/__init__.py +0 -0
  2. agentops/client/api/base.py +28 -30
  3. agentops/client/api/versions/v3.py +29 -25
  4. agentops/client/api/versions/v4.py +87 -46
  5. agentops/client/client.py +98 -29
  6. agentops/client/http/README.md +87 -0
  7. agentops/client/http/http_client.py +126 -172
  8. agentops/config.py +8 -2
  9. agentops/instrumentation/OpenTelemetry.md +133 -0
  10. agentops/instrumentation/README.md +167 -0
  11. agentops/instrumentation/__init__.py +13 -1
  12. agentops/instrumentation/agentic/ag2/__init__.py +18 -0
  13. agentops/instrumentation/agentic/ag2/instrumentor.py +922 -0
  14. agentops/instrumentation/agentic/agno/__init__.py +19 -0
  15. agentops/instrumentation/agentic/agno/attributes/__init__.py +20 -0
  16. agentops/instrumentation/agentic/agno/attributes/agent.py +250 -0
  17. agentops/instrumentation/agentic/agno/attributes/metrics.py +214 -0
  18. agentops/instrumentation/agentic/agno/attributes/storage.py +158 -0
  19. agentops/instrumentation/agentic/agno/attributes/team.py +195 -0
  20. agentops/instrumentation/agentic/agno/attributes/tool.py +210 -0
  21. agentops/instrumentation/agentic/agno/attributes/workflow.py +254 -0
  22. agentops/instrumentation/agentic/agno/instrumentor.py +1313 -0
  23. agentops/instrumentation/agentic/crewai/LICENSE +201 -0
  24. agentops/instrumentation/agentic/crewai/NOTICE.md +10 -0
  25. agentops/instrumentation/agentic/crewai/__init__.py +6 -0
  26. agentops/instrumentation/agentic/crewai/crewai_span_attributes.py +335 -0
  27. agentops/instrumentation/agentic/crewai/instrumentation.py +535 -0
  28. agentops/instrumentation/agentic/crewai/version.py +1 -0
  29. agentops/instrumentation/agentic/google_adk/__init__.py +19 -0
  30. agentops/instrumentation/agentic/google_adk/instrumentor.py +68 -0
  31. agentops/instrumentation/agentic/google_adk/patch.py +767 -0
  32. agentops/instrumentation/agentic/haystack/__init__.py +1 -0
  33. agentops/instrumentation/agentic/haystack/instrumentor.py +186 -0
  34. agentops/instrumentation/agentic/langgraph/__init__.py +3 -0
  35. agentops/instrumentation/agentic/langgraph/attributes.py +54 -0
  36. agentops/instrumentation/agentic/langgraph/instrumentation.py +598 -0
  37. agentops/instrumentation/agentic/langgraph/version.py +1 -0
  38. agentops/instrumentation/agentic/openai_agents/README.md +156 -0
  39. agentops/instrumentation/agentic/openai_agents/SPANS.md +145 -0
  40. agentops/instrumentation/agentic/openai_agents/TRACING_API.md +144 -0
  41. agentops/instrumentation/agentic/openai_agents/__init__.py +30 -0
  42. agentops/instrumentation/agentic/openai_agents/attributes/common.py +549 -0
  43. agentops/instrumentation/agentic/openai_agents/attributes/completion.py +172 -0
  44. agentops/instrumentation/agentic/openai_agents/attributes/model.py +58 -0
  45. agentops/instrumentation/agentic/openai_agents/attributes/tokens.py +275 -0
  46. agentops/instrumentation/agentic/openai_agents/exporter.py +469 -0
  47. agentops/instrumentation/agentic/openai_agents/instrumentor.py +107 -0
  48. agentops/instrumentation/agentic/openai_agents/processor.py +58 -0
  49. agentops/instrumentation/agentic/smolagents/README.md +88 -0
  50. agentops/instrumentation/agentic/smolagents/__init__.py +12 -0
  51. agentops/instrumentation/agentic/smolagents/attributes/agent.py +354 -0
  52. agentops/instrumentation/agentic/smolagents/attributes/model.py +205 -0
  53. agentops/instrumentation/agentic/smolagents/instrumentor.py +286 -0
  54. agentops/instrumentation/agentic/smolagents/stream_wrapper.py +258 -0
  55. agentops/instrumentation/agentic/xpander/__init__.py +15 -0
  56. agentops/instrumentation/agentic/xpander/context.py +112 -0
  57. agentops/instrumentation/agentic/xpander/instrumentor.py +877 -0
  58. agentops/instrumentation/agentic/xpander/trace_probe.py +86 -0
  59. agentops/instrumentation/agentic/xpander/version.py +3 -0
  60. agentops/instrumentation/common/README.md +65 -0
  61. agentops/instrumentation/common/attributes.py +1 -2
  62. agentops/instrumentation/providers/anthropic/__init__.py +24 -0
  63. agentops/instrumentation/providers/anthropic/attributes/__init__.py +23 -0
  64. agentops/instrumentation/providers/anthropic/attributes/common.py +64 -0
  65. agentops/instrumentation/providers/anthropic/attributes/message.py +541 -0
  66. agentops/instrumentation/providers/anthropic/attributes/tools.py +231 -0
  67. agentops/instrumentation/providers/anthropic/event_handler_wrapper.py +90 -0
  68. agentops/instrumentation/providers/anthropic/instrumentor.py +146 -0
  69. agentops/instrumentation/providers/anthropic/stream_wrapper.py +436 -0
  70. agentops/instrumentation/providers/google_genai/README.md +33 -0
  71. agentops/instrumentation/providers/google_genai/__init__.py +24 -0
  72. agentops/instrumentation/providers/google_genai/attributes/__init__.py +25 -0
  73. agentops/instrumentation/providers/google_genai/attributes/chat.py +125 -0
  74. agentops/instrumentation/providers/google_genai/attributes/common.py +88 -0
  75. agentops/instrumentation/providers/google_genai/attributes/model.py +284 -0
  76. agentops/instrumentation/providers/google_genai/instrumentor.py +170 -0
  77. agentops/instrumentation/providers/google_genai/stream_wrapper.py +238 -0
  78. agentops/instrumentation/providers/ibm_watsonx_ai/__init__.py +28 -0
  79. agentops/instrumentation/providers/ibm_watsonx_ai/attributes/__init__.py +27 -0
  80. agentops/instrumentation/providers/ibm_watsonx_ai/attributes/attributes.py +277 -0
  81. agentops/instrumentation/providers/ibm_watsonx_ai/attributes/common.py +104 -0
  82. agentops/instrumentation/providers/ibm_watsonx_ai/instrumentor.py +162 -0
  83. agentops/instrumentation/providers/ibm_watsonx_ai/stream_wrapper.py +302 -0
  84. agentops/instrumentation/providers/mem0/__init__.py +45 -0
  85. agentops/instrumentation/providers/mem0/common.py +377 -0
  86. agentops/instrumentation/providers/mem0/instrumentor.py +270 -0
  87. agentops/instrumentation/providers/mem0/memory.py +430 -0
  88. agentops/instrumentation/providers/openai/__init__.py +21 -0
  89. agentops/instrumentation/providers/openai/attributes/__init__.py +7 -0
  90. agentops/instrumentation/providers/openai/attributes/common.py +55 -0
  91. agentops/instrumentation/providers/openai/attributes/response.py +607 -0
  92. agentops/instrumentation/providers/openai/config.py +36 -0
  93. agentops/instrumentation/providers/openai/instrumentor.py +312 -0
  94. agentops/instrumentation/providers/openai/stream_wrapper.py +941 -0
  95. agentops/instrumentation/providers/openai/utils.py +44 -0
  96. agentops/instrumentation/providers/openai/v0.py +176 -0
  97. agentops/instrumentation/providers/openai/v0_wrappers.py +483 -0
  98. agentops/instrumentation/providers/openai/wrappers/__init__.py +30 -0
  99. agentops/instrumentation/providers/openai/wrappers/assistant.py +277 -0
  100. agentops/instrumentation/providers/openai/wrappers/chat.py +259 -0
  101. agentops/instrumentation/providers/openai/wrappers/completion.py +109 -0
  102. agentops/instrumentation/providers/openai/wrappers/embeddings.py +94 -0
  103. agentops/instrumentation/providers/openai/wrappers/image_gen.py +75 -0
  104. agentops/instrumentation/providers/openai/wrappers/responses.py +191 -0
  105. agentops/instrumentation/providers/openai/wrappers/shared.py +81 -0
  106. agentops/instrumentation/utilities/concurrent_futures/__init__.py +10 -0
  107. agentops/instrumentation/utilities/concurrent_futures/instrumentation.py +206 -0
  108. agentops/integration/callbacks/dspy/__init__.py +11 -0
  109. agentops/integration/callbacks/dspy/callback.py +471 -0
  110. agentops/integration/callbacks/langchain/README.md +59 -0
  111. agentops/integration/callbacks/langchain/__init__.py +15 -0
  112. agentops/integration/callbacks/langchain/callback.py +791 -0
  113. agentops/integration/callbacks/langchain/utils.py +54 -0
  114. agentops/legacy/crewai.md +121 -0
  115. agentops/logging/instrument_logging.py +4 -0
  116. agentops/sdk/README.md +220 -0
  117. agentops/sdk/core.py +75 -32
  118. agentops/sdk/descriptors/classproperty.py +28 -0
  119. agentops/sdk/exporters.py +152 -33
  120. agentops/semconv/README.md +125 -0
  121. agentops/semconv/span_kinds.py +0 -2
  122. agentops/validation.py +102 -63
  123. {mseep_agentops-0.4.18.dist-info → mseep_agentops-0.4.23.dist-info}/METADATA +30 -40
  124. mseep_agentops-0.4.23.dist-info/RECORD +178 -0
  125. {mseep_agentops-0.4.18.dist-info → mseep_agentops-0.4.23.dist-info}/WHEEL +1 -2
  126. mseep_agentops-0.4.18.dist-info/RECORD +0 -94
  127. mseep_agentops-0.4.18.dist-info/top_level.txt +0 -2
  128. tests/conftest.py +0 -10
  129. tests/unit/client/__init__.py +0 -1
  130. tests/unit/client/test_http_adapter.py +0 -221
  131. tests/unit/client/test_http_client.py +0 -206
  132. tests/unit/conftest.py +0 -54
  133. tests/unit/sdk/__init__.py +0 -1
  134. tests/unit/sdk/instrumentation_tester.py +0 -207
  135. tests/unit/sdk/test_attributes.py +0 -392
  136. tests/unit/sdk/test_concurrent_instrumentation.py +0 -468
  137. tests/unit/sdk/test_decorators.py +0 -763
  138. tests/unit/sdk/test_exporters.py +0 -241
  139. tests/unit/sdk/test_factory.py +0 -1188
  140. tests/unit/sdk/test_internal_span_processor.py +0 -397
  141. tests/unit/sdk/test_resource_attributes.py +0 -35
  142. tests/unit/test_config.py +0 -82
  143. tests/unit/test_context_manager.py +0 -777
  144. tests/unit/test_events.py +0 -27
  145. tests/unit/test_host_env.py +0 -54
  146. tests/unit/test_init_py.py +0 -501
  147. tests/unit/test_serialization.py +0 -433
  148. tests/unit/test_session.py +0 -676
  149. tests/unit/test_user_agent.py +0 -34
  150. tests/unit/test_validation.py +0 -405
  151. {tests → agentops/instrumentation/agentic/openai_agents/attributes}/__init__.py +0 -0
  152. /tests/unit/__init__.py → /agentops/instrumentation/providers/openai/attributes/tools.py +0 -0
  153. {mseep_agentops-0.4.18.dist-info → mseep_agentops-0.4.23.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"""