monocle-apptrace 0.3.0b4__py3-none-any.whl → 0.3.0b6__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 (45) hide show
  1. monocle_apptrace/__main__.py +19 -0
  2. monocle_apptrace/exporters/azure/blob_exporter.py +7 -0
  3. monocle_apptrace/exporters/monocle_exporters.py +5 -4
  4. monocle_apptrace/exporters/okahu/okahu_exporter.py +1 -1
  5. monocle_apptrace/instrumentation/common/constants.py +22 -0
  6. monocle_apptrace/instrumentation/common/instrumentor.py +119 -39
  7. monocle_apptrace/instrumentation/common/span_handler.py +103 -44
  8. monocle_apptrace/instrumentation/common/utils.py +161 -5
  9. monocle_apptrace/instrumentation/common/wrapper.py +58 -37
  10. monocle_apptrace/instrumentation/common/wrapper_method.py +34 -7
  11. monocle_apptrace/instrumentation/metamodel/botocore/_helper.py +0 -31
  12. monocle_apptrace/instrumentation/metamodel/botocore/handlers/botocore_span_handler.py +25 -0
  13. monocle_apptrace/instrumentation/metamodel/botocore/methods.py +6 -6
  14. monocle_apptrace/instrumentation/metamodel/flask/__init__.py +0 -0
  15. monocle_apptrace/instrumentation/metamodel/flask/_helper.py +29 -0
  16. monocle_apptrace/instrumentation/metamodel/flask/methods.py +13 -0
  17. monocle_apptrace/instrumentation/metamodel/haystack/entities/inference.py +1 -1
  18. monocle_apptrace/instrumentation/metamodel/haystack/methods.py +2 -1
  19. monocle_apptrace/instrumentation/metamodel/langchain/_helper.py +4 -0
  20. monocle_apptrace/instrumentation/metamodel/langchain/entities/inference.py +3 -2
  21. monocle_apptrace/instrumentation/metamodel/langchain/methods.py +12 -6
  22. monocle_apptrace/instrumentation/metamodel/langgraph/__init__.py +0 -0
  23. monocle_apptrace/instrumentation/metamodel/langgraph/_helper.py +48 -0
  24. monocle_apptrace/instrumentation/metamodel/langgraph/entities/__init__.py +0 -0
  25. monocle_apptrace/instrumentation/metamodel/langgraph/entities/inference.py +56 -0
  26. monocle_apptrace/instrumentation/metamodel/langgraph/methods.py +14 -0
  27. monocle_apptrace/instrumentation/metamodel/llamaindex/_helper.py +37 -19
  28. monocle_apptrace/instrumentation/metamodel/llamaindex/entities/agent.py +47 -0
  29. monocle_apptrace/instrumentation/metamodel/llamaindex/entities/inference.py +5 -3
  30. monocle_apptrace/instrumentation/metamodel/llamaindex/methods.py +15 -3
  31. monocle_apptrace/instrumentation/metamodel/openai/__init__.py +0 -0
  32. monocle_apptrace/instrumentation/metamodel/openai/_helper.py +112 -0
  33. monocle_apptrace/instrumentation/metamodel/openai/entities/__init__.py +0 -0
  34. monocle_apptrace/instrumentation/metamodel/openai/entities/inference.py +71 -0
  35. monocle_apptrace/instrumentation/metamodel/openai/entities/retrieval.py +43 -0
  36. monocle_apptrace/instrumentation/metamodel/openai/methods.py +45 -0
  37. monocle_apptrace/instrumentation/metamodel/requests/__init__.py +4 -0
  38. monocle_apptrace/instrumentation/metamodel/requests/_helper.py +31 -0
  39. monocle_apptrace/instrumentation/metamodel/requests/methods.py +12 -0
  40. {monocle_apptrace-0.3.0b4.dist-info → monocle_apptrace-0.3.0b6.dist-info}/METADATA +2 -1
  41. monocle_apptrace-0.3.0b6.dist-info/RECORD +68 -0
  42. monocle_apptrace-0.3.0b4.dist-info/RECORD +0 -48
  43. {monocle_apptrace-0.3.0b4.dist-info → monocle_apptrace-0.3.0b6.dist-info}/WHEEL +0 -0
  44. {monocle_apptrace-0.3.0b4.dist-info → monocle_apptrace-0.3.0b6.dist-info}/licenses/LICENSE +0 -0
  45. {monocle_apptrace-0.3.0b4.dist-info → monocle_apptrace-0.3.0b6.dist-info}/licenses/NOTICE +0 -0
@@ -1,16 +1,33 @@
1
- import logging
2
- from typing import Callable, Generic, Optional, TypeVar
1
+ import logging, json
2
+ import os
3
+ from typing import Callable, Generic, Optional, TypeVar, Mapping
4
+ import threading, asyncio
3
5
 
4
- from opentelemetry.context import attach, detach, get_current, get_value, set_value
5
- from opentelemetry.trace import NonRecordingSpan, Span
6
+ from opentelemetry.context import attach, detach, get_current, get_value, set_value, Context
7
+ from opentelemetry.trace import NonRecordingSpan, Span, get_tracer
6
8
  from opentelemetry.trace.propagation import _SPAN_KEY
9
+ from opentelemetry.sdk.trace import id_generator, TracerProvider
10
+ from opentelemetry.propagate import inject, extract
11
+ from opentelemetry import baggage
12
+ from monocle_apptrace.instrumentation.common.constants import MONOCLE_SCOPE_NAME_PREFIX, SCOPE_METHOD_FILE, SCOPE_CONFIG_PATH, llm_type_map
7
13
 
8
14
  T = TypeVar('T')
9
15
  U = TypeVar('U')
10
16
 
11
17
  logger = logging.getLogger(__name__)
12
18
 
19
+ monocle_tracer_provider: TracerProvider = None
13
20
  embedding_model_context = {}
21
+ scope_id_generator = id_generator.RandomIdGenerator()
22
+ http_scopes:dict[str:str] = {}
23
+
24
+ def set_tracer_provider(tracer_provider: TracerProvider):
25
+ global monocle_tracer_provider
26
+ monocle_tracer_provider = tracer_provider
27
+
28
+ def get_tracer_provider() -> TracerProvider:
29
+ global monocle_tracer_provider
30
+ return monocle_tracer_provider
14
31
 
15
32
  def set_span_attribute(span, name, value):
16
33
  if value is not None:
@@ -139,6 +156,131 @@ def get_nested_value(data, keys):
139
156
  def get_keys_as_tuple(dictionary, *keys):
140
157
  return tuple(next((value for key, value in dictionary.items() if key.endswith(k) and value is not None), None) for k in keys)
141
158
 
159
+ def load_scopes() -> dict:
160
+ methods_data = []
161
+ scope_methods = []
162
+ try:
163
+ scope_config_file_path = os.environ.get(SCOPE_CONFIG_PATH,
164
+ os.path.join(os.getcwd(), SCOPE_METHOD_FILE))
165
+ with open(scope_config_file_path) as f:
166
+ methods_data = json.load(f)
167
+ for method in methods_data:
168
+ if method.get('http_header'):
169
+ http_scopes[method.get('http_header')] = method.get('scope_name')
170
+ else:
171
+ scope_methods.append(method)
172
+ except Exception as e:
173
+ logger.debug(f"Error loading scope methods from file: {e}")
174
+ return scope_methods
175
+
176
+ def __generate_scope_id() -> str:
177
+ global scope_id_generator
178
+ return f"{hex(scope_id_generator.generate_trace_id())}"
179
+
180
+ def set_scope(scope_name: str, scope_value:str = None) -> object:
181
+ return set_scopes({scope_name: scope_value})
182
+
183
+ def set_scopes(scopes:dict[str, object], baggage_context:Context = None) -> object:
184
+ if baggage_context is None:
185
+ baggage_context:Context = get_current()
186
+ for scope_name, scope_value in scopes.items():
187
+ if scope_value is None:
188
+ scope_value = __generate_scope_id()
189
+ baggage_context = baggage.set_baggage(f"{MONOCLE_SCOPE_NAME_PREFIX}{scope_name}", scope_value, baggage_context)
190
+
191
+ token:object = attach(baggage_context)
192
+ return token
193
+
194
+ def remove_scope(token:object) -> None:
195
+ remove_scopes(token)
196
+
197
+ def remove_scopes(token:object) -> None:
198
+ if token is not None:
199
+ detach(token)
200
+
201
+ def get_scopes() -> dict[str, object]:
202
+ monocle_scopes:dict[str, object] = {}
203
+ for key, val in baggage.get_all().items():
204
+ if key.startswith(MONOCLE_SCOPE_NAME_PREFIX):
205
+ monocle_scopes[key[len(MONOCLE_SCOPE_NAME_PREFIX):]] = val
206
+ return monocle_scopes
207
+
208
+ def get_baggage_for_scopes():
209
+ baggage_context:Context = None
210
+ for scope_key, scope_value in get_scopes():
211
+ monocle_scope_name = f"{MONOCLE_SCOPE_NAME_PREFIX}{scope_key}"
212
+ baggage_context = baggage.set_baggage(monocle_scope_name, scope_value, context=baggage_context)
213
+ return baggage_context
214
+
215
+ def set_scopes_from_baggage(baggage_context:Context):
216
+ for scope_key, scope_value in baggage.get_all(baggage_context):
217
+ if scope_key.startswith(MONOCLE_SCOPE_NAME_PREFIX):
218
+ scope_name = scope_key[len(MONOCLE_SCOPE_NAME_PREFIX):]
219
+ set_scope(scope_name, scope_value)
220
+
221
+ def extract_http_headers(headers) -> object:
222
+ global http_scopes
223
+ trace_context:Context = extract(headers, context=get_current())
224
+ imported_scope:dict[str, object] = {}
225
+ for http_header, http_scope in http_scopes.items():
226
+ if http_header in headers:
227
+ imported_scope[http_scope] = f"{http_header}: {headers[http_header]}"
228
+ token = set_scopes(imported_scope, trace_context)
229
+ return token
230
+
231
+ def clear_http_scopes(token:object) -> None:
232
+ global http_scopes
233
+ remove_scopes(token)
234
+
235
+ def http_route_handler(func, *args, **kwargs):
236
+ if 'req' in kwargs and hasattr(kwargs['req'], 'headers'):
237
+ headers = kwargs['req'].headers
238
+ else:
239
+ headers = None
240
+ token = None
241
+ if headers is not None:
242
+ token = extract_http_headers(headers)
243
+ try:
244
+ result = func(*args, **kwargs)
245
+ return result
246
+ finally:
247
+ if token is not None:
248
+ clear_http_scopes(token)
249
+
250
+ async def http_async_route_handler(func, *args, **kwargs):
251
+ if 'req' in kwargs and hasattr(kwargs['req'], 'headers'):
252
+ headers = kwargs['req'].headers
253
+ else:
254
+ headers = None
255
+ return async_wrapper(func, None, headers, *args, **kwargs)
256
+
257
+ def run_async_with_scope(method, scope_name, headers, *args, **kwargs):
258
+ token = None
259
+ if scope_name:
260
+ token = set_scope(scope_name)
261
+ elif headers:
262
+ token = extract_http_headers(headers)
263
+ try:
264
+ return asyncio.run(method(*args, **kwargs))
265
+ finally:
266
+ if token:
267
+ remove_scope(token)
268
+
269
+ def async_wrapper(method, scope_name=None, headers=None, *args, **kwargs):
270
+ try:
271
+ run_loop = asyncio.get_running_loop()
272
+ except RuntimeError:
273
+ run_loop = None
274
+
275
+ if run_loop and run_loop.is_running():
276
+ results = []
277
+ thread = threading.Thread(target=lambda: results.append(run_async_with_scope(method, scope_name, headers, *args, **kwargs)))
278
+ thread.start()
279
+ thread.join()
280
+ return_value = results[0] if len(results) > 0 else None
281
+ return return_value
282
+ else:
283
+ return run_async_with_scope(method, scope_name, headers, *args, **kwargs)
142
284
 
143
285
  class Option(Generic[T]):
144
286
  def __init__(self, value: Optional[T]):
@@ -168,4 +310,18 @@ def try_option(func: Callable[..., T], *args, **kwargs) -> Option[T]:
168
310
  try:
169
311
  return Option(func(*args, **kwargs))
170
312
  except Exception:
171
- return Option(None)
313
+ return Option(None)
314
+
315
+ def get_llm_type(instance):
316
+ try:
317
+ llm_type = llm_type_map.get(type(instance).__name__.lower())
318
+ return llm_type
319
+ except:
320
+ pass
321
+
322
+ def resolve_from_alias(my_map, alias):
323
+ """Find a alias that is not none from list of aliases"""
324
+ for i in alias:
325
+ if i in my_map.keys():
326
+ return my_map[i]
327
+ return None
@@ -1,20 +1,20 @@
1
1
  # pylint: disable=protected-access
2
2
  import logging
3
-
4
3
  from opentelemetry.trace import Tracer
4
+ from opentelemetry.context import set_value, attach, detach, get_value
5
5
 
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
9
  with_tracer_wrapper,
10
+ set_scope,
11
+ remove_scope,
12
+ async_wrapper
10
13
  )
11
- from monocle_apptrace.instrumentation.metamodel.botocore import _helper
14
+ from monocle_apptrace.instrumentation.common.constants import WORKFLOW_TYPE_KEY
12
15
  logger = logging.getLogger(__name__)
13
16
 
14
-
15
- @with_tracer_wrapper
16
- def task_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, args, kwargs):
17
-
17
+ def wrapper_processor(async_task: bool, tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, args, kwargs):
18
18
  # Some Langchain objects are wrapped elsewhere, so we ignore them here
19
19
  if instance.__class__.__name__ in ("AgentExecutor"):
20
20
  return wrapped(*args, **kwargs)
@@ -26,44 +26,65 @@ def task_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instanc
26
26
  else:
27
27
  name = get_fully_qualified_class_name(instance)
28
28
 
29
- handler.validate(to_wrap, wrapped, instance, args, kwargs)
30
- handler.set_context_properties(to_wrap, wrapped, instance, args, kwargs)
31
-
32
- if to_wrap.get('skip_span'):
33
- return_value = wrapped(*args, **kwargs)
34
- _helper.botocore_processor(tracer, to_wrap, wrapped, instance, args, kwargs, return_value)
29
+ 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
+ token = SpanHandler.attach_workflow_type(to_wrap=to_wrap)
35
+ if skip_scan:
36
+ if async_task:
37
+ return_value = async_wrapper(wrapped, None, None, *args, **kwargs)
38
+ else:
39
+ return_value = wrapped(*args, **kwargs)
40
+ else:
41
+ return_value = span_processor(name, async_task, tracer, handler, to_wrap, wrapped, instance, args, kwargs)
35
42
  return return_value
43
+ finally:
44
+ handler.detach_workflow_type(token)
45
+ handler.post_tracing(to_wrap, wrapped, instance, args, kwargs, return_value)
36
46
 
47
+ def span_processor(name: str, async_task: bool, tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, args, kwargs):
48
+ # For singleton spans, eg OpenAI inference generate a workflow span to format the workflow specific attributes
49
+ return_value = None
37
50
  with tracer.start_as_current_span(name) as span:
38
- handler.pre_task_processing(to_wrap, wrapped, instance, args, span)
39
- return_value = wrapped(*args, **kwargs)
40
- handler.hydrate_span(to_wrap, wrapped, instance, args, kwargs, return_value, span)
41
- handler.post_task_processing(to_wrap, wrapped, instance, args, kwargs, return_value, span)
42
-
51
+ # Since Spanhandler can be overridden, ensure we set default monocle attributes.
52
+ SpanHandler.set_default_monocle_attributes(span)
53
+ if SpanHandler.is_root_span(span):
54
+ SpanHandler.set_workflow_properties(span, to_wrap)
55
+ if handler.is_non_workflow_root_span(span, to_wrap):
56
+ # This is a direct API call of a non-framework type, call the span_processor recursively for the actual span
57
+ return_value = span_processor(name, async_task, tracer, handler, to_wrap, wrapped, instance, args, kwargs)
58
+ else:
59
+ handler.pre_task_processing(to_wrap, wrapped, instance, args, kwargs, span)
60
+ if async_task:
61
+ return_value = async_wrapper(wrapped, None, None, *args, **kwargs)
62
+ else:
63
+ return_value = wrapped(*args, **kwargs)
64
+ handler.hydrate_span(to_wrap, wrapped, instance, args, kwargs, return_value, span)
65
+ handler.post_task_processing(to_wrap, wrapped, instance, args, kwargs, return_value, span)
43
66
  return return_value
44
67
 
68
+ @with_tracer_wrapper
69
+ def task_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, args, kwargs):
70
+ return wrapper_processor(False, tracer, handler, to_wrap, wrapped, instance, args, kwargs)
45
71
 
46
72
  @with_tracer_wrapper
47
73
  async def atask_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, args, kwargs):
48
- """Instruments and calls every function defined in TO_WRAP."""
49
-
50
- # Some Langchain objects are wrapped elsewhere, so we ignore them here
51
- if instance.__class__.__name__ in ("AgentExecutor"):
52
- return wrapped(*args, **kwargs)
53
-
54
- if hasattr(instance, "name") and instance.name:
55
- name = f"{to_wrap.get('span_name')}.{instance.name.lower()}"
56
- elif to_wrap.get("span_name"):
57
- name = to_wrap.get("span_name")
58
- else:
59
- name = get_fully_qualified_class_name(instance)
60
-
61
- handler.validate(to_wrap, wrapped, instance, args, kwargs)
62
- handler.set_context_properties(to_wrap, wrapped, instance, args, kwargs)
63
- with tracer.start_as_current_span(name) as span:
64
- handler.pre_task_processing(to_wrap, wrapped, instance, args, span)
65
- return_value = wrapped(*args, **kwargs)
66
- handler.hydrate_span(to_wrap, wrapped, instance, args, kwargs, return_value, span)
67
- handler.post_task_processing(to_wrap, wrapped, instance, args, kwargs, return_value, span)
74
+ return wrapper_processor(True, tracer, handler, to_wrap, wrapped, instance, args, kwargs)
68
75
 
76
+ @with_tracer_wrapper
77
+ def scope_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, args, kwargs):
78
+ scope_name = to_wrap.get('scope_name', None)
79
+ if scope_name:
80
+ token = set_scope(scope_name)
81
+ return_value = wrapped(*args, **kwargs)
82
+ if scope_name:
83
+ remove_scope(token)
69
84
  return return_value
85
+
86
+ @with_tracer_wrapper
87
+ async def ascope_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, args, kwargs):
88
+ scope_name = to_wrap.get('scope_name', None)
89
+ return_value = async_wrapper(wrapped, scope_name, None, *args, **kwargs)
90
+ return return_value
@@ -1,12 +1,20 @@
1
1
  # pylint: disable=too-few-public-methods
2
- from monocle_apptrace.instrumentation.common.wrapper import task_wrapper
2
+ from typing import Any, Dict
3
+ from monocle_apptrace.instrumentation.common.wrapper import task_wrapper, scope_wrapper
4
+ from monocle_apptrace.instrumentation.common.span_handler import SpanHandler, NonFrameworkSpanHandler
3
5
  from monocle_apptrace.instrumentation.metamodel.botocore.methods import BOTOCORE_METHODS
6
+ from monocle_apptrace.instrumentation.metamodel.botocore.handlers.botocore_span_handler import BotoCoreSpanHandler
4
7
  from monocle_apptrace.instrumentation.metamodel.langchain.methods import (
5
8
  LANGCHAIN_METHODS,
6
9
  )
7
10
  from monocle_apptrace.instrumentation.metamodel.llamaindex.methods import (LLAMAINDEX_METHODS, )
8
11
  from monocle_apptrace.instrumentation.metamodel.haystack.methods import (HAYSTACK_METHODS, )
9
-
12
+ from monocle_apptrace.instrumentation.metamodel.openai.methods import (OPENAI_METHODS,)
13
+ from monocle_apptrace.instrumentation.metamodel.langgraph.methods import LANGGRAPH_METHODS
14
+ 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.requests.methods import (REQUESTS_METHODS, )
17
+ from monocle_apptrace.instrumentation.metamodel.requests._helper import RequestSpanHandler
10
18
 
11
19
  class WrapperMethod:
12
20
  def __init__(
@@ -17,16 +25,23 @@ class WrapperMethod:
17
25
  span_name: str = None,
18
26
  output_processor : str = None,
19
27
  wrapper_method = task_wrapper,
20
- span_handler = 'default'
28
+ span_handler = 'default',
29
+ scope_name: str = None,
30
+ span_type: str = None
21
31
  ):
22
32
  self.package = package
23
33
  self.object = object_name
24
34
  self.method = method
25
35
  self.span_name = span_name
26
36
  self.output_processor=output_processor
27
- self.span_handler = span_handler
37
+ self.span_type = span_type
28
38
 
29
- self.wrapper_method = wrapper_method
39
+ self.span_handler:SpanHandler.__class__ = span_handler
40
+ self.scope_name = scope_name
41
+ if scope_name:
42
+ self.wrapper_method = scope_wrapper
43
+ else:
44
+ self.wrapper_method = wrapper_method
30
45
 
31
46
  def to_dict(self) -> dict:
32
47
  # Create a dictionary representation of the instance
@@ -37,9 +52,21 @@ class WrapperMethod:
37
52
  'span_name': self.span_name,
38
53
  'output_processor': self.output_processor,
39
54
  'wrapper_method': self.wrapper_method,
40
- 'span_handler': self.span_handler
55
+ 'span_handler': self.span_handler,
56
+ 'scope_name': self.scope_name,
57
+ 'span_type': self.span_type
41
58
  }
42
59
  return instance_dict
43
60
 
61
+ def get_span_handler(self) -> SpanHandler:
62
+ return self.span_handler()
63
+
64
+ DEFAULT_METHODS_LIST = LANGCHAIN_METHODS + LLAMAINDEX_METHODS + HAYSTACK_METHODS + BOTOCORE_METHODS + FLASK_METHODS + REQUESTS_METHODS + LANGGRAPH_METHODS + OPENAI_METHODS
44
65
 
45
- DEFAULT_METHODS_LIST = LANGCHAIN_METHODS + LLAMAINDEX_METHODS + HAYSTACK_METHODS + BOTOCORE_METHODS
66
+ MONOCLE_SPAN_HANDLERS: Dict[str, SpanHandler] = {
67
+ "default": SpanHandler(),
68
+ "botocore_handler": BotoCoreSpanHandler(),
69
+ "flask_handler": FlaskSpanHandler(),
70
+ "request_handler": RequestSpanHandler(),
71
+ "non_framework_handler": NonFrameworkSpanHandler()
72
+ }
@@ -81,37 +81,6 @@ def resolve_from_alias(my_map, alias):
81
81
  return my_map[i]
82
82
  return None
83
83
 
84
-
85
- def botocore_processor(tracer, to_wrap, wrapped, instance, args, kwargs,return_value):
86
- service_name = kwargs.get("service_name")
87
- service_method_mapping = {
88
- "sagemaker-runtime": "invoke_endpoint",
89
- "bedrock-runtime": "converse",
90
- }
91
- if service_name in service_method_mapping:
92
- method_name = service_method_mapping[service_name]
93
- original_method = getattr(return_value, method_name, None)
94
-
95
- if original_method:
96
- instrumented_method = _instrumented_endpoint_invoke(
97
- to_wrap, wrapped,return_value, original_method, tracer, service_name
98
- )
99
- setattr(return_value, method_name, instrumented_method)
100
-
101
-
102
- def _instrumented_endpoint_invoke(to_wrap,wrapped, instance, fn, tracer,service_name):
103
- @wraps(fn)
104
- def with_instrumentation(*args, **kwargs):
105
- span_name="botocore-"+service_name+"-invoke-endpoint"
106
- handler = SpanHandler()
107
- with tracer.start_as_current_span(span_name) as span:
108
- response = fn(*args, **kwargs)
109
- handler.hydrate_span(to_wrap, span=span,wrapped=wrapped, instance=instance,args=args, kwargs=kwargs, result=response)
110
- return response
111
-
112
- return with_instrumentation
113
-
114
-
115
84
  def update_span_from_llm_response(response, instance):
116
85
  meta_dict = {}
117
86
  if response is not None and isinstance(response, dict) and "usage" in response:
@@ -0,0 +1,25 @@
1
+ from monocle_apptrace.instrumentation.common.span_handler import SpanHandler
2
+
3
+ class BotoCoreSpanHandler(SpanHandler):
4
+
5
+ def _botocore_processor(self, to_wrap, wrapped, instance, args, kwargs, return_value):
6
+ service_name = kwargs.get("service_name")
7
+ service_method_mapping = {
8
+ "sagemaker-runtime": "invoke_endpoint",
9
+ "bedrock-runtime": "converse",
10
+ }
11
+ if service_name in service_method_mapping:
12
+ method_name = service_method_mapping[service_name]
13
+ original_method = getattr(return_value, method_name, None)
14
+ span_name = "botocore-" + service_name + "-invoke-endpoint"
15
+ # wrap_util(original_method, span_name)
16
+ if original_method:
17
+ instrumentor = self.instrumentor
18
+ if instrumentor:
19
+ instrumented_method = instrumentor(to_wrap, wrapped, span_name, return_value, original_method)
20
+ setattr(return_value, method_name, instrumented_method)
21
+
22
+ def post_tracing(self, to_wrap, wrapped, instance, args, kwargs, return_value):
23
+ self._botocore_processor(to_wrap=to_wrap, wrapped=wrapped, instance=instance, return_value=return_value, args=args,
24
+ kwargs=kwargs)
25
+ return super().pre_tracing(to_wrap, wrapped, instance, args, kwargs)
@@ -3,14 +3,14 @@ from monocle_apptrace.instrumentation.metamodel.botocore.entities.inference impo
3
3
  INFERENCE,
4
4
  )
5
5
 
6
- BOTOCORE_METHODS = [{
7
-
6
+ BOTOCORE_METHODS = [
7
+ {
8
8
  "package": "botocore.client",
9
9
  "object": "ClientCreator",
10
10
  "method": "create_client",
11
11
  "wrapper_method": task_wrapper,
12
- "skip_span": True,
13
- "output_processor": INFERENCE
14
-
15
- }
12
+ "span_handler":"botocore_handler",
13
+ "output_processor": INFERENCE,
14
+ "skip_span": True
15
+ }
16
16
  ]
@@ -0,0 +1,29 @@
1
+ from threading import local
2
+ 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
+ from monocle_apptrace.instrumentation.common.span_handler import SpanHandler
6
+ token_data = local()
7
+ token_data.current_token = None
8
+
9
+ def flask_pre_tracing(args):
10
+ headers = dict()
11
+ for key, value in args[0].items():
12
+ if key.startswith("HTTP_"):
13
+ new_key = key[5:].lower().replace("_", "-")
14
+ headers[new_key] = value
15
+ token_data.current_token = extract_http_headers(headers)
16
+
17
+ def flask_post_tracing():
18
+ clear_http_scopes(token_data.current_token)
19
+ token_data.current_token = None
20
+
21
+ class FlaskSpanHandler(SpanHandler):
22
+
23
+ def pre_tracing(self, to_wrap, wrapped, instance, args, kwargs):
24
+ flask_pre_tracing(args)
25
+ return super().pre_tracing(to_wrap, wrapped, instance, args, kwargs)
26
+
27
+ def post_tracing(self, to_wrap, wrapped, instance, args, kwargs, return_value):
28
+ flask_post_tracing()
29
+ return super().post_tracing(to_wrap, wrapped, instance, args, kwargs, return_value)
@@ -0,0 +1,13 @@
1
+ from monocle_apptrace.instrumentation.common.wrapper import task_wrapper
2
+
3
+ FLASK_METHODS = [
4
+ {
5
+ "package": "flask.app",
6
+ "object": "Flask",
7
+ "method": "wsgi_app",
8
+ "span_name": "Flask.wsgi_app",
9
+ "wrapper_method": task_wrapper,
10
+ "span_handler": "flask_handler",
11
+ "skip_span": True
12
+ }
13
+ ]
@@ -9,7 +9,7 @@ INFERENCE = {
9
9
  {
10
10
  "_comment": "provider type ,name , deployment , inference_endpoint",
11
11
  "attribute": "type",
12
- "accessor": lambda arguments: 'inference.azure_oai'
12
+ "accessor": lambda arguments: 'inference.azure_openai'
13
13
  },
14
14
  {
15
15
  "attribute": "provider_name",
@@ -37,6 +37,7 @@ HAYSTACK_METHODS = [
37
37
  "package": "haystack.core.pipeline.pipeline",
38
38
  "object": "Pipeline",
39
39
  "method": "run",
40
- "wrapper_method": task_wrapper
40
+ "wrapper_method": task_wrapper,
41
+ "span_type": "workflow"
41
42
  }
42
43
  ]
@@ -22,6 +22,10 @@ def extract_messages(args):
22
22
  if args and isinstance(args, (list, tuple)) and hasattr(args[0], 'text'):
23
23
  return [args[0].text]
24
24
  if args and isinstance(args, (list, tuple)) and len(args) > 0:
25
+ if isinstance(args[0], list) and len(args[0]) > 0:
26
+ first_msg = args[0][0]
27
+ if hasattr(first_msg, 'content') and hasattr(first_msg, 'type') and first_msg.type == "human":
28
+ return args[0][0].content
25
29
  if hasattr(args[0], "messages") and isinstance(args[0].messages, list):
26
30
  for msg in args[0].messages:
27
31
  if hasattr(msg, 'content') and hasattr(msg, 'type'):
@@ -1,7 +1,7 @@
1
1
  from monocle_apptrace.instrumentation.metamodel.langchain import (
2
2
  _helper,
3
3
  )
4
- from monocle_apptrace.instrumentation.common.utils import resolve_from_alias
4
+ from monocle_apptrace.instrumentation.common.utils import resolve_from_alias, get_llm_type
5
5
 
6
6
  INFERENCE = {
7
7
  "type": "inference",
@@ -10,7 +10,8 @@ INFERENCE = {
10
10
  {
11
11
  "_comment": "provider type ,name , deployment , inference_endpoint",
12
12
  "attribute": "type",
13
- "accessor": lambda arguments: 'inference.azure_oai'
13
+ "accessor": lambda arguments: 'inference.' + (get_llm_type(arguments['instance']) or 'generic')
14
+
14
15
  },
15
16
  {
16
17
  "attribute": "provider_name",
@@ -11,13 +11,15 @@ LANGCHAIN_METHODS = [
11
11
  "package": "langchain.prompts.base",
12
12
  "object": "BasePromptTemplate",
13
13
  "method": "invoke",
14
- "wrapper_method": task_wrapper
14
+ "wrapper_method": task_wrapper,
15
+ "span_type": "workflow"
15
16
  },
16
17
  {
17
18
  "package": "langchain.prompts.base",
18
19
  "object": "BasePromptTemplate",
19
20
  "method": "ainvoke",
20
- "wrapper_method": atask_wrapper
21
+ "wrapper_method": atask_wrapper,
22
+ "span_type": "workflow"
21
23
  },
22
24
  {
23
25
  "package": "langchain.chat_models.base",
@@ -80,26 +82,30 @@ LANGCHAIN_METHODS = [
80
82
  "package": "langchain.schema",
81
83
  "object": "BaseOutputParser",
82
84
  "method": "invoke",
83
- "wrapper_method": task_wrapper
85
+ "wrapper_method": task_wrapper,
86
+ "span_type": "workflow"
84
87
  },
85
88
  {
86
89
  "package": "langchain.schema",
87
90
  "object": "BaseOutputParser",
88
91
  "method": "ainvoke",
89
- "wrapper_method": atask_wrapper
92
+ "wrapper_method": atask_wrapper,
93
+ "span_type": "workflow"
90
94
  },
91
95
  {
92
96
  "package": "langchain.schema.runnable",
93
97
  "object": "RunnableSequence",
94
98
  "method": "invoke",
95
99
  "span_name": "langchain.workflow",
96
- "wrapper_method": task_wrapper
100
+ "wrapper_method": task_wrapper,
101
+ "span_type": "workflow"
97
102
  },
98
103
  {
99
104
  "package": "langchain.schema.runnable",
100
105
  "object": "RunnableSequence",
101
106
  "method": "ainvoke",
102
107
  "span_name": "langchain.workflow",
103
- "wrapper_method": atask_wrapper
108
+ "wrapper_method": atask_wrapper,
109
+ "span_type": "workflow"
104
110
  }
105
111
  ]