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
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)
|
tests/unit/test_host_env.py
DELETED
@@ -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%"
|
tests/unit/test_init_py.py
DELETED
@@ -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.")
|