monocle-apptrace 0.4.0b2__py3-none-any.whl → 0.4.1__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/instrumentation/__init__.py +2 -1
- monocle_apptrace/instrumentation/common/constants.py +3 -0
- monocle_apptrace/instrumentation/common/instrumentor.py +1 -1
- monocle_apptrace/instrumentation/common/span_handler.py +8 -6
- monocle_apptrace/instrumentation/common/utils.py +33 -2
- monocle_apptrace/instrumentation/common/wrapper.py +59 -79
- monocle_apptrace/instrumentation/common/wrapper_method.py +5 -1
- monocle_apptrace/instrumentation/metamodel/anthropic/_helper.py +29 -4
- monocle_apptrace/instrumentation/metamodel/anthropic/entities/inference.py +12 -2
- monocle_apptrace/instrumentation/metamodel/azfunc/_helper.py +78 -0
- monocle_apptrace/instrumentation/metamodel/azfunc/entities/http.py +51 -0
- monocle_apptrace/instrumentation/metamodel/azfunc/methods.py +23 -0
- monocle_apptrace/instrumentation/metamodel/azfunc/wrapper.py +23 -0
- monocle_apptrace/instrumentation/metamodel/azureaiinference/__init__.py +1 -0
- monocle_apptrace/instrumentation/metamodel/azureaiinference/_helper.py +216 -0
- monocle_apptrace/instrumentation/metamodel/azureaiinference/entities/inference.py +208 -0
- monocle_apptrace/instrumentation/metamodel/azureaiinference/methods.py +23 -0
- monocle_apptrace/instrumentation/metamodel/botocore/_helper.py +42 -17
- monocle_apptrace/instrumentation/metamodel/botocore/entities/inference.py +11 -3
- monocle_apptrace/instrumentation/metamodel/flask/_helper.py +1 -1
- monocle_apptrace/instrumentation/metamodel/haystack/_helper.py +20 -12
- monocle_apptrace/instrumentation/metamodel/haystack/entities/inference.py +10 -2
- monocle_apptrace/instrumentation/metamodel/langchain/_helper.py +19 -13
- monocle_apptrace/instrumentation/metamodel/langchain/entities/inference.py +10 -2
- monocle_apptrace/instrumentation/metamodel/llamaindex/_helper.py +21 -13
- monocle_apptrace/instrumentation/metamodel/llamaindex/entities/inference.py +10 -2
- monocle_apptrace/instrumentation/metamodel/openai/_helper.py +31 -10
- monocle_apptrace/instrumentation/metamodel/openai/entities/inference.py +12 -1
- monocle_apptrace/instrumentation/metamodel/teamsai/_helper.py +50 -4
- monocle_apptrace/instrumentation/metamodel/teamsai/entities/inference/actionplanner_output_processor.py +32 -12
- monocle_apptrace/instrumentation/metamodel/teamsai/methods.py +30 -17
- monocle_apptrace/instrumentation/metamodel/teamsai/sample.json +448 -0
- {monocle_apptrace-0.4.0b2.dist-info → monocle_apptrace-0.4.1.dist-info}/METADATA +21 -18
- {monocle_apptrace-0.4.0b2.dist-info → monocle_apptrace-0.4.1.dist-info}/RECORD +37 -28
- {monocle_apptrace-0.4.0b2.dist-info → monocle_apptrace-0.4.1.dist-info}/WHEEL +0 -0
- {monocle_apptrace-0.4.0b2.dist-info → monocle_apptrace-0.4.1.dist-info}/licenses/LICENSE +0 -0
- {monocle_apptrace-0.4.0b2.dist-info → monocle_apptrace-0.4.1.dist-info}/licenses/NOTICE +0 -0
|
@@ -1 +1,2 @@
|
|
|
1
|
-
from .common import *
|
|
1
|
+
from .common import *
|
|
2
|
+
from .metamodel.azfunc.wrapper import monocle_trace_azure_function_route
|
|
@@ -54,6 +54,9 @@ llm_type_map = {
|
|
|
54
54
|
"anthropic": "anthropic",
|
|
55
55
|
"chatanthropic":"anthropic",
|
|
56
56
|
"anthropicchatgenerator":"anthropic",
|
|
57
|
+
"chatcompletionsclient": "azure_ai_inference",
|
|
58
|
+
"embeddingsclient": "azure_ai_inference",
|
|
59
|
+
"imageembeddingsclient": "azure_ai_inference",
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
MONOCLE_INSTRUMENTOR = "monocle_apptrace"
|
|
@@ -362,7 +362,7 @@ def monocle_trace_http_route(func):
|
|
|
362
362
|
if inspect.iscoroutinefunction(func):
|
|
363
363
|
@wraps(func)
|
|
364
364
|
async def wrapper(*args, **kwargs):
|
|
365
|
-
return http_async_route_handler(func, *args, **kwargs)
|
|
365
|
+
return await http_async_route_handler(func, *args, **kwargs)
|
|
366
366
|
return wrapper
|
|
367
367
|
else:
|
|
368
368
|
@wraps(func)
|
|
@@ -83,10 +83,10 @@ class SpanHandler:
|
|
|
83
83
|
def post_task_processing(self, to_wrap, wrapped, instance, args, kwargs, result, span:Span):
|
|
84
84
|
pass
|
|
85
85
|
|
|
86
|
-
def hydrate_span(self, to_wrap, wrapped, instance, args, kwargs, result, span, ex:Exception = None) -> bool:
|
|
86
|
+
def hydrate_span(self, to_wrap, wrapped, instance, args, kwargs, result, span, parent_span = None, ex:Exception = None) -> bool:
|
|
87
87
|
try:
|
|
88
88
|
detected_error_in_attribute = self.hydrate_attributes(to_wrap, wrapped, instance, args, kwargs, result, span)
|
|
89
|
-
detected_error_in_event = self.hydrate_events(to_wrap, wrapped, instance, args, kwargs, result, span, ex)
|
|
89
|
+
detected_error_in_event = self.hydrate_events(to_wrap, wrapped, instance, args, kwargs, result, span, parent_span, ex)
|
|
90
90
|
if detected_error_in_attribute or detected_error_in_event:
|
|
91
91
|
span.set_attribute(MONOCLE_DETECTED_SPAN_ERROR, True)
|
|
92
92
|
finally:
|
|
@@ -134,18 +134,20 @@ class SpanHandler:
|
|
|
134
134
|
span.set_attribute("entity.count", span_index)
|
|
135
135
|
return detected_error
|
|
136
136
|
|
|
137
|
-
def hydrate_events(self, to_wrap, wrapped, instance, args, kwargs, ret_result, span, ex:Exception=None) -> bool:
|
|
137
|
+
def hydrate_events(self, to_wrap, wrapped, instance, args, kwargs, ret_result, span, parent_span=None, ex:Exception=None) -> bool:
|
|
138
138
|
detected_error:bool = False
|
|
139
139
|
if 'output_processor' in to_wrap and to_wrap["output_processor"] is not None:
|
|
140
140
|
output_processor=to_wrap['output_processor']
|
|
141
141
|
skip_processors:list[str] = self.skip_processor(to_wrap, wrapped, instance, span, args, kwargs) or []
|
|
142
142
|
|
|
143
143
|
arguments = {"instance": instance, "args": args, "kwargs": kwargs, "result": ret_result, "exception":ex}
|
|
144
|
-
|
|
144
|
+
# Process events if they are defined in the output_processor.
|
|
145
|
+
# In case of inference.modelapi skip the event processing unless the span has an exception
|
|
146
|
+
if 'events' in output_processor and ('events' not in skip_processors or ex is not None):
|
|
145
147
|
events = output_processor['events']
|
|
146
148
|
for event in events:
|
|
147
149
|
event_name = event.get("name")
|
|
148
|
-
if 'events.'+event_name in skip_processors:
|
|
150
|
+
if 'events.'+event_name in skip_processors and ex is None:
|
|
149
151
|
continue
|
|
150
152
|
event_attributes = {}
|
|
151
153
|
attributes = event.get("attributes", [])
|
|
@@ -157,7 +159,7 @@ class SpanHandler:
|
|
|
157
159
|
result = accessor(arguments)
|
|
158
160
|
if result and isinstance(result, dict):
|
|
159
161
|
result = dict((key, value) for key, value in result.items() if value is not None)
|
|
160
|
-
if result and isinstance(result, (str, list, dict)):
|
|
162
|
+
if result and isinstance(result, (int, str, list, dict)):
|
|
161
163
|
if attribute_key is not None:
|
|
162
164
|
event_attributes[attribute_key] = result
|
|
163
165
|
else:
|
|
@@ -236,6 +236,13 @@ def set_scopes_from_baggage(baggage_context:Context):
|
|
|
236
236
|
scope_name = scope_key[len(MONOCLE_SCOPE_NAME_PREFIX):]
|
|
237
237
|
set_scope(scope_name, scope_value)
|
|
238
238
|
|
|
239
|
+
def get_parent_span() -> Span:
|
|
240
|
+
parent_span: Span = None
|
|
241
|
+
_parent_span_context = get_current()
|
|
242
|
+
if _parent_span_context is not None and _parent_span_context.get(_SPAN_KEY, None):
|
|
243
|
+
parent_span = _parent_span_context.get(_SPAN_KEY, None)
|
|
244
|
+
return parent_span
|
|
245
|
+
|
|
239
246
|
def extract_http_headers(headers) -> object:
|
|
240
247
|
global http_scopes
|
|
241
248
|
trace_context:Context = extract(headers, context=get_current())
|
|
@@ -362,11 +369,19 @@ def get_llm_type(instance):
|
|
|
362
369
|
except:
|
|
363
370
|
pass
|
|
364
371
|
|
|
372
|
+
def get_status(arguments):
|
|
373
|
+
if arguments['exception'] is not None:
|
|
374
|
+
return 'error'
|
|
375
|
+
else:
|
|
376
|
+
return 'success'
|
|
377
|
+
|
|
365
378
|
def get_exception_status_code(arguments):
|
|
366
|
-
if arguments['exception'] is not None and hasattr(arguments['exception'], 'code'):
|
|
379
|
+
if arguments['exception'] is not None and hasattr(arguments['exception'], 'code') and arguments['exception'].code is not None:
|
|
367
380
|
return arguments['exception'].code
|
|
368
|
-
|
|
381
|
+
elif arguments['exception'] is not None:
|
|
369
382
|
return 'error'
|
|
383
|
+
else:
|
|
384
|
+
return 'success'
|
|
370
385
|
|
|
371
386
|
def get_exception_message(arguments):
|
|
372
387
|
if arguments['exception'] is not None:
|
|
@@ -377,6 +392,22 @@ def get_exception_message(arguments):
|
|
|
377
392
|
else:
|
|
378
393
|
return ''
|
|
379
394
|
|
|
395
|
+
def get_status_code(arguments):
|
|
396
|
+
if arguments["exception"] is not None:
|
|
397
|
+
return get_exception_status_code(arguments)
|
|
398
|
+
elif hasattr(arguments["result"], "status"):
|
|
399
|
+
return arguments["result"].status
|
|
400
|
+
else:
|
|
401
|
+
return 'success'
|
|
402
|
+
|
|
403
|
+
def get_status(arguments):
|
|
404
|
+
if arguments["exception"] is not None:
|
|
405
|
+
return 'error'
|
|
406
|
+
elif get_status_code(arguments) == 'success':
|
|
407
|
+
return 'success'
|
|
408
|
+
else:
|
|
409
|
+
return 'error'
|
|
410
|
+
|
|
380
411
|
def patch_instance_method(obj, method_name, func):
|
|
381
412
|
"""
|
|
382
413
|
Patch a special method (like __iter__) for a single instance.
|
|
@@ -9,7 +9,8 @@ from monocle_apptrace.instrumentation.common.utils import (
|
|
|
9
9
|
set_scopes,
|
|
10
10
|
with_tracer_wrapper,
|
|
11
11
|
set_scope,
|
|
12
|
-
remove_scope
|
|
12
|
+
remove_scope,
|
|
13
|
+
get_parent_span
|
|
13
14
|
)
|
|
14
15
|
from monocle_apptrace.instrumentation.common.constants import WORKFLOW_TYPE_KEY, ADD_NEW_WORKFLOW
|
|
15
16
|
logger = logging.getLogger(__name__)
|
|
@@ -35,10 +36,10 @@ def pre_process_span(name, tracer, handler, add_workflow_span, to_wrap, wrapped,
|
|
|
35
36
|
except Exception as e:
|
|
36
37
|
logger.info(f"Warning: Error occurred in pre_task_processing: {e}")
|
|
37
38
|
|
|
38
|
-
def post_process_span(handler, to_wrap, wrapped, instance, args, kwargs, return_value, span, ex = None):
|
|
39
|
+
def post_process_span(handler, to_wrap, wrapped, instance, args, kwargs, return_value, span, parent_span=None, ex = None):
|
|
39
40
|
if not (SpanHandler.is_root_span(span) or get_value(ADD_NEW_WORKFLOW) == True):
|
|
40
41
|
try:
|
|
41
|
-
handler.hydrate_span(to_wrap, wrapped, instance, args, kwargs, return_value, span, ex)
|
|
42
|
+
handler.hydrate_span(to_wrap, wrapped, instance, args, kwargs, return_value, span, parent_span, ex)
|
|
42
43
|
except Exception as e:
|
|
43
44
|
logger.info(f"Warning: Error occurred in hydrate_span: {e}")
|
|
44
45
|
|
|
@@ -59,45 +60,35 @@ def monocle_wrapper_span_processor(tracer: Tracer, handler: SpanHandler, to_wrap
|
|
|
59
60
|
name = get_span_name(to_wrap, instance)
|
|
60
61
|
return_value = None
|
|
61
62
|
span_status = None
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if SpanHandler.is_root_span(span) or add_workflow_span:
|
|
67
|
-
# Recursive call for the actual span
|
|
68
|
-
return_value, span_status = monocle_wrapper_span_processor(tracer, handler, to_wrap, wrapped, instance, source_path, False, args, kwargs)
|
|
69
|
-
span.set_status(span_status)
|
|
70
|
-
else:
|
|
71
|
-
ex:Exception = None
|
|
72
|
-
try:
|
|
73
|
-
with SpanHandler.workflow_type(to_wrap, span):
|
|
74
|
-
return_value = wrapped(*args, **kwargs)
|
|
75
|
-
except Exception as e:
|
|
76
|
-
ex = e
|
|
77
|
-
raise
|
|
78
|
-
finally:
|
|
79
|
-
post_process_span(handler, to_wrap, wrapped, instance, args, kwargs, return_value, span, ex)
|
|
80
|
-
span_status = span.status
|
|
81
|
-
else:
|
|
82
|
-
span = tracer.start_span(name)
|
|
83
|
-
|
|
63
|
+
auto_close_span = get_auto_close_span(to_wrap, kwargs)
|
|
64
|
+
parent_span = get_parent_span()
|
|
65
|
+
with tracer.start_as_current_span(name, end_on_exit=auto_close_span) as span:
|
|
84
66
|
pre_process_span(name, tracer, handler, add_workflow_span, to_wrap, wrapped, instance, args, kwargs, span, source_path)
|
|
85
67
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
span.
|
|
90
|
-
|
|
91
|
-
try:
|
|
92
|
-
with SpanHandler.workflow_type(to_wrap, span):
|
|
93
|
-
return_value = wrapped(*args, **kwargs)
|
|
94
|
-
finally:
|
|
95
|
-
if to_wrap.get("output_processor") and to_wrap.get("output_processor").get("response_processor"):
|
|
96
|
-
# Process the stream
|
|
97
|
-
to_wrap.get("output_processor").get("response_processor")(to_wrap, return_value, post_process_span_internal)
|
|
98
|
-
else:
|
|
68
|
+
if SpanHandler.is_root_span(span) or add_workflow_span:
|
|
69
|
+
# Recursive call for the actual span
|
|
70
|
+
return_value, span_status = monocle_wrapper_span_processor(tracer, handler, to_wrap, wrapped, instance, source_path, False, args, kwargs)
|
|
71
|
+
span.set_status(span_status)
|
|
72
|
+
if not auto_close_span:
|
|
99
73
|
span.end()
|
|
100
|
-
|
|
74
|
+
else:
|
|
75
|
+
ex:Exception = None
|
|
76
|
+
try:
|
|
77
|
+
with SpanHandler.workflow_type(to_wrap, span):
|
|
78
|
+
return_value = wrapped(*args, **kwargs)
|
|
79
|
+
except Exception as e:
|
|
80
|
+
ex = e
|
|
81
|
+
raise
|
|
82
|
+
finally:
|
|
83
|
+
def post_process_span_internal(ret_val):
|
|
84
|
+
post_process_span(handler, to_wrap, wrapped, instance, args, kwargs, ret_val, span, parent_span ,ex)
|
|
85
|
+
if not auto_close_span:
|
|
86
|
+
span.end()
|
|
87
|
+
if ex is None and not auto_close_span and to_wrap.get("output_processor") and to_wrap.get("output_processor").get("response_processor"):
|
|
88
|
+
to_wrap.get("output_processor").get("response_processor")(to_wrap, return_value, post_process_span_internal)
|
|
89
|
+
else:
|
|
90
|
+
post_process_span_internal(return_value)
|
|
91
|
+
span_status = span.status
|
|
101
92
|
return return_value, span_status
|
|
102
93
|
|
|
103
94
|
def monocle_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs):
|
|
@@ -129,44 +120,34 @@ async def amonocle_wrapper_span_processor(tracer: Tracer, handler: SpanHandler,
|
|
|
129
120
|
name = get_span_name(to_wrap, instance)
|
|
130
121
|
return_value = None
|
|
131
122
|
span_status = None
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
if SpanHandler.is_root_span(span) or add_workflow_span:
|
|
137
|
-
# Recursive call for the actual span
|
|
138
|
-
return_value, span_status = await amonocle_wrapper_span_processor(tracer, handler, to_wrap, wrapped, instance, source_path, False, args, kwargs)
|
|
139
|
-
span.set_status(span_status)
|
|
140
|
-
else:
|
|
141
|
-
ex:Exception = None
|
|
142
|
-
try:
|
|
143
|
-
with SpanHandler.workflow_type(to_wrap, span):
|
|
144
|
-
return_value = await wrapped(*args, **kwargs)
|
|
145
|
-
except Exception as e:
|
|
146
|
-
ex = e
|
|
147
|
-
raise
|
|
148
|
-
finally:
|
|
149
|
-
post_process_span(handler, to_wrap, wrapped, instance, args, kwargs, return_value, span, ex)
|
|
150
|
-
span_status = span.status
|
|
151
|
-
else:
|
|
152
|
-
span = tracer.start_span(name)
|
|
153
|
-
|
|
123
|
+
auto_close_span = get_auto_close_span(to_wrap, kwargs)
|
|
124
|
+
parent_span = get_parent_span()
|
|
125
|
+
with tracer.start_as_current_span(name, end_on_exit=auto_close_span) as span:
|
|
154
126
|
pre_process_span(name, tracer, handler, add_workflow_span, to_wrap, wrapped, instance, args, kwargs, span, source_path)
|
|
155
127
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
span.
|
|
160
|
-
|
|
161
|
-
try:
|
|
162
|
-
with SpanHandler.workflow_type(to_wrap, span):
|
|
163
|
-
return_value = await wrapped(*args, **kwargs)
|
|
164
|
-
finally:
|
|
165
|
-
if to_wrap.get("output_processor") and to_wrap.get("output_processor").get("response_processor"):
|
|
166
|
-
# Process the stream
|
|
167
|
-
to_wrap.get("output_processor").get("response_processor")(to_wrap, return_value, post_process_span_internal)
|
|
168
|
-
else:
|
|
128
|
+
if SpanHandler.is_root_span(span) or add_workflow_span:
|
|
129
|
+
# Recursive call for the actual span
|
|
130
|
+
return_value, span_status = await amonocle_wrapper_span_processor(tracer, handler, to_wrap, wrapped, instance, source_path, False, args, kwargs)
|
|
131
|
+
span.set_status(span_status)
|
|
132
|
+
if not auto_close_span:
|
|
169
133
|
span.end()
|
|
134
|
+
else:
|
|
135
|
+
ex:Exception = None
|
|
136
|
+
try:
|
|
137
|
+
with SpanHandler.workflow_type(to_wrap, span):
|
|
138
|
+
return_value = await wrapped(*args, **kwargs)
|
|
139
|
+
except Exception as e:
|
|
140
|
+
ex = e
|
|
141
|
+
raise
|
|
142
|
+
finally:
|
|
143
|
+
def post_process_span_internal(ret_val):
|
|
144
|
+
post_process_span(handler, to_wrap, wrapped, instance, args, kwargs, ret_val, span, parent_span,ex)
|
|
145
|
+
if not auto_close_span:
|
|
146
|
+
span.end()
|
|
147
|
+
if ex is None and not auto_close_span and to_wrap.get("output_processor") and to_wrap.get("output_processor").get("response_processor"):
|
|
148
|
+
to_wrap.get("output_processor").get("response_processor")(to_wrap, return_value, post_process_span_internal)
|
|
149
|
+
else:
|
|
150
|
+
post_process_span_internal(return_value)
|
|
170
151
|
span_status = span.status
|
|
171
152
|
return return_value, span.status
|
|
172
153
|
|
|
@@ -229,7 +210,7 @@ async def ascope_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped,
|
|
|
229
210
|
@with_tracer_wrapper
|
|
230
211
|
def scopes_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs):
|
|
231
212
|
scope_values = to_wrap.get('scope_values', None)
|
|
232
|
-
scope_values = evaluate_scope_values(args, kwargs, scope_values)
|
|
213
|
+
scope_values = evaluate_scope_values(args, kwargs, to_wrap, scope_values)
|
|
233
214
|
token = None
|
|
234
215
|
try:
|
|
235
216
|
if scope_values:
|
|
@@ -243,7 +224,7 @@ def scopes_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, insta
|
|
|
243
224
|
@with_tracer_wrapper
|
|
244
225
|
async def ascopes_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs):
|
|
245
226
|
scope_values = to_wrap.get('scope_values', None)
|
|
246
|
-
scope_values = evaluate_scope_values(args, kwargs, scope_values)
|
|
227
|
+
scope_values = evaluate_scope_values(args, kwargs, to_wrap, scope_values)
|
|
247
228
|
token = None
|
|
248
229
|
try:
|
|
249
230
|
if scope_values:
|
|
@@ -254,7 +235,7 @@ async def ascopes_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped
|
|
|
254
235
|
if token:
|
|
255
236
|
remove_scope(token)
|
|
256
237
|
|
|
257
|
-
def evaluate_scope_values(args, kwargs, scope_values):
|
|
238
|
+
def evaluate_scope_values(args, kwargs, to_wrap, scope_values):
|
|
258
239
|
if callable(scope_values):
|
|
259
240
|
try:
|
|
260
241
|
scope_values = scope_values(args, kwargs)
|
|
@@ -263,5 +244,4 @@ def evaluate_scope_values(args, kwargs, scope_values):
|
|
|
263
244
|
scope_values = None
|
|
264
245
|
if isinstance(scope_values, dict):
|
|
265
246
|
return scope_values
|
|
266
|
-
return None
|
|
267
|
-
|
|
247
|
+
return None
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
from typing import Any, Dict
|
|
3
3
|
from monocle_apptrace.instrumentation.common.wrapper import task_wrapper, scope_wrapper
|
|
4
4
|
from monocle_apptrace.instrumentation.common.span_handler import SpanHandler, NonFrameworkSpanHandler
|
|
5
|
+
from monocle_apptrace.instrumentation.metamodel.azureaiinference.methods import AZURE_AI_INFERENCE_METHODS
|
|
5
6
|
from monocle_apptrace.instrumentation.metamodel.botocore.methods import BOTOCORE_METHODS
|
|
6
7
|
from monocle_apptrace.instrumentation.metamodel.botocore.handlers.botocore_span_handler import BotoCoreSpanHandler
|
|
7
8
|
from monocle_apptrace.instrumentation.metamodel.langchain.methods import (
|
|
@@ -20,6 +21,8 @@ from monocle_apptrace.instrumentation.metamodel.teamsai.methods import (TEAMAI_M
|
|
|
20
21
|
from monocle_apptrace.instrumentation.metamodel.anthropic.methods import (ANTHROPIC_METHODS, )
|
|
21
22
|
from monocle_apptrace.instrumentation.metamodel.aiohttp.methods import (AIOHTTP_METHODS, )
|
|
22
23
|
from monocle_apptrace.instrumentation.metamodel.aiohttp._helper import aiohttpSpanHandler
|
|
24
|
+
from monocle_apptrace.instrumentation.metamodel.azfunc._helper import (azureSpanHandler)
|
|
25
|
+
from monocle_apptrace.instrumentation.metamodel.azfunc.methods import AZFUNC_HTTP_METHODS
|
|
23
26
|
class WrapperMethod:
|
|
24
27
|
def __init__(
|
|
25
28
|
self,
|
|
@@ -68,7 +71,7 @@ class WrapperMethod:
|
|
|
68
71
|
def get_span_handler(self) -> SpanHandler:
|
|
69
72
|
return self.span_handler()
|
|
70
73
|
|
|
71
|
-
DEFAULT_METHODS_LIST = LANGCHAIN_METHODS + LLAMAINDEX_METHODS + HAYSTACK_METHODS + BOTOCORE_METHODS + FLASK_METHODS + REQUESTS_METHODS + LANGGRAPH_METHODS + OPENAI_METHODS + TEAMAI_METHODS + ANTHROPIC_METHODS + AIOHTTP_METHODS
|
|
74
|
+
DEFAULT_METHODS_LIST = LANGCHAIN_METHODS + LLAMAINDEX_METHODS + HAYSTACK_METHODS + BOTOCORE_METHODS + FLASK_METHODS + REQUESTS_METHODS + LANGGRAPH_METHODS + OPENAI_METHODS + TEAMAI_METHODS + ANTHROPIC_METHODS + AIOHTTP_METHODS + AZURE_AI_INFERENCE_METHODS + AZFUNC_HTTP_METHODS
|
|
72
75
|
|
|
73
76
|
MONOCLE_SPAN_HANDLERS: Dict[str, SpanHandler] = {
|
|
74
77
|
"default": SpanHandler(),
|
|
@@ -79,4 +82,5 @@ MONOCLE_SPAN_HANDLERS: Dict[str, SpanHandler] = {
|
|
|
79
82
|
"request_handler": RequestSpanHandler(),
|
|
80
83
|
"non_framework_handler": NonFrameworkSpanHandler(),
|
|
81
84
|
"openai_handler": OpenAISpanHandler(),
|
|
85
|
+
"azure_func_handler": azureSpanHandler(),
|
|
82
86
|
}
|
|
@@ -9,6 +9,7 @@ from monocle_apptrace.instrumentation.common.utils import (
|
|
|
9
9
|
get_keys_as_tuple,
|
|
10
10
|
get_nested_value,
|
|
11
11
|
try_option,
|
|
12
|
+
get_exception_message,
|
|
12
13
|
)
|
|
13
14
|
|
|
14
15
|
|
|
@@ -39,12 +40,36 @@ def extract_messages(kwargs):
|
|
|
39
40
|
logger.warning("Warning: Error occurred in extract_messages: %s", str(e))
|
|
40
41
|
return []
|
|
41
42
|
|
|
43
|
+
def get_exception_status_code(arguments):
|
|
44
|
+
if arguments['exception'] is not None and hasattr(arguments['exception'], 'status_code') and arguments['exception'].status_code is not None:
|
|
45
|
+
return arguments['exception'].status_code
|
|
46
|
+
elif arguments['exception'] is not None:
|
|
47
|
+
return 'error'
|
|
48
|
+
else:
|
|
49
|
+
return 'success'
|
|
42
50
|
|
|
43
|
-
def
|
|
51
|
+
def get_status_code(arguments):
|
|
52
|
+
if arguments["exception"] is not None:
|
|
53
|
+
return get_exception_status_code(arguments)
|
|
54
|
+
elif hasattr(arguments["result"], "status"):
|
|
55
|
+
return arguments["result"].status
|
|
56
|
+
else:
|
|
57
|
+
return 'success'
|
|
58
|
+
|
|
59
|
+
def extract_assistant_message(arguments):
|
|
44
60
|
try:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
61
|
+
status = get_status_code(arguments)
|
|
62
|
+
response: str = ""
|
|
63
|
+
if status == 'success':
|
|
64
|
+
if arguments['result'] is not None and hasattr(arguments['result'],"content") and len(arguments['result'].content) >0:
|
|
65
|
+
if hasattr(arguments['result'].content[0],"text"):
|
|
66
|
+
response = arguments['result'].content[0].text
|
|
67
|
+
else:
|
|
68
|
+
if arguments["exception"] is not None:
|
|
69
|
+
response = get_exception_message(arguments)
|
|
70
|
+
elif hasattr(arguments["result"], "error"):
|
|
71
|
+
response = arguments["result"].error
|
|
72
|
+
return response
|
|
48
73
|
except (IndexError, AttributeError) as e:
|
|
49
74
|
logger.warning("Warning: Error occurred in extract_assistant_message: %s", str(e))
|
|
50
75
|
return None
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from monocle_apptrace.instrumentation.metamodel.anthropic import (
|
|
2
2
|
_helper,
|
|
3
3
|
)
|
|
4
|
-
from monocle_apptrace.instrumentation.common.utils import resolve_from_alias, get_llm_type
|
|
4
|
+
from monocle_apptrace.instrumentation.common.utils import (resolve_from_alias, get_llm_type,
|
|
5
|
+
get_status, get_status_code
|
|
6
|
+
)
|
|
5
7
|
|
|
6
8
|
INFERENCE = {
|
|
7
9
|
"type": "inference",
|
|
@@ -52,10 +54,18 @@ INFERENCE = {
|
|
|
52
54
|
{
|
|
53
55
|
"name": "data.output",
|
|
54
56
|
"attributes": [
|
|
57
|
+
{
|
|
58
|
+
"attribute": "status",
|
|
59
|
+
"accessor": lambda arguments: get_status(arguments)
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"attribute": "status_code",
|
|
63
|
+
"accessor": lambda arguments: _helper.get_status_code(arguments)
|
|
64
|
+
},
|
|
55
65
|
{
|
|
56
66
|
"_comment": "this is result from LLM",
|
|
57
67
|
"attribute": "response",
|
|
58
|
-
"accessor": lambda arguments: _helper.extract_assistant_message(arguments
|
|
68
|
+
"accessor": lambda arguments: _helper.extract_assistant_message(arguments)
|
|
59
69
|
}
|
|
60
70
|
]
|
|
61
71
|
},
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from threading import local
|
|
3
|
+
from monocle_apptrace.instrumentation.common.utils import extract_http_headers, clear_http_scopes, try_option, Option, MonocleSpanException
|
|
4
|
+
from monocle_apptrace.instrumentation.common.span_handler import SpanHandler
|
|
5
|
+
from monocle_apptrace.instrumentation.common.constants import HTTP_SUCCESS_CODES
|
|
6
|
+
from urllib.parse import unquote, urlparse, ParseResult
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
MAX_DATA_LENGTH = 1000
|
|
11
|
+
token_data = local()
|
|
12
|
+
token_data.current_token = None
|
|
13
|
+
|
|
14
|
+
def get_url(kwargs) -> ParseResult:
|
|
15
|
+
url_str = try_option(getattr, kwargs['req'], 'url')
|
|
16
|
+
url = url_str.unwrap_or(None)
|
|
17
|
+
if url is not None:
|
|
18
|
+
return urlparse(url)
|
|
19
|
+
else:
|
|
20
|
+
return None
|
|
21
|
+
|
|
22
|
+
def get_route(kwargs) -> str:
|
|
23
|
+
url:ParseResult = get_url(kwargs)
|
|
24
|
+
if url is not None:
|
|
25
|
+
return url.path
|
|
26
|
+
|
|
27
|
+
def get_method(kwargs) -> str:
|
|
28
|
+
# return args[0]['method'] if 'method' in args[0] else ""
|
|
29
|
+
http_method: Option[str] = try_option(getattr, kwargs['req'], 'method')
|
|
30
|
+
return http_method.unwrap_or("")
|
|
31
|
+
|
|
32
|
+
def get_params(kwargs) -> dict:
|
|
33
|
+
url:ParseResult = get_url(kwargs)
|
|
34
|
+
if url is not None:
|
|
35
|
+
return unquote(url.query)
|
|
36
|
+
|
|
37
|
+
def get_body(kwargs) -> dict:
|
|
38
|
+
if hasattr(kwargs['req'], 'get_body'):
|
|
39
|
+
response = kwargs.get_body()
|
|
40
|
+
if isinstance(response, bytes):
|
|
41
|
+
response = response.decode('utf-8', errors='ignore')
|
|
42
|
+
else:
|
|
43
|
+
response = ""
|
|
44
|
+
return response
|
|
45
|
+
|
|
46
|
+
def extract_response(result) -> str:
|
|
47
|
+
if hasattr(result, 'get_body'):
|
|
48
|
+
response = result.get_body() # text[0:max(result.text.__len__(), MAX_DATA_LENGTH)]
|
|
49
|
+
if isinstance(response, bytes):
|
|
50
|
+
response = response.decode('utf-8', errors='ignore')
|
|
51
|
+
else:
|
|
52
|
+
response = ""
|
|
53
|
+
return response
|
|
54
|
+
|
|
55
|
+
def extract_status(result) -> str:
|
|
56
|
+
status = f"{result.status_code}" if hasattr(result, 'status_code') else ""
|
|
57
|
+
if status not in HTTP_SUCCESS_CODES:
|
|
58
|
+
error_message = extract_response(result)
|
|
59
|
+
raise MonocleSpanException(f"error: {status} - {error_message}")
|
|
60
|
+
return status
|
|
61
|
+
|
|
62
|
+
def azure_func_pre_tracing(kwargs):
|
|
63
|
+
headers = kwargs['req'].headers if hasattr(kwargs['req'], 'headers') else {}
|
|
64
|
+
token_data.current_token = extract_http_headers(headers)
|
|
65
|
+
|
|
66
|
+
def azure_func_post_tracing():
|
|
67
|
+
clear_http_scopes(token_data.current_token)
|
|
68
|
+
token_data.current_token = None
|
|
69
|
+
|
|
70
|
+
class azureSpanHandler(SpanHandler):
|
|
71
|
+
|
|
72
|
+
def pre_tracing(self, to_wrap, wrapped, instance, args, kwargs):
|
|
73
|
+
azure_func_pre_tracing(kwargs)
|
|
74
|
+
return super().pre_tracing(to_wrap, wrapped, instance, args, kwargs)
|
|
75
|
+
|
|
76
|
+
def post_tracing(self, to_wrap, wrapped, instance, args, kwargs, return_value):
|
|
77
|
+
azure_func_post_tracing()
|
|
78
|
+
return super().post_tracing(to_wrap, wrapped, instance, args, kwargs, return_value)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from monocle_apptrace.instrumentation.metamodel.azfunc import _helper
|
|
2
|
+
AZFUNC_HTTP_PROCESSOR = {
|
|
3
|
+
"type": "http.process",
|
|
4
|
+
"attributes": [
|
|
5
|
+
[
|
|
6
|
+
{
|
|
7
|
+
"_comment": "request method, request URI",
|
|
8
|
+
"attribute": "method",
|
|
9
|
+
"accessor": lambda arguments: _helper.get_method(arguments['kwargs'])
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"_comment": "request method, request URI",
|
|
13
|
+
"attribute": "route",
|
|
14
|
+
"accessor": lambda arguments: _helper.get_route(arguments['kwargs'])
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"_comment": "request method, request URI",
|
|
18
|
+
"attribute": "body",
|
|
19
|
+
"accessor": lambda arguments: _helper.get_body(arguments['kwargs'])
|
|
20
|
+
},
|
|
21
|
+
]
|
|
22
|
+
],
|
|
23
|
+
"events": [
|
|
24
|
+
{
|
|
25
|
+
"name": "data.input",
|
|
26
|
+
"attributes": [
|
|
27
|
+
{
|
|
28
|
+
"_comment": "route params",
|
|
29
|
+
"attribute": "params",
|
|
30
|
+
"accessor": lambda arguments: _helper.get_params(arguments['kwargs'])
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"name": "data.output",
|
|
36
|
+
"attributes": [
|
|
37
|
+
{
|
|
38
|
+
"_comment": "status from HTTP response",
|
|
39
|
+
"attribute": "status",
|
|
40
|
+
"accessor": lambda arguments: _helper.extract_status(arguments['result'])
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"_comment": "this is result from LLM",
|
|
44
|
+
"attribute": "response",
|
|
45
|
+
"accessor": lambda arguments: _helper.extract_response(arguments['result'])
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
]
|
|
51
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from monocle_apptrace.instrumentation.common.wrapper import atask_wrapper, task_wrapper
|
|
2
|
+
from monocle_apptrace.instrumentation.metamodel.azfunc.entities.http import AZFUNC_HTTP_PROCESSOR
|
|
3
|
+
|
|
4
|
+
AZFUNC_HTTP_METHODS = [
|
|
5
|
+
{
|
|
6
|
+
"package": "monocle_apptrace.instrumentation.metamodel.azfunc.wrapper",
|
|
7
|
+
"object": "AzureFunctionRouteWrapper",
|
|
8
|
+
"method": "run_async",
|
|
9
|
+
"span_name": "azure_function_route",
|
|
10
|
+
"wrapper_method": atask_wrapper,
|
|
11
|
+
"span_handler": "azure_func_handler",
|
|
12
|
+
"output_processor": AZFUNC_HTTP_PROCESSOR
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"package": "monocle_apptrace.instrumentation.metamodel.azfunc.wrapper",
|
|
16
|
+
"object": "AzureFunctionRouteWrapper",
|
|
17
|
+
"method": "run_sync",
|
|
18
|
+
"span_name": "azure_function_route",
|
|
19
|
+
"wrapper_method": task_wrapper,
|
|
20
|
+
"span_handler": "azure_func_handler",
|
|
21
|
+
"output_processor": AZFUNC_HTTP_PROCESSOR
|
|
22
|
+
}
|
|
23
|
+
]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from functools import wraps
|
|
2
|
+
import inspect
|
|
3
|
+
|
|
4
|
+
def monocle_trace_azure_function_route(func):
|
|
5
|
+
if inspect.iscoroutinefunction(func):
|
|
6
|
+
@wraps(func)
|
|
7
|
+
async def wrapper(*args, **kwargs):
|
|
8
|
+
return await AzureFunctionRouteWrapper.run_async(func, *args, **kwargs)
|
|
9
|
+
return wrapper
|
|
10
|
+
else:
|
|
11
|
+
@wraps(func)
|
|
12
|
+
def wrapper(*args, **kwargs):
|
|
13
|
+
return AzureFunctionRouteWrapper.run_sync(func ,*args, **kwargs)
|
|
14
|
+
return wrapper
|
|
15
|
+
|
|
16
|
+
class AzureFunctionRouteWrapper:
|
|
17
|
+
@staticmethod
|
|
18
|
+
async def run_async(func, *args, **kwargs):
|
|
19
|
+
return await func(*args, **kwargs)
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
def run_sync(func, *args, **kwargs):
|
|
23
|
+
return func(*args, **kwargs)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Azure AI Inference instrumentation module
|