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.
- monocle_apptrace/__main__.py +19 -0
- monocle_apptrace/exporters/azure/blob_exporter.py +7 -0
- monocle_apptrace/exporters/monocle_exporters.py +5 -4
- monocle_apptrace/exporters/okahu/okahu_exporter.py +1 -1
- monocle_apptrace/instrumentation/common/constants.py +22 -0
- monocle_apptrace/instrumentation/common/instrumentor.py +119 -39
- monocle_apptrace/instrumentation/common/span_handler.py +103 -44
- monocle_apptrace/instrumentation/common/utils.py +161 -5
- monocle_apptrace/instrumentation/common/wrapper.py +58 -37
- monocle_apptrace/instrumentation/common/wrapper_method.py +34 -7
- monocle_apptrace/instrumentation/metamodel/botocore/_helper.py +0 -31
- monocle_apptrace/instrumentation/metamodel/botocore/handlers/botocore_span_handler.py +25 -0
- monocle_apptrace/instrumentation/metamodel/botocore/methods.py +6 -6
- monocle_apptrace/instrumentation/metamodel/flask/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/flask/_helper.py +29 -0
- monocle_apptrace/instrumentation/metamodel/flask/methods.py +13 -0
- monocle_apptrace/instrumentation/metamodel/haystack/entities/inference.py +1 -1
- monocle_apptrace/instrumentation/metamodel/haystack/methods.py +2 -1
- monocle_apptrace/instrumentation/metamodel/langchain/_helper.py +4 -0
- monocle_apptrace/instrumentation/metamodel/langchain/entities/inference.py +3 -2
- monocle_apptrace/instrumentation/metamodel/langchain/methods.py +12 -6
- monocle_apptrace/instrumentation/metamodel/langgraph/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/langgraph/_helper.py +48 -0
- monocle_apptrace/instrumentation/metamodel/langgraph/entities/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/langgraph/entities/inference.py +56 -0
- monocle_apptrace/instrumentation/metamodel/langgraph/methods.py +14 -0
- monocle_apptrace/instrumentation/metamodel/llamaindex/_helper.py +37 -19
- monocle_apptrace/instrumentation/metamodel/llamaindex/entities/agent.py +47 -0
- monocle_apptrace/instrumentation/metamodel/llamaindex/entities/inference.py +5 -3
- monocle_apptrace/instrumentation/metamodel/llamaindex/methods.py +15 -3
- monocle_apptrace/instrumentation/metamodel/openai/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/openai/_helper.py +112 -0
- monocle_apptrace/instrumentation/metamodel/openai/entities/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/openai/entities/inference.py +71 -0
- monocle_apptrace/instrumentation/metamodel/openai/entities/retrieval.py +43 -0
- monocle_apptrace/instrumentation/metamodel/openai/methods.py +45 -0
- monocle_apptrace/instrumentation/metamodel/requests/__init__.py +4 -0
- monocle_apptrace/instrumentation/metamodel/requests/_helper.py +31 -0
- monocle_apptrace/instrumentation/metamodel/requests/methods.py +12 -0
- {monocle_apptrace-0.3.0b4.dist-info → monocle_apptrace-0.3.0b6.dist-info}/METADATA +2 -1
- monocle_apptrace-0.3.0b6.dist-info/RECORD +68 -0
- monocle_apptrace-0.3.0b4.dist-info/RECORD +0 -48
- {monocle_apptrace-0.3.0b4.dist-info → monocle_apptrace-0.3.0b6.dist-info}/WHEEL +0 -0
- {monocle_apptrace-0.3.0b4.dist-info → monocle_apptrace-0.3.0b6.dist-info}/licenses/LICENSE +0 -0
- {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
|
-
|
|
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.
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
37
|
+
self.span_type = span_type
|
|
28
38
|
|
|
29
|
-
self.
|
|
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
|
-
|
|
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
|
-
"
|
|
13
|
-
"output_processor": INFERENCE
|
|
14
|
-
|
|
15
|
-
}
|
|
12
|
+
"span_handler":"botocore_handler",
|
|
13
|
+
"output_processor": INFERENCE,
|
|
14
|
+
"skip_span": True
|
|
15
|
+
}
|
|
16
16
|
]
|
|
File without changes
|
|
@@ -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.
|
|
12
|
+
"accessor": lambda arguments: 'inference.azure_openai'
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"attribute": "provider_name",
|
|
@@ -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.
|
|
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
|
]
|
|
File without changes
|