Skip to content

Latest commit

 

History

History
197 lines (159 loc) · 5.63 KB

File metadata and controls

197 lines (159 loc) · 5.63 KB

When and Where append_event is Called

Overview

append_event is the method that persists events to the session storage (database, in-memory, etc.). It's called by the Runner class during agent execution to save events as they are generated.

Call Flow

User Request
    ↓
runner.run_async()
    ↓
_exec_with_plugin()
    ↓
Agent generates events
    ↓
append_event() ← Called for each non-partial event
    ↓
Event persisted to session storage

Who Calls append_event?

1. Runner._exec_with_plugin() (Primary Caller)

Location: src/google/adk/runners.py:656

This is the main entry point where events are appended during agent execution.

When:

  • During normal agent execution flow
  • For each non-partial event generated by the agent
  • After early exit events from plugins

Code:

async for event in agen:
    if not event.partial:  # Only append complete events
        if self._should_append_event(event, is_live_call):
            await self.session_service.append_event(
                session=session, event=event
            )

Called for:

  • Model responses (text, function calls)
  • Tool execution results
  • Agent state changes
  • Any complete event from the agent

2. Runner._append_new_message_to_session()

Location: src/google/adk/runners.py:739

When:

  • When a new user message is added to the session
  • Called at the start of run_async() to save the user's input

Code:

event = Event(
    invocation_id=invocation_context.invocation_id,
    author='user',
    content=new_message,
)
await self.session_service.append_event(session=session, event=event)

3. Runner.rewind_async()

Location: src/google/adk/runners.py:494

When:

  • When rewinding a session to a previous point
  • Creates a rewind event that marks the rollback point

Code:

rewind_event = Event(
    invocation_id=new_invocation_context_id(),
    author='user',
    actions=EventActions(rewind_before_invocation_id=...),
)
await self.session_service.append_event(session=session, event=rewind_event)

4. Runner._exec_with_plugin() - Early Exit

Location: src/google/adk/runners.py:645

When:

  • When a plugin's before_run callback returns early exit content
  • Allows plugins to short-circuit agent execution

Code:

if isinstance(early_exit_result, types.Content):
    early_exit_event = Event(...)
    if self._should_append_event(early_exit_event, is_live_call):
        await self.session_service.append_event(
            session=session, event=early_exit_event
        )

5. Event Compaction

Location: src/google/adk/runners.py:601 (via compaction plugins)

When:

  • After an invocation completes
  • When compaction plugins merge/compress events

Code:

compaction_event = await compactor.maybe_compact_events(events=session.events)
if compaction_event:
    await self.session_service.append_event(
        session=session, event=compaction_event
    )

When Does append_event Run?

✅ Events That ARE Appended:

  1. Complete (non-partial) events from the agent
  2. User messages when they're added to the session
  3. Model responses (text, function calls)
  4. Tool execution results
  5. Rewind events when sessions are rolled back
  6. Compaction events after invocation completes
  7. Early exit events from plugins

❌ Events That Are NOT Appended:

  1. Partial events (event.partial == True)

    • These are streaming chunks that haven't completed yet
    • Only the final complete event is appended
  2. Live audio responses (in live mode)

    • Filtered out by _should_append_event()
    • Audio data is stored in artifacts instead

Example Flow in Your Postgres App Server

# 1. User sends request to /chat endpoint
POST /chat {"message": "Hi", "orderItemUUID": "..."}

# 2. Runner.run_async() is called
async for event in runner.run_async(
    user_id=user_id,
    session_id=session.id,
    new_message=new_message,
):

# 3. Inside run_async():
#    a. _append_new_message_to_session() is called
#       → append_event() saves user message ✓
#
#    b. Agent processes and generates events
#       → _exec_with_plugin() processes each event
#
#    c. For each non-partial event:
#       → append_event() saves model response ✓
#       → append_event() saves tool results ✓
#       → append_event() saves state changes ✓
#
# 4. Event is yielded to client (SSE stream)
    yield f'data: {event.model_dump_json()}\n\n'

What Happens Inside append_event?

In PostgresSessionService (src/google/adk/sessions/postgres_session_service.py:197):

  1. Validates the event and session are not null
  2. Skips if event is partial
  3. Retrieves the session from database
  4. Adds the event to the session's events list
  5. Updates the session's last_update_time
  6. Saves the updated session back to PostgreSQL
  7. Calls parent class append_event() for additional processing

Key Points

  1. append_event is called by the Runner, not directly by your code
  2. Only complete (non-partial) events are persisted
  3. Called automatically during agent execution flow
  4. Thread-safe - each request has its own session context
  5. Idempotent - can be called multiple times safely

Summary

  • Who: The Runner class calls append_event
  • When:
    • During agent execution (for each complete event)
    • When user messages are added
    • During session rewinds
    • After compaction runs
  • Where: Inside Runner._exec_with_plugin() and related methods
  • Why: To persist the conversation history to the session storage