monocle-apptrace 0.4.1__py3-none-any.whl → 0.5.0__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 (91) hide show
  1. monocle_apptrace/__main__.py +1 -1
  2. monocle_apptrace/exporters/file_exporter.py +125 -37
  3. monocle_apptrace/instrumentation/common/__init__.py +16 -1
  4. monocle_apptrace/instrumentation/common/constants.py +14 -1
  5. monocle_apptrace/instrumentation/common/instrumentor.py +19 -152
  6. monocle_apptrace/instrumentation/common/method_wrappers.py +376 -0
  7. monocle_apptrace/instrumentation/common/span_handler.py +58 -32
  8. monocle_apptrace/instrumentation/common/utils.py +52 -15
  9. monocle_apptrace/instrumentation/common/wrapper.py +124 -18
  10. monocle_apptrace/instrumentation/common/wrapper_method.py +48 -1
  11. monocle_apptrace/instrumentation/metamodel/a2a/__init__.py +0 -0
  12. monocle_apptrace/instrumentation/metamodel/a2a/_helper.py +37 -0
  13. monocle_apptrace/instrumentation/metamodel/a2a/entities/__init__.py +0 -0
  14. monocle_apptrace/instrumentation/metamodel/a2a/entities/inference.py +112 -0
  15. monocle_apptrace/instrumentation/metamodel/a2a/methods.py +22 -0
  16. monocle_apptrace/instrumentation/metamodel/adk/__init__.py +0 -0
  17. monocle_apptrace/instrumentation/metamodel/adk/_helper.py +182 -0
  18. monocle_apptrace/instrumentation/metamodel/adk/entities/agent.py +50 -0
  19. monocle_apptrace/instrumentation/metamodel/adk/entities/tool.py +57 -0
  20. monocle_apptrace/instrumentation/metamodel/adk/methods.py +24 -0
  21. monocle_apptrace/instrumentation/metamodel/agents/__init__.py +0 -0
  22. monocle_apptrace/instrumentation/metamodel/agents/_helper.py +220 -0
  23. monocle_apptrace/instrumentation/metamodel/agents/agents_processor.py +152 -0
  24. monocle_apptrace/instrumentation/metamodel/agents/entities/__init__.py +0 -0
  25. monocle_apptrace/instrumentation/metamodel/agents/entities/inference.py +191 -0
  26. monocle_apptrace/instrumentation/metamodel/agents/methods.py +56 -0
  27. monocle_apptrace/instrumentation/metamodel/aiohttp/_helper.py +6 -11
  28. monocle_apptrace/instrumentation/metamodel/anthropic/_helper.py +112 -18
  29. monocle_apptrace/instrumentation/metamodel/anthropic/entities/inference.py +18 -10
  30. monocle_apptrace/instrumentation/metamodel/azfunc/_helper.py +13 -11
  31. monocle_apptrace/instrumentation/metamodel/azfunc/entities/http.py +5 -0
  32. monocle_apptrace/instrumentation/metamodel/azureaiinference/_helper.py +88 -8
  33. monocle_apptrace/instrumentation/metamodel/azureaiinference/entities/inference.py +22 -8
  34. monocle_apptrace/instrumentation/metamodel/botocore/_helper.py +92 -16
  35. monocle_apptrace/instrumentation/metamodel/botocore/entities/inference.py +13 -8
  36. monocle_apptrace/instrumentation/metamodel/botocore/handlers/botocore_span_handler.py +1 -1
  37. monocle_apptrace/instrumentation/metamodel/fastapi/__init__.py +0 -0
  38. monocle_apptrace/instrumentation/metamodel/fastapi/_helper.py +82 -0
  39. monocle_apptrace/instrumentation/metamodel/fastapi/entities/__init__.py +0 -0
  40. monocle_apptrace/instrumentation/metamodel/fastapi/entities/http.py +44 -0
  41. monocle_apptrace/instrumentation/metamodel/fastapi/methods.py +23 -0
  42. monocle_apptrace/instrumentation/metamodel/finish_types.py +463 -0
  43. monocle_apptrace/instrumentation/metamodel/flask/_helper.py +6 -11
  44. monocle_apptrace/instrumentation/metamodel/gemini/__init__.py +0 -0
  45. monocle_apptrace/instrumentation/metamodel/gemini/_helper.py +120 -0
  46. monocle_apptrace/instrumentation/metamodel/gemini/entities/__init__.py +0 -0
  47. monocle_apptrace/instrumentation/metamodel/gemini/entities/inference.py +86 -0
  48. monocle_apptrace/instrumentation/metamodel/gemini/entities/retrieval.py +43 -0
  49. monocle_apptrace/instrumentation/metamodel/gemini/methods.py +31 -0
  50. monocle_apptrace/instrumentation/metamodel/haystack/_helper.py +79 -8
  51. monocle_apptrace/instrumentation/metamodel/haystack/entities/inference.py +15 -10
  52. monocle_apptrace/instrumentation/metamodel/haystack/methods.py +7 -0
  53. monocle_apptrace/instrumentation/metamodel/lambdafunc/_helper.py +78 -0
  54. monocle_apptrace/instrumentation/metamodel/lambdafunc/entities/http.py +51 -0
  55. monocle_apptrace/instrumentation/metamodel/lambdafunc/methods.py +23 -0
  56. monocle_apptrace/instrumentation/metamodel/lambdafunc/wrapper.py +23 -0
  57. monocle_apptrace/instrumentation/metamodel/langchain/_helper.py +145 -19
  58. monocle_apptrace/instrumentation/metamodel/langchain/entities/inference.py +19 -10
  59. monocle_apptrace/instrumentation/metamodel/langgraph/_helper.py +67 -10
  60. monocle_apptrace/instrumentation/metamodel/langgraph/entities/inference.py +127 -20
  61. monocle_apptrace/instrumentation/metamodel/langgraph/langgraph_processor.py +46 -0
  62. monocle_apptrace/instrumentation/metamodel/langgraph/methods.py +35 -9
  63. monocle_apptrace/instrumentation/metamodel/litellm/__init__.py +0 -0
  64. monocle_apptrace/instrumentation/metamodel/litellm/_helper.py +89 -0
  65. monocle_apptrace/instrumentation/metamodel/litellm/entities/__init__.py +0 -0
  66. monocle_apptrace/instrumentation/metamodel/litellm/entities/inference.py +108 -0
  67. monocle_apptrace/instrumentation/metamodel/litellm/methods.py +19 -0
  68. monocle_apptrace/instrumentation/metamodel/llamaindex/_helper.py +227 -16
  69. monocle_apptrace/instrumentation/metamodel/llamaindex/entities/agent.py +127 -10
  70. monocle_apptrace/instrumentation/metamodel/llamaindex/entities/inference.py +13 -8
  71. monocle_apptrace/instrumentation/metamodel/llamaindex/llamaindex_processor.py +62 -0
  72. monocle_apptrace/instrumentation/metamodel/llamaindex/methods.py +68 -1
  73. monocle_apptrace/instrumentation/metamodel/mcp/__init__.py +0 -0
  74. monocle_apptrace/instrumentation/metamodel/mcp/_helper.py +118 -0
  75. monocle_apptrace/instrumentation/metamodel/mcp/entities/__init__.py +0 -0
  76. monocle_apptrace/instrumentation/metamodel/mcp/entities/inference.py +48 -0
  77. monocle_apptrace/instrumentation/metamodel/mcp/mcp_processor.py +8 -0
  78. monocle_apptrace/instrumentation/metamodel/mcp/methods.py +21 -0
  79. monocle_apptrace/instrumentation/metamodel/openai/_helper.py +188 -16
  80. monocle_apptrace/instrumentation/metamodel/openai/entities/inference.py +148 -92
  81. monocle_apptrace/instrumentation/metamodel/openai/entities/retrieval.py +1 -1
  82. monocle_apptrace/instrumentation/metamodel/teamsai/_helper.py +53 -23
  83. monocle_apptrace/instrumentation/metamodel/teamsai/entities/inference/actionplanner_output_processor.py +1 -1
  84. monocle_apptrace/instrumentation/metamodel/teamsai/entities/inference/teamsai_output_processor.py +15 -9
  85. monocle_apptrace/instrumentation/metamodel/teamsai/sample.json +0 -4
  86. {monocle_apptrace-0.4.1.dist-info → monocle_apptrace-0.5.0.dist-info}/METADATA +27 -11
  87. monocle_apptrace-0.5.0.dist-info/RECORD +142 -0
  88. monocle_apptrace-0.4.1.dist-info/RECORD +0 -96
  89. {monocle_apptrace-0.4.1.dist-info → monocle_apptrace-0.5.0.dist-info}/WHEEL +0 -0
  90. {monocle_apptrace-0.4.1.dist-info → monocle_apptrace-0.5.0.dist-info}/licenses/LICENSE +0 -0
  91. {monocle_apptrace-0.4.1.dist-info → monocle_apptrace-0.5.0.dist-info}/licenses/NOTICE +0 -0
@@ -0,0 +1,376 @@
1
+ import logging
2
+ import inspect
3
+ from typing import Dict, List, Optional, Any
4
+ from functools import wraps
5
+ import inspect
6
+ from opentelemetry.context import attach, get_current, detach
7
+ from opentelemetry.sdk.trace import Span
8
+ from opentelemetry.trace.span import INVALID_SPAN
9
+ from opentelemetry.trace import get_tracer
10
+ from contextlib import contextmanager, asynccontextmanager
11
+ from monocle_apptrace.instrumentation.common.span_handler import SpanHandler
12
+ from monocle_apptrace.instrumentation.common.wrapper import atask_wrapper, get_current_monocle_span, set_monocle_span_in_context, task_wrapper
13
+ from monocle_apptrace.instrumentation.common.utils import (
14
+ set_scope, remove_scope, http_route_handler, http_async_route_handler
15
+ )
16
+ from monocle_apptrace.instrumentation.common.constants import MONOCLE_INSTRUMENTOR
17
+ from monocle_apptrace.instrumentation.common.instrumentor import get_tracer_provider
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+
23
+
24
+ def start_trace(
25
+ span_name: Optional[str] = None,
26
+ attributes: Optional[Dict[str, Any]] = None,
27
+ events: Optional[List[Dict[str, Any]]] = None
28
+ ):
29
+ """
30
+ Starts a new trace. All the spans created after this call will be part of the same trace.
31
+
32
+ Args:
33
+ span_name: Optional custom span name. If None, uses the default span name.
34
+ attributes: Optional dictionary of custom attributes to set on the span.
35
+ events: Optional list of events to add to the span. Each event should be a dict with
36
+ 'name' and optionally 'attributes' keys.
37
+
38
+ Returns:
39
+ Token: A token representing the attached context for the span.
40
+ This token is to be used later to stop the current trace.
41
+ Returns None if tracing fails.
42
+
43
+ Raises:
44
+ Exception: The function catches all exceptions internally and logs a warning.
45
+ """
46
+ try:
47
+ tracer = get_tracer(instrumenting_module_name= MONOCLE_INSTRUMENTOR, tracer_provider= get_tracer_provider())
48
+ span_name = span_name or "custom_span"
49
+ span = tracer.start_span(name=span_name)
50
+ updated_span_context = set_monocle_span_in_context(span=span)
51
+
52
+ # Set default monocle attributes
53
+ SpanHandler.set_default_monocle_attributes(span)
54
+ if SpanHandler.is_root_span(span):
55
+ SpanHandler.set_workflow_properties(span)
56
+
57
+ # Set custom attributes and events using common method
58
+ _setup_span_attributes_and_events(span, attributes, events)
59
+
60
+ token = attach(updated_span_context)
61
+ return token
62
+ except Exception as e:
63
+ logger.warning(f"Failed to start trace: {e}")
64
+ return None
65
+
66
+ def stop_trace(
67
+ token,
68
+ final_attributes: Optional[Dict[str, Any]] = None,
69
+ final_events: Optional[List[Dict[str, Any]]] = None
70
+ ) -> None:
71
+ """
72
+ Stop the active trace. All the spans created after this will not be part of the trace.
73
+
74
+ Args:
75
+ token: The token that was returned when the trace was started. Can be None in which case only the span is ended.
76
+ final_attributes: Optional dictionary of final attributes to set on the span before ending.
77
+ final_events: Optional list of final events to add to the span before ending.
78
+
79
+ Returns:
80
+ None
81
+ """
82
+ try:
83
+ parent_span = get_current_monocle_span()
84
+ if parent_span is not None and parent_span != INVALID_SPAN:
85
+ # Set final attributes and events using common method
86
+ _setup_span_attributes_and_events(parent_span, final_attributes, final_events)
87
+ parent_span.end()
88
+ if token is not None:
89
+ detach(token)
90
+ except Exception as e:
91
+ logger.warning(f"Failed to stop trace: {e}")
92
+
93
+ def start_scope(
94
+ scope_name: str,
95
+ scope_value: Optional[str] = None
96
+ ) -> object:
97
+ """
98
+ Start a new scope with the given name and optional value. If no value is provided, a random UUID will be generated.
99
+ All the spans, across traces created after this call will have the scope attached until the scope is stopped.
100
+
101
+ Args:
102
+ scope_name: The name of the scope.
103
+ scope_value: Optional value of the scope. If None, a random UUID will be generated.
104
+
105
+ Returns:
106
+ Token: A token representing the attached context for the scope. This token is to be used later to stop the current scope.
107
+ """
108
+ try:
109
+ # Set the scope using existing utility
110
+ token = set_scope(scope_name, scope_value)
111
+ return token
112
+ except Exception as e:
113
+ logger.warning(f"Failed to start scope: {e}")
114
+ return None
115
+
116
+ def stop_scope(
117
+ token: object
118
+ ) -> None:
119
+ """
120
+ Stop the active scope. All the spans created after this will not have the scope attached.
121
+
122
+ Args:
123
+ token: The token that was returned when the scope was started.
124
+
125
+ Returns:
126
+ None
127
+ """
128
+ try:
129
+ # Remove the scope
130
+ remove_scope(token)
131
+ except Exception as e:
132
+ logger.warning(f"Failed to stop scope: {e}")
133
+ return
134
+
135
+
136
+ @contextmanager
137
+ def monocle_trace(
138
+ span_name: Optional[str] = None,
139
+ attributes: Optional[Dict[str, Any]] = None,
140
+ events: Optional[List[Dict[str, Any]]] = None
141
+ ):
142
+ """
143
+ Context manager to start and stop a trace. All the spans, across traces created within the encapsulated code will have same trace ID
144
+
145
+ Args:
146
+ span_name: Optional custom span name.
147
+ attributes: Optional dictionary of custom attributes to set on the span.
148
+ events: Optional list of events to add to the span at start.
149
+ """
150
+ try:
151
+ tracer = get_tracer(instrumenting_module_name=MONOCLE_INSTRUMENTOR, tracer_provider=get_tracer_provider())
152
+ span_name = span_name or "custom_span"
153
+
154
+ with tracer.start_as_current_span(span_name) as span:
155
+ # Set default monocle attributes
156
+ SpanHandler.set_default_monocle_attributes(span)
157
+ if SpanHandler.is_root_span(span):
158
+ SpanHandler.set_workflow_properties(span)
159
+
160
+ # Set custom attributes and events using common method
161
+ _setup_span_attributes_and_events(span, attributes, events)
162
+
163
+ try:
164
+ yield
165
+ finally:
166
+ pass
167
+
168
+
169
+ except Exception as e:
170
+ logger.warning(f"Failed in monocle_trace: {e}")
171
+ yield # Still yield to not break the context manager
172
+
173
+ @asynccontextmanager
174
+ async def amonocle_trace(
175
+ span_name: Optional[str] = None,
176
+ attributes: Optional[Dict[str, Any]] = None,
177
+ events: Optional[List[Dict[str, Any]]] = None
178
+ ):
179
+ """
180
+ Async context manager to start and stop a trace. All the spans, across traces created within the encapsulated code will have same trace ID
181
+
182
+ Args:
183
+ span_name: Optional custom span name.
184
+ attributes: Optional dictionary of custom attributes to set on the span.
185
+ events: Optional list of events to add to the span at start.
186
+ """
187
+ try:
188
+ tracer = get_tracer(instrumenting_module_name=MONOCLE_INSTRUMENTOR, tracer_provider=get_tracer_provider())
189
+ span_name = span_name or "custom_span"
190
+
191
+ with tracer.start_as_current_span(span_name) as span:
192
+ # Set default monocle attributes
193
+ SpanHandler.set_default_monocle_attributes(span)
194
+ if SpanHandler.is_root_span(span):
195
+ SpanHandler.set_workflow_properties(span)
196
+
197
+ # Set custom attributes and events using common method
198
+ _setup_span_attributes_and_events(span, attributes, events)
199
+
200
+ try:
201
+ yield
202
+ finally:
203
+ pass
204
+
205
+
206
+ except Exception as e:
207
+ logger.warning(f"Failed in amonocle_trace: {e}")
208
+ yield # Still yield to not break the context manager
209
+
210
+ @contextmanager
211
+ def monocle_trace_scope(
212
+ scope_name: str,
213
+ scope_value: Optional[str] = None
214
+ ):
215
+ """
216
+ Context manager to start and stop a scope. All the spans, across traces created within the encapsulated code will have the scope attached.
217
+
218
+ Args:
219
+ scope_name: The name of the scope.
220
+ scope_value: Optional value of the scope. If None, a random UUID will be generated.
221
+ """
222
+ token = start_scope(scope_name, scope_value)
223
+ try:
224
+ yield
225
+ finally:
226
+ stop_scope(token)
227
+
228
+ @asynccontextmanager
229
+ async def amonocle_trace_scope(
230
+ scope_name: str,
231
+ scope_value: Optional[str] = None
232
+ ):
233
+ """
234
+ Async context manager to start and stop a scope. All the spans, across traces created within the encapsulated code will have the scope attached.
235
+
236
+ Args:
237
+ scope_name: The name of the scope.
238
+ scope_value: Optional value of the scope. If None, a random UUID will be generated.
239
+ """
240
+ token = start_scope(scope_name, scope_value)
241
+ try:
242
+ yield
243
+ finally:
244
+ stop_scope(token)
245
+
246
+ def monocle_trace_scope_method(
247
+ scope_name: str,
248
+ scope_value: Optional[str] = None
249
+ ):
250
+ """
251
+ Decorator to start and stop a scope for a method. All the spans, across traces created in the method will have the scope attached.
252
+
253
+ Args:
254
+ scope_name: The name of the scope.
255
+ scope_value: Optional value of the scope. If None, a random UUID will be generated.
256
+ """
257
+ def decorator(func):
258
+ if inspect.iscoroutinefunction(func):
259
+ @wraps(func)
260
+ async def wrapper(*args, **kwargs):
261
+ async with amonocle_trace_scope(
262
+ scope_name, scope_value
263
+ ):
264
+ result = await func(*args, **kwargs)
265
+ return result
266
+ return wrapper
267
+ else:
268
+ @wraps(func)
269
+ def wrapper(*args, **kwargs):
270
+ with monocle_trace_scope(
271
+ scope_name, scope_value
272
+ ):
273
+ result = func(*args, **kwargs)
274
+ return result
275
+ return wrapper
276
+ return decorator
277
+
278
+ def monocle_trace_method(
279
+ span_name: Optional[str] = None
280
+ ):
281
+ """
282
+ Decorator to start and stop a trace for a method. All the spans created in the method will be part of the same trace.
283
+
284
+ Args:
285
+ span_name: Optional custom span name. If None, uses the decorated function's name.
286
+ """
287
+
288
+ def decorator(func):
289
+ tracer = get_tracer(instrumenting_module_name=MONOCLE_INSTRUMENTOR, tracer_provider=get_tracer_provider())
290
+ handler = SpanHandler()
291
+ source_path= func.__code__.co_filename + ":" + str(func.__code__.co_firstlineno)
292
+ # Use function name as span name if not provided
293
+ effective_span_name = span_name or func.__name__ or "custom_span"
294
+
295
+ if inspect.iscoroutinefunction(func):
296
+ @wraps(func)
297
+ async def wrapper(*args, **kwargs):
298
+ return await atask_wrapper(
299
+ tracer=tracer,
300
+ handler=handler,
301
+ to_wrap={
302
+ "span_name": effective_span_name,
303
+ "output_processor":{
304
+ "type": "custom",
305
+ }
306
+ }
307
+ )( wrapped=func,
308
+ instance=None,
309
+ source_path=source_path,
310
+ args=args,
311
+ kwargs=kwargs)
312
+ return wrapper
313
+ else:
314
+ @wraps(func)
315
+ def wrapper(*args, **kwargs):
316
+ return task_wrapper(
317
+ tracer=tracer,
318
+ handler=handler,
319
+ to_wrap={
320
+ "span_name": effective_span_name,
321
+ "output_processor":{
322
+ "type": "custom",
323
+ }
324
+ }
325
+ )( wrapped=func,
326
+ instance=None,
327
+ source_path=source_path,
328
+ args=args,
329
+ kwargs=kwargs)
330
+ return wrapper
331
+ return decorator
332
+
333
+ def monocle_trace_http_route(func):
334
+ """
335
+ Decorator to start and stop a continue traces and scope for a http route. It will also initiate new scopes from the http headers if configured in ``monocle_scopes.json``
336
+ All the spans, across traces created in the route will have the scope attached.
337
+ """
338
+ if inspect.iscoroutinefunction(func):
339
+ @wraps(func)
340
+ async def wrapper(*args, **kwargs):
341
+ return await http_async_route_handler(func, *args, **kwargs)
342
+ return wrapper
343
+ else:
344
+ @wraps(func)
345
+ def wrapper(*args, **kwargs):
346
+ return http_route_handler(func, *args, **kwargs)
347
+ return wrapper
348
+
349
+
350
+ def _setup_span_attributes_and_events(
351
+ span,
352
+ attributes: Optional[Dict[str, Any]] = None,
353
+ events: Optional[List[Dict[str, Any]]] = None
354
+ ) -> None:
355
+ """
356
+ Common method to set attributes and events on a span.
357
+
358
+ Args:
359
+ span: The span to configure
360
+ attributes: Optional dictionary of custom attributes to set on the span
361
+ events: Optional list of events to add to the span
362
+ """
363
+ # Set custom attributes if provided
364
+ if attributes:
365
+ for key, value in attributes.items():
366
+ if value is not None:
367
+ span.set_attribute(key, value)
368
+
369
+ # Add custom events if provided
370
+ if events:
371
+ for event in events:
372
+ event_name = event.get('name')
373
+ event_attributes = event.get('attributes', {})
374
+ if event_name:
375
+ span.add_event(event_name, event_attributes)
376
+
@@ -11,17 +11,30 @@ from monocle_apptrace.instrumentation.common.constants import (
11
11
  MONOCLE_SDK_VERSION, MONOCLE_SDK_LANGUAGE, MONOCLE_DETECTED_SPAN_ERROR
12
12
  )
13
13
  from monocle_apptrace.instrumentation.common.utils import set_attribute, get_scopes, MonocleSpanException, get_monocle_version
14
- from monocle_apptrace.instrumentation.common.constants import WORKFLOW_TYPE_KEY, WORKFLOW_TYPE_GENERIC
14
+ from monocle_apptrace.instrumentation.common.constants import WORKFLOW_TYPE_KEY, WORKFLOW_TYPE_GENERIC, CHILD_ERROR_CODE
15
15
 
16
16
  logger = logging.getLogger(__name__)
17
17
 
18
18
  WORKFLOW_TYPE_MAP = {
19
+ "llama_index.core.agent.workflow": WORKFLOW_TYPE_GENERIC,
19
20
  "llama_index": "workflow.llamaindex",
20
21
  "langchain": "workflow.langchain",
21
22
  "haystack": "workflow.haystack",
22
23
  "teams.ai": "workflow.teams_ai",
24
+ "langgraph": "workflow.langgraph",
25
+ "openai": "workflow.openai",
26
+ "anthropic": "workflow.anthropic",
27
+ "gemini": "workflow.gemini",
28
+ "litellm": "workflow.litellm",
23
29
  }
24
30
 
31
+ FRAMEWORK_WORKFLOW_LIST = [
32
+ "workflow.llamaindex",
33
+ "workflow.langchain",
34
+ "workflow.haystack",
35
+ "workflow.teams_ai",
36
+ "workflow.litellm",
37
+ ]
25
38
  class SpanHandler:
26
39
 
27
40
  def __init__(self,instrumentor=None):
@@ -36,7 +49,7 @@ class SpanHandler:
36
49
  def pre_tracing(self, to_wrap, wrapped, instance, args, kwargs):
37
50
  pass
38
51
 
39
- def post_tracing(self, to_wrap, wrapped, instance, args, kwargs, return_value):
52
+ def post_tracing(self, to_wrap, wrapped, instance, args, kwargs, return_value, token=None):
40
53
  pass
41
54
 
42
55
  def skip_span(self, to_wrap, wrapped, instance, args, kwargs) -> bool:
@@ -55,8 +68,11 @@ class SpanHandler:
55
68
  return span_type
56
69
 
57
70
  def pre_task_processing(self, to_wrap, wrapped, instance, args,kwargs, span):
58
- if "pipeline" in to_wrap['package']:
59
- set_attribute(QUERY, args[0]['prompt_builder']['question'])
71
+ try:
72
+ if "pipeline" in to_wrap['package']:
73
+ set_attribute(QUERY, args[0]['prompt_builder']['question'])
74
+ except Exception as e:
75
+ logger.warning("Warning: Error occurred in pre_task_processing: %s", str(e))
60
76
 
61
77
  @staticmethod
62
78
  def set_default_monocle_attributes(span: Span, source_path = "" ):
@@ -66,6 +82,9 @@ class SpanHandler:
66
82
  span.set_attribute("span_source", source_path)
67
83
  for scope_key, scope_value in get_scopes().items():
68
84
  span.set_attribute(f"scope.{scope_key}", scope_value)
85
+ workflow_name = SpanHandler.get_workflow_name(span=span)
86
+ if workflow_name:
87
+ span.set_attribute("workflow.name", workflow_name)
69
88
 
70
89
  @staticmethod
71
90
  def set_workflow_properties(span: Span, to_wrap = None):
@@ -75,17 +94,14 @@ class SpanHandler:
75
94
 
76
95
  @staticmethod
77
96
  def set_non_workflow_properties(span: Span, to_wrap = None):
78
- workflow_name = SpanHandler.get_workflow_name(span=span)
79
- if workflow_name:
80
- span.set_attribute("workflow.name", workflow_name)
81
97
  span.set_attribute("span.type", "generic")
82
98
 
83
- def post_task_processing(self, to_wrap, wrapped, instance, args, kwargs, result, span:Span):
99
+ def post_task_processing(self, to_wrap, wrapped, instance, args, kwargs, result, ex, span:Span, parent_span:Span):
84
100
  pass
85
101
 
86
102
  def hydrate_span(self, to_wrap, wrapped, instance, args, kwargs, result, span, parent_span = None, ex:Exception = None) -> bool:
87
103
  try:
88
- detected_error_in_attribute = self.hydrate_attributes(to_wrap, wrapped, instance, args, kwargs, result, span)
104
+ detected_error_in_attribute = self.hydrate_attributes(to_wrap, wrapped, instance, args, kwargs, result, span, parent_span)
89
105
  detected_error_in_event = self.hydrate_events(to_wrap, wrapped, instance, args, kwargs, result, span, parent_span, ex)
90
106
  if detected_error_in_attribute or detected_error_in_event:
91
107
  span.set_attribute(MONOCLE_DETECTED_SPAN_ERROR, True)
@@ -93,7 +109,7 @@ class SpanHandler:
93
109
  if span.status.status_code == StatusCode.UNSET and ex is None:
94
110
  span.set_status(StatusCode.OK)
95
111
 
96
- def hydrate_attributes(self, to_wrap, wrapped, instance, args, kwargs, result, span:Span) -> bool:
112
+ def hydrate_attributes(self, to_wrap, wrapped, instance, args, kwargs, result, span:Span, parent_span:Span) -> bool:
97
113
  detected_error:bool = False
98
114
  span_index = 0
99
115
  if SpanHandler.is_root_span(span):
@@ -104,6 +120,7 @@ class SpanHandler:
104
120
  skip_processors:list[str] = self.skip_processor(to_wrap, wrapped, instance, span, args, kwargs) or []
105
121
 
106
122
  if 'attributes' in output_processor and 'attributes' not in skip_processors:
123
+ arguments = {"instance":instance, "args":args, "kwargs":kwargs, "result":result, "parent_span":parent_span, "span":span}
107
124
  for processors in output_processor["attributes"]:
108
125
  for processor in processors:
109
126
  attribute = processor.get('attribute')
@@ -112,10 +129,9 @@ class SpanHandler:
112
129
  if attribute and accessor:
113
130
  attribute_name = f"entity.{span_index+1}.{attribute}"
114
131
  try:
115
- arguments = {"instance":instance, "args":args, "kwargs":kwargs, "result":result}
116
- result = accessor(arguments)
117
- if result and isinstance(result, (str, list)):
118
- span.set_attribute(attribute_name, result)
132
+ processor_result = accessor(arguments)
133
+ if processor_result and isinstance(processor_result, (str, list)):
134
+ span.set_attribute(attribute_name, processor_result)
119
135
  except MonocleSpanException as e:
120
136
  span.set_status(StatusCode.ERROR, e.message)
121
137
  detected_error = True
@@ -134,13 +150,13 @@ class SpanHandler:
134
150
  span.set_attribute("entity.count", span_index)
135
151
  return detected_error
136
152
 
137
- def hydrate_events(self, to_wrap, wrapped, instance, args, kwargs, ret_result, span, parent_span=None, ex:Exception=None) -> bool:
153
+ def hydrate_events(self, to_wrap, wrapped, instance, args, kwargs, ret_result, span: Span, parent_span=None, ex:Exception=None) -> bool:
138
154
  detected_error:bool = False
139
155
  if 'output_processor' in to_wrap and to_wrap["output_processor"] is not None:
140
156
  output_processor=to_wrap['output_processor']
141
157
  skip_processors:list[str] = self.skip_processor(to_wrap, wrapped, instance, span, args, kwargs) or []
142
158
 
143
- arguments = {"instance": instance, "args": args, "kwargs": kwargs, "result": ret_result, "exception":ex}
159
+ arguments = {"instance": instance, "args": args, "kwargs": kwargs, "result": ret_result, "exception":ex, "parent_span":parent_span, "span": span}
144
160
  # Process events if they are defined in the output_processor.
145
161
  # In case of inference.modelapi skip the event processing unless the span has an exception
146
162
  if 'events' in output_processor and ('events' not in skip_processors or ex is not None):
@@ -170,10 +186,16 @@ class SpanHandler:
170
186
  except Exception as e:
171
187
  logger.debug(f"Error evaluating accessor for attribute '{attribute_key}': {e}")
172
188
  matching_timestamp = getattr(ret_result, "timestamps", {}).get(event_name, None)
173
- if isinstance(matching_timestamp, int):
174
- span.add_event(name=event_name, attributes=event_attributes, timestamp=matching_timestamp)
175
- else:
176
- span.add_event(name=event_name, attributes=event_attributes)
189
+ alreadyExist = False
190
+ for existing_event in span.events:
191
+ if event_name == existing_event.name:
192
+ existing_event.attributes._dict.update(event_attributes)
193
+ alreadyExist = True
194
+ if not alreadyExist:
195
+ if isinstance(matching_timestamp, int):
196
+ span.add_event(name=event_name, attributes=event_attributes, timestamp=matching_timestamp)
197
+ else:
198
+ span.add_event(name=event_name, attributes=event_attributes)
177
199
  return detected_error
178
200
 
179
201
  @staticmethod
@@ -187,6 +209,16 @@ class SpanHandler:
187
209
  workflow_type = SpanHandler.get_workflow_type(to_wrap)
188
210
  span.set_attribute(f"entity.{span_index}.type", workflow_type)
189
211
 
212
+ def get_workflow_name_in_progress(self) -> str:
213
+ return get_value(WORKFLOW_TYPE_KEY)
214
+
215
+ @staticmethod
216
+ def is_framework_workflow(workflow_type) -> bool:
217
+ return workflow_type in FRAMEWORK_WORKFLOW_LIST
218
+
219
+ def is_framework_span_in_progress(self) -> bool:
220
+ return SpanHandler.is_framework_workflow(self.get_workflow_name_in_progress())
221
+
190
222
  @staticmethod
191
223
  def get_workflow_type(to_wrap):
192
224
  # workflow type
@@ -194,7 +226,7 @@ class SpanHandler:
194
226
  if to_wrap is not None:
195
227
  package_name = to_wrap.get('package')
196
228
  for (package, framework_workflow_type) in WORKFLOW_TYPE_MAP.items():
197
- if (package_name is not None and package in package_name):
229
+ if (package_name is not None and package_name.startswith(package)):
198
230
  workflow_type = framework_workflow_type
199
231
  break
200
232
  return workflow_type
@@ -231,7 +263,7 @@ class SpanHandler:
231
263
  token = None
232
264
  if to_wrap:
233
265
  workflow_type = SpanHandler.get_workflow_type(to_wrap)
234
- if workflow_type != WORKFLOW_TYPE_GENERIC:
266
+ if SpanHandler.is_framework_workflow(workflow_type):
235
267
  token = attach(set_value(WORKFLOW_TYPE_KEY,
236
268
  SpanHandler.get_workflow_type(to_wrap), context))
237
269
  return token
@@ -252,21 +284,15 @@ class SpanHandler:
252
284
 
253
285
 
254
286
  class NonFrameworkSpanHandler(SpanHandler):
255
-
256
- def get_workflow_name_in_progress(self) -> str:
257
- return get_value(WORKFLOW_TYPE_KEY)
258
-
259
- def is_framework_span_in_progess(self) -> bool:
260
- return self.get_workflow_name_in_progress() in WORKFLOW_TYPE_MAP.values()
261
-
262
287
  # If the language framework is being executed, then skip generating direct openAI attributes and events
263
288
  def skip_processor(self, to_wrap, wrapped, instance, span, args, kwargs) -> list[str]:
264
- if self.is_framework_span_in_progess():
289
+ if super().is_framework_span_in_progress():
265
290
  return ["attributes", "events"]
266
291
 
267
292
  def set_span_type(self, to_wrap, wrapped, instance, output_processor, span:Span, args, kwargs) -> str:
268
293
  span_type = super().set_span_type(to_wrap, wrapped, instance, output_processor, span, args, kwargs)
269
- if self.is_framework_span_in_progess() and span_type is not None:
294
+ if self.is_framework_span_in_progress() and span_type is not None:
270
295
  span_type = span_type+".modelapi"
271
296
  span.set_attribute("span.type", span_type)
272
- return span_type
297
+ return span_type
298
+