monocle-apptrace 0.5.3__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.

Files changed (29) hide show
  1. monocle_apptrace/exporters/file_exporter.py +7 -1
  2. monocle_apptrace/instrumentation/common/instrumentor.py +1 -1
  3. monocle_apptrace/instrumentation/common/span_handler.py +2 -1
  4. monocle_apptrace/instrumentation/common/wrapper_method.py +3 -1
  5. monocle_apptrace/instrumentation/metamodel/adk/_helper.py +6 -4
  6. monocle_apptrace/instrumentation/metamodel/adk/entities/agent.py +6 -1
  7. monocle_apptrace/instrumentation/metamodel/agents/_helper.py +5 -5
  8. monocle_apptrace/instrumentation/metamodel/agents/entities/inference.py +7 -2
  9. monocle_apptrace/instrumentation/metamodel/finish_types.py +32 -1
  10. monocle_apptrace/instrumentation/metamodel/hugging_face/__init__.py +0 -0
  11. monocle_apptrace/instrumentation/metamodel/hugging_face/_helper.py +138 -0
  12. monocle_apptrace/instrumentation/metamodel/hugging_face/entities/__init__.py +0 -0
  13. monocle_apptrace/instrumentation/metamodel/hugging_face/entities/inference.py +97 -0
  14. monocle_apptrace/instrumentation/metamodel/hugging_face/methods.py +23 -0
  15. monocle_apptrace/instrumentation/metamodel/langgraph/_helper.py +4 -2
  16. monocle_apptrace/instrumentation/metamodel/langgraph/entities/inference.py +7 -2
  17. monocle_apptrace/instrumentation/metamodel/mcp/_helper.py +6 -5
  18. monocle_apptrace/instrumentation/metamodel/mistral/_helper.py +98 -49
  19. monocle_apptrace/instrumentation/metamodel/mistral/entities/inference.py +14 -5
  20. monocle_apptrace/instrumentation/metamodel/mistral/entities/retrieval.py +41 -0
  21. monocle_apptrace/instrumentation/metamodel/mistral/methods.py +17 -0
  22. {monocle_apptrace-0.5.3.dist-info → monocle_apptrace-0.6.0.dist-info}/METADATA +9 -76
  23. {monocle_apptrace-0.5.3.dist-info → monocle_apptrace-0.6.0.dist-info}/RECORD +26 -23
  24. monocle_apptrace/README.md +0 -101
  25. monocle_apptrace/mcp_server.py +0 -94
  26. monocle_apptrace-0.5.3.dist-info/licenses/NOTICE +0 -4
  27. {monocle_apptrace-0.5.3.dist-info → monocle_apptrace-0.6.0.dist-info}/WHEEL +0 -0
  28. {monocle_apptrace-0.5.3.dist-info → monocle_apptrace-0.6.0.dist-info}/entry_points.txt +0 -0
  29. {monocle_apptrace-0.5.3.dist-info → monocle_apptrace-0.6.0.dist-info}/licenses/LICENSE +0 -0
@@ -55,6 +55,9 @@ class FileSpanExporter(SpanExporterBase):
55
55
  else:
56
56
  return self._process_spans(spans, is_root_span=is_root_span)
57
57
 
58
+ def set_service_name(self, service_name: str) -> None:
59
+ self.service_name = service_name
60
+
58
61
  def _cleanup_expired_handles(self) -> None:
59
62
  """Close and remove file handles that have exceeded the timeout."""
60
63
  current_time = datetime.now()
@@ -130,7 +133,10 @@ class FileSpanExporter(SpanExporterBase):
130
133
 
131
134
  # Process spans for each trace
132
135
  for trace_id, trace_spans in spans_by_trace.items():
133
- service_name = trace_spans[0].resource.attributes.get(SERVICE_NAME, "unknown")
136
+ if self.service_name is not None:
137
+ service_name = self.service_name
138
+ else:
139
+ service_name = trace_spans[0].resource.attributes.get(SERVICE_NAME, "unknown")
134
140
  handle, file_path, is_first_span = self._get_or_create_handle(trace_id, service_name)
135
141
 
136
142
  if handle is None:
@@ -165,7 +165,7 @@ def setup_monocle_telemetry(
165
165
  span_handlers: Dict[str,SpanHandler] = None,
166
166
  wrapper_methods: List[Union[dict,WrapperMethod]] = None,
167
167
  union_with_default_methods: bool = True,
168
- monocle_exporters_list:str = None) -> None:
168
+ monocle_exporters_list:str = None) -> MonocleInstrumentor:
169
169
  """
170
170
  Set up Monocle telemetry for the application.
171
171
 
@@ -26,7 +26,8 @@ WORKFLOW_TYPE_MAP = {
26
26
  "anthropic": "workflow.anthropic",
27
27
  "gemini": "workflow.gemini",
28
28
  "litellm": "workflow.litellm",
29
- "mistralai": "workflow.mistral"
29
+ "mistralai": "workflow.mistral",
30
+ "huggingface_hub": "workflow.huggingface"
30
31
  }
31
32
 
32
33
  FRAMEWORK_WORKFLOW_LIST = [
@@ -5,6 +5,7 @@ from monocle_apptrace.instrumentation.common.span_handler import SpanHandler, No
5
5
  from monocle_apptrace.instrumentation.metamodel.azureaiinference.methods import AZURE_AI_INFERENCE_METHODS
6
6
  from monocle_apptrace.instrumentation.metamodel.botocore.methods import BOTOCORE_METHODS
7
7
  from monocle_apptrace.instrumentation.metamodel.botocore.handlers.botocore_span_handler import BotoCoreSpanHandler
8
+ from monocle_apptrace.instrumentation.metamodel.hugging_face.methods import HUGGING_FACE_METHODS
8
9
  from monocle_apptrace.instrumentation.metamodel.langchain.methods import (
9
10
  LANGCHAIN_METHODS,
10
11
  )
@@ -109,7 +110,8 @@ DEFAULT_METHODS_LIST = (
109
110
  A2A_CLIENT_METHODS +
110
111
  LITELLM_METHODS +
111
112
  ADK_METHODS +
112
- MISTRAL_METHODS
113
+ MISTRAL_METHODS +
114
+ HUGGING_FACE_METHODS
113
115
  )
114
116
 
115
117
  MONOCLE_SPAN_HANDLERS: Dict[str, SpanHandler] = {
@@ -82,7 +82,7 @@ def extract_agent_input(arguments: Dict[str, Any]) -> Any:
82
82
  Returns:
83
83
  Any: The extracted input data
84
84
  """
85
- return arguments['args'][0].user_content.parts[0].text
85
+ return [arguments['args'][0].user_content.parts[0].text]
86
86
 
87
87
  def extract_agent_request_input(arguments: Dict[str, Any]) -> Any:
88
88
  """
@@ -94,7 +94,7 @@ def extract_agent_request_input(arguments: Dict[str, Any]) -> Any:
94
94
  Returns:
95
95
  Any: The extracted input data
96
96
  """
97
- return arguments['kwargs']['new_message'].parts[0].text if 'new_message' in arguments['kwargs'] else None
97
+ return [arguments['kwargs']['new_message'].parts[0].text] if 'new_message' in arguments['kwargs'] else []
98
98
 
99
99
  def extract_agent_response(result: Any) -> Any:
100
100
  """
@@ -107,7 +107,9 @@ def extract_agent_response(result: Any) -> Any:
107
107
  Any: The extracted response data
108
108
  """
109
109
  if result:
110
- return result.content.parts[0].text
110
+ return str(result.content.parts[0].text)
111
+ else:
112
+ return ""
111
113
 
112
114
  def get_tool_name(instance: Any) -> str:
113
115
  """
@@ -179,7 +181,7 @@ def extract_tool_input(arguments: Dict[str, Any]) -> Any:
179
181
  Returns:
180
182
  Any: The extracted input data
181
183
  """
182
- return str(arguments['kwargs'].get('args'))
184
+ return [str(arguments['kwargs'].get('args'))]
183
185
 
184
186
  def extract_tool_response(result: Any) -> Any:
185
187
  """
@@ -1,6 +1,7 @@
1
1
  from monocle_apptrace.instrumentation.common.constants import SPAN_SUBTYPES, SPAN_TYPES
2
2
  from monocle_apptrace.instrumentation.common.utils import get_error_message
3
3
  from monocle_apptrace.instrumentation.metamodel.adk import _helper
4
+ from monocle_apptrace.instrumentation.common.utils import get_error_message
4
5
  AGENT = {
5
6
  "type": SPAN_TYPES.AGENTIC_INVOCATION,
6
7
  "subtype": SPAN_SUBTYPES.ROUTING,
@@ -34,7 +35,7 @@ AGENT = {
34
35
  "attributes": [
35
36
  {
36
37
  "_comment": "this is Agent input",
37
- "attribute": "query",
38
+ "attribute": "input",
38
39
  "accessor": lambda arguments: _helper.extract_agent_input(arguments)
39
40
  }
40
41
  ]
@@ -42,6 +43,10 @@ AGENT = {
42
43
  {
43
44
  "name":"data.output",
44
45
  "attributes": [
46
+ {
47
+ "attribute": "error_code",
48
+ "accessor": lambda arguments: get_error_message(arguments)
49
+ },
45
50
  {
46
51
  "_comment": "this is response from LLM",
47
52
  "attribute": "response",
@@ -50,21 +50,21 @@ def extract_agent_input(arguments):
50
50
  if len(arguments["args"]) > 1:
51
51
  input_data = arguments["args"][1]
52
52
  if isinstance(input_data, str):
53
- return input_data
53
+ return [input_data]
54
54
  elif isinstance(input_data, list):
55
55
  # Handle list of input items
56
- return get_json_dumps(input_data)
56
+ return input_data
57
57
 
58
58
  # Fallback to kwargs
59
59
  if "original_input" in arguments["kwargs"]:
60
60
  input_data = arguments["kwargs"]["original_input"]
61
61
  if isinstance(input_data, str):
62
- return input_data
62
+ return [input_data]
63
63
  elif isinstance(input_data, list):
64
- return get_json_dumps(input_data)
64
+ return input_data
65
65
  except Exception as e:
66
66
  logger.warning("Warning: Error occurred in extract_agent_input: %s", str(e))
67
- return None
67
+ return []
68
68
 
69
69
 
70
70
  def get_agent_name(arguments) -> str:
@@ -1,6 +1,7 @@
1
1
  from monocle_apptrace.instrumentation.common.constants import AGENT_REQUEST_SPAN_NAME, SPAN_SUBTYPES, SPAN_TYPES
2
2
  from monocle_apptrace.instrumentation.common.utils import get_error_message
3
3
  from monocle_apptrace.instrumentation.metamodel.agents import _helper
4
+ from monocle_apptrace.instrumentation.common.utils import get_error_message
4
5
 
5
6
  AGENT = {
6
7
  "type": SPAN_TYPES.AGENTIC_INVOCATION,
@@ -35,7 +36,7 @@ AGENT = {
35
36
  "attributes": [
36
37
  {
37
38
  "_comment": "this is Agent input",
38
- "attribute": "query",
39
+ "attribute": "input",
39
40
  "accessor": lambda arguments: _helper.extract_agent_input(
40
41
  arguments
41
42
  ),
@@ -45,6 +46,10 @@ AGENT = {
45
46
  {
46
47
  "name": "data.output",
47
48
  "attributes": [
49
+ {
50
+ "attribute": "error_code",
51
+ "accessor": lambda arguments: get_error_message(arguments)
52
+ },
48
53
  {
49
54
  "_comment": "this is response from Agent",
50
55
  "attribute": "response",
@@ -73,7 +78,7 @@ AGENT = {
73
78
  }
74
79
 
75
80
  AGENT_REQUEST = {
76
- "type": AGENT_REQUEST_SPAN_NAME,
81
+ "type": SPAN_TYPES.AGENTIC_REQUEST,
77
82
  "subtype": SPAN_SUBTYPES.PLANNING,
78
83
  "attributes": [
79
84
  [
@@ -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)
@@ -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
+
@@ -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
+ ]
@@ -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
- return message.content
31
- return None
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": "query",
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": AGENT_REQUEST_SPAN_NAME,
58
+ "type": SPAN_TYPES.AGENTIC_REQUEST,
54
59
  "subtype": SPAN_SUBTYPES.PLANNING,
55
60
  "attributes": [
56
61
  [
@@ -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
- args
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):