Skip to content

Commit fbbedf4

Browse files
committed
feat: improve OpenTelemetry semantic convention attribute coverage
Add missing GenAI semantic convention attributes across instrumentation plugins: - execute_tool spans: add tool_type="function" (Recommended) - invoke_agent spans: add agent_id (Conditionally Required) - inference spans: add server_address/server_port (Conditionally Required) - inference spans: add response_id extraction (Recommended) Plugins modified: - hermes-agent: agent_id, server_address/port from base_url, tool_type - vita: agent_id, tool_type, response_id, server_address inference - langchain: agent_id, tool_type - dashscope: server_address/port - widesearch: agent_id - agentscope: tool_type - qwen-agent: agent_id, tool_type - minisweagent: agent_id - google-adk: agent_id, tool_type - claude-agent-sdk: tool_type Change-Id: I21fc87489630d2ac6a0d9df22ef70dde60c57fff Co-developed-by: Qoder <noreply@qoder.com>
1 parent eeac763 commit fbbedf4

12 files changed

Lines changed: 62 additions & 1 deletion

File tree

  • instrumentation-loongsuite
    • loongsuite-instrumentation-agentscope
    • loongsuite-instrumentation-claude-agent-sdk/src/opentelemetry/instrumentation/claude_agent_sdk
    • loongsuite-instrumentation-dashscope/src/opentelemetry/instrumentation/dashscope/utils
    • loongsuite-instrumentation-google-adk/src/opentelemetry/instrumentation/google_adk/internal
    • loongsuite-instrumentation-hermes-agent/src/opentelemetry/instrumentation/hermes_agent
    • loongsuite-instrumentation-langchain/src/opentelemetry/instrumentation/langchain/internal
    • loongsuite-instrumentation-minisweagent/src/opentelemetry/instrumentation/minisweagent/internal
    • loongsuite-instrumentation-qwen-agent/src/opentelemetry/instrumentation/qwen_agent
    • loongsuite-instrumentation-vita/src/opentelemetry/instrumentation/vita
    • loongsuite-instrumentation-widesearch/src/opentelemetry/instrumentation/widesearch

instrumentation-loongsuite/loongsuite-instrumentation-agentscope/src/opentelemetry/instrumentation/agentscope/patch.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ async def wrap_tool_call(wrapped, instance, args, kwargs, handler):
403403
# Create invocation object with all tool data
404404
invocation = ExecuteToolInvocation(
405405
tool_name=tool_name,
406+
tool_type="function",
406407
tool_call_id=tool_id,
407408
tool_description=tool_description,
408409
tool_call_arguments=tool_args,

instrumentation-loongsuite/loongsuite-instrumentation-agentscope/tests/test_skill_detection.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ async def fake_tool_generator():
283283

284284
invocation = ExecuteToolInvocation(
285285
tool_name="read_file",
286+
tool_type="function",
286287
skill_name="news",
287288
skill_id="workspace:default:news",
288289
skill_description="Latest news",

instrumentation-loongsuite/loongsuite-instrumentation-claude-agent-sdk/src/opentelemetry/instrumentation/claude_agent_sdk/patch.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ def _create_tool_spans_from_message(
159159
try:
160160
tool_invocation = ExecuteToolInvocation(
161161
tool_name=tool_name,
162+
tool_type="function",
162163
tool_call_id=tool_use_id,
163164
tool_call_arguments=tool_input,
164165
tool_description=tool_name,

instrumentation-loongsuite/loongsuite-instrumentation-dashscope/src/opentelemetry/instrumentation/dashscope/utils/generation.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,8 @@ def _create_invocation_from_generation(
469469

470470
invocation = LLMInvocation(request_model=request_model)
471471
invocation.provider = "dashscope"
472+
invocation.server_address = "dashscope.aliyuncs.com"
473+
invocation.server_port = 443
472474
invocation.input_messages = _extract_input_messages(kwargs)
473475

474476
# Extract tool definitions and convert to FunctionToolDefinition objects

instrumentation-loongsuite/loongsuite-instrumentation-google-adk/src/opentelemetry/instrumentation/google_adk/internal/_plugin.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ async def before_run_callback(
119119
invocation = InvokeAgentInvocation(
120120
provider="google_adk",
121121
agent_name=invocation_context.app_name,
122+
agent_id=invocation_context.app_name,
122123
)
123124

124125
# Set conversation_id if available
@@ -275,6 +276,7 @@ async def before_agent_callback(
275276
invocation = InvokeAgentInvocation(
276277
provider="google_adk",
277278
agent_name=agent.name,
279+
agent_id=agent.name,
278280
)
279281

280282
# Set agent attributes
@@ -508,6 +510,7 @@ async def before_tool_callback(
508510
# Create invocation object
509511
invocation = ExecuteToolInvocation(
510512
tool_name=tool.name,
513+
tool_type="function",
511514
provider="google_adk",
512515
)
513516

instrumentation-loongsuite/loongsuite-instrumentation-hermes-agent/src/opentelemetry/instrumentation/hermes_agent/helpers.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,7 @@ def create_agent_invocation(
571571
invocation = InvokeAgentInvocation(
572572
provider=_HERMES_AGENT_SYSTEM,
573573
agent_name="Hermes",
574+
agent_id="hermes-agent",
574575
conversation_id=getattr(instance, "session_id", None),
575576
request_model=getattr(instance, "model", None),
576577
input_messages=[
@@ -609,6 +610,25 @@ def should_create_entry_for_agent(instance: Any) -> bool:
609610
return bool(_entry_platform(instance))
610611

611612

613+
def _extract_server_info(instance: Any) -> tuple[str | None, int | None]:
614+
"""Extract server address and port from instance base_url."""
615+
base_url = getattr(instance, "base_url", None)
616+
if not base_url:
617+
return None, None
618+
base_url_str = str(base_url).rstrip("/")
619+
try:
620+
from urllib.parse import urlparse
621+
622+
parsed = urlparse(base_url_str)
623+
address = parsed.hostname
624+
port = parsed.port
625+
if port is None:
626+
port = 443 if parsed.scheme == "https" else 80
627+
return address, port
628+
except Exception:
629+
return None, None
630+
631+
612632
def create_llm_invocation(instance: Any, api_kwargs: Any) -> LLMInvocation:
613633
if not isinstance(api_kwargs, dict):
614634
api_kwargs = {}
@@ -620,6 +640,8 @@ def create_llm_invocation(instance: Any, api_kwargs: Any) -> LLMInvocation:
620640
if max_tokens is None:
621641
max_tokens = api_kwargs.get("max_output_tokens")
622642

643+
server_address, server_port = _extract_server_info(instance)
644+
623645
invocation = LLMInvocation(
624646
provider=provider_name(instance),
625647
request_model=request_model or None,
@@ -633,6 +655,8 @@ def create_llm_invocation(instance: Any, api_kwargs: Any) -> LLMInvocation:
633655
presence_penalty=api_kwargs.get("presence_penalty"),
634656
seed=api_kwargs.get("seed"),
635657
stop_sequences=api_kwargs.get("stop"),
658+
server_address=server_address,
659+
server_port=server_port,
636660
)
637661
return invocation
638662

@@ -683,6 +707,7 @@ def create_tool_invocation(
683707
tool_name=tool_name,
684708
provider=provider or _HERMES_AGENT_SYSTEM,
685709
)
710+
invocation.tool_type = "function"
686711
if invocation.provider:
687712
invocation.attributes["gen_ai.provider.name"] = invocation.provider
688713
if arguments is not None:

instrumentation-loongsuite/loongsuite-instrumentation-langchain/src/opentelemetry/instrumentation/langchain/internal/_tracer.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ def _start_agent(self, run: Run) -> None:
407407
invocation = InvokeAgentInvocation(
408408
provider="langchain",
409409
agent_name=agent_name,
410+
agent_id=agent_name or None,
410411
input_messages=input_messages,
411412
)
412413
self._handler.start_invoke_agent(invocation, context=parent_ctx)
@@ -571,6 +572,7 @@ def _on_tool_start(self, run: Run) -> None:
571572
tool_name=run.name or "unknown_tool",
572573
tool_call_arguments=input_str,
573574
tool_call_id=tool_call_id,
575+
tool_type="function",
574576
)
575577
self._handler.start_execute_tool(invocation, context=parent_ctx)
576578
rd = _RunData(

instrumentation-loongsuite/loongsuite-instrumentation-minisweagent/src/opentelemetry/instrumentation/minisweagent/internal/agent_wrappers.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,8 @@ def __call__(
113113
han.start_entry(entry_inv, context=context_api.get_current())
114114

115115
inv = InvokeAgentInvocation(
116-
provider="minisweagent", agent_name=agent_name
116+
provider="minisweagent", agent_name=agent_name,
117+
agent_id=agent_name,
117118
)
118119
inv.request_model = _request_model_from_agent(instance)
119120
inv.attributes.setdefault("gen_ai.framework", "minisweagent")

instrumentation-loongsuite/loongsuite-instrumentation-qwen-agent/src/opentelemetry/instrumentation/qwen_agent/utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,7 @@ def _create_agent_invocation(
406406
invocation = InvokeAgentInvocation(
407407
provider=provider_name,
408408
agent_name=agent_name,
409+
agent_id=agent_name,
409410
agent_description=agent_description,
410411
request_model=request_model,
411412
input_messages=input_messages,
@@ -462,6 +463,7 @@ def _create_tool_invocation(
462463

463464
return ExecuteToolInvocation(
464465
tool_name=tool_name,
466+
tool_type="function",
465467
tool_call_arguments=parsed_args,
466468
tool_description=tool_description,
467469
)

instrumentation-loongsuite/loongsuite-instrumentation-vita/src/opentelemetry/instrumentation/vita/patch.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ def wrap_generate_next_message(
300300
invocation = InvokeAgentInvocation(
301301
provider="vitabench",
302302
agent_name=agent_name,
303+
agent_id=agent_name,
303304
request_model=model,
304305
)
305306

@@ -398,6 +399,11 @@ def wrap_generate(
398399
# response_model_name
399400
invocation.response_model_name = model
400401

402+
# response_id
403+
resp_id = getattr(result, "id", None) or getattr(result, "response_id", None)
404+
if resp_id:
405+
invocation.response_id = str(resp_id)
406+
401407
# finish_reasons
402408
if getattr(result, "tool_calls", None):
403409
invocation.finish_reasons = ["tool_calls"]
@@ -433,6 +439,7 @@ def wrap_get_response(
433439
tool_name=tool_name,
434440
tool_call_id=tool_call_id,
435441
provider="vitabench",
442+
tool_type="function",
436443
)
437444

438445
# tool_call_arguments

0 commit comments

Comments
 (0)