@@ -400,6 +400,7 @@ async def run_async(
400400 new_message : Optional [types .Content ] = None ,
401401 state_delta : Optional [dict [str , Any ]] = None ,
402402 run_config : Optional [RunConfig ] = None ,
403+ metadata : Optional [dict [str , Any ]] = None ,
403404 ) -> AsyncGenerator [Event , None ]:
404405 """Main entry method to run the agent in this runner.
405406
@@ -417,6 +418,9 @@ async def run_async(
417418 new_message: A new message to append to the session.
418419 state_delta: Optional state changes to apply to the session.
419420 run_config: The run config for the agent.
421+ metadata: Optional per-request metadata that will be passed to callbacks.
422+ This allows passing request-specific context such as user_id, trace_id,
423+ or memory context keys to before_model_callback and other callbacks.
420424
421425 Yields:
422426 The events generated by the agent.
@@ -426,13 +430,16 @@ async def run_async(
426430 new_message are None.
427431 """
428432 run_config = run_config or RunConfig ()
433+ # Create a shallow copy to isolate from caller's modifications
434+ metadata = metadata .copy () if metadata else None
429435
430436 if new_message and not new_message .role :
431437 new_message .role = 'user'
432438
433439 async def _run_with_trace (
434440 new_message : Optional [types .Content ] = None ,
435441 invocation_id : Optional [str ] = None ,
442+ metadata : Optional [dict [str , Any ]] = None ,
436443 ) -> AsyncGenerator [Event , None ]:
437444 with tracer .start_as_current_span ('invocation' ):
438445 session = await self .session_service .get_session (
@@ -463,6 +470,7 @@ async def _run_with_trace(
463470 invocation_id = invocation_id ,
464471 run_config = run_config ,
465472 state_delta = state_delta ,
473+ metadata = metadata ,
466474 )
467475 if invocation_context .end_of_agents .get (
468476 invocation_context .agent .name
@@ -476,6 +484,7 @@ async def _run_with_trace(
476484 new_message = new_message , # new_message is not None.
477485 run_config = run_config ,
478486 state_delta = state_delta ,
487+ metadata = metadata ,
479488 )
480489
481490 async def execute (ctx : InvocationContext ) -> AsyncGenerator [Event ]:
@@ -502,7 +511,9 @@ async def execute(ctx: InvocationContext) -> AsyncGenerator[Event]:
502511 self .app , session , self .session_service
503512 )
504513
505- async with Aclosing (_run_with_trace (new_message , invocation_id )) as agen :
514+ async with Aclosing (
515+ _run_with_trace (new_message , invocation_id , metadata )
516+ ) as agen :
506517 async for event in agen :
507518 yield event
508519
@@ -1186,6 +1197,7 @@ async def _setup_context_for_new_invocation(
11861197 new_message : types .Content ,
11871198 run_config : RunConfig ,
11881199 state_delta : Optional [dict [str , Any ]],
1200+ metadata : Optional [dict [str , Any ]] = None ,
11891201 ) -> InvocationContext :
11901202 """Sets up the context for a new invocation.
11911203
@@ -1194,6 +1206,7 @@ async def _setup_context_for_new_invocation(
11941206 new_message: The new message to process and append to the session.
11951207 run_config: The run config of the agent.
11961208 state_delta: Optional state changes to apply to the session.
1209+ metadata: Optional per-request metadata to pass to callbacks.
11971210
11981211 Returns:
11991212 The invocation context for the new invocation.
@@ -1203,6 +1216,7 @@ async def _setup_context_for_new_invocation(
12031216 session ,
12041217 new_message = new_message ,
12051218 run_config = run_config ,
1219+ metadata = metadata ,
12061220 )
12071221 # Step 2: Handle new message, by running callbacks and appending to
12081222 # session.
@@ -1225,6 +1239,7 @@ async def _setup_context_for_resumed_invocation(
12251239 invocation_id : Optional [str ],
12261240 run_config : RunConfig ,
12271241 state_delta : Optional [dict [str , Any ]],
1242+ metadata : Optional [dict [str , Any ]] = None ,
12281243 ) -> InvocationContext :
12291244 """Sets up the context for a resumed invocation.
12301245
@@ -1234,6 +1249,7 @@ async def _setup_context_for_resumed_invocation(
12341249 invocation_id: The invocation id to resume.
12351250 run_config: The run config of the agent.
12361251 state_delta: Optional state changes to apply to the session.
1252+ metadata: Optional per-request metadata to pass to callbacks.
12371253
12381254 Returns:
12391255 The invocation context for the resumed invocation.
@@ -1259,6 +1275,7 @@ async def _setup_context_for_resumed_invocation(
12591275 new_message = user_message ,
12601276 run_config = run_config ,
12611277 invocation_id = invocation_id ,
1278+ metadata = metadata ,
12621279 )
12631280 # Step 3: Maybe handle new message.
12641281 if new_message :
@@ -1303,6 +1320,7 @@ def _new_invocation_context(
13031320 new_message : Optional [types .Content ] = None ,
13041321 live_request_queue : Optional [LiveRequestQueue ] = None ,
13051322 run_config : Optional [RunConfig ] = None ,
1323+ metadata : Optional [dict [str , Any ]] = None ,
13061324 ) -> InvocationContext :
13071325 """Creates a new invocation context.
13081326
@@ -1312,6 +1330,7 @@ def _new_invocation_context(
13121330 new_message: The new message for the context.
13131331 live_request_queue: The live request queue for the context.
13141332 run_config: The run config for the context.
1333+ metadata: Optional per-request metadata for the context.
13151334
13161335 Returns:
13171336 The new invocation context.
@@ -1343,6 +1362,7 @@ def _new_invocation_context(
13431362 live_request_queue = live_request_queue ,
13441363 run_config = run_config ,
13451364 resumability_config = self .resumability_config ,
1365+ metadata = metadata ,
13461366 )
13471367
13481368 def _new_invocation_context_for_live (
0 commit comments