mseep-agentops 0.4.18__py3-none-any.whl → 0.4.23__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agentops/__init__.py +0 -0
- agentops/client/api/base.py +28 -30
- agentops/client/api/versions/v3.py +29 -25
- agentops/client/api/versions/v4.py +87 -46
- agentops/client/client.py +98 -29
- agentops/client/http/README.md +87 -0
- agentops/client/http/http_client.py +126 -172
- agentops/config.py +8 -2
- agentops/instrumentation/OpenTelemetry.md +133 -0
- agentops/instrumentation/README.md +167 -0
- agentops/instrumentation/__init__.py +13 -1
- agentops/instrumentation/agentic/ag2/__init__.py +18 -0
- agentops/instrumentation/agentic/ag2/instrumentor.py +922 -0
- agentops/instrumentation/agentic/agno/__init__.py +19 -0
- agentops/instrumentation/agentic/agno/attributes/__init__.py +20 -0
- agentops/instrumentation/agentic/agno/attributes/agent.py +250 -0
- agentops/instrumentation/agentic/agno/attributes/metrics.py +214 -0
- agentops/instrumentation/agentic/agno/attributes/storage.py +158 -0
- agentops/instrumentation/agentic/agno/attributes/team.py +195 -0
- agentops/instrumentation/agentic/agno/attributes/tool.py +210 -0
- agentops/instrumentation/agentic/agno/attributes/workflow.py +254 -0
- agentops/instrumentation/agentic/agno/instrumentor.py +1313 -0
- agentops/instrumentation/agentic/crewai/LICENSE +201 -0
- agentops/instrumentation/agentic/crewai/NOTICE.md +10 -0
- agentops/instrumentation/agentic/crewai/__init__.py +6 -0
- agentops/instrumentation/agentic/crewai/crewai_span_attributes.py +335 -0
- agentops/instrumentation/agentic/crewai/instrumentation.py +535 -0
- agentops/instrumentation/agentic/crewai/version.py +1 -0
- agentops/instrumentation/agentic/google_adk/__init__.py +19 -0
- agentops/instrumentation/agentic/google_adk/instrumentor.py +68 -0
- agentops/instrumentation/agentic/google_adk/patch.py +767 -0
- agentops/instrumentation/agentic/haystack/__init__.py +1 -0
- agentops/instrumentation/agentic/haystack/instrumentor.py +186 -0
- agentops/instrumentation/agentic/langgraph/__init__.py +3 -0
- agentops/instrumentation/agentic/langgraph/attributes.py +54 -0
- agentops/instrumentation/agentic/langgraph/instrumentation.py +598 -0
- agentops/instrumentation/agentic/langgraph/version.py +1 -0
- agentops/instrumentation/agentic/openai_agents/README.md +156 -0
- agentops/instrumentation/agentic/openai_agents/SPANS.md +145 -0
- agentops/instrumentation/agentic/openai_agents/TRACING_API.md +144 -0
- agentops/instrumentation/agentic/openai_agents/__init__.py +30 -0
- agentops/instrumentation/agentic/openai_agents/attributes/common.py +549 -0
- agentops/instrumentation/agentic/openai_agents/attributes/completion.py +172 -0
- agentops/instrumentation/agentic/openai_agents/attributes/model.py +58 -0
- agentops/instrumentation/agentic/openai_agents/attributes/tokens.py +275 -0
- agentops/instrumentation/agentic/openai_agents/exporter.py +469 -0
- agentops/instrumentation/agentic/openai_agents/instrumentor.py +107 -0
- agentops/instrumentation/agentic/openai_agents/processor.py +58 -0
- agentops/instrumentation/agentic/smolagents/README.md +88 -0
- agentops/instrumentation/agentic/smolagents/__init__.py +12 -0
- agentops/instrumentation/agentic/smolagents/attributes/agent.py +354 -0
- agentops/instrumentation/agentic/smolagents/attributes/model.py +205 -0
- agentops/instrumentation/agentic/smolagents/instrumentor.py +286 -0
- agentops/instrumentation/agentic/smolagents/stream_wrapper.py +258 -0
- agentops/instrumentation/agentic/xpander/__init__.py +15 -0
- agentops/instrumentation/agentic/xpander/context.py +112 -0
- agentops/instrumentation/agentic/xpander/instrumentor.py +877 -0
- agentops/instrumentation/agentic/xpander/trace_probe.py +86 -0
- agentops/instrumentation/agentic/xpander/version.py +3 -0
- agentops/instrumentation/common/README.md +65 -0
- agentops/instrumentation/common/attributes.py +1 -2
- agentops/instrumentation/providers/anthropic/__init__.py +24 -0
- agentops/instrumentation/providers/anthropic/attributes/__init__.py +23 -0
- agentops/instrumentation/providers/anthropic/attributes/common.py +64 -0
- agentops/instrumentation/providers/anthropic/attributes/message.py +541 -0
- agentops/instrumentation/providers/anthropic/attributes/tools.py +231 -0
- agentops/instrumentation/providers/anthropic/event_handler_wrapper.py +90 -0
- agentops/instrumentation/providers/anthropic/instrumentor.py +146 -0
- agentops/instrumentation/providers/anthropic/stream_wrapper.py +436 -0
- agentops/instrumentation/providers/google_genai/README.md +33 -0
- agentops/instrumentation/providers/google_genai/__init__.py +24 -0
- agentops/instrumentation/providers/google_genai/attributes/__init__.py +25 -0
- agentops/instrumentation/providers/google_genai/attributes/chat.py +125 -0
- agentops/instrumentation/providers/google_genai/attributes/common.py +88 -0
- agentops/instrumentation/providers/google_genai/attributes/model.py +284 -0
- agentops/instrumentation/providers/google_genai/instrumentor.py +170 -0
- agentops/instrumentation/providers/google_genai/stream_wrapper.py +238 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/__init__.py +28 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/attributes/__init__.py +27 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/attributes/attributes.py +277 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/attributes/common.py +104 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/instrumentor.py +162 -0
- agentops/instrumentation/providers/ibm_watsonx_ai/stream_wrapper.py +302 -0
- agentops/instrumentation/providers/mem0/__init__.py +45 -0
- agentops/instrumentation/providers/mem0/common.py +377 -0
- agentops/instrumentation/providers/mem0/instrumentor.py +270 -0
- agentops/instrumentation/providers/mem0/memory.py +430 -0
- agentops/instrumentation/providers/openai/__init__.py +21 -0
- agentops/instrumentation/providers/openai/attributes/__init__.py +7 -0
- agentops/instrumentation/providers/openai/attributes/common.py +55 -0
- agentops/instrumentation/providers/openai/attributes/response.py +607 -0
- agentops/instrumentation/providers/openai/config.py +36 -0
- agentops/instrumentation/providers/openai/instrumentor.py +312 -0
- agentops/instrumentation/providers/openai/stream_wrapper.py +941 -0
- agentops/instrumentation/providers/openai/utils.py +44 -0
- agentops/instrumentation/providers/openai/v0.py +176 -0
- agentops/instrumentation/providers/openai/v0_wrappers.py +483 -0
- agentops/instrumentation/providers/openai/wrappers/__init__.py +30 -0
- agentops/instrumentation/providers/openai/wrappers/assistant.py +277 -0
- agentops/instrumentation/providers/openai/wrappers/chat.py +259 -0
- agentops/instrumentation/providers/openai/wrappers/completion.py +109 -0
- agentops/instrumentation/providers/openai/wrappers/embeddings.py +94 -0
- agentops/instrumentation/providers/openai/wrappers/image_gen.py +75 -0
- agentops/instrumentation/providers/openai/wrappers/responses.py +191 -0
- agentops/instrumentation/providers/openai/wrappers/shared.py +81 -0
- agentops/instrumentation/utilities/concurrent_futures/__init__.py +10 -0
- agentops/instrumentation/utilities/concurrent_futures/instrumentation.py +206 -0
- agentops/integration/callbacks/dspy/__init__.py +11 -0
- agentops/integration/callbacks/dspy/callback.py +471 -0
- agentops/integration/callbacks/langchain/README.md +59 -0
- agentops/integration/callbacks/langchain/__init__.py +15 -0
- agentops/integration/callbacks/langchain/callback.py +791 -0
- agentops/integration/callbacks/langchain/utils.py +54 -0
- agentops/legacy/crewai.md +121 -0
- agentops/logging/instrument_logging.py +4 -0
- agentops/sdk/README.md +220 -0
- agentops/sdk/core.py +75 -32
- agentops/sdk/descriptors/classproperty.py +28 -0
- agentops/sdk/exporters.py +152 -33
- agentops/semconv/README.md +125 -0
- agentops/semconv/span_kinds.py +0 -2
- agentops/validation.py +102 -63
- {mseep_agentops-0.4.18.dist-info → mseep_agentops-0.4.23.dist-info}/METADATA +30 -40
- mseep_agentops-0.4.23.dist-info/RECORD +178 -0
- {mseep_agentops-0.4.18.dist-info → mseep_agentops-0.4.23.dist-info}/WHEEL +1 -2
- mseep_agentops-0.4.18.dist-info/RECORD +0 -94
- mseep_agentops-0.4.18.dist-info/top_level.txt +0 -2
- tests/conftest.py +0 -10
- tests/unit/client/__init__.py +0 -1
- tests/unit/client/test_http_adapter.py +0 -221
- tests/unit/client/test_http_client.py +0 -206
- tests/unit/conftest.py +0 -54
- tests/unit/sdk/__init__.py +0 -1
- tests/unit/sdk/instrumentation_tester.py +0 -207
- tests/unit/sdk/test_attributes.py +0 -392
- tests/unit/sdk/test_concurrent_instrumentation.py +0 -468
- tests/unit/sdk/test_decorators.py +0 -763
- tests/unit/sdk/test_exporters.py +0 -241
- tests/unit/sdk/test_factory.py +0 -1188
- tests/unit/sdk/test_internal_span_processor.py +0 -397
- tests/unit/sdk/test_resource_attributes.py +0 -35
- tests/unit/test_config.py +0 -82
- tests/unit/test_context_manager.py +0 -777
- tests/unit/test_events.py +0 -27
- tests/unit/test_host_env.py +0 -54
- tests/unit/test_init_py.py +0 -501
- tests/unit/test_serialization.py +0 -433
- tests/unit/test_session.py +0 -676
- tests/unit/test_user_agent.py +0 -34
- tests/unit/test_validation.py +0 -405
- {tests → agentops/instrumentation/agentic/openai_agents/attributes}/__init__.py +0 -0
- /tests/unit/__init__.py → /agentops/instrumentation/providers/openai/attributes/tools.py +0 -0
- {mseep_agentops-0.4.18.dist-info → mseep_agentops-0.4.23.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,607 @@
|
|
1
|
+
from typing import List, Union
|
2
|
+
from agentops.logging import logger
|
3
|
+
from agentops.semconv import (
|
4
|
+
SpanAttributes,
|
5
|
+
MessageAttributes,
|
6
|
+
)
|
7
|
+
from agentops.instrumentation.common.attributes import (
|
8
|
+
AttributeMap,
|
9
|
+
IndexedAttributeMap,
|
10
|
+
_extract_attributes_from_mapping,
|
11
|
+
_extract_attributes_from_mapping_with_index,
|
12
|
+
)
|
13
|
+
|
14
|
+
try:
|
15
|
+
from openai.types.responses import (
|
16
|
+
FunctionTool,
|
17
|
+
WebSearchTool,
|
18
|
+
FileSearchTool,
|
19
|
+
ComputerTool,
|
20
|
+
Response,
|
21
|
+
ResponseUsage,
|
22
|
+
ResponseReasoningItem,
|
23
|
+
ResponseOutputMessage,
|
24
|
+
ResponseOutputText,
|
25
|
+
ResponseFunctionToolCall,
|
26
|
+
ResponseFunctionWebSearch,
|
27
|
+
ResponseFileSearchToolCall,
|
28
|
+
ResponseComputerToolCall,
|
29
|
+
)
|
30
|
+
from openai.types.responses.response_usage import InputTokensDetails, OutputTokensDetails
|
31
|
+
|
32
|
+
ToolTypes = Union[
|
33
|
+
FunctionTool,
|
34
|
+
WebSearchTool,
|
35
|
+
FileSearchTool,
|
36
|
+
]
|
37
|
+
ResponseOutputTypes = Union[
|
38
|
+
ResponseOutputMessage,
|
39
|
+
ResponseOutputText,
|
40
|
+
ResponseFunctionToolCall,
|
41
|
+
ResponseFunctionWebSearch,
|
42
|
+
ResponseComputerToolCall,
|
43
|
+
ResponseFileSearchToolCall,
|
44
|
+
]
|
45
|
+
except ImportError as e:
|
46
|
+
logger.debug(f"[agentops.instrumentation.openai_agents] Could not import OpenAI Agents SDK types: {e}")
|
47
|
+
|
48
|
+
|
49
|
+
RESPONSE_ATTRIBUTES: AttributeMap = {
|
50
|
+
# Response(
|
51
|
+
# id='resp_67ddd0196a4c81929f7e3783a80f18110b486458d6766f93',
|
52
|
+
# created_at=1742589977.0,
|
53
|
+
# error=None,
|
54
|
+
# incomplete_details=None,
|
55
|
+
# instructions='You are a helpful assistant...',
|
56
|
+
# metadata={},
|
57
|
+
# model='gpt-4o-2024-08-06',
|
58
|
+
# object='response',
|
59
|
+
# output=[
|
60
|
+
# ...
|
61
|
+
# ],
|
62
|
+
# parallel_tool_calls=True,
|
63
|
+
# temperature=1.0,
|
64
|
+
# tool_choice='auto',
|
65
|
+
# tools=[
|
66
|
+
# ...)
|
67
|
+
# ],
|
68
|
+
# top_p=1.0,
|
69
|
+
# max_output_tokens=None,
|
70
|
+
# previous_response_id=None,
|
71
|
+
# reasoning=Reasoning(
|
72
|
+
# ...
|
73
|
+
# ),
|
74
|
+
# status='completed',
|
75
|
+
# text=ResponseTextConfig(format=ResponseFormatText(type='text')),
|
76
|
+
# truncation='disabled',
|
77
|
+
# usage=ResponseUsage(
|
78
|
+
# ...
|
79
|
+
# ),
|
80
|
+
# user=None,
|
81
|
+
# store=True
|
82
|
+
# )
|
83
|
+
SpanAttributes.LLM_RESPONSE_ID: "id",
|
84
|
+
SpanAttributes.LLM_REQUEST_MODEL: "model",
|
85
|
+
SpanAttributes.LLM_RESPONSE_MODEL: "model",
|
86
|
+
SpanAttributes.LLM_OPENAI_RESPONSE_INSTRUCTIONS: "instructions",
|
87
|
+
SpanAttributes.LLM_REQUEST_MAX_TOKENS: "max_output_tokens",
|
88
|
+
SpanAttributes.LLM_REQUEST_TEMPERATURE: "temperature",
|
89
|
+
SpanAttributes.LLM_REQUEST_TOP_P: "top_p",
|
90
|
+
}
|
91
|
+
|
92
|
+
|
93
|
+
RESPONSE_TOOL_ATTRIBUTES: IndexedAttributeMap = {
|
94
|
+
# FunctionTool(
|
95
|
+
# name='get_weather',
|
96
|
+
# parameters={'properties': {'location': {'title': 'Location', 'type': 'string'}}, 'required': ['location'], 'title': 'get_weather_args', 'type': 'object', 'additionalProperties': False},
|
97
|
+
# strict=True,
|
98
|
+
# type='function',
|
99
|
+
# description='Get the current weather for a location.'
|
100
|
+
# )
|
101
|
+
MessageAttributes.TOOL_CALL_TYPE: "type",
|
102
|
+
MessageAttributes.TOOL_CALL_NAME: "name",
|
103
|
+
MessageAttributes.TOOL_CALL_DESCRIPTION: "description",
|
104
|
+
MessageAttributes.TOOL_CALL_ARGUMENTS: "parameters",
|
105
|
+
# TODO `strict` is not converted
|
106
|
+
}
|
107
|
+
|
108
|
+
|
109
|
+
RESPONSE_TOOL_WEB_SEARCH_ATTRIBUTES: IndexedAttributeMap = {
|
110
|
+
# WebSearchTool(
|
111
|
+
# type='web_search_preview',
|
112
|
+
# search_context_size='medium',
|
113
|
+
# user_location=UserLocation(
|
114
|
+
# type='approximate',
|
115
|
+
# city=None,
|
116
|
+
# country='US',
|
117
|
+
# region=None,
|
118
|
+
# timezone=None
|
119
|
+
# )
|
120
|
+
# )
|
121
|
+
MessageAttributes.TOOL_CALL_NAME: "type",
|
122
|
+
# `parameters` is added by the `get_response_tool_web_search_attributes` function,
|
123
|
+
# which contains `search_context_size` and `user_location`.
|
124
|
+
MessageAttributes.TOOL_CALL_ARGUMENTS: "parameters",
|
125
|
+
}
|
126
|
+
|
127
|
+
|
128
|
+
RESPONSE_TOOL_FILE_SEARCH_ATTRIBUTES: IndexedAttributeMap = {
|
129
|
+
# FileSearchTool(
|
130
|
+
# type='file_search',
|
131
|
+
# vector_store_ids=['store_123', 'store_456'],
|
132
|
+
# filters=Filters(
|
133
|
+
# key='value'
|
134
|
+
# ),
|
135
|
+
# max_num_results=10,
|
136
|
+
# ranking_options=RankingOptions(
|
137
|
+
# ranker='default-2024-11-15',
|
138
|
+
# score_threshold=0.8
|
139
|
+
# )
|
140
|
+
# )
|
141
|
+
MessageAttributes.TOOL_CALL_TYPE: "type",
|
142
|
+
# `parameters` is added by the `get_response_tool_file_search_attributes` function,
|
143
|
+
# which contains `vector_store_ids`, `filters`, `max_num_results`, and `ranking_options`.
|
144
|
+
MessageAttributes.TOOL_CALL_ARGUMENTS: "parameters",
|
145
|
+
}
|
146
|
+
|
147
|
+
|
148
|
+
RESPONSE_TOOL_COMPUTER_ATTRIBUTES: IndexedAttributeMap = {
|
149
|
+
# ComputerTool(
|
150
|
+
# display_height=1080.0,
|
151
|
+
# display_width=1920.0,
|
152
|
+
# environment='mac',
|
153
|
+
# type='computer_use_preview'
|
154
|
+
# )
|
155
|
+
MessageAttributes.TOOL_CALL_TYPE: "type",
|
156
|
+
# `parameters` is added by the `get_response_tool_computer_attributes` function,
|
157
|
+
# which contains `display_height`, `display_width`, `environment`, etc.
|
158
|
+
MessageAttributes.TOOL_CALL_ARGUMENTS: "parameters",
|
159
|
+
}
|
160
|
+
|
161
|
+
|
162
|
+
RESPONSE_OUTPUT_MESSAGE_ATTRIBUTES: IndexedAttributeMap = {
|
163
|
+
# ResponseOutputMessage(
|
164
|
+
# id='msg_67ddcad3b6008192b521035d8b71fc570db7bfce93fd916a',
|
165
|
+
# content=[
|
166
|
+
# ...
|
167
|
+
# ],
|
168
|
+
# role='assistant',
|
169
|
+
# status='completed',
|
170
|
+
# type='message'
|
171
|
+
# )
|
172
|
+
MessageAttributes.COMPLETION_ID: "id",
|
173
|
+
MessageAttributes.COMPLETION_TYPE: "type",
|
174
|
+
MessageAttributes.COMPLETION_ROLE: "role",
|
175
|
+
MessageAttributes.COMPLETION_FINISH_REASON: "status",
|
176
|
+
}
|
177
|
+
|
178
|
+
|
179
|
+
RESPONSE_OUTPUT_TEXT_ATTRIBUTES: IndexedAttributeMap = {
|
180
|
+
# ResponseOutputText(
|
181
|
+
# annotations=[],
|
182
|
+
# text='Recursion is a programming technique ...',
|
183
|
+
# type='output_text'
|
184
|
+
# )
|
185
|
+
MessageAttributes.COMPLETION_TYPE: "type",
|
186
|
+
MessageAttributes.COMPLETION_CONTENT: "text",
|
187
|
+
# TODO `annotations` are not converted
|
188
|
+
}
|
189
|
+
|
190
|
+
|
191
|
+
RESPONSE_OUTPUT_REASONING_ATTRIBUTES: IndexedAttributeMap = {
|
192
|
+
# ResponseReasoningItem(
|
193
|
+
# id='reasoning_12345',
|
194
|
+
# summary=[
|
195
|
+
# Summary(
|
196
|
+
# text='The model used a step-by-step approach to solve the problem.',
|
197
|
+
# type='summary_text'
|
198
|
+
# )
|
199
|
+
# ],
|
200
|
+
# type='reasoning',
|
201
|
+
# status='completed'
|
202
|
+
# )
|
203
|
+
MessageAttributes.COMPLETION_ID: "id",
|
204
|
+
MessageAttributes.COMPLETION_TYPE: "type",
|
205
|
+
MessageAttributes.COMPLETION_FINISH_REASON: "status",
|
206
|
+
# TODO `summary` is not converted
|
207
|
+
}
|
208
|
+
|
209
|
+
|
210
|
+
RESPONSE_OUTPUT_TOOL_ATTRIBUTES: IndexedAttributeMap = {
|
211
|
+
# ResponseFunctionToolCall(
|
212
|
+
# id='ftc_67ddcad3b6008192b521035d8b71fc570db7bfce93fd916a',
|
213
|
+
# arguments='{"location": "New York"}',
|
214
|
+
# call_id='call_12345',
|
215
|
+
# name='get_weather',
|
216
|
+
# type='function_call',
|
217
|
+
# status='completed'
|
218
|
+
# )
|
219
|
+
MessageAttributes.COMPLETION_TOOL_CALL_ID: "id",
|
220
|
+
MessageAttributes.COMPLETION_TOOL_CALL_TYPE: "type",
|
221
|
+
MessageAttributes.COMPLETION_TOOL_CALL_NAME: "name",
|
222
|
+
MessageAttributes.COMPLETION_TOOL_CALL_ARGUMENTS: "arguments",
|
223
|
+
# TODO `status` & `call_id` are not converted
|
224
|
+
}
|
225
|
+
|
226
|
+
|
227
|
+
RESPONSE_OUTPUT_TOOL_WEB_SEARCH_ATTRIBUTES: IndexedAttributeMap = {
|
228
|
+
# ResponseFunctionWebSearch(
|
229
|
+
# id='ws_67eda37a5f18819280bf8b64f315bfa70091ec39ac46b411',
|
230
|
+
# status='completed',
|
231
|
+
# type='web_search_call'
|
232
|
+
# )
|
233
|
+
MessageAttributes.COMPLETION_TOOL_CALL_ID: "id",
|
234
|
+
MessageAttributes.COMPLETION_TOOL_CALL_TYPE: "type",
|
235
|
+
MessageAttributes.COMPLETION_TOOL_CALL_STATUS: "status",
|
236
|
+
}
|
237
|
+
|
238
|
+
RESPONSE_OUTPUT_TOOL_WEB_SEARCH_URL_ANNOTATIONS: IndexedAttributeMap = {
|
239
|
+
# AnnotationURLCitation(
|
240
|
+
# end_index=747,
|
241
|
+
# start_index=553,
|
242
|
+
# title="You can now play a real-time AI-rendered Quake II in your browser",
|
243
|
+
# type='url_citation',
|
244
|
+
# url='https://www.tomshardware.com/video-games/you-can-now-play-a-real-time-ai-rendered-quake-ii-in-your-browser-microsofts-whamm-offers-generative-ai-for-games?utm_source=openai'
|
245
|
+
# )
|
246
|
+
MessageAttributes.COMPLETION_ANNOTATION_END_INDEX: "end_index",
|
247
|
+
MessageAttributes.COMPLETION_ANNOTATION_START_INDEX: "start_index",
|
248
|
+
MessageAttributes.COMPLETION_ANNOTATION_TITLE: "title",
|
249
|
+
MessageAttributes.COMPLETION_ANNOTATION_TYPE: "type",
|
250
|
+
MessageAttributes.COMPLETION_ANNOTATION_URL: "url",
|
251
|
+
}
|
252
|
+
|
253
|
+
|
254
|
+
RESPONSE_OUTPUT_TOOL_COMPUTER_ATTRIBUTES: IndexedAttributeMap = {
|
255
|
+
# ResponseComputerToolCall(
|
256
|
+
# id='comp_12345',
|
257
|
+
# action=Action(
|
258
|
+
# type='click',
|
259
|
+
# target='button_submit'
|
260
|
+
# ),
|
261
|
+
# call_id='call_67890',
|
262
|
+
# pending_safety_checks=[
|
263
|
+
# PendingSafetyCheck(
|
264
|
+
# type='check_type',
|
265
|
+
# status='pending'
|
266
|
+
# )
|
267
|
+
# ],
|
268
|
+
# status='completed',
|
269
|
+
# type='computer_call'
|
270
|
+
# )
|
271
|
+
# TODO semantic conventions for `ResponseComputerToolCall` are not defined yet
|
272
|
+
}
|
273
|
+
|
274
|
+
|
275
|
+
RESPONSE_OUTPUT_TOOL_FILE_SEARCH_ATTRIBUTES: IndexedAttributeMap = {
|
276
|
+
# ResponseFileSearchToolCall(
|
277
|
+
# id='fsc_12345',
|
278
|
+
# queries=['example query'],
|
279
|
+
# status='completed',
|
280
|
+
# type='file_search_call',
|
281
|
+
# results=[
|
282
|
+
# Result(
|
283
|
+
# attributes={'key1': 'value1', 'key2': 42},
|
284
|
+
# file_id='file_67890',
|
285
|
+
# filename='example.txt',
|
286
|
+
# score=0.95,
|
287
|
+
# text='Example text retrieved from the file.'
|
288
|
+
# ),
|
289
|
+
# ...
|
290
|
+
# ]
|
291
|
+
# )
|
292
|
+
# TODO semantic conventions for `ResponseFileSearchToolCall` are not defined yet
|
293
|
+
}
|
294
|
+
|
295
|
+
|
296
|
+
RESPONSE_USAGE_ATTRIBUTES: AttributeMap = {
|
297
|
+
SpanAttributes.LLM_USAGE_COMPLETION_TOKENS: "output_tokens",
|
298
|
+
SpanAttributes.LLM_USAGE_PROMPT_TOKENS: "input_tokens",
|
299
|
+
SpanAttributes.LLM_USAGE_TOTAL_TOKENS: "total_tokens",
|
300
|
+
}
|
301
|
+
|
302
|
+
|
303
|
+
# usage attributes are shared with `input_details_tokens` and `output_details_tokens`
|
304
|
+
RESPONSE_USAGE_DETAILS_ATTRIBUTES: AttributeMap = {
|
305
|
+
SpanAttributes.LLM_USAGE_CACHE_READ_INPUT_TOKENS: "cached_tokens",
|
306
|
+
SpanAttributes.LLM_USAGE_REASONING_TOKENS: "reasoning_tokens",
|
307
|
+
}
|
308
|
+
|
309
|
+
|
310
|
+
RESPONSE_REASONING_ATTRIBUTES: AttributeMap = {
|
311
|
+
# Reasoning(
|
312
|
+
# effort='medium',
|
313
|
+
# generate_summary=None,
|
314
|
+
# )
|
315
|
+
# TODO `effort` and `generate_summary` need semantic conventions
|
316
|
+
}
|
317
|
+
|
318
|
+
|
319
|
+
def get_response_kwarg_attributes(kwargs: dict) -> AttributeMap:
|
320
|
+
"""Handles interpretation of openai Responses.create method keyword arguments."""
|
321
|
+
|
322
|
+
# Just gather the attributes that are not present in the Response object
|
323
|
+
# TODO We could gather more here and have more context available in the
|
324
|
+
# event of an error during the request execution.
|
325
|
+
|
326
|
+
# Method signature for `Responses.create`:
|
327
|
+
# input: Union[str, ResponseInputParam],
|
328
|
+
# model: Union[str, ChatModel],
|
329
|
+
# include: Optional[List[ResponseIncludable]] | NotGiven = NOT_GIVEN,
|
330
|
+
# instructions: Optional[str] | NotGiven = NOT_GIVEN,
|
331
|
+
# max_output_tokens: Optional[int] | NotGiven = NOT_GIVEN,
|
332
|
+
# metadata: Optional[Metadata] | NotGiven = NOT_GIVEN,
|
333
|
+
# parallel_tool_calls: Optional[bool] | NotGiven = NOT_GIVEN,
|
334
|
+
# previous_response_id: Optional[str] | NotGiven = NOT_GIVEN,
|
335
|
+
# reasoning: Optional[Reasoning] | NotGiven = NOT_GIVEN,
|
336
|
+
# store: Optional[bool] | NotGiven = NOT_GIVEN,
|
337
|
+
# stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN,
|
338
|
+
# temperature: Optional[float] | NotGiven = NOT_GIVEN,
|
339
|
+
# text: ResponseTextConfigParam | NotGiven = NOT_GIVEN,
|
340
|
+
# tool_choice: response_create_params.ToolChoice | NotGiven = NOT_GIVEN,
|
341
|
+
# tools: Iterable[ToolParam] | NotGiven = NOT_GIVEN,
|
342
|
+
# top_p: Optional[float] | NotGiven = NOT_GIVEN,
|
343
|
+
# truncation: Optional[Literal["auto", "disabled"]] | NotGiven = NOT_GIVEN,
|
344
|
+
# user: str | NotGiven = NOT_GIVEN,
|
345
|
+
# extra_headers: Headers | None = None,
|
346
|
+
# extra_query: Query | None = None,
|
347
|
+
# extra_body: Body | None = None,
|
348
|
+
# timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
|
349
|
+
attributes = {}
|
350
|
+
|
351
|
+
# `input` can either be a `str` or a list of many internal types, so we duck
|
352
|
+
# type our way into some usable common attributes
|
353
|
+
_input: Union[str, list, None] = kwargs.get("input")
|
354
|
+
if isinstance(_input, str):
|
355
|
+
attributes[MessageAttributes.PROMPT_ROLE.format(i=0)] = "user"
|
356
|
+
attributes[MessageAttributes.PROMPT_CONTENT.format(i=0)] = _input
|
357
|
+
|
358
|
+
elif isinstance(_input, list):
|
359
|
+
for i, prompt in enumerate(_input):
|
360
|
+
# Object type is pretty diverse, so we handle common attributes, but do so
|
361
|
+
# conditionally because not all attributes are guaranteed to exist
|
362
|
+
if hasattr(prompt, "type"):
|
363
|
+
attributes[MessageAttributes.PROMPT_TYPE.format(i=i)] = prompt.type
|
364
|
+
if hasattr(prompt, "role"):
|
365
|
+
attributes[MessageAttributes.PROMPT_ROLE.format(i=i)] = prompt.role
|
366
|
+
if hasattr(prompt, "content"):
|
367
|
+
attributes[MessageAttributes.PROMPT_CONTENT.format(i=i)] = prompt.content
|
368
|
+
|
369
|
+
else:
|
370
|
+
logger.debug(f"[agentops.instrumentation.openai.response] '{type(_input)}' is not a recognized input type.")
|
371
|
+
|
372
|
+
# `model` is always `str` (`ChatModel` type is just a string literal)
|
373
|
+
attributes[SpanAttributes.LLM_REQUEST_MODEL] = str(kwargs.get("model"))
|
374
|
+
|
375
|
+
return attributes
|
376
|
+
|
377
|
+
|
378
|
+
# We call this `response_response` because in OpenAI Agents the `Response` is
|
379
|
+
# a return type from the `responses` module
|
380
|
+
def get_response_response_attributes(response: "Response") -> AttributeMap:
|
381
|
+
"""Handles interpretation of an openai Response object."""
|
382
|
+
attributes = _extract_attributes_from_mapping(response.__dict__, RESPONSE_ATTRIBUTES)
|
383
|
+
|
384
|
+
if response.output:
|
385
|
+
attributes.update(get_response_output_attributes(response.output))
|
386
|
+
|
387
|
+
if response.tools:
|
388
|
+
attributes.update(get_response_tools_attributes(response.tools))
|
389
|
+
|
390
|
+
if response.reasoning:
|
391
|
+
attributes.update(_extract_attributes_from_mapping(response.reasoning.__dict__, RESPONSE_REASONING_ATTRIBUTES))
|
392
|
+
|
393
|
+
if response.usage:
|
394
|
+
attributes.update(get_response_usage_attributes(response.usage))
|
395
|
+
|
396
|
+
return attributes
|
397
|
+
|
398
|
+
|
399
|
+
def get_response_output_attributes(output: List["ResponseOutputTypes"]) -> AttributeMap:
|
400
|
+
"""Handles interpretation of an openai Response `output` list."""
|
401
|
+
attributes = {}
|
402
|
+
|
403
|
+
for i, output_item in enumerate(output):
|
404
|
+
if isinstance(output_item, ResponseOutputMessage):
|
405
|
+
attributes.update(get_response_output_message_attributes(i, output_item))
|
406
|
+
|
407
|
+
elif isinstance(output_item, ResponseReasoningItem):
|
408
|
+
attributes.update(
|
409
|
+
_extract_attributes_from_mapping_with_index(output_item, RESPONSE_OUTPUT_REASONING_ATTRIBUTES, i)
|
410
|
+
)
|
411
|
+
|
412
|
+
elif isinstance(output_item, ResponseFunctionToolCall):
|
413
|
+
attributes.update(
|
414
|
+
_extract_attributes_from_mapping_with_index(output_item, RESPONSE_OUTPUT_TOOL_ATTRIBUTES, i=i, j=0)
|
415
|
+
)
|
416
|
+
|
417
|
+
elif isinstance(output_item, ResponseFunctionWebSearch):
|
418
|
+
attributes.update(
|
419
|
+
_extract_attributes_from_mapping_with_index(
|
420
|
+
output_item, RESPONSE_OUTPUT_TOOL_WEB_SEARCH_ATTRIBUTES, i=i, j=0
|
421
|
+
)
|
422
|
+
)
|
423
|
+
|
424
|
+
elif isinstance(output_item, ResponseComputerToolCall):
|
425
|
+
attributes.update(
|
426
|
+
_extract_attributes_from_mapping_with_index(
|
427
|
+
output_item, RESPONSE_OUTPUT_TOOL_COMPUTER_ATTRIBUTES, i=i, j=0
|
428
|
+
)
|
429
|
+
)
|
430
|
+
|
431
|
+
elif isinstance(output_item, ResponseFileSearchToolCall):
|
432
|
+
attributes.update(
|
433
|
+
_extract_attributes_from_mapping_with_index(
|
434
|
+
output_item, RESPONSE_OUTPUT_TOOL_FILE_SEARCH_ATTRIBUTES, i=i, j=0
|
435
|
+
)
|
436
|
+
)
|
437
|
+
|
438
|
+
else:
|
439
|
+
logger.debug(f"[agentops.instrumentation.openai.response] '{output_item}' is not a recognized output type.")
|
440
|
+
|
441
|
+
return attributes
|
442
|
+
|
443
|
+
|
444
|
+
def get_response_output_text_attributes(output_text: "ResponseOutputText", index: int) -> AttributeMap:
|
445
|
+
"""Handles interpretation of an openai ResponseOutputText object."""
|
446
|
+
# This function is a helper to handle the ResponseOutputText type specifically
|
447
|
+
attributes = _extract_attributes_from_mapping_with_index(output_text, RESPONSE_OUTPUT_TEXT_ATTRIBUTES, index)
|
448
|
+
|
449
|
+
if hasattr(output_text, "annotations"):
|
450
|
+
for j, output_text_annotation in enumerate(output_text.annotations):
|
451
|
+
attributes.update(
|
452
|
+
_extract_attributes_from_mapping_with_index(
|
453
|
+
output_text_annotation, RESPONSE_OUTPUT_TOOL_WEB_SEARCH_URL_ANNOTATIONS, i=index, j=j
|
454
|
+
)
|
455
|
+
)
|
456
|
+
|
457
|
+
return attributes
|
458
|
+
|
459
|
+
|
460
|
+
def get_response_output_message_attributes(index: int, message: "ResponseOutputMessage") -> AttributeMap:
|
461
|
+
"""Handles interpretation of an openai ResponseOutputMessage object."""
|
462
|
+
attributes = _extract_attributes_from_mapping_with_index(message, RESPONSE_OUTPUT_MESSAGE_ATTRIBUTES, index)
|
463
|
+
|
464
|
+
if message.content:
|
465
|
+
for i, content in enumerate(message.content):
|
466
|
+
if isinstance(content, ResponseOutputText):
|
467
|
+
attributes.update(get_response_output_text_attributes(content, i))
|
468
|
+
|
469
|
+
else:
|
470
|
+
logger.debug(
|
471
|
+
f"[agentops.instrumentation.openai.response] '{content}' is not a recognized content type."
|
472
|
+
)
|
473
|
+
|
474
|
+
return attributes
|
475
|
+
|
476
|
+
|
477
|
+
def get_response_tools_attributes(tools: List["ToolTypes"]) -> AttributeMap:
|
478
|
+
"""Handles interpretation of openai Response `tools` list."""
|
479
|
+
attributes = {}
|
480
|
+
|
481
|
+
for i, tool in enumerate(tools):
|
482
|
+
if isinstance(tool, FunctionTool):
|
483
|
+
attributes.update(_extract_attributes_from_mapping_with_index(tool, RESPONSE_TOOL_ATTRIBUTES, i))
|
484
|
+
|
485
|
+
elif isinstance(tool, WebSearchTool):
|
486
|
+
attributes.update(get_response_tool_web_search_attributes(tool, i))
|
487
|
+
|
488
|
+
elif isinstance(tool, FileSearchTool):
|
489
|
+
attributes.update(get_response_tool_file_search_attributes(tool, i))
|
490
|
+
|
491
|
+
elif isinstance(tool, ComputerTool):
|
492
|
+
attributes.update(get_response_tool_computer_attributes(tool, i))
|
493
|
+
|
494
|
+
else:
|
495
|
+
logger.debug(f"[agentops.instrumentation.openai.response] '{tool}' is not a recognized tool type.")
|
496
|
+
|
497
|
+
return attributes
|
498
|
+
|
499
|
+
|
500
|
+
def get_response_tool_web_search_attributes(tool: "WebSearchTool", index: int) -> AttributeMap:
|
501
|
+
"""Handles interpretation of an openai WebSearchTool object."""
|
502
|
+
parameters = {}
|
503
|
+
if hasattr(tool, "search_context_size"):
|
504
|
+
parameters["search_context_size"] = tool.search_context_size
|
505
|
+
|
506
|
+
if hasattr(tool, "user_location"):
|
507
|
+
parameters["user_location"] = tool.user_location.__dict__
|
508
|
+
|
509
|
+
tool_data = tool.__dict__
|
510
|
+
if parameters:
|
511
|
+
# add parameters to the tool_data dict so we can format them with the other attributes
|
512
|
+
tool_data["parameters"] = parameters
|
513
|
+
|
514
|
+
return _extract_attributes_from_mapping_with_index(tool_data, RESPONSE_TOOL_WEB_SEARCH_ATTRIBUTES, index)
|
515
|
+
|
516
|
+
|
517
|
+
def get_response_tool_file_search_attributes(tool: "FileSearchTool", index: int) -> AttributeMap:
|
518
|
+
"""Handles interpretation of an openai FileSearchTool object."""
|
519
|
+
parameters = {}
|
520
|
+
|
521
|
+
if hasattr(tool, "vector_store_ids"):
|
522
|
+
parameters["vector_store_ids"] = tool.vector_store_ids
|
523
|
+
|
524
|
+
if hasattr(tool, "filters"):
|
525
|
+
parameters["filters"] = tool.filters.__dict__
|
526
|
+
|
527
|
+
if hasattr(tool, "max_num_results"):
|
528
|
+
parameters["max_num_results"] = tool.max_num_results
|
529
|
+
|
530
|
+
if hasattr(tool, "ranking_options"):
|
531
|
+
parameters["ranking_options"] = tool.ranking_options.__dict__
|
532
|
+
|
533
|
+
tool_data = tool.__dict__
|
534
|
+
if parameters:
|
535
|
+
# add parameters to the tool_data dict so we can format them with the other attributes
|
536
|
+
tool_data["parameters"] = parameters
|
537
|
+
|
538
|
+
return _extract_attributes_from_mapping_with_index(tool_data, RESPONSE_TOOL_FILE_SEARCH_ATTRIBUTES, index)
|
539
|
+
|
540
|
+
|
541
|
+
def get_response_tool_computer_attributes(tool: "ComputerTool", index: int) -> AttributeMap:
|
542
|
+
"""Handles interpretation of an openai ComputerTool object."""
|
543
|
+
parameters = {}
|
544
|
+
|
545
|
+
if hasattr(tool, "display_height"):
|
546
|
+
parameters["display_height"] = tool.display_height
|
547
|
+
|
548
|
+
if hasattr(tool, "display_width"):
|
549
|
+
parameters["display_width"] = tool.display_width
|
550
|
+
|
551
|
+
if hasattr(tool, "environment"):
|
552
|
+
parameters["environment"] = tool.environment
|
553
|
+
|
554
|
+
tool_data = tool.__dict__
|
555
|
+
if parameters:
|
556
|
+
# add parameters to the tool_data dict so we can format them with the other attributes
|
557
|
+
tool_data["parameters"] = parameters
|
558
|
+
|
559
|
+
return _extract_attributes_from_mapping_with_index(tool_data, RESPONSE_TOOL_COMPUTER_ATTRIBUTES, index)
|
560
|
+
|
561
|
+
|
562
|
+
def get_response_usage_attributes(usage: "ResponseUsage") -> AttributeMap:
|
563
|
+
"""Handles interpretation of an openai ResponseUsage object."""
|
564
|
+
# ResponseUsage(
|
565
|
+
# input_tokens=0,
|
566
|
+
# output_tokens=0,
|
567
|
+
# output_tokens_details=OutputTokensDetails(reasoning_tokens=0),
|
568
|
+
# total_tokens=0,
|
569
|
+
# input_tokens_details={'cached_tokens': 0}
|
570
|
+
# )
|
571
|
+
attributes = {}
|
572
|
+
|
573
|
+
attributes.update(_extract_attributes_from_mapping(usage.__dict__, RESPONSE_USAGE_ATTRIBUTES))
|
574
|
+
|
575
|
+
# input_tokens_details is an `InputTokensDetails` object or `dict` if it exists
|
576
|
+
if hasattr(usage, "input_tokens_details"):
|
577
|
+
input_details = usage.input_tokens_details
|
578
|
+
if input_details is None:
|
579
|
+
pass
|
580
|
+
|
581
|
+
elif isinstance(input_details, InputTokensDetails):
|
582
|
+
attributes.update(
|
583
|
+
_extract_attributes_from_mapping(input_details.__dict__, RESPONSE_USAGE_DETAILS_ATTRIBUTES)
|
584
|
+
)
|
585
|
+
|
586
|
+
elif isinstance(input_details, dict): # openai-agents often returns a dict for some reason.
|
587
|
+
attributes.update(_extract_attributes_from_mapping(input_details, RESPONSE_USAGE_DETAILS_ATTRIBUTES))
|
588
|
+
|
589
|
+
else:
|
590
|
+
logger.debug(
|
591
|
+
f"[agentops.instrumentation.openai.response] '{input_details}' is not a recognized input details type."
|
592
|
+
)
|
593
|
+
|
594
|
+
# output_tokens_details is an `OutputTokensDetails` object
|
595
|
+
output_details = usage.output_tokens_details
|
596
|
+
if output_details is None:
|
597
|
+
pass
|
598
|
+
|
599
|
+
elif isinstance(output_details, OutputTokensDetails):
|
600
|
+
attributes.update(_extract_attributes_from_mapping(output_details.__dict__, RESPONSE_USAGE_DETAILS_ATTRIBUTES))
|
601
|
+
|
602
|
+
else:
|
603
|
+
logger.debug(
|
604
|
+
f"[agentops.instrumentation.openai.response] '{output_details}' is not a recognized output details type."
|
605
|
+
)
|
606
|
+
|
607
|
+
return attributes
|
@@ -0,0 +1,36 @@
|
|
1
|
+
"""Configuration for OpenAI instrumentation.
|
2
|
+
|
3
|
+
This module provides a global configuration object that can be used to customize
|
4
|
+
the behavior of OpenAI instrumentation across all components.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from typing import Callable, Optional, Dict
|
8
|
+
from typing_extensions import Protocol
|
9
|
+
|
10
|
+
|
11
|
+
class UploadImageCallable(Protocol):
|
12
|
+
"""Protocol for the upload_base64_image function."""
|
13
|
+
|
14
|
+
async def __call__(self, trace_id: str, span_id: str, image_name: str, base64_string: str) -> str:
|
15
|
+
"""Upload a base64 image and return the URL."""
|
16
|
+
...
|
17
|
+
|
18
|
+
|
19
|
+
class Config:
|
20
|
+
"""Global configuration for OpenAI instrumentation.
|
21
|
+
|
22
|
+
Attributes:
|
23
|
+
enrich_token_usage: Whether to calculate token usage for streaming responses
|
24
|
+
enrich_assistant: Whether to enrich assistant responses with additional data
|
25
|
+
exception_logger: Optional function to log exceptions
|
26
|
+
get_common_metrics_attributes: Function to get common attributes for metrics
|
27
|
+
upload_base64_image: Optional async function to upload base64 images
|
28
|
+
enable_trace_context_propagation: Whether to propagate trace context in headers
|
29
|
+
"""
|
30
|
+
|
31
|
+
enrich_token_usage: bool = True
|
32
|
+
enrich_assistant: bool = True
|
33
|
+
exception_logger: Optional[Callable[[Exception], None]] = None
|
34
|
+
get_common_metrics_attributes: Callable[[], Dict[str, str]] = lambda: {}
|
35
|
+
upload_base64_image: Optional[UploadImageCallable] = None
|
36
|
+
enable_trace_context_propagation: bool = True
|