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
|
@@ -2,7 +2,7 @@ from monocle_apptrace.instrumentation.common.wrapper import atask_wrapper, task_
|
|
|
2
2
|
from monocle_apptrace.instrumentation.metamodel.llamaindex.entities.inference import (
|
|
3
3
|
INFERENCE,
|
|
4
4
|
)
|
|
5
|
-
from monocle_apptrace.instrumentation.metamodel.llamaindex.entities.agent import AGENT
|
|
5
|
+
from monocle_apptrace.instrumentation.metamodel.llamaindex.entities.agent import AGENT, TOOLS, AGENT_REQUEST
|
|
6
6
|
from monocle_apptrace.instrumentation.metamodel.llamaindex.entities.retrieval import (
|
|
7
7
|
RETRIEVAL,
|
|
8
8
|
)
|
|
@@ -78,6 +78,14 @@ LLAMAINDEX_METHODS = [
|
|
|
78
78
|
"wrapper_method": atask_wrapper,
|
|
79
79
|
"output_processor": INFERENCE
|
|
80
80
|
},
|
|
81
|
+
{
|
|
82
|
+
"package": "llama_index.core.agent.workflow.multi_agent_workflow",
|
|
83
|
+
"object": "AgentWorkflow",
|
|
84
|
+
"method": "run",
|
|
85
|
+
"span_handler": "llamaindex_agent_handler",
|
|
86
|
+
"wrapper_method": task_wrapper,
|
|
87
|
+
"output_processor": AGENT_REQUEST
|
|
88
|
+
},
|
|
81
89
|
{
|
|
82
90
|
"package": "llama_index.core.agent",
|
|
83
91
|
"object": "ReActAgent",
|
|
@@ -85,6 +93,51 @@ LLAMAINDEX_METHODS = [
|
|
|
85
93
|
"wrapper_method": task_wrapper,
|
|
86
94
|
"output_processor": AGENT
|
|
87
95
|
},
|
|
96
|
+
{
|
|
97
|
+
"package": "llama_index.core.agent",
|
|
98
|
+
"object": "ReActAgent",
|
|
99
|
+
"method": "achat",
|
|
100
|
+
"wrapper_method": atask_wrapper,
|
|
101
|
+
"output_processor": AGENT
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"package": "llama_index.core.agent.workflow.function_agent",
|
|
105
|
+
"object": "FunctionAgent",
|
|
106
|
+
"method": "finalize",
|
|
107
|
+
"wrapper_method": atask_wrapper,
|
|
108
|
+
"output_processor": AGENT
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"package": "llama_index.core.agent.workflow.function_agent",
|
|
112
|
+
"object": "FunctionAgent",
|
|
113
|
+
"method": "take_step",
|
|
114
|
+
"span_handler": "llamaindex_agent_handler",
|
|
115
|
+
"wrapper_method": atask_wrapper
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"package": "llama_index.core.tools.function_tool",
|
|
119
|
+
"object": "FunctionTool",
|
|
120
|
+
"method": "call",
|
|
121
|
+
"span_handler": "llamaindex_single_agent_tool_handler",
|
|
122
|
+
"wrapper_method": task_wrapper,
|
|
123
|
+
"output_processor": TOOLS
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"package": "llama_index.core.tools.function_tool",
|
|
127
|
+
"object": "FunctionTool",
|
|
128
|
+
"method": "acall",
|
|
129
|
+
"span_handler": "llamaindex_single_agent_tool_handler",
|
|
130
|
+
"wrapper_method": atask_wrapper,
|
|
131
|
+
"output_processor": TOOLS
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"package": "llama_index.core.agent.workflow.multi_agent_workflow",
|
|
135
|
+
"object": "AgentWorkflow",
|
|
136
|
+
"method": "_call_tool",
|
|
137
|
+
"span_handler": "llamaindex_tool_handler",
|
|
138
|
+
"wrapper_method": atask_wrapper,
|
|
139
|
+
"output_processor": TOOLS
|
|
140
|
+
},
|
|
88
141
|
{
|
|
89
142
|
"package": "llama_index.llms.anthropic",
|
|
90
143
|
"object": "Anthropic",
|
|
@@ -98,5 +151,19 @@ LLAMAINDEX_METHODS = [
|
|
|
98
151
|
"method": "achat",
|
|
99
152
|
"wrapper_method": atask_wrapper,
|
|
100
153
|
"output_processor": INFERENCE
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"package": "llama_index.llms.gemini",
|
|
157
|
+
"object": "Gemini",
|
|
158
|
+
"method": "chat",
|
|
159
|
+
"wrapper_method": task_wrapper,
|
|
160
|
+
"output_processor": INFERENCE
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"package": "llama_index.llms.gemini",
|
|
164
|
+
"object": "Gemini",
|
|
165
|
+
"method": "achat",
|
|
166
|
+
"wrapper_method": atask_wrapper,
|
|
167
|
+
"output_processor": INFERENCE
|
|
101
168
|
}
|
|
102
169
|
]
|
|
File without changes
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
from monocle_apptrace.instrumentation.common.utils import with_tracer_wrapper
|
|
2
|
+
from opentelemetry.context import attach, set_value, get_value, detach
|
|
3
|
+
from monocle_apptrace.instrumentation.common.utils import resolve_from_alias
|
|
4
|
+
import logging
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def log(arguments):
|
|
11
|
+
print(f"Arguments: {arguments}")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_output_text(arguments):
|
|
15
|
+
# arguments["result"].content[0].text
|
|
16
|
+
if (
|
|
17
|
+
"result" in arguments
|
|
18
|
+
and hasattr(arguments["result"], "tools")
|
|
19
|
+
and isinstance(arguments["result"].tools, list)
|
|
20
|
+
):
|
|
21
|
+
tools = []
|
|
22
|
+
for tool in arguments["result"].tools:
|
|
23
|
+
if hasattr(tool, "name"):
|
|
24
|
+
tools.append(tool.name)
|
|
25
|
+
return tools
|
|
26
|
+
if (
|
|
27
|
+
"result" in arguments
|
|
28
|
+
and hasattr(arguments["result"], "content")
|
|
29
|
+
and isinstance(arguments["result"].content, list)
|
|
30
|
+
):
|
|
31
|
+
ret_val = []
|
|
32
|
+
for content in arguments["result"].content:
|
|
33
|
+
if hasattr(content, "text"):
|
|
34
|
+
ret_val.append(content.text)
|
|
35
|
+
return ret_val
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_name(arguments):
|
|
39
|
+
"""Get the name of the tool from the instance."""
|
|
40
|
+
|
|
41
|
+
args = arguments["args"]
|
|
42
|
+
if (
|
|
43
|
+
args
|
|
44
|
+
and hasattr(args[0], "root")
|
|
45
|
+
and hasattr(args[0].root, "params")
|
|
46
|
+
and hasattr(args[0].root.params, "name")
|
|
47
|
+
):
|
|
48
|
+
# If the first argument has a root with params and name, return that name
|
|
49
|
+
return args[0].root.params.name
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def get_type(arguments):
|
|
53
|
+
"""Get the type of the tool from the instance."""
|
|
54
|
+
args = arguments["args"]
|
|
55
|
+
if args and hasattr(args[0], "root") and hasattr(args[0].root, "method"):
|
|
56
|
+
# If the first argument has a root with a method, return that method's name
|
|
57
|
+
return args[0].root.method
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_params_arguments(arguments):
|
|
61
|
+
"""Get the params of the tool from the instance."""
|
|
62
|
+
|
|
63
|
+
args = arguments["args"]
|
|
64
|
+
if (
|
|
65
|
+
args
|
|
66
|
+
and hasattr(args[0], "root")
|
|
67
|
+
and hasattr(args[0].root, "params")
|
|
68
|
+
and hasattr(args[0].root.params, "arguments")
|
|
69
|
+
):
|
|
70
|
+
# If the first argument has a root with params and arguments, return those arguments
|
|
71
|
+
try:
|
|
72
|
+
return json.dumps(args[0].root.params.arguments)
|
|
73
|
+
except (TypeError, ValueError) as e:
|
|
74
|
+
logger.error(f"Error serializing arguments: {e}")
|
|
75
|
+
return str(args[0].root.params.arguments)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def get_url(arguments):
|
|
79
|
+
"""Get the URL of the tool from the instance."""
|
|
80
|
+
url = get_value("mcp.url", None)
|
|
81
|
+
|
|
82
|
+
return url
|
|
83
|
+
|
|
84
|
+
# this extracts the url from the langchain mcp adapter tools and attaches it to the context.
|
|
85
|
+
@with_tracer_wrapper
|
|
86
|
+
def langchain_mcp_wrapper(
|
|
87
|
+
tracer: any, handler: any, to_wrap, wrapped, instance, source_path, args, kwargs
|
|
88
|
+
):
|
|
89
|
+
return_value = None
|
|
90
|
+
try:
|
|
91
|
+
return_value = wrapped(*args, **kwargs)
|
|
92
|
+
return return_value
|
|
93
|
+
finally:
|
|
94
|
+
if (
|
|
95
|
+
return_value
|
|
96
|
+
and hasattr(return_value, "coroutine")
|
|
97
|
+
and kwargs.get("connection", None)
|
|
98
|
+
):
|
|
99
|
+
try:
|
|
100
|
+
# extract the URL from the connection and attach it to the context
|
|
101
|
+
url = kwargs.get("connection").get("url", None)
|
|
102
|
+
if url:
|
|
103
|
+
# wrap coroutine methods and attach the URL to the context
|
|
104
|
+
|
|
105
|
+
original_coroutine = return_value.coroutine
|
|
106
|
+
|
|
107
|
+
async def wrapped_coroutine(*args1, **kwargs1):
|
|
108
|
+
token = None
|
|
109
|
+
try:
|
|
110
|
+
token = attach(set_value("mcp.url", url))
|
|
111
|
+
return await original_coroutine(*args1, **kwargs1)
|
|
112
|
+
finally:
|
|
113
|
+
detach(token)
|
|
114
|
+
|
|
115
|
+
return_value.coroutine = wrapped_coroutine
|
|
116
|
+
|
|
117
|
+
except Exception as e:
|
|
118
|
+
pass
|
|
File without changes
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from monocle_apptrace.instrumentation.metamodel.mcp import _helper
|
|
2
|
+
|
|
3
|
+
TOOLS = {
|
|
4
|
+
"type": "agentic.mcp.invocation",
|
|
5
|
+
"attributes": [
|
|
6
|
+
[
|
|
7
|
+
{
|
|
8
|
+
"_comment": "name of the tool",
|
|
9
|
+
"attribute": "name",
|
|
10
|
+
"accessor": lambda arguments: _helper.get_name(arguments),
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"_comment": "tool type",
|
|
14
|
+
"attribute": "type",
|
|
15
|
+
"accessor": lambda arguments: "mcp.server",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"_comment": "tool url",
|
|
19
|
+
"attribute": "url",
|
|
20
|
+
"accessor": lambda arguments: _helper.get_url(arguments),
|
|
21
|
+
},
|
|
22
|
+
]
|
|
23
|
+
],
|
|
24
|
+
"events": [
|
|
25
|
+
{
|
|
26
|
+
"name": "data.input",
|
|
27
|
+
"attributes": [
|
|
28
|
+
{
|
|
29
|
+
"_comment": "this is Tool input",
|
|
30
|
+
"attribute": "input",
|
|
31
|
+
"accessor": lambda arguments: _helper.get_params_arguments(
|
|
32
|
+
arguments
|
|
33
|
+
),
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "data.output",
|
|
39
|
+
"attributes": [
|
|
40
|
+
{
|
|
41
|
+
"_comment": "this is Tool output",
|
|
42
|
+
"attribute": "output",
|
|
43
|
+
"accessor": lambda arguments: _helper.get_output_text(arguments)
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from monocle_apptrace.instrumentation.common.span_handler import SpanHandler
|
|
2
|
+
from monocle_apptrace.instrumentation.metamodel.mcp._helper import (
|
|
3
|
+
get_name
|
|
4
|
+
)
|
|
5
|
+
|
|
6
|
+
class MCPAgentHandler(SpanHandler):
|
|
7
|
+
def skip_span(self, to_wrap, wrapped, instance, args, kwargs) -> bool:
|
|
8
|
+
return get_name({"args": args, "kwargs": kwargs}) is None or args[0].root.method == "tools/list"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from monocle_apptrace.instrumentation.common.wrapper import atask_wrapper
|
|
2
|
+
from monocle_apptrace.instrumentation.metamodel.mcp import _helper
|
|
3
|
+
from monocle_apptrace.instrumentation.metamodel.mcp.entities.inference import TOOLS
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
MCP_METHODS = [
|
|
7
|
+
{
|
|
8
|
+
"package": "mcp.shared.session",
|
|
9
|
+
"object": "BaseSession",
|
|
10
|
+
"method": "send_request",
|
|
11
|
+
"wrapper_method": atask_wrapper,
|
|
12
|
+
"span_handler": "mcp_agent_handler",
|
|
13
|
+
"output_processor": TOOLS,
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"package": "langchain_mcp_adapters.tools",
|
|
17
|
+
"object": "",
|
|
18
|
+
"method": "convert_mcp_tool_to_langchain_tool",
|
|
19
|
+
"wrapper_method": _helper.langchain_mcp_wrapper,
|
|
20
|
+
},
|
|
21
|
+
]
|
|
@@ -3,33 +3,123 @@ This module provides utility functions for extracting system, user,
|
|
|
3
3
|
and assistant messages from various input formats.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
import json
|
|
6
7
|
import logging
|
|
8
|
+
from opentelemetry.context import get_value
|
|
7
9
|
from monocle_apptrace.instrumentation.common.utils import (
|
|
8
10
|
Option,
|
|
11
|
+
get_json_dumps,
|
|
9
12
|
try_option,
|
|
10
13
|
get_exception_message,
|
|
11
14
|
get_parent_span,
|
|
12
15
|
get_status_code,
|
|
13
16
|
)
|
|
14
17
|
from monocle_apptrace.instrumentation.common.span_handler import NonFrameworkSpanHandler, WORKFLOW_TYPE_MAP
|
|
18
|
+
from monocle_apptrace.instrumentation.metamodel.finish_types import (
|
|
19
|
+
map_openai_finish_reason_to_finish_type,
|
|
20
|
+
OPENAI_FINISH_REASON_MAPPING
|
|
21
|
+
)
|
|
22
|
+
from monocle_apptrace.instrumentation.common.constants import AGENT_PREFIX_KEY, CHILD_ERROR_CODE, INFERENCE_AGENT_DELEGATION, INFERENCE_COMMUNICATION, INFERENCE_TOOL_CALL
|
|
15
23
|
|
|
16
24
|
logger = logging.getLogger(__name__)
|
|
17
25
|
|
|
18
|
-
|
|
19
26
|
def extract_messages(kwargs):
|
|
20
27
|
"""Extract system and user messages"""
|
|
21
28
|
try:
|
|
22
29
|
messages = []
|
|
23
30
|
if 'instructions' in kwargs:
|
|
24
|
-
messages.append({'
|
|
31
|
+
messages.append({'system': kwargs.get('instructions', {})})
|
|
25
32
|
if 'input' in kwargs:
|
|
26
|
-
|
|
33
|
+
if isinstance(kwargs['input'], str):
|
|
34
|
+
messages.append({'user': kwargs.get('input', "")})
|
|
35
|
+
# [
|
|
36
|
+
# {
|
|
37
|
+
# "role": "developer",
|
|
38
|
+
# "content": "Talk like a pirate."
|
|
39
|
+
# },
|
|
40
|
+
# {
|
|
41
|
+
# "role": "user",
|
|
42
|
+
# "content": "Are semicolons optional in JavaScript?"
|
|
43
|
+
# }
|
|
44
|
+
# ]
|
|
45
|
+
if isinstance(kwargs['input'], list):
|
|
46
|
+
# kwargs['input']
|
|
47
|
+
# [
|
|
48
|
+
# {
|
|
49
|
+
# "content": "I need to book a flight from NYC to LAX and also book the Hilton hotel in Los Angeles. Also check the weather in Los Angeles.",
|
|
50
|
+
# "role": "user"
|
|
51
|
+
# },
|
|
52
|
+
# {
|
|
53
|
+
# "arguments": "{}",
|
|
54
|
+
# "call_id": "call_dSljcToR2LWwqWibPt0qjeHD",
|
|
55
|
+
# "name": "transfer_to_flight_agent",
|
|
56
|
+
# "type": "function_call",
|
|
57
|
+
# "id": "fc_689c30f96f708191aabb0ffd8098cdbd016ef325124ac05f",
|
|
58
|
+
# "status": "completed"
|
|
59
|
+
# },
|
|
60
|
+
# {
|
|
61
|
+
# "arguments": "{}",
|
|
62
|
+
# "call_id": "call_z0MTZroziWDUd0fxVemGM5Pg",
|
|
63
|
+
# "name": "transfer_to_hotel_agent",
|
|
64
|
+
# "type": "function_call",
|
|
65
|
+
# "id": "fc_689c30f99b808191a8743ff407fa8ee2016ef325124ac05f",
|
|
66
|
+
# "status": "completed"
|
|
67
|
+
# },
|
|
68
|
+
# {
|
|
69
|
+
# "arguments": "{\"city\":\"Los Angeles\"}",
|
|
70
|
+
# "call_id": "call_rrdRSPv5vcB4pgl6P4W8U2bX",
|
|
71
|
+
# "name": "get_weather_tool",
|
|
72
|
+
# "type": "function_call",
|
|
73
|
+
# "id": "fc_689c30f9b824819196d4ad9379d570f7016ef325124ac05f",
|
|
74
|
+
# "status": "completed"
|
|
75
|
+
# },
|
|
76
|
+
# {
|
|
77
|
+
# "call_id": "call_rrdRSPv5vcB4pgl6P4W8U2bX",
|
|
78
|
+
# "output": "The weather in Los Angeles is sunny and 75.",
|
|
79
|
+
# "type": "function_call_output"
|
|
80
|
+
# },
|
|
81
|
+
# {
|
|
82
|
+
# "call_id": "call_z0MTZroziWDUd0fxVemGM5Pg",
|
|
83
|
+
# "output": "Multiple handoffs detected, ignoring this one.",
|
|
84
|
+
# "type": "function_call_output"
|
|
85
|
+
# },
|
|
86
|
+
# {
|
|
87
|
+
# "call_id": "call_dSljcToR2LWwqWibPt0qjeHD",
|
|
88
|
+
# "output": "{\"assistant\": \"Flight Agent\"}",
|
|
89
|
+
# "type": "function_call_output"
|
|
90
|
+
# }
|
|
91
|
+
# ]
|
|
92
|
+
for item in kwargs['input']:
|
|
93
|
+
if isinstance(item, dict) and 'role' in item and 'content' in item:
|
|
94
|
+
messages.append({item['role']: item['content']})
|
|
95
|
+
elif isinstance(item, dict) and 'type' in item and item['type'] == 'function_call':
|
|
96
|
+
messages.append({
|
|
97
|
+
"tool_function": item.get("name", ""),
|
|
98
|
+
"tool_arguments": item.get("arguments", ""),
|
|
99
|
+
"call_id": item.get("call_id", "")
|
|
100
|
+
})
|
|
101
|
+
elif isinstance(item, dict) and 'type' in item and item['type'] == 'function_call_output':
|
|
102
|
+
messages.append({
|
|
103
|
+
"call_id": item.get("call_id", ""),
|
|
104
|
+
"output": item.get("output", "")
|
|
105
|
+
})
|
|
27
106
|
if 'messages' in kwargs and len(kwargs['messages']) >0:
|
|
28
107
|
for msg in kwargs['messages']:
|
|
29
108
|
if msg.get('content') and msg.get('role'):
|
|
30
109
|
messages.append({msg['role']: msg['content']})
|
|
110
|
+
elif msg.get('tool_calls') and msg.get('role'):
|
|
111
|
+
try:
|
|
112
|
+
tool_call_messages = []
|
|
113
|
+
for tool_call in msg['tool_calls']:
|
|
114
|
+
tool_call_messages.append(get_json_dumps({
|
|
115
|
+
"tool_function": tool_call.function.name,
|
|
116
|
+
"tool_arguments": tool_call.function.arguments,
|
|
117
|
+
}))
|
|
118
|
+
messages.append({msg['role']: tool_call_messages})
|
|
119
|
+
except Exception as e:
|
|
120
|
+
logger.warning("Warning: Error occurred while processing tool calls: %s", str(e))
|
|
31
121
|
|
|
32
|
-
return [
|
|
122
|
+
return [get_json_dumps(message) for message in messages]
|
|
33
123
|
except Exception as e:
|
|
34
124
|
logger.warning("Warning: Error occurred in extract_messages: %s", str(e))
|
|
35
125
|
return []
|
|
@@ -37,25 +127,62 @@ def extract_messages(kwargs):
|
|
|
37
127
|
|
|
38
128
|
def extract_assistant_message(arguments):
|
|
39
129
|
try:
|
|
130
|
+
messages = []
|
|
40
131
|
status = get_status_code(arguments)
|
|
41
|
-
|
|
42
|
-
if status == 'success':
|
|
132
|
+
if status == 'success' or status == 'completed':
|
|
43
133
|
response = arguments["result"]
|
|
44
|
-
if hasattr(response,"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
134
|
+
if hasattr(response, "tools") and isinstance(response.tools, list) and len(response.tools) > 0 and isinstance(response.tools[0], dict):
|
|
135
|
+
tools = []
|
|
136
|
+
for tool in response.tools:
|
|
137
|
+
tools.append({
|
|
138
|
+
"tool_id": tool.get("id", ""),
|
|
139
|
+
"tool_name": tool.get("name", ""),
|
|
140
|
+
"tool_arguments": tool.get("arguments", "")
|
|
141
|
+
})
|
|
142
|
+
messages.append({"tools": tools})
|
|
143
|
+
if hasattr(response, "output") and isinstance(response.output, list) and len(response.output) > 0:
|
|
144
|
+
response_messages = []
|
|
145
|
+
role = "assistant"
|
|
146
|
+
for response_message in response.output:
|
|
147
|
+
if(response_message.type == "function_call"):
|
|
148
|
+
role = "tools"
|
|
149
|
+
response_messages.append({
|
|
150
|
+
"tool_id": response_message.call_id,
|
|
151
|
+
"tool_name": response_message.name,
|
|
152
|
+
"tool_arguments": response_message.arguments
|
|
153
|
+
})
|
|
154
|
+
if len(response_messages) > 0:
|
|
155
|
+
messages.append({role: response_messages})
|
|
156
|
+
|
|
157
|
+
if hasattr(response, "output_text") and len(response.output_text):
|
|
158
|
+
role = response.role if hasattr(response, "role") else "assistant"
|
|
159
|
+
messages.append({role: response.output_text})
|
|
160
|
+
if (
|
|
161
|
+
response is not None
|
|
162
|
+
and hasattr(response, "choices")
|
|
163
|
+
and len(response.choices) > 0
|
|
164
|
+
):
|
|
165
|
+
if hasattr(response.choices[0], "message"):
|
|
166
|
+
role = (
|
|
167
|
+
response.choices[0].message.role
|
|
168
|
+
if hasattr(response.choices[0].message, "role")
|
|
169
|
+
else "assistant"
|
|
170
|
+
)
|
|
171
|
+
messages.append({role: response.choices[0].message.content})
|
|
172
|
+
return get_json_dumps(messages[0]) if messages else ""
|
|
49
173
|
else:
|
|
50
174
|
if arguments["exception"] is not None:
|
|
51
|
-
|
|
175
|
+
return get_exception_message(arguments)
|
|
52
176
|
elif hasattr(arguments["result"], "error"):
|
|
53
|
-
|
|
54
|
-
|
|
177
|
+
return arguments["result"].error
|
|
178
|
+
|
|
55
179
|
except (IndexError, AttributeError) as e:
|
|
56
|
-
logger.warning(
|
|
180
|
+
logger.warning(
|
|
181
|
+
"Warning: Error occurred in extract_assistant_message: %s", str(e)
|
|
182
|
+
)
|
|
57
183
|
return None
|
|
58
184
|
|
|
185
|
+
|
|
59
186
|
def extract_provider_name(instance):
|
|
60
187
|
provider_url: Option[str] = try_option(getattr, instance._client.base_url, 'host')
|
|
61
188
|
return provider_url.unwrap_or(None)
|
|
@@ -129,7 +256,7 @@ def get_inference_type(instance):
|
|
|
129
256
|
|
|
130
257
|
class OpenAISpanHandler(NonFrameworkSpanHandler):
|
|
131
258
|
def is_teams_span_in_progress(self) -> bool:
|
|
132
|
-
return self.
|
|
259
|
+
return self.is_framework_span_in_progress() and self.get_workflow_name_in_progress() == WORKFLOW_TYPE_MAP["teams.ai"]
|
|
133
260
|
|
|
134
261
|
# If openAI is being called by Teams AI SDK, then retain the metadata part of the span events
|
|
135
262
|
def skip_processor(self, to_wrap, wrapped, instance, span, args, kwargs) -> list[str]:
|
|
@@ -144,3 +271,48 @@ class OpenAISpanHandler(NonFrameworkSpanHandler):
|
|
|
144
271
|
return super().hydrate_events(to_wrap, wrapped, instance, args, kwargs, ret_result, span=parent_span, parent_span=None, ex=ex)
|
|
145
272
|
|
|
146
273
|
return super().hydrate_events(to_wrap, wrapped, instance, args, kwargs, ret_result, span, parent_span=parent_span, ex=ex)
|
|
274
|
+
|
|
275
|
+
def post_task_processing(self, to_wrap, wrapped, instance, args, kwargs, result, ex, span, parent_span):
|
|
276
|
+
# TeamsAI doesn't capture the status and other metadata from underlying OpenAI SDK.
|
|
277
|
+
# Thus we save the OpenAI status code in the parent span and retrieve it here to preserve meaningful error codes.
|
|
278
|
+
if self.is_teams_span_in_progress() and ex is not None:
|
|
279
|
+
if len(span.events) > 1 and span.events[1].name == "data.output" and span.events[1].attributes.get("error_code") is not None:
|
|
280
|
+
parent_span.set_attribute(CHILD_ERROR_CODE, span.events[1].attributes.get("error_code"))
|
|
281
|
+
super().post_task_processing(to_wrap, wrapped, instance, args, kwargs, result, ex, span, parent_span)
|
|
282
|
+
|
|
283
|
+
def extract_finish_reason(arguments):
|
|
284
|
+
"""Extract finish_reason from OpenAI response"""
|
|
285
|
+
try:
|
|
286
|
+
if arguments["exception"] is not None:
|
|
287
|
+
if hasattr(arguments["exception"], "code") and arguments["exception"].code in OPENAI_FINISH_REASON_MAPPING.keys():
|
|
288
|
+
return arguments["exception"].code
|
|
289
|
+
response = arguments["result"]
|
|
290
|
+
|
|
291
|
+
# Handle streaming responses
|
|
292
|
+
if hasattr(response, "finish_reason") and response.finish_reason:
|
|
293
|
+
return response.finish_reason
|
|
294
|
+
|
|
295
|
+
# Handle non-streaming responses
|
|
296
|
+
if response is not None and hasattr(response, "choices") and len(response.choices) > 0:
|
|
297
|
+
if hasattr(response.choices[0], "finish_reason"):
|
|
298
|
+
return response.choices[0].finish_reason
|
|
299
|
+
except (IndexError, AttributeError) as e:
|
|
300
|
+
logger.warning("Warning: Error occurred in extract_finish_reason: %s", str(e))
|
|
301
|
+
return None
|
|
302
|
+
return None
|
|
303
|
+
|
|
304
|
+
def map_finish_reason_to_finish_type(finish_reason):
|
|
305
|
+
"""Map OpenAI finish_reason to finish_type based on the possible errors mapping"""
|
|
306
|
+
return map_openai_finish_reason_to_finish_type(finish_reason)
|
|
307
|
+
|
|
308
|
+
def agent_inference_type(arguments):
|
|
309
|
+
"""Extract agent inference type from OpenAI response"""
|
|
310
|
+
message = json.loads(extract_assistant_message(arguments))
|
|
311
|
+
# message["tools"][0]["tool_name"]
|
|
312
|
+
if message and message.get("tools") and isinstance(message["tools"], list) and len(message["tools"]) > 0:
|
|
313
|
+
agent_prefix = get_value(AGENT_PREFIX_KEY)
|
|
314
|
+
tool_name = message["tools"][0].get("tool_name", "")
|
|
315
|
+
if tool_name and agent_prefix and tool_name.startswith(agent_prefix):
|
|
316
|
+
return INFERENCE_AGENT_DELEGATION
|
|
317
|
+
return INFERENCE_TOOL_CALL
|
|
318
|
+
return INFERENCE_COMMUNICATION
|