monocle-apptrace 0.5.0b1__py3-none-any.whl → 0.5.1b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of monocle-apptrace might be problematic. Click here for more details.
- monocle_apptrace/exporters/file_exporter.py +2 -1
- monocle_apptrace/instrumentation/common/__init__.py +7 -5
- monocle_apptrace/instrumentation/common/constants.py +103 -12
- monocle_apptrace/instrumentation/common/instrumentor.py +1 -6
- monocle_apptrace/instrumentation/common/method_wrappers.py +10 -125
- monocle_apptrace/instrumentation/common/scope_wrapper.py +126 -0
- monocle_apptrace/instrumentation/common/span_handler.py +32 -8
- monocle_apptrace/instrumentation/common/utils.py +34 -3
- monocle_apptrace/instrumentation/common/wrapper.py +208 -41
- monocle_apptrace/instrumentation/common/wrapper_method.py +9 -1
- monocle_apptrace/instrumentation/metamodel/a2a/entities/inference.py +3 -1
- monocle_apptrace/instrumentation/metamodel/adk/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/adk/_helper.py +206 -0
- monocle_apptrace/instrumentation/metamodel/adk/entities/agent.py +111 -0
- monocle_apptrace/instrumentation/metamodel/adk/entities/tool.py +59 -0
- monocle_apptrace/instrumentation/metamodel/adk/methods.py +31 -0
- monocle_apptrace/instrumentation/metamodel/agents/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/agents/_helper.py +225 -0
- monocle_apptrace/instrumentation/metamodel/agents/agents_processor.py +174 -0
- monocle_apptrace/instrumentation/metamodel/agents/entities/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/agents/entities/inference.py +196 -0
- monocle_apptrace/instrumentation/metamodel/agents/methods.py +55 -0
- monocle_apptrace/instrumentation/metamodel/aiohttp/entities/http.py +2 -1
- monocle_apptrace/instrumentation/metamodel/anthropic/_helper.py +82 -5
- monocle_apptrace/instrumentation/metamodel/anthropic/entities/inference.py +6 -1
- monocle_apptrace/instrumentation/metamodel/azfunc/entities/http.py +2 -1
- monocle_apptrace/instrumentation/metamodel/azureaiinference/entities/inference.py +2 -1
- monocle_apptrace/instrumentation/metamodel/botocore/entities/inference.py +2 -1
- monocle_apptrace/instrumentation/metamodel/fastapi/entities/http.py +2 -1
- monocle_apptrace/instrumentation/metamodel/fastapi/methods.py +18 -18
- monocle_apptrace/instrumentation/metamodel/finish_types.py +79 -1
- monocle_apptrace/instrumentation/metamodel/flask/entities/http.py +2 -1
- monocle_apptrace/instrumentation/metamodel/gemini/entities/inference.py +7 -3
- monocle_apptrace/instrumentation/metamodel/gemini/entities/retrieval.py +2 -1
- monocle_apptrace/instrumentation/metamodel/gemini/methods.py +8 -1
- monocle_apptrace/instrumentation/metamodel/haystack/_helper.py +64 -0
- monocle_apptrace/instrumentation/metamodel/haystack/entities/inference.py +12 -1
- monocle_apptrace/instrumentation/metamodel/haystack/entities/retrieval.py +2 -1
- monocle_apptrace/instrumentation/metamodel/lambdafunc/entities/http.py +2 -1
- monocle_apptrace/instrumentation/metamodel/langchain/_helper.py +18 -0
- monocle_apptrace/instrumentation/metamodel/langchain/entities/inference.py +6 -1
- monocle_apptrace/instrumentation/metamodel/langchain/entities/retrieval.py +2 -1
- monocle_apptrace/instrumentation/metamodel/langgraph/_helper.py +6 -0
- monocle_apptrace/instrumentation/metamodel/langgraph/entities/inference.py +10 -5
- monocle_apptrace/instrumentation/metamodel/langgraph/langgraph_processor.py +11 -4
- monocle_apptrace/instrumentation/metamodel/langgraph/methods.py +27 -23
- monocle_apptrace/instrumentation/metamodel/litellm/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/litellm/_helper.py +89 -0
- monocle_apptrace/instrumentation/metamodel/litellm/entities/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/litellm/entities/inference.py +109 -0
- monocle_apptrace/instrumentation/metamodel/litellm/methods.py +19 -0
- monocle_apptrace/instrumentation/metamodel/llamaindex/entities/agent.py +9 -4
- monocle_apptrace/instrumentation/metamodel/llamaindex/entities/inference.py +2 -1
- monocle_apptrace/instrumentation/metamodel/llamaindex/entities/retrieval.py +2 -1
- monocle_apptrace/instrumentation/metamodel/llamaindex/llamaindex_processor.py +14 -3
- monocle_apptrace/instrumentation/metamodel/llamaindex/methods.py +1 -1
- monocle_apptrace/instrumentation/metamodel/mcp/_helper.py +2 -1
- monocle_apptrace/instrumentation/metamodel/mcp/entities/inference.py +3 -1
- monocle_apptrace/instrumentation/metamodel/mcp/mcp_processor.py +0 -5
- monocle_apptrace/instrumentation/metamodel/mcp/methods.py +1 -1
- monocle_apptrace/instrumentation/metamodel/openai/_helper.py +110 -5
- monocle_apptrace/instrumentation/metamodel/openai/entities/inference.py +59 -13
- monocle_apptrace/instrumentation/metamodel/requests/entities/http.py +2 -1
- monocle_apptrace/instrumentation/metamodel/teamsai/_helper.py +12 -1
- monocle_apptrace/instrumentation/metamodel/teamsai/entities/inference/teamsai_output_processor.py +12 -1
- monocle_apptrace/mcp_server.py +94 -0
- {monocle_apptrace-0.5.0b1.dist-info → monocle_apptrace-0.5.1b1.dist-info}/METADATA +41 -11
- {monocle_apptrace-0.5.0b1.dist-info → monocle_apptrace-0.5.1b1.dist-info}/RECORD +72 -53
- monocle_apptrace-0.5.1b1.dist-info/entry_points.txt +2 -0
- {monocle_apptrace-0.5.0b1.dist-info → monocle_apptrace-0.5.1b1.dist-info}/WHEEL +0 -0
- {monocle_apptrace-0.5.0b1.dist-info → monocle_apptrace-0.5.1b1.dist-info}/licenses/LICENSE +0 -0
- {monocle_apptrace-0.5.0b1.dist-info → monocle_apptrace-0.5.1b1.dist-info}/licenses/NOTICE +0 -0
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
# pylint: disable=protected-access
|
|
2
|
+
from contextlib import contextmanager
|
|
3
|
+
import os
|
|
4
|
+
from typing import AsyncGenerator, Iterator, Optional
|
|
2
5
|
import logging
|
|
3
6
|
from opentelemetry.trace import Tracer
|
|
7
|
+
from opentelemetry.trace.propagation import _SPAN_KEY, set_span_in_context, get_current_span
|
|
8
|
+
from opentelemetry.trace import propagation
|
|
4
9
|
from opentelemetry.context import set_value, attach, detach, get_value
|
|
10
|
+
from opentelemetry.context import create_key, get_value, set_value
|
|
11
|
+
from opentelemetry.context.context import Context
|
|
12
|
+
from opentelemetry.trace.span import INVALID_SPAN, Span
|
|
13
|
+
from opentelemetry.trace.status import StatusCode
|
|
5
14
|
|
|
6
15
|
from monocle_apptrace.instrumentation.common.span_handler import SpanHandler
|
|
7
16
|
from monocle_apptrace.instrumentation.common.utils import (
|
|
@@ -9,10 +18,14 @@ from monocle_apptrace.instrumentation.common.utils import (
|
|
|
9
18
|
with_tracer_wrapper,
|
|
10
19
|
set_scope,
|
|
11
20
|
remove_scope,
|
|
12
|
-
|
|
21
|
+
get_current_monocle_span,
|
|
22
|
+
set_monocle_span_in_context
|
|
13
23
|
)
|
|
14
|
-
from monocle_apptrace.instrumentation.common.constants import WORKFLOW_TYPE_KEY, ADD_NEW_WORKFLOW
|
|
24
|
+
from monocle_apptrace.instrumentation.common.constants import WORKFLOW_TYPE_KEY, ADD_NEW_WORKFLOW, AGENTIC_SPANS
|
|
25
|
+
from monocle_apptrace.instrumentation.common.scope_wrapper import monocle_trace_scope
|
|
26
|
+
|
|
15
27
|
logger = logging.getLogger(__name__)
|
|
28
|
+
ISOLATE_MONOCLE_SPANS = os.getenv("MONOCLE_ISOLATE_SPANS", "true").lower() == "true"
|
|
16
29
|
|
|
17
30
|
def get_auto_close_span(to_wrap, kwargs):
|
|
18
31
|
try:
|
|
@@ -38,6 +51,8 @@ def pre_process_span(name, tracer, handler, add_workflow_span, to_wrap, wrapped,
|
|
|
38
51
|
def post_process_span(handler, to_wrap, wrapped, instance, args, kwargs, return_value, span, parent_span, ex):
|
|
39
52
|
if not (SpanHandler.is_root_span(span) or get_value(ADD_NEW_WORKFLOW) == True):
|
|
40
53
|
try:
|
|
54
|
+
if parent_span == INVALID_SPAN:
|
|
55
|
+
parent_span = None
|
|
41
56
|
handler.hydrate_span(to_wrap, wrapped, instance, args, kwargs, return_value, span, parent_span, ex)
|
|
42
57
|
except Exception as e:
|
|
43
58
|
logger.info(f"Warning: Error occurred in hydrate_span: {e}")
|
|
@@ -60,33 +75,43 @@ def monocle_wrapper_span_processor(tracer: Tracer, handler: SpanHandler, to_wrap
|
|
|
60
75
|
return_value = None
|
|
61
76
|
span_status = None
|
|
62
77
|
auto_close_span = get_auto_close_span(to_wrap, kwargs)
|
|
63
|
-
parent_span =
|
|
64
|
-
with tracer
|
|
78
|
+
parent_span = get_current_monocle_span()
|
|
79
|
+
with start_as_monocle_span(tracer, name, auto_close_span) as span:
|
|
65
80
|
pre_process_span(name, tracer, handler, add_workflow_span, to_wrap, wrapped, instance, args, kwargs, span, source_path)
|
|
66
81
|
|
|
67
82
|
if SpanHandler.is_root_span(span) or add_workflow_span:
|
|
68
83
|
# Recursive call for the actual span
|
|
69
84
|
return_value, span_status = monocle_wrapper_span_processor(tracer, handler, to_wrap, wrapped, instance, source_path, False, args, kwargs)
|
|
70
|
-
span.set_status(
|
|
85
|
+
span.set_status(StatusCode.OK)
|
|
71
86
|
if not auto_close_span:
|
|
72
87
|
span.end()
|
|
73
88
|
else:
|
|
74
89
|
ex:Exception = None
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
post_process_span(handler, to_wrap, wrapped, instance, args, kwargs, ret_val, span, parent_span ,ex)
|
|
84
|
-
if not auto_close_span:
|
|
85
|
-
span.end()
|
|
86
|
-
if ex is None and not auto_close_span and to_wrap.get("output_processor") and to_wrap.get("output_processor").get("response_processor"):
|
|
87
|
-
to_wrap.get("output_processor").get("response_processor")(to_wrap, return_value, post_process_span_internal)
|
|
88
|
-
else:
|
|
90
|
+
to_wrap = get_wrapper_with_next_processor(to_wrap, handler, instance, args, kwargs)
|
|
91
|
+
if has_more_processors(to_wrap):
|
|
92
|
+
try:
|
|
93
|
+
return_value, span_status = monocle_wrapper_span_processor(tracer, handler, to_wrap, wrapped, instance, source_path, False, args, kwargs)
|
|
94
|
+
except Exception as e:
|
|
95
|
+
ex = e
|
|
96
|
+
raise
|
|
97
|
+
finally:
|
|
89
98
|
post_process_span_internal(return_value)
|
|
99
|
+
else:
|
|
100
|
+
try:
|
|
101
|
+
with SpanHandler.workflow_type(to_wrap, span):
|
|
102
|
+
return_value = wrapped(*args, **kwargs)
|
|
103
|
+
except Exception as e:
|
|
104
|
+
ex = e
|
|
105
|
+
raise
|
|
106
|
+
finally:
|
|
107
|
+
def post_process_span_internal(ret_val):
|
|
108
|
+
post_process_span(handler, to_wrap, wrapped, instance, args, kwargs, ret_val, span, parent_span ,ex)
|
|
109
|
+
if not auto_close_span:
|
|
110
|
+
span.end()
|
|
111
|
+
if ex is None and not auto_close_span and to_wrap.get("output_processor") and to_wrap.get("output_processor").get("response_processor"):
|
|
112
|
+
to_wrap.get("output_processor").get("response_processor")(to_wrap, return_value, post_process_span_internal)
|
|
113
|
+
else:
|
|
114
|
+
post_process_span_internal(return_value)
|
|
90
115
|
span_status = span.status
|
|
91
116
|
return return_value, span_status
|
|
92
117
|
|
|
@@ -105,7 +130,8 @@ def monocle_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, inst
|
|
|
105
130
|
add_workflow_span = get_value(ADD_NEW_WORKFLOW) == True
|
|
106
131
|
token = attach(set_value(ADD_NEW_WORKFLOW, False))
|
|
107
132
|
try:
|
|
108
|
-
|
|
133
|
+
with monocle_trace_scope(get_builtin_scope_names(to_wrap)):
|
|
134
|
+
return_value, span_status = monocle_wrapper_span_processor(tracer, handler, to_wrap, wrapped, instance, source_path, add_workflow_span, args, kwargs)
|
|
109
135
|
finally:
|
|
110
136
|
detach(token)
|
|
111
137
|
return return_value
|
|
@@ -115,41 +141,103 @@ def monocle_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, inst
|
|
|
115
141
|
except Exception as e:
|
|
116
142
|
logger.info(f"Warning: Error occurred in post_tracing: {e}")
|
|
117
143
|
|
|
118
|
-
async def amonocle_wrapper_span_processor(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, add_workflow_span,
|
|
144
|
+
async def amonocle_wrapper_span_processor(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, add_workflow_span,
|
|
145
|
+
args, kwargs):
|
|
119
146
|
# Main span processing logic
|
|
120
147
|
name = get_span_name(to_wrap, instance)
|
|
121
148
|
return_value = None
|
|
122
149
|
span_status = None
|
|
123
150
|
auto_close_span = get_auto_close_span(to_wrap, kwargs)
|
|
124
|
-
parent_span =
|
|
125
|
-
with tracer
|
|
151
|
+
parent_span = get_current_monocle_span()
|
|
152
|
+
with start_as_monocle_span(tracer, name, auto_close_span) as span:
|
|
126
153
|
pre_process_span(name, tracer, handler, add_workflow_span, to_wrap, wrapped, instance, args, kwargs, span, source_path)
|
|
127
154
|
|
|
128
155
|
if SpanHandler.is_root_span(span) or add_workflow_span:
|
|
129
156
|
# Recursive call for the actual span
|
|
130
157
|
return_value, span_status = await amonocle_wrapper_span_processor(tracer, handler, to_wrap, wrapped, instance, source_path, False, args, kwargs)
|
|
131
|
-
span.set_status(
|
|
158
|
+
span.set_status(StatusCode.OK)
|
|
132
159
|
if not auto_close_span:
|
|
133
160
|
span.end()
|
|
134
161
|
else:
|
|
135
162
|
ex:Exception = None
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
post_process_span(handler, to_wrap, wrapped, instance, args, kwargs, ret_val, span, parent_span, ex)
|
|
145
|
-
if not auto_close_span:
|
|
146
|
-
span.end()
|
|
147
|
-
if ex is None and not auto_close_span and to_wrap.get("output_processor") and to_wrap.get("output_processor").get("response_processor"):
|
|
148
|
-
to_wrap.get("output_processor").get("response_processor")(to_wrap, return_value, post_process_span_internal)
|
|
149
|
-
else:
|
|
163
|
+
to_wrap = get_wrapper_with_next_processor(to_wrap, handler, instance, args, kwargs)
|
|
164
|
+
if has_more_processors(to_wrap):
|
|
165
|
+
try:
|
|
166
|
+
return_value, span_status = await amonocle_wrapper_span_processor(tracer, handler, to_wrap, wrapped, instance, source_path, False, args, kwargs)
|
|
167
|
+
except Exception as e:
|
|
168
|
+
ex = e
|
|
169
|
+
raise
|
|
170
|
+
finally:
|
|
150
171
|
post_process_span_internal(return_value)
|
|
172
|
+
else:
|
|
173
|
+
try:
|
|
174
|
+
with SpanHandler.workflow_type(to_wrap, span):
|
|
175
|
+
return_value = await wrapped(*args, **kwargs)
|
|
176
|
+
except Exception as e:
|
|
177
|
+
ex = e
|
|
178
|
+
raise
|
|
179
|
+
finally:
|
|
180
|
+
def post_process_span_internal(ret_val):
|
|
181
|
+
post_process_span(handler, to_wrap, wrapped, instance, args, kwargs, ret_val, span, parent_span, ex)
|
|
182
|
+
if not auto_close_span:
|
|
183
|
+
span.end()
|
|
184
|
+
if ex is None and not auto_close_span and to_wrap.get("output_processor") and to_wrap.get("output_processor").get("response_processor"):
|
|
185
|
+
to_wrap.get("output_processor").get("response_processor")(to_wrap, return_value, post_process_span_internal)
|
|
186
|
+
else:
|
|
187
|
+
post_process_span_internal(return_value)
|
|
151
188
|
span_status = span.status
|
|
152
|
-
return return_value,
|
|
189
|
+
return return_value, span_status
|
|
190
|
+
|
|
191
|
+
async def amonocle_iter_wrapper_span_processor(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, add_workflow_span,
|
|
192
|
+
args, kwargs) -> AsyncGenerator[any, None]:
|
|
193
|
+
# Main span processing logic
|
|
194
|
+
name = get_span_name(to_wrap, instance)
|
|
195
|
+
auto_close_span = get_auto_close_span(to_wrap, kwargs)
|
|
196
|
+
parent_span = get_current_monocle_span()
|
|
197
|
+
last_item = None
|
|
198
|
+
|
|
199
|
+
with start_as_monocle_span(tracer, name, auto_close_span) as span:
|
|
200
|
+
pre_process_span(name, tracer, handler, add_workflow_span, to_wrap, wrapped, instance, args, kwargs, span, source_path)
|
|
201
|
+
|
|
202
|
+
if SpanHandler.is_root_span(span) or add_workflow_span:
|
|
203
|
+
# Recursive call for the actual span
|
|
204
|
+
async for item in amonocle_iter_wrapper_span_processor(tracer, handler, to_wrap, wrapped, instance, source_path, False, args, kwargs):
|
|
205
|
+
yield item
|
|
206
|
+
span.set_status(StatusCode.OK)
|
|
207
|
+
if not auto_close_span:
|
|
208
|
+
span.end()
|
|
209
|
+
else:
|
|
210
|
+
ex:Exception = None
|
|
211
|
+
to_wrap = get_wrapper_with_next_processor(to_wrap, handler, instance, args, kwargs)
|
|
212
|
+
if has_more_processors(to_wrap):
|
|
213
|
+
try:
|
|
214
|
+
async for item in amonocle_iter_wrapper_span_processor(tracer, handler, to_wrap, wrapped, instance, source_path, False, args, kwargs):
|
|
215
|
+
last_item = item
|
|
216
|
+
yield item
|
|
217
|
+
except Exception as e:
|
|
218
|
+
ex = e
|
|
219
|
+
raise
|
|
220
|
+
finally:
|
|
221
|
+
post_process_span(handler, to_wrap, wrapped, instance, args, kwargs, last_item, span, parent_span, ex)
|
|
222
|
+
else:
|
|
223
|
+
try:
|
|
224
|
+
with SpanHandler.workflow_type(to_wrap, span):
|
|
225
|
+
async for item in wrapped(*args, **kwargs):
|
|
226
|
+
last_item = item
|
|
227
|
+
yield item
|
|
228
|
+
except Exception as e:
|
|
229
|
+
ex = e
|
|
230
|
+
raise
|
|
231
|
+
finally:
|
|
232
|
+
def post_process_span_internal(ret_val):
|
|
233
|
+
post_process_span(handler, to_wrap, wrapped, instance, args, kwargs, ret_val, span, parent_span, ex)
|
|
234
|
+
if not auto_close_span:
|
|
235
|
+
span.end()
|
|
236
|
+
if ex is None and not auto_close_span and to_wrap.get("output_processor") and to_wrap.get("output_processor").get("response_processor"):
|
|
237
|
+
to_wrap.get("output_processor").get("response_processor")(to_wrap, None, post_process_span_internal)
|
|
238
|
+
else:
|
|
239
|
+
post_process_span_internal(last_item)
|
|
240
|
+
return
|
|
153
241
|
|
|
154
242
|
async def amonocle_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs):
|
|
155
243
|
return_value = None
|
|
@@ -166,7 +254,9 @@ async def amonocle_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrappe
|
|
|
166
254
|
add_workflow_span = get_value(ADD_NEW_WORKFLOW) == True
|
|
167
255
|
token = attach(set_value(ADD_NEW_WORKFLOW, False))
|
|
168
256
|
try:
|
|
169
|
-
|
|
257
|
+
with monocle_trace_scope(get_builtin_scope_names(to_wrap)):
|
|
258
|
+
return_value, span_status = await amonocle_wrapper_span_processor(tracer, handler, to_wrap, wrapped, instance, source_path,
|
|
259
|
+
add_workflow_span, args, kwargs)
|
|
170
260
|
finally:
|
|
171
261
|
detach(token)
|
|
172
262
|
return return_value
|
|
@@ -176,6 +266,33 @@ async def amonocle_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrappe
|
|
|
176
266
|
except Exception as e:
|
|
177
267
|
logger.info(f"Warning: Error occurred in post_tracing: {e}")
|
|
178
268
|
|
|
269
|
+
async def amonocle_iter_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs) -> AsyncGenerator[any, None]:
|
|
270
|
+
token = None
|
|
271
|
+
pre_trace_token = None
|
|
272
|
+
try:
|
|
273
|
+
try:
|
|
274
|
+
pre_trace_token = handler.pre_tracing(to_wrap, wrapped, instance, args, kwargs)
|
|
275
|
+
except Exception as e:
|
|
276
|
+
logger.info(f"Warning: Error occurred in pre_tracing: {e}")
|
|
277
|
+
if to_wrap.get('skip_span', False) or handler.skip_span(to_wrap, wrapped, instance, args, kwargs):
|
|
278
|
+
async for item in wrapped(*args, **kwargs):
|
|
279
|
+
yield item
|
|
280
|
+
else:
|
|
281
|
+
add_workflow_span = get_value(ADD_NEW_WORKFLOW) == True
|
|
282
|
+
token = attach(set_value(ADD_NEW_WORKFLOW, False))
|
|
283
|
+
try:
|
|
284
|
+
with monocle_trace_scope(get_builtin_scope_names(to_wrap)):
|
|
285
|
+
async for item in amonocle_iter_wrapper_span_processor(tracer, handler, to_wrap, wrapped, instance, source_path, add_workflow_span, args, kwargs):
|
|
286
|
+
yield item
|
|
287
|
+
finally:
|
|
288
|
+
detach(token)
|
|
289
|
+
return
|
|
290
|
+
finally:
|
|
291
|
+
try:
|
|
292
|
+
handler.post_tracing(to_wrap, wrapped, instance, args, kwargs, None, pre_trace_token)
|
|
293
|
+
except Exception as e:
|
|
294
|
+
logger.info(f"Warning: Error occurred in post_tracing: {e}")
|
|
295
|
+
|
|
179
296
|
@with_tracer_wrapper
|
|
180
297
|
def task_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs):
|
|
181
298
|
return monocle_wrapper(tracer, handler, to_wrap, wrapped, instance, source_path, args, kwargs)
|
|
@@ -184,6 +301,12 @@ def task_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instanc
|
|
|
184
301
|
async def atask_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs):
|
|
185
302
|
return await amonocle_wrapper(tracer, handler, to_wrap, wrapped, instance, source_path, args, kwargs)
|
|
186
303
|
|
|
304
|
+
@with_tracer_wrapper
|
|
305
|
+
async def atask_iter_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs) -> AsyncGenerator[any, None]:
|
|
306
|
+
async for item in amonocle_iter_wrapper(tracer, handler, to_wrap, wrapped, instance, source_path, args, kwargs):
|
|
307
|
+
yield item
|
|
308
|
+
return
|
|
309
|
+
|
|
187
310
|
@with_tracer_wrapper
|
|
188
311
|
def scope_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs):
|
|
189
312
|
scope_name = to_wrap.get('scope_name', None)
|
|
@@ -245,4 +368,48 @@ def evaluate_scope_values(args, kwargs, to_wrap, scope_values):
|
|
|
245
368
|
scope_values = None
|
|
246
369
|
if isinstance(scope_values, dict):
|
|
247
370
|
return scope_values
|
|
248
|
-
return None
|
|
371
|
+
return None
|
|
372
|
+
|
|
373
|
+
@contextmanager
|
|
374
|
+
def start_as_monocle_span(tracer: Tracer, name: str, auto_close_span: bool) -> Iterator["Span"]:
|
|
375
|
+
""" Wrapper to OTEL start_as_current_span to isolate monocle and non monocle spans.
|
|
376
|
+
This essentiall links monocle and non-monocle spans separately which is default behavior.
|
|
377
|
+
It can be optionally overridden by setting the environment variable MONOCLE_ISOLATE_SPANS to false.
|
|
378
|
+
"""
|
|
379
|
+
if not ISOLATE_MONOCLE_SPANS:
|
|
380
|
+
# If not isolating, use the default start_as_current_span
|
|
381
|
+
yield tracer.start_as_current_span(name, end_on_exit=auto_close_span)
|
|
382
|
+
return
|
|
383
|
+
original_span = get_current_span()
|
|
384
|
+
monocle_span_token = attach(set_span_in_context(get_current_monocle_span()))
|
|
385
|
+
with tracer.start_as_current_span(name, end_on_exit=auto_close_span) as span:
|
|
386
|
+
new_monocle_token = attach(set_monocle_span_in_context(span))
|
|
387
|
+
original_span_token = attach(set_span_in_context(original_span))
|
|
388
|
+
yield span
|
|
389
|
+
detach(original_span_token)
|
|
390
|
+
detach(new_monocle_token)
|
|
391
|
+
detach(monocle_span_token)
|
|
392
|
+
|
|
393
|
+
def get_builtin_scope_names(to_wrap) -> str:
|
|
394
|
+
output_processor = to_wrap.get("output_processor", None)
|
|
395
|
+
span_type = output_processor.get("type", None) if output_processor else None
|
|
396
|
+
if span_type and span_type in AGENTIC_SPANS:
|
|
397
|
+
return span_type
|
|
398
|
+
return None
|
|
399
|
+
|
|
400
|
+
def get_wrapper_with_next_processor(to_wrap, handler, instance, args, kwargs):
|
|
401
|
+
if has_more_processors(to_wrap):
|
|
402
|
+
next_output_processor_list = to_wrap.get('output_processor_list',[]).copy()
|
|
403
|
+
while len(next_output_processor_list) > 0:
|
|
404
|
+
next_output_processor = next_output_processor_list.pop(0)
|
|
405
|
+
if handler.should_skip(next_output_processor, instance, args, kwargs):
|
|
406
|
+
next_output_processor = None
|
|
407
|
+
else:
|
|
408
|
+
break
|
|
409
|
+
to_wrap = to_wrap.copy()
|
|
410
|
+
to_wrap['output_processor_list'] = next_output_processor_list
|
|
411
|
+
to_wrap['output_processor'] = next_output_processor
|
|
412
|
+
return to_wrap
|
|
413
|
+
|
|
414
|
+
def has_more_processors(to_wrap) -> bool:
|
|
415
|
+
return len(to_wrap.get('output_processor_list', [])) > 0
|
|
@@ -15,6 +15,8 @@ from monocle_apptrace.instrumentation.metamodel.openai.methods import (OPENAI_ME
|
|
|
15
15
|
from monocle_apptrace.instrumentation.metamodel.openai._helper import OpenAISpanHandler
|
|
16
16
|
from monocle_apptrace.instrumentation.metamodel.langgraph.methods import LANGGRAPH_METHODS
|
|
17
17
|
from monocle_apptrace.instrumentation.metamodel.langgraph.langgraph_processor import LanggraphAgentHandler, LanggraphToolHandler
|
|
18
|
+
from monocle_apptrace.instrumentation.metamodel.agents.methods import AGENTS_METHODS
|
|
19
|
+
from monocle_apptrace.instrumentation.metamodel.agents.agents_processor import AgentsSpanHandler
|
|
18
20
|
from monocle_apptrace.instrumentation.metamodel.flask.methods import (FLASK_METHODS, )
|
|
19
21
|
from monocle_apptrace.instrumentation.metamodel.flask._helper import FlaskSpanHandler, FlaskResponseSpanHandler
|
|
20
22
|
from monocle_apptrace.instrumentation.metamodel.requests.methods import (REQUESTS_METHODS, )
|
|
@@ -33,6 +35,8 @@ from monocle_apptrace.instrumentation.metamodel.lambdafunc.methods import LAMBDA
|
|
|
33
35
|
from monocle_apptrace.instrumentation.metamodel.mcp.methods import MCP_METHODS
|
|
34
36
|
from monocle_apptrace.instrumentation.metamodel.mcp.mcp_processor import MCPAgentHandler
|
|
35
37
|
from monocle_apptrace.instrumentation.metamodel.a2a.methods import A2A_CLIENT_METHODS
|
|
38
|
+
from monocle_apptrace.instrumentation.metamodel.litellm.methods import LITELLM_METHODS
|
|
39
|
+
from monocle_apptrace.instrumentation.metamodel.adk.methods import ADK_METHODS
|
|
36
40
|
|
|
37
41
|
class WrapperMethod:
|
|
38
42
|
def __init__(
|
|
@@ -90,6 +94,7 @@ DEFAULT_METHODS_LIST = (
|
|
|
90
94
|
FLASK_METHODS +
|
|
91
95
|
REQUESTS_METHODS +
|
|
92
96
|
LANGGRAPH_METHODS +
|
|
97
|
+
AGENTS_METHODS +
|
|
93
98
|
OPENAI_METHODS +
|
|
94
99
|
TEAMAI_METHODS +
|
|
95
100
|
ANTHROPIC_METHODS +
|
|
@@ -100,7 +105,9 @@ DEFAULT_METHODS_LIST = (
|
|
|
100
105
|
FASTAPI_METHODS +
|
|
101
106
|
LAMBDA_HTTP_METHODS +
|
|
102
107
|
MCP_METHODS +
|
|
103
|
-
A2A_CLIENT_METHODS
|
|
108
|
+
A2A_CLIENT_METHODS +
|
|
109
|
+
LITELLM_METHODS +
|
|
110
|
+
ADK_METHODS
|
|
104
111
|
)
|
|
105
112
|
|
|
106
113
|
MONOCLE_SPAN_HANDLERS: Dict[str, SpanHandler] = {
|
|
@@ -118,6 +125,7 @@ MONOCLE_SPAN_HANDLERS: Dict[str, SpanHandler] = {
|
|
|
118
125
|
"fastapi_response_handler": FastAPIResponseSpanHandler(),
|
|
119
126
|
"langgraph_agent_handler": LanggraphAgentHandler(),
|
|
120
127
|
"langgraph_tool_handler": LanggraphToolHandler(),
|
|
128
|
+
"agents_agent_handler": AgentsSpanHandler(),
|
|
121
129
|
"llamaindex_tool_handler": LlamaIndexToolHandler(),
|
|
122
130
|
"llamaindex_agent_handler": LlamaIndexAgentHandler(),
|
|
123
131
|
"llamaindex_single_agent_tool_handler": LlamaIndexSingleAgenttToolHandlerWrapper(),
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
from monocle_apptrace.instrumentation.common.constants import SPAN_SUBTYPES, SPAN_TYPES
|
|
1
2
|
from monocle_apptrace.instrumentation.metamodel.a2a import _helper
|
|
2
3
|
|
|
3
4
|
A2A_CLIENT = {
|
|
4
|
-
"type":
|
|
5
|
+
"type": SPAN_TYPES.AGENTIC_INVOCATION,
|
|
6
|
+
"subtype": SPAN_SUBTYPES.ROUTING,
|
|
5
7
|
"attributes": [
|
|
6
8
|
[
|
|
7
9
|
{
|
|
File without changes
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Helper functions for the ADK (Agent Development Kit) metamodel instrumentation.
|
|
3
|
+
This module provides utility functions to extract various attributes from agent and tool instances.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from ast import arguments
|
|
7
|
+
from typing import Any, Dict, Optional
|
|
8
|
+
from monocle_apptrace.instrumentation.metamodel.finish_types import map_adk_finish_reason_to_finish_type
|
|
9
|
+
|
|
10
|
+
def get_model_name(args):
|
|
11
|
+
return args[0].model if hasattr(args[0], 'model') else None
|
|
12
|
+
|
|
13
|
+
def get_inference_type(arguments):
|
|
14
|
+
""" Find inference type from argument """
|
|
15
|
+
return 'inference.gemini' ## TBD verify non-gemini inference types
|
|
16
|
+
|
|
17
|
+
def extract_inference_endpoint(instance):
|
|
18
|
+
""" Get inference service end point"""
|
|
19
|
+
if hasattr(instance,'api_client') and hasattr(instance.api_client, '_api_client'):
|
|
20
|
+
if hasattr(instance.api_client._api_client._http_options,'base_url'):
|
|
21
|
+
return instance.api_client._api_client._http_options.base_url
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
def extract_message(arguments):
|
|
25
|
+
return str(arguments['args'][0].contents)
|
|
26
|
+
|
|
27
|
+
def extract_assistant_message(arguments):
|
|
28
|
+
return str(arguments['result'].content.parts)
|
|
29
|
+
|
|
30
|
+
def update_span_from_llm_response(response, instance):
|
|
31
|
+
meta_dict = {}
|
|
32
|
+
if response is not None and hasattr(response, "usage_metadata") and response.usage_metadata is not None:
|
|
33
|
+
token_usage = response.usage_metadata
|
|
34
|
+
if token_usage is not None:
|
|
35
|
+
meta_dict.update({"completion_tokens": token_usage.candidates_token_count})
|
|
36
|
+
meta_dict.update({"prompt_tokens": token_usage.prompt_token_count })
|
|
37
|
+
meta_dict.update({"total_tokens": token_usage.total_token_count})
|
|
38
|
+
return meta_dict
|
|
39
|
+
|
|
40
|
+
def extract_finish_reason(arguments):
|
|
41
|
+
if arguments["exception"] is not None:
|
|
42
|
+
return None
|
|
43
|
+
if hasattr(arguments['result'], 'error_code'):
|
|
44
|
+
return arguments['result'].error_code
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
def map_finish_reason_to_finish_type(finish_reason:str):
|
|
48
|
+
return map_adk_finish_reason_to_finish_type(finish_reason)
|
|
49
|
+
|
|
50
|
+
def get_agent_name(instance: Any) -> str:
|
|
51
|
+
"""
|
|
52
|
+
Extract the name of the agent from the given instance.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
instance: The agent instance to extract name from
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
str: The name of the agent, or a default value if not found
|
|
59
|
+
"""
|
|
60
|
+
return getattr(instance, 'name', 'unknown_agent')
|
|
61
|
+
|
|
62
|
+
def get_agent_description(instance: Any) -> str:
|
|
63
|
+
"""
|
|
64
|
+
Extract the description of the agent from the given instance.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
instance: The agent instance to extract description from
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
str: The description of the agent, or a default value if not found
|
|
71
|
+
"""
|
|
72
|
+
return getattr(instance, 'description', 'No description available')
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def extract_agent_input(arguments: Dict[str, Any]) -> Any:
|
|
76
|
+
"""
|
|
77
|
+
Extract the input data from agent arguments.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
arguments: Dictionary containing agent call arguments
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
Any: The extracted input data
|
|
84
|
+
"""
|
|
85
|
+
return arguments['args'][0].user_content.parts[0].text
|
|
86
|
+
|
|
87
|
+
def extract_agent_request_input(arguments: Dict[str, Any]) -> Any:
|
|
88
|
+
"""
|
|
89
|
+
Extract the input data from agent request.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
arguments: Dictionary containing agent call arguments
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
Any: The extracted input data
|
|
96
|
+
"""
|
|
97
|
+
return arguments['kwargs']['new_message'].parts[0].text if 'new_message' in arguments['kwargs'] else None
|
|
98
|
+
|
|
99
|
+
def extract_agent_response(result: Any) -> Any:
|
|
100
|
+
"""
|
|
101
|
+
Extract the response data from agent result.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
result: The result returned by the agent
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
Any: The extracted response data
|
|
108
|
+
"""
|
|
109
|
+
if result:
|
|
110
|
+
return result.content.parts[0].text
|
|
111
|
+
|
|
112
|
+
def get_tool_name(instance: Any) -> str:
|
|
113
|
+
"""
|
|
114
|
+
Extract the name of the tool from the given instance.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
instance: The tool instance to extract name from
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
str: The name of the tool, or a default value if not found
|
|
121
|
+
"""
|
|
122
|
+
return getattr(instance, 'name', getattr(instance, '__name__', 'unknown_tool'))
|
|
123
|
+
|
|
124
|
+
def get_tool_description(instance: Any) -> str:
|
|
125
|
+
"""
|
|
126
|
+
Extract the description of the tool from the given instance.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
instance: The tool instance to extract description from
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
str: The description of the tool, or a default value if not found
|
|
133
|
+
"""
|
|
134
|
+
return getattr(instance, 'description', getattr(instance, '__doc__', 'No description available'))
|
|
135
|
+
|
|
136
|
+
def get_source_agent(arguments) -> str:
|
|
137
|
+
"""
|
|
138
|
+
Get the name of the source agent (the agent that is calling a tool or delegating to another agent).
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
str: The name of the source agent
|
|
142
|
+
"""
|
|
143
|
+
return arguments['kwargs']['tool_context'].agent_name
|
|
144
|
+
|
|
145
|
+
def get_delegating_agent(arguments) -> str:
|
|
146
|
+
"""
|
|
147
|
+
Get the name of the delegating agent (the agent that is delegating a task to another agent).
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
arguments: Dictionary containing agent call arguments
|
|
151
|
+
Returns:
|
|
152
|
+
str: The name of the delegating agent
|
|
153
|
+
"""
|
|
154
|
+
from_agent = arguments['args'][0].agent.name if hasattr(arguments['args'][0], 'agent') else None
|
|
155
|
+
if from_agent is not None:
|
|
156
|
+
if get_agent_name(arguments['instance']) == from_agent:
|
|
157
|
+
return None
|
|
158
|
+
return from_agent
|
|
159
|
+
|
|
160
|
+
def should_skip_delegation(arguments):
|
|
161
|
+
"""
|
|
162
|
+
Determine whether to skip the delegation based on the arguments.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
arguments: Dictionary containing agent call arguments
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
bool: True if delegation should be skipped, False otherwise
|
|
169
|
+
"""
|
|
170
|
+
return get_delegating_agent(arguments) is None
|
|
171
|
+
|
|
172
|
+
def extract_tool_input(arguments: Dict[str, Any]) -> Any:
|
|
173
|
+
"""
|
|
174
|
+
Extract the input data from tool arguments.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
arguments: Dictionary containing tool call arguments
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
Any: The extracted input data
|
|
181
|
+
"""
|
|
182
|
+
return str(arguments['kwargs'].get('args'))
|
|
183
|
+
|
|
184
|
+
def extract_tool_response(result: Any) -> Any:
|
|
185
|
+
"""
|
|
186
|
+
Extract the response data from tool result.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
result: The result returned by the tool
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
Any: The extracted response data
|
|
193
|
+
"""
|
|
194
|
+
return str(result)
|
|
195
|
+
|
|
196
|
+
def get_target_agent(instance: Any) -> str:
|
|
197
|
+
"""
|
|
198
|
+
Extract the name of the target agent (the agent being called/delegated to).
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
instance: The target agent instance
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
str: The name of the target agent
|
|
205
|
+
"""
|
|
206
|
+
return getattr(instance, 'name', getattr(instance, '__name__', 'unknown_target_agent'))
|