diff --git a/docs/develop/typescript/index.mdx b/docs/develop/typescript/index.mdx index 4cf2e9edcc..eba8a0c866 100644 --- a/docs/develop/typescript/index.mdx +++ b/docs/develop/typescript/index.mdx @@ -85,6 +85,7 @@ Once your local Temporal Service is set up, continue building with the following ## [Integrations](/develop/typescript/integrations) - [Braintrust integration](https://www.braintrust.dev/docs/integrations/sdk-integrations/temporal#typescript) +- [Google ADK integration](/develop/typescript/integrations/google-adk-agents) - [LangSmith integration](/develop/typescript/integrations/langsmith) - [Mastra integration](https://mastra.ai/guides/deployment/temporal) - [OpenAI Agents SDK integration](/develop/typescript/integrations/openai-agents) diff --git a/docs/develop/typescript/integrations/google-adk-agents.mdx b/docs/develop/typescript/integrations/google-adk-agents.mdx new file mode 100644 index 0000000000..7d97a390c1 --- /dev/null +++ b/docs/develop/typescript/integrations/google-adk-agents.mdx @@ -0,0 +1,260 @@ +--- +id: google-adk-agents +title: Google ADK integration +sidebar_label: Google ADK +toc_max_heading_level: 2 +tags: + - Google ADK + - TypeScript SDK + - Temporal SDKs +description: Run Google ADK agents as durable Temporal Workflows in TypeScript, with model and MCP calls running as retryable Activities. +--- + +import { ReleaseNoteHeader } from '@site/src/components'; + +The Temporal Google Agent Development Kit (ADK) integration runs an ADK agent graph as a durable Temporal Workflow. +The agent loop, tool selection, and state run in the Workflow, while model inference and Model Context Protocol (MCP) +operations run as Activities. Completed calls are recorded in Event History and aren't repeated during replay. + +`GoogleAdkPlugin` configures the Worker and Workflow bundle. In Workflow code, `TemporalModel` replaces a standard ADK +model and routes each model call to an Activity. The integration also provides Workflow-safe APIs for Activity-backed +tools, MCP servers, and streamed model responses. + + + +## Prerequisites + +- This guide assumes you are familiar with Google ADK. If you aren't, refer to the + [Google ADK documentation](https://google.github.io/adk-docs/) for an introduction to agents, runners, and tools. +- If you are new to Temporal, read [Understanding Temporal](/evaluate/understanding-temporal) or take the + [Temporal 101](https://learn.temporal.io/courses/temporal_101/) course. +- Set up your local development environment by following + [Set up your local with the TypeScript SDK](/develop/typescript/set-up-your-local-typescript). Leave the Temporal + development server running to run the samples locally. +- Install Node.js 22 or later to run the samples. + +## Install the Google ADK integration + +Install the integration and its Google ADK peer dependencies. Keep all `@temporalio/*` packages in your application on +the same version. + +```bash +npm install @temporalio/google-adk-agents "@google/adk@>=1.5.0 <1.6.0" "@google/genai@^2.9.0" +``` + +Version 1.23.0 of the integration supports `@google/adk` 1.5.x. The upper bound is required because the Workflow bundle +uses compatibility shims for that ADK line. Newer ADK versions can fail while bundling the Workflow. + +The Worker reads Gemini credentials from `GOOGLE_GENAI_API_KEY` or `GEMINI_API_KEY`. Credentials remain in the Worker +process. Model requests and responses are Activity inputs and results, so they are stored in Event History. Use a +[Payload Codec to encrypt sensitive data](/develop/typescript/best-practices/data-handling/data-encryption), and account +for the [programming model limits](/cloud/limits#programming-model-level), including the 2 MB limit on a single payload. + +## Run an ADK agent in a Workflow + +The [basic Google ADK sample](https://github.com/temporalio/samples-typescript/tree/main/google-adk-agents/src/basic) +contains a complete Workflow, Worker, and Client. It uses the standard ADK `LlmAgent` and `InMemoryRunner` APIs in the +Workflow, with `TemporalModel` as the agent's model. The runner iterates over ADK events and returns the final response. + +The Worker registers `GoogleAdkPlugin`, which installs the model Activities and the Workflow bundler configuration. +The Client starts the Workflow normally and doesn't need the plugin. + +Use each API from its package entry point: + +| Entry point | APIs | +| --- | --- | +| `@temporalio/google-adk-agents` | `GoogleAdkPlugin` for Worker code | +| `@temporalio/google-adk-agents/workflow` | `TemporalModel`, `TemporalMCPToolset`, and `activityAsTool` for Workflow code | +| `@temporalio/google-adk-agents/testing` | `fakeModelProvider` and `mockMCPToolset` for tests | + +Start the Worker, then run the Client from the sample directory: + +```bash +npx tsx src/basic/worker.ts +npx tsx src/basic/client.ts +``` + +## Configure model calls + +Pass Activity options to `TemporalModel` to set timeouts, retries, a Task Queue, or an Activity summary. The default +`startToCloseTimeout` is one minute. + +```ts +const model = new TemporalModel('gemini-2.5-flash', { + activity: { + startToCloseTimeout: '5 minutes', + heartbeatTimeout: '30 seconds', + retry: { maximumAttempts: 3 }, + }, +}); +``` + +The plugin disables retries in the underlying model SDK so that the Activity retry policy controls retries and backoff. +Set `heartbeatTimeout` to detect a dead Worker and deliver cancellation to a long model call. The Activity heartbeats on +a timer, so a Heartbeat Timeout doesn't detect a stalled call; `startToCloseTimeout` bounds a stalled call. + +## Add tools and MCP servers + +Google ADK function tools run as part of the agent graph inside the Workflow. Use them for deterministic operations, +such as transforming values or updating agent state. A tool that reads a file, calls an API, queries a database, or +performs other I/O must run outside the Workflow. + +Use `activityAsTool` to expose an existing Activity to an agent. Its `name` must match an Activity registered in the +Worker's `activities` option. The model's arguments object becomes the Activity's single argument, and the `activity` +option controls its timeouts and retries. The [tools sample](https://github.com/temporalio/samples-typescript/tree/main/google-adk-agents/src/tools) +shows the Workflow and Worker configuration together. + +For MCP, register a named factory with `mcpToolsets: { : factory }` in the Worker plugin, then create a +`TemporalMCPToolset` with the same name in Workflow code. Listing tools and calling them execute as Activities. Each +operation opens a new MCP session, so session state doesn't carry between operations unless the factory returns a +long-lived toolset. MCP failures often have no status and are retryable, so set `activity.retry.maximumAttempts` on the +`TemporalMCPToolset` when retries must be bounded. See the complete Worker and Workflow pair in the +[MCP sample](https://github.com/temporalio/samples-typescript/tree/main/google-adk-agents/src/mcp). + +## Stream model responses + +Install `@temporalio/workflow-streams`, host a `WorkflowStream` at the top of the Workflow, and set `streamingTopic` in +the `TemporalModel` options. The model call must also request streaming, either through ADK runner configuration with +`StreamingMode.SSE` or by calling `generateContentAsync(request, true)`. A streaming call without a topic fails. + +The model Activity publishes chunks to the topic and returns the complete response to the Workflow. The Activity result +is the deterministic value used during replay. Stream delivery is at-least-once, and a retried Activity publishes its +chunks again from the beginning. + +The [streaming sample](https://github.com/temporalio/samples-typescript/tree/main/google-adk-agents/src/streaming) +shows how a Workflow publishes chunks and waits for a stream consumer to finish. + +## Test and observe your agents + +The testing entry point provides `fakeModelProvider` and `mockMCPToolset`. Pass them to `GoogleAdkPlugin` to test without +model credentials or a live MCP server while exercising the Worker plugin, Workflow bundle, and Activities. + +Use [replay testing](/develop/typescript/best-practices/testing-suite#replay) for Workflow changes. Pass +`plugins: [new GoogleAdkPlugin()]` to `Worker.runReplayHistory` because the plugin's bundler configuration is required +to load Google ADK in the replay sandbox. + +Compose `GoogleAdkPlugin` after `OpenTelemetryPlugin` from `@temporalio/interceptors-opentelemetry` to export ADK's +agent, model, and tool spans from the Workflow sandbox. + +```ts +plugins: [new OpenTelemetryPlugin({ resource, spanProcessor }), new GoogleAdkPlugin()] +``` + +The OpenTelemetry plugin's Worker sink suppresses span export during replay. Export is at-least-once because a failed or +timed-out Workflow Task can execute again without being a replay. +The [observability sample](https://github.com/temporalio/samples-typescript/tree/main/google-adk-agents/src/observability) +shows the plugin order and an OpenTelemetry span processor that records model usage. + +ADK span attributes can contain prompts and model responses. Send them only to an approved destination or remove +sensitive attributes in the span processor. `ADK_CAPTURE_MESSAGE_CONTENT_IN_SPANS` doesn't control this behavior in a +Workflow because the Workflow sandbox doesn't expose Worker environment variables. + +## Reference and troubleshooting + +### Feature support + +| Feature | Support | Notes | +| --- | --- | --- | +| ADK agent graphs, including sequential and delegated multi-agent patterns | Supported | Compose agents normally; delegated agents remain part of the durable Workflow graph. | +| Function tools and Activities as tools | Supported | Regular function tools run in the Workflow and must be deterministic; `activityAsTool` moves I/O to Activities. | +| MCP toolsets | Supported | `toolFilter` accepts only string arrays, not ADK `ToolPredicate`, and filters advertised names after prefixing. Connection-parameter factories open one session per Activity; factory-returned `BaseToolset`s may remain long-lived. | +| Human approval through Signals or Updates | Supported | Use Signals for asynchronous input or Updates when the caller needs an accepted result. | +| Structured model output | Supported | ADK output schemas work with durable agent execution. | +| SSE response streaming | Supported | Requires `streamingTopic`; delivery is at-least-once, and retries may republish from the beginning. | +| OpenTelemetry tracing | Supported | Register `OpenTelemetryPlugin` before `GoogleAdkPlugin`. | +| Live bidirectional streaming through `BaseLlm.connect` | Not supported in Workflows | Use SSE response streaming for supported streaming behavior. | + +### Composing with other plugins + +Register observability and governance plugins before `GoogleAdkPlugin`. In particular, place `OpenTelemetryPlugin` +first so that ADK's Workflow-side spans bind to its tracer provider. Register `GoogleAdkPlugin` only on the Worker; a +Client plugin isn't required. + +Custom payload and failure converter modules load before the plugin's polyfills. If either module imports `@google/adk` +or `@google/genai`, import `@temporalio/google-adk-agents/workflow` first in that module. + +### Replay safety + +The ADK runner, agent graph, regular function tools, and callbacks execute inside the Workflow and must remain +deterministic. `TemporalModel`, `TemporalMCPToolset`, and `activityAsTool` move model calls, MCP operations, and other I/O +into Activities. Completed Activity results are read from Event History during replay. + +The full model request and response cross the Activity boundary and are recorded in Event History. For long-running +conversations, use [Continue-As-New](/develop/typescript/workflows/continue-as-new) before Event History approaches its +limits. + +### Configuration + +Options under `activity` accept the standard TypeScript SDK `ActivityOptions` fields. + +| API | Option | Default | Behavior | +| --- | --- | --- | --- | +| `GoogleAdkPlugin` | `modelProvider` | ADK `LLMRegistry` | Resolves model names in model Activities. Use it for another provider, proxy, or test double. | +| `GoogleAdkPlugin` | `mcpToolsets` | `{}` | Maps names to MCP factories and registers `-listTools` and `-callTool` Activities. | +| `TemporalModel` | `activity` | `startToCloseTimeout: '1 minute'` | Configures every model Activity. | +| `TemporalModel` | `summary` | ADK agent name, then `adk.invokeModel ` | Sets the Activity summary. A function receives the request and must be deterministic. This takes precedence over `activity.summary`. | +| `TemporalModel` | `streamingTopic` | None | Publishes SSE response chunks to this Workflow streams topic when streaming is requested. | +| `TemporalModel` | `streamingBatchInterval` | `'100 milliseconds'` | Sets how frequently response chunks are batched for publication. | + +| API | Option | Default | Behavior | +| --- | --- | --- | --- | +| `TemporalMCPToolset` | `name` | Required | Selects the Worker-registered factory and names its Activity pair. | +| `TemporalMCPToolset` | `toolFilter` | All tools | Advertises only listed tool names, matched after applying `prefix`. ADK `ToolPredicate` filters aren't supported. | +| `TemporalMCPToolset` | `prefix` | None | Advertises each tool as `_` without changing its MCP server name. | +| `TemporalMCPToolset` | `activity` | `startToCloseTimeout: '1 minute'` | Configures tool discovery and tool-call Activities. | +| `TemporalMCPToolset` | `connectionParams` | None | Creates a real MCP toolset only outside a Workflow. Worker-side configuration belongs in `mcpToolsets`. | +| `activityAsTool` | `name` | Required | Names the tool and the registered Activity it calls. | +| `activityAsTool` | `description` | Required | Describes the tool to the model. | +| `activityAsTool` | `parameters` | Empty object schema | Defines the arguments passed to the Activity as its single input. | +| `activityAsTool` | `activity` | `startToCloseTimeout: '1 minute'` | Configures the Activity call. | +| `FakeLlm` | `model` | `'fake-model'` | Sets the test double's model name. | +| `FakeLlm` | `responses` | One canned text response | Sets the responses yielded in order. | +| `fakeModelProvider` | `responses` | One canned text response | Returns a `FakeLlm` for every model name. | +| `mockMCPToolset` | `definitions` | Required | Creates an MCP factory from tool declarations and handlers. | + +An MCP factory can return connection parameters or a `BaseToolset`. Connection parameters create and close one MCP +session per Activity. A `BaseToolset` remains owned by the factory, isn't closed by the plugin, and can maintain state. + +### Failure behavior + +The plugin exports constants for its public `ApplicationFailure.type` values. Model and MCP failures originate in +Activities, so catch the surrounding `ActivityFailure` and inspect its cause chain for these types. + +| Failure type | Meaning | +| --- | --- | +| `GoogleAdkModelError[.]` | A model call failed. Statuses 408, 409, 429, and 5xx are retryable; other HTTP statuses are non-retryable. A failure without a status is retryable. `x-should-retry` overrides this classification, and `retry-after` or `retry-after-ms` sets the next retry delay. | +| `GoogleAdkMCPError[.]` | MCP discovery or a tool call failed. It uses the same status classification as model errors. Failures without a status are retryable, so set `activity.retry.maximumAttempts` to bound retries. | +| `GoogleAdkMCPToolNotFound` | A factory-provided `BaseToolset` didn't contain the requested tool. This failure is non-retryable. | +| `GoogleAdkStreamingTopicRequired` | SSE streaming was requested without `streamingTopic`. This failure is non-retryable and is thrown directly in the Workflow. | +| `GoogleAdkUnsupported` | `BaseLlm.connect` was called in a Workflow. This failure is non-retryable and is thrown directly in the Workflow. | + +ADK converts an error from an agent's model call into an event. The integration records that failure and re-raises it +after the Workflow or handler frame returns. To recover in an ADK `onModelErrorCallback`, pass the error to +`markModelFailureHandled` and return a substitute event created with ADK's `createEvent`. Cancellation can't be handled +this way. + +If a model call fails with a sandbox error such as `fetch is not defined`, check that the agent uses +`new TemporalModel(...)` instead of a raw model string. A raw model makes ADK attempt the network call inside the +Workflow instead of routing it to an Activity. + +## Resources + +- The [basic sample](https://github.com/temporalio/samples-typescript/tree/main/google-adk-agents/src/basic) runs one + durable model call. +- The [tools sample](https://github.com/temporalio/samples-typescript/tree/main/google-adk-agents/src/tools) exposes an + Activity as an ADK tool. +- The [agent patterns sample](https://github.com/temporalio/samples-typescript/tree/main/google-adk-agents/src/agent-patterns) + delegates work across multiple agents. +- The [MCP sample](https://github.com/temporalio/samples-typescript/tree/main/google-adk-agents/src/mcp) connects an MCP + server through Activities. +- The [streaming sample](https://github.com/temporalio/samples-typescript/tree/main/google-adk-agents/src/streaming) + publishes model response chunks through Workflow streams. +- The [human approval sample](https://github.com/temporalio/samples-typescript/tree/main/google-adk-agents/src/human-approval) + gates a long-running tool with a Signal or Update. +- The [observability sample](https://github.com/temporalio/samples-typescript/tree/main/google-adk-agents/src/observability) + exports ADK spans and records model usage. +- [Google ADK integration package](https://www.npmjs.com/package/@temporalio/google-adk-agents) +- [Google ADK integration source](https://github.com/temporalio/sdk-typescript/tree/main/contrib/google-adk-agents) +- [Google ADK samples](https://github.com/temporalio/samples-typescript/tree/main/google-adk-agents) +- [Google ADK documentation](https://google.github.io/adk-docs/) +- [Temporal TypeScript SDK documentation](/develop/typescript) diff --git a/sidebars.js b/sidebars.js index ba42163641..481b4f8010 100644 --- a/sidebars.js +++ b/sidebars.js @@ -1131,6 +1131,7 @@ const developTypeScriptCategory = { }, items: [ 'develop/typescript/integrations/ai-sdk', + 'develop/typescript/integrations/google-adk-agents', 'develop/typescript/integrations/langsmith', 'develop/typescript/integrations/openai-agents', 'develop/typescript/integrations/strands-agents', diff --git a/src/components/IntegrationsGrid/integrations-data.json b/src/components/IntegrationsGrid/integrations-data.json index 081cb5624e..7ab5c7880b 100644 --- a/src/components/IntegrationsGrid/integrations-data.json +++ b/src/components/IntegrationsGrid/integrations-data.json @@ -73,7 +73,7 @@ }, { "name": "Google ADK", - "description": "Orchestrate Google ADK agents with durable Temporal Workflows.", + "description": "Run Google ADK agents as durable Temporal Workflows.", "tags": [ "Agent framework" ], @@ -82,13 +82,22 @@ }, { "name": "Google ADK", - "description": "Run Google ADK agents with durable execution using the Temporal Go SDK.", + "description": "Run Google ADK agents as durable Temporal Workflows.", "tags": [ "Agent framework" ], "sdk": "Go", "href": "/develop/go/integrations/google-adk" }, + { + "name": "Google ADK", + "description": "Run Google ADK agents as durable Temporal Workflows.", + "tags": [ + "Agent framework" + ], + "sdk": "TypeScript", + "href": "/develop/typescript/integrations/google-adk-agents" + }, { "name": "Google GenAI", "description": "Call Google Gemini models durably from Temporal Workflows with the Google Gen AI SDK.",