# Events

<div class="language-support-tag">
  <span class="lst-supported">Supported in ADK</span><span class="lst-python">Python v0.1.0</span><span class="lst-typescript">TypeScript v0.2.0</span><span class="lst-go">Go v0.1.0</span><span class="lst-java">Java v0.1.0</span><span class="lst-kotlin">Kotlin v0.1.0</span>
</div>

Events are the fundamental units of information flow within the Agent Development Kit (ADK). They represent every significant occurrence during an agent's interaction lifecycle, from initial user input to the final response and all the steps in between. Understanding events is crucial because they are the primary way components communicate, state is managed, and control flow is directed.

## What Events Are and Why They Matter

An `Event` in ADK is an immutable record representing a specific point in the agent's execution. It captures user messages, agent replies, requests to use tools (function calls), tool results, state changes, control signals, and errors.

=== "Python"
    Technically, it's an instance of the `google.adk.events.Event` class, which builds upon the basic `LlmResponse` structure by adding essential ADK-specific metadata and an `actions` payload.

    ```python
    # Conceptual Structure of an Event (Python)
    # from google.adk.events import Event, EventActions
    # from google.genai import types

    # class Event(LlmResponse): # Simplified view
    #     # --- LlmResponse fields ---
    #     content: Optional[types.Content]
    #     partial: Optional[bool]
    #     # ... other response fields ...

    #     # --- ADK specific additions ---
    #     author: str          # 'user' or agent name
    #     invocation_id: str   # ID for the whole interaction run
    #     id: str              # Unique ID for this specific event
    #     timestamp: float     # Creation time
    #     actions: EventActions # Important for side-effects & control
    #     branch: Optional[str] # Hierarchy path
    #     # ...
    ```

=== "TypeScript"
    In TypeScript, this is an interface of type `Event`.

    ```typescript
    import {Content} from '@google/genai';

    /**
     * Conceptual Structure of an Event (TypeScript)
     */
    export interface Event extends LlmResponse {
      /** Unique ID for this specific event. */
      id: string;
      /** ID for the whole interaction run. */
      invocationId: string;
      /** 'user' or agent name. */
      author?: string;
      /** Important for side-effects & control. */
      actions: EventActions;
      /** Creation time. */
      timestamp: number;
      /** Is it streaming output? */
      partial?: boolean;
      /** Is the turn finished? */
      turnComplete?: boolean;
      /** Hierarchy path. */
      branch?: string;
      /** List of IDs for long-running tools. */
      longRunningToolIds?: string[];
      /** The content of the response. */
      content?: Content;
      // ... other LlmResponse fields like errorCode, errorMessage
    }
    ```

=== "Go"
    In Go, this is a struct of type `google.golang.org/adk/session.Event`.

    ```go
    // Conceptual Structure of an Event (Go - See session/session.go)
    // Simplified view based on the session.Event struct
    type Event struct {
        // --- Fields from embedded model.LLMResponse ---
        model.LLMResponse

        // --- ADK specific additions ---
        Author       string         // 'user' or agent name
        InvocationID string         // ID for the whole interaction run
        ID           string         // Unique ID for this specific event
        Timestamp    time.Time      // Creation time
        Actions      EventActions   // Important for side-effects & control
        Branch       string         // Hierarchy path
        // ... other fields
    }

    // model.LLMResponse contains the Content field
    type LLMResponse struct {
        Content *genai.Content
        // ... other fields
    }
    ```

=== "Java"
    In Java, this is an instance of the `com.google.adk.events.Event` class. It also builds upon a basic response structure by adding essential ADK-specific metadata and an `actions` payload.

    ```java
    // Conceptual Structure of an Event (Java - See com.google.adk.events.Event.java)
    // Simplified view based on the provided com.google.adk.events.Event.java
    // public class Event extends JsonBaseModel {
    //     // --- Fields analogous to LlmResponse ---
    //     private Optional<Content> content;
    //     private Optional<Boolean> partial;
    //     // ... other response fields like errorCode, errorMessage ...

    //     // --- ADK specific additions ---
    //     private String author;         // 'user' or agent name
    //     private String invocationId;   // ID for the whole interaction run
    //     private String id;             // Unique ID for this specific event
    //     private long timestamp;        // Creation time (epoch milliseconds)
    //     private EventActions actions;  // Important for side-effects & control
    //     private Optional<String> branch; // Hierarchy path
    //     // ... other fields like turnComplete, longRunningToolIds etc.
    // }
    ```

=== "Kotlin"
    In Kotlin, this is an instance of the `com.google.adk.kt.events.Event` class.

    ```kotlin
    // Conceptual Structure of an Event (Kotlin)
    // data class Event(
    //     val author: String,
    //     val content: Content? = null,
    //     val actions: EventActions = EventActions(),
    //     val invocationId: String? = null,
    //     val branch: String? = null,
    //     val timestamp: Long = Clock.System.now().toEpochMilliseconds(),
    //     val id: String = Uuid.random(),
    //     val partial: Boolean = false,
    //     val turnComplete: Boolean = false,
    //     val longRunningToolIds: Set<String> = emptySet()
    // )
    ```



Events are central to ADK's operation for several key reasons:

1.  **Communication:** They serve as the standard message format between the user interface, the `Runner`, agents, the LLM, and tools. Everything flows as an `Event`.

2.  **Signaling State & Artifact Changes:** Events carry instructions for state modifications and track artifact updates. The `SessionService` uses these signals to ensure persistence. In Python changes are signaled via `event.actions.state_delta` and `event.actions.artifact_delta`.

3.  **Control Flow:** Specific fields like `event.actions.transfer_to_agent` or `event.actions.escalate` act as signals that direct the framework, determining which agent runs next or if a loop should terminate.

4.  **History & Observability:** The sequence of events recorded in `session.events` provides a complete, chronological history of an interaction, invaluable for debugging, auditing, and understanding agent behavior step-by-step.

In essence, the entire process, from a user's query to the agent's final answer, is orchestrated through the generation, interpretation, and processing of `Event` objects.


## Understanding and Using Events

As a developer, you'll primarily interact with the stream of events yielded by the `Runner`. Here's how to understand and extract information from them:

!!! Note
    The specific parameters or method names for the primitives may vary slightly by SDK language (e.g., `event.content()` in Python, `event.content().get().parts()` in Java). Refer to the language-specific API documentation for details.

### Identifying Event Origin and Type

Quickly determine what an event represents by checking:

*   **Who sent it? (`event.author`)**
    *   `'user'`: Indicates input directly from the end-user.
    *   `'AgentName'`: Indicates output or action from a specific agent (e.g., `'WeatherAgent'`, `'SummarizerAgent'`).
*   **What's the main payload? (`event.content` and `event.content.parts`)**
    *   **Text:** Indicates a conversational message. For Python, check if `event.content.parts[0].text` exists. For Java, check if `event.content()` is present, its `parts()` are present and not empty, and the first part's `text()` is present.
    *   **Tool Call Request:** Check `event.get_function_calls()`. If not empty, the LLM is asking to execute one or more tools. Each item in the list has `.name` and `.args`.
    *   **Tool Result:** Check `event.get_function_responses()`. If not empty, this event carries the result(s) from tool execution(s). Each item has `.name` and `.response` (the dictionary returned by the tool). *Note:* For history structuring, the `role` inside the `content` is often `'user'`, but the event `author` is typically the agent that requested the tool call.

*   **Is it streaming output? (`event.partial`)**
    Indicates whether this is an incomplete chunk of text from the LLM.
    *   `True`: More text will follow.
    *   `False` or `None`/`Optional.empty()`: This part of the content is complete (though the overall turn might not be finished if `turn_complete` is also false).

=== "Python"

    ```python
    # Pseudocode: Basic event identification (Python)
    # async for event in runner.run_async(...):
    #     print(f"Event from: {event.author}")
    #
    #     if event.content and event.content.parts:
    #         if event.get_function_calls():
    #             print("  Type: Tool Call Request")
    #         elif event.get_function_responses():
    #             print("  Type: Tool Result")
    #         elif event.content.parts[0].text:
    #             if event.partial:
    #                 print("  Type: Streaming Text Chunk")
    #             else:
    #                 print("  Type: Complete Text Message")
    #         else:
    #             print("  Type: Other Content (e.g., code result)")
    #     elif event.actions and (event.actions.state_delta or event.actions.artifact_delta):
    #         print("  Type: State/Artifact Update")
    #     else:
    #         print("  Type: Control Signal or Other")
    ```

=== "TypeScript"

    ```typescript
    // Pseudocode: Basic event identification (TypeScript)
    import {
      Event,
      getFunctionCalls,
      getFunctionResponses
    } from '@google/adk';

    export async function processEvents(runnerEvents: AsyncIterable<Event>) {
      for await (const event of runnerEvents) {
        console.log(`Event from: ${event.author}`);

        if (event.content && event.content.parts && event.content.parts.length > 0) {
          if (getFunctionCalls(event).length > 0) {
            console.log('  Type: Tool Call Request');
          } else if (getFunctionResponses(event).length > 0) {
            console.log('  Type: Tool Result');
          } else if (event.content.parts[0].text) {
            if (event.partial) {
              console.log('  Type: Streaming Text Chunk');
            } else {
              console.log('  Type: Complete Text Message');
            }
          } else {
            console.log('  Type: Other Content (e.g., code result)');
          }
        } else if (
          event.actions &&
          (Object.keys(event.actions.stateDelta).length > 0 ||
            Object.keys(event.actions.artifactDelta).length > 0)
        ) {
          console.log('  Type: State/Artifact Update');
        } else {
          console.log('  Type: Control Signal or Other');
        }
      }
    }
    ```

=== "Go"

    ```go
      // Pseudocode: Basic event identification (Go)
    import (
      "fmt"
      "google.golang.org/adk/session"
      "google.golang.org/genai"
    )

    func hasFunctionCalls(content *genai.Content) bool {
      if content == nil {
        return false
      }
      for _, part := range content.Parts {
        if part.FunctionCall != nil {
          return true
        }
      }
      return false
    }

    func hasFunctionResponses(content *genai.Content) bool {
      if content == nil {
        return false
      }
      for _, part := range content.Parts {
        if part.FunctionResponse != nil {
          return true
        }
      }
      return false
    }

    func processEvents(events <-chan *session.Event) {
      for event := range events {
        fmt.Printf("Event from: %s\n", event.Author)

        if event.LLMResponse != nil && event.LLMResponse.Content != nil {
          if hasFunctionCalls(event.LLMResponse.Content) {
            fmt.Println("  Type: Tool Call Request")
          } else if hasFunctionResponses(event.LLMResponse.Content) {
            fmt.Println("  Type: Tool Result")
          } else if len(event.LLMResponse.Content.Parts) > 0 {
            if event.LLMResponse.Content.Parts[0].Text != "" {
              if event.LLMResponse.Partial {
                fmt.Println("  Type: Streaming Text Chunk")
              } else {
                fmt.Println("  Type: Complete Text Message")
              }
            } else {
              fmt.Println("  Type: Other Content (e.g., code result)")
            }
          }
        } else if len(event.Actions.StateDelta) > 0 {
          fmt.Println("  Type: State Update")
        } else {
          fmt.Println("  Type: Control Signal or Other")
        }
      }
    }

    ```

=== "Java"

    ```java
    // Pseudocode: Basic event identification (Java)
    // import com.google.genai.types.Content;
    // import com.google.adk.events.Event;
    // import com.google.adk.events.EventActions;

    // runner.runAsync(...).forEach(event -> { // Assuming a synchronous stream or reactive stream
    //     System.out.println("Event from: " + event.author());
    //
    //     if (event.content().isPresent()) {
    //         Content content = event.content().get();
    //         if (!event.functionCalls().isEmpty()) {
    //             System.out.println("  Type: Tool Call Request");
    //         } else if (!event.functionResponses().isEmpty()) {
    //             System.out.println("  Type: Tool Result");
    //         } else if (content.parts().isPresent() && !content.parts().get().isEmpty() &&
    //                    content.parts().get().get(0).text().isPresent()) {
    //             if (event.partial().orElse(false)) {
    //                 System.out.println("  Type: Streaming Text Chunk");
    //             } else {
    //                 System.out.println("  Type: Complete Text Message");
    //             }
    //         } else {
    //             System.out.println("  Type: Other Content (e.g., code result)");
    //         }
    //     } else if (event.actions() != null &&
    //                ((event.actions().stateDelta() != null && !event.actions().stateDelta().isEmpty()) ||
    //                 (event.actions().artifactDelta() != null && !event.actions().artifactDelta().isEmpty()))) {
    //         System.out.println("  Type: State/Artifact Update");
    //     } else {
    //         System.out.println("  Type: Control Signal or Other");
    //     }
    // });
    ```

=== "Kotlin"

    ```kotlin
    // Pseudocode: Basic event identification (Kotlin)
    // runner.runAsync(...).collect { event ->
    //     println("Event from: ${event.author}")
    //
    //     val content = event.content
    //     if (content != null && content.parts.isNotEmpty()) {
    //         if (event.functionCalls().isNotEmpty()) {
    //             println("  Type: Tool Call Request")
    //         } else if (event.functionResponses().isNotEmpty()) {
    //             println("  Type: Tool Result")
    //         } else if (content.parts[0].text != null) {
    //             if (event.partial) {
    //                 println("  Type: Streaming Text Chunk")
    //             } else {
    //                 println("  Type: Complete Text Message")
    //             }
    //         } else {
    //             println("  Type: Other Content (e.g., code result)")
    //         }
    //     } else if (event.actions.stateDelta.isNotEmpty() || event.actions.artifactDelta.isNotEmpty()) {
    //         println("  Type: State/Artifact Update")
    //     } else {
    //         println("  Type: Control Signal or Other")
    //     }
    // }
    ```

### Extracting Key Information

Once you know the event type, access the relevant data:

*   **Text Content:**
    Always check for the presence of content and parts before accessing text. In Python its `text = event.content.parts[0].text`.

*   **Function Call Details:**

    === "Python"

        ```python
        calls = event.get_function_calls()
        if calls:
            for call in calls:
                tool_name = call.name
                arguments = call.args # This is usually a dictionary
                print(f"  Tool: {tool_name}, Args: {arguments}")
                # Application might dispatch execution based on this
        ```

    === "TypeScript"

        ```typescript
        export function handleFunctionCalls(event: Event) {
            const calls = getFunctionCalls(event);
            if (calls.length > 0) {
                for (const call of calls) {
                    const toolName = call.name;
                    const argumentsDict = call.args; // This is an object
                    console.log(`  Tool: ${toolName}, Args: ${JSON.stringify(argumentsDict)}`);
                }
            }
        }
        ```

    === "Go"

        ```go
        import (
            "fmt"
            "google.golang.org/adk/session"
            "google.golang.org/genai"
        )

        func handleFunctionCalls(event *session.Event) {
            if event.LLMResponse == nil || event.LLMResponse.Content == nil {
                return
            }
            calls := event.Content.FunctionCalls()
            if len(calls) > 0 {
                for _, call := range calls {
                    toolName := call.Name
                    arguments := call.Args
                    fmt.Printf("  Tool: %s, Args: %v\n", toolName, arguments)
                    // Application might dispatch execution based on this
                }
            }
        }
        ```

    === "Java"

        ```java
        import com.google.genai.types.FunctionCall;
        import com.google.common.collect.ImmutableList;
        import java.util.Map;

        ImmutableList<FunctionCall> calls = event.functionCalls(); // from Event.java
        if (!calls.isEmpty()) {
          for (FunctionCall call : calls) {
            String toolName = call.name().get();
            // args is Optional<Map<String, Object>>
            Map<String, Object> arguments = call.args().get();
                   System.out.println("  Tool: " + toolName + ", Args: " + arguments);
            // Application might dispatch execution based on this
          }
        }
        ```

*   **Function Response Details:**

    === "Python"

        ```python
        responses = event.get_function_responses()
        if responses:
            for response in responses:
                tool_name = response.name
                result_dict = response.response # The dictionary returned by the tool
                print(f"  Tool Result: {tool_name} -> {result_dict}")
        ```

    === "TypeScript"

        ```typescript
        // Pseudocode: Handle function responses (TypeScript)
        export function handleFunctionResponses(event: Event) {
            const responses = getFunctionResponses(event);
            if (responses.length > 0) {
                for (const response of responses) {
                    const toolName = response.name;
                    const result = response.response; // The object returned by the tool
                    console.log(`  Tool Result: ${toolName} -> ${JSON.stringify(result)}`);
                }
            }
        }
        ```

    === "Go"

        ```go
        import (
            "fmt"
            "google.golang.org/adk/session"
            "google.golang.org/genai"
        )

        func handleFunctionResponses(event *session.Event) {
            if event.LLMResponse == nil || event.LLMResponse.Content == nil {
                return
            }
            responses := event.Content.FunctionResponses()
            if len(responses) > 0 {
                for _, response := range responses {
                    toolName := response.Name
                    result := response.Response
                    fmt.Printf("  Tool Result: %s -> %v\n", toolName, result)
                }
            }
        }
        ```

    === "Java"

        ```java
        import com.google.genai.types.FunctionResponse;
        import com.google.common.collect.ImmutableList;
        import java.util.Map;

        ImmutableList<FunctionResponse> responses = event.functionResponses(); // from Event.java
        if (!responses.isEmpty()) {
            for (FunctionResponse response : responses) {
                String toolName = response.name().get();
                Map<String, String> result= response.response().get(); // Check before getting the response
                System.out.println("  Tool Result: " + toolName + " -> " + result);
            }
        }
        ```

*   **Identifiers:**
    *   `event.id`: Unique ID for this specific event instance.
    *   `event.invocation_id`: ID for the entire user-request-to-final-response cycle this event belongs to. Useful for logging and tracing.

### Detecting Actions and Side Effects

The `event.actions` object signals changes that occurred or should occur. Always check if `event.actions` and it's fields/ methods exists before accessing them.

*   **State Changes:** Gives you a collection of key-value pairs that were modified in the session state during the step that produced this event.

    === "Python"
        `delta = event.actions.state_delta` (a dictionary of `{key: value}` pairs).
        ```python
        if event.actions and event.actions.state_delta:
            print(f"  State changes: {event.actions.state_delta}")
            # Update local UI or application state if necessary
        ```

    === "TypeScript"
        `delta = event.actions.stateDelta` (an object of `{key: value}` pairs).
        ```typescript
        export function handleStateChanges(event: Event) {
            if (event.actions && Object.keys(event.actions.stateDelta).length > 0) {
                console.log(`  State changes: ${JSON.stringify(event.actions.stateDelta)}`);
                // Update local UI or application state if necessary
            }
        }
        ```

    === "Go"
        `delta := event.Actions.StateDelta` (a `map[string]any`)
        ```go
        import (
            "fmt"
            "google.golang.org/adk/session"
        )

        func handleStateChanges(event *session.Event) {
            if len(event.Actions.StateDelta) > 0 {
                fmt.Printf("  State changes: %v\n", event.Actions.StateDelta)
                // Update local UI or application state if necessary
            }
        }
        ```

    === "Java"
        `ConcurrentMap<String, Object> delta = event.actions().stateDelta();`

        ```java
        import java.util.concurrent.ConcurrentMap;
        import com.google.adk.events.EventActions;

        EventActions actions = event.actions(); // Assuming event.actions() is not null
        if (actions != null && actions.stateDelta() != null && !actions.stateDelta().isEmpty()) {
            ConcurrentMap<String, Object> stateChanges = actions.stateDelta();
            System.out.println("  State changes: " + stateChanges);
            // Update local UI or application state if necessary
        }
        ```

*   **Artifact Saves:** Gives you a collection indicating which artifacts were saved and their new version number (or relevant `Part` information).

    === "Python"
        `artifact_changes = event.actions.artifact_delta` (a dictionary of `{filename: version}`).
        ```python
        if event.actions and event.actions.artifact_delta:
            print(f"  Artifacts saved: {event.actions.artifact_delta}")
            # UI might refresh an artifact list
        ```

    === "TypeScript"
        `artifact_changes = event.actions.artifactDelta` (an object of `{filename: version}`).
        ```typescript
        export function handleArtifactChanges(event: Event) {
            if (event.actions && Object.keys(event.actions.artifactDelta).length > 0) {
                console.log(`  Artifacts saved: ${JSON.stringify(event.actions.artifactDelta)}`);
                // UI might refresh an artifact list
            }
        }
        ```

    === "Go"
        `artifactChanges := event.Actions.ArtifactDelta` (a `map[string]artifact.Artifact`)
        ```go
        import (
            "fmt"
            "google.golang.org/adk/artifact"
            "google.golang.org/adk/session"
        )

        func handleArtifactChanges(event *session.Event) {
            if len(event.Actions.ArtifactDelta) > 0 {
                fmt.Printf("  Artifacts saved: %v\n", event.Actions.ArtifactDelta)
                // UI might refresh an artifact list
                // Iterate through event.Actions.ArtifactDelta to get filename and artifact.Artifact details
                for filename, art := range event.Actions.ArtifactDelta {
                    fmt.Printf("    Filename: %s, Version: %d, MIMEType: %s\n", filename, art.Version, art.MIMEType)
                }
            }
        }
        ```

    === "Java"
        `ConcurrentMap<String, Part> artifactChanges = event.actions().artifactDelta();`

        ```java
        import java.util.concurrent.ConcurrentMap;
        import com.google.genai.types.Part;
        import com.google.adk.events.EventActions;

        EventActions actions = event.actions(); // Assuming event.actions() is not null
        if (actions != null && actions.artifactDelta() != null && !actions.artifactDelta().isEmpty()) {
            ConcurrentMap<String, Part> artifactChanges = actions.artifactDelta();
            System.out.println("  Artifacts saved: " + artifactChanges);
            // UI might refresh an artifact list
            // Iterate through artifactChanges.entrySet() to get filename and Part details
        }
        ```

*   **Control Flow Signals:** Check boolean flags or string values:

    === "Python"
        *   `event.actions.transfer_to_agent` (string): Control should pass to the named agent.
        *   `event.actions.escalate` (bool): A loop should terminate.
        *   `event.actions.skip_summarization` (bool): A tool result should not be summarized by the LLM.
        ```python
        if event.actions:
            if event.actions.transfer_to_agent:
                print(f"  Signal: Transfer to {event.actions.transfer_to_agent}")
            if event.actions.escalate:
                print("  Signal: Escalate (terminate loop)")
            if event.actions.skip_summarization:
                print("  Signal: Skip summarization for tool result")
        ```

    === "TypeScript"
        *   `event.actions.transferToAgent` (string): Control should pass to the named agent.
        *   `event.actions.escalate` (boolean): A loop should terminate.
        *   `event.actions.skipSummarization` (boolean): A tool result should not be summarized by the LLM.
        ```typescript
        export function handleControlFlow(event: Event) {
            if (event.actions) {
                if (event.actions.transferToAgent) {
                    console.log(`  Signal: Transfer to ${event.actions.transferToAgent}`);
                }
                if (event.actions.escalate) {
                    console.log('  Signal: Escalate (terminate loop)');
                }
                if (event.actions.skipSummarization) {
                    console.log('  Signal: Skip summarization for tool result');
                }
            }
        }
        ```

    === "Go"
        *   `event.Actions.TransferToAgent` (string): Control should pass to the named agent.
        *   `event.Actions.Escalate` (bool): A loop should terminate.
        *   `event.Actions.SkipSummarization` (bool): A tool result should not be summarized by the LLM.
        ```go
        import (
            "fmt"
            "google.golang.org/adk/session"
        )

        func handleControlFlow(event *session.Event) {
            if event.Actions.TransferToAgent != "" {
                fmt.Printf("  Signal: Transfer to %s\n", event.Actions.TransferToAgent)
            }
            if event.Actions.Escalate {
                fmt.Println("  Signal: Escalate (terminate loop)")
            }
            if event.Actions.SkipSummarization {
                fmt.Println("  Signal: Skip summarization for tool result")
            }
        }
        ```

    === "Java"
        *   `event.actions().transferToAgent()` (returns `Optional<String>`): Control should pass to the named agent.
        *   `event.actions().escalate()` (returns `Optional<Boolean>`): A loop should terminate.
        *   `event.actions().skipSummarization()` (returns `Optional<Boolean>`): A tool result should not be summarized by the LLM.

        ```java
        import com.google.adk.events.EventActions;
        import java.util.Optional;

        EventActions actions = event.actions(); // Assuming event.actions() is not null
        if (actions != null) {
            Optional<String> transferAgent = actions.transferToAgent();
            if (transferAgent.isPresent()) {
                System.out.println("  Signal: Transfer to " + transferAgent.get());
            }

            Optional<Boolean> escalate = actions.escalate();
            if (escalate.orElse(false)) { // or escalate.isPresent() && escalate.get()
                System.out.println("  Signal: Escalate (terminate loop)");
            }

            Optional<Boolean> skipSummarization = actions.skipSummarization();
            if (skipSummarization.orElse(false)) { // or skipSummarization.isPresent() && skipSummarization.get()
                System.out.println("  Signal: Skip summarization for tool result");
            }
        }
        ```

### Determining if an Event is a "Final" Response

Use the built-in helper method `event.is_final_response()` to identify events suitable for display as the agent's complete output for a turn.

*   **Purpose:** Filters out intermediate steps (like tool calls, partial streaming text, internal state updates) from the final user-facing message(s).
*   **When `True`?**
    1.  The event contains a tool result (`function_response`) and `skip_summarization` is `True`.
    2.  The event contains a tool call (`function_call`) for a tool marked as `is_long_running=True`. In Java, check if the `longRunningToolIds` list is empty:
        *   `event.longRunningToolIds().isPresent() && !event.longRunningToolIds().get().isEmpty()` is `true`.
    3.  OR, **all** of the following are met:
        *   No function calls (`get_function_calls()` is empty).
        *   No function responses (`get_function_responses()` is empty).
        *   Not a partial stream chunk (`partial` is not `True`).
        *   Doesn't end with a code execution result that might need further processing/display.
*   **Usage:** Filter the event stream in your application logic.

    === "Python"
        ```python
        # Pseudocode: Handling final responses in application (Python)
        # full_response_text = ""
        # async for event in runner.run_async(...):
        #     # Accumulate streaming text if needed...
        #     if event.partial and event.content and event.content.parts and event.content.parts[0].text:
        #         full_response_text += event.content.parts[0].text
        #
        #     # Check if it's a final, displayable event
        #     if event.is_final_response():
        #         print("\n--- Final Output Detected ---")
        #         if event.content and event.content.parts and event.content.parts[0].text:
        #              # If it's the final part of a stream, use accumulated text
        #              final_text = full_response_text + (event.content.parts[0].text if not event.partial else "")
        #              print(f"Display to user: {final_text.strip()}")
        #              full_response_text = "" # Reset accumulator
        #         elif event.actions and event.actions.skip_summarization and event.get_function_responses():
        #              # Handle displaying the raw tool result if needed
        #              response_data = event.get_function_responses()[0].response
        #              print(f"Display raw tool result: {response_data}")
        #         elif hasattr(event, 'long_running_tool_ids') and event.long_running_tool_ids:
        #              print("Display message: Tool is running in background...")
        #         else:
        #              # Handle other types of final responses if applicable
        #              print("Display: Final non-textual response or signal.")
        ```

    === "TypeScript"
        ```typescript
        // Pseudocode: Handling final responses in application (TypeScript)
        import {
            Event,
            getFunctionResponses,
            isFinalResponse,
            stringifyContent
        } from '@google/adk';

        async function handleFinalResponses(runnerEvents: AsyncIterable<Event>) {
            let fullResponseText = '';

            for await (const event of runnerEvents) {
                // Accumulate streaming text if needed...
                if (event.partial) {
                    fullResponseText += stringifyContent(event);
                }

                // Check if it's a final, displayable event
                if (isFinalResponse(event)) {
                    console.log('\n--- Final Output Detected ---');

                    const eventText = stringifyContent(event);
                    if (fullResponseText || eventText) {
                        // If it's the final part of a stream (or a single message), use accumulated text
                        const finalText = fullResponseText + (event.partial ? '' : eventText);
                        console.log(`Display to user: ${finalText.trim()}`);
                        fullResponseText = ''; // Reset accumulator
                    } else if (
                        event.actions?.skipSummarization &&
                        getFunctionResponses(event).length > 0
                    ) {
                        // Handle displaying the raw tool result if needed
                        const responseData = getFunctionResponses(event)[0].response;
                        console.log(`Display raw tool result: ${JSON.stringify(responseData)}`);
                    } else if (event.longRunningToolIds && event.longRunningToolIds.length > 0) {
                        console.log('Display message: Tool is running in background...');
                    } else {
                        // Handle other types of final responses if applicable
                        console.log('Display: Final non-textual response or signal.');
                    }
                }
            }
        }
        ```

    === "Go"

        ```go
        // Pseudocode: Handling final responses in application (Go)
        import (
            "fmt"
            "strings"
            "google.golang.org/adk/session"
            "google.golang.org/genai"
        )

        // isFinalResponse checks if an event is a final response suitable for display.
        func isFinalResponse(event *session.Event) bool {
            if event.LLMResponse != nil {
                // Condition 1: Tool result with skip summarization.
                if event.LLMResponse.Content != nil && len(event.LLMResponse.Content.FunctionResponses()) > 0 && event.Actions.SkipSummarization {
                    return true
                }
                // Condition 2: Long-running tool call.
                if len(event.LongRunningToolIDs) > 0 {
                    return true
                }
                // Condition 3: A complete message without tool calls or responses.
                if (event.LLMResponse.Content == nil ||
                    (len(event.LLMResponse.Content.FunctionCalls()) == 0 && len(event.LLMResponse.Content.FunctionResponses()) == 0)) &&
                    !event.LLMResponse.Partial {
                    return true
                }
            }
            return false
        }

        func handleFinalResponses() {
            var fullResponseText strings.Builder
            // for event := range runner.Run(...) { // Example loop
            // 	// Accumulate streaming text if needed...
            // 	if event.LLMResponse != nil && event.LLMResponse.Partial && event.LLMResponse.Content != nil {
            // 		if len(event.LLMResponse.Content.Parts) > 0 && event.LLMResponse.Content.Parts[0].Text != "" {
            // 			fullResponseText.WriteString(event.LLMResponse.Content.Parts[0].Text)
            // 		}
            // 	}
            //
            // 	// Check if it's a final, displayable event
            // 	if isFinalResponse(event) {
            // 		fmt.Println("\n--- Final Output Detected ---")
            // 		if event.LLMResponse != nil && event.LLMResponse.Content != nil {
            // 			if len(event.LLMResponse.Content.Parts) > 0 && event.LLMResponse.Content.Parts[0].Text != "" {
            // 				// If it's the final part of a stream, use accumulated text
            // 				finalText := fullResponseText.String()
            // 				if !event.LLMResponse.Partial {
            // 					finalText += event.LLMResponse.Content.Parts[0].Text
            // 				}
            // 				fmt.Printf("Display to user: %s\n", strings.TrimSpace(finalText))
            // 				fullResponseText.Reset() // Reset accumulator
            // 			}
            // 		} else if event.Actions.SkipSummarization && event.LLMResponse.Content != nil && len(event.LLMResponse.Content.FunctionResponses()) > 0 {
            // 			// Handle displaying the raw tool result if needed
            // 			responseData := event.LLMResponse.Content.FunctionResponses()[0].Response
            // 			fmt.Printf("Display raw tool result: %v\n", responseData)
            // 		} else if len(event.LongRunningToolIDs) > 0 {
            // 			fmt.Println("Display message: Tool is running in background...")
            // 		} else {
            // 			// Handle other types of final responses if applicable
            // 			fmt.Println("Display: Final non-textual response or signal.")
            // 		}
            // 	}
            // }
        }
        ```

    === "Java"
        ```java
        // Pseudocode: Handling final responses in application (Java)
        import com.google.adk.events.Event;
        import com.google.genai.types.Content;
        import com.google.genai.types.FunctionResponse;
        import java.util.Map;

        StringBuilder fullResponseText = new StringBuilder();
        runner.run(...).forEach(event -> { // Assuming a stream of events
             // Accumulate streaming text if needed...
             if (event.partial().orElse(false) && event.content().isPresent()) {
                 event.content().flatMap(Content::parts).ifPresent(parts -> {
                     if (!parts.isEmpty() && parts.get(0).text().isPresent()) {
                         fullResponseText.append(parts.get(0).text().get());
                    }
                 });
             }

             // Check if it's a final, displayable event
             if (event.finalResponse()) { // Using the method from Event.java
                 System.out.println("\n--- Final Output Detected ---");
                 if (event.content().isPresent() &&
                     event.content().flatMap(Content::parts).map(parts -> !parts.isEmpty() && parts.get(0).text().isPresent()).orElse(false)) {
                     // If it's the final part of a stream, use accumulated text
                     String eventText = event.content().get().parts().get().get(0).text().get();
                     String finalText = fullResponseText.toString() + (event.partial().orElse(false) ? "" : eventText);
                     System.out.println("Display to user: " + finalText.trim());
                     fullResponseText.setLength(0); // Reset accumulator
                 } else if (event.actions() != null && event.actions().skipSummarization().orElse(false)
                            && !event.functionResponses().isEmpty()) {
                     // Handle displaying the raw tool result if needed,
                     // especially if finalResponse() was true due to other conditions
                     // or if you want to display skipped summarization results regardless of finalResponse()
                     Map<String, Object> responseData = event.functionResponses().get(0).response().get();
                     System.out.println("Display raw tool result: " + responseData);
                 } else if (event.longRunningToolIds().isPresent() && !event.longRunningToolIds().get().isEmpty()) {
                     // This case is covered by event.finalResponse()
                     System.out.println("Display message: Tool is running in background...");
                 } else {
                     // Handle other types of final responses if applicable
                     System.out.println("Display: Final non-textual response or signal.");
                 }
             }
         });
        ```

By carefully examining these aspects of an event, you can build robust applications that react appropriately to the rich information flowing through the ADK system.

## How Events Flow: Generation and Processing

Events are created at different points and processed systematically by the framework. Understanding this flow helps clarify how actions and history are managed.

*   **Generation Sources:**
    *   **User Input:** The `Runner` typically wraps initial user messages or mid-conversation inputs into an `Event` with `author='user'`.
    *   **Agent Logic:** Agents (`BaseAgent`, `LlmAgent`) explicitly `yield Event(...)` objects (setting `author=self.name`) to communicate responses or signal actions.
    *   **LLM Responses:** The ADK model integration layer translates raw LLM output (text, function calls, errors) into `Event` objects, authored by the calling agent.
    *   **Tool Results:** After a tool executes, the framework generates an `Event` containing the `function_response`. The `author` is typically the agent that requested the tool, while the `role` inside the `content` is set to `'user'` for the LLM history.


*   **Processing Flow:**
    1.  **Yield/Return:** An event is generated and yielded (Python) or returned/emitted (Java) by its source.
    2.  **Runner Receives:** The main `Runner` executing the agent receives the event.
    3.  **SessionService Processing:** The `Runner` sends the event to the configured `SessionService`. This is a critical step:
        *   **Applies Deltas:** The service merges `event.actions.state_delta` into `session.state` and updates internal records based on `event.actions.artifact_delta`. (Note: The actual artifact *saving* usually happened earlier when `context.save_artifact` was called).
        *   **Finalizes Metadata:** Assigns a unique `event.id` if not present, may update `event.timestamp`.
        *   **Persists to History:** Appends the processed event to the `session.events` list.
    4.  **External Yield:** The `Runner` yields (Python) or returns/emits (Java) the processed event outwards to the calling application (e.g., the code that invoked `runner.run_async`).

This flow ensures that state changes and history are consistently recorded alongside the communication content of each event.


## Common Event Examples (Illustrative Patterns)

Here are concise examples of typical events you might see in the stream:

*   **User Input:**
    ```json
    {
      "author": "user",
      "invocation_id": "e-xyz...",
      "content": {"parts": [{"text": "Book a flight to London for next Tuesday"}]}
      // actions usually empty
    }
    ```
*   **Agent Final Text Response:** (`is_final_response() == True`)
    ```json
    {
      "author": "TravelAgent",
      "invocation_id": "e-xyz...",
      "content": {"parts": [{"text": "Okay, I can help with that. Could you confirm the departure city?"}]},
      "partial": false,
      "turn_complete": true
      // actions might have state delta, etc.
    }
    ```
*   **Agent Streaming Text Response:** (`is_final_response() == False`)
    ```json
    {
      "author": "SummaryAgent",
      "invocation_id": "e-abc...",
      "content": {"parts": [{"text": "The document discusses three main points:"}]},
      "partial": true,
      "turn_complete": false
    }
    // ... more partial=True events follow ...
    ```
*   **Tool Call Request (by LLM):** (`is_final_response() == False`)
    ```json
    {
      "author": "TravelAgent",
      "invocation_id": "e-xyz...",
      "content": {"parts": [{"function_call": {"name": "find_airports", "args": {"city": "London"}}}]}
      // actions usually empty
    }
    ```
*   **Tool Result Provided (to LLM):** (`is_final_response()` depends on `skip_summarization`)
    ```json
    {
      "author": "TravelAgent", // Author is agent that requested the call
      "invocation_id": "e-xyz...",
      "content": {
        "role": "user", // Role for LLM history
        "parts": [{"function_response": {"name": "find_airports", "response": {"result": ["LHR", "LGW", "STN"]}}}]
      }
      // actions might have skip_summarization=True
    }
    ```
*   **State/Artifact Update Only:** (`is_final_response() == False`)
    ```json
    {
      "author": "InternalUpdater",
      "invocation_id": "e-def...",
      "content": null,
      "actions": {
        "state_delta": {"user_status": "verified"},
        "artifact_delta": {"verification_doc.pdf": 2}
      }
    }
    ```
*   **Agent Transfer Signal:** (`is_final_response() == False`)
    ```json
    {
      "author": "OrchestratorAgent",
      "invocation_id": "e-789...",
      "content": {"parts": [{"function_call": {"name": "transfer_to_agent", "args": {"agent_name": "BillingAgent"}}}]},
      "actions": {"transfer_to_agent": "BillingAgent"} // Added by framework
    }
    ```
*   **Loop Escalation Signal:** (`is_final_response() == False`)
    ```json
    {
      "author": "CheckerAgent",
      "invocation_id": "e-loop...",
      "content": {"parts": [{"text": "Maximum retries reached."}]}, // Optional content
      "actions": {"escalate": true}
    }
    ```

## Additional Context and Event Details

Beyond the core concepts, here are a few specific details about context and events that are important for certain use cases:

1.  **`ToolContext.function_call_id` (Linking Tool Actions):**
    *   When an LLM requests a tool (FunctionCall), that request has an ID. The `ToolContext` provided to your tool function includes this `function_call_id`.
    *   **Importance:** This ID is crucial for linking actions like authentication back to the specific tool request that initiated them, especially if multiple tools are called in one turn. The framework uses this ID internally.

2.  **How State/Artifact Changes are Recorded:**
    *   When you modify state or save an artifact using `CallbackContext` or `ToolContext`, these changes aren't immediately written to persistent storage.
    *   Instead, they populate the `state_delta` and `artifact_delta` fields within the `EventActions` object.
    *   This `EventActions` object is attached to the *next event* generated after the change (e.g., the agent's response or a tool result event).
    *   The `SessionService.append_event` method reads these deltas from the incoming event and applies them to the session's persistent state and artifact records. This ensures changes are tied chronologically to the event stream.

3.  **State Scope Prefixes (`app:`, `user:`, `temp:`):**
    *   When managing state via `context.state`, you can optionally use prefixes:
        *   `app:my_setting`: Suggests state relevant to the entire application (requires a persistent `SessionService`).
        *   `user:user_preference`: Suggests state relevant to the specific user across sessions (requires a persistent `SessionService`).
        *   `temp:intermediate_result` or no prefix: Typically session-specific or temporary state for the current invocation.
    *   The underlying `SessionService` determines how these prefixes are handled for persistence.

4.  **Error Events:**
    *   An `Event` can represent an error. Check the `event.error_code` and `event.error_message` fields (inherited from `LlmResponse`).
    *   Errors might originate from the LLM (e.g., safety filters, resource limits) or potentially be packaged by the framework if a tool fails critically. Check tool `FunctionResponse` content for typical tool-specific errors.
    ```json
    // Example Error Event (conceptual)
    {
      "author": "LLMAgent",
      "invocation_id": "e-err...",
      "content": null,
      "error_code": "SAFETY_FILTER_TRIGGERED",
      "error_message": "Response blocked due to safety settings.",
      "actions": {}
    }
    ```

These details provide a more complete picture for advanced use cases involving tool authentication, state persistence scope, and error handling within the event stream.

## Best Practices for Working with Events

To use events effectively in your ADK applications:

*   **Clear Authorship:** When building custom agents, ensure correct attribution for agent actions in the history. The framework generally handles authorship correctly for LLM/tool events.

    === "Python"
        Use `yield Event(author=self.name, ...)` in `BaseAgent` subclasses.

    === "TypeScript"
        When constructing an `Event` in your custom agent logic, set the author, for example: `createEvent({ author: this.name, ... })`

    === "Go"
        In custom agent `Run` methods, the framework typically handles authorship. If creating an event manually, set the author: `yield(&session.Event{Author: a.name, ...}, nil)`

    === "Java"
        When constructing an `Event` in your custom agent logic, set the author, for example: `Event.builder().author(this.getAgentName()) // ... .build();`

*   **Semantic Content & Actions:** Use `event.content` for the core message/data (text, function call/response). Use `event.actions` specifically for signaling side effects (state/artifact deltas) or control flow (`transfer`, `escalate`, `skip_summarization`).
*   **Idempotency Awareness:** Understand that the `SessionService` is responsible for applying the state/artifact changes signaled in `event.actions`. While ADK services aim for consistency, consider potential downstream effects if your application logic re-processes events.
*   **Use `is_final_response()`:** Rely on this helper method in your application/UI layer to identify complete, user-facing text responses. Avoid manually replicating its logic.
*   **Leverage History:** The session's event list is your primary debugging tool. Examine the sequence of authors, content, and actions to trace execution and diagnose issues.
*   **Use Metadata:** Use `invocation_id` to correlate all events within a single user interaction. Use `event.id` to reference specific, unique occurrences.

Treating events as structured messages with clear purposes for their content and actions is key to building, debugging, and managing complex agent behaviors in ADK.
