monocle-apptrace 0.3.1b1__py3-none-any.whl → 0.4.0b2__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.

Files changed (46) hide show
  1. monocle_apptrace/exporters/aws/s3_exporter.py +3 -1
  2. monocle_apptrace/exporters/azure/blob_exporter.py +2 -2
  3. monocle_apptrace/exporters/base_exporter.py +10 -4
  4. monocle_apptrace/exporters/file_exporter.py +19 -4
  5. monocle_apptrace/exporters/monocle_exporters.py +8 -5
  6. monocle_apptrace/exporters/okahu/okahu_exporter.py +5 -2
  7. monocle_apptrace/instrumentation/common/__init__.py +1 -1
  8. monocle_apptrace/instrumentation/common/constants.py +12 -5
  9. monocle_apptrace/instrumentation/common/instrumentor.py +44 -22
  10. monocle_apptrace/instrumentation/common/span_handler.py +100 -50
  11. monocle_apptrace/instrumentation/common/tracing.md +68 -0
  12. monocle_apptrace/instrumentation/common/utils.py +101 -63
  13. monocle_apptrace/instrumentation/common/wrapper.py +223 -48
  14. monocle_apptrace/instrumentation/common/wrapper_method.py +15 -7
  15. monocle_apptrace/instrumentation/metamodel/aiohttp/__init__.py +0 -0
  16. monocle_apptrace/instrumentation/metamodel/aiohttp/_helper.py +66 -0
  17. monocle_apptrace/instrumentation/metamodel/aiohttp/entities/http.py +51 -0
  18. monocle_apptrace/instrumentation/metamodel/aiohttp/methods.py +13 -0
  19. monocle_apptrace/instrumentation/metamodel/anthropic/methods.py +4 -2
  20. monocle_apptrace/instrumentation/metamodel/flask/_helper.py +50 -3
  21. monocle_apptrace/instrumentation/metamodel/flask/entities/http.py +48 -0
  22. monocle_apptrace/instrumentation/metamodel/flask/methods.py +10 -1
  23. monocle_apptrace/instrumentation/metamodel/haystack/_helper.py +17 -4
  24. monocle_apptrace/instrumentation/metamodel/haystack/entities/inference.py +5 -2
  25. monocle_apptrace/instrumentation/metamodel/haystack/methods.py +8 -4
  26. monocle_apptrace/instrumentation/metamodel/langchain/_helper.py +12 -4
  27. monocle_apptrace/instrumentation/metamodel/langchain/entities/inference.py +1 -1
  28. monocle_apptrace/instrumentation/metamodel/langchain/methods.py +6 -14
  29. monocle_apptrace/instrumentation/metamodel/llamaindex/_helper.py +13 -9
  30. monocle_apptrace/instrumentation/metamodel/llamaindex/entities/inference.py +1 -1
  31. monocle_apptrace/instrumentation/metamodel/llamaindex/methods.py +16 -15
  32. monocle_apptrace/instrumentation/metamodel/openai/_helper.py +10 -2
  33. monocle_apptrace/instrumentation/metamodel/openai/entities/inference.py +174 -26
  34. monocle_apptrace/instrumentation/metamodel/openai/methods.py +6 -8
  35. monocle_apptrace/instrumentation/metamodel/requests/_helper.py +31 -0
  36. monocle_apptrace/instrumentation/metamodel/requests/entities/http.py +51 -0
  37. monocle_apptrace/instrumentation/metamodel/requests/methods.py +2 -1
  38. monocle_apptrace/instrumentation/metamodel/teamsai/_helper.py +55 -5
  39. monocle_apptrace/instrumentation/metamodel/teamsai/entities/inference/actionplanner_output_processor.py +13 -33
  40. monocle_apptrace/instrumentation/metamodel/teamsai/entities/inference/teamsai_output_processor.py +24 -20
  41. monocle_apptrace/instrumentation/metamodel/teamsai/methods.py +42 -8
  42. {monocle_apptrace-0.3.1b1.dist-info → monocle_apptrace-0.4.0b2.dist-info}/METADATA +2 -1
  43. {monocle_apptrace-0.3.1b1.dist-info → monocle_apptrace-0.4.0b2.dist-info}/RECORD +46 -39
  44. {monocle_apptrace-0.3.1b1.dist-info → monocle_apptrace-0.4.0b2.dist-info}/WHEEL +0 -0
  45. {monocle_apptrace-0.3.1b1.dist-info → monocle_apptrace-0.4.0b2.dist-info}/licenses/LICENSE +0 -0
  46. {monocle_apptrace-0.3.1b1.dist-info → monocle_apptrace-0.4.0b2.dist-info}/licenses/NOTICE +0 -0
@@ -6,76 +6,204 @@ from opentelemetry.context import set_value, attach, detach, get_value
6
6
  from monocle_apptrace.instrumentation.common.span_handler import SpanHandler
7
7
  from monocle_apptrace.instrumentation.common.utils import (
8
8
  get_fully_qualified_class_name,
9
+ set_scopes,
9
10
  with_tracer_wrapper,
10
11
  set_scope,
11
- remove_scope,
12
- async_wrapper
12
+ remove_scope
13
13
  )
14
- from monocle_apptrace.instrumentation.common.constants import WORKFLOW_TYPE_KEY
14
+ from monocle_apptrace.instrumentation.common.constants import WORKFLOW_TYPE_KEY, ADD_NEW_WORKFLOW
15
15
  logger = logging.getLogger(__name__)
16
16
 
17
- def wrapper_processor(async_task: bool, tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, args, kwargs):
18
- # Some Langchain objects are wrapped elsewhere, so we ignore them here
19
- if instance.__class__.__name__ in ("AgentExecutor"):
20
- return wrapped(*args, **kwargs)
17
+ def get_auto_close_span(to_wrap, kwargs):
18
+ try:
19
+ if to_wrap.get("output_processor") and to_wrap.get("output_processor").get("is_auto_close"):
20
+ return to_wrap.get("output_processor").get("is_auto_close")(kwargs)
21
+ return True
22
+ except Exception as e:
23
+ logger.warning("Warning: Error occurred in get_auto_close_span: %s", str(e))
24
+ return True
25
+
26
+ def pre_process_span(name, tracer, handler, add_workflow_span, to_wrap, wrapped, instance, args, kwargs, span, source_path):
27
+ SpanHandler.set_default_monocle_attributes(span, source_path)
28
+ if SpanHandler.is_root_span(span) or add_workflow_span:
29
+ # This is a direct API call of a non-framework type
30
+ SpanHandler.set_workflow_properties(span, to_wrap)
31
+ else:
32
+ SpanHandler.set_non_workflow_properties(span)
33
+ try:
34
+ handler.pre_task_processing(to_wrap, wrapped, instance, args, kwargs, span)
35
+ except Exception as e:
36
+ logger.info(f"Warning: Error occurred in pre_task_processing: {e}")
37
+
38
+ def post_process_span(handler, to_wrap, wrapped, instance, args, kwargs, return_value, span, ex = None):
39
+ if not (SpanHandler.is_root_span(span) or get_value(ADD_NEW_WORKFLOW) == True):
40
+ try:
41
+ handler.hydrate_span(to_wrap, wrapped, instance, args, kwargs, return_value, span, ex)
42
+ except Exception as e:
43
+ logger.info(f"Warning: Error occurred in hydrate_span: {e}")
44
+
45
+ try:
46
+ handler.post_task_processing(to_wrap, wrapped, instance, args, kwargs, return_value, span)
47
+ except Exception as e:
48
+ logger.info(f"Warning: Error occurred in post_task_processing: {e}")
21
49
 
22
- if hasattr(instance, "name") and instance.name:
23
- name = f"{to_wrap.get('span_name')}.{instance.name.lower()}"
24
- elif to_wrap.get("span_name"):
50
+ def get_span_name(to_wrap, instance):
51
+ if to_wrap.get("span_name"):
25
52
  name = to_wrap.get("span_name")
26
53
  else:
27
54
  name = get_fully_qualified_class_name(instance)
55
+ return name
28
56
 
57
+ def monocle_wrapper_span_processor(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, add_workflow_span, args, kwargs):
58
+ # Main span processing logic
59
+ name = get_span_name(to_wrap, instance)
29
60
  return_value = None
30
- token = None
31
- try:
32
- handler.pre_tracing(to_wrap, wrapped, instance, args, kwargs)
33
- skip_scan:bool = to_wrap.get('skip_span') or handler.skip_span(to_wrap, wrapped, instance, args, kwargs)
34
- if not to_wrap.get('skip_span'):
35
- token = SpanHandler.attach_workflow_type(to_wrap=to_wrap)
36
- if skip_scan:
37
- if async_task:
38
- return_value = async_wrapper(wrapped, None, None, None, *args, **kwargs)
61
+ span_status = None
62
+ if(get_auto_close_span(to_wrap, kwargs)):
63
+ with tracer.start_as_current_span(name) as span:
64
+ pre_process_span(name, tracer, handler, add_workflow_span, to_wrap, wrapped, instance, args, kwargs, span, source_path)
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)
39
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
+
84
+ pre_process_span(name, tracer, handler, add_workflow_span, to_wrap, wrapped, instance, args, kwargs, span, source_path)
85
+
86
+ def post_process_span_internal(ret_val):
87
+ nonlocal handler, to_wrap, wrapped, instance, args, kwargs, span
88
+ post_process_span(handler, to_wrap, wrapped, instance, args, kwargs, ret_val, span)
89
+ span.end()
90
+
91
+ try:
92
+ with SpanHandler.workflow_type(to_wrap, span):
40
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:
99
+ span.end()
100
+ span_status = span.status
101
+ return return_value, span_status
102
+
103
+ def monocle_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs):
104
+ return_value = None
105
+ token = None
106
+ try:
107
+ try:
108
+ handler.pre_tracing(to_wrap, wrapped, instance, args, kwargs)
109
+ except Exception as e:
110
+ logger.info(f"Warning: Error occurred in pre_tracing: {e}")
111
+ if to_wrap.get('skip_span', False) or handler.skip_span(to_wrap, wrapped, instance, args, kwargs):
112
+ return_value = wrapped(*args, **kwargs)
41
113
  else:
42
- return_value = span_processor(name, async_task, tracer, handler, to_wrap, wrapped, instance, args, kwargs)
114
+ add_workflow_span = get_value(ADD_NEW_WORKFLOW) == True
115
+ token = attach(set_value(ADD_NEW_WORKFLOW, False))
116
+ try:
117
+ return_value, span_status = monocle_wrapper_span_processor(tracer, handler, to_wrap, wrapped, instance, source_path, add_workflow_span, args, kwargs)
118
+ finally:
119
+ detach(token)
43
120
  return return_value
44
121
  finally:
45
- handler.detach_workflow_type(token)
46
- handler.post_tracing(to_wrap, wrapped, instance, args, kwargs, return_value)
122
+ try:
123
+ handler.post_tracing(to_wrap, wrapped, instance, args, kwargs, return_value)
124
+ except Exception as e:
125
+ logger.info(f"Warning: Error occurred in post_tracing: {e}")
126
+
127
+ async def amonocle_wrapper_span_processor(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, add_workflow_span, args, kwargs):
128
+ # Main span processing logic
129
+ name = get_span_name(to_wrap, instance)
130
+ return_value = None
131
+ span_status = None
132
+ if(get_auto_close_span(to_wrap, kwargs)):
133
+ with tracer.start_as_current_span(name) as span:
134
+ pre_process_span(name, tracer, handler, add_workflow_span, to_wrap, wrapped, instance, args, kwargs, span, source_path)
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
+
154
+ pre_process_span(name, tracer, handler, add_workflow_span, to_wrap, wrapped, instance, args, kwargs, span, source_path)
155
+
156
+ def post_process_span_internal(ret_val):
157
+ nonlocal handler, to_wrap, wrapped, instance, args, kwargs, span
158
+ post_process_span(handler, to_wrap, wrapped, instance, args, kwargs, ret_val, span)
159
+ span.end()
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:
169
+ span.end()
170
+ span_status = span.status
171
+ return return_value, span.status
47
172
 
48
- def span_processor(name: str, async_task: bool, tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, args, kwargs):
49
- # For singleton spans, eg OpenAI inference generate a workflow span to format the workflow specific attributes
173
+ async def amonocle_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs):
50
174
  return_value = None
51
- with tracer.start_as_current_span(name) as span:
52
- # Since Spanhandler can be overridden, ensure we set default monocle attributes.
53
- SpanHandler.set_default_monocle_attributes(span)
54
- if SpanHandler.is_root_span(span):
55
- SpanHandler.set_workflow_properties(span, to_wrap)
56
- if handler.is_non_workflow_root_span(span, to_wrap):
57
- # This is a direct API call of a non-framework type, call the span_processor recursively for the actual span
58
- return_value = span_processor(name, async_task, tracer, handler, to_wrap, wrapped, instance, args, kwargs)
175
+ token = None
176
+ try:
177
+ try:
178
+ handler.pre_tracing(to_wrap, wrapped, instance, args, kwargs)
179
+ except Exception as e:
180
+ logger.info(f"Warning: Error occurred in pre_tracing: {e}")
181
+ if to_wrap.get('skip_span', False) or handler.skip_span(to_wrap, wrapped, instance, args, kwargs):
182
+ return_value = await wrapped(*args, **kwargs)
59
183
  else:
60
- handler.pre_task_processing(to_wrap, wrapped, instance, args, kwargs, span)
61
- if async_task:
62
- return_value = async_wrapper(wrapped, None, None, None, *args, **kwargs)
63
- else:
64
- return_value = wrapped(*args, **kwargs)
65
- handler.hydrate_span(to_wrap, wrapped, instance, args, kwargs, return_value, span)
66
- handler.post_task_processing(to_wrap, wrapped, instance, args, kwargs, return_value, span)
67
- return return_value
184
+ add_workflow_span = get_value(ADD_NEW_WORKFLOW) == True
185
+ token = attach(set_value(ADD_NEW_WORKFLOW, False))
186
+ try:
187
+ return_value, span_status = await amonocle_wrapper_span_processor(tracer, handler, to_wrap, wrapped, instance, source_path, add_workflow_span, args, kwargs)
188
+ finally:
189
+ detach(token)
190
+ return return_value
191
+ finally:
192
+ try:
193
+ handler.post_tracing(to_wrap, wrapped, instance, args, kwargs, return_value)
194
+ except Exception as e:
195
+ logger.info(f"Warning: Error occurred in post_tracing: {e}")
68
196
 
69
197
  @with_tracer_wrapper
70
- def task_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, args, kwargs):
71
- return wrapper_processor(False, tracer, handler, to_wrap, wrapped, instance, args, kwargs)
198
+ def task_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs):
199
+ return monocle_wrapper(tracer, handler, to_wrap, wrapped, instance, source_path, args, kwargs)
72
200
 
73
201
  @with_tracer_wrapper
74
- async def atask_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, args, kwargs):
75
- return wrapper_processor(True, tracer, handler, to_wrap, wrapped, instance, args, kwargs)
202
+ async def atask_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs):
203
+ return await amonocle_wrapper(tracer, handler, to_wrap, wrapped, instance, source_path, args, kwargs)
76
204
 
77
205
  @with_tracer_wrapper
78
- def scope_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, args, kwargs):
206
+ def scope_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs):
79
207
  scope_name = to_wrap.get('scope_name', None)
80
208
  if scope_name:
81
209
  token = set_scope(scope_name)
@@ -85,8 +213,55 @@ def scope_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instan
85
213
  return return_value
86
214
 
87
215
  @with_tracer_wrapper
88
- async def ascope_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, args, kwargs):
216
+ async def ascope_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs):
89
217
  scope_name = to_wrap.get('scope_name', None)
90
218
  scope_value = to_wrap.get('scope_value', None)
91
- return_value = async_wrapper(wrapped, scope_name, scope_value, None, *args, **kwargs)
92
- return return_value
219
+ token = None
220
+ try:
221
+ if scope_name:
222
+ token = set_scope(scope_name, scope_value)
223
+ return_value = await wrapped(*args, **kwargs)
224
+ return return_value
225
+ finally:
226
+ if token:
227
+ remove_scope(token)
228
+
229
+ @with_tracer_wrapper
230
+ def scopes_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs):
231
+ scope_values = to_wrap.get('scope_values', None)
232
+ scope_values = evaluate_scope_values(args, kwargs, scope_values)
233
+ token = None
234
+ try:
235
+ if scope_values:
236
+ token = set_scopes(scope_values)
237
+ return_value = wrapped(*args, **kwargs)
238
+ return return_value
239
+ finally:
240
+ if token:
241
+ remove_scope(token)
242
+
243
+ @with_tracer_wrapper
244
+ async def ascopes_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs):
245
+ scope_values = to_wrap.get('scope_values', None)
246
+ scope_values = evaluate_scope_values(args, kwargs, scope_values)
247
+ token = None
248
+ try:
249
+ if scope_values:
250
+ token = set_scopes(scope_values)
251
+ return_value = await wrapped(*args, **kwargs)
252
+ return return_value
253
+ finally:
254
+ if token:
255
+ remove_scope(token)
256
+
257
+ def evaluate_scope_values(args, kwargs, scope_values):
258
+ if callable(scope_values):
259
+ try:
260
+ scope_values = scope_values(args, kwargs)
261
+ except Exception as e:
262
+ logger.warning("Warning: Error occurred in evaluate_scope_values: %s", str(e))
263
+ scope_values = None
264
+ if isinstance(scope_values, dict):
265
+ return scope_values
266
+ return None
267
+
@@ -10,14 +10,16 @@ from monocle_apptrace.instrumentation.metamodel.langchain.methods import (
10
10
  from monocle_apptrace.instrumentation.metamodel.llamaindex.methods import (LLAMAINDEX_METHODS, )
11
11
  from monocle_apptrace.instrumentation.metamodel.haystack.methods import (HAYSTACK_METHODS, )
12
12
  from monocle_apptrace.instrumentation.metamodel.openai.methods import (OPENAI_METHODS,)
13
+ from monocle_apptrace.instrumentation.metamodel.openai._helper import OpenAISpanHandler
13
14
  from monocle_apptrace.instrumentation.metamodel.langgraph.methods import LANGGRAPH_METHODS
14
15
  from monocle_apptrace.instrumentation.metamodel.flask.methods import (FLASK_METHODS, )
15
- from monocle_apptrace.instrumentation.metamodel.flask._helper import FlaskSpanHandler
16
+ from monocle_apptrace.instrumentation.metamodel.flask._helper import FlaskSpanHandler, FlaskResponseSpanHandler
16
17
  from monocle_apptrace.instrumentation.metamodel.requests.methods import (REQUESTS_METHODS, )
17
18
  from monocle_apptrace.instrumentation.metamodel.requests._helper import RequestSpanHandler
18
19
  from monocle_apptrace.instrumentation.metamodel.teamsai.methods import (TEAMAI_METHODS, )
19
20
  from monocle_apptrace.instrumentation.metamodel.anthropic.methods import (ANTHROPIC_METHODS, )
20
-
21
+ from monocle_apptrace.instrumentation.metamodel.aiohttp.methods import (AIOHTTP_METHODS, )
22
+ from monocle_apptrace.instrumentation.metamodel.aiohttp._helper import aiohttpSpanHandler
21
23
  class WrapperMethod:
22
24
  def __init__(
23
25
  self,
@@ -29,7 +31,8 @@ class WrapperMethod:
29
31
  wrapper_method = task_wrapper,
30
32
  span_handler = 'default',
31
33
  scope_name: str = None,
32
- span_type: str = None
34
+ span_type: str = None,
35
+ scope_values = None,
33
36
  ):
34
37
  self.package = package
35
38
  self.object = object_name
@@ -37,10 +40,11 @@ class WrapperMethod:
37
40
  self.span_name = span_name
38
41
  self.output_processor=output_processor
39
42
  self.span_type = span_type
43
+ self.scope_values = scope_values
40
44
 
41
45
  self.span_handler:SpanHandler.__class__ = span_handler
42
46
  self.scope_name = scope_name
43
- if scope_name:
47
+ if scope_name and not scope_values:
44
48
  self.wrapper_method = scope_wrapper
45
49
  else:
46
50
  self.wrapper_method = wrapper_method
@@ -56,19 +60,23 @@ class WrapperMethod:
56
60
  'wrapper_method': self.wrapper_method,
57
61
  'span_handler': self.span_handler,
58
62
  'scope_name': self.scope_name,
59
- 'span_type': self.span_type
63
+ 'span_type': self.span_type,
64
+ 'scope_values': self.scope_values,
60
65
  }
61
66
  return instance_dict
62
67
 
63
68
  def get_span_handler(self) -> SpanHandler:
64
69
  return self.span_handler()
65
70
 
66
- DEFAULT_METHODS_LIST = LANGCHAIN_METHODS + LLAMAINDEX_METHODS + HAYSTACK_METHODS + BOTOCORE_METHODS + FLASK_METHODS + REQUESTS_METHODS + LANGGRAPH_METHODS + OPENAI_METHODS + TEAMAI_METHODS + ANTHROPIC_METHODS
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
67
72
 
68
73
  MONOCLE_SPAN_HANDLERS: Dict[str, SpanHandler] = {
69
74
  "default": SpanHandler(),
75
+ "aiohttp_handler": aiohttpSpanHandler(),
70
76
  "botocore_handler": BotoCoreSpanHandler(),
71
77
  "flask_handler": FlaskSpanHandler(),
78
+ "flask_response_handler": FlaskResponseSpanHandler(),
72
79
  "request_handler": RequestSpanHandler(),
73
- "non_framework_handler": NonFrameworkSpanHandler()
80
+ "non_framework_handler": NonFrameworkSpanHandler(),
81
+ "openai_handler": OpenAISpanHandler(),
74
82
  }
@@ -0,0 +1,66 @@
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
7
+
8
+ logger = logging.getLogger(__name__)
9
+ MAX_DATA_LENGTH = 1000
10
+ token_data = local()
11
+ token_data.current_token = None
12
+
13
+ def get_route(args) -> str:
14
+ route_path: Option[str] = try_option(getattr, args[0], 'path')
15
+ return route_path.unwrap_or("")
16
+
17
+ def get_method(args) -> str:
18
+ # return args[0]['method'] if 'method' in args[0] else ""
19
+ http_method: Option[str] = try_option(getattr, args[0], 'method')
20
+ return http_method.unwrap_or("")
21
+
22
+ def get_params(args) -> dict:
23
+ params: Option[str] = try_option(getattr, args[0], 'query_string')
24
+ return unquote(params.unwrap_or(""))
25
+
26
+ def get_body(args) -> dict:
27
+ return ""
28
+
29
+ def extract_response(result) -> str:
30
+ if hasattr(result, 'text'):
31
+ response = result.text[0:max(result.text.__len__(), MAX_DATA_LENGTH)]
32
+ else:
33
+ response = ""
34
+ return response
35
+
36
+ def extract_status(result) -> str:
37
+ status = f"{result.status}" if hasattr(result, 'status') else ""
38
+ if status not in HTTP_SUCCESS_CODES:
39
+ error_message = extract_response(result)
40
+ raise MonocleSpanException(f"error: {status} - {error_message}")
41
+ return status
42
+
43
+ def aiohttp_pre_tracing(args):
44
+ token_data.current_token = extract_http_headers(args[0].headers)
45
+
46
+ def aiohttp_post_tracing():
47
+ clear_http_scopes(token_data.current_token)
48
+ token_data.current_token = None
49
+
50
+ def aiohttp_skip_span(args) -> bool:
51
+ if get_method(args) == "HEAD":
52
+ return True
53
+ return False
54
+
55
+ class aiohttpSpanHandler(SpanHandler):
56
+
57
+ def pre_tracing(self, to_wrap, wrapped, instance, args, kwargs):
58
+ aiohttp_pre_tracing(args)
59
+ return super().pre_tracing(to_wrap, wrapped, instance, args, kwargs)
60
+
61
+ def post_tracing(self, to_wrap, wrapped, instance, args, kwargs, return_value):
62
+ aiohttp_post_tracing()
63
+ return super().post_tracing(to_wrap, wrapped, instance, args, kwargs, return_value)
64
+
65
+ def skip_span(self, to_wrap, wrapped, instance, args, kwargs) -> bool:
66
+ return aiohttp_skip_span(args)
@@ -0,0 +1,51 @@
1
+ from monocle_apptrace.instrumentation.metamodel.aiohttp import _helper
2
+ AIO_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['args'])
10
+ },
11
+ {
12
+ "_comment": "request method, request URI",
13
+ "attribute": "route",
14
+ "accessor": lambda arguments: _helper.get_route(arguments['args'])
15
+ },
16
+ {
17
+ "_comment": "request method, request URI",
18
+ "attribute": "body",
19
+ "accessor": lambda arguments: _helper.get_body(arguments['args'])
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['args'])
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,13 @@
1
+ from monocle_apptrace.instrumentation.common.wrapper import atask_wrapper
2
+ from monocle_apptrace.instrumentation.metamodel.aiohttp.entities.http import AIO_HTTP_PROCESSOR
3
+
4
+ AIOHTTP_METHODS = [
5
+ {
6
+ "package": "aiohttp.web_app",
7
+ "object": "Application",
8
+ "method": "_handle",
9
+ "wrapper_method": atask_wrapper,
10
+ "span_handler": "aiohttp_handler",
11
+ "output_processor": AIO_HTTP_PROCESSOR
12
+ }
13
+ ]
@@ -5,17 +5,19 @@ from monocle_apptrace.instrumentation.metamodel.anthropic.entities.inference imp
5
5
 
6
6
  ANTHROPIC_METHODS = [
7
7
  {
8
- "package": "anthropic.resources.messages.messages",
8
+ "package": "anthropic.resources",
9
9
  "object": "Messages",
10
10
  "method": "create",
11
11
  "wrapper_method": task_wrapper,
12
+ "span_handler": "non_framework_handler",
12
13
  "output_processor": INFERENCE
13
14
  },
14
15
  {
15
- "package": "anthropic.resources.messages.messages",
16
+ "package": "anthropic.resources",
16
17
  "object": "AsyncMessages",
17
18
  "method": "create",
18
19
  "wrapper_method": atask_wrapper,
20
+ "span_handler": "non_framework_handler",
19
21
  "output_processor": INFERENCE
20
22
  },
21
23
 
@@ -1,11 +1,46 @@
1
+ import logging
1
2
  from threading import local
2
3
  from monocle_apptrace.instrumentation.common.utils import extract_http_headers, clear_http_scopes
3
- from opentelemetry.propagate import extract
4
- from opentelemetry.context import Context, attach, detach
5
4
  from monocle_apptrace.instrumentation.common.span_handler import SpanHandler
5
+ from monocle_apptrace.instrumentation.common.constants import HTTP_SUCCESS_CODES
6
+ from monocle_apptrace.instrumentation.common.utils import MonocleSpanException
7
+ from urllib.parse import unquote
8
+ from opentelemetry.context import get_current
9
+ from opentelemetry.trace import Span, get_current_span
10
+ from opentelemetry.trace.propagation import _SPAN_KEY
11
+
12
+ logger = logging.getLogger(__name__)
13
+ MAX_DATA_LENGTH = 1000
6
14
  token_data = local()
7
15
  token_data.current_token = None
8
16
 
17
+ def get_route(args) -> str:
18
+ return args[0]['PATH_INFO'] if 'PATH_INFO' in args[0] else ""
19
+
20
+ def get_method(args) -> str:
21
+ return args[0]['REQUEST_METHOD'] if 'REQUEST_METHOD' in args[0] else ""
22
+
23
+ def get_params(args) -> dict:
24
+ params = args[0]['QUERY_STRING'] if 'QUERY_STRING' in args[0] else ""
25
+ return unquote(params)
26
+
27
+ def get_body(args) -> dict:
28
+ return ""
29
+
30
+ def extract_response(instance) -> str:
31
+ if hasattr(instance, 'data') and hasattr(instance, 'content_length'):
32
+ response = instance.data[0:max(instance.content_length, MAX_DATA_LENGTH)]
33
+ else:
34
+ response = ""
35
+ return response
36
+
37
+ def extract_status(instance) -> str:
38
+ status = f"{instance.status_code}" if hasattr(instance, 'status_code') else ""
39
+ if status not in HTTP_SUCCESS_CODES:
40
+ error_message = extract_response(instance)
41
+ raise MonocleSpanException(f"error: {status} - {error_message}")
42
+ return status
43
+
9
44
  def flask_pre_tracing(args):
10
45
  headers = dict()
11
46
  for key, value in args[0].items():
@@ -26,4 +61,16 @@ class FlaskSpanHandler(SpanHandler):
26
61
 
27
62
  def post_tracing(self, to_wrap, wrapped, instance, args, kwargs, return_value):
28
63
  flask_post_tracing()
29
- return super().post_tracing(to_wrap, wrapped, instance, args, kwargs, return_value)
64
+ return super().post_tracing(to_wrap, wrapped, instance, args, kwargs, return_value)
65
+
66
+ class FlaskResponseSpanHandler(SpanHandler):
67
+ def post_tracing(self, to_wrap, wrapped, instance, args, kwargs, return_value):
68
+ try:
69
+ _parent_span_context = get_current()
70
+ if _parent_span_context is not None:
71
+ parent_span: Span = _parent_span_context.get(_SPAN_KEY, None)
72
+ if parent_span is not None:
73
+ self.hydrate_events(to_wrap, wrapped, instance, args, kwargs, return_value, parent_span)
74
+ except Exception as e:
75
+ logger.info(f"Failed to propogate flask response: {e}")
76
+ super().post_tracing(to_wrap, wrapped, instance, args, kwargs, return_value)
@@ -0,0 +1,48 @@
1
+ from monocle_apptrace.instrumentation.metamodel.flask import _helper
2
+ FLASK_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['args'])
10
+ },
11
+ {
12
+ "_comment": "request method, request URI",
13
+ "attribute": "route",
14
+ "accessor": lambda arguments: _helper.get_route(arguments['args'])
15
+ },
16
+ ]
17
+ ]
18
+ }
19
+
20
+ FLASK_RESPONSE_PROCESSOR = {
21
+ "events": [
22
+ {
23
+ "name": "data.input",
24
+ "attributes": [
25
+ {
26
+ "_comment": "route params",
27
+ "attribute": "params",
28
+ "accessor": lambda arguments: _helper.get_params(arguments['args'])
29
+ }
30
+ ]
31
+ },
32
+ {
33
+ "name": "data.output",
34
+ "attributes": [
35
+ {
36
+ "_comment": "status from HTTP response",
37
+ "attribute": "status",
38
+ "accessor": lambda arguments: _helper.extract_status(arguments['instance'])
39
+ },
40
+ {
41
+ "_comment": "this is result from LLM",
42
+ "attribute": "response",
43
+ "accessor": lambda arguments: _helper.extract_response(arguments['instance'])
44
+ }
45
+ ]
46
+ }
47
+ ]
48
+ }