monocle-apptrace 0.4.2__py3-none-any.whl → 0.5.0b1__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.
Potentially problematic release.
This version of monocle-apptrace might be problematic. Click here for more details.
- monocle_apptrace/__main__.py +1 -1
- monocle_apptrace/exporters/file_exporter.py +123 -36
- monocle_apptrace/instrumentation/common/__init__.py +16 -1
- monocle_apptrace/instrumentation/common/constants.py +6 -1
- monocle_apptrace/instrumentation/common/instrumentor.py +19 -152
- monocle_apptrace/instrumentation/common/method_wrappers.py +380 -0
- monocle_apptrace/instrumentation/common/span_handler.py +39 -24
- monocle_apptrace/instrumentation/common/utils.py +20 -14
- monocle_apptrace/instrumentation/common/wrapper.py +10 -9
- monocle_apptrace/instrumentation/common/wrapper_method.py +39 -1
- monocle_apptrace/instrumentation/metamodel/a2a/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/a2a/_helper.py +37 -0
- monocle_apptrace/instrumentation/metamodel/a2a/entities/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/a2a/entities/inference.py +112 -0
- monocle_apptrace/instrumentation/metamodel/a2a/methods.py +22 -0
- monocle_apptrace/instrumentation/metamodel/aiohttp/_helper.py +6 -11
- monocle_apptrace/instrumentation/metamodel/anthropic/_helper.py +35 -18
- monocle_apptrace/instrumentation/metamodel/anthropic/entities/inference.py +14 -10
- monocle_apptrace/instrumentation/metamodel/azfunc/_helper.py +13 -11
- monocle_apptrace/instrumentation/metamodel/azfunc/entities/http.py +5 -0
- monocle_apptrace/instrumentation/metamodel/azureaiinference/_helper.py +88 -8
- monocle_apptrace/instrumentation/metamodel/azureaiinference/entities/inference.py +22 -8
- monocle_apptrace/instrumentation/metamodel/botocore/_helper.py +92 -16
- monocle_apptrace/instrumentation/metamodel/botocore/entities/inference.py +13 -8
- monocle_apptrace/instrumentation/metamodel/botocore/handlers/botocore_span_handler.py +1 -1
- monocle_apptrace/instrumentation/metamodel/fastapi/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/fastapi/_helper.py +82 -0
- monocle_apptrace/instrumentation/metamodel/fastapi/entities/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/fastapi/entities/http.py +44 -0
- monocle_apptrace/instrumentation/metamodel/fastapi/methods.py +23 -0
- monocle_apptrace/instrumentation/metamodel/finish_types.py +387 -0
- monocle_apptrace/instrumentation/metamodel/flask/_helper.py +6 -11
- monocle_apptrace/instrumentation/metamodel/gemini/_helper.py +51 -7
- monocle_apptrace/instrumentation/metamodel/gemini/entities/inference.py +17 -9
- monocle_apptrace/instrumentation/metamodel/gemini/entities/retrieval.py +43 -0
- monocle_apptrace/instrumentation/metamodel/gemini/methods.py +10 -0
- monocle_apptrace/instrumentation/metamodel/haystack/_helper.py +15 -8
- monocle_apptrace/instrumentation/metamodel/haystack/entities/inference.py +5 -10
- monocle_apptrace/instrumentation/metamodel/haystack/methods.py +7 -0
- monocle_apptrace/instrumentation/metamodel/lambdafunc/_helper.py +78 -0
- monocle_apptrace/instrumentation/metamodel/lambdafunc/entities/http.py +51 -0
- monocle_apptrace/instrumentation/metamodel/lambdafunc/methods.py +23 -0
- monocle_apptrace/instrumentation/metamodel/lambdafunc/wrapper.py +23 -0
- monocle_apptrace/instrumentation/metamodel/langchain/_helper.py +127 -19
- monocle_apptrace/instrumentation/metamodel/langchain/entities/inference.py +15 -10
- monocle_apptrace/instrumentation/metamodel/langgraph/_helper.py +67 -10
- monocle_apptrace/instrumentation/metamodel/langgraph/entities/inference.py +127 -20
- monocle_apptrace/instrumentation/metamodel/langgraph/langgraph_processor.py +43 -0
- monocle_apptrace/instrumentation/metamodel/langgraph/methods.py +29 -5
- monocle_apptrace/instrumentation/metamodel/llamaindex/_helper.py +227 -16
- monocle_apptrace/instrumentation/metamodel/llamaindex/entities/agent.py +127 -10
- monocle_apptrace/instrumentation/metamodel/llamaindex/entities/inference.py +13 -8
- monocle_apptrace/instrumentation/metamodel/llamaindex/llamaindex_processor.py +51 -0
- monocle_apptrace/instrumentation/metamodel/llamaindex/methods.py +68 -1
- monocle_apptrace/instrumentation/metamodel/mcp/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/mcp/_helper.py +118 -0
- monocle_apptrace/instrumentation/metamodel/mcp/entities/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/mcp/entities/inference.py +48 -0
- monocle_apptrace/instrumentation/metamodel/mcp/mcp_processor.py +13 -0
- monocle_apptrace/instrumentation/metamodel/mcp/methods.py +21 -0
- monocle_apptrace/instrumentation/metamodel/openai/_helper.py +83 -16
- monocle_apptrace/instrumentation/metamodel/openai/entities/inference.py +103 -92
- monocle_apptrace/instrumentation/metamodel/openai/entities/retrieval.py +1 -1
- monocle_apptrace/instrumentation/metamodel/teamsai/_helper.py +41 -22
- monocle_apptrace/instrumentation/metamodel/teamsai/entities/inference/actionplanner_output_processor.py +1 -1
- monocle_apptrace/instrumentation/metamodel/teamsai/entities/inference/teamsai_output_processor.py +5 -9
- monocle_apptrace/instrumentation/metamodel/teamsai/sample.json +0 -4
- {monocle_apptrace-0.4.2.dist-info → monocle_apptrace-0.5.0b1.dist-info}/METADATA +14 -3
- {monocle_apptrace-0.4.2.dist-info → monocle_apptrace-0.5.0b1.dist-info}/RECORD +72 -47
- {monocle_apptrace-0.4.2.dist-info → monocle_apptrace-0.5.0b1.dist-info}/WHEEL +0 -0
- {monocle_apptrace-0.4.2.dist-info → monocle_apptrace-0.5.0b1.dist-info}/licenses/LICENSE +0 -0
- {monocle_apptrace-0.4.2.dist-info → monocle_apptrace-0.5.0b1.dist-info}/licenses/NOTICE +0 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides common finish reason mappings and finish type enums
|
|
3
|
+
for different AI providers (OpenAI, Anthropic, Gemini, LangChain, LlamaIndex, Azure AI Inference).
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from enum import Enum
|
|
7
|
+
|
|
8
|
+
class FinishType(Enum):
|
|
9
|
+
"""Enum for standardized finish types across all AI providers."""
|
|
10
|
+
SUCCESS = "success"
|
|
11
|
+
TRUNCATED = "truncated"
|
|
12
|
+
CONTENT_FILTER = "content_filter"
|
|
13
|
+
ERROR = "error"
|
|
14
|
+
REFUSAL = "refusal"
|
|
15
|
+
RATE_LIMITED = "rate_limited"
|
|
16
|
+
|
|
17
|
+
# OpenAI finish reason mapping
|
|
18
|
+
OPENAI_FINISH_REASON_MAPPING = {
|
|
19
|
+
"stop": FinishType.SUCCESS.value,
|
|
20
|
+
"tool_calls": FinishType.SUCCESS.value,
|
|
21
|
+
"function_call": FinishType.SUCCESS.value, # deprecated but still possible
|
|
22
|
+
"length": FinishType.TRUNCATED.value,
|
|
23
|
+
"content_filter": FinishType.CONTENT_FILTER.value
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
# Anthropic finish reason mapping
|
|
27
|
+
ANTHROPIC_FINISH_REASON_MAPPING = {
|
|
28
|
+
"end_turn": FinishType.SUCCESS.value, # Natural completion
|
|
29
|
+
"max_tokens": FinishType.TRUNCATED.value, # Hit max_tokens limit
|
|
30
|
+
"stop_sequence": FinishType.SUCCESS.value, # Hit user stop sequence
|
|
31
|
+
"tool_use": FinishType.SUCCESS.value, # Tool use triggered
|
|
32
|
+
"pause_turn": FinishType.SUCCESS.value, # Paused for tool or server action
|
|
33
|
+
"refusal": FinishType.REFUSAL.value, # Refused for safety/ethics
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# Gemini finish reason mapping
|
|
37
|
+
GEMINI_FINISH_REASON_MAPPING = {
|
|
38
|
+
"STOP": FinishType.SUCCESS.value,
|
|
39
|
+
"MAX_TOKENS": FinishType.TRUNCATED.value,
|
|
40
|
+
"SAFETY": FinishType.CONTENT_FILTER.value,
|
|
41
|
+
"RECITATION": FinishType.CONTENT_FILTER.value,
|
|
42
|
+
"OTHER": FinishType.ERROR.value,
|
|
43
|
+
"FINISH_REASON_UNSPECIFIED": None
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# LlamaIndex finish reason mapping
|
|
47
|
+
# LlamaIndex often wraps underlying provider responses, similar to LangChain
|
|
48
|
+
LLAMAINDEX_FINISH_REASON_MAPPING = {
|
|
49
|
+
# Standard completion reasons
|
|
50
|
+
"stop": FinishType.SUCCESS.value,
|
|
51
|
+
"complete": FinishType.SUCCESS.value,
|
|
52
|
+
"finished": FinishType.SUCCESS.value,
|
|
53
|
+
"success": FinishType.SUCCESS.value,
|
|
54
|
+
|
|
55
|
+
# Token limits
|
|
56
|
+
"length": FinishType.TRUNCATED.value,
|
|
57
|
+
"max_tokens": FinishType.TRUNCATED.value,
|
|
58
|
+
"token_limit": FinishType.TRUNCATED.value,
|
|
59
|
+
"truncated": FinishType.TRUNCATED.value,
|
|
60
|
+
|
|
61
|
+
# Tool/function calling
|
|
62
|
+
"tool_calls": FinishType.SUCCESS.value,
|
|
63
|
+
"function_call": FinishType.SUCCESS.value,
|
|
64
|
+
"agent_finish": FinishType.SUCCESS.value,
|
|
65
|
+
|
|
66
|
+
# Content filtering and safety
|
|
67
|
+
"content_filter": FinishType.CONTENT_FILTER.value,
|
|
68
|
+
"safety": FinishType.CONTENT_FILTER.value,
|
|
69
|
+
"filtered": FinishType.CONTENT_FILTER.value,
|
|
70
|
+
|
|
71
|
+
# Errors
|
|
72
|
+
"error": FinishType.ERROR.value,
|
|
73
|
+
"failed": FinishType.ERROR.value,
|
|
74
|
+
"exception": FinishType.ERROR.value,
|
|
75
|
+
|
|
76
|
+
# Provider-specific reasons that might pass through LlamaIndex
|
|
77
|
+
# OpenAI reasons
|
|
78
|
+
"end_turn": FinishType.SUCCESS.value, # Anthropic
|
|
79
|
+
"stop_sequence": FinishType.SUCCESS.value, # Anthropic
|
|
80
|
+
"STOP": FinishType.SUCCESS.value, # Gemini
|
|
81
|
+
"SAFETY": FinishType.CONTENT_FILTER.value, # Gemini
|
|
82
|
+
"RECITATION": FinishType.CONTENT_FILTER.value, # Gemini
|
|
83
|
+
"OTHER": FinishType.ERROR.value, # Gemini
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# Azure AI Inference finish reason mapping
|
|
87
|
+
AZURE_AI_INFERENCE_FINISH_REASON_MAPPING = {
|
|
88
|
+
# Standard completion reasons
|
|
89
|
+
"stop": FinishType.SUCCESS.value,
|
|
90
|
+
"completed": FinishType.SUCCESS.value,
|
|
91
|
+
"finished": FinishType.SUCCESS.value,
|
|
92
|
+
|
|
93
|
+
# Token limits
|
|
94
|
+
"length": FinishType.TRUNCATED.value,
|
|
95
|
+
"max_tokens": FinishType.TRUNCATED.value,
|
|
96
|
+
"token_limit": FinishType.TRUNCATED.value,
|
|
97
|
+
"max_completion_tokens": FinishType.TRUNCATED.value,
|
|
98
|
+
|
|
99
|
+
# Tool/function calling
|
|
100
|
+
"tool_calls": FinishType.SUCCESS.value,
|
|
101
|
+
"function_call": FinishType.SUCCESS.value,
|
|
102
|
+
|
|
103
|
+
# Content filtering and safety
|
|
104
|
+
"content_filter": FinishType.CONTENT_FILTER.value,
|
|
105
|
+
"content_filtered": FinishType.CONTENT_FILTER.value,
|
|
106
|
+
"safety": FinishType.CONTENT_FILTER.value,
|
|
107
|
+
"responsible_ai_policy": FinishType.CONTENT_FILTER.value,
|
|
108
|
+
|
|
109
|
+
# Errors
|
|
110
|
+
"error": FinishType.ERROR.value,
|
|
111
|
+
"failed": FinishType.ERROR.value,
|
|
112
|
+
"exception": FinishType.ERROR.value,
|
|
113
|
+
"timeout": FinishType.ERROR.value,
|
|
114
|
+
|
|
115
|
+
# Azure-specific reasons
|
|
116
|
+
"model_error": FinishType.ERROR.value,
|
|
117
|
+
"service_unavailable": FinishType.ERROR.value,
|
|
118
|
+
"rate_limit": FinishType.ERROR.value,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# AWS Bedrock finish reason mapping
|
|
122
|
+
# Based on AWS Bedrock Converse API and model-specific APIs
|
|
123
|
+
BEDROCK_FINISH_REASON_MAPPING = {
|
|
124
|
+
# Standard completion reasons
|
|
125
|
+
"end_turn": FinishType.SUCCESS.value, # Natural completion
|
|
126
|
+
"stop": FinishType.SUCCESS.value, # Hit stop sequence
|
|
127
|
+
"stop_sequence": FinishType.SUCCESS.value, # Stop sequence triggered
|
|
128
|
+
"completed": FinishType.SUCCESS.value, # Completion finished successfully
|
|
129
|
+
|
|
130
|
+
# Token limits
|
|
131
|
+
"max_tokens": FinishType.TRUNCATED.value, # Hit max_tokens limit
|
|
132
|
+
"length": FinishType.TRUNCATED.value, # Token length limit
|
|
133
|
+
"max_length": FinishType.TRUNCATED.value, # Maximum length reached
|
|
134
|
+
"token_limit": FinishType.TRUNCATED.value, # Token limit reached
|
|
135
|
+
|
|
136
|
+
# Tool/function calling
|
|
137
|
+
"tool_use": FinishType.SUCCESS.value, # Tool use triggered
|
|
138
|
+
"function_call": FinishType.SUCCESS.value, # Function call triggered
|
|
139
|
+
|
|
140
|
+
# Content filtering and safety
|
|
141
|
+
"content_filter": FinishType.CONTENT_FILTER.value, # Content filtered
|
|
142
|
+
"content_filtered": FinishType.CONTENT_FILTER.value, # Content was filtered
|
|
143
|
+
"safety": FinishType.CONTENT_FILTER.value, # Safety filter triggered
|
|
144
|
+
"guardrails": FinishType.CONTENT_FILTER.value, # Bedrock guardrails triggered
|
|
145
|
+
"blocked": FinishType.CONTENT_FILTER.value, # Request blocked
|
|
146
|
+
|
|
147
|
+
# Errors
|
|
148
|
+
"error": FinishType.ERROR.value, # General error
|
|
149
|
+
"failed": FinishType.ERROR.value, # Request failed
|
|
150
|
+
"exception": FinishType.ERROR.value, # Exception occurred
|
|
151
|
+
"timeout": FinishType.ERROR.value, # Request timeout
|
|
152
|
+
"model_error": FinishType.ERROR.value, # Model-specific error
|
|
153
|
+
"service_unavailable": FinishType.ERROR.value, # Service unavailable
|
|
154
|
+
"throttled": FinishType.ERROR.value, # Request throttled
|
|
155
|
+
"rate_limit": FinishType.ERROR.value, # Rate limit exceeded
|
|
156
|
+
"validation_error": FinishType.ERROR.value, # Validation error
|
|
157
|
+
|
|
158
|
+
# Model-specific reasons (various Bedrock models)
|
|
159
|
+
# Claude models via Bedrock
|
|
160
|
+
"end_turn": FinishType.SUCCESS.value, # Already defined above
|
|
161
|
+
"max_tokens": FinishType.TRUNCATED.value, # Already defined above
|
|
162
|
+
"stop_sequence": FinishType.SUCCESS.value, # Already defined above
|
|
163
|
+
"tool_use": FinishType.SUCCESS.value, # Already defined above
|
|
164
|
+
|
|
165
|
+
# AI21 models via Bedrock
|
|
166
|
+
"endoftext": FinishType.SUCCESS.value, # AI21 end of text
|
|
167
|
+
"length": FinishType.TRUNCATED.value, # AI21 length limit
|
|
168
|
+
|
|
169
|
+
# Cohere models via Bedrock
|
|
170
|
+
"COMPLETE": FinishType.SUCCESS.value, # Cohere completion
|
|
171
|
+
"MAX_TOKENS": FinishType.TRUNCATED.value, # Cohere max tokens
|
|
172
|
+
"ERROR": FinishType.ERROR.value, # Cohere error
|
|
173
|
+
|
|
174
|
+
# Meta Llama models via Bedrock
|
|
175
|
+
"stop": FinishType.SUCCESS.value, # Already defined above
|
|
176
|
+
"length": FinishType.TRUNCATED.value, # Already defined above
|
|
177
|
+
|
|
178
|
+
# Amazon Titan models via Bedrock
|
|
179
|
+
"FINISH": FinishType.SUCCESS.value, # Titan finish
|
|
180
|
+
"LENGTH": FinishType.TRUNCATED.value, # Titan length limit
|
|
181
|
+
"CONTENT_FILTERED": FinishType.CONTENT_FILTER.value, # Titan content filter
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
# LangChain finish reason mapping
|
|
185
|
+
# LangChain often wraps underlying provider responses, so we include common finish reasons
|
|
186
|
+
# that might appear in LangChain response objects
|
|
187
|
+
LANGCHAIN_FINISH_REASON_MAPPING = {
|
|
188
|
+
# Standard completion reasons
|
|
189
|
+
"stop": FinishType.SUCCESS.value,
|
|
190
|
+
"complete": FinishType.SUCCESS.value,
|
|
191
|
+
"finished": FinishType.SUCCESS.value,
|
|
192
|
+
|
|
193
|
+
# Token limits
|
|
194
|
+
"length": FinishType.TRUNCATED.value,
|
|
195
|
+
"max_tokens": FinishType.TRUNCATED.value,
|
|
196
|
+
"token_limit": FinishType.TRUNCATED.value,
|
|
197
|
+
|
|
198
|
+
# Tool/function calling
|
|
199
|
+
"tool_calls": FinishType.SUCCESS.value,
|
|
200
|
+
"function_call": FinishType.SUCCESS.value,
|
|
201
|
+
|
|
202
|
+
# Content filtering and safety
|
|
203
|
+
"content_filter": FinishType.CONTENT_FILTER.value,
|
|
204
|
+
"safety": FinishType.CONTENT_FILTER.value,
|
|
205
|
+
"filtered": FinishType.CONTENT_FILTER.value,
|
|
206
|
+
|
|
207
|
+
# Errors
|
|
208
|
+
"error": FinishType.ERROR.value,
|
|
209
|
+
"failed": FinishType.ERROR.value,
|
|
210
|
+
"exception": FinishType.ERROR.value,
|
|
211
|
+
|
|
212
|
+
# Provider-specific reasons that might pass through LangChain
|
|
213
|
+
# OpenAI reasons
|
|
214
|
+
"stop": FinishType.SUCCESS.value, # Already defined above
|
|
215
|
+
|
|
216
|
+
# Anthropic reasons
|
|
217
|
+
"end_turn": FinishType.SUCCESS.value,
|
|
218
|
+
"stop_sequence": FinishType.SUCCESS.value,
|
|
219
|
+
|
|
220
|
+
# Gemini reasons
|
|
221
|
+
"STOP": FinishType.SUCCESS.value,
|
|
222
|
+
"SAFETY": FinishType.CONTENT_FILTER.value,
|
|
223
|
+
"RECITATION": FinishType.CONTENT_FILTER.value,
|
|
224
|
+
"OTHER": FinishType.ERROR.value,
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
TEAMSAI_FINISH_REASON_MAPPING = {
|
|
228
|
+
"success": FinishType.SUCCESS.value,
|
|
229
|
+
"error": FinishType.ERROR.value,
|
|
230
|
+
"too_long": FinishType.TRUNCATED.value,
|
|
231
|
+
"rate_limited": FinishType.RATE_LIMITED.value,
|
|
232
|
+
"invalid_response": FinishType.ERROR.value,
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
def map_openai_finish_reason_to_finish_type(finish_reason):
|
|
236
|
+
"""Map OpenAI finish_reason to standardized finish_type."""
|
|
237
|
+
if not finish_reason:
|
|
238
|
+
return None
|
|
239
|
+
return OPENAI_FINISH_REASON_MAPPING.get(finish_reason, None)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def map_anthropic_finish_reason_to_finish_type(finish_reason):
|
|
243
|
+
"""Map Anthropic stop_reason to standardized finish_type."""
|
|
244
|
+
if not finish_reason:
|
|
245
|
+
return None
|
|
246
|
+
return ANTHROPIC_FINISH_REASON_MAPPING.get(finish_reason, None)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def map_gemini_finish_reason_to_finish_type(finish_reason):
|
|
250
|
+
"""Map Gemini finish_reason to standardized finish_type."""
|
|
251
|
+
if not finish_reason:
|
|
252
|
+
return None
|
|
253
|
+
return GEMINI_FINISH_REASON_MAPPING.get(finish_reason, None)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def map_langchain_finish_reason_to_finish_type(finish_reason):
|
|
257
|
+
"""Map LangChain finish_reason to standardized finish_type."""
|
|
258
|
+
if not finish_reason:
|
|
259
|
+
return None
|
|
260
|
+
|
|
261
|
+
# Convert to lowercase for case-insensitive matching
|
|
262
|
+
finish_reason_lower = finish_reason.lower() if isinstance(finish_reason, str) else str(finish_reason).lower()
|
|
263
|
+
|
|
264
|
+
# Try direct mapping first
|
|
265
|
+
if finish_reason in LANGCHAIN_FINISH_REASON_MAPPING:
|
|
266
|
+
return LANGCHAIN_FINISH_REASON_MAPPING[finish_reason]
|
|
267
|
+
|
|
268
|
+
# Try lowercase mapping
|
|
269
|
+
if finish_reason_lower in LANGCHAIN_FINISH_REASON_MAPPING:
|
|
270
|
+
return LANGCHAIN_FINISH_REASON_MAPPING[finish_reason_lower]
|
|
271
|
+
|
|
272
|
+
# If no direct mapping, try to infer from common patterns
|
|
273
|
+
if any(keyword in finish_reason_lower for keyword in ['stop', 'complete', 'success', 'done']):
|
|
274
|
+
return FinishType.SUCCESS.value
|
|
275
|
+
elif any(keyword in finish_reason_lower for keyword in ['length', 'token', 'limit', 'truncat']):
|
|
276
|
+
return FinishType.TRUNCATED.value
|
|
277
|
+
elif any(keyword in finish_reason_lower for keyword in ['filter', 'safety', 'block']):
|
|
278
|
+
return FinishType.CONTENT_FILTER.value
|
|
279
|
+
elif any(keyword in finish_reason_lower for keyword in ['error', 'fail', 'exception']):
|
|
280
|
+
return FinishType.ERROR.value
|
|
281
|
+
|
|
282
|
+
return None
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def map_llamaindex_finish_reason_to_finish_type(finish_reason):
|
|
286
|
+
"""Map LlamaIndex finish_reason to standardized finish_type."""
|
|
287
|
+
if not finish_reason:
|
|
288
|
+
return None
|
|
289
|
+
|
|
290
|
+
# Convert to lowercase for case-insensitive matching
|
|
291
|
+
finish_reason_lower = finish_reason.lower() if isinstance(finish_reason, str) else str(finish_reason).lower()
|
|
292
|
+
|
|
293
|
+
# Try direct mapping first
|
|
294
|
+
if finish_reason in LLAMAINDEX_FINISH_REASON_MAPPING:
|
|
295
|
+
return LLAMAINDEX_FINISH_REASON_MAPPING[finish_reason]
|
|
296
|
+
|
|
297
|
+
# Try lowercase mapping
|
|
298
|
+
if finish_reason_lower in LLAMAINDEX_FINISH_REASON_MAPPING:
|
|
299
|
+
return LLAMAINDEX_FINISH_REASON_MAPPING[finish_reason_lower]
|
|
300
|
+
|
|
301
|
+
# If no direct mapping, try to infer from common patterns
|
|
302
|
+
if any(keyword in finish_reason_lower for keyword in ['stop', 'complete', 'success', 'done', 'finish']):
|
|
303
|
+
return FinishType.SUCCESS.value
|
|
304
|
+
elif any(keyword in finish_reason_lower for keyword in ['length', 'token', 'limit', 'truncat']):
|
|
305
|
+
return FinishType.TRUNCATED.value
|
|
306
|
+
elif any(keyword in finish_reason_lower for keyword in ['filter', 'safety', 'block']):
|
|
307
|
+
return FinishType.CONTENT_FILTER.value
|
|
308
|
+
elif any(keyword in finish_reason_lower for keyword in ['error', 'fail', 'exception']):
|
|
309
|
+
return FinishType.ERROR.value
|
|
310
|
+
|
|
311
|
+
return None
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def map_azure_ai_inference_finish_reason_to_finish_type(finish_reason):
|
|
315
|
+
"""Map Azure AI Inference finish_reason to standardized finish_type."""
|
|
316
|
+
if not finish_reason:
|
|
317
|
+
return None
|
|
318
|
+
|
|
319
|
+
# Convert to lowercase for case-insensitive matching
|
|
320
|
+
finish_reason_lower = finish_reason.lower() if isinstance(finish_reason, str) else str(finish_reason).lower()
|
|
321
|
+
|
|
322
|
+
# Try direct mapping first
|
|
323
|
+
if finish_reason in AZURE_AI_INFERENCE_FINISH_REASON_MAPPING:
|
|
324
|
+
return AZURE_AI_INFERENCE_FINISH_REASON_MAPPING[finish_reason]
|
|
325
|
+
|
|
326
|
+
# Try lowercase mapping
|
|
327
|
+
if finish_reason_lower in AZURE_AI_INFERENCE_FINISH_REASON_MAPPING:
|
|
328
|
+
return AZURE_AI_INFERENCE_FINISH_REASON_MAPPING[finish_reason_lower]
|
|
329
|
+
|
|
330
|
+
# If no direct mapping, try to infer from common patterns
|
|
331
|
+
if any(keyword in finish_reason_lower for keyword in ['stop', 'complete', 'success', 'done', 'finish']):
|
|
332
|
+
return FinishType.SUCCESS.value
|
|
333
|
+
elif any(keyword in finish_reason_lower for keyword in ['length', 'token', 'limit', 'truncat']):
|
|
334
|
+
return FinishType.TRUNCATED.value
|
|
335
|
+
elif any(keyword in finish_reason_lower for keyword in ['filter', 'safety', 'block', 'responsible_ai', 'content_filter']):
|
|
336
|
+
return FinishType.CONTENT_FILTER.value
|
|
337
|
+
elif any(keyword in finish_reason_lower for keyword in ['error', 'fail', 'exception', 'timeout', 'unavailable', 'rate_limit']):
|
|
338
|
+
return FinishType.ERROR.value
|
|
339
|
+
|
|
340
|
+
return None
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def map_bedrock_finish_reason_to_finish_type(finish_reason):
|
|
344
|
+
"""Map AWS Bedrock finish_reason/stopReason to standardized finish_type."""
|
|
345
|
+
if not finish_reason:
|
|
346
|
+
return None
|
|
347
|
+
|
|
348
|
+
# Convert to lowercase for case-insensitive matching
|
|
349
|
+
finish_reason_lower = finish_reason.lower() if isinstance(finish_reason, str) else str(finish_reason).lower()
|
|
350
|
+
|
|
351
|
+
# Try direct mapping first
|
|
352
|
+
if finish_reason in BEDROCK_FINISH_REASON_MAPPING:
|
|
353
|
+
return BEDROCK_FINISH_REASON_MAPPING[finish_reason]
|
|
354
|
+
|
|
355
|
+
# Try lowercase mapping
|
|
356
|
+
if finish_reason_lower in BEDROCK_FINISH_REASON_MAPPING:
|
|
357
|
+
return BEDROCK_FINISH_REASON_MAPPING[finish_reason_lower]
|
|
358
|
+
|
|
359
|
+
# If no direct mapping, try to infer from common patterns
|
|
360
|
+
if any(keyword in finish_reason_lower for keyword in ['stop', 'complete', 'success', 'done', 'finish', 'end_turn', 'endoftext']):
|
|
361
|
+
return FinishType.SUCCESS.value
|
|
362
|
+
elif any(keyword in finish_reason_lower for keyword in ['length', 'token', 'limit', 'truncat', 'max_tokens']):
|
|
363
|
+
return FinishType.TRUNCATED.value
|
|
364
|
+
elif any(keyword in finish_reason_lower for keyword in ['filter', 'safety', 'block', 'guardrails', 'content_filter']):
|
|
365
|
+
return FinishType.CONTENT_FILTER.value
|
|
366
|
+
elif any(keyword in finish_reason_lower for keyword in ['error', 'fail', 'exception', 'timeout', 'unavailable', 'rate_limit', 'throttled', 'validation']):
|
|
367
|
+
return FinishType.ERROR.value
|
|
368
|
+
|
|
369
|
+
return None
|
|
370
|
+
|
|
371
|
+
def map_teamsai_finish_reason_to_finish_type(finish_reason):
|
|
372
|
+
"""Map TeamsAI finish_reason to standardized finish_type."""
|
|
373
|
+
if not finish_reason:
|
|
374
|
+
return None
|
|
375
|
+
|
|
376
|
+
# Convert to lowercase for case-insensitive matching
|
|
377
|
+
finish_reason_lower = finish_reason.lower() if isinstance(finish_reason, str) else str(finish_reason).lower()
|
|
378
|
+
|
|
379
|
+
# Try direct mapping first
|
|
380
|
+
if finish_reason in TEAMSAI_FINISH_REASON_MAPPING:
|
|
381
|
+
return TEAMSAI_FINISH_REASON_MAPPING[finish_reason]
|
|
382
|
+
|
|
383
|
+
# Try lowercase mapping
|
|
384
|
+
if finish_reason_lower in TEAMSAI_FINISH_REASON_MAPPING:
|
|
385
|
+
return TEAMSAI_FINISH_REASON_MAPPING[finish_reason_lower]
|
|
386
|
+
|
|
387
|
+
return None
|
|
@@ -11,8 +11,6 @@ from opentelemetry.trace.propagation import _SPAN_KEY
|
|
|
11
11
|
|
|
12
12
|
logger = logging.getLogger(__name__)
|
|
13
13
|
MAX_DATA_LENGTH = 1000
|
|
14
|
-
token_data = local()
|
|
15
|
-
token_data.current_token = None
|
|
16
14
|
|
|
17
15
|
def get_route(args) -> str:
|
|
18
16
|
return args[0]['PATH_INFO'] if 'PATH_INFO' in args[0] else ""
|
|
@@ -47,21 +45,18 @@ def flask_pre_tracing(args):
|
|
|
47
45
|
if key.startswith("HTTP_"):
|
|
48
46
|
new_key = key[5:].lower().replace("_", "-")
|
|
49
47
|
headers[new_key] = value
|
|
50
|
-
|
|
48
|
+
return extract_http_headers(headers)
|
|
51
49
|
|
|
52
|
-
def flask_post_tracing():
|
|
53
|
-
clear_http_scopes(
|
|
54
|
-
token_data.current_token = None
|
|
50
|
+
def flask_post_tracing(token):
|
|
51
|
+
clear_http_scopes(token)
|
|
55
52
|
|
|
56
53
|
class FlaskSpanHandler(SpanHandler):
|
|
57
54
|
|
|
58
55
|
def pre_tracing(self, to_wrap, wrapped, instance, args, kwargs):
|
|
59
|
-
flask_pre_tracing(args)
|
|
60
|
-
return super().pre_tracing(to_wrap, wrapped, instance, args, kwargs)
|
|
56
|
+
return flask_pre_tracing(args)
|
|
61
57
|
|
|
62
|
-
def post_tracing(self, to_wrap, wrapped, instance, args, kwargs, return_value):
|
|
63
|
-
flask_post_tracing()
|
|
64
|
-
return super().post_tracing(to_wrap, wrapped, instance, args, kwargs, return_value)
|
|
58
|
+
def post_tracing(self, to_wrap, wrapped, instance, args, kwargs, return_value, token):
|
|
59
|
+
flask_post_tracing(token)
|
|
65
60
|
|
|
66
61
|
class FlaskResponseSpanHandler(SpanHandler):
|
|
67
62
|
def post_tracing(self, to_wrap, wrapped, instance, args, kwargs, return_value):
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from monocle_apptrace.instrumentation.common.utils import (
|
|
3
3
|
get_exception_message,
|
|
4
|
+
get_json_dumps,
|
|
4
5
|
get_status_code,
|
|
5
6
|
)
|
|
7
|
+
from monocle_apptrace.instrumentation.metamodel.finish_types import (
|
|
8
|
+
map_gemini_finish_reason_to_finish_type,
|
|
9
|
+
GEMINI_FINISH_REASON_MAPPING
|
|
10
|
+
)
|
|
6
11
|
|
|
7
12
|
logger = logging.getLogger(__name__)
|
|
8
13
|
|
|
@@ -32,9 +37,9 @@ def extract_messages(kwargs):
|
|
|
32
37
|
if hasattr(part, 'text'):
|
|
33
38
|
messages.append({getattr(content, 'role', 'user'): part.text})
|
|
34
39
|
elif isinstance(contents, str):
|
|
35
|
-
messages.append({'
|
|
40
|
+
messages.append({'user': contents})
|
|
36
41
|
|
|
37
|
-
return [
|
|
42
|
+
return [get_json_dumps(message) for message in messages]
|
|
38
43
|
except Exception as e:
|
|
39
44
|
logger.warning("Warning: Error occurred in extract_messages: %s", str(e))
|
|
40
45
|
return []
|
|
@@ -42,20 +47,35 @@ def extract_messages(kwargs):
|
|
|
42
47
|
def extract_assistant_message(arguments):
|
|
43
48
|
try:
|
|
44
49
|
status = get_status_code(arguments)
|
|
45
|
-
|
|
50
|
+
messages = []
|
|
51
|
+
role = "assistant"
|
|
52
|
+
if hasattr(arguments['result'], "candidates") and len(arguments['result'].candidates) > 0 and hasattr(arguments['result'].candidates[0], "content") and hasattr(arguments['result'].candidates[0].content, "role"):
|
|
53
|
+
role = arguments["result"].candidates[0].content.role
|
|
46
54
|
if status == 'success':
|
|
47
55
|
if hasattr(arguments['result'], "text") and len(arguments['result'].text):
|
|
48
|
-
|
|
56
|
+
messages.append({role: arguments['result'].text})
|
|
49
57
|
else:
|
|
50
58
|
if arguments["exception"] is not None:
|
|
51
|
-
|
|
59
|
+
return get_exception_message(arguments)
|
|
52
60
|
elif hasattr(arguments["result"], "error"):
|
|
53
|
-
|
|
54
|
-
return
|
|
61
|
+
return arguments["result"].error
|
|
62
|
+
return get_json_dumps(messages[0]) if messages else ""
|
|
55
63
|
except (IndexError, AttributeError) as e:
|
|
56
64
|
logger.warning("Warning: Error occurred in extract_assistant_message: %s", str(e))
|
|
57
65
|
return None
|
|
58
66
|
|
|
67
|
+
def update_input_span_events(kwargs):
|
|
68
|
+
if 'contents' in kwargs and isinstance(kwargs['contents'], list) and len(kwargs['contents']) > 0:
|
|
69
|
+
query = kwargs['contents'][0]
|
|
70
|
+
return query
|
|
71
|
+
|
|
72
|
+
def update_output_span_events(results):
|
|
73
|
+
if hasattr(results,'embeddings') and isinstance(results.embeddings, list) and len(results.embeddings) > 0:
|
|
74
|
+
embeddings = results.embeddings[0]
|
|
75
|
+
if hasattr(embeddings, 'values') and isinstance(embeddings.values, list) and len(embeddings.values) > 100:
|
|
76
|
+
output = str(results.embeddings[0].values[:100]) + "..."
|
|
77
|
+
return output
|
|
78
|
+
|
|
59
79
|
def extract_inference_endpoint(instance):
|
|
60
80
|
try:
|
|
61
81
|
if hasattr(instance,'_api_client') and hasattr(instance._api_client, '_http_options'):
|
|
@@ -74,3 +94,27 @@ def update_span_from_llm_response(response, instance):
|
|
|
74
94
|
meta_dict.update({"prompt_tokens": token_usage.prompt_token_count })
|
|
75
95
|
meta_dict.update({"total_tokens": token_usage.total_token_count})
|
|
76
96
|
return meta_dict
|
|
97
|
+
|
|
98
|
+
def extract_finish_reason(arguments):
|
|
99
|
+
"""Extract finish_reason from Gemini response"""
|
|
100
|
+
try:
|
|
101
|
+
if arguments["exception"] is not None:
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
response = arguments["result"]
|
|
105
|
+
|
|
106
|
+
# Handle Gemini response structure
|
|
107
|
+
if (response is not None and
|
|
108
|
+
hasattr(response, "candidates") and
|
|
109
|
+
len(response.candidates) > 0 and
|
|
110
|
+
hasattr(response.candidates[0], "finish_reason")):
|
|
111
|
+
return response.candidates[0].finish_reason
|
|
112
|
+
|
|
113
|
+
except (IndexError, AttributeError) as e:
|
|
114
|
+
logger.warning("Warning: Error occurred in extract_finish_reason: %s", str(e))
|
|
115
|
+
return None
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
def map_finish_reason_to_finish_type(finish_reason):
|
|
119
|
+
"""Map Gemini finish_reason to finish_type based on the possible errors mapping"""
|
|
120
|
+
return map_gemini_finish_reason_to_finish_type(finish_reason)
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from monocle_apptrace.instrumentation.metamodel.gemini import (
|
|
2
2
|
_helper,
|
|
3
3
|
)
|
|
4
|
-
from monocle_apptrace.instrumentation.common.utils import
|
|
4
|
+
from monocle_apptrace.instrumentation.common.utils import get_error_message
|
|
5
|
+
|
|
5
6
|
INFERENCE = {
|
|
6
7
|
"type": "inference",
|
|
7
8
|
"attributes": [
|
|
@@ -9,7 +10,7 @@ INFERENCE = {
|
|
|
9
10
|
{
|
|
10
11
|
"_comment": "provider type , inference_endpoint",
|
|
11
12
|
"attribute": "type",
|
|
12
|
-
"accessor": lambda arguments: 'inference.gemini'
|
|
13
|
+
"accessor": lambda arguments: 'inference.vertexai' if hasattr(arguments['instance'],"vertexai") and arguments['instance'].vertexai else 'inference.gemini'
|
|
13
14
|
},
|
|
14
15
|
{
|
|
15
16
|
"attribute": "inference_endpoint",
|
|
@@ -46,13 +47,8 @@ INFERENCE = {
|
|
|
46
47
|
"name": "data.output",
|
|
47
48
|
"attributes": [
|
|
48
49
|
{
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"accessor": lambda arguments: get_status(arguments)
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
"attribute": "status_code",
|
|
55
|
-
"accessor": lambda arguments: get_status_code(arguments)
|
|
50
|
+
"attribute": "error_code",
|
|
51
|
+
"accessor": lambda arguments: get_error_message(arguments)
|
|
56
52
|
},
|
|
57
53
|
{
|
|
58
54
|
"attribute": "response",
|
|
@@ -66,6 +62,18 @@ INFERENCE = {
|
|
|
66
62
|
{
|
|
67
63
|
"_comment": "this is metadata usage from LLM",
|
|
68
64
|
"accessor": lambda arguments: _helper.update_span_from_llm_response(arguments['result'], arguments['instance'])
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"_comment": "finish reason from Gemini response",
|
|
68
|
+
"attribute": "finish_reason",
|
|
69
|
+
"accessor": lambda arguments: _helper.extract_finish_reason(arguments)
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"_comment": "finish type mapped from finish reason",
|
|
73
|
+
"attribute": "finish_type",
|
|
74
|
+
"accessor": lambda arguments: _helper.map_finish_reason_to_finish_type(
|
|
75
|
+
_helper.extract_finish_reason(arguments)
|
|
76
|
+
)
|
|
69
77
|
}
|
|
70
78
|
]
|
|
71
79
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from monocle_apptrace.instrumentation.metamodel.gemini import (
|
|
2
|
+
_helper,
|
|
3
|
+
)
|
|
4
|
+
|
|
5
|
+
RETRIEVAL = {
|
|
6
|
+
"type": "retrieval",
|
|
7
|
+
"attributes": [
|
|
8
|
+
[
|
|
9
|
+
{
|
|
10
|
+
"_comment": "Embedding Model",
|
|
11
|
+
"attribute": "name",
|
|
12
|
+
"accessor": lambda arguments: _helper.resolve_from_alias(arguments['kwargs'],
|
|
13
|
+
['model'])
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"attribute": "type",
|
|
17
|
+
"accessor": lambda arguments: 'model.embedding.' + _helper.resolve_from_alias(arguments['kwargs'],
|
|
18
|
+
['model'])
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
],
|
|
22
|
+
"events": [
|
|
23
|
+
{
|
|
24
|
+
"name": "data.input",
|
|
25
|
+
"attributes": [
|
|
26
|
+
{
|
|
27
|
+
"attribute": "input",
|
|
28
|
+
"accessor": lambda arguments: _helper.update_input_span_events(arguments['kwargs'])
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "data.output",
|
|
34
|
+
"attributes": [
|
|
35
|
+
{
|
|
36
|
+
"attribute": "response",
|
|
37
|
+
"accessor": lambda arguments: _helper.update_output_span_events(arguments['result'])
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
]
|
|
43
|
+
}
|
|
@@ -2,6 +2,9 @@ from monocle_apptrace.instrumentation.common.wrapper import task_wrapper
|
|
|
2
2
|
from monocle_apptrace.instrumentation.metamodel.gemini.entities.inference import (
|
|
3
3
|
INFERENCE,
|
|
4
4
|
)
|
|
5
|
+
from monocle_apptrace.instrumentation.metamodel.gemini.entities.retrieval import (
|
|
6
|
+
RETRIEVAL,
|
|
7
|
+
)
|
|
5
8
|
|
|
6
9
|
GEMINI_METHODS = [
|
|
7
10
|
{
|
|
@@ -10,5 +13,12 @@ GEMINI_METHODS = [
|
|
|
10
13
|
"method": "generate_content",
|
|
11
14
|
"wrapper_method": task_wrapper,
|
|
12
15
|
"output_processor": INFERENCE,
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"package": "google.genai.models",
|
|
19
|
+
"object": "Models",
|
|
20
|
+
"method": "embed_content",
|
|
21
|
+
"wrapper_method": task_wrapper,
|
|
22
|
+
"output_processor": RETRIEVAL,
|
|
13
23
|
}
|
|
14
24
|
]
|