Skip to content

Conversation

krisztianfekete
Copy link

@krisztianfekete krisztianfekete commented Aug 16, 2025

This PR adopts the current OpenTelemetry Semantic Conventions for genAI agents and frameworks as per https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-agent-spans/

Fixes #1826

Sets

  • span.set_attribute('gen_ai.system', 'gcp.vertex_ai') according to the well-known system names.
  • gen_ai.operation.name for Agent invocations to invoke_agent and chat for LLM calls according to specs

Adds missing attributes:

  • gen_ai.response.model
  • gen_ai.request.temperature

Adds new utility functions to support proper OpenTelemetry conventions for naming and event emission:

  • _create_span_name(operation_name: str, model_name: str) -> str
    Creates span names following the OTel convention: {operation_name} {model_name}

  • add_genai_prompt_event(span: trace.Span, prompt_content: str)
    Adds GenAI prompt events using the standard gen_ai.content.prompt event name

  • add_genai_completion_event(span: trace.Span, completion_content: str)
    Adds GenAI completion events using the standard gen_ai.content.completion event name

Local demo script:

1. Testing LLM call tracing...
{
    "name": "demo_llm_call",
    "context": {
        "trace_id": "0x53246dca2bc392616675092825be5a88",
        "span_id": "0xbece85a4aec59573",
        "trace_state": "[]"
    },
    "kind": "SpanKind.INTERNAL",
    "parent_id": null,
    "start_time": "2025-08-28T09:54:58.002595Z",
    "end_time": "2025-08-28T09:54:58.002803Z",
    "status": {
        "status_code": "UNSET"
    },
    "attributes": {
        "gen_ai.system": "gcp.vertex_ai",
        "gen_ai.operation.name": "chat",
        "gen_ai.request.model": "gemini-1.5-pro",
        "gcp.vertex.agent.invocation_id": "demo_invocation",
        "gcp.vertex.agent.session_id": "640f2e0e-3c43-4bf4-9d22-707a4e9b549e",
        "gcp.vertex.agent.event_id": "demo_event_1",
        "gcp.vertex.agent.llm_request": "{\"model\": \"gemini-1.5-pro\", \"config\": {\"temperature\": 0.7, \"top_p\": 0.9, \"max_output_tokens\": 100}, \"contents\": [{\"parts\": [{\"text\": \"Hello!\"}], \"role\": \"user\"}]}",
        "gen_ai.request.top_p": 0.9,
        "gen_ai.request.max_tokens": 100,
        "gen_ai.request.temperature": 0.7,
        "gcp.vertex.agent.llm_response": "{\"turn_complete\":true,\"finish_reason\":\"STOP\",\"usage_metadata\":{\"candidates_token_count\":25,\"prompt_token_count\":10,\"total_token_count\":35}}",
        "gen_ai.usage.input_tokens": 10,
        "gen_ai.usage.output_tokens": 25,
        "gen_ai.response.finish_reasons": [
            "stop"
        ]
    },
    "events": [
        {
            "name": "gen_ai.content.prompt",
            "timestamp": "2025-08-28T09:54:58.002716Z",
            "attributes": {
                "gen_ai.prompt": "{\"role\": \"user\", \"content\": \"Hello!\"}"
            }
        },
        {
            "name": "gen_ai.content.completion",
            "timestamp": "2025-08-28T09:54:58.002721Z",
            "attributes": {
                "gen_ai.completion": "{\"role\": \"assistant\", \"content\": \"Hi there!\"}"
            }
        }
    ],
    "links": [],
    "resource": {
        "attributes": {
            "telemetry.sdk.language": "python",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.version": "1.36.0",
            "service.name": "unknown_service"
        },
        "schema_url": ""
    }
}

==================================================

2. Testing tool call tracing...
{
    "name": "demo_tool_call",
    "context": {
        "trace_id": "0x68031540beab4ee631d2c45d0de03a27",
        "span_id": "0xa261409434ff7b90",
        "trace_state": "[]"
    },
    "kind": "SpanKind.INTERNAL",
    "parent_id": null,
    "start_time": "2025-08-28T09:54:58.003070Z",
    "end_time": "2025-08-28T09:54:58.003118Z",
    "status": {
        "status_code": "UNSET"
    },
    "attributes": {
        "gen_ai.system": "gcp.vertex_ai",
        "gen_ai.operation.name": "execute_tool",
        "gen_ai.tool.name": "demo_tool",
        "gen_ai.tool.description": "A demo tool for testing",
        "gen_ai.tool.call.id": "tool_call_123",
        "gcp.vertex.agent.tool_call_args": "{\"param1\": \"value1\", \"param2\": 42}",
        "gcp.vertex.agent.event_id": "demo_tool_event",
        "gcp.vertex.agent.tool_response": "{\"result\": \"Tool executed successfully\"}",
        "gcp.vertex.agent.llm_request": "{}",
        "gcp.vertex.agent.llm_response": "{}"
    },
    "events": [],
    "links": [],
    "resource": {
        "attributes": {
            "telemetry.sdk.language": "python",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.version": "1.36.0",
            "service.name": "unknown_service"
        },
        "schema_url": ""
    }
}
==================================================

3. Testing agent invocation via Runner...
Agent event: author=hello_agent, text=Hello from HelloAgent
{
    "name": "agent_run [hello_agent]",
    "context": {
        "trace_id": "0xe20e54834d39c088855790473227b98c",
        "span_id": "0x4ada5d741199c335",
        "trace_state": "[]"
    },
    "kind": "SpanKind.INTERNAL",
    "parent_id": "0x2ac728e5a5fb5e48",
    "start_time": "2025-08-28T09:54:58.003419Z",
    "end_time": "2025-08-28T09:54:58.003485Z",
    "status": {
        "status_code": "UNSET"
    },
    "attributes": {},
    "events": [],
    "links": [],
    "resource": {
        "attributes": {
            "telemetry.sdk.language": "python",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.version": "1.36.0",
            "service.name": "unknown_service"
        },
        "schema_url": ""
    }
}
{
    "name": "invocation",
    "context": {
        "trace_id": "0xe20e54834d39c088855790473227b98c",
        "span_id": "0x2ac728e5a5fb5e48",
        "trace_state": "[]"
    },
    "kind": "SpanKind.INTERNAL",
    "parent_id": "0xdd09dc780dad1aff",
    "start_time": "2025-08-28T09:54:58.003329Z",
    "end_time": "2025-08-28T09:54:58.003604Z",
    "status": {
        "status_code": "UNSET"
    },
    "attributes": {
        "gen_ai.operation.name": "invoke_agent"
    },
    "events": [],
    "links": [],
    "resource": {
        "attributes": {
            "telemetry.sdk.language": "python",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.version": "1.36.0",
            "service.name": "unknown_service"
        },
        "schema_url": ""
    }
}
{
    "name": "demo_runner_invocation",
    "context": {
        "trace_id": "0xe20e54834d39c088855790473227b98c",
        "span_id": "0xdd09dc780dad1aff",
        "trace_state": "[]"
    },
    "kind": "SpanKind.INTERNAL",
    "parent_id": null,
    "start_time": "2025-08-28T09:54:58.003263Z",
    "end_time": "2025-08-28T09:54:58.003719Z",
    "status": {
        "status_code": "UNSET"
    },
    "attributes": {},
    "events": [],
    "links": [],
    "resource": {
        "attributes": {
            "telemetry.sdk.language": "python",
            "telemetry.sdk.name": "opentelemetry",
            "telemetry.sdk.version": "1.36.0",
            "service.name": "unknown_service"
        },
        "schema_url": ""
    }
}

Copy link

google-cla bot commented Aug 16, 2025

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@adk-bot adk-bot added bot triaged [Bot] This issue is triaged by ADK bot tracing [Component] This issue is related to OpenTelemetry tracing labels Aug 16, 2025
@adk-bot
Copy link
Collaborator

adk-bot commented Aug 16, 2025

Response from ADK Triaging Agent

Hello @krisztianfekete, thank you for your contribution!

It looks like a couple of automated checks are failing on this PR. To help move this forward, could you please address the following?

  • Commit Message Format: The conventionalcommits.org check is failing. Please ensure your commit message and PR title follow the Conventional Commits specification as outlined in our contribution guidelines.
  • Contributor License Agreement (CLA): The cla/google check has failed. Please ensure you have signed the CLA, as this is a requirement for merging contributions. The google-cla bot has provided instructions in an earlier comment.

Addressing these items will help our reviewers proceed with the review process. Thanks!

@adk-bot adk-bot requested a review from Jacksunwei August 16, 2025 09:32
@krisztianfekete krisztianfekete changed the title Adopt OTel semantic conventions for agents and frameworks adopt OTel semantic conventions for agents and frameworks Aug 16, 2025
@krisztianfekete krisztianfekete changed the title adopt OTel semantic conventions for agents and frameworks Adopt OTel semantic conventions for agents and frameworks Aug 16, 2025
@krisztianfekete krisztianfekete force-pushed the main branch 2 times, most recently from aa86026 to e5ef820 Compare August 16, 2025 09:42
@krisztianfekete
Copy link
Author

@Jacksunwei, can you please take a look at this PR once you have a chance?

@seanzhou1023 seanzhou1023 added agent engine [Component] This issue is related to Agent Engine deployment and removed agent engine [Component] This issue is related to Agent Engine deployment labels Aug 19, 2025
@krisztianfekete
Copy link
Author

Hey @Jacksunwei, can you please take a look at this?

Copy link

@RKest RKest left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, thank you for the PR and apologies for the delay.

We've recently started working on GenAI semconv alignment. Our internal approach differs slightly: we tentatively plan to use logs for prompts/responses and use the opentelemetry-instrumentation-google-genai library for instrumenting LLM calls.

Because of these differences, your PR will likely by put on hold for the time being.

Still whenever you get a chance, please address the questions in this review, as it would give us valuable feedback to consider in our implementation.

Comment on lines +183 to +193
if hasattr(llm_response, 'id') and llm_response.id:
span.set_attribute('gen_ai.response.id', llm_response.id)

# Set response model if different from request model
if (
hasattr(llm_response, 'model')
and llm_response.model
and llm_response.model != llm_request.model
):
span.set_attribute('gen_ai.response.model', llm_response.model)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where did you find the llm_response.model and llm_response.id field?
They are not a part of the pydantic model https://github.com/google/adk-python/blob/main/src/google/adk/models/llm_response.py#L26

Comment on lines +187 to +192
if (
hasattr(llm_response, 'model')
and llm_response.model
and llm_response.model != llm_request.model
):
span.set_attribute('gen_ai.response.model', llm_response.model)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reasoning for only setting the gen_ai.response.model when it's different to gen_ai.request.model?

Is this behavior specified somewhere, so it would be reasonable to assume both are the same if the response model is missing?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SemConv marks gen_ai.request.model as “Conditionally Required (if available)” and gen_ai.response.model as “Recommended,” not required, so it might makes sense to only include it if it provides additional information. I have a vague recollection of this practice from one of the many semconv GH issues discussing this, but cannot find it now.

@krisztianfekete
Copy link
Author

krisztianfekete commented Aug 28, 2025

Hi, thank you for the PR and apologies for the delay.

We've recently started working on GenAI semconv alignment. Our internal approach differs slightly: we tentatively plan to use logs for prompts/responses and use the opentelemetry-instrumentation-google-genai library for instrumenting LLM calls.

That's the discussion I am aiming to start. It was decided in OTel community that both span attributes AND logs should be a valid options to emit input/output messages and the end users should be able to use one or both depending on their requirements.

Because of these differences, your PR will likely by put on hold for the time being.

Do you have an ETA on this effort? There are also some missing attributes that would be a useful addition and I am happy to spin those out in a separate PR as I don't expect those to be contraversail, e.g. gen_ai.tool.name, gen_ai.operation.name, etc.

@RKest
Copy link

RKest commented Aug 29, 2025

That's the discussion I am aiming to start. [...] and the end users should be able to use one or both depending on their requirements.

Ultimately instrumentation of LLM inputs/outputs will not be done in the ADK codebase, but instead in opentelemetry-instrumentation-google-genai (and TBD for non-gemini LLM requests).
I believe that would be a more appropriate place to pick up this point.

Do you have an ETA on this effort?

You should see the first changes within one to two weeks.

I am happy to spin those out in a separate PR as I don't expect those to be contraversail, e.g. gen_ai.tool.name, gen_ai.operation.name, etc.

I appreciate the offer and would be happy to review it.
Just keep in mind to align with latest OTLP semconv (e.g. I believe gen_ai.content.completion and gen_ai.content.prompt events for example have been replaced by gen_ai.choice and gen_ai.user.message in v.1.28).

This effort would overlap with my current work, but I think having more pairs of eyes on this would be beneficial.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bot triaged [Bot] This issue is triaged by ADK bot tracing [Component] This issue is related to OpenTelemetry tracing
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Need support standard gen_ai span attributions
4 participants