Skip to content

Commit 3ab4ce0

Browse files
feat(tracer): support invocation attrs (#89)
* feat: support invocation attrs * fix format * add app_name to invocation * add print trace id
1 parent 9cfb7a0 commit 3ab4ce0

File tree

4 files changed

+100
-8
lines changed

4 files changed

+100
-8
lines changed

veadk/agent.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,13 +114,8 @@ def model_post_init(self, __context: Any) -> None:
114114
self.tools.append(load_memory)
115115

116116
if self.tracers:
117-
self.before_model_callback = []
118-
self.after_model_callback = []
119-
self.after_tool_callback = []
120117
for tracer in self.tracers:
121-
self.before_model_callback.append(tracer.tracer_hook_before_model)
122-
self.after_model_callback.append(tracer.tracer_hook_after_model)
123-
self.after_tool_callback.append(tracer.tracer_hook_after_tool)
118+
tracer.do_hooks(self)
124119

125120
logger.info(f"Agent `{self.name}` init done.")
126121
logger.debug(

veadk/runner.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
from google.adk.agents import RunConfig
1717
from google.adk.agents.run_config import StreamingMode
18+
from google.adk.plugins.base_plugin import BasePlugin
1819
from google.adk.runners import Runner as ADKRunner
1920
from google.genai import types
2021
from google.genai.types import Blob
@@ -23,6 +24,7 @@
2324
from veadk.agent import Agent
2425
from veadk.evaluation import EvalSetRecorder
2526
from veadk.memory.short_term_memory import ShortTermMemory
27+
from veadk.tracing.base_tracer import UserMessagePlugin
2628
from veadk.types import MediaMessage
2729
from veadk.utils.logger import get_logger
2830
from veadk.utils.misc import read_png_to_bytes
@@ -44,6 +46,7 @@ def __init__(
4446
self,
4547
agent: Agent | RemoteVeAgent,
4648
short_term_memory: ShortTermMemory,
49+
plugins: list[BasePlugin] = [],
4750
app_name: str = "veadk_default_app",
4851
user_id: str = "veadk_default_user",
4952
):
@@ -65,11 +68,20 @@ def __init__(
6568
else:
6669
self.long_term_memory = None
6770

71+
# process plugins
72+
try:
73+
# try to detect tracer
74+
_ = self.agent.tracers[0]
75+
plugins.extend([UserMessagePlugin(name="user_message_plugin")])
76+
except Exception:
77+
logger.debug("Agent has no tracers, telemetry plugin not added.")
78+
6879
self.runner = ADKRunner(
6980
app_name=self.app_name,
7081
agent=self.agent,
7182
session_service=self.session_service,
7283
memory_service=self.long_term_memory,
84+
plugins=plugins,
7385
)
7486

7587
def _convert_messages(self, messages) -> list:
@@ -163,8 +175,30 @@ async def run(
163175
if save_tracing_data:
164176
self.save_tracing_file(session_id)
165177

178+
self._print_trace_id()
179+
166180
return final_output
167181

182+
def _print_trace_id(self):
183+
if not isinstance(self.agent, Agent):
184+
logger.warning(
185+
("The agent is not an instance of VeADK Agent, no trace id provided.")
186+
)
187+
return
188+
189+
if not self.agent.tracers:
190+
logger.warning(
191+
"No tracer is configured in the agent, no trace id provided."
192+
)
193+
return
194+
195+
try:
196+
trace_id = self.agent.tracers[0].get_trace_id() # type: ignore
197+
logger.info(f"Trace id: {trace_id}")
198+
except Exception as e:
199+
logger.warning(f"Get tracer id failed as {e}")
200+
return
201+
168202
def save_tracing_file(self, session_id: str) -> str:
169203
if not isinstance(self.agent, Agent):
170204
logger.warning(

veadk/tracing/base_tracer.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,69 @@
1717
from typing import Any, Optional
1818

1919
from google.adk.agents.callback_context import CallbackContext
20+
from google.adk.agents.invocation_context import InvocationContext
2021
from google.adk.models.llm_request import LlmRequest
2122
from google.adk.models.llm_response import LlmResponse
23+
from google.adk.plugins.base_plugin import BasePlugin
2224
from google.adk.tools import BaseTool, ToolContext
25+
from google.genai import types
2326
from opentelemetry import trace
2427

2528
from veadk.utils.logger import get_logger
2629

2730
logger = get_logger(__name__)
2831

2932

33+
class UserMessagePlugin(BasePlugin):
34+
def __init__(self, name: str):
35+
super().__init__(name)
36+
37+
async def on_user_message_callback(
38+
self,
39+
*,
40+
invocation_context: InvocationContext,
41+
user_message: types.Content,
42+
) -> Optional[types.Content]:
43+
"""Callback executed when a user message is received before an invocation starts.
44+
45+
This callback helps logging and modifying the user message before the
46+
runner starts the invocation.
47+
48+
Args:
49+
invocation_context: The context for the entire invocation.
50+
user_message: The message content input by user.
51+
52+
Returns:
53+
An optional `types.Content` to be returned to the ADK. Returning a
54+
value to replace the user message. Returning `None` to proceed
55+
normally.
56+
"""
57+
trace.get_tracer("gcp.vertex.agent")
58+
span = trace.get_current_span()
59+
60+
logger.debug(f"User message plugin works, catch {span}")
61+
span_name = getattr(span, "name", None)
62+
if span_name and span_name.startswith("invocation"):
63+
agent_name = invocation_context.agent.name
64+
invoke_branch = (
65+
invocation_context.branch if invocation_context.branch else agent_name
66+
)
67+
current_session = invocation_context.session
68+
69+
span.set_attribute("app_name", current_session.app_name)
70+
span.set_attribute("user_id", current_session.user_id)
71+
span.set_attribute("session_id", current_session.id)
72+
73+
span.set_attribute("agent_name", agent_name)
74+
span.set_attribute("invoke_branch", invoke_branch)
75+
76+
logger.debug(
77+
f"Add attributes to {span_name}: app_name={current_session.app_name}, user_id={current_session.user_id}, session_id={current_session.id}, agent_name={agent_name}, invoke_branch={invoke_branch}"
78+
)
79+
80+
return None
81+
82+
3083
def replace_bytes_with_empty(data):
3184
"""
3285
Recursively traverse the data structure and replace all bytes types with empty strings.

veadk/tracing/telemetry/opentelemetry_tracer.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,11 @@ class OpentelemetryTracer(BaseModel, BaseTracer):
5151
description="The exporters to export spans.",
5252
)
5353
name: str = Field(
54-
DEFAULT_VEADK_TRACER_NAME, description="The identifier of tracer."
54+
default=DEFAULT_VEADK_TRACER_NAME, description="The identifier of tracer."
5555
)
5656

5757
app_name: str = Field(
58-
"veadk_app",
58+
default="veadk_app",
5959
description="The identifier of app.",
6060
)
6161

@@ -127,6 +127,16 @@ def _init_tracer_provider(self) -> None:
127127
self._processors.append(processor)
128128
logger.debug(f"Init OpentelemetryTracer with {len(self.exporters)} exporters.")
129129

130+
def get_trace_id(self) -> str:
131+
if not self._inmemory_exporter:
132+
return ""
133+
try:
134+
trace_id = hex(int(self._inmemory_exporter._real_exporter.trace_id))[2:]
135+
except Exception:
136+
return ""
137+
138+
return trace_id
139+
130140
@override
131141
def dump(
132142
self,

0 commit comments

Comments
 (0)