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.

Files changed (88) hide show
  1. monocle_apptrace/__main__.py +1 -1
  2. monocle_apptrace/exporters/file_exporter.py +125 -37
  3. monocle_apptrace/instrumentation/common/__init__.py +16 -1
  4. monocle_apptrace/instrumentation/common/constants.py +14 -1
  5. monocle_apptrace/instrumentation/common/instrumentor.py +19 -152
  6. monocle_apptrace/instrumentation/common/method_wrappers.py +376 -0
  7. monocle_apptrace/instrumentation/common/span_handler.py +58 -32
  8. monocle_apptrace/instrumentation/common/utils.py +52 -15
  9. monocle_apptrace/instrumentation/common/wrapper.py +124 -18
  10. monocle_apptrace/instrumentation/common/wrapper_method.py +47 -1
  11. monocle_apptrace/instrumentation/metamodel/a2a/__init__.py +0 -0
  12. monocle_apptrace/instrumentation/metamodel/a2a/_helper.py +37 -0
  13. monocle_apptrace/instrumentation/metamodel/a2a/entities/__init__.py +0 -0
  14. monocle_apptrace/instrumentation/metamodel/a2a/entities/inference.py +112 -0
  15. monocle_apptrace/instrumentation/metamodel/a2a/methods.py +22 -0
  16. monocle_apptrace/instrumentation/metamodel/adk/__init__.py +0 -0
  17. monocle_apptrace/instrumentation/metamodel/adk/_helper.py +182 -0
  18. monocle_apptrace/instrumentation/metamodel/adk/entities/agent.py +50 -0
  19. monocle_apptrace/instrumentation/metamodel/adk/entities/tool.py +57 -0
  20. monocle_apptrace/instrumentation/metamodel/adk/methods.py +24 -0
  21. monocle_apptrace/instrumentation/metamodel/agents/__init__.py +0 -0
  22. monocle_apptrace/instrumentation/metamodel/agents/_helper.py +220 -0
  23. monocle_apptrace/instrumentation/metamodel/agents/agents_processor.py +152 -0
  24. monocle_apptrace/instrumentation/metamodel/agents/entities/__init__.py +0 -0
  25. monocle_apptrace/instrumentation/metamodel/agents/entities/inference.py +191 -0
  26. monocle_apptrace/instrumentation/metamodel/agents/methods.py +56 -0
  27. monocle_apptrace/instrumentation/metamodel/aiohttp/_helper.py +6 -11
  28. monocle_apptrace/instrumentation/metamodel/anthropic/_helper.py +112 -18
  29. monocle_apptrace/instrumentation/metamodel/anthropic/entities/inference.py +18 -10
  30. monocle_apptrace/instrumentation/metamodel/azfunc/_helper.py +13 -11
  31. monocle_apptrace/instrumentation/metamodel/azfunc/entities/http.py +5 -0
  32. monocle_apptrace/instrumentation/metamodel/azureaiinference/_helper.py +88 -8
  33. monocle_apptrace/instrumentation/metamodel/azureaiinference/entities/inference.py +22 -8
  34. monocle_apptrace/instrumentation/metamodel/botocore/_helper.py +92 -16
  35. monocle_apptrace/instrumentation/metamodel/botocore/entities/inference.py +13 -8
  36. monocle_apptrace/instrumentation/metamodel/botocore/handlers/botocore_span_handler.py +1 -1
  37. monocle_apptrace/instrumentation/metamodel/fastapi/__init__.py +0 -0
  38. monocle_apptrace/instrumentation/metamodel/fastapi/_helper.py +82 -0
  39. monocle_apptrace/instrumentation/metamodel/fastapi/entities/__init__.py +0 -0
  40. monocle_apptrace/instrumentation/metamodel/fastapi/entities/http.py +44 -0
  41. monocle_apptrace/instrumentation/metamodel/fastapi/methods.py +23 -0
  42. monocle_apptrace/instrumentation/metamodel/finish_types.py +463 -0
  43. monocle_apptrace/instrumentation/metamodel/flask/_helper.py +6 -11
  44. monocle_apptrace/instrumentation/metamodel/gemini/_helper.py +51 -7
  45. monocle_apptrace/instrumentation/metamodel/gemini/entities/inference.py +22 -11
  46. monocle_apptrace/instrumentation/metamodel/gemini/entities/retrieval.py +43 -0
  47. monocle_apptrace/instrumentation/metamodel/gemini/methods.py +18 -1
  48. monocle_apptrace/instrumentation/metamodel/haystack/_helper.py +79 -8
  49. monocle_apptrace/instrumentation/metamodel/haystack/entities/inference.py +15 -10
  50. monocle_apptrace/instrumentation/metamodel/haystack/methods.py +7 -0
  51. monocle_apptrace/instrumentation/metamodel/lambdafunc/_helper.py +78 -0
  52. monocle_apptrace/instrumentation/metamodel/lambdafunc/entities/http.py +51 -0
  53. monocle_apptrace/instrumentation/metamodel/lambdafunc/methods.py +23 -0
  54. monocle_apptrace/instrumentation/metamodel/lambdafunc/wrapper.py +23 -0
  55. monocle_apptrace/instrumentation/metamodel/langchain/_helper.py +145 -19
  56. monocle_apptrace/instrumentation/metamodel/langchain/entities/inference.py +19 -10
  57. monocle_apptrace/instrumentation/metamodel/langgraph/_helper.py +67 -10
  58. monocle_apptrace/instrumentation/metamodel/langgraph/entities/inference.py +127 -20
  59. monocle_apptrace/instrumentation/metamodel/langgraph/langgraph_processor.py +46 -0
  60. monocle_apptrace/instrumentation/metamodel/langgraph/methods.py +35 -9
  61. monocle_apptrace/instrumentation/metamodel/litellm/__init__.py +0 -0
  62. monocle_apptrace/instrumentation/metamodel/litellm/_helper.py +89 -0
  63. monocle_apptrace/instrumentation/metamodel/litellm/entities/__init__.py +0 -0
  64. monocle_apptrace/instrumentation/metamodel/litellm/entities/inference.py +108 -0
  65. monocle_apptrace/instrumentation/metamodel/litellm/methods.py +19 -0
  66. monocle_apptrace/instrumentation/metamodel/llamaindex/_helper.py +227 -16
  67. monocle_apptrace/instrumentation/metamodel/llamaindex/entities/agent.py +127 -10
  68. monocle_apptrace/instrumentation/metamodel/llamaindex/entities/inference.py +13 -8
  69. monocle_apptrace/instrumentation/metamodel/llamaindex/llamaindex_processor.py +62 -0
  70. monocle_apptrace/instrumentation/metamodel/llamaindex/methods.py +68 -1
  71. monocle_apptrace/instrumentation/metamodel/mcp/__init__.py +0 -0
  72. monocle_apptrace/instrumentation/metamodel/mcp/_helper.py +118 -0
  73. monocle_apptrace/instrumentation/metamodel/mcp/entities/__init__.py +0 -0
  74. monocle_apptrace/instrumentation/metamodel/mcp/entities/inference.py +48 -0
  75. monocle_apptrace/instrumentation/metamodel/mcp/mcp_processor.py +8 -0
  76. monocle_apptrace/instrumentation/metamodel/mcp/methods.py +21 -0
  77. monocle_apptrace/instrumentation/metamodel/openai/_helper.py +188 -16
  78. monocle_apptrace/instrumentation/metamodel/openai/entities/inference.py +148 -92
  79. monocle_apptrace/instrumentation/metamodel/openai/entities/retrieval.py +1 -1
  80. monocle_apptrace/instrumentation/metamodel/teamsai/_helper.py +53 -23
  81. monocle_apptrace/instrumentation/metamodel/teamsai/entities/inference/actionplanner_output_processor.py +1 -1
  82. monocle_apptrace/instrumentation/metamodel/teamsai/entities/inference/teamsai_output_processor.py +15 -9
  83. monocle_apptrace/instrumentation/metamodel/teamsai/sample.json +0 -4
  84. {monocle_apptrace-0.4.2.dist-info → monocle_apptrace-0.5.0.dist-info}/METADATA +27 -11
  85. {monocle_apptrace-0.4.2.dist-info → monocle_apptrace-0.5.0.dist-info}/RECORD +88 -47
  86. {monocle_apptrace-0.4.2.dist-info → monocle_apptrace-0.5.0.dist-info}/WHEEL +0 -0
  87. {monocle_apptrace-0.4.2.dist-info → monocle_apptrace-0.5.0.dist-info}/licenses/LICENSE +0 -0
  88. {monocle_apptrace-0.4.2.dist-info → monocle_apptrace-0.5.0.dist-info}/licenses/NOTICE +0 -0
@@ -0,0 +1,191 @@
1
+ from monocle_apptrace.instrumentation.metamodel.agents import _helper
2
+
3
+ AGENT = {
4
+ "type": "agentic.invocation",
5
+ "attributes": [
6
+ [
7
+ {
8
+ "_comment": "agent type",
9
+ "attribute": "type",
10
+ "accessor": lambda arguments: _helper.AGENTS_AGENT_NAME_KEY,
11
+ },
12
+ {
13
+ "_comment": "name of the agent",
14
+ "attribute": "name",
15
+ "accessor": lambda arguments: _helper.get_agent_name(arguments),
16
+ },
17
+ {
18
+ "_comment": "agent description",
19
+ "attribute": "description",
20
+ "accessor": lambda arguments: _helper.get_agent_description(arguments),
21
+ },
22
+ {
23
+ "_comment": "agent instructions",
24
+ "attribute": "instructions",
25
+ "accessor": lambda arguments: _helper.get_agent_instructions(arguments),
26
+ },
27
+ ]
28
+ ],
29
+ "events": [
30
+ {
31
+ "name": "data.input",
32
+ "attributes": [
33
+ {
34
+ "_comment": "this is Agent input",
35
+ "attribute": "query",
36
+ "accessor": lambda arguments: _helper.extract_agent_input(
37
+ arguments
38
+ ),
39
+ }
40
+ ],
41
+ },
42
+ {
43
+ "name": "data.output",
44
+ "attributes": [
45
+ {
46
+ "_comment": "this is response from Agent",
47
+ "attribute": "response",
48
+ "accessor": lambda arguments: _helper.extract_agent_response(
49
+ arguments["result"]
50
+ ),
51
+ }
52
+ ],
53
+ },
54
+ {
55
+ "name": "metadata",
56
+ "attributes": [
57
+ {
58
+ "_comment": "this is metadata from Agent response",
59
+ "accessor": lambda arguments: _helper.update_span_from_agent_response(
60
+ arguments["result"]
61
+ ),
62
+ }
63
+ ],
64
+ },
65
+ ],
66
+ }
67
+
68
+ AGENT_REQUEST = {
69
+ "type": "agentic.request",
70
+ "attributes": [
71
+ [
72
+ {
73
+ "_comment": "agent type",
74
+ "attribute": "type",
75
+ "accessor": lambda arguments: _helper.AGENTS_AGENT_NAME_KEY,
76
+ }
77
+ ],
78
+ ],
79
+ "events": [
80
+ {
81
+ "name": "data.input",
82
+ "attributes": [
83
+ {
84
+ "_comment": "this is Agent input",
85
+ "attribute": "input",
86
+ "accessor": lambda arguments: _helper.extract_agent_input(
87
+ arguments
88
+ ),
89
+ }
90
+ ],
91
+ },
92
+ {
93
+ "name": "data.output",
94
+ "attributes": [
95
+ {
96
+ "_comment": "this is response from Agent",
97
+ "attribute": "response",
98
+ "accessor": lambda arguments: _helper.extract_agent_response(
99
+ arguments["result"]
100
+ ),
101
+ }
102
+ ],
103
+ },
104
+ ],
105
+ }
106
+
107
+ TOOLS = {
108
+ "type": "agentic.tool.invocation",
109
+ "attributes": [
110
+ [
111
+ {
112
+ "_comment": "tool type",
113
+ "attribute": "type",
114
+ "accessor": lambda arguments: "tool.openai_agents",
115
+ },
116
+ {
117
+ "_comment": "name of the tool",
118
+ "attribute": "name",
119
+ "accessor": lambda arguments: _helper.get_tool_name(
120
+ arguments["instance"]
121
+ ),
122
+ },
123
+ {
124
+ "_comment": "tool description",
125
+ "attribute": "description",
126
+ "accessor": lambda arguments: _helper.get_tool_description(
127
+ arguments["instance"]
128
+ ),
129
+ },
130
+ ],
131
+ [
132
+ {
133
+ "_comment": "name of the agent",
134
+ "attribute": "name",
135
+ "accessor": lambda arguments: _helper.get_source_agent(),
136
+ },
137
+ {
138
+ "_comment": "agent type",
139
+ "attribute": "type",
140
+ "accessor": lambda arguments: _helper.AGENTS_AGENT_NAME_KEY,
141
+ },
142
+ ],
143
+ ],
144
+ "events": [
145
+ {
146
+ "name": "data.input",
147
+ "attributes": [
148
+ {
149
+ "_comment": "this is Tool input",
150
+ "attribute": "Inputs",
151
+ "accessor": lambda arguments: _helper.extract_tool_input(arguments),
152
+ },
153
+ ],
154
+ },
155
+ {
156
+ "name": "data.output",
157
+ "attributes": [
158
+ {
159
+ "_comment": "this is response from Tool",
160
+ "attribute": "response",
161
+ "accessor": lambda arguments: _helper.extract_tool_response(
162
+ arguments["result"]
163
+ ),
164
+ }
165
+ ],
166
+ },
167
+ ],
168
+ }
169
+
170
+ AGENT_DELEGATION = {
171
+ "type": "agentic.delegation",
172
+ "attributes": [
173
+ [
174
+ {
175
+ "_comment": "agent type",
176
+ "attribute": "type",
177
+ "accessor": lambda arguments: _helper.AGENTS_AGENT_NAME_KEY,
178
+ },
179
+ {
180
+ "_comment": "name of the source agent",
181
+ "attribute": "from_agent",
182
+ "accessor": lambda arguments: _helper.get_source_agent(),
183
+ },
184
+ {
185
+ "_comment": "name of the target agent",
186
+ "attribute": "to_agent",
187
+ "accessor": lambda arguments: _helper.extract_handoff_target(arguments),
188
+ },
189
+ ]
190
+ ],
191
+ }
@@ -0,0 +1,56 @@
1
+ from monocle_apptrace.instrumentation.common.wrapper import task_wrapper, atask_wrapper
2
+ from monocle_apptrace.instrumentation.metamodel.agents.entities.inference import (
3
+ AGENT,
4
+ AGENT_DELEGATION,
5
+ TOOLS,
6
+ )
7
+ from monocle_apptrace.instrumentation.metamodel.agents.agents_processor import (
8
+ constructor_wrapper,
9
+ handoff_constructor_wrapper,
10
+ )
11
+
12
+ AGENTS_METHODS = [
13
+ # Main agent runner methods
14
+ {
15
+ "package": "agents.run",
16
+ "object": "Runner",
17
+ "method": "run",
18
+ "wrapper_method": atask_wrapper,
19
+ "span_handler": "agents_agent_handler",
20
+ "output_processor": AGENT,
21
+ },
22
+ {
23
+ "package": "agents.run",
24
+ "object": "Runner",
25
+ "method": "run_sync",
26
+ "wrapper_method": task_wrapper,
27
+ "span_handler": "agents_agent_handler",
28
+ "output_processor": AGENT,
29
+ },
30
+ # AgentRunner class methods (internal runner)
31
+ {
32
+ "package": "agents.run",
33
+ "object": "AgentRunner",
34
+ "method": "_run_single_turn",
35
+ "wrapper_method": atask_wrapper,
36
+ "span_handler": "agents_agent_handler",
37
+ "output_processor": AGENT,
38
+ },
39
+ # Function tool decorator - wrap the function_tool function directly
40
+ {
41
+ "package": "agents.tool",
42
+ "object": "FunctionTool",
43
+ "method": "__init__", # Empty string means wrap the function itself
44
+ "wrapper_method": constructor_wrapper,
45
+ "span_handler": "agents_tool_handler",
46
+ "output_processor": TOOLS,
47
+ },
48
+ {
49
+ "package": "agents.handoffs",
50
+ "object": "Handoff",
51
+ "method": "__init__", # Empty string means wrap the function itself
52
+ "wrapper_method": handoff_constructor_wrapper,
53
+ "span_handler": "agents_tool_handler",
54
+ "output_processor": AGENT_DELEGATION,
55
+ },
56
+ ]
@@ -7,8 +7,6 @@ from urllib.parse import unquote
7
7
 
8
8
  logger = logging.getLogger(__name__)
9
9
  MAX_DATA_LENGTH = 1000
10
- token_data = local()
11
- token_data.current_token = None
12
10
 
13
11
  def get_route(args) -> str:
14
12
  route_path: Option[str] = try_option(getattr, args[0], 'path')
@@ -41,11 +39,10 @@ def extract_status(result) -> str:
41
39
  return status
42
40
 
43
41
  def aiohttp_pre_tracing(args):
44
- token_data.current_token = extract_http_headers(args[0].headers)
42
+ return extract_http_headers(args[0].headers)
45
43
 
46
- def aiohttp_post_tracing():
47
- clear_http_scopes(token_data.current_token)
48
- token_data.current_token = None
44
+ def aiohttp_post_tracing(token):
45
+ clear_http_scopes(token)
49
46
 
50
47
  def aiohttp_skip_span(args) -> bool:
51
48
  if get_method(args) == "HEAD":
@@ -55,12 +52,10 @@ def aiohttp_skip_span(args) -> bool:
55
52
  class aiohttpSpanHandler(SpanHandler):
56
53
 
57
54
  def pre_tracing(self, to_wrap, wrapped, instance, args, kwargs):
58
- aiohttp_pre_tracing(args)
59
- return super().pre_tracing(to_wrap, wrapped, instance, args, kwargs)
55
+ return aiohttp_pre_tracing(args)
60
56
 
61
- def post_tracing(self, to_wrap, wrapped, instance, args, kwargs, return_value):
62
- aiohttp_post_tracing()
63
- return super().post_tracing(to_wrap, wrapped, instance, args, kwargs, return_value)
57
+ def post_tracing(self, to_wrap, wrapped, instance, args, kwargs, return_value, token):
58
+ aiohttp_post_tracing(token)
64
59
 
65
60
  def skip_span(self, to_wrap, wrapped, instance, args, kwargs) -> bool:
66
61
  return aiohttp_skip_span(args)
@@ -3,14 +3,20 @@ 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
  get_keys_as_tuple,
10
13
  get_nested_value,
14
+ get_status_code,
11
15
  try_option,
12
16
  get_exception_message,
13
17
  )
18
+ from monocle_apptrace.instrumentation.metamodel.finish_types import map_anthropic_finish_reason_to_finish_type
19
+ from monocle_apptrace.instrumentation.common.constants import AGENT_PREFIX_KEY, INFERENCE_AGENT_DELEGATION, INFERENCE_COMMUNICATION, INFERENCE_TOOL_CALL
14
20
 
15
21
 
16
22
  logger = logging.getLogger(__name__)
@@ -30,12 +36,13 @@ def extract_messages(kwargs):
30
36
  """Extract system and user messages"""
31
37
  try:
32
38
  messages = []
39
+ if "system" in kwargs and isinstance(kwargs["system"], str):
40
+ messages.append({"system": kwargs["system"]})
33
41
  if 'messages' in kwargs and len(kwargs['messages']) >0:
34
42
  for msg in kwargs['messages']:
35
43
  if msg.get('content') and msg.get('role'):
36
44
  messages.append({msg['role']: msg['content']})
37
-
38
- return [str(message) for message in messages]
45
+ return [get_json_dumps(message) for message in messages]
39
46
  except Exception as e:
40
47
  logger.warning("Warning: Error occurred in extract_messages: %s", str(e))
41
48
  return []
@@ -48,28 +55,55 @@ def get_exception_status_code(arguments):
48
55
  else:
49
56
  return 'success'
50
57
 
51
- def get_status_code(arguments):
52
- if arguments["exception"] is not None:
53
- return get_exception_status_code(arguments)
54
- elif hasattr(arguments["result"], "status"):
55
- return arguments["result"].status
56
- else:
57
- return 'success'
58
-
59
58
  def extract_assistant_message(arguments):
60
59
  try:
61
60
  status = get_status_code(arguments)
62
- response: str = ""
61
+ response = arguments["result"]
63
62
  if status == 'success':
64
- if arguments['result'] is not None and hasattr(arguments['result'],"content") and len(arguments['result'].content) >0:
65
- if hasattr(arguments['result'].content[0],"text"):
66
- response = arguments['result'].content[0].text
63
+ messages = []
64
+ role = response.role if hasattr(response, 'role') else "assistant"
65
+
66
+ # Handle tool use content blocks
67
+ if hasattr(response, "content") and response.content:
68
+ tools = []
69
+ text_content = []
70
+
71
+ for content_block in response.content:
72
+ if hasattr(content_block, "type"):
73
+ if content_block.type == "tool_use":
74
+ # Extract tool use information
75
+ tool_info = {
76
+ "tool_id": getattr(content_block, "id", ""),
77
+ "tool_name": getattr(content_block, "name", ""),
78
+ "tool_arguments": getattr(content_block, "input", "")
79
+ }
80
+ tools.append(tool_info)
81
+ elif content_block.type == "text":
82
+ # Extract text content
83
+ if hasattr(content_block, "text"):
84
+ text_content.append(content_block.text)
85
+
86
+ # If we have tools, add them to the message
87
+ if tools:
88
+ messages.append({"tools": tools})
89
+
90
+ # If we have text content, add it to the message
91
+ if text_content:
92
+ messages.append({role: " ".join(text_content)})
93
+
94
+ # Fallback to original logic if no content blocks were processed
95
+ if not messages and len(response.content) > 0:
96
+ if hasattr(response.content[0], "text"):
97
+ messages.append({role: response.content[0].text})
98
+
99
+ # Return first message if list is not empty
100
+ return get_json_dumps(messages[0]) if messages else ""
67
101
  else:
68
102
  if arguments["exception"] is not None:
69
- response = get_exception_message(arguments)
103
+ return get_exception_message(arguments)
70
104
  elif hasattr(arguments["result"], "error"):
71
- response = arguments["result"].error
72
- return response
105
+ return arguments["result"].error
106
+
73
107
  except (IndexError, AttributeError) as e:
74
108
  logger.warning("Warning: Error occurred in extract_assistant_message: %s", str(e))
75
109
  return None
@@ -86,4 +120,64 @@ def update_span_from_llm_response(response):
86
120
  meta_dict.update({"completion_tokens": getattr(response.usage, "output_tokens", 0)})
87
121
  meta_dict.update({"prompt_tokens": getattr(response.usage, "input_tokens", 0)})
88
122
  meta_dict.update({"total_tokens": getattr(response.usage, "input_tokens", 0)+getattr(response.usage, "output_tokens", 0)})
89
- return meta_dict
123
+ return meta_dict
124
+
125
+ def extract_finish_reason(arguments):
126
+ """Extract stop_reason from Anthropic response (Claude)."""
127
+ try:
128
+ # Arguments may be a dict with 'result' or just the response object
129
+ response = arguments.get("result") if isinstance(arguments, dict) else arguments
130
+ if response is not None and hasattr(response, "stop_reason"):
131
+ return response.stop_reason
132
+ except Exception as e:
133
+ logger.warning("Warning: Error occurred in extract_finish_reason: %s", str(e))
134
+ return None
135
+ return None
136
+
137
+ def map_finish_reason_to_finish_type(finish_reason):
138
+ """Map Anthropic stop_reason to finish_type, similar to OpenAI mapping."""
139
+ return map_anthropic_finish_reason_to_finish_type(finish_reason)
140
+
141
+ def agent_inference_type(arguments):
142
+ """Extract agent inference type from Anthropic response"""
143
+ try:
144
+ status = get_status_code(arguments)
145
+ if status == 'success' or status == 'completed':
146
+ response = arguments["result"]
147
+
148
+ # Check if stop_reason indicates tool use
149
+ if hasattr(response, "stop_reason") and response.stop_reason == "tool_use":
150
+ # Check if this is agent delegation by looking at tool names
151
+ if hasattr(response, "content") and response.content:
152
+ agent_prefix = get_value(AGENT_PREFIX_KEY)
153
+ for content_block in response.content:
154
+ if (hasattr(content_block, "type") and
155
+ content_block.type == "tool_use" and
156
+ hasattr(content_block, "name")):
157
+ tool_name = content_block.name
158
+ if agent_prefix and tool_name.startswith(agent_prefix):
159
+ return INFERENCE_AGENT_DELEGATION
160
+ # If we found tool use but no agent delegation, it's a regular tool call
161
+ return INFERENCE_TOOL_CALL
162
+
163
+ # Fallback: check the extracted message for tool content
164
+ assistant_message = extract_assistant_message(arguments)
165
+ if assistant_message:
166
+ try:
167
+ message = json.loads(assistant_message)
168
+ if message and isinstance(message, dict):
169
+ assistant_content = message.get("assistant", "")
170
+ if assistant_content:
171
+ agent_prefix = get_value(AGENT_PREFIX_KEY)
172
+ if agent_prefix and agent_prefix in assistant_content:
173
+ return INFERENCE_AGENT_DELEGATION
174
+ except (json.JSONDecodeError, TypeError):
175
+ # If JSON parsing fails, fall back to string analysis
176
+ agent_prefix = get_value(AGENT_PREFIX_KEY)
177
+ if agent_prefix and agent_prefix in assistant_message:
178
+ return INFERENCE_AGENT_DELEGATION
179
+
180
+ return INFERENCE_COMMUNICATION
181
+ except Exception as e:
182
+ logger.warning("Warning: Error occurred in agent_inference_type: %s", str(e))
183
+ return INFERENCE_COMMUNICATION
@@ -1,9 +1,7 @@
1
1
  from monocle_apptrace.instrumentation.metamodel.anthropic import (
2
2
  _helper,
3
3
  )
4
- from monocle_apptrace.instrumentation.common.utils import (resolve_from_alias, get_llm_type,
5
- get_status, get_status_code
6
- )
4
+ from monocle_apptrace.instrumentation.common.utils import (get_error_message, resolve_from_alias)
7
5
 
8
6
  INFERENCE = {
9
7
  "type": "inference",
@@ -12,7 +10,7 @@ INFERENCE = {
12
10
  {
13
11
  "_comment": "provider type ,name , deployment , inference_endpoint",
14
12
  "attribute": "type",
15
- "accessor": lambda arguments: 'inference.' + (get_llm_type(arguments['instance']) or 'generic')
13
+ "accessor": lambda arguments: 'inference.anthropic'
16
14
 
17
15
  },
18
16
  {
@@ -55,12 +53,8 @@ INFERENCE = {
55
53
  "name": "data.output",
56
54
  "attributes": [
57
55
  {
58
- "attribute": "status",
59
- "accessor": lambda arguments: get_status(arguments)
60
- },
61
- {
62
- "attribute": "status_code",
63
- "accessor": lambda arguments: _helper.get_status_code(arguments)
56
+ "attribute": "error_code",
57
+ "accessor": lambda arguments: get_error_message(arguments)
64
58
  },
65
59
  {
66
60
  "_comment": "this is result from LLM",
@@ -75,6 +69,20 @@ INFERENCE = {
75
69
  {
76
70
  "_comment": "this is metadata usage from LLM",
77
71
  "accessor": lambda arguments: _helper.update_span_from_llm_response(arguments['result'])
72
+ },
73
+ {
74
+ "_comment": "finish reason from Anthropic response",
75
+ "attribute": "finish_reason",
76
+ "accessor": lambda arguments: _helper.extract_finish_reason(arguments)
77
+ },
78
+ {
79
+ "_comment": "finish type mapped from finish reason",
80
+ "attribute": "finish_type",
81
+ "accessor": lambda arguments: _helper.map_finish_reason_to_finish_type(_helper.extract_finish_reason(arguments))
82
+ },
83
+ {
84
+ "attribute": "inference_sub_type",
85
+ "accessor": lambda arguments: _helper.agent_inference_type(arguments)
78
86
  }
79
87
  ]
80
88
  }
@@ -8,8 +8,6 @@ from urllib.parse import unquote, urlparse, ParseResult
8
8
 
9
9
  logger = logging.getLogger(__name__)
10
10
  MAX_DATA_LENGTH = 1000
11
- token_data = local()
12
- token_data.current_token = None
13
11
 
14
12
  def get_url(kwargs) -> ParseResult:
15
13
  url_str = try_option(getattr, kwargs['req'], 'url')
@@ -19,6 +17,13 @@ def get_url(kwargs) -> ParseResult:
19
17
  else:
20
18
  return None
21
19
 
20
+ def get_function_name(kwargs) -> str:
21
+ context = kwargs.get('context', None)
22
+ if context is not None and hasattr(context, 'function_name'):
23
+ return context.function_name
24
+ return ""
25
+
26
+
22
27
  def get_route(kwargs) -> str:
23
28
  url:ParseResult = get_url(kwargs)
24
29
  if url is not None:
@@ -61,18 +66,15 @@ def extract_status(result) -> str:
61
66
 
62
67
  def azure_func_pre_tracing(kwargs):
63
68
  headers = kwargs['req'].headers if hasattr(kwargs['req'], 'headers') else {}
64
- token_data.current_token = extract_http_headers(headers)
69
+ return extract_http_headers(headers)
65
70
 
66
- def azure_func_post_tracing():
67
- clear_http_scopes(token_data.current_token)
68
- token_data.current_token = None
71
+ def azure_func_post_tracing(token):
72
+ clear_http_scopes(token)
69
73
 
70
74
  class azureSpanHandler(SpanHandler):
71
75
 
72
76
  def pre_tracing(self, to_wrap, wrapped, instance, args, kwargs):
73
- azure_func_pre_tracing(kwargs)
74
- return super().pre_tracing(to_wrap, wrapped, instance, args, kwargs)
77
+ return azure_func_pre_tracing(kwargs)
75
78
 
76
- def post_tracing(self, to_wrap, wrapped, instance, args, kwargs, return_value):
77
- azure_func_post_tracing()
78
- return super().post_tracing(to_wrap, wrapped, instance, args, kwargs, return_value)
79
+ def post_tracing(self, to_wrap, wrapped, instance, args, kwargs, return_value, token):
80
+ azure_func_post_tracing(token)
@@ -18,6 +18,11 @@ AZFUNC_HTTP_PROCESSOR = {
18
18
  "attribute": "body",
19
19
  "accessor": lambda arguments: _helper.get_body(arguments['kwargs'])
20
20
  },
21
+ {
22
+ "_comment": "request function name",
23
+ "attribute": "function_name",
24
+ "accessor": lambda arguments: _helper.get_function_name(arguments['kwargs'])
25
+ }
21
26
  ]
22
27
  ],
23
28
  "events": [