Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/google/adk/telemetry/_experimental_semconv.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,8 +433,14 @@ def _to_system_instructions(
def set_operation_details_common_attributes(
operation_details_common_attributes: MutableMapping[str, AttributeValue],
attributes: Mapping[str, AttributeValue],
):
log_only_attributes: Mapping[str, AttributeValue] | None = None,
) -> None:
operation_details_common_attributes.update(attributes)
if log_only_attributes and get_content_capturing_mode() in (
'EVENT_ONLY',
'SPAN_AND_EVENT',
):
operation_details_common_attributes.update(log_only_attributes)


async def set_operation_details_attributes_from_request(
Expand Down
60 changes: 49 additions & 11 deletions src/google/adk/telemetry/tracing.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@

from .. import version
from ..utils.model_name_utils import is_gemini_model
from ._experimental_semconv import get_content_capturing_mode
from ._experimental_semconv import is_experimental_semconv
from ._experimental_semconv import maybe_log_completion_details
from ._experimental_semconv import set_operation_details_attributes_from_request
Expand Down Expand Up @@ -556,20 +557,26 @@ def use_generate_content_span(
common_attributes = {
GEN_AI_AGENT_NAME: invocation_context.agent.name,
GEN_AI_CONVERSATION_ID: invocation_context.session.id,
USER_ID: invocation_context.session.user_id,
'gcp.vertex.agent.event_id': model_response_event.id,
'gcp.vertex.agent.invocation_id': invocation_context.invocation_id,
}
log_only_common_attributes = {}
if invocation_context.session.user_id is not None:
log_only_common_attributes[USER_ID] = invocation_context.session.user_id
if (
_is_gemini_agent(invocation_context.agent)
and _instrumented_with_opentelemetry_instrumentation_google_genai()
):
with _use_extra_generate_content_attributes(common_attributes):
with _use_extra_generate_content_attributes(
common_attributes,
log_only_extra_attributes=log_only_common_attributes,
):
yield
else:
with _use_native_generate_content_span_stable_semconv(
llm_request=llm_request,
common_attributes=common_attributes,
log_only_common_attributes=log_only_common_attributes,
) as span:
yield span.span

Expand All @@ -590,24 +597,32 @@ async def use_inference_span(
common_attributes = {
GEN_AI_AGENT_NAME: invocation_context.agent.name,
GEN_AI_CONVERSATION_ID: invocation_context.session.id,
USER_ID: invocation_context.session.user_id,
'gcp.vertex.agent.event_id': model_response_event.id,
'gcp.vertex.agent.invocation_id': invocation_context.invocation_id,
}
log_only_common_attributes = {}
if invocation_context.session.user_id is not None:
log_only_common_attributes[USER_ID] = invocation_context.session.user_id
if (
_is_gemini_agent(invocation_context.agent)
and _instrumented_with_opentelemetry_instrumentation_google_genai()
):
with _use_extra_generate_content_attributes(common_attributes):
with _use_extra_generate_content_attributes(
common_attributes,
log_only_extra_attributes=log_only_common_attributes,
):
yield
else:
async with _use_native_generate_content_span(
llm_request=llm_request,
common_attributes=common_attributes,
log_only_common_attributes=log_only_common_attributes,
) as gc_span:
if is_experimental_semconv():
set_operation_details_common_attributes(
gc_span.operation_details_common_attributes, common_attributes
gc_span.operation_details_common_attributes,
common_attributes,
log_only_attributes=log_only_common_attributes,
)
try:
yield gc_span
Expand Down Expand Up @@ -664,6 +679,7 @@ def _instrumented_with_opentelemetry_instrumentation_google_genai() -> bool:
@contextmanager
def _use_extra_generate_content_attributes(
extra_attributes: Mapping[str, AttributeValue],
log_only_extra_attributes: Mapping[str, AttributeValue] | None = None,
):
try:
from opentelemetry.instrumentation.google_genai import GENERATE_CONTENT_EXTRA_ATTRIBUTES_CONTEXT_KEY
Expand All @@ -675,13 +691,25 @@ def _use_extra_generate_content_attributes(
+ ' Please upgrade to version to 0.6b0 or above.'
)
yield

return

tok = otel_context.attach(
otel_context.set_value(
GENERATE_CONTENT_EXTRA_ATTRIBUTES_CONTEXT_KEY, extra_attributes
)
ctx = otel_context.set_value(
GENERATE_CONTENT_EXTRA_ATTRIBUTES_CONTEXT_KEY, extra_attributes
)
if log_only_extra_attributes:
try:
from opentelemetry.instrumentation.google_genai import GENERATE_CONTENT_EVENT_ONLY_EXTRA_ATTRIBUTES_CONTEXT_KEY

ctx = otel_context.set_value(
GENERATE_CONTENT_EVENT_ONLY_EXTRA_ATTRIBUTES_CONTEXT_KEY,
log_only_extra_attributes,
context=ctx,
)
except (ImportError, AttributeError):
pass

tok = otel_context.attach(ctx)
try:
yield
finally:
Expand Down Expand Up @@ -713,6 +741,7 @@ def _set_common_generate_content_attributes(
def _use_native_generate_content_span_stable_semconv(
llm_request: LlmRequest,
common_attributes: Mapping[str, AttributeValue],
log_only_common_attributes: Mapping[str, AttributeValue] | None = None,
) -> Iterator[GenerateContentSpan]:
with tracer.start_as_current_span(
f"generate_content {llm_request.model or ''}"
Expand All @@ -734,12 +763,18 @@ def _use_native_generate_content_span_stable_semconv(
attributes={GEN_AI_SYSTEM: _guess_gemini_system_name()},
)
)
user_message_attributes = {GEN_AI_SYSTEM: _guess_gemini_system_name()}
if _should_log_prompt_response_content() and log_only_common_attributes:
user_id = log_only_common_attributes.get(USER_ID)
if user_id is not None:
user_message_attributes[USER_ID] = user_id

for content in llm_request.contents:
otel_logger.emit(
LogRecord(
event_name='gen_ai.user.message',
body={'content': _serialize_content_with_elision(content)},
attributes={GEN_AI_SYSTEM: _guess_gemini_system_name()},
attributes=user_message_attributes,
)
)

Expand All @@ -750,10 +785,13 @@ def _use_native_generate_content_span_stable_semconv(
async def _use_native_generate_content_span(
llm_request: LlmRequest,
common_attributes: Mapping[str, AttributeValue],
log_only_common_attributes: Mapping[str, AttributeValue] | None = None,
) -> AsyncIterator[GenerateContentSpan]:
if not is_experimental_semconv():
with _use_native_generate_content_span_stable_semconv(
llm_request, common_attributes
llm_request,
common_attributes,
log_only_common_attributes=log_only_common_attributes,
) as gc_span:
yield gc_span
return
Expand Down
2 changes: 0 additions & 2 deletions tests/unittests/telemetry/test_node_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,6 @@ async def some_node(ctx, node_input):
),
'gen_ai.request.model': 'mock',
'gen_ai.system': 'gemini',
'user.id': 'some_user',
},
children=[
SpanDigest(
Expand Down Expand Up @@ -327,7 +326,6 @@ async def some_node(ctx, node_input):
),
'gen_ai.request.model': 'mock',
'gen_ai.system': 'gemini',
'user.id': 'some_user',
},
),
],
Expand Down
Loading