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
tests/unit/test_events.py DELETED
@@ -1,27 +0,0 @@
1
- import time
2
-
3
-
4
- import agentops
5
- from agentops import ActionEvent, ErrorEvent
6
-
7
-
8
- class TestEvents:
9
- def setup_method(self):
10
- self.api_key = "11111111-1111-4111-8111-111111111111"
11
- self.event_type = "test_event_type"
12
-
13
- def test_record_timestamp(self, mock_req):
14
- agentops.init(api_key=self.api_key)
15
-
16
- event = ActionEvent()
17
- time.sleep(0.15)
18
- agentops.record(event)
19
-
20
- assert event.init_timestamp != event.end_timestamp
21
-
22
- def test_record_error_event(self, mock_req):
23
- agentops.init(api_key=self.api_key)
24
-
25
- event = ErrorEvent(logs=None)
26
- time.sleep(0.15)
27
- agentops.record(event)
@@ -1,54 +0,0 @@
1
- from unittest.mock import patch
2
-
3
-
4
- # noinspection PyProtectedMember
5
- from psutil._common import sdiskpart, sdiskusage
6
-
7
- import agentops.helpers.system as host_env
8
-
9
-
10
- def mock_partitions():
11
- # Try to create with new fields first, fall back to old format if it fails
12
- try:
13
- return [
14
- sdiskpart( # noqa: E501
15
- device="/dev/sda1",
16
- mountpoint="/",
17
- fstype="ext4",
18
- opts="rw,relatime",
19
- maxfile=255, # type: ignore
20
- maxpath=4096, # type: ignore
21
- )
22
- ]
23
- except TypeError:
24
- # Fallback for older versions that don't have maxfile/maxpath
25
- return [sdiskpart(device="/dev/sda1", mountpoint="/", fstype="ext4", opts="rw,relatime")]
26
-
27
-
28
- def mock_disk_usage(partition):
29
- if partition == "/":
30
- return sdiskusage(total=(1024**3), used=0, free=(1024**3), percent=100)
31
- else:
32
- raise PermissionError("Device access exception should have been caught")
33
-
34
-
35
- class TestHostEnv:
36
- @patch("psutil.disk_partitions", new=lambda: [mock_partitions()[0]])
37
- @patch("psutil.disk_usage", new=mock_disk_usage)
38
- def test_disk_info(self):
39
- self.assert_disk_info()
40
-
41
- @patch("psutil.disk_partitions", new=mock_partitions)
42
- @patch("psutil.disk_usage", new=mock_disk_usage)
43
- def test_disk_info_skips_oserror(self):
44
- self.assert_disk_info()
45
-
46
- def assert_disk_info(self):
47
- disk_info = host_env.get_disk_details()
48
- assert list(disk_info.keys()) == ["/dev/sda1"]
49
- sda1 = disk_info["/dev/sda1"]
50
- assert sda1["Mountpoint"] == "/"
51
- assert sda1["Total"] == "1.00 GB"
52
- assert sda1["Used"] == "0.00 GB"
53
- assert sda1["Free"] == "1.00 GB"
54
- assert sda1["Percentage"] == "100%"
@@ -1,501 +0,0 @@
1
- from unittest.mock import patch, MagicMock
2
- import agentops
3
- import threading
4
-
5
-
6
- def test_get_client_singleton():
7
- # Should always return the same instance
8
- c1 = agentops.get_client()
9
- c2 = agentops.get_client()
10
- assert c1 is c2
11
-
12
-
13
- def test_get_client_thread_safety():
14
- # Should not create multiple clients in threads
15
- results = []
16
-
17
- def worker():
18
- results.append(agentops.get_client())
19
-
20
- threads = [threading.Thread(target=worker) for _ in range(5)]
21
- for t in threads:
22
- t.start()
23
- for t in threads:
24
- t.join()
25
- assert all(r is results[0] for r in results)
26
-
27
-
28
- def test_init_merges_tags(monkeypatch):
29
- with patch("agentops.get_client") as mock_get_client:
30
- mock_client = MagicMock()
31
- mock_get_client.return_value = mock_client
32
- agentops.init(tags=["a"], default_tags=["b"]) # Should merge
33
- assert {"a", "b"}.issubset(set(mock_client.init.call_args[1]["default_tags"]))
34
-
35
-
36
- def test_init_warns_on_deprecated_tags(monkeypatch):
37
- with patch("agentops.get_client") as mock_get_client, patch("agentops.warn_deprecated_param") as mock_warn:
38
- mock_client = MagicMock()
39
- mock_get_client.return_value = mock_client
40
- agentops.init(tags=["a"])
41
- mock_warn.assert_called_once_with("tags", "default_tags")
42
-
43
-
44
- def test_init_jupyter_detection(monkeypatch):
45
- with patch("agentops.get_client") as mock_get_client:
46
- mock_client = MagicMock()
47
- mock_get_client.return_value = mock_client
48
- # Simulate Jupyter by patching get_ipython
49
- import builtins
50
-
51
- builtins.get_ipython = lambda: type("Z", (), {"__name__": "ZMQInteractiveShell"})()
52
- agentops.init()
53
- del builtins.get_ipython
54
-
55
-
56
- def test_init_jupyter_detection_nameerror():
57
- with patch("agentops.get_client") as mock_get_client:
58
- mock_client = MagicMock()
59
- mock_get_client.return_value = mock_client
60
- # Simulate NameError when get_ipython() is called
61
- import builtins
62
-
63
- original_get_ipython = getattr(builtins, "get_ipython", None)
64
- builtins.get_ipython = lambda: None # This will cause NameError
65
- try:
66
- agentops.init()
67
- except NameError:
68
- pass # Expected
69
- finally:
70
- if original_get_ipython:
71
- builtins.get_ipython = original_get_ipython
72
- else:
73
- delattr(builtins, "get_ipython")
74
-
75
-
76
- def test_configure_valid_and_invalid_params():
77
- with patch("agentops.get_client") as mock_get_client, patch("agentops.logger") as mock_logger:
78
- mock_client = MagicMock()
79
- mock_get_client.return_value = mock_client
80
- # Valid param
81
- agentops.configure(api_key="foo")
82
- mock_client.configure.assert_called_with(api_key="foo")
83
- # Invalid param
84
- agentops.configure(bad_param=123)
85
- mock_logger.warning.assert_any_call("Invalid configuration parameters: {'bad_param'}")
86
-
87
-
88
- def test_record_sets_end_timestamp():
89
- class Dummy:
90
- end_timestamp = None
91
-
92
- with patch("agentops.helpers.time.get_ISO_time", return_value="now"):
93
- d = Dummy()
94
- agentops.record(d)
95
- assert d.end_timestamp == "now"
96
-
97
-
98
- def test_record_no_end_timestamp():
99
- class Dummy:
100
- pass
101
-
102
- d = Dummy()
103
- assert agentops.record(d) is d
104
-
105
-
106
- def test_update_trace_metadata_success():
107
- with patch("agentops.tracer") as mock_tracer, patch("agentops.get_current_span") as mock_get_span:
108
- mock_span = MagicMock()
109
- mock_span.is_recording.return_value = True
110
- mock_span.name = "foo.SESSION"
111
- mock_get_span.return_value = mock_span
112
- mock_tracer.get_active_traces.return_value = {}
113
- mock_tracer.initialized = True
114
- result = agentops.update_trace_metadata({"foo": "bar"})
115
- assert result is True
116
- mock_span.set_attribute.assert_called()
117
-
118
-
119
- def test_update_trace_metadata_no_active_span():
120
- with patch("agentops.tracer") as mock_tracer, patch("agentops.get_current_span", return_value=None):
121
- mock_tracer.get_active_traces.return_value = {}
122
- mock_tracer.initialized = True
123
- assert not agentops.update_trace_metadata({"foo": "bar"})
124
-
125
-
126
- def test_update_trace_metadata_not_recording():
127
- with patch("agentops.tracer") as mock_tracer, patch("agentops.get_current_span") as mock_get_span:
128
- mock_span = MagicMock()
129
- mock_span.is_recording.return_value = False
130
- mock_get_span.return_value = mock_span
131
- mock_tracer.get_active_traces.return_value = {}
132
- mock_tracer.initialized = True
133
- assert not agentops.update_trace_metadata({"foo": "bar"})
134
-
135
-
136
- def test_update_trace_metadata_invalid_type():
137
- with patch("agentops.tracer") as mock_tracer, patch("agentops.get_current_span") as mock_get_span:
138
- mock_span = MagicMock()
139
- mock_span.is_recording.return_value = True
140
- mock_span.name = "foo.SESSION"
141
- mock_get_span.return_value = mock_span
142
- mock_tracer.get_active_traces.return_value = {}
143
- mock_tracer.initialized = True
144
- # Value is a dict, which is not allowed
145
- assert not agentops.update_trace_metadata({"foo": {"bar": 1}})
146
-
147
-
148
- def test_update_trace_metadata_list_type():
149
- with patch("agentops.tracer") as mock_tracer, patch("agentops.get_current_span") as mock_get_span:
150
- mock_span = MagicMock()
151
- mock_span.is_recording.return_value = True
152
- mock_span.name = "foo.SESSION"
153
- mock_get_span.return_value = mock_span
154
- mock_tracer.get_active_traces.return_value = {}
155
- mock_tracer.initialized = True
156
- # List of valid types
157
- assert agentops.update_trace_metadata({"foo": [1, 2, 3]})
158
- mock_span.set_attribute.assert_called()
159
-
160
-
161
- def test_update_trace_metadata_extract_key_single_part():
162
- with patch("agentops.tracer") as mock_tracer, patch("agentops.get_current_span") as mock_get_span:
163
- mock_span = MagicMock()
164
- mock_span.is_recording.return_value = True
165
- mock_span.name = "foo.SESSION"
166
- mock_get_span.return_value = mock_span
167
- mock_tracer.get_active_traces.return_value = {}
168
- mock_tracer.initialized = True
169
-
170
- # Test with a semantic convention that has only one part (len < 2)
171
- with patch("agentops.AgentAttributes") as mock_attrs:
172
- mock_attrs.__dict__ = {"SINGLE": "single_value"}
173
- result = agentops.update_trace_metadata({"single_value": "test"})
174
- assert result is True
175
-
176
-
177
- def test_update_trace_metadata_skip_gen_ai_attributes():
178
- with patch("agentops.tracer") as mock_tracer, patch("agentops.get_current_span") as mock_get_span:
179
- mock_span = MagicMock()
180
- mock_span.is_recording.return_value = True
181
- mock_span.name = "foo.SESSION"
182
- mock_get_span.return_value = mock_span
183
- mock_tracer.get_active_traces.return_value = {}
184
- mock_tracer.initialized = True
185
-
186
- # Test that gen_ai attributes are skipped
187
- with patch("agentops.AgentAttributes") as mock_attrs:
188
- mock_attrs.__dict__ = {"GEN_AI_ATTR": "gen_ai.something"}
189
- agentops.update_trace_metadata({"gen_ai.something": "test"})
190
- # Should still work but skip the gen_ai attribute
191
-
192
-
193
- def test_update_trace_metadata_trace_id_conversion_error():
194
- with patch("agentops.tracer") as mock_tracer, patch("agentops.get_current_span") as mock_get_span:
195
- mock_span = MagicMock()
196
- mock_span.is_recording.return_value = True
197
- mock_span.name = "child_span" # Not a session span
198
- mock_span.get_span_context.return_value.trace_id = "invalid_hex"
199
- mock_get_span.return_value = mock_span
200
- mock_tracer.get_active_traces.return_value = {"trace1": MagicMock()}
201
- mock_tracer.initialized = True
202
-
203
- # This should handle the ValueError from int("invalid_hex", 16)
204
- agentops.update_trace_metadata({"foo": "bar"})
205
- # The function should handle the error gracefully
206
-
207
-
208
- def test_update_trace_metadata_no_active_traces():
209
- with (
210
- patch("agentops.tracer") as mock_tracer,
211
- patch("agentops.get_current_span", return_value=None),
212
- patch("agentops.logger") as mock_logger,
213
- ):
214
- mock_tracer.get_active_traces.return_value = {}
215
- mock_tracer.initialized = True
216
- result = agentops.update_trace_metadata({"foo": "bar"})
217
- assert result is False
218
- mock_logger.warning.assert_called_with("No active trace found. Cannot update metadata.")
219
-
220
-
221
- def test_update_trace_metadata_span_not_recording():
222
- with (
223
- patch("agentops.tracer") as mock_tracer,
224
- patch("agentops.get_current_span") as mock_get_span,
225
- patch("agentops.logger") as mock_logger,
226
- ):
227
- mock_span = MagicMock()
228
- mock_span.is_recording.return_value = False
229
- mock_get_span.return_value = mock_span
230
- mock_tracer.get_active_traces.return_value = {}
231
- mock_tracer.initialized = True
232
- result = agentops.update_trace_metadata({"foo": "bar"})
233
- assert result is False
234
- mock_logger.warning.assert_called_with("No active trace found. Cannot update metadata.")
235
-
236
-
237
- def test_update_trace_metadata_list_invalid_types():
238
- with (
239
- patch("agentops.tracer") as mock_tracer,
240
- patch("agentops.get_current_span") as mock_get_span,
241
- patch("agentops.logger") as mock_logger,
242
- ):
243
- mock_span = MagicMock()
244
- mock_span.is_recording.return_value = True
245
- mock_span.name = "foo.SESSION"
246
- mock_get_span.return_value = mock_span
247
- mock_tracer.get_active_traces.return_value = {}
248
- mock_tracer.initialized = True
249
-
250
- # List with invalid types (dict)
251
- agentops.update_trace_metadata({"foo": [{"invalid": "type"}]})
252
- mock_logger.warning.assert_called_with("No valid metadata attributes were updated")
253
-
254
-
255
- def test_update_trace_metadata_invalid_value_type():
256
- with (
257
- patch("agentops.tracer") as mock_tracer,
258
- patch("agentops.get_current_span") as mock_get_span,
259
- patch("agentops.logger") as mock_logger,
260
- ):
261
- mock_span = MagicMock()
262
- mock_span.is_recording.return_value = True
263
- mock_span.name = "foo.SESSION"
264
- mock_get_span.return_value = mock_span
265
- mock_tracer.get_active_traces.return_value = {}
266
- mock_tracer.initialized = True
267
-
268
- # Invalid value type (dict)
269
- agentops.update_trace_metadata({"foo": {"invalid": "type"}})
270
- mock_logger.warning.assert_called_with("No valid metadata attributes were updated")
271
-
272
-
273
- def test_update_trace_metadata_semantic_convention_mapping():
274
- with (
275
- patch("agentops.tracer") as mock_tracer,
276
- patch("agentops.get_current_span") as mock_get_span,
277
- patch("agentops.logger") as mock_logger,
278
- ):
279
- mock_span = MagicMock()
280
- mock_span.is_recording.return_value = True
281
- mock_span.name = "foo.SESSION"
282
- mock_get_span.return_value = mock_span
283
- mock_tracer.get_active_traces.return_value = {}
284
- mock_tracer.initialized = True
285
-
286
- # Test semantic convention mapping
287
- with patch("agentops.AgentAttributes") as mock_attrs:
288
- mock_attrs.__dict__ = {"TEST_ATTR": "agent.test_attribute"}
289
- agentops.update_trace_metadata({"agent_test_attribute": "test"})
290
- mock_logger.debug.assert_called_with("Successfully updated 1 metadata attributes on trace")
291
-
292
-
293
- def test_update_trace_metadata_exception_handling():
294
- with (
295
- patch("agentops.tracer") as mock_tracer,
296
- patch("agentops.get_current_span") as mock_get_span,
297
- patch("agentops.logger") as mock_logger,
298
- ):
299
- mock_span = MagicMock()
300
- mock_span.is_recording.return_value = True
301
- mock_span.name = "foo.SESSION"
302
- mock_span.set_attribute.side_effect = Exception("Test error")
303
- mock_get_span.return_value = mock_span
304
- mock_tracer.get_active_traces.return_value = {}
305
- mock_tracer.initialized = True
306
-
307
- agentops.update_trace_metadata({"foo": "bar"})
308
- mock_logger.error.assert_called_with("Error updating trace metadata: Test error")
309
-
310
-
311
- def test_update_trace_metadata_no_valid_attributes():
312
- with (
313
- patch("agentops.tracer") as mock_tracer,
314
- patch("agentops.get_current_span") as mock_get_span,
315
- patch("agentops.logger") as mock_logger,
316
- ):
317
- mock_span = MagicMock()
318
- mock_span.is_recording.return_value = True
319
- mock_span.name = "foo.SESSION"
320
- mock_get_span.return_value = mock_span
321
- mock_tracer.get_active_traces.return_value = {}
322
- mock_tracer.initialized = True
323
-
324
- # All values are None
325
- agentops.update_trace_metadata({"foo": None, "bar": None})
326
- mock_logger.warning.assert_called_with("No valid metadata attributes were updated")
327
-
328
-
329
- def test_start_trace_auto_init_failure():
330
- with (
331
- patch("agentops.tracer") as mock_tracer,
332
- patch("agentops.init") as mock_init,
333
- patch("agentops.logger") as mock_logger,
334
- ):
335
- mock_tracer.initialized = False
336
- mock_init.side_effect = Exception("Init failed")
337
-
338
- agentops.start_trace("test")
339
- mock_logger.error.assert_called_with(
340
- "SDK auto-initialization failed during start_trace: Init failed. Cannot start trace."
341
- )
342
-
343
-
344
- def test_start_trace_auto_init_still_not_initialized():
345
- with (
346
- patch("agentops.tracer") as mock_tracer,
347
- patch("agentops.init") as _,
348
- patch("agentops.logger") as mock_logger,
349
- ):
350
- mock_tracer.initialized = False
351
-
352
- agentops.start_trace("test")
353
- mock_logger.error.assert_called_with("SDK initialization failed. Cannot start trace.")
354
-
355
-
356
- def test_end_trace_not_initialized():
357
- with patch("agentops.tracer") as mock_tracer, patch("agentops.logger") as mock_logger:
358
- mock_tracer.initialized = False
359
- agentops.end_trace()
360
- mock_logger.warning.assert_called_with("AgentOps SDK not initialized. Cannot end trace.")
361
-
362
-
363
- def test_update_trace_metadata_not_initialized():
364
- with patch("agentops.tracer") as mock_tracer, patch("agentops.logger") as mock_logger:
365
- mock_tracer.initialized = False
366
- agentops.update_trace_metadata({"foo": "bar"})
367
- mock_logger.warning.assert_called_with("AgentOps SDK not initialized. Cannot update trace metadata.")
368
-
369
-
370
- def test_all_exports_importable():
371
- # Just import all symbols to ensure they're present
372
- from agentops import (
373
- init,
374
- configure,
375
- )
376
-
377
- assert callable(init)
378
- assert callable(configure)
379
-
380
-
381
- def test_update_trace_metadata_use_current_span_when_no_parent_found():
382
- with patch("agentops.tracer") as mock_tracer, patch("agentops.get_current_span") as mock_get_span:
383
- mock_span = MagicMock()
384
- mock_span.is_recording.return_value = True
385
- mock_span.name = "child_span" # Not a session span
386
- mock_span.get_span_context.return_value.trace_id = 12345
387
- mock_get_span.return_value = mock_span
388
- mock_tracer.get_active_traces.return_value = {"trace1": MagicMock()}
389
- mock_tracer.initialized = True
390
-
391
- # When no parent trace is found, should use current span
392
- agentops.update_trace_metadata({"foo": "bar"})
393
- # The function should work with current span
394
-
395
-
396
- def test_update_trace_metadata_use_current_span_when_no_active_traces():
397
- with patch("agentops.tracer") as mock_tracer, patch("agentops.get_current_span") as mock_get_span:
398
- mock_span = MagicMock()
399
- mock_span.is_recording.return_value = True
400
- mock_span.name = "child_span" # Not a session span
401
- mock_get_span.return_value = mock_span
402
- mock_tracer.get_active_traces.return_value = {}
403
- mock_tracer.initialized = True
404
-
405
- # When no active traces, should use current span
406
- agentops.update_trace_metadata({"foo": "bar"})
407
- # The function should work with current span
408
-
409
-
410
- def test_update_trace_metadata_use_most_recent_trace():
411
- with (
412
- patch("agentops.tracer") as mock_tracer,
413
- patch("agentops.get_current_span", return_value=None),
414
- patch("agentops.logger") as mock_logger,
415
- ):
416
- mock_trace_context = MagicMock()
417
- mock_trace_context.span = MagicMock()
418
- mock_trace_context.span.is_recording.return_value = True
419
- mock_tracer.get_active_traces.return_value = {"trace1": mock_trace_context}
420
- mock_tracer.initialized = True
421
-
422
- agentops.update_trace_metadata({"foo": "bar"})
423
- mock_logger.debug.assert_called_with("Successfully updated 1 metadata attributes on trace")
424
-
425
-
426
- def test_end_trace_with_trace_context():
427
- with patch("agentops.tracer") as mock_tracer:
428
- mock_tracer.initialized = True
429
- mock_trace_context = MagicMock()
430
- agentops.end_trace(mock_trace_context, "Error")
431
- mock_tracer.end_trace.assert_called_with(trace_context=mock_trace_context, end_state="Error")
432
-
433
-
434
- def test_init_jupyter_detection_actual_nameerror():
435
- with patch("agentops.get_client") as mock_get_client:
436
- mock_client = MagicMock()
437
- mock_get_client.return_value = mock_client
438
- # Actually remove get_ipython to trigger NameError
439
- import builtins
440
-
441
- original_get_ipython = getattr(builtins, "get_ipython", None)
442
- if hasattr(builtins, "get_ipython"):
443
- delattr(builtins, "get_ipython")
444
- try:
445
- agentops.init()
446
- finally:
447
- if original_get_ipython:
448
- builtins.get_ipython = original_get_ipython
449
-
450
-
451
- def test_end_trace_with_default_state():
452
- with patch("agentops.tracer") as mock_tracer:
453
- mock_tracer.initialized = True
454
- from agentops import TraceState
455
-
456
- agentops.end_trace() # Should use default TraceState.SUCCESS
457
- mock_tracer.end_trace.assert_called_with(trace_context=None, end_state=TraceState.SUCCESS)
458
-
459
-
460
- def test_update_trace_metadata_extract_key_single_part_actual():
461
- with patch("agentops.tracer") as mock_tracer, patch("agentops.get_current_span") as mock_get_span:
462
- mock_span = MagicMock()
463
- mock_span.is_recording.return_value = True
464
- mock_span.name = "foo.SESSION"
465
- mock_get_span.return_value = mock_span
466
- mock_tracer.get_active_traces.return_value = {}
467
- mock_tracer.initialized = True
468
-
469
- # Test with a semantic convention that has only one part (len < 2)
470
- with patch("agentops.AgentAttributes") as mock_attrs:
471
- mock_attrs.__dict__ = {"SINGLE": "single"}
472
- agentops.update_trace_metadata({"single": "test"})
473
- # The function should handle single-part attributes
474
-
475
-
476
- def test_update_trace_metadata_skip_gen_ai_attributes_actual():
477
- with patch("agentops.tracer") as mock_tracer, patch("agentops.get_current_span") as mock_get_span:
478
- mock_span = MagicMock()
479
- mock_span.is_recording.return_value = True
480
- mock_span.name = "foo.SESSION"
481
- mock_get_span.return_value = mock_span
482
- mock_tracer.get_active_traces.return_value = {}
483
- mock_tracer.initialized = True
484
-
485
- # Test that gen_ai attributes are actually skipped in the mapping
486
- with patch("agentops.AgentAttributes") as mock_attrs:
487
- mock_attrs.__dict__ = {"GEN_AI_ATTR": "gen_ai.something"}
488
- agentops.update_trace_metadata({"gen_ai.something": "test"})
489
- # Should still work but the gen_ai attribute should be skipped in mapping
490
-
491
-
492
- def test_update_trace_metadata_no_active_traces_actual():
493
- with (
494
- patch("agentops.tracer") as mock_tracer,
495
- patch("agentops.get_current_span", return_value=None),
496
- patch("agentops.logger") as mock_logger,
497
- ):
498
- mock_tracer.get_active_traces.return_value = {}
499
- mock_tracer.initialized = True
500
- agentops.update_trace_metadata({"foo": "bar"})
501
- mock_logger.warning.assert_called_with("No active trace found. Cannot update metadata.")