Skip to content

Commit df05ed6

Browse files
google-genai-botcopybara-github
authored andcommitted
feat: migrate invocation_context to callback_context
Update plugin manager and built-in plugins to prioritize CallbackContext. Keep InvocationContext access for legacy plugins with adapter. Change callback docs/tests to cover the new context. PiperOrigin-RevId: 818822267
1 parent 2158b3c commit df05ed6

10 files changed

Lines changed: 110 additions & 240 deletions

File tree

src/google/adk/agents/readonly_context.py

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -55,21 +55,6 @@ def state(self) -> MappingProxyType[str, Any]:
5555
return MappingProxyType(self._invocation_context.session.state)
5656

5757
@property
58-
def user_id(self) -> str:
59-
"""The user ID for the current invocation."""
60-
return self._invocation_context.user_id
61-
62-
@property
63-
def app_name(self) -> str:
64-
"""The application name for the current invocation."""
65-
return self._invocation_context.app_name
66-
67-
@property
68-
def session_id(self) -> str:
69-
"""The session ID for the current invocation."""
70-
return self._invocation_context.session.id
71-
72-
@property
73-
def branch(self) -> Optional[str]:
74-
"""The branch name for the current invocation, if any."""
75-
return self._invocation_context.branch
58+
def session(self) -> Session:
59+
"""The current session for this invocation."""
60+
return self._invocation_context.session

src/google/adk/cli/plugins/recordings_plugin.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
from .recordings_schema import ToolRecording
4040

4141
if TYPE_CHECKING:
42+
from ...agents.invocation_context import InvocationContext
4243
from ...tools.base_tool import BaseTool
4344
from ...tools.tool_context import ToolContext
4445

@@ -74,10 +75,10 @@ def __init__(self, *, name: str = "adk_recordings") -> None:
7475

7576
@override
7677
async def before_run_callback(
77-
self, *, callback_context: CallbackContext
78+
self, *, invocation_context: InvocationContext
7879
) -> Optional[types.Content]:
7980
"""Always create fresh per-invocation recording state when enabled."""
80-
ctx = callback_context
81+
ctx = CallbackContext(invocation_context)
8182
if self._is_record_mode_on(ctx):
8283
# Always create/overwrite the state for this invocation
8384
self._create_invocation_state(ctx)
@@ -279,10 +280,10 @@ async def on_tool_error_callback(
279280

280281
@override
281282
async def after_run_callback(
282-
self, *, callback_context: CallbackContext
283+
self, *, invocation_context: InvocationContext
283284
) -> None:
284285
"""Finalize and persist recordings, then clean per-invocation state."""
285-
ctx = callback_context
286+
ctx = CallbackContext(invocation_context)
286287
if not self._is_record_mode_on(ctx):
287288
return None
288289

src/google/adk/cli/plugins/replay_plugin.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
from .recordings_schema import ToolRecording
3939

4040
if TYPE_CHECKING:
41+
from ...agents.invocation_context import InvocationContext
4142
from ...tools.base_tool import BaseTool
4243
from ...tools.tool_context import ToolContext
4344

@@ -80,10 +81,10 @@ def __init__(self, *, name: str = "adk_replay") -> None:
8081

8182
@override
8283
async def before_run_callback(
83-
self, *, callback_context: CallbackContext
84+
self, *, invocation_context: InvocationContext
8485
) -> Optional[types.Content]:
8586
"""Load replay recordings when enabled."""
86-
ctx = callback_context
87+
ctx = CallbackContext(invocation_context)
8788
if self._is_replay_mode_on(ctx):
8889
# Load the replay state for this invocation
8990
self._load_invocation_state(ctx)
@@ -155,10 +156,10 @@ async def before_tool_callback(
155156

156157
@override
157158
async def after_run_callback(
158-
self, *, callback_context: CallbackContext
159+
self, *, invocation_context: InvocationContext
159160
) -> None:
160161
"""Clean up replay state after invocation completes."""
161-
ctx = callback_context
162+
ctx = CallbackContext(invocation_context)
162163
if not self._is_replay_mode_on(ctx):
163164
return None
164165

src/google/adk/plugins/base_plugin.py

Lines changed: 39 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -111,125 +111,81 @@ def __init__(self, name: str):
111111
super().__init__()
112112
self.name = name
113113

114-
if TYPE_CHECKING:
115-
116-
async def on_user_message_callback(
117-
self,
118-
*,
119-
callback_context: Optional[CallbackContext] = None,
120-
user_message: types.Content,
121-
invocation_context: Optional[InvocationContext] = None,
122-
) -> Optional[types.Content]:
123-
"""Callback executed when a user message is received before an invocation starts.
124-
125-
Plugins can implement this with either callback_context (new) or
126-
invocation_context (deprecated) or both.
127-
"""
128-
129-
async def before_run_callback(
130-
self,
131-
*,
132-
callback_context: Optional[CallbackContext] = None,
133-
invocation_context: Optional[InvocationContext] = None,
134-
) -> Optional[types.Content]:
135-
"""Callback executed before the ADK runner runs.
136-
137-
Plugins can implement this with either callback_context (new) or
138-
invocation_context (deprecated) or both.
139-
"""
140-
141-
async def on_event_callback(
142-
self,
143-
*,
144-
callback_context: Optional[CallbackContext] = None,
145-
event: Event,
146-
invocation_context: Optional[InvocationContext] = None,
147-
) -> Optional[Event]:
148-
"""Callback executed after an event is yielded from runner.
149-
150-
Plugins can implement this with either callback_context (new) or
151-
invocation_context (deprecated) or both.
152-
"""
153-
154-
async def after_run_callback(
155-
self,
156-
*,
157-
callback_context: Optional[CallbackContext] = None,
158-
invocation_context: Optional[InvocationContext] = None,
159-
) -> None:
160-
"""Callback executed after an ADK runner run has completed.
161-
162-
Plugins can implement this with either callback_context (new) or
163-
invocation_context (deprecated) or both.
164-
"""
165-
166-
# Runtime implementation accepts both via **kwargs
167114
async def on_user_message_callback(
168-
self, **kwargs: Any
115+
self,
116+
*,
117+
invocation_context: InvocationContext,
118+
user_message: types.Content,
169119
) -> Optional[types.Content]:
170120
"""Callback executed when a user message is received before an invocation starts.
171121
172122
This callback helps logging and modifying the user message before the
173123
runner starts the invocation.
174124
175125
Args:
176-
callback_context: The context for the callback execution.
126+
invocation_context: The context for the entire invocation.
177127
user_message: The message content input by user.
178-
invocation_context: DEPRECATED. Use callback_context instead. The context
179-
for the entire invocation. This parameter is maintained for backward
180-
compatibility and will be removed in a future version.
181128
182129
Returns:
183-
The modified user message or None if no modification is needed.
130+
An optional `types.Content` to be returned to the ADK. Returning a
131+
value to replace the user message. Returning `None` to proceed
132+
normally.
184133
"""
185-
return None
134+
pass
186135

187-
async def before_run_callback(self, **kwargs: Any) -> Optional[types.Content]:
136+
async def before_run_callback(
137+
self, *, invocation_context: InvocationContext
138+
) -> Optional[types.Content]:
188139
"""Callback executed before the ADK runner runs.
189140
190-
This is the first lifecycle hook and is ideal for global setup, logging,
191-
or checks that may stop the invocation from running.
141+
This is the first callback to be called in the lifecycle, ideal for global
142+
setup or initialization tasks.
192143
193144
Args:
194-
callback_context: The context for the callback execution.
195-
invocation_context: DEPRECATED. Use callback_context instead. The context
196-
for the entire invocation. This parameter is maintained for backward
197-
compatibility and will be removed in a future version.
145+
invocation_context: The context for the entire invocation, containing
146+
session information, the root agent, etc.
198147
199148
Returns:
200-
Optional `types.Content` to halt execution and return the value to the
201-
caller. Return `None` to proceed normally.
149+
An optional `Event` to be returned to the ADK. Returning a value to
150+
halt execution of the runner and ends the runner with that event. Return
151+
`None` to proceed normally.
202152
"""
203-
return None
153+
pass
204154

205-
async def on_event_callback(self, **kwargs: Any) -> Optional[Event]:
155+
async def on_event_callback(
156+
self, *, invocation_context: InvocationContext, event: Event
157+
) -> Optional[Event]:
206158
"""Callback executed after an event is yielded from runner.
207159
160+
This is the ideal place to make modification to the event before the event
161+
is handled by the underlying agent app.
162+
208163
Args:
209-
callback_context: The context for the callback execution.
164+
invocation_context: The context for the entire invocation.
210165
event: The event raised by the runner.
211-
invocation_context: DEPRECATED. Use callback_context instead. The context
212-
for the entire invocation. This parameter is maintained for backward
213-
compatibility and will be removed in a future version.
214166
215167
Returns:
216-
The modified event or None if no modification is needed.
168+
An optional value. A non-`None` return may be used by the framework to
169+
modify or replace the response. Returning `None` allows the original
170+
response to be used.
217171
"""
218-
return None
172+
pass
219173

220-
async def after_run_callback(self, **kwargs: Any) -> None:
174+
async def after_run_callback(
175+
self, *, invocation_context: InvocationContext
176+
) -> None:
221177
"""Callback executed after an ADK runner run has completed.
222178
179+
This is the final callback in the ADK lifecycle, suitable for cleanup, final
180+
logging, or reporting tasks.
181+
223182
Args:
224-
callback_context: The context for the callback execution.
225-
invocation_context: DEPRECATED. Use callback_context instead. The context
226-
for the entire invocation. This parameter is maintained for backward
227-
compatibility and will be removed in a future version.
183+
invocation_context: The context for the entire invocation.
228184
229185
Returns:
230186
None
231187
"""
232-
return None
188+
pass
233189

234190
async def before_agent_callback(
235191
self, *, agent: BaseAgent, callback_context: CallbackContext

src/google/adk/plugins/global_instruction_plugin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ async def before_model_callback(
7979
return None
8080

8181
# Resolve the global instruction (handle both string and InstructionProvider)
82-
readonly_context = callback_context
82+
readonly_context = ReadonlyContext(callback_context.invocation_context)
8383
final_global_instruction = await self._resolve_global_instruction(
8484
readonly_context
8585
)

src/google/adk/plugins/logging_plugin.py

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -69,32 +69,38 @@ def __init__(self, name: str = "logging_plugin"):
6969
async def on_user_message_callback(
7070
self,
7171
*,
72-
callback_context: CallbackContext,
72+
invocation_context: InvocationContext,
7373
user_message: types.Content,
7474
) -> Optional[types.Content]:
7575
"""Log user message and invocation start."""
7676
self._log(f"🚀 USER MESSAGE RECEIVED")
77-
self._log(f" Invocation ID: {callback_context.invocation_id}")
78-
self._log(f" Session ID: {callback_context.session_id}")
79-
self._log(f" User ID: {callback_context.user_id}")
80-
self._log(f" App Name: {callback_context.app_name}")
81-
self._log(f" Root Agent: {callback_context.agent_name}")
77+
self._log(f" Invocation ID: {invocation_context.invocation_id}")
78+
self._log(f" Session ID: {invocation_context.session.id}")
79+
self._log(f" User ID: {invocation_context.user_id}")
80+
self._log(f" App Name: {invocation_context.app_name}")
81+
self._log(
82+
" Root Agent:"
83+
f" {invocation_context.agent.name if hasattr(invocation_context.agent, 'name') else 'Unknown'}"
84+
)
8285
self._log(f" User Content: {self._format_content(user_message)}")
83-
if callback_context.branch:
84-
self._log(f" Branch: {callback_context.branch}")
86+
if invocation_context.branch:
87+
self._log(f" Branch: {invocation_context.branch}")
8588
return None
8689

8790
async def before_run_callback(
88-
self, *, callback_context: CallbackContext
91+
self, *, invocation_context: InvocationContext
8992
) -> Optional[types.Content]:
9093
"""Log invocation start."""
9194
self._log(f"🏃 INVOCATION STARTING")
92-
self._log(f" Invocation ID: {callback_context.invocation_id}")
93-
self._log(f" Starting Agent: {callback_context.agent_name}")
95+
self._log(f" Invocation ID: {invocation_context.invocation_id}")
96+
self._log(
97+
" Starting Agent:"
98+
f" {invocation_context.agent.name if hasattr(invocation_context.agent, 'name') else 'Unknown'}"
99+
)
94100
return None
95101

96102
async def on_event_callback(
97-
self, *, callback_context: CallbackContext, event: Event
103+
self, *, invocation_context: InvocationContext, event: Event
98104
) -> Optional[Event]:
99105
"""Log events yielded from the runner."""
100106
self._log(f"📢 EVENT YIELDED")
@@ -117,12 +123,15 @@ async def on_event_callback(
117123
return None
118124

119125
async def after_run_callback(
120-
self, *, callback_context: CallbackContext
126+
self, *, invocation_context: InvocationContext
121127
) -> Optional[None]:
122128
"""Log invocation completion."""
123129
self._log(f"✅ INVOCATION COMPLETED")
124-
self._log(f" Invocation ID: {callback_context.invocation_id}")
125-
self._log(f" Final Agent: {callback_context.agent_name}")
130+
self._log(f" Invocation ID: {invocation_context.invocation_id}")
131+
self._log(
132+
" Final Agent:"
133+
f" {invocation_context.agent.name if hasattr(invocation_context.agent, 'name') else 'Unknown'}"
134+
)
126135
return None
127136

128137
async def before_agent_callback(
@@ -132,8 +141,8 @@ async def before_agent_callback(
132141
self._log(f"🤖 AGENT STARTING")
133142
self._log(f" Agent Name: {callback_context.agent_name}")
134143
self._log(f" Invocation ID: {callback_context.invocation_id}")
135-
if callback_context.branch:
136-
self._log(f" Branch: {callback_context.branch}")
144+
if callback_context._invocation_context.branch:
145+
self._log(f" Branch: {callback_context._invocation_context.branch}")
137146
return None
138147

139148
async def after_agent_callback(

0 commit comments

Comments
 (0)