monocle-apptrace 0.4.2__py3-none-any.whl → 0.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of monocle-apptrace might be problematic. Click here for more details.
- monocle_apptrace/__main__.py +1 -1
- monocle_apptrace/exporters/file_exporter.py +125 -37
- monocle_apptrace/instrumentation/common/__init__.py +16 -1
- monocle_apptrace/instrumentation/common/constants.py +14 -1
- monocle_apptrace/instrumentation/common/instrumentor.py +19 -152
- monocle_apptrace/instrumentation/common/method_wrappers.py +376 -0
- monocle_apptrace/instrumentation/common/span_handler.py +58 -32
- monocle_apptrace/instrumentation/common/utils.py +52 -15
- monocle_apptrace/instrumentation/common/wrapper.py +124 -18
- monocle_apptrace/instrumentation/common/wrapper_method.py +47 -1
- monocle_apptrace/instrumentation/metamodel/a2a/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/a2a/_helper.py +37 -0
- monocle_apptrace/instrumentation/metamodel/a2a/entities/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/a2a/entities/inference.py +112 -0
- monocle_apptrace/instrumentation/metamodel/a2a/methods.py +22 -0
- monocle_apptrace/instrumentation/metamodel/adk/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/adk/_helper.py +182 -0
- monocle_apptrace/instrumentation/metamodel/adk/entities/agent.py +50 -0
- monocle_apptrace/instrumentation/metamodel/adk/entities/tool.py +57 -0
- monocle_apptrace/instrumentation/metamodel/adk/methods.py +24 -0
- monocle_apptrace/instrumentation/metamodel/agents/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/agents/_helper.py +220 -0
- monocle_apptrace/instrumentation/metamodel/agents/agents_processor.py +152 -0
- monocle_apptrace/instrumentation/metamodel/agents/entities/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/agents/entities/inference.py +191 -0
- monocle_apptrace/instrumentation/metamodel/agents/methods.py +56 -0
- monocle_apptrace/instrumentation/metamodel/aiohttp/_helper.py +6 -11
- monocle_apptrace/instrumentation/metamodel/anthropic/_helper.py +112 -18
- monocle_apptrace/instrumentation/metamodel/anthropic/entities/inference.py +18 -10
- monocle_apptrace/instrumentation/metamodel/azfunc/_helper.py +13 -11
- monocle_apptrace/instrumentation/metamodel/azfunc/entities/http.py +5 -0
- monocle_apptrace/instrumentation/metamodel/azureaiinference/_helper.py +88 -8
- monocle_apptrace/instrumentation/metamodel/azureaiinference/entities/inference.py +22 -8
- monocle_apptrace/instrumentation/metamodel/botocore/_helper.py +92 -16
- monocle_apptrace/instrumentation/metamodel/botocore/entities/inference.py +13 -8
- monocle_apptrace/instrumentation/metamodel/botocore/handlers/botocore_span_handler.py +1 -1
- monocle_apptrace/instrumentation/metamodel/fastapi/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/fastapi/_helper.py +82 -0
- monocle_apptrace/instrumentation/metamodel/fastapi/entities/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/fastapi/entities/http.py +44 -0
- monocle_apptrace/instrumentation/metamodel/fastapi/methods.py +23 -0
- monocle_apptrace/instrumentation/metamodel/finish_types.py +463 -0
- monocle_apptrace/instrumentation/metamodel/flask/_helper.py +6 -11
- monocle_apptrace/instrumentation/metamodel/gemini/_helper.py +51 -7
- monocle_apptrace/instrumentation/metamodel/gemini/entities/inference.py +22 -11
- monocle_apptrace/instrumentation/metamodel/gemini/entities/retrieval.py +43 -0
- monocle_apptrace/instrumentation/metamodel/gemini/methods.py +18 -1
- monocle_apptrace/instrumentation/metamodel/haystack/_helper.py +79 -8
- monocle_apptrace/instrumentation/metamodel/haystack/entities/inference.py +15 -10
- monocle_apptrace/instrumentation/metamodel/haystack/methods.py +7 -0
- monocle_apptrace/instrumentation/metamodel/lambdafunc/_helper.py +78 -0
- monocle_apptrace/instrumentation/metamodel/lambdafunc/entities/http.py +51 -0
- monocle_apptrace/instrumentation/metamodel/lambdafunc/methods.py +23 -0
- monocle_apptrace/instrumentation/metamodel/lambdafunc/wrapper.py +23 -0
- monocle_apptrace/instrumentation/metamodel/langchain/_helper.py +145 -19
- monocle_apptrace/instrumentation/metamodel/langchain/entities/inference.py +19 -10
- monocle_apptrace/instrumentation/metamodel/langgraph/_helper.py +67 -10
- monocle_apptrace/instrumentation/metamodel/langgraph/entities/inference.py +127 -20
- monocle_apptrace/instrumentation/metamodel/langgraph/langgraph_processor.py +46 -0
- monocle_apptrace/instrumentation/metamodel/langgraph/methods.py +35 -9
- 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 +108 -0
- monocle_apptrace/instrumentation/metamodel/litellm/methods.py +19 -0
- monocle_apptrace/instrumentation/metamodel/llamaindex/_helper.py +227 -16
- monocle_apptrace/instrumentation/metamodel/llamaindex/entities/agent.py +127 -10
- monocle_apptrace/instrumentation/metamodel/llamaindex/entities/inference.py +13 -8
- monocle_apptrace/instrumentation/metamodel/llamaindex/llamaindex_processor.py +62 -0
- monocle_apptrace/instrumentation/metamodel/llamaindex/methods.py +68 -1
- monocle_apptrace/instrumentation/metamodel/mcp/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/mcp/_helper.py +118 -0
- monocle_apptrace/instrumentation/metamodel/mcp/entities/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/mcp/entities/inference.py +48 -0
- monocle_apptrace/instrumentation/metamodel/mcp/mcp_processor.py +8 -0
- monocle_apptrace/instrumentation/metamodel/mcp/methods.py +21 -0
- monocle_apptrace/instrumentation/metamodel/openai/_helper.py +188 -16
- monocle_apptrace/instrumentation/metamodel/openai/entities/inference.py +148 -92
- monocle_apptrace/instrumentation/metamodel/openai/entities/retrieval.py +1 -1
- monocle_apptrace/instrumentation/metamodel/teamsai/_helper.py +53 -23
- monocle_apptrace/instrumentation/metamodel/teamsai/entities/inference/actionplanner_output_processor.py +1 -1
- monocle_apptrace/instrumentation/metamodel/teamsai/entities/inference/teamsai_output_processor.py +15 -9
- monocle_apptrace/instrumentation/metamodel/teamsai/sample.json +0 -4
- {monocle_apptrace-0.4.2.dist-info → monocle_apptrace-0.5.0.dist-info}/METADATA +27 -11
- {monocle_apptrace-0.4.2.dist-info → monocle_apptrace-0.5.0.dist-info}/RECORD +88 -47
- {monocle_apptrace-0.4.2.dist-info → monocle_apptrace-0.5.0.dist-info}/WHEEL +0 -0
- {monocle_apptrace-0.4.2.dist-info → monocle_apptrace-0.5.0.dist-info}/licenses/LICENSE +0 -0
- {monocle_apptrace-0.4.2.dist-info → monocle_apptrace-0.5.0.dist-info}/licenses/NOTICE +0 -0
|
@@ -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, get_llm_type, get_status, get_status_code
|
|
4
|
+
from monocle_apptrace.instrumentation.common.utils import get_error_message, resolve_from_alias, get_llm_type, get_status, get_status_code
|
|
5
5
|
|
|
6
6
|
INFERENCE = {
|
|
7
7
|
"type": "inference.framework",
|
|
@@ -30,11 +30,11 @@ INFERENCE = {
|
|
|
30
30
|
{
|
|
31
31
|
"_comment": "LLM Model",
|
|
32
32
|
"attribute": "name",
|
|
33
|
-
"accessor": lambda arguments: resolve_from_alias(arguments['instance'].__dict__, ['model', 'model_name', 'endpoint_name', 'deployment_name'])
|
|
33
|
+
"accessor": lambda arguments: resolve_from_alias(arguments['instance'].__dict__, ['model', 'model_name', 'endpoint_name', 'deployment_name', 'model_id'])
|
|
34
34
|
},
|
|
35
35
|
{
|
|
36
36
|
"attribute": "type",
|
|
37
|
-
"accessor": lambda arguments: 'model.llm.' + resolve_from_alias(arguments['instance'].__dict__, ['model', 'model_name', 'endpoint_name', 'deployment_name'])
|
|
37
|
+
"accessor": lambda arguments: 'model.llm.' + resolve_from_alias(arguments['instance'].__dict__, ['model', 'model_name', 'endpoint_name', 'deployment_name', 'model_id'])
|
|
38
38
|
}
|
|
39
39
|
]
|
|
40
40
|
],
|
|
@@ -53,13 +53,8 @@ INFERENCE = {
|
|
|
53
53
|
"name": "data.output",
|
|
54
54
|
"attributes": [
|
|
55
55
|
{
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"accessor": lambda arguments: get_status(arguments)
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
"attribute": "status_code",
|
|
62
|
-
"accessor": lambda arguments: get_status_code(arguments)
|
|
56
|
+
"attribute": "error_code",
|
|
57
|
+
"accessor": lambda arguments: get_error_message(arguments)
|
|
63
58
|
},
|
|
64
59
|
{
|
|
65
60
|
"attribute": "response",
|
|
@@ -73,6 +68,20 @@ INFERENCE = {
|
|
|
73
68
|
{
|
|
74
69
|
"_comment": "this is metadata usage from LLM",
|
|
75
70
|
"accessor": lambda arguments: _helper.update_span_from_llm_response(arguments['result'], arguments['instance'])
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"attribute": "finish_reason",
|
|
74
|
+
"accessor": lambda arguments: _helper.extract_finish_reason(arguments)
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"attribute": "finish_type",
|
|
78
|
+
"accessor": lambda arguments: _helper.map_finish_reason_to_finish_type(
|
|
79
|
+
_helper.extract_finish_reason(arguments)
|
|
80
|
+
)
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"attribute": "inference_sub_type",
|
|
84
|
+
"accessor": lambda arguments: _helper.agent_inference_type(arguments)
|
|
76
85
|
}
|
|
77
86
|
]
|
|
78
87
|
}
|
|
@@ -1,15 +1,20 @@
|
|
|
1
|
+
from opentelemetry.context import get_value
|
|
1
2
|
from monocle_apptrace.instrumentation.common.utils import resolve_from_alias
|
|
2
3
|
import logging
|
|
3
4
|
logger = logging.getLogger(__name__)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
DELEGATION_NAME_PREFIX = 'transfer_to_'
|
|
7
|
+
ROOT_AGENT_NAME = 'LangGraph'
|
|
8
|
+
LANGGRAPTH_AGENT_NAME_KEY = "agent.langgraph"
|
|
9
|
+
|
|
10
|
+
def extract_agent_response(response):
|
|
6
11
|
try:
|
|
7
|
-
if 'messages' in response:
|
|
12
|
+
if response is not None and 'messages' in response:
|
|
8
13
|
output = response["messages"][-1]
|
|
9
14
|
return str(output.content)
|
|
10
15
|
except Exception as e:
|
|
11
|
-
logger.warning("Warning: Error occurred in
|
|
12
|
-
|
|
16
|
+
logger.warning("Warning: Error occurred in handle_response: %s", str(e))
|
|
17
|
+
return ""
|
|
13
18
|
|
|
14
19
|
def agent_instructions(arguments):
|
|
15
20
|
if callable(arguments['kwargs']['agent'].instructions):
|
|
@@ -17,11 +22,13 @@ def agent_instructions(arguments):
|
|
|
17
22
|
else:
|
|
18
23
|
return arguments['kwargs']['agent'].instructions
|
|
19
24
|
|
|
20
|
-
def
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
def extract_agent_input(arguments):
|
|
26
|
+
if arguments['result'] is not None and 'messages' in arguments['result']:
|
|
27
|
+
history = arguments['result']['messages']
|
|
28
|
+
for message in history:
|
|
29
|
+
if hasattr(message, 'content') and hasattr(message, 'type') and message.type == "human": # Check if the message is a HumanMessage
|
|
30
|
+
return message.content
|
|
31
|
+
return None
|
|
25
32
|
|
|
26
33
|
def get_inference_endpoint(arguments):
|
|
27
34
|
inference_endpoint = resolve_from_alias(arguments['instance'].client.__dict__, ['azure_endpoint', 'api_base', '_base_url'])
|
|
@@ -33,7 +40,6 @@ def tools(instance):
|
|
|
33
40
|
if hasattr(tools,'bound') and hasattr(tools.bound,'tools_by_name'):
|
|
34
41
|
return list(tools.bound.tools_by_name.keys())
|
|
35
42
|
|
|
36
|
-
|
|
37
43
|
def update_span_from_llm_response(response):
|
|
38
44
|
meta_dict = {}
|
|
39
45
|
token_usage = None
|
|
@@ -46,3 +52,54 @@ def update_span_from_llm_response(response):
|
|
|
46
52
|
meta_dict.update({"prompt_tokens": token_usage.get('prompt_tokens')})
|
|
47
53
|
meta_dict.update({"total_tokens": token_usage.get('total_tokens')})
|
|
48
54
|
return meta_dict
|
|
55
|
+
|
|
56
|
+
def extract_tool_response(result):
|
|
57
|
+
if result is not None and hasattr(result, 'content'):
|
|
58
|
+
return result.content
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
def get_status(result):
|
|
62
|
+
if result is not None and hasattr(result, 'status'):
|
|
63
|
+
return result.status
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
def extract_tool_input(arguments):
|
|
67
|
+
tool_input = arguments['args'][0]
|
|
68
|
+
if isinstance(tool_input, str):
|
|
69
|
+
return [tool_input]
|
|
70
|
+
else:
|
|
71
|
+
return list(tool_input.values())
|
|
72
|
+
|
|
73
|
+
def get_name(instance):
|
|
74
|
+
return instance.name if hasattr(instance, 'name') else ""
|
|
75
|
+
|
|
76
|
+
def get_agent_name(instance) -> str:
|
|
77
|
+
return get_name(instance)
|
|
78
|
+
|
|
79
|
+
def get_tool_name(instance) -> str:
|
|
80
|
+
return get_name(instance)
|
|
81
|
+
|
|
82
|
+
def is_delegation_tool(instance) -> bool:
|
|
83
|
+
return get_name(instance).startswith(DELEGATION_NAME_PREFIX)
|
|
84
|
+
|
|
85
|
+
def get_target_agent(instance) -> str:
|
|
86
|
+
return get_name(instance).replace(DELEGATION_NAME_PREFIX, '', 1)
|
|
87
|
+
|
|
88
|
+
def is_root_agent_name(instance) -> bool:
|
|
89
|
+
return get_name(instance) == ROOT_AGENT_NAME
|
|
90
|
+
|
|
91
|
+
def get_source_agent() -> str:
|
|
92
|
+
"""Get the name of the agent that initiated the request."""
|
|
93
|
+
from_agent = get_value(LANGGRAPTH_AGENT_NAME_KEY)
|
|
94
|
+
return from_agent if from_agent is not None else ""
|
|
95
|
+
|
|
96
|
+
def get_description(instance) -> str:
|
|
97
|
+
return instance.description if hasattr(instance, 'description') else ""
|
|
98
|
+
|
|
99
|
+
def get_agent_description(instance) -> str:
|
|
100
|
+
"""Get the description of the agent."""
|
|
101
|
+
return get_description(instance)
|
|
102
|
+
|
|
103
|
+
def get_tool_description(instance) -> str:
|
|
104
|
+
"""Get the description of the tool."""
|
|
105
|
+
return get_description(instance)
|
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
from monocle_apptrace.instrumentation.metamodel.langgraph import (
|
|
2
2
|
_helper
|
|
3
3
|
)
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
|
|
5
|
+
AGENT = {
|
|
6
|
+
"type": "agentic.invocation",
|
|
6
7
|
"attributes": [
|
|
7
8
|
[
|
|
8
9
|
{
|
|
9
10
|
"_comment": "agent type",
|
|
10
11
|
"attribute": "type",
|
|
11
|
-
"accessor": lambda arguments:'agent.
|
|
12
|
+
"accessor": lambda arguments:'agent.langgraph'
|
|
12
13
|
},
|
|
13
14
|
{
|
|
14
15
|
"_comment": "name of the agent",
|
|
15
16
|
"attribute": "name",
|
|
16
|
-
"accessor": lambda arguments:arguments['instance']
|
|
17
|
+
"accessor": lambda arguments: _helper.get_agent_name(arguments['instance'])
|
|
17
18
|
},
|
|
18
19
|
{
|
|
19
|
-
"_comment": "agent
|
|
20
|
-
"attribute": "
|
|
21
|
-
"accessor": lambda arguments: _helper.
|
|
20
|
+
"_comment": "agent description",
|
|
21
|
+
"attribute": "description",
|
|
22
|
+
"accessor": lambda arguments: _helper.get_agent_description(arguments['instance'])
|
|
22
23
|
}
|
|
23
24
|
]
|
|
24
25
|
],
|
|
@@ -27,9 +28,9 @@ INFERENCE = {
|
|
|
27
28
|
"name":"data.input",
|
|
28
29
|
"attributes": [
|
|
29
30
|
{
|
|
30
|
-
"_comment": "this is
|
|
31
|
+
"_comment": "this is Agent input",
|
|
31
32
|
"attribute": "query",
|
|
32
|
-
"accessor": lambda arguments: _helper.
|
|
33
|
+
"accessor": lambda arguments: _helper.extract_agent_input(arguments)
|
|
33
34
|
}
|
|
34
35
|
]
|
|
35
36
|
},
|
|
@@ -39,18 +40,124 @@ INFERENCE = {
|
|
|
39
40
|
{
|
|
40
41
|
"_comment": "this is response from LLM",
|
|
41
42
|
"attribute": "response",
|
|
42
|
-
"accessor": lambda arguments: _helper.
|
|
43
|
+
"accessor": lambda arguments: _helper.extract_agent_response(arguments['result'])
|
|
43
44
|
}
|
|
44
45
|
]
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
"name": "metadata",
|
|
48
|
-
"attributes": [
|
|
49
|
-
{
|
|
50
|
-
"_comment": "this is metadata usage from LLM",
|
|
51
|
-
"accessor": lambda arguments: _helper.update_span_from_llm_response(arguments['result'])
|
|
52
|
-
}
|
|
53
|
-
]
|
|
54
46
|
}
|
|
55
47
|
]
|
|
56
|
-
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
AGENT_REQUEST = {
|
|
51
|
+
"type": "agentic.request",
|
|
52
|
+
"attributes": [
|
|
53
|
+
[
|
|
54
|
+
{
|
|
55
|
+
"_comment": "agent type",
|
|
56
|
+
"attribute": "type",
|
|
57
|
+
"accessor": lambda arguments:'agent.langgraph'
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
],
|
|
61
|
+
"events": [
|
|
62
|
+
{
|
|
63
|
+
"name":"data.input",
|
|
64
|
+
"attributes": [
|
|
65
|
+
{
|
|
66
|
+
"_comment": "this is Agent input",
|
|
67
|
+
"attribute": "input",
|
|
68
|
+
"accessor": lambda arguments: _helper.extract_agent_input(arguments)
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"name":"data.output",
|
|
74
|
+
"attributes": [
|
|
75
|
+
{
|
|
76
|
+
"_comment": "this is response from LLM",
|
|
77
|
+
"attribute": "response",
|
|
78
|
+
"accessor": lambda arguments: _helper.extract_agent_response(arguments['result'])
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
TOOLS = {
|
|
86
|
+
"type": "agentic.tool.invocation",
|
|
87
|
+
"attributes": [
|
|
88
|
+
[
|
|
89
|
+
{
|
|
90
|
+
"_comment": "tool type",
|
|
91
|
+
"attribute": "type",
|
|
92
|
+
"accessor": lambda arguments:'tool.langgraph'
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"_comment": "name of the tool",
|
|
96
|
+
"attribute": "name",
|
|
97
|
+
"accessor": lambda arguments: _helper.get_tool_name(arguments['instance'])
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"_comment": "tool description",
|
|
101
|
+
"attribute": "description",
|
|
102
|
+
"accessor": lambda arguments: _helper.get_tool_description(arguments['instance'])
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
[
|
|
106
|
+
{
|
|
107
|
+
"_comment": "name of the agent",
|
|
108
|
+
"attribute": "name",
|
|
109
|
+
"accessor": lambda arguments: _helper.get_source_agent()
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"_comment": "agent type",
|
|
113
|
+
"attribute": "type",
|
|
114
|
+
"accessor": lambda arguments:'agent.langgraph'
|
|
115
|
+
}
|
|
116
|
+
]
|
|
117
|
+
],
|
|
118
|
+
"events": [
|
|
119
|
+
{
|
|
120
|
+
"name":"data.input",
|
|
121
|
+
"attributes": [
|
|
122
|
+
{
|
|
123
|
+
"_comment": "this is Tool input",
|
|
124
|
+
"attribute": "Inputs",
|
|
125
|
+
"accessor": lambda arguments: _helper.extract_tool_input(arguments)
|
|
126
|
+
},
|
|
127
|
+
]
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"name":"data.output",
|
|
131
|
+
"attributes": [
|
|
132
|
+
{
|
|
133
|
+
"_comment": "this is response from Tool",
|
|
134
|
+
"attribute": "response",
|
|
135
|
+
"accessor": lambda arguments: _helper.extract_tool_response(arguments['result'])
|
|
136
|
+
}
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
AGENT_DELEGATION = {
|
|
143
|
+
"type": "agentic.delegation",
|
|
144
|
+
"attributes": [
|
|
145
|
+
[
|
|
146
|
+
{
|
|
147
|
+
"_comment": "agent type",
|
|
148
|
+
"attribute": "type",
|
|
149
|
+
"accessor": lambda arguments:'agent.langgraph'
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"_comment": "name of the agent",
|
|
153
|
+
"attribute": "from_agent",
|
|
154
|
+
"accessor": lambda arguments: _helper.get_source_agent()
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"_comment": "name of the agent called",
|
|
158
|
+
"attribute": "to_agent",
|
|
159
|
+
"accessor": lambda arguments: _helper.get_target_agent(arguments['instance'])
|
|
160
|
+
}
|
|
161
|
+
]
|
|
162
|
+
]
|
|
163
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from opentelemetry.context import set_value, attach, detach
|
|
2
|
+
from monocle_apptrace.instrumentation.common.constants import AGENT_PREFIX_KEY
|
|
3
|
+
from monocle_apptrace.instrumentation.common.span_handler import SpanHandler
|
|
4
|
+
from monocle_apptrace.instrumentation.metamodel.langgraph._helper import (
|
|
5
|
+
DELEGATION_NAME_PREFIX, get_name, is_root_agent_name, is_delegation_tool, LANGGRAPTH_AGENT_NAME_KEY
|
|
6
|
+
|
|
7
|
+
)
|
|
8
|
+
from monocle_apptrace.instrumentation.metamodel.langgraph.entities.inference import (
|
|
9
|
+
AGENT_DELEGATION, AGENT_REQUEST
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
class LanggraphAgentHandler(SpanHandler):
|
|
13
|
+
def pre_tracing(self, to_wrap, wrapped, instance, args, kwargs):
|
|
14
|
+
context = set_value(LANGGRAPTH_AGENT_NAME_KEY, get_name(instance))
|
|
15
|
+
context = set_value(AGENT_PREFIX_KEY, DELEGATION_NAME_PREFIX, context)
|
|
16
|
+
return attach(context)
|
|
17
|
+
|
|
18
|
+
def post_tracing(self, to_wrap, wrapped, instance, args, kwargs, result, token):
|
|
19
|
+
if token is not None:
|
|
20
|
+
detach(token)
|
|
21
|
+
|
|
22
|
+
# In multi agent scenarios, the root agent is the one that orchestrates the other agents. LangGraph generates an extra root level invoke()
|
|
23
|
+
# call on top of the supervisor agent invoke().
|
|
24
|
+
# This span handler resets the parent invoke call as generic type to avoid duplicate attributes/events in supervisor span and this root span.
|
|
25
|
+
def hydrate_span(self, to_wrap, wrapped, instance, args, kwargs, result, span, parent_span = None, ex:Exception = None) -> bool:
|
|
26
|
+
if is_root_agent_name(instance) and "parent.agent.span" in span.attributes:
|
|
27
|
+
agent_request_wrapper = to_wrap.copy()
|
|
28
|
+
agent_request_wrapper["output_processor"] = AGENT_REQUEST
|
|
29
|
+
else:
|
|
30
|
+
agent_request_wrapper = to_wrap
|
|
31
|
+
if hasattr(instance, 'name') and parent_span is not None and not SpanHandler.is_root_span(parent_span):
|
|
32
|
+
parent_span.set_attribute("parent.agent.span", True)
|
|
33
|
+
return super().hydrate_span(agent_request_wrapper, wrapped, instance, args, kwargs, result, span, parent_span, ex)
|
|
34
|
+
|
|
35
|
+
class LanggraphToolHandler(SpanHandler):
|
|
36
|
+
# LangGraph uses an internal tool to initate delegation to other agents. The method is tool invoke() with tool name as `transfer_to_<agent_name>`.
|
|
37
|
+
# Hence we usea different output processor for tool invoke() to format the span as agentic.delegation.
|
|
38
|
+
def hydrate_span(self, to_wrap, wrapped, instance, args, kwargs, result, span, parent_span = None, ex:Exception = None) -> bool:
|
|
39
|
+
if is_delegation_tool(instance):
|
|
40
|
+
agent_request_wrapper = to_wrap.copy()
|
|
41
|
+
agent_request_wrapper["output_processor"] = AGENT_DELEGATION
|
|
42
|
+
else:
|
|
43
|
+
agent_request_wrapper = to_wrap
|
|
44
|
+
|
|
45
|
+
return super().hydrate_span(agent_request_wrapper, wrapped, instance, args, kwargs, result, span, parent_span, ex)
|
|
46
|
+
|
|
@@ -1,14 +1,40 @@
|
|
|
1
|
-
from monocle_apptrace.instrumentation.common.wrapper import task_wrapper
|
|
1
|
+
from monocle_apptrace.instrumentation.common.wrapper import task_wrapper, atask_wrapper
|
|
2
2
|
from monocle_apptrace.instrumentation.metamodel.langgraph.entities.inference import (
|
|
3
|
-
|
|
3
|
+
AGENT,
|
|
4
|
+
TOOLS,
|
|
4
5
|
)
|
|
6
|
+
|
|
5
7
|
LANGGRAPH_METHODS = [
|
|
6
8
|
{
|
|
7
9
|
"package": "langgraph.graph.state",
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
10
|
+
"object": "CompiledStateGraph",
|
|
11
|
+
"method": "invoke",
|
|
12
|
+
"wrapper_method": task_wrapper,
|
|
13
|
+
"span_handler": "langgraph_agent_handler",
|
|
14
|
+
"output_processor": AGENT,
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"package": "langgraph.graph.state",
|
|
18
|
+
"object": "CompiledStateGraph",
|
|
19
|
+
"method": "ainvoke",
|
|
20
|
+
"wrapper_method": atask_wrapper,
|
|
21
|
+
"span_handler": "langgraph_agent_handler",
|
|
22
|
+
"output_processor": AGENT,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"package": "langchain_core.tools.base",
|
|
26
|
+
"object": "BaseTool",
|
|
27
|
+
"method": "run",
|
|
28
|
+
"wrapper_method": task_wrapper,
|
|
29
|
+
"span_handler": "langgraph_tool_handler",
|
|
30
|
+
"output_processor": TOOLS,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"package": "langchain_core.tools.base",
|
|
34
|
+
"object": "BaseTool",
|
|
35
|
+
"method": "arun",
|
|
36
|
+
"wrapper_method": atask_wrapper,
|
|
37
|
+
"span_handler": "langgraph_tool_handler",
|
|
38
|
+
"output_processor": TOOLS,
|
|
39
|
+
},
|
|
40
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides utility functions for extracting system, user,
|
|
3
|
+
and assistant messages from various input formats.
|
|
4
|
+
"""
|
|
5
|
+
import json
|
|
6
|
+
import logging
|
|
7
|
+
from monocle_apptrace.instrumentation.common.utils import (
|
|
8
|
+
Option,
|
|
9
|
+
get_json_dumps,
|
|
10
|
+
try_option,
|
|
11
|
+
get_exception_message,
|
|
12
|
+
get_status_code,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def extract_messages(kwargs):
|
|
19
|
+
"""Extract system and user messages"""
|
|
20
|
+
try:
|
|
21
|
+
messages = []
|
|
22
|
+
if 'messages' in kwargs and len(kwargs['messages']) > 0:
|
|
23
|
+
for msg in kwargs['messages']:
|
|
24
|
+
if msg.get('content') and msg.get('role'):
|
|
25
|
+
messages.append({msg['role']: msg['content']})
|
|
26
|
+
|
|
27
|
+
return [get_json_dumps(message) for message in messages]
|
|
28
|
+
except Exception as e:
|
|
29
|
+
logger.warning("Warning: Error occurred in extract_messages: %s", str(e))
|
|
30
|
+
return []
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def extract_assistant_message(arguments):
|
|
34
|
+
try:
|
|
35
|
+
messages = []
|
|
36
|
+
status = get_status_code(arguments)
|
|
37
|
+
if status == 'success' or status == 'completed':
|
|
38
|
+
response = arguments["result"]
|
|
39
|
+
if (response is not None and hasattr(response, "choices") and len(response.choices) > 0):
|
|
40
|
+
if hasattr(response.choices[0], "message"):
|
|
41
|
+
role = (
|
|
42
|
+
response.choices[0].message.role
|
|
43
|
+
if hasattr(response.choices[0].message, "role")
|
|
44
|
+
else "assistant"
|
|
45
|
+
)
|
|
46
|
+
messages.append({role: response.choices[0].message.content})
|
|
47
|
+
return get_json_dumps(messages[0]) if messages else ""
|
|
48
|
+
else:
|
|
49
|
+
if arguments["exception"] is not None:
|
|
50
|
+
return get_exception_message(arguments)
|
|
51
|
+
elif hasattr(arguments["result"], "error"):
|
|
52
|
+
return arguments["result"].error
|
|
53
|
+
|
|
54
|
+
except (IndexError, AttributeError) as e:
|
|
55
|
+
logger.warning(
|
|
56
|
+
"Warning: Error occurred in extract_assistant_message: %s", str(e)
|
|
57
|
+
)
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
def extract_provider_name(url):
|
|
61
|
+
"""Extract host from a URL string (e.g., https://api.openai.com/v1/ -> api.openai.com)"""
|
|
62
|
+
if not url:
|
|
63
|
+
return None
|
|
64
|
+
return url.split("//")[-1].split("/")[0]
|
|
65
|
+
|
|
66
|
+
def resolve_from_alias(my_map, alias):
|
|
67
|
+
"""Find a alias that is not none from list of aliases"""
|
|
68
|
+
|
|
69
|
+
for i in alias:
|
|
70
|
+
if i in my_map.keys():
|
|
71
|
+
return my_map[i]
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def update_span_from_llm_response(response):
|
|
76
|
+
meta_dict = {}
|
|
77
|
+
token_usage = None
|
|
78
|
+
if response is not None:
|
|
79
|
+
if token_usage is None and hasattr(response, "usage") and response.usage is not None:
|
|
80
|
+
token_usage = response.usage
|
|
81
|
+
elif token_usage is None and hasattr(response, "response_metadata"):
|
|
82
|
+
token_usage = getattr(response.response_metadata, "token_usage", None) \
|
|
83
|
+
if hasattr(response.response_metadata, "token_usage") \
|
|
84
|
+
else response.response_metadata.get("token_usage", None)
|
|
85
|
+
if token_usage is not None:
|
|
86
|
+
meta_dict.update({"completion_tokens": getattr(token_usage, "completion_tokens", None) or getattr(token_usage, "output_tokens", None)})
|
|
87
|
+
meta_dict.update({"prompt_tokens": getattr(token_usage, "prompt_tokens", None) or getattr(token_usage, "input_tokens", None)})
|
|
88
|
+
meta_dict.update({"total_tokens": getattr(token_usage, "total_tokens")})
|
|
89
|
+
return meta_dict
|
|
File without changes
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
from monocle_apptrace.instrumentation.metamodel.litellm import (
|
|
2
|
+
_helper,
|
|
3
|
+
)
|
|
4
|
+
from monocle_apptrace.instrumentation.common.utils import (
|
|
5
|
+
get_error_message,
|
|
6
|
+
resolve_from_alias,
|
|
7
|
+
get_llm_type,
|
|
8
|
+
)
|
|
9
|
+
INFERENCE = {
|
|
10
|
+
"type": "inference",
|
|
11
|
+
"attributes": [
|
|
12
|
+
[
|
|
13
|
+
{
|
|
14
|
+
"_comment": "provider type ,name , deployment , inference_endpoint",
|
|
15
|
+
"attribute": "type",
|
|
16
|
+
"accessor": lambda arguments: "inference."
|
|
17
|
+
+ (get_llm_type(arguments['instance']) or 'generic')
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"attribute": "provider_name",
|
|
21
|
+
"accessor": lambda arguments: _helper.extract_provider_name(
|
|
22
|
+
resolve_from_alias(
|
|
23
|
+
arguments["kwargs"],
|
|
24
|
+
["azure_endpoint", "api_base", "endpoint"],
|
|
25
|
+
)
|
|
26
|
+
),
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"attribute": "deployment",
|
|
30
|
+
"accessor": lambda arguments: resolve_from_alias(
|
|
31
|
+
arguments["kwargs"].__dict__,
|
|
32
|
+
[
|
|
33
|
+
"engine",
|
|
34
|
+
"azure_deployment",
|
|
35
|
+
"deployment_name",
|
|
36
|
+
"deployment_id",
|
|
37
|
+
"deployment",
|
|
38
|
+
],
|
|
39
|
+
),
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"attribute": "inference_endpoint",
|
|
43
|
+
"accessor": lambda arguments: resolve_from_alias(
|
|
44
|
+
arguments["kwargs"],
|
|
45
|
+
["azure_endpoint", "api_base", "endpoint"],
|
|
46
|
+
)
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
[
|
|
50
|
+
{
|
|
51
|
+
"_comment": "LLM Model",
|
|
52
|
+
"attribute": "name",
|
|
53
|
+
"accessor": lambda arguments: resolve_from_alias(
|
|
54
|
+
arguments["kwargs"],
|
|
55
|
+
["model", "model_name", "endpoint_name", "deployment_name"],
|
|
56
|
+
),
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"attribute": "type",
|
|
60
|
+
"accessor": lambda arguments: "model.llm."
|
|
61
|
+
+ resolve_from_alias(
|
|
62
|
+
arguments["kwargs"],
|
|
63
|
+
["model", "model_name", "endpoint_name", "deployment_name"],
|
|
64
|
+
),
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
],
|
|
68
|
+
"events": [
|
|
69
|
+
{
|
|
70
|
+
"name": "data.input",
|
|
71
|
+
"attributes": [
|
|
72
|
+
{
|
|
73
|
+
"_comment": "this is instruction and user query to LLM",
|
|
74
|
+
"attribute": "input",
|
|
75
|
+
"accessor": lambda arguments: _helper.extract_messages(
|
|
76
|
+
arguments["kwargs"]
|
|
77
|
+
),
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"name": "data.output",
|
|
83
|
+
"attributes": [
|
|
84
|
+
|
|
85
|
+
{
|
|
86
|
+
"attribute": "error_code",
|
|
87
|
+
"accessor": lambda arguments: get_error_message(arguments)
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"_comment": "this is result from LLM",
|
|
91
|
+
"attribute": "response",
|
|
92
|
+
"accessor": lambda arguments: _helper.extract_assistant_message(arguments),
|
|
93
|
+
}
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"name": "metadata",
|
|
98
|
+
"attributes": [
|
|
99
|
+
{
|
|
100
|
+
"_comment": "this is metadata usage from LLM",
|
|
101
|
+
"accessor": lambda arguments: _helper.update_span_from_llm_response(
|
|
102
|
+
arguments["result"]
|
|
103
|
+
),
|
|
104
|
+
}
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from monocle_apptrace.instrumentation.common.wrapper import atask_wrapper, task_wrapper
|
|
2
|
+
from monocle_apptrace.instrumentation.metamodel.litellm.entities.inference import INFERENCE
|
|
3
|
+
|
|
4
|
+
LITELLM_METHODS = [
|
|
5
|
+
{
|
|
6
|
+
"package": "litellm.llms.openai.openai",
|
|
7
|
+
"object": "OpenAIChatCompletion",
|
|
8
|
+
"method": "completion",
|
|
9
|
+
"wrapper_method": task_wrapper,
|
|
10
|
+
"output_processor": INFERENCE
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"package": "litellm.llms.azure.azure",
|
|
14
|
+
"object": "AzureChatCompletion",
|
|
15
|
+
"method": "completion",
|
|
16
|
+
"wrapper_method": task_wrapper,
|
|
17
|
+
"output_processor": INFERENCE
|
|
18
|
+
}
|
|
19
|
+
]
|