From 3f9c04b574a44c963a55a75498ec93ce464cab86 Mon Sep 17 00:00:00 2001 From: Joe Fernandez Date: Sat, 13 Dec 2025 01:33:00 +0000 Subject: [PATCH] docs: Add TypeScript language support tags, Components (3 of 3) - add TS language support tags - re-order TS examples to be second - minor fixes: remove trailing spaces --- docs/artifacts/index.md | 576 +++++----- docs/callbacks/index.md | 48 +- docs/context/index.md | 1020 +++++++++--------- docs/events/index.md | 4 +- docs/grounding/google_search_grounding.md | 36 +- docs/grounding/vertex_ai_search_grounding.md | 12 +- docs/mcp/index.md | 2 +- docs/release-notes.md | 1 + docs/sessions/index.md | 2 +- docs/sessions/memory.md | 6 +- docs/sessions/session.md | 92 +- docs/sessions/state.md | 122 +-- 12 files changed, 964 insertions(+), 957 deletions(-) diff --git a/docs/artifacts/index.md b/docs/artifacts/index.md index 18ab46cb5..367d24f03 100644 --- a/docs/artifacts/index.md +++ b/docs/artifacts/index.md @@ -1,7 +1,7 @@ # Artifacts
- Supported in ADKPython v0.1.0Go v0.1.0Java v0.1.0 + Supported in ADKPython v0.1.0TypeScript v0.2.0Go v0.1.0Java v0.1.0
In ADK, **Artifacts** represent a crucial mechanism for managing named, versioned binary data associated either with a specific user interaction session or persistently with a user across multiple sessions. They allow your agents and tools to handle data beyond simple text strings, enabling richer interactions involving files, images, audio, and other binary formats. @@ -41,16 +41,31 @@ In ADK, **Artifacts** represent a crucial mechanism for managing named, versione print(f"Artifact Data (first 10 bytes): {image_artifact.inline_data.data[:10]}...") ``` +=== "Typescript" + + ```typescript + import type { Part } from '@google/genai'; + import { createPartFromBase64 } from '@google/genai'; + + // Assume 'imageBytes' contains the binary data of a PNG image + const imageBytes = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]); // Placeholder + + const imageArtifact: Part = createPartFromBase64(imageBytes.toString('base64'), "image/png"); + + console.log(`Artifact MIME Type: ${imageArtifact.inlineData?.mimeType}`); + // Note: Accessing raw bytes would require decoding from base64. + ``` + === "Go" ```go - import ( - "log" + import ( + "log" - "google.golang.org/genai" - ) + "google.golang.org/genai" + ) - --8<-- "examples/go/snippets/artifacts/main.go:representation" + --8<-- "examples/go/snippets/artifacts/main.go:representation" ``` === "Java" @@ -76,21 +91,6 @@ In ADK, **Artifacts** represent a crucial mechanism for managing named, versione } ``` -=== "Typescript" - - ```typescript - import type { Part } from '@google/genai'; - import { createPartFromBase64 } from '@google/genai'; - - // Assume 'imageBytes' contains the binary data of a PNG image - const imageBytes = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]); // Placeholder - - const imageArtifact: Part = createPartFromBase64(imageBytes.toString('base64'), "image/png"); - - console.log(`Artifact MIME Type: ${imageArtifact.inlineData?.mimeType}`); - // Note: Accessing raw bytes would require decoding from base64. - ``` - * **Persistence & Management:** Artifacts are not stored directly within the agent or session state. Their storage and retrieval are managed by a dedicated **Artifact Service** (an implementation of `BaseArtifactService`, defined in `google.adk.artifacts`. ADK provides various implementations, such as: * An in-memory service for testing or temporary storage (e.g., `InMemoryArtifactService` in Python, defined in `google.adk.artifacts.in_memory_artifact_service.py`). * A service for persistent storage using Google Cloud Storage (GCS) (e.g., `GcsArtifactService` in Python, defined in `google.adk.artifacts.gcs_artifact_service.py`). @@ -100,10 +100,10 @@ In ADK, **Artifacts** represent a crucial mechanism for managing named, versione While session `state` is suitable for storing small pieces of configuration or conversational context (like strings, numbers, booleans, or small dictionaries/lists), Artifacts are designed for scenarios involving binary or large data: -1. **Handling Non-Textual Data:** Easily store and retrieve images, audio clips, video snippets, PDFs, spreadsheets, or any other file format relevant to your agent's function. -2. **Persisting Large Data:** Session state is generally not optimized for storing large amounts of data. Artifacts provide a dedicated mechanism for persisting larger blobs without cluttering the session state. -3. **User File Management:** Provide capabilities for users to upload files (which can be saved as artifacts) and retrieve or download files generated by the agent (loaded from artifacts). -4. **Sharing Outputs:** Enable tools or agents to generate binary outputs (like a PDF report or a generated image) that can be saved via `save_artifact` and later accessed by other parts of the application or even in subsequent sessions (if using user namespacing). +1. **Handling Non-Textual Data:** Easily store and retrieve images, audio clips, video snippets, PDFs, spreadsheets, or any other file format relevant to your agent's function. +2. **Persisting Large Data:** Session state is generally not optimized for storing large amounts of data. Artifacts provide a dedicated mechanism for persisting larger blobs without cluttering the session state. +3. **User File Management:** Provide capabilities for users to upload files (which can be saved as artifacts) and retrieve or download files generated by the agent (loaded from artifacts). +4. **Sharing Outputs:** Enable tools or agents to generate binary outputs (like a PDF report or a generated image) that can be saved via `save_artifact` and later accessed by other parts of the application or even in subsequent sessions (if using user namespacing). 5. **Caching Binary Data:** Store the results of computationally expensive operations that produce binary data (e.g., rendering a complex chart image) as artifacts to avoid regenerating them on subsequent requests. In essence, whenever your agent needs to work with file-like binary data that needs to be persisted, versioned, or shared, Artifacts managed by an `ArtifactService` are the appropriate mechanism within ADK. @@ -142,14 +142,14 @@ Understanding artifacts involves grasping a few key components: the service that ### Artifact Service (`BaseArtifactService`) -* **Role:** The central component responsible for the actual storage and retrieval logic for artifacts. It defines *how* and *where* artifacts are persisted. +* **Role:** The central component responsible for the actual storage and retrieval logic for artifacts. It defines *how* and *where* artifacts are persisted. -* **Interface:** Defined by the abstract base class `BaseArtifactService`. Any concrete implementation must provide methods for: +* **Interface:** Defined by the abstract base class `BaseArtifactService`. Any concrete implementation must provide methods for: - * `Save Artifact`: Stores the artifact data and returns its assigned version number. - * `Load Artifact`: Retrieves a specific version (or the latest) of an artifact. - * `List Artifact keys`: Lists the unique filenames of artifacts within a given scope. - * `Delete Artifact`: Removes an artifact (and potentially all its versions, depending on implementation). + * `Save Artifact`: Stores the artifact data and returns its assigned version number. + * `Load Artifact`: Retrieves a specific version (or the latest) of an artifact. + * `List Artifact keys`: Lists the unique filenames of artifacts within a given scope. + * `Delete Artifact`: Removes an artifact (and potentially all its versions, depending on implementation). * `List versions`: Lists all available version numbers for a specific artifact filename. * **Configuration:** You provide an instance of an artifact service (e.g., `InMemoryArtifactService`, `GcsArtifactService`) when initializing the `Runner`. The `Runner` then makes this service available to agents and tools via the `InvocationContext`. @@ -176,32 +176,53 @@ Understanding artifacts involves grasping a few key components: the service that # Now, contexts within runs managed by this runner can use artifact methods ``` +=== "Typescript" + + ```typescript + import { InMemoryRunner } from '@google/adk'; + import { LlmAgent } from '@google/adk'; + import { InMemoryArtifactService } from '@google/adk'; + + // Example: Configuring the Runner with an Artifact Service + const myAgent = new LlmAgent({name: "artifact_user_agent", model: "gemini-2.5-flash"}); + const artifactService = new InMemoryArtifactService(); // Choose an implementation + const sessionService = new InMemoryArtifactService(); + + const runner = new InMemoryRunner({ + agent: myAgent, + appName: "my_artifact_app", + sessionService: sessionService, + artifactService: artifactService, // Provide the service instance here + }); + // Now, contexts within runs managed by this runner can use artifact methods + ``` + === "Go" ```go - import ( - "context" - "log" - - "google.golang.org/adk/agent/llmagent" - "google.golang.org/adk/artifactservice" - "google.golang.org/adk/llm/gemini" - "google.golang.org/adk/runner" - "google.golang.org/adk/sessionservice" - "google.golang.org/genai" + import ( + "context" + "log" + + "google.golang.org/adk/agent/llmagent" + "google.golang.org/adk/artifactservice" + "google.golang.org/adk/llm/gemini" + "google.golang.org/adk/runner" + "google.golang.org/adk/sessionservice" + "google.golang.org/genai" ) --8<-- "examples/go/snippets/artifacts/main.go:configure-runner" ``` === "Java" - + ```java import com.google.adk.agents.LlmAgent; import com.google.adk.runner.Runner; import com.google.adk.sessions.InMemorySessionService; import com.google.adk.artifacts.InMemoryArtifactService; - + // Example: Configuring the Runner with an Artifact Service LlmAgent myAgent = LlmAgent.builder() .name("artifact_user_agent") @@ -214,34 +235,13 @@ Understanding artifacts involves grasping a few key components: the service that // Now, contexts within runs managed by this runner can use artifact methods ``` -=== "Typescript" - - ```typescript - import { InMemoryRunner } from '@google/adk'; - import { LlmAgent } from '@google/adk'; - import { InMemoryArtifactService } from '@google/adk'; - - // Example: Configuring the Runner with an Artifact Service - const myAgent = new LlmAgent({name: "artifact_user_agent", model: "gemini-2.5-flash"}); - const artifactService = new InMemoryArtifactService(); // Choose an implementation - const sessionService = new InMemoryArtifactService(); - - const runner = new InMemoryRunner({ - agent: myAgent, - appName: "my_artifact_app", - sessionService: sessionService, - artifactService: artifactService, // Provide the service instance here - }); - // Now, contexts within runs managed by this runner can use artifact methods - ``` - ### Artifact Data -* **Standard Representation:** Artifact content is universally represented using the `google.genai.types.Part` object, the same structure used for parts of LLM messages. +* **Standard Representation:** Artifact content is universally represented using the `google.genai.types.Part` object, the same structure used for parts of LLM messages. -* **Key Attribute (`inline_data`):** For artifacts, the most relevant attribute is `inline_data`, which is a `google.genai.types.Blob` object containing: +* **Key Attribute (`inline_data`):** For artifacts, the most relevant attribute is `inline_data`, which is a `google.genai.types.Blob` object containing: - * `data` (`bytes`): The raw binary content of the artifact. + * `data` (`bytes`): The raw binary content of the artifact. * `mime_type` (`str`): A standard MIME type string (e.g., `'application/pdf'`, `'image/png'`, `'audio/mpeg'`) describing the nature of the binary data. **This is crucial for correct interpretation when loading the artifact.** === "Python" @@ -263,25 +263,6 @@ Understanding artifacts involves grasping a few key components: the service that print(f"Created Python artifact with MIME type: {pdf_artifact_py.inline_data.mime_type}") ``` - -=== "Go" - - ```go - import ( - "log" - "os" - - "google.golang.org/genai" - ) - - --8<-- "examples/go/snippets/artifacts/main.go:artifact-data" - ``` - -=== "Java" - - ```java - --8<-- "examples/java/snippets/src/main/java/artifacts/ArtifactDataExample.java:full_code" - ``` === "Typescript" @@ -297,29 +278,48 @@ Understanding artifacts involves grasping a few key components: the service that console.log(`Created TypeScript artifact with MIME Type: ${pdfArtifact.inlineData?.mimeType}`); ``` +=== "Go" + + ```go + import ( + "log" + "os" + + "google.golang.org/genai" + ) + + --8<-- "examples/go/snippets/artifacts/main.go:artifact-data" + ``` + +=== "Java" + + ```java + --8<-- "examples/java/snippets/src/main/java/artifacts/ArtifactDataExample.java:full_code" + ``` + ### Filename -* **Identifier:** A simple string used to name and retrieve an artifact within its specific namespace. -* **Uniqueness:** Filenames must be unique within their scope (either the session or the user namespace). +* **Identifier:** A simple string used to name and retrieve an artifact within its specific namespace. +* **Uniqueness:** Filenames must be unique within their scope (either the session or the user namespace). * **Best Practice:** Use descriptive names, potentially including file extensions (e.g., `"monthly_report.pdf"`, `"user_avatar.jpg"`), although the extension itself doesn't dictate behavior – the `mime_type` does. ### Versioning -* **Automatic Versioning:** The artifact service automatically handles versioning. When you call `save_artifact`, the service determines the next available version number (typically starting from 0 and incrementing) for that specific filename and scope. -* **Returned by `save_artifact`:** The `save_artifact` method returns the integer version number that was assigned to the newly saved artifact. -* **Retrieval:** - * `load_artifact(..., version=None)` (default): Retrieves the *latest* available version of the artifact. - * `load_artifact(..., version=N)`: Retrieves the specific version `N`. +* **Automatic Versioning:** The artifact service automatically handles versioning. When you call `save_artifact`, the service determines the next available version number (typically starting from 0 and incrementing) for that specific filename and scope. +* **Returned by `save_artifact`:** The `save_artifact` method returns the integer version number that was assigned to the newly saved artifact. +* **Retrieval:** + * `load_artifact(..., version=None)` (default): Retrieves the *latest* available version of the artifact. + * `load_artifact(..., version=N)`: Retrieves the specific version `N`. * **Listing Versions:** The `list_versions` method (on the service, not context) can be used to find all existing version numbers for an artifact. ### Namespacing (Session vs. User) -* **Concept:** Artifacts can be scoped either to a specific session or more broadly to a user across all their sessions within the application. This scoping is determined by the `filename` format and handled internally by the `ArtifactService`. +* **Concept:** Artifacts can be scoped either to a specific session or more broadly to a user across all their sessions within the application. This scoping is determined by the `filename` format and handled internally by the `ArtifactService`. -* **Default (Session Scope):** If you use a plain filename like `"report.pdf"`, the artifact is associated with the specific `app_name`, `user_id`, *and* `session_id`. It's only accessible within that exact session context. +* **Default (Session Scope):** If you use a plain filename like `"report.pdf"`, the artifact is associated with the specific `app_name`, `user_id`, *and* `session_id`. It's only accessible within that exact session context. -* **User Scope (`"user:"` prefix):** If you prefix the filename with `"user:"`, like `"user:profile.png"`, the artifact is associated only with the `app_name` and `user_id`. It can be accessed or updated from *any* session belonging to that user within the app. +* **User Scope (`"user:"` prefix):** If you prefix the filename with `"user:"`, like `"user:profile.png"`, the artifact is associated only with the `app_name` and `user_id`. It can be accessed or updated from *any* session belonging to that user within the app. === "Python" @@ -341,52 +341,52 @@ Understanding artifacts involves grasping a few key components: the service that # and scope it to app_name and user_id, making it accessible across sessions for that user. ``` +=== "Typescript" + + ```typescript + // Example illustrating namespace difference (conceptual) + + // Session-specific artifact filename + const sessionReportFilename = "summary.txt"; + + // User-specific artifact filename + const userConfigFilename = "user:settings.json"; + + // When saving 'summary.txt' via context.saveArtifact, it's tied to the current appName, userId, and sessionId. + // When saving 'user:settings.json' via context.saveArtifact, the ArtifactService implementation recognizes the "user:" prefix and scopes it to appName and userId, making it accessible across sessions for that user. + ``` + === "Go" ```go - import ( - "log" - ) + import ( + "log" + ) - --8<-- "examples/go/snippets/artifacts/main.go:namespacing" + --8<-- "examples/go/snippets/artifacts/main.go:namespacing" ``` === "Java" ```java // Example illustrating namespace difference (conceptual) - + // Session-specific artifact filename String sessionReportFilename = "summary.txt"; - + // User-specific artifact filename String userConfigFilename = "user:settings.json"; // The "user:" prefix is key - + // When saving 'summary.txt' via context.save_artifact, // it's tied to the current app_name, user_id, and session_id. // artifactService.saveArtifact(appName, userId, sessionId1, sessionReportFilename, someData); - + // When saving 'user:settings.json' via context.save_artifact, // the ArtifactService implementation should recognize the "user:" prefix // and scope it to app_name and user_id, making it accessible across sessions for that user. // artifactService.saveArtifact(appName, userId, sessionId1, userConfigFilename, someData); ``` -=== "Typescript" - - ```typescript - // Example illustrating namespace difference (conceptual) - - // Session-specific artifact filename - const sessionReportFilename = "summary.txt"; - - // User-specific artifact filename - const userConfigFilename = "user:settings.json"; - - // When saving 'summary.txt' via context.saveArtifact, it's tied to the current appName, userId, and sessionId. - // When saving 'user:settings.json' via context.saveArtifact, the ArtifactService implementation recognizes the "user:" prefix and scopes it to appName and userId, making it accessible across sessions for that user. - ``` - These core concepts work together to provide a flexible system for managing binary data within the ADK framework. ## Interacting with Artifacts (via Context Objects) @@ -423,22 +423,44 @@ Before you can use any artifact methods via the context objects, you **must** pr ``` If no `artifact_service` is configured in the `InvocationContext` (which happens if it's not passed to the `Runner`), calling `save_artifact`, `load_artifact`, or `list_artifacts` on the context objects will raise a `ValueError`. +=== "Typescript" + + ```typescript + import { LlmAgent, InMemoryRunner, InMemoryArtifactService } from '@google/adk'; + + // Your agent definition + const agent = new LlmAgent({name: "my_agent", model: "gemini-2.5-flash"}); + + // Instantiate the desired artifact service + const artifactService = new InMemoryArtifactService(); + + // Provide it to the Runner + const runner = new InMemoryRunner({ + agent: agent, + appName: "artifact_app", + sessionService: new InMemoryArtifactService(), + artifactService: artifactService, // Service must be provided here + }); + // If no artifactService is configured, calling artifact methods on context objects will throw an error. + ``` + In Java, if an `ArtifactService` instance is not available (e.g., `null`) when artifact operations are attempted, it would typically result in a `NullPointerException` or a custom error, depending on how your application is structured. Robust applications often use dependency injection frameworks to manage service lifecycles and ensure availability. + === "Go" ```go - import ( - "context" - "log" - - "google.golang.org/adk/agent/llmagent" - "google.golang.org/adk/artifactservice" - "google.golang.org/adk/llm/gemini" - "google.golang.org/adk/runner" - "google.golang.org/adk/sessionservice" - "google.golang.org/genai" - ) + import ( + "context" + "log" + + "google.golang.org/adk/agent/llmagent" + "google.golang.org/adk/artifactservice" + "google.golang.org/adk/llm/gemini" + "google.golang.org/adk/runner" + "google.golang.org/adk/sessionservice" + "google.golang.org/genai" + ) - --8<-- "examples/go/snippets/artifacts/main.go:prerequisite" + --8<-- "examples/go/snippets/artifacts/main.go:prerequisite" ``` === "Java" @@ -450,53 +472,30 @@ Before you can use any artifact methods via the context objects, you **must** pr import com.google.adk.artifacts.InMemoryArtifactService; // Or GcsArtifactService import com.google.adk.runner.Runner; import com.google.adk.sessions.InMemorySessionService; - + public class SampleArtifactAgent { - + public static void main(String[] args) { - + // Your agent definition LlmAgent agent = LlmAgent.builder() .name("my_agent") .model("gemini-2.0-flash") .build(); - + // Instantiate the desired artifact service InMemoryArtifactService artifactService = new InMemoryArtifactService(); - + // Provide it to the Runner Runner runner = new Runner(agent, "APP_NAME", artifactService, // Service must be provided here new InMemorySessionService()); - + } } ``` -=== "Typescript" - - ```typescript - import { LlmAgent, InMemoryRunner, InMemoryArtifactService } from '@google/adk'; - - // Your agent definition - const agent = new LlmAgent({name: "my_agent", model: "gemini-2.5-flash"}); - - // Instantiate the desired artifact service - const artifactService = new InMemoryArtifactService(); - - // Provide it to the Runner - const runner = new InMemoryRunner({ - agent: agent, - appName: "artifact_app", - sessionService: new InMemoryArtifactService(), - artifactService: artifactService, // Service must be provided here - }); - // If no artifactService is configured, calling artifact methods on context objects will throw an error. - ``` - In Java, if an `ArtifactService` instance is not available (e.g., `null`) when artifact operations are attempted, it would typically result in a `NullPointerException` or a custom error, depending on how your application is structured. Robust applications often use dependency injection frameworks to manage service lifecycles and ensure availability. - - ### Accessing Methods The artifact interaction methods are available directly on instances of `CallbackContext` (passed to agent and model callbacks) and `ToolContext` (passed to tool callbacks). Remember that `ToolContext` inherits from `CallbackContext`. @@ -537,22 +536,43 @@ The artifact interaction methods are available directly on instances of `Callbac # await save_generated_report_py(callback_context, report_data) ``` + === "Typescript" + + ```typescript + import type { Part } from '@google/genai'; + import { createPartFromBase64 } from '@google/genai'; + import { CallbackContext } from '@google/adk'; + + async function saveGeneratedReport(context: CallbackContext, reportBytes: Uint8Array): Promise { + /**Saves generated PDF report bytes as an artifact.*/ + const reportArtifact: Part = createPartFromBase64(reportBytes.toString('base64'), "application/pdf"); + + const filename = "generated_report.pdf"; + + try { + const version = await context.saveArtifact(filename, reportArtifact); + console.log(`Successfully saved TypeScript artifact '${filename}' as version ${version}.`); + } catch (e: any) { + console.error(`Error saving TypeScript artifact: ${e.message}. Is ArtifactService configured in Runner?`); + } + } + ``` === "Go" ```go - import ( - "log" + import ( + "log" - "google.golang.org/adk/agent" - "google.golang.org/adk/llm" - "google.golang.org/genai" - ) + "google.golang.org/adk/agent" + "google.golang.org/adk/llm" + "google.golang.org/genai" + ) - --8<-- "examples/go/snippets/artifacts/main.go:saving-artifacts" + --8<-- "examples/go/snippets/artifacts/main.go:saving-artifacts" ``` === "Java" - + ```java import com.google.adk.agents.CallbackContext; import com.google.adk.artifacts.BaseArtifactService; @@ -585,28 +605,6 @@ The artifact interaction methods are available directly on instances of `Callbac } ``` -=== "Typescript" - - ```typescript - import type { Part } from '@google/genai'; - import { createPartFromBase64 } from '@google/genai'; - import { CallbackContext } from '@google/adk'; - - async function saveGeneratedReport(context: CallbackContext, reportBytes: Uint8Array): Promise { - /**Saves generated PDF report bytes as an artifact.*/ - const reportArtifact: Part = createPartFromBase64(reportBytes.toString('base64'), "application/pdf"); - - const filename = "generated_report.pdf"; - - try { - const version = await context.saveArtifact(filename, reportArtifact); - console.log(`Successfully saved TypeScript artifact '${filename}' as version ${version}.`); - } catch (e: any) { - console.error(`Error saving TypeScript artifact: ${e.message}. Is ArtifactService configured in Runner?`); - } - } - ``` - #### Loading Artifacts * **Code Example:** @@ -651,17 +649,46 @@ The artifact interaction methods are available directly on instances of `Callbac # await process_latest_report_py(callback_context) ``` + === "Typescript" + + ```typescript + import { CallbackContext } from '@google/adk'; + + async function processLatestReport(context: CallbackContext): Promise { + /**Loads the latest report artifact and processes its data.*/ + const filename = "generated_report.pdf"; + try { + // Load the latest version + const reportArtifact = await context.loadArtifact(filename); + + if (reportArtifact?.inlineData) { + console.log(`Successfully loaded latest TypeScript artifact '${filename}'.`); + console.log(`MIME Type: ${reportArtifact.inlineData.mimeType}`); + // Process the reportArtifact.inlineData.data (base64 string) + const pdfData = Buffer.from(reportArtifact.inlineData.data, 'base64'); + console.log(`Report size: ${pdfData.length} bytes.`); + // ... further processing ... + } else { + console.log(`TypeScript artifact '${filename}' not found.`); + } + + } catch (e: any) { + console.error(`Error loading TypeScript artifact: ${e.message}. Is ArtifactService configured?`); + } + } + ``` + === "Go" ```go - import ( - "log" + import ( + "log" - "google.golang.org/adk/agent" - "google.golang.org/adk/llm" - ) + "google.golang.org/adk/agent" + "google.golang.org/adk/llm" + ) - --8<-- "examples/go/snippets/artifacts/main.go:loading-artifacts" + --8<-- "examples/go/snippets/artifacts/main.go:loading-artifacts" ``` === "Java" @@ -749,34 +776,6 @@ The artifact interaction methods are available directly on instances of `Callbac } } ``` - === "Typescript" - - ```typescript - import { CallbackContext } from '@google/adk'; - - async function processLatestReport(context: CallbackContext): Promise { - /**Loads the latest report artifact and processes its data.*/ - const filename = "generated_report.pdf"; - try { - // Load the latest version - const reportArtifact = await context.loadArtifact(filename); - - if (reportArtifact?.inlineData) { - console.log(`Successfully loaded latest TypeScript artifact '${filename}'.`); - console.log(`MIME Type: ${reportArtifact.inlineData.mimeType}`); - // Process the reportArtifact.inlineData.data (base64 string) - const pdfData = Buffer.from(reportArtifact.inlineData.data, 'base64'); - console.log(`Report size: ${pdfData.length} bytes.`); - // ... further processing ... - } else { - console.log(`TypeScript artifact '${filename}' not found.`); - } - - } catch (e: any) { - console.error(`Error loading TypeScript artifact: ${e.message}. Is ArtifactService configured?`); - } - } - ``` #### Listing Artifact Filenames @@ -809,20 +808,43 @@ The artifact interaction methods are available directly on instances of `Callbac # list_files_tool = FunctionTool(func=list_user_files_py) ``` + === "Typescript" + + ```typescript + import { ToolContext } from '@google/adk'; + + async function listUserFiles(toolContext: ToolContext): Promise { + /**Tool to list available artifacts for the user.*/ + try { + const availableFiles = await toolContext.listArtifacts(); + if (!availableFiles || availableFiles.length === 0) { + return "You have no saved artifacts."; + } else { + // Format the list for the user/LLM + const fileListStr = availableFiles.map(fname => `- ${fname}`).join("\n"); + return `Here are your available TypeScript artifacts:\n${fileListStr}`; + } + } catch (e: any) { + console.error(`Error listing TypeScript artifacts: ${e.message}. Is ArtifactService configured?`); + return "Error: Could not list TypeScript artifacts."; + } + } + ``` + === "Go" ```go - import ( - "fmt" - "log" - "strings" - - "google.golang.org/adk/agent" - "google.golang.org/adk/llm" - "google.golang.org/genai" - ) + import ( + "fmt" + "log" + "strings" + + "google.golang.org/adk/agent" + "google.golang.org/adk/llm" + "google.golang.org/genai" + ) - --8<-- "examples/go/snippets/artifacts/main.go:listing-artifacts" + --8<-- "examples/go/snippets/artifacts/main.go:listing-artifacts" ``` === "Java" @@ -903,28 +925,6 @@ The artifact interaction methods are available directly on instances of `Callbac } } ``` - === "Typescript" - - ```typescript - import { ToolContext } from '@google/adk'; - - async function listUserFiles(toolContext: ToolContext): Promise { - /**Tool to list available artifacts for the user.*/ - try { - const availableFiles = await toolContext.listArtifacts(); - if (!availableFiles || availableFiles.length === 0) { - return "You have no saved artifacts."; - } else { - // Format the list for the user/LLM - const fileListStr = availableFiles.map(fname => `- ${fname}`).join("\n"); - return `Here are your available TypeScript artifacts:\n${fileListStr}`; - } - } catch (e: any) { - console.error(`Error listing TypeScript artifacts: ${e.message}. Is ArtifactService configured?`); - return "Error: Could not list TypeScript artifacts."; - } - } - ``` These methods for saving, loading, and listing provide a convenient and consistent way to manage binary data persistence within ADK, whether using Python's context objects or directly interacting with the `BaseArtifactService` in Java, regardless of the chosen backend storage implementation. @@ -958,14 +958,29 @@ ADK provides concrete implementations of the `BaseArtifactService` interface, of # runner = Runner(..., artifact_service=in_memory_service_py) ``` + === "Typescript" + + ```typescript + import { InMemoryArtifactService } from '@google/adk'; + + // Simply instantiate the class + const inMemoryService = new InMemoryArtifactService(); + + // This instance would then be provided to your Runner. + // const runner = new InMemoryRunner({ + // /* other services */, + // artifactService: inMemoryService + // }); + ``` + === "Go" ```go - import ( - "google.golang.org/adk/artifactservice" - ) + import ( + "google.golang.org/adk/artifactservice" + ) - --8<-- "examples/go/snippets/artifacts/main.go:in-memory-service" + --8<-- "examples/go/snippets/artifacts/main.go:in-memory-service" ``` === "Java" @@ -990,21 +1005,6 @@ ADK provides concrete implementations of the `BaseArtifactService` interface, of } ``` - === "Typescript" - - ```typescript - import { InMemoryArtifactService } from '@google/adk'; - - // Simply instantiate the class - const inMemoryService = new InMemoryArtifactService(); - - // This instance would then be provided to your Runner. - // const runner = new InMemoryRunner({ - // /* other services */, - // artifactService: inMemoryService - // }); - ``` - ### GcsArtifactService @@ -1056,17 +1056,17 @@ Choosing the appropriate `ArtifactService` implementation depends on your applic To use artifacts effectively and maintainably: -* **Choose the Right Service:** Use `InMemoryArtifactService` for rapid prototyping, testing, and scenarios where persistence isn't needed. Use `GcsArtifactService` (or implement your own `BaseArtifactService` for other backends) for production environments requiring data persistence and scalability. -* **Meaningful Filenames:** Use clear, descriptive filenames. Including relevant extensions (`.pdf`, `.png`, `.wav`) helps humans understand the content, even though the `mime_type` dictates programmatic handling. Establish conventions for temporary vs. persistent artifact names. -* **Specify Correct MIME Types:** Always provide an accurate `mime_type` when creating the `types.Part` for `save_artifact`. This is critical for applications or tools that later `load_artifact` to interpret the `bytes` data correctly. Use standard IANA MIME types where possible. -* **Understand Versioning:** Remember that `load_artifact()` without a specific `version` argument retrieves the *latest* version. If your logic depends on a specific historical version of an artifact, be sure to provide the integer version number when loading. -* **Use Namespacing (`user:`) Deliberately:** Only use the `"user:"` prefix for filenames when the data truly belongs to the user and should be accessible across all their sessions. For data specific to a single conversation or session, use regular filenames without the prefix. -* **Error Handling:** - * Always check if an `artifact_service` is actually configured before calling context methods (`save_artifact`, `load_artifact`, `list_artifacts`) – they will raise a `ValueError` if the service is `None`. - * Check the return value of `load_artifact`, as it will be `None` if the artifact or version doesn't exist. Don't assume it always returns a `Part`. - * Be prepared to handle exceptions from the underlying storage service, especially with `GcsArtifactService` (e.g., `google.api_core.exceptions.Forbidden` for permission issues, `NotFound` if the bucket doesn't exist, network errors). -* **Size Considerations:** Artifacts are suitable for typical file sizes, but be mindful of potential costs and performance impacts with extremely large files, especially with cloud storage. `InMemoryArtifactService` can consume significant memory if storing many large artifacts. Evaluate if very large data might be better handled through direct GCS links or other specialized storage solutions rather than passing entire byte arrays in-memory. -* **Cleanup Strategy:** For persistent storage like `GcsArtifactService`, artifacts remain until explicitly deleted. If artifacts represent temporary data or have a limited lifespan, implement a strategy for cleanup. This might involve: - * Using GCS lifecycle policies on the bucket. - * Building specific tools or administrative functions that utilize the `artifact_service.delete_artifact` method (note: delete is *not* exposed via context objects for safety). +* **Choose the Right Service:** Use `InMemoryArtifactService` for rapid prototyping, testing, and scenarios where persistence isn't needed. Use `GcsArtifactService` (or implement your own `BaseArtifactService` for other backends) for production environments requiring data persistence and scalability. +* **Meaningful Filenames:** Use clear, descriptive filenames. Including relevant extensions (`.pdf`, `.png`, `.wav`) helps humans understand the content, even though the `mime_type` dictates programmatic handling. Establish conventions for temporary vs. persistent artifact names. +* **Specify Correct MIME Types:** Always provide an accurate `mime_type` when creating the `types.Part` for `save_artifact`. This is critical for applications or tools that later `load_artifact` to interpret the `bytes` data correctly. Use standard IANA MIME types where possible. +* **Understand Versioning:** Remember that `load_artifact()` without a specific `version` argument retrieves the *latest* version. If your logic depends on a specific historical version of an artifact, be sure to provide the integer version number when loading. +* **Use Namespacing (`user:`) Deliberately:** Only use the `"user:"` prefix for filenames when the data truly belongs to the user and should be accessible across all their sessions. For data specific to a single conversation or session, use regular filenames without the prefix. +* **Error Handling:** + * Always check if an `artifact_service` is actually configured before calling context methods (`save_artifact`, `load_artifact`, `list_artifacts`) – they will raise a `ValueError` if the service is `None`. + * Check the return value of `load_artifact`, as it will be `None` if the artifact or version doesn't exist. Don't assume it always returns a `Part`. + * Be prepared to handle exceptions from the underlying storage service, especially with `GcsArtifactService` (e.g., `google.api_core.exceptions.Forbidden` for permission issues, `NotFound` if the bucket doesn't exist, network errors). +* **Size Considerations:** Artifacts are suitable for typical file sizes, but be mindful of potential costs and performance impacts with extremely large files, especially with cloud storage. `InMemoryArtifactService` can consume significant memory if storing many large artifacts. Evaluate if very large data might be better handled through direct GCS links or other specialized storage solutions rather than passing entire byte arrays in-memory. +* **Cleanup Strategy:** For persistent storage like `GcsArtifactService`, artifacts remain until explicitly deleted. If artifacts represent temporary data or have a limited lifespan, implement a strategy for cleanup. This might involve: + * Using GCS lifecycle policies on the bucket. + * Building specific tools or administrative functions that utilize the `artifact_service.delete_artifact` method (note: delete is *not* exposed via context objects for safety). * Carefully managing filenames to allow pattern-based deletion if needed. diff --git a/docs/callbacks/index.md b/docs/callbacks/index.md index 9c117321d..e550eff95 100644 --- a/docs/callbacks/index.md +++ b/docs/callbacks/index.md @@ -1,7 +1,7 @@ # Callbacks: Observe, Customize, and Control Agent Behavior
- Supported in ADKPython v0.1.0Go v0.1.0Java v0.1.0 + Supported in ADKPython v0.1.0TypeScript v0.2.0Go v0.1.0Java v0.1.0
Callbacks are a cornerstone feature of ADK, providing a powerful mechanism to hook into an agent's execution process. They allow you to observe, customize, and even control the agent's behavior at specific, predefined points without modifying the core ADK framework code. @@ -20,26 +20,26 @@ Callbacks are a cornerstone feature of ADK, providing a powerful mechanism to ho **Why use them?** Callbacks unlock significant flexibility and enable advanced agent capabilities: -* **Observe & Debug:** Log detailed information at critical steps for monitoring and troubleshooting. -* **Customize & Control:** Modify data flowing through the agent (like LLM requests or tool results) or even bypass certain steps entirely based on your logic. -* **Implement Guardrails:** Enforce safety rules, validate inputs/outputs, or prevent disallowed operations. -* **Manage State:** Read or dynamically update the agent's session state during execution. +* **Observe & Debug:** Log detailed information at critical steps for monitoring and troubleshooting. +* **Customize & Control:** Modify data flowing through the agent (like LLM requests or tool results) or even bypass certain steps entirely based on your logic. +* **Implement Guardrails:** Enforce safety rules, validate inputs/outputs, or prevent disallowed operations. +* **Manage State:** Read or dynamically update the agent's session state during execution. * **Integrate & Enhance:** Trigger external actions (API calls, notifications) or add features like caching. !!! tip When implementing security guardrails and policies, use ADK Plugins for - better modularity and flexibility than Callbacks. For more details, see + better modularity and flexibility than Callbacks. For more details, see [Callbacks and Plugins for Security Guardrails](/adk-docs/safety/#callbacks-and-plugins-for-security-guardrails). -**How are they added:** +**How are they added:** ??? "Code" === "Python" - + ```python --8<-- "examples/python/snippets/callbacks/callback_basic.py:callback_basic" ``` - + === "Go" ```go @@ -50,7 +50,7 @@ Callbacks are a cornerstone feature of ADK, providing a powerful mechanism to ho ``` === "Java" - + ```java --8<-- "examples/java/snippets/src/main/java/callbacks/AgentWithBeforeModelCallback.java:init" ``` @@ -63,21 +63,21 @@ When the ADK framework encounters a point where a callback can run (e.g., just b **Controlling the Flow (The Core Mechanism):** The most powerful aspect of callbacks lies in how their **return value** influences the agent's subsequent actions. This is how you intercept and control the execution flow: -1. **`return None` (Allow Default Behavior):** +1. **`return None` (Allow Default Behavior):** * The specific return type can vary depending on the language. In Java, the equivalent return type is `Optional.empty()`. Refer to the API documentation for language specific guidance. - * This is the standard way to signal that your callback has finished its work (e.g., logging, inspection, minor modifications to *mutable* input arguments like `llm_request`) and that the ADK agent should **proceed with its normal operation**. - * For `before_*` callbacks (`before_agent`, `before_model`, `before_tool`), returning `None` means the next step in the sequence (running the agent logic, calling the LLM, executing the tool) will occur. + * This is the standard way to signal that your callback has finished its work (e.g., logging, inspection, minor modifications to *mutable* input arguments like `llm_request`) and that the ADK agent should **proceed with its normal operation**. + * For `before_*` callbacks (`before_agent`, `before_model`, `before_tool`), returning `None` means the next step in the sequence (running the agent logic, calling the LLM, executing the tool) will occur. * For `after_*` callbacks (`after_agent`, `after_model`, `after_tool`), returning `None` means the result just produced by the preceding step (the agent's output, the LLM's response, the tool's result) will be used as is. -2. **`return ` (Override Default Behavior):** +2. **`return ` (Override Default Behavior):** - * Returning a *specific type of object* (instead of `None`) is how you **override** the ADK agent's default behavior. The framework will use the object you return and *skip* the step that would normally follow or *replace* the result that was just generated. - * **`before_agent_callback` → `types.Content`**: Skips the agent's main execution logic (`_run_async_impl` / `_run_live_impl`). The returned `Content` object is immediately treated as the agent's final output for this turn. Useful for handling simple requests directly or enforcing access control. - * **`before_model_callback` → `LlmResponse`**: Skips the call to the external Large Language Model. The returned `LlmResponse` object is processed as if it were the actual response from the LLM. Ideal for implementing input guardrails, prompt validation, or serving cached responses. - * **`before_tool_callback` → `dict` or `Map`**: Skips the execution of the actual tool function (or sub-agent). The returned `dict` is used as the result of the tool call, which is then typically passed back to the LLM. Perfect for validating tool arguments, applying policy restrictions, or returning mocked/cached tool results. - * **`after_agent_callback` → `types.Content`**: *Replaces* the `Content` that the agent's run logic just produced. - * **`after_model_callback` → `LlmResponse`**: *Replaces* the `LlmResponse` received from the LLM. Useful for sanitizing outputs, adding standard disclaimers, or modifying the LLM's response structure. + * Returning a *specific type of object* (instead of `None`) is how you **override** the ADK agent's default behavior. The framework will use the object you return and *skip* the step that would normally follow or *replace* the result that was just generated. + * **`before_agent_callback` → `types.Content`**: Skips the agent's main execution logic (`_run_async_impl` / `_run_live_impl`). The returned `Content` object is immediately treated as the agent's final output for this turn. Useful for handling simple requests directly or enforcing access control. + * **`before_model_callback` → `LlmResponse`**: Skips the call to the external Large Language Model. The returned `LlmResponse` object is processed as if it were the actual response from the LLM. Ideal for implementing input guardrails, prompt validation, or serving cached responses. + * **`before_tool_callback` → `dict` or `Map`**: Skips the execution of the actual tool function (or sub-agent). The returned `dict` is used as the result of the tool call, which is then typically passed back to the LLM. Perfect for validating tool arguments, applying policy restrictions, or returning mocked/cached tool results. + * **`after_agent_callback` → `types.Content`**: *Replaces* the `Content` that the agent's run logic just produced. + * **`after_model_callback` → `LlmResponse`**: *Replaces* the `LlmResponse` received from the LLM. Useful for sanitizing outputs, adding standard disclaimers, or modifying the LLM's response structure. * **`after_tool_callback` → `dict` or `Map`**: *Replaces* the `dict` result returned by the tool. Allows for post-processing or standardization of tool outputs before they are sent back to the LLM. **Conceptual Code Example (Guardrail):** @@ -86,16 +86,16 @@ This example demonstrates the common pattern for a guardrail using `before_model ??? "Code" === "Python" - + ```python --8<-- "examples/python/snippets/callbacks/before_model_callback.py" ``` - + === "Go" ```go --8<-- "examples/go/snippets/callbacks/main.go:imports" - + --8<-- "examples/go/snippets/callbacks/main.go:guardrail_init" ``` @@ -103,5 +103,5 @@ This example demonstrates the common pattern for a guardrail using `before_model ```java --8<-- "examples/java/snippets/src/main/java/callbacks/BeforeModelGuardrailExample.java:init" ``` - + By understanding this mechanism of returning `None` versus returning specific objects, you can precisely control the agent's execution path, making callbacks an essential tool for building sophisticated and reliable agents with ADK. diff --git a/docs/context/index.md b/docs/context/index.md index 58aff311b..280aa2bf2 100644 --- a/docs/context/index.md +++ b/docs/context/index.md @@ -1,7 +1,7 @@ # Context
- Supported in ADKPython v0.1.0Go v0.1.0Java v0.1.0 + Supported in ADKPython v0.1.0TypeScript v0.2.0Go v0.1.0Java v0.1.0
In the Agent Development Kit (ADK), "context" refers to the crucial bundle of information available to your agent and its tools during specific operations. Think of it as the necessary background knowledge and resources needed to handle a current task or conversation turn effectively. @@ -24,11 +24,11 @@ The central piece holding all this information together for a single, complete u ```python # Conceptual Pseudocode: How the framework provides context (Internal Logic) - + # runner = Runner(agent=my_root_agent, session_service=..., artifact_service=...) # user_message = types.Content(...) # session = session_service.get_session(...) # Or create new - + # --- Inside runner.run_async(...) --- # 1. Framework creates the main context for this specific run # invocation_context = InvocationContext( @@ -50,6 +50,34 @@ The central piece holding all this information together for a single, complete u # As a developer, you work with the context objects provided in method arguments. ``` +=== "TypeScript" + + ```typescript + /* Conceptual Pseudocode: How the framework provides context (Internal Logic) */ + + const runner = new InMemoryRunner({ agent: myRootAgent }); + const session = await runner.sessionService.createSession({ ... }); + const userMessage = createUserContent(...); + + // --- Inside runner.runAsync(...) --- + // 1. Framework creates the main context for this specific run + const invocationContext = new InvocationContext({ + invocationId: "unique-id-for-this-run", + session: session, + userContent: userMessage, + agent: myRootAgent, // The starting agent + sessionService: runner.sessionService, + pluginManager: runner.pluginManager, + // ... other necessary fields ... + }); + // + // 2. Framework calls the agent's run method, passing the context implicitly + await myRootAgent.runAsync(invocationContext); + // --- End Internal Logic --- + + // As a developer, you work with the context objects provided in method arguments. + ``` + === "Go" ```go @@ -82,34 +110,6 @@ The central piece holding all this information together for a single, complete u } ``` -=== "TypeScript" - - ```typescript - /* Conceptual Pseudocode: How the framework provides context (Internal Logic) */ - - const runner = new InMemoryRunner({ agent: myRootAgent }); - const session = await runner.sessionService.createSession({ ... }); - const userMessage = createUserContent(...); - - // --- Inside runner.runAsync(...) --- - // 1. Framework creates the main context for this specific run - const invocationContext = new InvocationContext({ - invocationId: "unique-id-for-this-run", - session: session, - userContent: userMessage, - agent: myRootAgent, // The starting agent - sessionService: runner.sessionService, - pluginManager: runner.pluginManager, - // ... other necessary fields ... - }); - // - // 2. Framework calls the agent's run method, passing the context implicitly - await myRootAgent.runAsync(invocationContext); - // --- End Internal Logic --- - - // As a developer, you work with the context objects provided in method arguments. - ``` - ## The Different types of Context While `InvocationContext` acts as the comprehensive internal container, ADK provides specialized context objects tailored to specific situations. This ensures you have the right tools and permissions for the task at hand without needing to handle the full complexity of the internal context everywhere. Here are the different "flavors" you'll encounter: @@ -121,14 +121,14 @@ While `InvocationContext` acts as the comprehensive internal container, ADK prov * **Use Case:** Primarily used when the agent's core logic needs direct access to the overall session or services, though often state and artifact interactions are delegated to callbacks/tools which use their own contexts. Also used to control the invocation itself (e.g., setting `ctx.end_invocation = True`). === "Python" - + ```python # Pseudocode: Agent implementation receiving InvocationContext from google.adk.agents import BaseAgent from google.adk.agents.invocation_context import InvocationContext from google.adk.events import Event from typing import AsyncGenerator - + class MyAgent(BaseAgent): async def _run_async_impl(self, ctx: InvocationContext) -> AsyncGenerator[Event, None]: # Direct access example @@ -138,7 +138,25 @@ While `InvocationContext` acts as the comprehensive internal container, ADK prov # ... agent logic using ctx ... yield # ... event ... ``` - + + === "TypeScript" + + ```typescript + // Pseudocode: Agent implementation receiving InvocationContext + import { BaseAgent, InvocationContext, Event } from '@google/adk'; + + class MyAgent extends BaseAgent { + async *runAsyncImpl(ctx: InvocationContext): AsyncGenerator { + // Direct access example + const agentName = ctx.agent.name; + const sessionId = ctx.session.id; + console.log(`Agent ${agentName} running in session ${sessionId} for invocation ${ctx.invocationId}`); + // ... agent logic using ctx ... + yield; // ... event ... + } + } + ``` + === "Go" ```go @@ -146,17 +164,17 @@ While `InvocationContext` acts as the comprehensive internal container, ADK prov "google.golang.org/adk/agent" "google.golang.org/adk/session" ) - + --8<-- "examples/go/snippets/context/main.go:invocation_context_agent" ``` === "Java" - + ```java // Pseudocode: Agent implementation receiving InvocationContext import com.google.adk.agents.BaseAgent; import com.google.adk.agents.InvocationContext; - + LlmAgent root_agent = LlmAgent.builder() .model("gemini-***") @@ -170,35 +188,35 @@ While `InvocationContext` acts as the comprehensive internal container, ADK prov .tools(sampleTool) .outputKey("YOUR_KEY") .build(); - + ConcurrentMap initialState = new ConcurrentHashMap<>(); initialState.put("YOUR_KEY", ""); - + InMemoryRunner runner = new InMemoryRunner(agent); Session session = runner .sessionService() .createSession(runner.appName(), USER_ID, initialState, SESSION_ID ) .blockingGet(); - + try (Scanner scanner = new Scanner(System.in, StandardCharsets.UTF_8)) { while (true) { System.out.print("\nYou > "); String userInput = scanner.nextLine(); - + if ("quit".equalsIgnoreCase(userInput)) { break; } - + Content userMsg = Content.fromParts(Part.fromText(userInput)); - Flowable events = + Flowable events = runner.runAsync(session.userId(), session.id(), userMsg); - + System.out.print("\nAgent > "); - events.blockingForEach(event -> + events.blockingForEach(event -> System.out.print(event.stringifyContent())); } - + protected Flowable runAsyncImpl(InvocationContext invocationContext) { // Direct access example String agentName = invocationContext.agent.name @@ -209,35 +227,17 @@ While `InvocationContext` acts as the comprehensive internal container, ADK prov } ``` - === "TypeScript" - - ```typescript - // Pseudocode: Agent implementation receiving InvocationContext - import { BaseAgent, InvocationContext, Event } from '@google/adk'; - - class MyAgent extends BaseAgent { - async *runAsyncImpl(ctx: InvocationContext): AsyncGenerator { - // Direct access example - const agentName = ctx.agent.name; - const sessionId = ctx.session.id; - console.log(`Agent ${agentName} running in session ${sessionId} for invocation ${ctx.invocationId}`); - // ... agent logic using ctx ... - yield; // ... event ... - } - } - ``` - 2. **`ReadonlyContext`** * **Where Used:** Provided in scenarios where only read access to basic information is needed and mutation is disallowed (e.g., `InstructionProvider` functions). It's also the base class for other contexts. * **Purpose:** Offers a safe, read-only view of fundamental contextual details. * **Key Contents:** `invocation_id`, `agent_name`, and a read-only *view* of the current `state`. === "Python" - + ```python # Pseudocode: Instruction provider receiving ReadonlyContext from google.adk.agents.readonly_context import ReadonlyContext - + def my_instruction_provider(context: ReadonlyContext) -> str: # Read-only access example user_tier = context.state().get("user_tier", "standard") # Can read state @@ -245,20 +245,35 @@ While `InvocationContext` acts as the comprehensive internal container, ADK prov return f"Process the request for a {user_tier} user." ``` + === "TypeScript" + + ```typescript + // Pseudocode: Instruction provider receiving ReadonlyContext + import { ReadonlyContext } from '@google/adk'; + + function myInstructionProvider(context: ReadonlyContext): string { + // Read-only access example + // The state object is read-only + const userTier = context.state.get('user_tier') ?? 'standard'; + // context.state.set('new_key', 'value'); // This would fail or throw an error + return `Process the request for a ${userTier} user.`; + } + ``` + === "Go" ```go import "google.golang.org/adk/agent" - + --8<-- "examples/go/snippets/context/main.go:readonly_context_instruction" ``` === "Java" - + ```java // Pseudocode: Instruction provider receiving ReadonlyContext import com.google.adk.agents.ReadonlyContext; - + public String myInstructionProvider(ReadonlyContext context){ // Read-only access example String userTier = context.state().get("user_tier", "standard"); @@ -267,21 +282,6 @@ While `InvocationContext` acts as the comprehensive internal container, ADK prov } ``` - === "TypeScript" - - ```typescript - // Pseudocode: Instruction provider receiving ReadonlyContext - import { ReadonlyContext } from '@google/adk'; - - function myInstructionProvider(context: ReadonlyContext): string { - // Read-only access example - // The state object is read-only - const userTier = context.state.get('user_tier') ?? 'standard'; - // context.state.set('new_key', 'value'); // This would fail or throw an error - return `Process the request for a ${userTier} user.`; - } - ``` - 3. **`CallbackContext`** * **Where Used:** Passed as `callback_context` to agent lifecycle callbacks (`before_agent_callback`, `after_agent_callback`) and model interaction callbacks (`before_model_callback`, `after_model_callback`). * **Purpose:** Facilitates inspecting and modifying state, interacting with artifacts, and accessing invocation details *specifically within callbacks*. @@ -291,25 +291,44 @@ While `InvocationContext` acts as the comprehensive internal container, ADK prov * Direct `user_content` access. === "Python" - + ```python # Pseudocode: Callback receiving CallbackContext from google.adk.agents.callback_context import CallbackContext from google.adk.models import LlmRequest from google.genai import types from typing import Optional - + def my_before_model_cb(callback_context: CallbackContext, request: LlmRequest) -> Optional[types.Content]: # Read/Write state example call_count = callback_context.state.get("model_calls", 0) callback_context.state["model_calls"] = call_count + 1 # Modify state - + # Optionally load an artifact # config_part = callback_context.load_artifact("model_config.json") print(f"Preparing model call #{call_count + 1} for invocation {callback_context.invocation_id}") return None # Allow model call to proceed ``` - + + === "TypeScript" + + ```typescript + // Pseudocode: Callback receiving CallbackContext + import { CallbackContext, LlmRequest } from '@google/adk'; + import { Content } from '@google/genai'; + + function myBeforeModelCb(callbackContext: CallbackContext, request: LlmRequest): Content | undefined { + // Read/Write state example + const callCount = (callbackContext.state.get('model_calls') as number) || 0; + callbackContext.state.set('model_calls', callCount + 1); // Modify state + + // Optionally load an artifact + // const configPart = await callbackContext.loadArtifact('model_config.json'); + console.log(`Preparing model call #${callCount + 1} for invocation ${callbackContext.invocationId}`); + return undefined; // Allow model call to proceed + } + ``` + === "Go" ```go @@ -317,24 +336,24 @@ While `InvocationContext` acts as the comprehensive internal container, ADK prov "google.golang.org/adk/agent" "google.golang.org/adk/model" ) - + --8<-- "examples/go/snippets/context/main.go:callback_context_callback" ``` === "Java" - + ```java // Pseudocode: Callback receiving CallbackContext import com.google.adk.agents.CallbackContext; import com.google.adk.models.LlmRequest; import com.google.genai.types.Content; import java.util.Optional; - + public Maybe myBeforeModelCb(CallbackContext callbackContext, LlmRequest request){ // Read/Write state example callCount = callbackContext.state().get("model_calls", 0) callbackContext.state().put("model_calls") = callCount + 1 # Modify state - + // Optionally load an artifact // Maybe configPart = callbackContext.loadArtifact("model_config.json"); System.out.println("Preparing model call " + callCount + 1); @@ -342,25 +361,6 @@ While `InvocationContext` acts as the comprehensive internal container, ADK prov } ``` - === "TypeScript" - - ```typescript - // Pseudocode: Callback receiving CallbackContext - import { CallbackContext, LlmRequest } from '@google/adk'; - import { Content } from '@google/genai'; - - function myBeforeModelCb(callbackContext: CallbackContext, request: LlmRequest): Content | undefined { - // Read/Write state example - const callCount = (callbackContext.state.get('model_calls') as number) || 0; - callbackContext.state.set('model_calls', callCount + 1); // Modify state - - // Optionally load an artifact - // const configPart = await callbackContext.loadArtifact('model_config.json'); - console.log(`Preparing model call #${callCount + 1} for invocation ${callbackContext.invocationId}`); - return undefined; // Allow model call to proceed - } - ``` - 4. **`ToolContext`** * **Where Used:** Passed as `tool_context` to the functions backing `FunctionTool`s and to tool execution callbacks (`before_tool_callback`, `after_tool_callback`). * **Purpose:** Provides everything `CallbackContext` does, plus specialized methods essential for tool execution, like handling authentication, searching memory, and listing artifacts. @@ -372,12 +372,12 @@ While `InvocationContext` acts as the comprehensive internal container, ADK prov * **`actions` Property:** Direct access to the `EventActions` object for this step, allowing the tool to signal state changes, auth requests, etc. === "Python" - + ```python # Pseudocode: Tool function receiving ToolContext from google.adk.tools import ToolContext from typing import Dict, Any - + # Assume this function is wrapped by a FunctionTool def search_external_api(query: str, tool_context: ToolContext) -> Dict[str, Any]: api_key = tool_context.state.get("api_key") @@ -388,33 +388,63 @@ While `InvocationContext` acts as the comprehensive internal container, ADK prov # Use the 'actions' property to signal the auth request has been made # tool_context.actions.requested_auth_configs[tool_context.function_call_id] = auth_config return {"status": "Auth Required"} - + # Use the API key... print(f"Tool executing for query '{query}' using API key. Invocation: {tool_context.invocation_id}") - + # Optionally search memory or list artifacts # relevant_docs = tool_context.search_memory(f"info related to {query}") # available_files = tool_context.list_artifacts() - + return {"result": f"Data for {query} fetched."} ``` - + + === "TypeScript" + + ```typescript + // Pseudocode: Tool function receiving ToolContext + import { ToolContext } from '@google/adk'; + + // __Assume this function is wrapped by a FunctionTool__ + function searchExternalApi(query: string, toolContext: ToolContext): { [key: string]: string } { + const apiKey = toolContext.state.get('api_key') as string; + if (!apiKey) { + // Define required auth config + // const authConfig = new AuthConfig(...); + // toolContext.requestCredential(authConfig); // Request credentials + // The 'actions' property is now automatically updated by requestCredential + return { status: 'Auth Required' }; + } + + // Use the API key... + console.log(`Tool executing for query '${query}' using API key. Invocation: ${toolContext.invocationId}`); + + // Optionally search memory or list artifacts + // Note: accessing services like memory/artifacts is typically async in TS, + // so you would need to mark this function 'async' if you reused them. + // toolContext.searchMemory(`info related to ${query}`).then(...) + // toolContext.listArtifacts().then(...) + + return { result: `Data for ${query} fetched.` }; + } + ``` + === "Go" ```go import "google.golang.org/adk/tool" - + --8<-- "examples/go/snippets/context/main.go:tool_context_tool" ``` === "Java" - + ```java // Pseudocode: Tool function receiving ToolContext import com.google.adk.tools.ToolContext; import java.util.HashMap; import java.util.Map; - + // Assume this function is wrapped by a FunctionTool public Map searchExternalApi(String query, ToolContext toolContext){ String apiKey = toolContext.state.get("api_key"); @@ -425,44 +455,14 @@ While `InvocationContext` acts as the comprehensive internal container, ADK prov // Use the 'actions' property to signal the auth request has been made ... return Map.of("status", "Auth Required"); - + // Use the API key... System.out.println("Tool executing for query " + query + " using API key. "); - + // Optionally list artifacts // Single> availableFiles = toolContext.listArtifacts(); - - return Map.of("result", "Data for " + query + " fetched"); - } - ``` - - === "TypeScript" - - ```typescript - // Pseudocode: Tool function receiving ToolContext - import { ToolContext } from '@google/adk'; - - // __Assume this function is wrapped by a FunctionTool__ - function searchExternalApi(query: string, toolContext: ToolContext): { [key: string]: string } { - const apiKey = toolContext.state.get('api_key') as string; - if (!apiKey) { - // Define required auth config - // const authConfig = new AuthConfig(...); - // toolContext.requestCredential(authConfig); // Request credentials - // The 'actions' property is now automatically updated by requestCredential - return { status: 'Auth Required' }; - } - - // Use the API key... - console.log(`Tool executing for query '${query}' using API key. Invocation: ${toolContext.invocationId}`); - - // Optionally search memory or list artifacts - // Note: accessing services like memory/artifacts is typically async in TS, - // so you would need to mark this function 'async' if you reused them. - // toolContext.searchMemory(`info related to ${query}`).then(...) - // toolContext.listArtifacts().then(...) - return { result: `Data for ${query} fetched.` }; + return Map.of("result", "Data for " + query + " fetched"); } ``` @@ -480,31 +480,60 @@ You'll frequently need to read information stored within the context. * **Reading Session State:** Access data saved in previous steps or user/app-level settings. Use dictionary-like access on the `state` property. === "Python" - + ```python # Pseudocode: In a Tool function from google.adk.tools import ToolContext - + def my_tool(tool_context: ToolContext, **kwargs): user_pref = tool_context.state.get("user_display_preference", "default_mode") api_endpoint = tool_context.state.get("app:api_endpoint") # Read app-level state - + if user_pref == "dark_mode": # ... apply dark mode logic ... pass print(f"Using API endpoint: {api_endpoint}") # ... rest of tool logic ... - + # Pseudocode: In a Callback function from google.adk.agents.callback_context import CallbackContext - + def my_callback(callback_context: CallbackContext, **kwargs): last_tool_result = callback_context.state.get("temp:last_api_result") # Read temporary state if last_tool_result: print(f"Found temporary result from last tool: {last_tool_result}") # ... callback logic ... ``` - + + === "TypeScript" + + ```typescript + // Pseudocode: In a Tool function + import { ToolContext } from '@google/adk'; + + async function myTool(toolContext: ToolContext) { + const userPref = toolContext.state.get('user_display_preference', 'default_mode'); + const apiEndpoint = toolContext.state.get('app:api_endpoint'); // Read app-level state + + if (userPref === 'dark_mode') { + // ... apply dark mode logic ... + } + console.log(`Using API endpoint: ${apiEndpoint}`); + // ... rest of tool logic ... + } + + // Pseudocode: In a Callback function + import { CallbackContext } from '@google/adk'; + + function myCallback(callbackContext: CallbackContext) { + const lastToolResult = callbackContext.state.get('temp:last_api_result'); // Read temporary state + if (lastToolResult) { + console.log(`Found temporary result from last tool: ${lastToolResult}`); + } + // ... callback logic ... + } + ``` + === "Go" ```go @@ -514,18 +543,18 @@ You'll frequently need to read information stored within the context. "google.golang.org/adk/tool" "google.golang.org/genai" ) - + --8<-- "examples/go/snippets/context/main.go:accessing_state_tool" --8<-- "examples/go/snippets/context/main.go:accessing_state_callback" ``` === "Java" - + ```java // Pseudocode: In a Tool function import com.google.adk.tools.ToolContext; - + public void myTool(ToolContext toolContext){ String userPref = toolContext.state().get("user_display_preference"); String apiEndpoint = toolContext.state().get("app:api_endpoint"); // Read app-level state @@ -536,11 +565,11 @@ You'll frequently need to read information stored within the context. System.out.println("Using API endpoint: " + api_endpoint); // ... rest of tool logic ... } - - + + // Pseudocode: In a Callback function import com.google.adk.agents.CallbackContext; - + public void myCallback(CallbackContext callbackContext){ String lastToolResult = (String) callbackContext.state().get("temp:last_api_result"); // Read temporary state } @@ -550,65 +579,51 @@ You'll frequently need to read information stored within the context. // ... callback logic ... ``` - === "TypeScript" - - ```typescript - // Pseudocode: In a Tool function - import { ToolContext } from '@google/adk'; - - async function myTool(toolContext: ToolContext) { - const userPref = toolContext.state.get('user_display_preference', 'default_mode'); - const apiEndpoint = toolContext.state.get('app:api_endpoint'); // Read app-level state - - if (userPref === 'dark_mode') { - // ... apply dark mode logic ... - } - console.log(`Using API endpoint: ${apiEndpoint}`); - // ... rest of tool logic ... - } - - // Pseudocode: In a Callback function - import { CallbackContext } from '@google/adk'; - - function myCallback(callbackContext: CallbackContext) { - const lastToolResult = callbackContext.state.get('temp:last_api_result'); // Read temporary state - if (lastToolResult) { - console.log(`Found temporary result from last tool: ${lastToolResult}`); - } - // ... callback logic ... - } - ``` - * **Getting Current Identifiers:** Useful for logging or custom logic based on the current operation. === "Python" - + ```python # Pseudocode: In any context (ToolContext shown) from google.adk.tools import ToolContext - + def log_tool_usage(tool_context: ToolContext, **kwargs): agent_name = tool_context.agent_nameSystem.out.println("Found temporary result from last tool: " + lastToolResult); inv_id = tool_context.invocation_id func_call_id = getattr(tool_context, 'function_call_id', 'N/A') # Specific to ToolContext - + print(f"Log: Invocation={inv_id}, Agent={agent_name}, FunctionCallID={func_call_id} - Tool Executed.") ``` - + + === "TypeScript" + + ```typescript + // Pseudocode: In any context (ToolContext shown) + import { ToolContext } from '@google/adk'; + + function logToolUsage(toolContext: ToolContext) { + const agentName = toolContext.agentName; + const invId = toolContext.invocationId; + const functionCallId = toolContext.functionCallId ?? 'N/A'; // Specific to ToolContext + + console.log(`Log: Invocation=${invId}, Agent=${agentName}, FunctionCallID=${functionCallId} - Tool Executed.`); + } + ``` + === "Go" ```go import "google.golang.org/adk/tool" - + --8<-- "examples/go/snippets/context/main.go:accessing_ids" ``` === "Java" - + ```java // Pseudocode: In any context (ToolContext shown) import com.google.adk.tools.ToolContext; - + public void logToolUsage(ToolContext toolContext){ String agentName = toolContext.agentName; String invId = toolContext.invocationId; @@ -617,36 +632,21 @@ You'll frequently need to read information stored within the context. } ``` - === "TypeScript" - - ```typescript - // Pseudocode: In any context (ToolContext shown) - import { ToolContext } from '@google/adk'; - - function logToolUsage(toolContext: ToolContext) { - const agentName = toolContext.agentName; - const invId = toolContext.invocationId; - const functionCallId = toolContext.functionCallId ?? 'N/A'; // Specific to ToolContext - - console.log(`Log: Invocation=${invId}, Agent=${agentName}, FunctionCallID=${functionCallId} - Tool Executed.`); - } - ``` - * **Accessing the Initial User Input:** Refer back to the message that started the current invocation. === "Python" - + ```python # Pseudocode: In a Callback from google.adk.agents.callback_context import CallbackContext - + def check_initial_intent(callback_context: CallbackContext, **kwargs): initial_text = "N/A" if callback_context.user_content and callback_context.user_content.parts: initial_text = callback_context.user_content.parts[0].text or "Non-text input" - + print(f"This invocation started with user input: '{initial_text}'") - + # Pseudocode: In an Agent's _run_async_impl # async def _run_async_impl(self, ctx: InvocationContext) -> AsyncGenerator[Event, None]: # if ctx.user_content and ctx.user_content.parts: @@ -654,7 +654,24 @@ You'll frequently need to read information stored within the context. # print(f"Agent logic remembering initial query: {initial_text}") # ... ``` - + + === "TypeScript" + + ```typescript + // Pseudocode: In a Callback + import { CallbackContext } from '@google/adk'; + + function checkInitialIntent(callbackContext: CallbackContext) { + let initialText = 'N/A'; + const userContent = callbackContext.userContent; + if (userContent?.parts?.length) { + initialText = userContent.parts[0].text ?? 'Non-text input'; + } + + console.log(`This invocation started with user input: '${initialText}'`); + } + ``` + === "Go" ```go @@ -662,16 +679,16 @@ You'll frequently need to read information stored within the context. "google.golang.org/adk/agent" "google.golang.org/genai" ) - + --8<-- "examples/go/snippets/context/main.go:accessing_initial_user_input" ``` === "Java" - + ```java // Pseudocode: In a Callback import com.google.adk.agents.CallbackContext; - + public void checkInitialIntent(CallbackContext callbackContext){ String initialText = "N/A"; if((!(callbackContext.userContent().isEmpty())) && (!(callbackContext.userContent().parts.isEmpty()))){ @@ -682,23 +699,6 @@ You'll frequently need to read information stored within the context. } ``` - === "TypeScript" - - ```typescript - // Pseudocode: In a Callback - import { CallbackContext } from '@google/adk'; - - function checkInitialIntent(callbackContext: CallbackContext) { - let initialText = 'N/A'; - const userContent = callbackContext.userContent; - if (userContent?.parts?.length) { - initialText = userContent.parts[0].text ?? 'Non-text input'; - } - - console.log(`This invocation started with user input: '${initialText}'`); - } - ``` - ### Managing State State is crucial for memory and data flow. When you modify state using `CallbackContext` or `ToolContext`, the changes are automatically tracked and persisted by the framework. @@ -713,31 +713,58 @@ State is crucial for memory and data flow. When you modify state using `Callback # Pseudocode: Tool 1 - Fetches user ID from google.adk.tools import ToolContext import uuid - + def get_user_profile(tool_context: ToolContext) -> dict: user_id = str(uuid.uuid4()) # Simulate fetching ID # Save the ID to state for the next tool tool_context.state["temp:current_user_id"] = user_id return {"profile_status": "ID generated"} - + # Pseudocode: Tool 2 - Uses user ID from state def get_user_orders(tool_context: ToolContext) -> dict: user_id = tool_context.state.get("temp:current_user_id") if not user_id: return {"error": "User ID not found in state"} - + print(f"Fetching orders for user ID: {user_id}") # ... logic to fetch orders using user_id ... return {"orders": ["order123", "order456"]} ``` + === "TypeScript" + + ```typescript + // Pseudocode: Tool 1 - Fetches user ID + import { ToolContext } from '@google/adk'; + import { v4 as uuidv4 } from 'uuid'; + + function getUserProfile(toolContext: ToolContext): Record { + const userId = uuidv4(); // Simulate fetching ID + // Save the ID to state for the next tool + toolContext.state.set('temp:current_user_id', userId); + return { profile_status: 'ID generated' }; + } + + // Pseudocode: Tool 2 - Uses user ID from state + function getUserOrders(toolContext: ToolContext): Record { + const userId = toolContext.state.get('temp:current_user_id'); + if (!userId) { + return { error: 'User ID not found in state' }; + } + + console.log(`Fetching orders for user ID: ${userId}`); + // ... logic to fetch orders using user_id ... + return { orders: ['order123', 'order456'] }; + } + ``` + === "Go" ```go import "google.golang.org/adk/tool" - + --8<-- "examples/go/snippets/context/main.go:passing_data_tool1" - + --8<-- "examples/go/snippets/context/main.go:passing_data_tool2" ``` @@ -747,14 +774,14 @@ State is crucial for memory and data flow. When you modify state using `Callback // Pseudocode: Tool 1 - Fetches user ID import com.google.adk.tools.ToolContext; import java.util.UUID; - + public Map getUserProfile(ToolContext toolContext){ String userId = UUID.randomUUID().toString(); // Save the ID to state for the next tool toolContext.state().put("temp:current_user_id", user_id); return Map.of("profile_status", "ID generated"); } - + // Pseudocode: Tool 2 - Uses user ID from state public Map getUserOrders(ToolContext toolContext){ String userId = toolContext.state().get("temp:current_user_id"); @@ -767,41 +794,14 @@ State is crucial for memory and data flow. When you modify state using `Callback } ``` - === "TypeScript" - - ```typescript - // Pseudocode: Tool 1 - Fetches user ID - import { ToolContext } from '@google/adk'; - import { v4 as uuidv4 } from 'uuid'; - - function getUserProfile(toolContext: ToolContext): Record { - const userId = uuidv4(); // Simulate fetching ID - // Save the ID to state for the next tool - toolContext.state.set('temp:current_user_id', userId); - return { profile_status: 'ID generated' }; - } - - // Pseudocode: Tool 2 - Uses user ID from state - function getUserOrders(toolContext: ToolContext): Record { - const userId = toolContext.state.get('temp:current_user_id'); - if (!userId) { - return { error: 'User ID not found in state' }; - } - - console.log(`Fetching orders for user ID: ${userId}`); - // ... logic to fetch orders using user_id ... - return { orders: ['order123', 'order456'] }; - } - ``` - * **Updating User Preferences:** === "Python" - + ```python # Pseudocode: Tool or Callback identifies a preference from google.adk.tools import ToolContext # Or CallbackContext - + def set_user_preference(tool_context: ToolContext, preference: str, value: str) -> dict: # Use 'user:' prefix for user-level state (if using a persistent SessionService) state_key = f"user:{preference}" @@ -809,21 +809,36 @@ State is crucial for memory and data flow. When you modify state using `Callback print(f"Set user preference '{preference}' to '{value}'") return {"status": "Preference updated"} ``` - + + === "TypeScript" + + ```typescript + // Pseudocode: Tool or Callback identifies a preference + import { ToolContext } from '@google/adk'; // Or CallbackContext + + function setUserPreference(toolContext: ToolContext, preference: string, value: string): Record { + // Use 'user:' prefix for user-level state (if using a persistent SessionService) + const stateKey = `user:${preference}`; + toolContext.state.set(stateKey, value); + console.log(`Set user preference '${preference}' to '${value}'`); + return { status: 'Preference updated' }; + } + ``` + === "Go" ```go import "google.golang.org/adk/tool" - + --8<-- "examples/go/snippets/context/main.go:updating_preferences" ``` === "Java" - + ```java // Pseudocode: Tool or Callback identifies a preference import com.google.adk.tools.ToolContext; // Or CallbackContext - + public Map setUserPreference(ToolContext toolContext, String preference, String value){ // Use 'user:' prefix for user-level state (if using a persistent SessionService) String stateKey = "user:" + preference; @@ -833,21 +848,6 @@ State is crucial for memory and data flow. When you modify state using `Callback } ``` - === "TypeScript" - - ```typescript - // Pseudocode: Tool or Callback identifies a preference - import { ToolContext } from '@google/adk'; // Or CallbackContext - - function setUserPreference(toolContext: ToolContext, preference: string, value: string): Record { - // Use 'user:' prefix for user-level state (if using a persistent SessionService) - const stateKey = `user:${preference}`; - toolContext.state.set(stateKey, value); - console.log(`Set user preference '${preference}' to '${value}'`); - return { status: 'Preference updated' }; - } - ``` - * **State Prefixes:** While basic state is session-specific, prefixes like `app:` and `user:` can be used with persistent `SessionService` implementations (like `DatabaseSessionService` or `VertexAiSessionService`) to indicate broader scope (app-wide or user-wide across sessions). `temp:` can denote data only relevant within the current invocation. ### Working with Artifacts @@ -859,12 +859,12 @@ Use artifacts to handle files or large data blobs associated with the session. C 1. **Ingest Reference (e.g., in a Setup Tool or Callback):** Save the *path or URI* of the document, not the entire content, as an artifact. === "Python" - + ```python # Pseudocode: In a callback or initial tool from google.adk.agents.callback_context import CallbackContext # Or ToolContext from google.genai import types - + def save_document_reference(context: CallbackContext, file_path: str) -> None: # Assume file_path is something like "gs://my-bucket/docs/report.pdf" or "/local/path/to/report.pdf" try: @@ -878,11 +878,36 @@ Use artifacts to handle files or large data blobs associated with the session. C print(f"Error saving artifact: {e}") # E.g., Artifact service not configured except Exception as e: print(f"Unexpected error saving artifact reference: {e}") - + # Example usage: # save_document_reference(callback_context, "gs://my-bucket/docs/report.pdf") ``` - + + === "TypeScript" + + ```typescript + // Pseudocode: In a callback or initial tool + import { CallbackContext } from '@google/adk'; // Or ToolContext + import type { Part } from '@google/genai'; + + async function saveDocumentReference(context: CallbackContext, filePath: string) { + // Assume filePath is something like "gs://my-bucket/docs/report.pdf" or "/local/path/to/report.pdf" + try { + // Create a Part containing the path/URI text + const artifactPart: Part = { text: filePath }; + const version = await context.saveArtifact('document_to_summarize.txt', artifactPart); + console.log(`Saved document reference '${filePath}' as artifact version ${version}`); + // Store the filename in state if needed by other tools + context.state.set('temp:doc_artifact_name', 'document_to_summarize.txt'); + } catch (e) { + console.error(`Unexpected error saving artifact reference: ${e}`); + } + } + + // Example usage: + // saveDocumentReference(callbackContext, "gs://my-bucket/docs/report.pdf"); + ``` + === "Go" ```go @@ -890,19 +915,19 @@ Use artifacts to handle files or large data blobs associated with the session. C "google.golang.org/adk/tool" "google.golang.org/genai" ) - + --8<-- "examples/go/snippets/context/main.go:artifacts_save_ref" ``` === "Java" - + ```java // Pseudocode: In a callback or initial tool import com.google.adk.agents.CallbackContext; import com.google.genai.types.Content; import com.google.genai.types.Part; - - + + pubic void saveDocumentReference(CallbackContext context, String filePath){ // Assume file_path is something like "gs://my-bucket/docs/report.pdf" or "/local/path/to/report.pdf" try{ @@ -916,34 +941,9 @@ Use artifacts to handle files or large data blobs associated with the session. C System.out.println("Unexpected error saving artifact reference: " + e); } } - - // Example usage: - // saveDocumentReference(context, "gs://my-bucket/docs/report.pdf") - ``` - - === "TypeScript" - - ```typescript - // Pseudocode: In a callback or initial tool - import { CallbackContext } from '@google/adk'; // Or ToolContext - import type { Part } from '@google/genai'; - - async function saveDocumentReference(context: CallbackContext, filePath: string) { - // Assume filePath is something like "gs://my-bucket/docs/report.pdf" or "/local/path/to/report.pdf" - try { - // Create a Part containing the path/URI text - const artifactPart: Part = { text: filePath }; - const version = await context.saveArtifact('document_to_summarize.txt', artifactPart); - console.log(`Saved document reference '${filePath}' as artifact version ${version}`); - // Store the filename in state if needed by other tools - context.state.set('temp:doc_artifact_name', 'document_to_summarize.txt'); - } catch (e) { - console.error(`Unexpected error saving artifact reference: ${e}`); - } - } // Example usage: - // saveDocumentReference(callbackContext, "gs://my-bucket/docs/report.pdf"); + // saveDocumentReference(context, "gs://my-bucket/docs/report.pdf") ``` 2. **Summarizer Tool:** Load the artifact to get the path/URI, read the actual document content using appropriate libraries, summarize, and return the result. @@ -1005,11 +1005,66 @@ Use artifacts to handle files or large data blobs associated with the session. C # return {"error": f"Error reading document {file_path}: {e}"} ``` + === "TypeScript" + + ```typescript + // Pseudocode: In the Summarizer tool function + import { ToolContext } from '@google/adk'; + + async function summarizeDocumentTool(toolContext: ToolContext): Promise> { + const artifactName = toolContext.state.get('temp:doc_artifact_name') as string; + if (!artifactName) { + return { error: 'Document artifact name not found in state.' }; + } + + try { + // 1. Load the artifact part containing the path/URI + const artifactPart = await toolContext.loadArtifact(artifactName); + if (!artifactPart?.text) { + return { error: `Could not load artifact or artifact has no text path: ${artifactName}` }; + } + + const filePath = artifactPart.text; + console.log(`Loaded document reference: ${filePath}`); + + // 2. Read the actual document content (outside ADK context) + let documentContent = ''; + if (filePath.startsWith('gs://')) { + // Example: Use GCS client library to download/read + // const storage = new Storage(); + // const bucket = storage.bucket('my-bucket'); + // const file = bucket.file(filePath.replace('gs://my-bucket/', '')); + // const [contents] = await file.download(); + // documentContent = contents.toString(); + } else if (filePath.startsWith('/')) { + // Example: Use local file system + // import { readFile } from 'fs/promises'; + // documentContent = await readFile(filePath, 'utf8'); + } else { + return { error: `Unsupported file path scheme: ${filePath}` }; + } + + // 3. Summarize the content + if (!documentContent) { + return { error: 'Failed to read document content.' }; + } + + // const summary = summarizeText(documentContent); // Call your summarization logic + const summary = `Summary of content from ${filePath}`; // Placeholder + + return { summary }; + + } catch (e) { + return { error: `Error processing artifact: ${e}` }; + } + } + ``` + === "Go" ```go import "google.golang.org/adk/tool" - + --8<-- "examples/go/snippets/context/main.go:artifacts_summarize" ``` @@ -1043,12 +1098,12 @@ Use artifacts to handle files or large data blobs associated with the session. C } else if(){ // Example: Use local file system to download/read into documentContent } else{ - return Map.of("error", "Unsupported file path scheme: " + filePath); + return Map.of("error", "Unsupported file path scheme: " + filePath); } // 3. Summarize the content if(documentContent.isEmpty()){ - return Map.of("error", "Failed to read document content."); + return Map.of("error", "Failed to read document content."); } // summary = summarizeText(documentContent) // Call your summarization logic @@ -1064,70 +1119,15 @@ Use artifacts to handle files or large data blobs associated with the session. C } } ``` - - === "TypeScript" - - ```typescript - // Pseudocode: In the Summarizer tool function - import { ToolContext } from '@google/adk'; - - async function summarizeDocumentTool(toolContext: ToolContext): Promise> { - const artifactName = toolContext.state.get('temp:doc_artifact_name') as string; - if (!artifactName) { - return { error: 'Document artifact name not found in state.' }; - } - - try { - // 1. Load the artifact part containing the path/URI - const artifactPart = await toolContext.loadArtifact(artifactName); - if (!artifactPart?.text) { - return { error: `Could not load artifact or artifact has no text path: ${artifactName}` }; - } - - const filePath = artifactPart.text; - console.log(`Loaded document reference: ${filePath}`); - - // 2. Read the actual document content (outside ADK context) - let documentContent = ''; - if (filePath.startsWith('gs://')) { - // Example: Use GCS client library to download/read - // const storage = new Storage(); - // const bucket = storage.bucket('my-bucket'); - // const file = bucket.file(filePath.replace('gs://my-bucket/', '')); - // const [contents] = await file.download(); - // documentContent = contents.toString(); - } else if (filePath.startsWith('/')) { - // Example: Use local file system - // import { readFile } from 'fs/promises'; - // documentContent = await readFile(filePath, 'utf8'); - } else { - return { error: `Unsupported file path scheme: ${filePath}` }; - } - - // 3. Summarize the content - if (!documentContent) { - return { error: 'Failed to read document content.' }; - } - - // const summary = summarizeText(documentContent); // Call your summarization logic - const summary = `Summary of content from ${filePath}`; // Placeholder - - return { summary }; - } catch (e) { - return { error: `Error processing artifact: ${e}` }; - } - } - ``` - * **Listing Artifacts:** Discover what files are available. - + === "Python" - + ```python # Pseudocode: In a tool function from google.adk.tools import ToolContext - + def check_available_docs(tool_context: ToolContext) -> dict: try: artifact_keys = tool_context.list_artifacts() @@ -1136,21 +1136,38 @@ Use artifacts to handle files or large data blobs associated with the session. C except ValueError as e: return {"error": f"Artifact service error: {e}"} ``` - + + === "TypeScript" + + ```typescript + // Pseudocode: In a tool function + import { ToolContext } from '@google/adk'; + + async function checkAvailableDocs(toolContext: ToolContext): Promise> { + try { + const artifactKeys = await toolContext.listArtifacts(); + console.log(`Available artifacts: ${artifactKeys}`); + return { available_docs: artifactKeys }; + } catch (e) { + return { error: `Artifact service error: ${e}` }; + } + } + ``` + === "Go" ```go import "google.golang.org/adk/tool" - + --8<-- "examples/go/snippets/context/main.go:artifacts_list" ``` === "Java" - + ```java // Pseudocode: In a tool function import com.google.adk.tools.ToolContext; - + public Map checkAvailableDocs(ToolContext toolContext){ try{ Single> artifactKeys = toolContext.listArtifacts(); @@ -1162,94 +1179,78 @@ Use artifacts to handle files or large data blobs associated with the session. C } ``` - === "TypeScript" - - ```typescript - // Pseudocode: In a tool function - import { ToolContext } from '@google/adk'; - - async function checkAvailableDocs(toolContext: ToolContext): Promise> { - try { - const artifactKeys = await toolContext.listArtifacts(); - console.log(`Available artifacts: ${artifactKeys}`); - return { available_docs: artifactKeys }; - } catch (e) { - return { error: `Artifact service error: ${e}` }; - } - } - ``` - -### Handling Tool Authentication +### Handling Tool Authentication
- Supported in ADKPython v0.1.0 + Supported in ADKPython v0.1.0TypeScript v0.2.0
Securely manage API keys or other credentials needed by tools. -```python -# Pseudocode: Tool requiring auth -from google.adk.tools import ToolContext -from google.adk.auth import AuthConfig # Assume appropriate AuthConfig is defined +=== "Python" -# Define your required auth configuration (e.g., OAuth, API Key) -MY_API_AUTH_CONFIG = AuthConfig(...) -AUTH_STATE_KEY = "user:my_api_credential" # Key to store retrieved credential + ```python + # Pseudocode: Tool requiring auth + from google.adk.tools import ToolContext + from google.adk.auth import AuthConfig # Assume appropriate AuthConfig is defined -def call_secure_api(tool_context: ToolContext, request_data: str) -> dict: - # 1. Check if credential already exists in state - credential = tool_context.state.get(AUTH_STATE_KEY) + # Define your required auth configuration (e.g., OAuth, API Key) + MY_API_AUTH_CONFIG = AuthConfig(...) + AUTH_STATE_KEY = "user:my_api_credential" # Key to store retrieved credential - if not credential: - # 2. If not, request it - print("Credential not found, requesting...") - try: - tool_context.request_credential(MY_API_AUTH_CONFIG) - # The framework handles yielding the event. The tool execution stops here for this turn. - return {"status": "Authentication required. Please provide credentials."} - except ValueError as e: - return {"error": f"Auth error: {e}"} # e.g., function_call_id missing - except Exception as e: - return {"error": f"Failed to request credential: {e}"} + def call_secure_api(tool_context: ToolContext, request_data: str) -> dict: + # 1. Check if credential already exists in state + credential = tool_context.state.get(AUTH_STATE_KEY) - # 3. If credential exists (might be from a previous turn after request) - # or if this is a subsequent call after auth flow completed externally - try: - # Optionally, re-validate/retrieve if needed, or use directly - # This might retrieve the credential if the external flow just completed - auth_credential_obj = tool_context.get_auth_response(MY_API_AUTH_CONFIG) - api_key = auth_credential_obj.api_key # Or access_token, etc. + if not credential: + # 2. If not, request it + print("Credential not found, requesting...") + try: + tool_context.request_credential(MY_API_AUTH_CONFIG) + # The framework handles yielding the event. The tool execution stops here for this turn. + return {"status": "Authentication required. Please provide credentials."} + except ValueError as e: + return {"error": f"Auth error: {e}"} # e.g., function_call_id missing + except Exception as e: + return {"error": f"Failed to request credential: {e}"} - # Store it back in state for future calls within the session - tool_context.state[AUTH_STATE_KEY] = auth_credential_obj.model_dump() # Persist retrieved credential + # 3. If credential exists (might be from a previous turn after request) + # or if this is a subsequent call after auth flow completed externally + try: + # Optionally, re-validate/retrieve if needed, or use directly + # This might retrieve the credential if the external flow just completed + auth_credential_obj = tool_context.get_auth_response(MY_API_AUTH_CONFIG) + api_key = auth_credential_obj.api_key # Or access_token, etc. - print(f"Using retrieved credential to call API with data: {request_data}") - # ... Make the actual API call using api_key ... - api_result = f"API result for {request_data}" + # Store it back in state for future calls within the session + tool_context.state[AUTH_STATE_KEY] = auth_credential_obj.model_dump() # Persist retrieved credential - return {"result": api_result} - except Exception as e: - # Handle errors retrieving/using the credential - print(f"Error using credential: {e}") - # Maybe clear the state key if credential is invalid? - # tool_context.state[AUTH_STATE_KEY] = None - return {"error": "Failed to use credential"} + print(f"Using retrieved credential to call API with data: {request_data}") + # ... Make the actual API call using api_key ... + api_result = f"API result for {request_data}" -``` + return {"result": api_result} + except Exception as e: + # Handle errors retrieving/using the credential + print(f"Error using credential: {e}") + # Maybe clear the state key if credential is invalid? + # tool_context.state[AUTH_STATE_KEY] = None + return {"error": "Failed to use credential"} + ``` - === "TypeScript" +=== "TypeScript" ```typescript // Pseudocode: Tool requiring auth import { ToolContext } from '@google/adk'; // AuthConfig from ADK or custom - + // Define a local AuthConfig interface as it's not publicly exported by ADK interface AuthConfig { credentialKey: string; authScheme: { type: string }; // Minimal representation for the example // Add other properties if they become relevant for the example } - + // Define your required auth configuration (e.g., OAuth, API Key) const MY_API_AUTH_CONFIG: AuthConfig = { credentialKey: 'my-api-key', // Example key @@ -1299,37 +1300,40 @@ def call_secure_api(tool_context: ToolContext, request_data: str) -> dict: } } ``` + *Remember: `request_credential` pauses the tool and signals the need for authentication. The user/system provides credentials, and on a subsequent call, `get_auth_response` (or checking state again) allows the tool to proceed.* The `tool_context.function_call_id` is used implicitly by the framework to link the request and response. -### Leveraging Memory +### Leveraging Memory
- Supported in ADKPython v0.1.0 + Supported in ADKPython v0.1.0TypeScript v0.2.0
Access relevant information from the past or external sources. -```python -# Pseudocode: Tool using memory search -from google.adk.tools import ToolContext - -def find_related_info(tool_context: ToolContext, topic: str) -> dict: - try: - search_results = tool_context.search_memory(f"Information about {topic}") - if search_results.results: - print(f"Found {len(search_results.results)} memory results for '{topic}'") - # Process search_results.results (which are SearchMemoryResponseEntry) - top_result_text = search_results.results[0].text - return {"memory_snippet": top_result_text} - else: - return {"message": "No relevant memories found."} - except ValueError as e: - return {"error": f"Memory service error: {e}"} # e.g., Service not configured - except Exception as e: - return {"error": f"Unexpected error searching memory: {e}"} -``` +=== "Python" - === "TypeScript" + ```python + # Pseudocode: Tool using memory search + from google.adk.tools import ToolContext + + def find_related_info(tool_context: ToolContext, topic: str) -> dict: + try: + search_results = tool_context.search_memory(f"Information about {topic}") + if search_results.results: + print(f"Found {len(search_results.results)} memory results for '{topic}'") + # Process search_results.results (which are SearchMemoryResponseEntry) + top_result_text = search_results.results[0].text + return {"memory_snippet": top_result_text} + else: + return {"message": "No relevant memories found."} + except ValueError as e: + return {"error": f"Memory service error: {e}"} # e.g., Service not configured + except Exception as e: + return {"error": f"Unexpected error searching memory: {e}"} + ``` + +=== "TypeScript" ```typescript // Pseudocode: Tool using memory search @@ -1352,40 +1356,42 @@ def find_related_info(tool_context: ToolContext, topic: str) -> dict: } ``` -### Advanced: Direct `InvocationContext` Usage +### Advanced: Direct `InvocationContext` Usage
- Supported in ADKPython v0.1.0 + Supported in ADKPython v0.1.0TypeScript v0.2.0
While most interactions happen via `CallbackContext` or `ToolContext`, sometimes the agent's core logic (`_run_async_impl`/`_run_live_impl`) needs direct access. -```python -# Pseudocode: Inside agent's _run_async_impl -from google.adk.agents import BaseAgent -from google.adk.agents.invocation_context import InvocationContext -from google.adk.events import Event -from typing import AsyncGenerator - -class MyControllingAgent(BaseAgent): - async def _run_async_impl(self, ctx: InvocationContext) -> AsyncGenerator[Event, None]: - # Example: Check if a specific service is available - if not ctx.memory_service: - print("Memory service is not available for this invocation.") - # Potentially change agent behavior - - # Example: Early termination based on some condition - if ctx.session.state.get("critical_error_flag"): - print("Critical error detected, ending invocation.") - ctx.end_invocation = True # Signal framework to stop processing - yield Event(author=self.name, invocation_id=ctx.invocation_id, content="Stopping due to critical error.") - return # Stop this agent's execution - - # ... Normal agent processing ... - yield # ... event ... -``` +=== "Python" - === "TypeScript" + ```python + # Pseudocode: Inside agent's _run_async_impl + from google.adk.agents import BaseAgent + from google.adk.agents.invocation_context import InvocationContext + from google.adk.events import Event + from typing import AsyncGenerator + + class MyControllingAgent(BaseAgent): + async def _run_async_impl(self, ctx: InvocationContext) -> AsyncGenerator[Event, None]: + # Example: Check if a specific service is available + if not ctx.memory_service: + print("Memory service is not available for this invocation.") + # Potentially change agent behavior + + # Example: Early termination based on some condition + if ctx.session.state.get("critical_error_flag"): + print("Critical error detected, ending invocation.") + ctx.end_invocation = True # Signal framework to stop processing + yield Event(author=self.name, invocation_id=ctx.invocation_id, content="Stopping due to critical error.") + return # Stop this agent's execution + + # ... Normal agent processing ... + yield # ... event ... + ``` + +=== "TypeScript" ```typescript // Pseudocode: Inside agent's runAsyncImpl diff --git a/docs/events/index.md b/docs/events/index.md index fdb9d2939..c8c1b62c0 100644 --- a/docs/events/index.md +++ b/docs/events/index.md @@ -1,7 +1,7 @@ # Events
- Supported in ADKPython v0.1.0Go v0.1.0Java v0.1.0 + Supported in ADKPython v0.1.0TypeScript v0.2.0Go v0.1.0Java v0.1.0
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. @@ -257,7 +257,7 @@ Once you know the event type, access the relevant data: * **Function Call Details:** === "Python" - + ```python calls = event.get_function_calls() if calls: diff --git a/docs/grounding/google_search_grounding.md b/docs/grounding/google_search_grounding.md index b011458e8..31d939dd5 100644 --- a/docs/grounding/google_search_grounding.md +++ b/docs/grounding/google_search_grounding.md @@ -1,5 +1,9 @@ # Understanding Google Search Grounding +
+ Supported in ADKPython v0.1.0TypeScript v0.2.0 +
+ [Google Search Grounding tool](../tools/built-in-tools.md#google-search) is a powerful feature in the Agent Development Kit (ADK) that enables AI agents to access real-time, authoritative information from the web. By connecting your agents to Google Search, you can provide users with up-to-date answers backed by reliable sources. This feature is particularly valuable for queries requiring current information like weather updates, news events, stock prices, or any facts that may have changed since the model's training data cutoff. When your agent determines that external information is needed, it automatically performs web searches and incorporates the results into its response with proper attribution. @@ -82,7 +86,7 @@ Under a project directory, run the following commands: echo "from . import agent" > google_search_agent/__init__.py # Step 3: Create an agent.py (the agent definition) and .env (Gemini authentication config) - type nul > google_search_agent\agent.py + type nul > google_search_agent\agent.py type nul > google_search_agent\.env ``` @@ -188,7 +192,7 @@ There are multiple ways to interact with your agent: ```shell adk web ``` - + !!!info "Note for Windows users" When hitting the `_make_subprocess_transport NotImplementedError`, consider using `adk web --no-reload` instead. @@ -217,7 +221,7 @@ There are multiple ways to interact with your agent: ``` To exit, use Cmd/Ctrl+C. -### 📝 Example prompts to try +### Example prompts to try With those questions, you can confirm that the agent is actually calling Google Search to get the latest weather and time. @@ -245,18 +249,18 @@ This diagram illustrates the step-by-step process of how a user query results in The grounding agent uses the data flow described in the diagram to retrieve, process, and incorporate external information into the final answer presented to the user. -1. **User Query**: An end-user interacts with your agent by asking a question or giving a command. -2. **ADK Orchestration** : The Agent Development Kit orchestrates the agent's behavior and passes the user's message to the core of your agent. -3. **LLM Analysis and Tool-Calling** : The agent's LLM (e.g., a Gemini model) analyzes the prompt. If it determines that external, up-to-date information is required, it triggers the grounding mechanism by calling the - google\_search tool. This is ideal for answering queries about recent news, weather, or facts not present in the model's training data. -4. **Grounding Service Interaction** : The google\_search tool interacts with an internal grounding service that formulates and sends one or more queries to the Google Search Index. -5. **Context Injection**: The grounding service retrieves the relevant web pages and snippets. It then integrates these search results into the model's context - before the final response is generated. This crucial step allows the model to "reason" over factual, real-time data. -6. **Grounded Response Generation**: The LLM, now informed by the fresh search results, generates a response that incorporates the retrieved information. -7. **Response Presentation with Sources** : The ADK receives the final grounded response, which includes the necessary source URLs and +1. **User Query**: An end-user interacts with your agent by asking a question or giving a command. +2. **ADK Orchestration** : The Agent Development Kit orchestrates the agent's behavior and passes the user's message to the core of your agent. +3. **LLM Analysis and Tool-Calling** : The agent's LLM (e.g., a Gemini model) analyzes the prompt. If it determines that external, up-to-date information is required, it triggers the grounding mechanism by calling the + google\_search tool. This is ideal for answering queries about recent news, weather, or facts not present in the model's training data. +4. **Grounding Service Interaction** : The google\_search tool interacts with an internal grounding service that formulates and sends one or more queries to the Google Search Index. +5. **Context Injection**: The grounding service retrieves the relevant web pages and snippets. It then integrates these search results into the model's context + before the final response is generated. This crucial step allows the model to "reason" over factual, real-time data. +6. **Grounded Response Generation**: The LLM, now informed by the fresh search results, generates a response that incorporates the retrieved information. +7. **Response Presentation with Sources** : The ADK receives the final grounded response, which includes the necessary source URLs and groundingMetadata, and presents it to the user with attribution. This allows end-users to verify the information and builds trust in the agent's answers. -### Understanding grounding with Google Search response +### Understanding grounding with Google Search response When the agent uses Google Search to ground a response, it returns a detailed set of information that includes not only the final text answer but also the sources it used to generate that answer. This metadata is crucial for verifying the response and for providing attribution to the original sources. @@ -314,9 +318,9 @@ The following is an example of the content object returned by the model after a The metadata provides a link between the text generated by the model and the sources that support it. Here is a step-by-step breakdown: -1. **groundingChunks**: This is a list of the web pages the model consulted. Each chunk contains the title of the webpage and a uri that links to the source. -2. **groundingSupports**: This list connects specific sentences in the final answer back to the groundingChunks. - * **segment**: This object identifies a specific portion of the final text answer, defined by its startIndex, endIndex, and the text itself. +1. **groundingChunks**: This is a list of the web pages the model consulted. Each chunk contains the title of the webpage and a uri that links to the source. +2. **groundingSupports**: This list connects specific sentences in the final answer back to the groundingChunks. + * **segment**: This object identifies a specific portion of the final text answer, defined by its startIndex, endIndex, and the text itself. * **groundingChunkIndices**: This array contains the index numbers that correspond to the sources listed in the groundingChunks. For example, the sentence "They defeated FC Porto 2-1..." is supported by information from groundingChunks at index 0 and 1 (both from mlssoccer.com and intermiamicf.com). ### How to display grounding responses with Google Search diff --git a/docs/grounding/vertex_ai_search_grounding.md b/docs/grounding/vertex_ai_search_grounding.md index 7fbe9a053..adaae8254 100644 --- a/docs/grounding/vertex_ai_search_grounding.md +++ b/docs/grounding/vertex_ai_search_grounding.md @@ -1,5 +1,9 @@ # Understanding Vertex AI Search Grounding +
+ Supported in ADKPython v0.1.0TypeScript v0.2.0 +
+ [Vertex AI Search Grounding tool](../tools/built-in-tools.md#vertex-ai-search) is a powerful feature in the Agent Development Kit (ADK) that enables AI agents to access information from your private enterprise documents and data repositories. By connecting your agents to indexed enterprise content, you can provide users with answers grounded in your organization's knowledge base. This feature is particularly valuable for enterprise-specific queries requiring information from internal documentation, policies, research papers, or any proprietary content that has been indexed in your [Vertex AI Search](https://cloud.google.com/enterprise-search) datastore. When your agent determines that information from your knowledge base is needed, it automatically searches your indexed documents and incorporates the results into its response with proper attribution. @@ -88,7 +92,7 @@ Under a project directory, run the following commands: echo "from . import agent" > vertex_search_agent/__init__.py # Step 3: Create an agent.py (the agent definition) and .env (authentication config) - type nul > vertex_search_agent\agent.py + type nul > vertex_search_agent\agent.py type nul > google_search_agent\.env ``` @@ -147,7 +151,7 @@ There are multiple ways to interact with your agent: ```shell adk web ``` - + !!!info "Note for Windows users" When hitting the `_make_subprocess_transport NotImplementedError`, consider using `adk web --no-reload` instead. @@ -176,7 +180,7 @@ There are multiple ways to interact with your agent: ``` To exit, use Cmd/Ctrl+C. -### 📝 Example prompts to try +### Example prompts to try With those questions, you can confirm that the agent is actually calling Vertex AI Search to get information from the Alphabet reports: @@ -305,7 +309,7 @@ Since grounding metadata is provided, you can choose to implement citation displ for event in events: if event.is_final_response(): print(event.content.parts[0].text) - + # Optional: Show source count if event.grounding_metadata: print(f"\nBased on {len(event.grounding_metadata.grounding_chunks)} documents") diff --git a/docs/mcp/index.md b/docs/mcp/index.md index dd185ba18..41ac121f1 100644 --- a/docs/mcp/index.md +++ b/docs/mcp/index.md @@ -1,7 +1,7 @@ # Model Context Protocol (MCP)
- Supported in ADKPythonGoJava + Supported in ADKPythonTypeScriptGoJava
The diff --git a/docs/release-notes.md b/docs/release-notes.md index 17d4b1d25..c9e333226 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -4,5 +4,6 @@ You can find the release notes in the code repositories for each supported language. For detailed information on ADK releases, see these locations: * [ADK Python release notes](https://github.com/google/adk-python/releases) +* [ADK TypeScript release notes](https://github.com/google/adk-js/releases) * [ADK Go release notes](https://github.com/google/adk-go/releases) * [ADK Java release notes](https://github.com/google/adk-java/releases) diff --git a/docs/sessions/index.md b/docs/sessions/index.md index 822147030..a33dd1eed 100644 --- a/docs/sessions/index.md +++ b/docs/sessions/index.md @@ -1,7 +1,7 @@ # Introduction to Conversational Context: Session, State, and Memory
- Supported in ADKPythonGoJava + Supported in ADKPythonTypeScriptGoJava
Meaningful, multi-turn conversations require agents to understand context. Just diff --git a/docs/sessions/memory.md b/docs/sessions/memory.md index f1398bd24..70e47ab87 100644 --- a/docs/sessions/memory.md +++ b/docs/sessions/memory.md @@ -1,11 +1,7 @@ # Memory: Long-Term Knowledge with `MemoryService`
- Supported in ADK - Python v0.1.0 - Go v0.1.0 - Java v0.1.0 - Typescript v0.2.0 + Supported in ADKPython v0.1.0Typescript v0.2.0Go v0.1.0Java v0.1.0
We've seen how `Session` tracks the history (`events`) and temporary data (`state`) for a *single, ongoing conversation*. But what if an agent needs to recall information from *past* conversations? This is where the concept of **Long-Term Knowledge** and the **`MemoryService`** come into play. diff --git a/docs/sessions/session.md b/docs/sessions/session.md index 48aa187a8..d7b941da5 100644 --- a/docs/sessions/session.md +++ b/docs/sessions/session.md @@ -1,11 +1,7 @@ # Session: Tracking Individual Conversations
- Supported in ADK - Python v0.1.0 - Go v0.1.0 - Java v0.1.0 - Typescript v0.2.0 + Supported in ADKPython v0.1.0Typescript v0.2.0Go v0.1.0Java v0.1.0
Following our Introduction, let's dive into the `Session`. Think back to the @@ -67,6 +63,37 @@ are its key properties: print("The final status of temp_service - ", temp_service) ``` +=== "TypeScript" + + ```typescript + import { InMemorySessionService } from "@google/adk"; + + // Create a simple session to examine its properties + const tempService = new InMemorySessionService(); + const exampleSession = await tempService.createSession({ + appName: "my_app", + userId: "example_user", + state: {"initial_key": "initial_value"} // State can be initialized + }); + + console.log("--- Examining Session Properties ---"); + console.log(`ID ('id'): ${exampleSession.id}`); + console.log(`Application Name ('appName'): ${exampleSession.appName}`); + console.log(`User ID ('userId'): ${exampleSession.userId}`); + console.log(`State ('state'): ${JSON.stringify(exampleSession.state)}`); // Note: Only shows initial state here + console.log(`Events ('events'): ${JSON.stringify(exampleSession.events)}`); // Initially empty + console.log(`Last Update ('lastUpdateTime'): ${exampleSession.lastUpdateTime}`); + console.log("---------------------------------"); + + // Clean up (optional for this example) + const finalStatus = await tempService.deleteSession({ + appName: exampleSession.appName, + userId: exampleSession.userId, + sessionId: exampleSession.id + }); + console.log("The final status of temp_service - ", finalStatus); + ``` + === "Go" ```go @@ -104,37 +131,6 @@ are its key properties: var unused = exampleSessionService.deleteSession(appName, userId, sessionId); ``` -=== "TypeScript" - - ```typescript - import { InMemorySessionService } from "@google/adk"; - - // Create a simple session to examine its properties - const tempService = new InMemorySessionService(); - const exampleSession = await tempService.createSession({ - appName: "my_app", - userId: "example_user", - state: {"initial_key": "initial_value"} // State can be initialized - }); - - console.log("--- Examining Session Properties ---"); - console.log(`ID ('id'): ${exampleSession.id}`); - console.log(`Application Name ('appName'): ${exampleSession.appName}`); - console.log(`User ID ('userId'): ${exampleSession.userId}`); - console.log(`State ('state'): ${JSON.stringify(exampleSession.state)}`); // Note: Only shows initial state here - console.log(`Events ('events'): ${JSON.stringify(exampleSession.events)}`); // Initially empty - console.log(`Last Update ('lastUpdateTime'): ${exampleSession.lastUpdateTime}`); - console.log("---------------------------------"); - - // Clean up (optional for this example) - const finalStatus = await tempService.deleteSession({ - appName: exampleSession.appName, - userId: exampleSession.userId, - sessionId: exampleSession.id - }); - console.log("The final status of temp_service - ", finalStatus); - ``` - *(**Note:** The state shown above is only the initial state. State updates happen via events, as discussed in the State section.)* @@ -179,6 +175,13 @@ the storage backend that best suits your needs: from google.adk.sessions import InMemorySessionService session_service = InMemorySessionService() ``` + === "TypeScript" + + ```typescript + import { InMemorySessionService } from "@google/adk"; + const sessionService = new InMemorySessionService(); + ``` + === "Go" ```go @@ -194,16 +197,11 @@ the storage backend that best suits your needs: InMemorySessionService exampleSessionService = new InMemorySessionService(); ``` - === "TypeScript" - - ```typescript - import { InMemorySessionService } from "@google/adk"; - const sessionService = new InMemorySessionService(); - ``` - 2. **`VertexAiSessionService`** - ![py_java_only](https://img.shields.io/badge/Currently_supported_in-Python_&_Java-blue){ title="This feature is currently available for Python and Java. TypeScript support is planned/ coming soon."} +
+ Supported in ADKPython v0.1.0Go v0.1.0Java v0.1.0 +
* **How it works:** Uses Google Cloud Vertex AI infrastructure via API calls for session management. @@ -283,11 +281,7 @@ the storage backend that best suits your needs: 3. **`DatabaseSessionService`**
- Supported in ADK - Python v0.1.0 - Go v0.1.0 - Java v0.1.0 - Typescript v0.2.0 + Supported in ADKPython v0.1.0Go v0.1.0
* **How it works:** Connects to a relational database (e.g., PostgreSQL, diff --git a/docs/sessions/state.md b/docs/sessions/state.md index 68e0f6dcd..c59a350ab 100644 --- a/docs/sessions/state.md +++ b/docs/sessions/state.md @@ -1,7 +1,7 @@ # State: The Session's Scratchpad
- Supported in ADKPython v0.1.0Go v0.1.0Java v0.1.0 + Supported in ADKPython v0.1.0TypeScript v0.2.0Go v0.1.0Java v0.1.0
Within each `Session` (our conversation thread), the **`state`** attribute acts like the agent's dedicated scratchpad for that specific interaction. While `session.events` holds the full history, `session.state` is where the agent stores and updates dynamic details needed *during* the conversation. @@ -102,12 +102,6 @@ To inject a value from the session state, enclose the key of the desired state v # "Write a short story about a cat, focusing on the theme: friendship." ``` -=== "Go" - - ```go - --8<-- "examples/go/snippets/sessions/instruction_template/instruction_template_example.go:key_template" - ``` - === "TypeScript" ```typescript @@ -119,9 +113,16 @@ To inject a value from the session state, enclose the key of the desired state v instruction: "Write a short story about a cat, focusing on the theme: {topic}." }); - // Assuming session.state['topic'] is set to "friendship", the LLM + // Assuming session.state['topic'] is set to "friendship", the LLM // will receive the following instruction: // "Write a short story about a cat, focusing on the theme: friendship." + ``` + +=== "Go" + + ```go + --8<-- "examples/go/snippets/sessions/instruction_template/instruction_template_example.go:key_template" + ``` #### Important Considerations @@ -156,12 +157,6 @@ The `InstructionProvider` function receives a `ReadonlyContext` object, which yo ) ``` -=== "Go" - - ```go - --8<-- "examples/go/snippets/sessions/instruction_provider/instruction_provider_example.go:bypass_state_injection" - ``` - === "TypeScript" ```typescript @@ -179,6 +174,13 @@ The `InstructionProvider` function receives a `ReadonlyContext` object, which yo name: "template_helper_agent", instruction: myInstructionProvider }); + ``` + +=== "Go" + + ```go + --8<-- "examples/go/snippets/sessions/instruction_provider/instruction_provider_example.go:bypass_state_injection" + ``` If you want to both use an `InstructionProvider` *and* inject state into your instructions, you can use the `inject_session_state` utility function. @@ -275,18 +277,6 @@ This is the simplest method for saving an agent's final text response directly i # Expected output might include: {'last_greeting': 'Hello there! How can I help you today?'} ``` -=== "Java" - - ```java - --8<-- "examples/java/snippets/src/main/java/state/GreetingAgentExample.java:full_code" - ``` - -=== "Go" - - ```go - --8<-- "examples/go/snippets/sessions/state_example/state_example.go:greeting" - ``` - === "TypeScript" ```typescript @@ -336,7 +326,19 @@ This is the simplest method for saving an agent's final text response directly i const updatedSession = await sessionService.getSession({ appName, userId, sessionId }); console.log(`State after agent run: ${JSON.stringify(updatedSession?.state)}`); // Expected output might include: {"last_greeting":"Hello there! How can I help you today?"} + ``` +=== "Go" + + ```go + --8<-- "examples/go/snippets/sessions/state_example/state_example.go:greeting" + ``` + +=== "Java" + + ```java + --8<-- "examples/java/snippets/src/main/java/state/GreetingAgentExample.java:full_code" + ``` Behind the scenes, the `Runner` uses the `output_key` to create the necessary `EventActions` with a `state_delta` and calls `append_event`. @@ -396,18 +398,6 @@ For more complex scenarios (updating multiple keys, non-string values, specific # Note: 'temp:validation_needed' is NOT present. ``` -=== "Go" - - ```go - --8<-- "examples/go/snippets/sessions/state_example/state_example.go:manual" - ``` - -=== "Java" - - ```java - --8<-- "examples/java/snippets/src/main/java/state/ManualStateUpdateExample.java:full_code" - ``` - === "TypeScript" ```typescript @@ -463,6 +453,18 @@ For more complex scenarios (updating multiple keys, non-string values, specific // Note: 'temp:validation_needed' is NOT present. ``` +=== "Go" + + ```go + --8<-- "examples/go/snippets/sessions/state_example/state_example.go:manual" + ``` + +=== "Java" + + ```java + --8<-- "examples/java/snippets/src/main/java/state/ManualStateUpdateExample.java:full_code" + ``` + **3. Via `CallbackContext` or `ToolContext` (Recommended for Callbacks and Tools)** Modifying state within agent callbacks (e.g., `on_before_agent_call`, `on_after_agent_call`) or tool functions is best done using the `state` attribute of the `CallbackContext` or `ToolContext` provided to your function. @@ -496,6 +498,28 @@ For more comprehensive details on context objects, refer to the [Context documen # ... rest of callback/tool logic ... ``` +=== "TypeScript" + + ```typescript + // In an agent callback or tool function + import { CallbackContext } from "@google/adk"; // or ToolContext + + function myCallbackOrToolFunction( + context: CallbackContext, // Or ToolContext + // ... other parameters ... + ) { + // Update existing state + const count = context.state.get("user_action_count", 0); + context.state.set("user_action_count", count + 1); + + // Add new state + context.state.set("temp:last_operation_status", "success"); + + // State changes are automatically part of the event's stateDelta + // ... rest of callback/tool logic ... + } + ``` + === "Go" ```go @@ -524,28 +548,6 @@ For more comprehensive details on context objects, refer to the [Context documen } ``` -=== "TypeScript" - - ```typescript - // In an agent callback or tool function - import { CallbackContext } from "@google/adk"; // or ToolContext - - function myCallbackOrToolFunction( - context: CallbackContext, // Or ToolContext - // ... other parameters ... - ) { - // Update existing state - const count = context.state.get("user_action_count", 0); - context.state.set("user_action_count", count + 1); - - // Add new state - context.state.set("temp:last_operation_status", "success"); - - // State changes are automatically part of the event's stateDelta - // ... rest of callback/tool logic ... - } - ``` - **What `append_event` Does:** * Adds the `Event` to `session.events`.