monocle-apptrace 0.5.2__py3-none-any.whl → 0.6.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.
- monocle_apptrace/exporters/file_exporter.py +15 -2
- monocle_apptrace/instrumentation/common/instrumentor.py +1 -1
- monocle_apptrace/instrumentation/common/span_handler.py +8 -4
- monocle_apptrace/instrumentation/common/utils.py +10 -2
- monocle_apptrace/instrumentation/common/wrapper_method.py +5 -1
- monocle_apptrace/instrumentation/metamodel/adk/_helper.py +6 -4
- monocle_apptrace/instrumentation/metamodel/adk/entities/agent.py +12 -2
- monocle_apptrace/instrumentation/metamodel/adk/entities/tool.py +8 -3
- monocle_apptrace/instrumentation/metamodel/agents/_helper.py +5 -5
- monocle_apptrace/instrumentation/metamodel/agents/entities/inference.py +22 -5
- monocle_apptrace/instrumentation/metamodel/aiohttp/_helper.py +22 -7
- monocle_apptrace/instrumentation/metamodel/aiohttp/entities/http.py +14 -3
- monocle_apptrace/instrumentation/metamodel/azfunc/_helper.py +21 -11
- monocle_apptrace/instrumentation/metamodel/azfunc/entities/http.py +7 -2
- monocle_apptrace/instrumentation/metamodel/fastapi/_helper.py +19 -6
- monocle_apptrace/instrumentation/metamodel/fastapi/entities/http.py +6 -2
- monocle_apptrace/instrumentation/metamodel/fastapi/methods.py +19 -19
- monocle_apptrace/instrumentation/metamodel/finish_types.py +32 -1
- monocle_apptrace/instrumentation/metamodel/flask/_helper.py +20 -6
- monocle_apptrace/instrumentation/metamodel/flask/entities/http.py +7 -2
- monocle_apptrace/instrumentation/metamodel/hugging_face/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/hugging_face/_helper.py +138 -0
- monocle_apptrace/instrumentation/metamodel/hugging_face/entities/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/hugging_face/entities/inference.py +97 -0
- monocle_apptrace/instrumentation/metamodel/hugging_face/methods.py +23 -0
- monocle_apptrace/instrumentation/metamodel/lambdafunc/_helper.py +25 -14
- monocle_apptrace/instrumentation/metamodel/lambdafunc/entities/http.py +7 -2
- monocle_apptrace/instrumentation/metamodel/langgraph/_helper.py +4 -2
- monocle_apptrace/instrumentation/metamodel/langgraph/entities/inference.py +8 -3
- monocle_apptrace/instrumentation/metamodel/llamaindex/entities/agent.py +1 -1
- monocle_apptrace/instrumentation/metamodel/mcp/_helper.py +6 -5
- monocle_apptrace/instrumentation/metamodel/mcp/entities/inference.py +5 -0
- monocle_apptrace/instrumentation/metamodel/mistral/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/mistral/_helper.py +223 -0
- monocle_apptrace/instrumentation/metamodel/mistral/entities/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/mistral/entities/inference.py +94 -0
- monocle_apptrace/instrumentation/metamodel/mistral/entities/retrieval.py +41 -0
- monocle_apptrace/instrumentation/metamodel/mistral/methods.py +58 -0
- monocle_apptrace/instrumentation/metamodel/teamsai/_helper.py +2 -2
- {monocle_apptrace-0.5.2.dist-info → monocle_apptrace-0.6.0.dist-info}/METADATA +9 -76
- {monocle_apptrace-0.5.2.dist-info → monocle_apptrace-0.6.0.dist-info}/RECORD +44 -36
- monocle_apptrace/README.md +0 -101
- monocle_apptrace/mcp_server.py +0 -94
- monocle_apptrace-0.5.2.dist-info/licenses/NOTICE +0 -4
- {monocle_apptrace-0.5.2.dist-info → monocle_apptrace-0.6.0.dist-info}/WHEEL +0 -0
- {monocle_apptrace-0.5.2.dist-info → monocle_apptrace-0.6.0.dist-info}/entry_points.txt +0 -0
- {monocle_apptrace-0.5.2.dist-info → monocle_apptrace-0.6.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
from monocle_apptrace.instrumentation.common.wrapper import atask_wrapper
|
|
1
|
+
from monocle_apptrace.instrumentation.common.wrapper import atask_wrapper,task_wrapper
|
|
2
2
|
from monocle_apptrace.instrumentation.metamodel.fastapi.entities.http import FASTAPI_HTTP_PROCESSOR, FASTAPI_RESPONSE_PROCESSOR
|
|
3
3
|
|
|
4
4
|
FASTAPI_METHODS = [
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
5
|
+
{
|
|
6
|
+
"package": "fastapi",
|
|
7
|
+
"object": "FastAPI",
|
|
8
|
+
"method": "__call__",
|
|
9
|
+
"wrapper_method": task_wrapper,
|
|
10
|
+
"span_name": "fastapi.request",
|
|
11
|
+
"span_handler": "fastapi_handler",
|
|
12
|
+
"output_processor": FASTAPI_HTTP_PROCESSOR,
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"package": "starlette.responses",
|
|
16
|
+
"object": "Response",
|
|
17
|
+
"method": "__call__",
|
|
18
|
+
"span_name": "fastapi.response",
|
|
19
|
+
"wrapper_method": task_wrapper,
|
|
20
|
+
"span_handler": "fastapi_response_handler",
|
|
21
|
+
"output_processor": FASTAPI_RESPONSE_PROCESSOR
|
|
22
|
+
}
|
|
23
23
|
]
|
|
@@ -14,6 +14,7 @@ class FinishType(Enum):
|
|
|
14
14
|
TOOL_CALL_ERROR = "tool_call_error"
|
|
15
15
|
REFUSAL = "refusal"
|
|
16
16
|
RATE_LIMITED = "rate_limited"
|
|
17
|
+
TOOL_CALL = "tool_call"
|
|
17
18
|
|
|
18
19
|
# OpenAI finish reason mapping
|
|
19
20
|
OPENAI_FINISH_REASON_MAPPING = {
|
|
@@ -274,6 +275,24 @@ HAYSTACK_FINISH_REASON_MAPPING = {
|
|
|
274
275
|
"OTHER": FinishType.ERROR.value,
|
|
275
276
|
}
|
|
276
277
|
|
|
278
|
+
MISTRAL_FINISH_REASON_MAPPING = {
|
|
279
|
+
"stop": FinishType.SUCCESS.value,
|
|
280
|
+
"tool_calls": FinishType.TOOL_CALL.value, # New category for tool calls
|
|
281
|
+
"length": FinishType.TRUNCATED.value,
|
|
282
|
+
# Mistral's API documentation does not explicitly mention other finish reasons like "content_filter" or "refusal".
|
|
283
|
+
# However, in case of an API-level error, the response itself would likely be an HTTP error rather than a
|
|
284
|
+
# successful response with a specific finish reason.
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
HUGGING_FACE_FINISH_REASON_MAPPING = {
|
|
288
|
+
"stop": FinishType.SUCCESS.value,
|
|
289
|
+
"tool_calls": FinishType.TOOL_CALL.value, # New category for tool calls
|
|
290
|
+
"length": FinishType.TRUNCATED.value,
|
|
291
|
+
# Hugging Face's API documentation does not explicitly mention other finish reasons like "content_filter" or "refusal".
|
|
292
|
+
# However, in case of an API-level error, the response itself would likely be an HTTP error rather than a
|
|
293
|
+
# successful response with a specific finish reason.
|
|
294
|
+
}
|
|
295
|
+
|
|
277
296
|
ADK_FINISH_REASON_MAPPING = GEMINI_FINISH_REASON_MAPPING
|
|
278
297
|
|
|
279
298
|
def map_openai_finish_reason_to_finish_type(finish_reason):
|
|
@@ -462,4 +481,16 @@ def map_adk_finish_reason_to_finish_type(finish_reason):
|
|
|
462
481
|
"""Map ADK finish_reason to standardized finish_type."""
|
|
463
482
|
if not finish_reason:
|
|
464
483
|
return None
|
|
465
|
-
return ADK_FINISH_REASON_MAPPING.get(finish_reason, None)
|
|
484
|
+
return ADK_FINISH_REASON_MAPPING.get(finish_reason, None)
|
|
485
|
+
|
|
486
|
+
def map_mistral_finish_reason_to_finish_type(finish_reason):
|
|
487
|
+
"""Map Mistral finish_reason to standardized finish_type."""
|
|
488
|
+
if not finish_reason:
|
|
489
|
+
return None
|
|
490
|
+
return MISTRAL_FINISH_REASON_MAPPING.get(finish_reason, None)
|
|
491
|
+
|
|
492
|
+
def map_hf_finish_reason_to_finish_type(finish_reason):
|
|
493
|
+
"""Map Hugging Face finish_reason to standardized finish_type."""
|
|
494
|
+
if not finish_reason:
|
|
495
|
+
return None
|
|
496
|
+
return HUGGING_FACE_FINISH_REASON_MAPPING.get(finish_reason, None)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from threading import local
|
|
3
|
-
from monocle_apptrace.instrumentation.common.utils import extract_http_headers, clear_http_scopes
|
|
3
|
+
from monocle_apptrace.instrumentation.common.utils import extract_http_headers, clear_http_scopes, get_exception_status_code
|
|
4
4
|
from monocle_apptrace.instrumentation.common.span_handler import SpanHandler
|
|
5
5
|
from monocle_apptrace.instrumentation.common.constants import HTTP_SUCCESS_CODES
|
|
6
6
|
from monocle_apptrace.instrumentation.common.utils import MonocleSpanException
|
|
@@ -22,6 +22,14 @@ def get_params(args) -> dict:
|
|
|
22
22
|
params = args[0]['QUERY_STRING'] if 'QUERY_STRING' in args[0] else ""
|
|
23
23
|
return unquote(params)
|
|
24
24
|
|
|
25
|
+
def get_url(args) -> str:
|
|
26
|
+
url = ""
|
|
27
|
+
if len(args) > 1 or not isinstance(args[0], dict):
|
|
28
|
+
if 'HTTP_HOST' in args[0]:
|
|
29
|
+
url = f"http://{args[0]['HTTP_HOST']}{args[0].get('REQUEST_URI', '')}"
|
|
30
|
+
|
|
31
|
+
return url
|
|
32
|
+
|
|
25
33
|
def get_body(args) -> dict:
|
|
26
34
|
return ""
|
|
27
35
|
|
|
@@ -32,11 +40,17 @@ def extract_response(instance) -> str:
|
|
|
32
40
|
response = ""
|
|
33
41
|
return response
|
|
34
42
|
|
|
35
|
-
def extract_status(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
def extract_status(arguments) -> str:
|
|
44
|
+
if arguments["exception"] is not None:
|
|
45
|
+
return get_exception_status_code(arguments)
|
|
46
|
+
instance = arguments['instance']
|
|
47
|
+
if hasattr(instance, 'status_code'):
|
|
48
|
+
status = f"{instance.status_code}"
|
|
49
|
+
if status not in HTTP_SUCCESS_CODES:
|
|
50
|
+
error_message = extract_response(instance)
|
|
51
|
+
raise MonocleSpanException(f"error: {status} - {error_message}", status)
|
|
52
|
+
else:
|
|
53
|
+
status = "success"
|
|
40
54
|
return status
|
|
41
55
|
|
|
42
56
|
def flask_pre_tracing(args):
|
|
@@ -14,6 +14,11 @@ FLASK_HTTP_PROCESSOR = {
|
|
|
14
14
|
"attribute": "route",
|
|
15
15
|
"accessor": lambda arguments: _helper.get_route(arguments['args'])
|
|
16
16
|
},
|
|
17
|
+
{
|
|
18
|
+
"_comment": "request method, request URI",
|
|
19
|
+
"attribute": "url",
|
|
20
|
+
"accessor": lambda arguments: _helper.get_url(arguments['args'])
|
|
21
|
+
},
|
|
17
22
|
]
|
|
18
23
|
]
|
|
19
24
|
}
|
|
@@ -35,8 +40,8 @@ FLASK_RESPONSE_PROCESSOR = {
|
|
|
35
40
|
"attributes": [
|
|
36
41
|
{
|
|
37
42
|
"_comment": "status from HTTP response",
|
|
38
|
-
"attribute": "
|
|
39
|
-
"accessor": lambda arguments: _helper.extract_status(arguments
|
|
43
|
+
"attribute": "error_code",
|
|
44
|
+
"accessor": lambda arguments: _helper.extract_status(arguments)
|
|
40
45
|
},
|
|
41
46
|
{
|
|
42
47
|
"_comment": "this is result from LLM",
|
|
File without changes
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
from opentelemetry.context import get_value
|
|
5
|
+
from monocle_apptrace.instrumentation.common.constants import (
|
|
6
|
+
AGENT_PREFIX_KEY,
|
|
7
|
+
INFERENCE_AGENT_DELEGATION,
|
|
8
|
+
INFERENCE_TURN_END,
|
|
9
|
+
INFERENCE_TOOL_CALL,
|
|
10
|
+
)
|
|
11
|
+
from monocle_apptrace.instrumentation.common.utils import (
|
|
12
|
+
Option,
|
|
13
|
+
get_json_dumps,
|
|
14
|
+
try_option,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from monocle_apptrace.instrumentation.metamodel.finish_types import map_hf_finish_reason_to_finish_type
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
def update_input_span_events(kwargs):
|
|
22
|
+
input_text = ""
|
|
23
|
+
print("DEBUG kwargs:", kwargs)
|
|
24
|
+
if "inputs" in kwargs:
|
|
25
|
+
if isinstance(kwargs["inputs"], list):
|
|
26
|
+
input_text = " | ".join(str(i) for i in kwargs["inputs"])
|
|
27
|
+
else:
|
|
28
|
+
input_text = str(kwargs["inputs"])
|
|
29
|
+
elif "messages" in kwargs:
|
|
30
|
+
input_text = json.dumps(kwargs["messages"])
|
|
31
|
+
return {"input": input_text} # always a dict with 'input'
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def update_output_span_events(result):
|
|
36
|
+
try:
|
|
37
|
+
if hasattr(result, "choices") and result.choices:
|
|
38
|
+
output = [c.message for c in result.choices]
|
|
39
|
+
output_str = json.dumps(output)
|
|
40
|
+
return output_str[:200] + "..." if len(output_str) > 200 else output_str
|
|
41
|
+
except Exception as e:
|
|
42
|
+
logger.warning("Error in update_output_span_events: %s", str(e))
|
|
43
|
+
return ""
|
|
44
|
+
|
|
45
|
+
def extract_messages(kwargs):
|
|
46
|
+
"""Extract system and user messages"""
|
|
47
|
+
try:
|
|
48
|
+
messages = []
|
|
49
|
+
if "system" in kwargs and isinstance(kwargs["system"], str):
|
|
50
|
+
messages.append({"system": kwargs["system"]})
|
|
51
|
+
if 'messages' in kwargs and kwargs['messages']:
|
|
52
|
+
for msg in kwargs['messages']:
|
|
53
|
+
if msg.get('content') and msg.get('role'):
|
|
54
|
+
messages.append({msg['role']: msg['content']})
|
|
55
|
+
return [get_json_dumps(message) for message in messages]
|
|
56
|
+
except Exception as e:
|
|
57
|
+
logger.warning("Warning: Error occurred in extract_messages: %s", str(e))
|
|
58
|
+
return []
|
|
59
|
+
|
|
60
|
+
def extract_assistant_message(arguments):
|
|
61
|
+
"""
|
|
62
|
+
Extract the assistant message from a Mistral response or stream chunks.
|
|
63
|
+
Returns a JSON string like {"assistant": "<text>"}.
|
|
64
|
+
"""
|
|
65
|
+
try:
|
|
66
|
+
result = arguments.get("result") if isinstance(arguments, dict) else arguments
|
|
67
|
+
if result is None:
|
|
68
|
+
return ""
|
|
69
|
+
|
|
70
|
+
# Handle full response
|
|
71
|
+
if hasattr(result, "choices") and result.choices:
|
|
72
|
+
msg_obj = result.choices[0].message
|
|
73
|
+
return get_json_dumps({msg_obj.role: msg_obj.content})
|
|
74
|
+
|
|
75
|
+
# Handle streaming: result might be a list of CompletionEvent chunks
|
|
76
|
+
if isinstance(result, list):
|
|
77
|
+
content = []
|
|
78
|
+
for chunk in result:
|
|
79
|
+
if hasattr(chunk, "data") and hasattr(chunk.data, "choices") and chunk.data.choices:
|
|
80
|
+
choice = chunk.data.choices[0]
|
|
81
|
+
if hasattr(choice, "delta") and hasattr(choice.delta, "content"):
|
|
82
|
+
content.append(choice.delta.content or "")
|
|
83
|
+
return get_json_dumps({"assistant": "".join(content)})
|
|
84
|
+
|
|
85
|
+
return ""
|
|
86
|
+
|
|
87
|
+
except Exception as e:
|
|
88
|
+
logger.warning("Warning in extract_assistant_message: %s", str(e))
|
|
89
|
+
return ""
|
|
90
|
+
|
|
91
|
+
def update_span_from_llm_response(result, include_token_counts=False):
|
|
92
|
+
tokens = {
|
|
93
|
+
"completion_tokens": getattr(result.usage, "completion_tokens", 0),
|
|
94
|
+
"prompt_tokens": getattr(result.usage, "prompt_tokens", 0),
|
|
95
|
+
"total_tokens": getattr(result.usage, "total_tokens", 0),
|
|
96
|
+
} if include_token_counts else {}
|
|
97
|
+
# Add other metadata fields like finish_reason, etc.
|
|
98
|
+
return {**tokens, "inference_sub_type": "turn_end"}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def get_exception_status_code(exc):
|
|
102
|
+
if exc is None:
|
|
103
|
+
return "success"
|
|
104
|
+
code = getattr(exc, "status_code", None)
|
|
105
|
+
if code == 401:
|
|
106
|
+
return "unauthorized"
|
|
107
|
+
elif code == 403:
|
|
108
|
+
return "forbidden"
|
|
109
|
+
elif code == 404:
|
|
110
|
+
return "not_found"
|
|
111
|
+
return "error"
|
|
112
|
+
|
|
113
|
+
def map_finish_reason_to_finish_type(finish_reason):
|
|
114
|
+
"""Map Hugging Face finish_reason to finish_type, similar to OpenAI mapping."""
|
|
115
|
+
return map_hf_finish_reason_to_finish_type(finish_reason)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def agent_inference_type(result):
|
|
119
|
+
"""
|
|
120
|
+
Simple agent inference type logic: if message contains AGENT_PREFIX_KEY,
|
|
121
|
+
mark as delegation; otherwise it's a normal turn_end.
|
|
122
|
+
"""
|
|
123
|
+
try:
|
|
124
|
+
assistant_msg = extract_assistant_message(result)
|
|
125
|
+
if assistant_msg and AGENT_PREFIX_KEY in assistant_msg:
|
|
126
|
+
return INFERENCE_AGENT_DELEGATION
|
|
127
|
+
except Exception as e:
|
|
128
|
+
logger.warning("Error in agent_inference_type: %s", str(e))
|
|
129
|
+
return INFERENCE_TURN_END
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def extract_finish_reason(result):
|
|
133
|
+
try:
|
|
134
|
+
return getattr(result, "finish_reason", None)
|
|
135
|
+
except Exception:
|
|
136
|
+
return None
|
|
137
|
+
|
|
138
|
+
|
|
File without changes
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
from monocle_apptrace.instrumentation.common.constants import SPAN_TYPES
|
|
2
|
+
from monocle_apptrace.instrumentation.common.utils import get_error_message, resolve_from_alias
|
|
3
|
+
from monocle_apptrace.instrumentation.metamodel.hugging_face import _helper
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
INFERENCE = {
|
|
7
|
+
"type": SPAN_TYPES.INFERENCE,
|
|
8
|
+
"attributes": [
|
|
9
|
+
[
|
|
10
|
+
{
|
|
11
|
+
"_comment": "provider type, name, deployment, inference_endpoint",
|
|
12
|
+
"attribute": "type",
|
|
13
|
+
"accessor": lambda arguments: "inference.huggingface"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"attribute": "provider_name",
|
|
17
|
+
"accessor": lambda arguments: "huggingface"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"attribute": "inference_endpoint",
|
|
21
|
+
"accessor": lambda arguments: "https://api-inference.huggingface.co/v1/"
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
[
|
|
25
|
+
{
|
|
26
|
+
"_comment": "LLM Model (repo ID on Hugging Face hub)",
|
|
27
|
+
"attribute": "name",
|
|
28
|
+
"accessor": lambda arguments: resolve_from_alias(
|
|
29
|
+
arguments["kwargs"],
|
|
30
|
+
["model", "model_name", "endpoint_name", "deployment_name"]
|
|
31
|
+
)
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"attribute": "type",
|
|
35
|
+
"accessor": lambda arguments: (
|
|
36
|
+
"model.llm." +
|
|
37
|
+
resolve_from_alias(
|
|
38
|
+
arguments["kwargs"],
|
|
39
|
+
["model", "model_name", "endpoint_name", "deployment_name"]
|
|
40
|
+
)
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
],
|
|
45
|
+
"events": [
|
|
46
|
+
{
|
|
47
|
+
"name": "data.input",
|
|
48
|
+
"attributes": [
|
|
49
|
+
{
|
|
50
|
+
"_comment": "this is instruction and user query to LLM",
|
|
51
|
+
"attribute": "input",
|
|
52
|
+
"accessor": lambda arguments: _helper.extract_messages(arguments["kwargs"])
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "data.output",
|
|
58
|
+
"attributes": [
|
|
59
|
+
{
|
|
60
|
+
"attribute": "error_code",
|
|
61
|
+
"accessor": lambda arguments: get_error_message(arguments)
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"_comment": "result from Hugging Face inference",
|
|
65
|
+
"attribute": "response",
|
|
66
|
+
"accessor": lambda arguments: _helper.extract_assistant_message(arguments)
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"name": "metadata",
|
|
72
|
+
"attributes": [
|
|
73
|
+
{
|
|
74
|
+
"_comment": "this is metadata usage from LLM, includes token counts",
|
|
75
|
+
"accessor": lambda arguments: _helper.update_span_from_llm_response(
|
|
76
|
+
arguments.get("result"),
|
|
77
|
+
include_token_counts=True # new flag for streaming handling
|
|
78
|
+
)
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"_comment": "finish reason from Hugging Face response",
|
|
82
|
+
"attribute": "finish_reason",
|
|
83
|
+
"accessor": lambda arguments: _helper.extract_finish_reason(arguments)
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"_comment": "finish type mapped from finish reason",
|
|
87
|
+
"attribute": "finish_type",
|
|
88
|
+
"accessor": lambda arguments: _helper.map_finish_reason_to_finish_type(_helper.extract_finish_reason(arguments))
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"attribute": "inference_sub_type",
|
|
92
|
+
"accessor": lambda arguments: _helper.agent_inference_type(arguments)
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from monocle_apptrace.instrumentation.common.wrapper import atask_wrapper, task_wrapper
|
|
2
|
+
from monocle_apptrace.instrumentation.metamodel.hugging_face.entities.inference import (
|
|
3
|
+
INFERENCE,
|
|
4
|
+
)
|
|
5
|
+
|
|
6
|
+
HUGGING_FACE_METHODS = [
|
|
7
|
+
{
|
|
8
|
+
"package": "huggingface_hub",
|
|
9
|
+
"object": "InferenceClient",
|
|
10
|
+
"method": "chat_completion", # sync
|
|
11
|
+
"wrapper_method": task_wrapper,
|
|
12
|
+
"span_handler": "non_framework_handler",
|
|
13
|
+
"output_processor": INFERENCE,
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"package": "huggingface_hub",
|
|
17
|
+
"object": "AsyncInferenceClient",
|
|
18
|
+
"method": "chat_completion", # async
|
|
19
|
+
"wrapper_method": atask_wrapper,
|
|
20
|
+
"span_handler": "non_framework_handler",
|
|
21
|
+
"output_processor": INFERENCE,
|
|
22
|
+
},
|
|
23
|
+
]
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from threading import local
|
|
3
|
-
from
|
|
3
|
+
from unittest import result
|
|
4
|
+
from monocle_apptrace.instrumentation.common.utils import extract_http_headers, clear_http_scopes, get_exception_status_code, try_option, Option, \
|
|
4
5
|
MonocleSpanException
|
|
5
6
|
from monocle_apptrace.instrumentation.common.span_handler import SpanHandler
|
|
6
7
|
from monocle_apptrace.instrumentation.common.constants import HTTP_SUCCESS_CODES
|
|
@@ -11,13 +12,18 @@ MAX_DATA_LENGTH = 1000
|
|
|
11
12
|
token_data = local()
|
|
12
13
|
token_data.current_token = None
|
|
13
14
|
|
|
14
|
-
def get_url(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
def get_url(args) -> str:
|
|
16
|
+
event = args[1]
|
|
17
|
+
host = event.get('headers', {}).get('Host', '')
|
|
18
|
+
stage = event.get('requestContext', {}).get('stage', '')
|
|
19
|
+
path = event.get('path', '')
|
|
20
|
+
query_params = event.get('queryStringParameters', {})
|
|
21
|
+
scheme = 'https' if event.get('headers', {}).get('X-Forwarded-Proto', 'http') == 'https' else 'http'
|
|
22
|
+
url = f"{scheme}://{host}/{stage}{path}"
|
|
23
|
+
if query_params:
|
|
24
|
+
from urllib.parse import urlencode
|
|
25
|
+
url += '?' + urlencode(query_params)
|
|
26
|
+
return url
|
|
21
27
|
|
|
22
28
|
def get_route(args) -> str:
|
|
23
29
|
event = args[1]
|
|
@@ -53,14 +59,19 @@ def extract_response(result) -> str:
|
|
|
53
59
|
return response
|
|
54
60
|
|
|
55
61
|
|
|
56
|
-
def extract_status(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
62
|
+
def extract_status(arguments) -> str:
|
|
63
|
+
if arguments["exception"] is not None:
|
|
64
|
+
return get_exception_status_code(arguments)
|
|
65
|
+
result = arguments['result']
|
|
66
|
+
if isinstance(result, dict) and 'statusCode' in result:
|
|
67
|
+
status = f"{result['statusCode']}"
|
|
68
|
+
if status not in HTTP_SUCCESS_CODES:
|
|
69
|
+
error_message = extract_response(result)
|
|
70
|
+
raise MonocleSpanException(f"error: {status} - {error_message}", status)
|
|
71
|
+
else:
|
|
72
|
+
status = "success"
|
|
61
73
|
return status
|
|
62
74
|
|
|
63
|
-
|
|
64
75
|
def lambda_func_pre_tracing(kwargs):
|
|
65
76
|
headers = kwargs['event'].get('headers', {}) if 'event' in kwargs else {}
|
|
66
77
|
return extract_http_headers(headers)
|
|
@@ -19,6 +19,11 @@ LAMBDA_HTTP_PROCESSOR = {
|
|
|
19
19
|
"attribute": "body",
|
|
20
20
|
"accessor": lambda arguments: _helper.get_body(arguments['args'])
|
|
21
21
|
},
|
|
22
|
+
{
|
|
23
|
+
"_comment": "request method, request URI",
|
|
24
|
+
"attribute": "url",
|
|
25
|
+
"accessor": lambda arguments: _helper.get_url(arguments['args'])
|
|
26
|
+
},
|
|
22
27
|
]
|
|
23
28
|
],
|
|
24
29
|
"events": [
|
|
@@ -37,8 +42,8 @@ LAMBDA_HTTP_PROCESSOR = {
|
|
|
37
42
|
"attributes": [
|
|
38
43
|
{
|
|
39
44
|
"_comment": "status from HTTP response",
|
|
40
|
-
"attribute": "
|
|
41
|
-
"accessor": lambda arguments: _helper.extract_status(arguments
|
|
45
|
+
"attribute": "error_code",
|
|
46
|
+
"accessor": lambda arguments: _helper.extract_status(arguments)
|
|
42
47
|
},
|
|
43
48
|
{
|
|
44
49
|
"_comment": "this is result from LLM",
|
|
@@ -25,10 +25,12 @@ def agent_instructions(arguments):
|
|
|
25
25
|
def extract_agent_input(arguments):
|
|
26
26
|
if arguments['result'] is not None and 'messages' in arguments['result']:
|
|
27
27
|
history = arguments['result']['messages']
|
|
28
|
+
messages = []
|
|
28
29
|
for message in history:
|
|
29
30
|
if hasattr(message, 'content') and hasattr(message, 'type') and message.type == "human": # Check if the message is a HumanMessage
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
messages.append(message.content)
|
|
32
|
+
return messages
|
|
33
|
+
return []
|
|
32
34
|
|
|
33
35
|
def get_inference_endpoint(arguments):
|
|
34
36
|
inference_endpoint = resolve_from_alias(arguments['instance'].client.__dict__, ['azure_endpoint', 'api_base', '_base_url'])
|
|
@@ -2,6 +2,7 @@ from monocle_apptrace.instrumentation.common.constants import AGENT_REQUEST_SPAN
|
|
|
2
2
|
from monocle_apptrace.instrumentation.metamodel.langgraph import (
|
|
3
3
|
_helper
|
|
4
4
|
)
|
|
5
|
+
from monocle_apptrace.instrumentation.common.utils import get_error_message
|
|
5
6
|
|
|
6
7
|
AGENT = {
|
|
7
8
|
"type": SPAN_TYPES.AGENTIC_INVOCATION,
|
|
@@ -31,7 +32,7 @@ AGENT = {
|
|
|
31
32
|
"attributes": [
|
|
32
33
|
{
|
|
33
34
|
"_comment": "this is Agent input",
|
|
34
|
-
"attribute": "
|
|
35
|
+
"attribute": "input",
|
|
35
36
|
"accessor": lambda arguments: _helper.extract_agent_input(arguments)
|
|
36
37
|
}
|
|
37
38
|
]
|
|
@@ -39,6 +40,10 @@ AGENT = {
|
|
|
39
40
|
{
|
|
40
41
|
"name":"data.output",
|
|
41
42
|
"attributes": [
|
|
43
|
+
{
|
|
44
|
+
"attribute": "error_code",
|
|
45
|
+
"accessor": lambda arguments: get_error_message(arguments)
|
|
46
|
+
},
|
|
42
47
|
{
|
|
43
48
|
"_comment": "this is response from LLM",
|
|
44
49
|
"attribute": "response",
|
|
@@ -50,7 +55,7 @@ AGENT = {
|
|
|
50
55
|
}
|
|
51
56
|
|
|
52
57
|
AGENT_REQUEST = {
|
|
53
|
-
"type":
|
|
58
|
+
"type": SPAN_TYPES.AGENTIC_REQUEST,
|
|
54
59
|
"subtype": SPAN_SUBTYPES.PLANNING,
|
|
55
60
|
"attributes": [
|
|
56
61
|
[
|
|
@@ -125,7 +130,7 @@ TOOLS = {
|
|
|
125
130
|
"attributes": [
|
|
126
131
|
{
|
|
127
132
|
"_comment": "this is Tool input",
|
|
128
|
-
"attribute": "
|
|
133
|
+
"attribute": "input",
|
|
129
134
|
"accessor": lambda arguments: _helper.extract_tool_input(arguments)
|
|
130
135
|
},
|
|
131
136
|
]
|
|
@@ -22,7 +22,7 @@ def get_output_text(arguments):
|
|
|
22
22
|
for tool in arguments["result"].tools:
|
|
23
23
|
if hasattr(tool, "name"):
|
|
24
24
|
tools.append(tool.name)
|
|
25
|
-
return tools
|
|
25
|
+
return ", ".join(tools)
|
|
26
26
|
if (
|
|
27
27
|
"result" in arguments
|
|
28
28
|
and hasattr(arguments["result"], "content")
|
|
@@ -32,7 +32,7 @@ def get_output_text(arguments):
|
|
|
32
32
|
for content in arguments["result"].content:
|
|
33
33
|
if hasattr(content, "text"):
|
|
34
34
|
ret_val.append(content.text)
|
|
35
|
-
return ret_val
|
|
35
|
+
return " ".join(ret_val)
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
def get_name(arguments):
|
|
@@ -63,17 +63,18 @@ def get_params_arguments(arguments):
|
|
|
63
63
|
|
|
64
64
|
args = arguments["args"]
|
|
65
65
|
if (
|
|
66
|
-
|
|
66
|
+
args
|
|
67
67
|
and hasattr(args[0], "root")
|
|
68
68
|
and hasattr(args[0].root, "params")
|
|
69
69
|
and hasattr(args[0].root.params, "arguments")
|
|
70
70
|
):
|
|
71
71
|
# If the first argument has a root with params and arguments, return those arguments
|
|
72
72
|
try:
|
|
73
|
-
return json.dumps(args[0].root.params.arguments)
|
|
73
|
+
return [json.dumps(args[0].root.params.arguments)]
|
|
74
74
|
except (TypeError, ValueError) as e:
|
|
75
75
|
logger.error(f"Error serializing arguments: {e}")
|
|
76
|
-
return str(args[0].root.params.arguments)
|
|
76
|
+
return [str(args[0].root.params.arguments)]
|
|
77
|
+
return []
|
|
77
78
|
|
|
78
79
|
|
|
79
80
|
def get_url(arguments):
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from monocle_apptrace.instrumentation.common.constants import SPAN_SUBTYPES, SPAN_TYPES
|
|
2
|
+
from monocle_apptrace.instrumentation.common.utils import get_error_message
|
|
2
3
|
from monocle_apptrace.instrumentation.metamodel.mcp import _helper
|
|
3
4
|
|
|
4
5
|
TOOLS = {
|
|
@@ -44,6 +45,10 @@ TOOLS = {
|
|
|
44
45
|
"attribute": "output",
|
|
45
46
|
"accessor": lambda arguments: _helper.get_output_text(arguments)
|
|
46
47
|
},
|
|
48
|
+
{
|
|
49
|
+
"attribute": "error_code",
|
|
50
|
+
"accessor": lambda arguments: get_error_message(arguments)
|
|
51
|
+
}
|
|
47
52
|
],
|
|
48
53
|
},
|
|
49
54
|
],
|
|
File without changes
|