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..9d2b3aba00 --- /dev/null +++ b/docs/develop/typescript/integrations/google-adk-agents.mdx @@ -0,0 +1,237 @@ +--- +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 agent graphs as durable Temporal Workflows while model and MCP calls execute as retryable Activities. +--- + +import { ReleaseNoteHeader } from '@site/src/components'; + +Temporal's integration with the [Google Agent Development Kit (ADK)](https://google.github.io/adk-docs/) lets you run +ADK agents as durable Temporal Workflows. The agent graph, including its orchestration, tool selection, and state, runs +inside the Workflow. Model inference and Model Context Protocol (MCP) calls run as Activities. + +This separation keeps the ADK programming model while adding Temporal's failure recovery. A Worker can stop while an +agent is running, then another Worker can replay the Workflow and continue from the last completed model or MCP call. +Temporal records each Activity result in Event History, so those calls aren't repeated during replay. + +The `GoogleAdkPlugin` configures the Worker for ADK, and `TemporalModel` replaces a standard ADK model inside Workflow +code. The integration also provides Workflow-safe APIs for Activity-backed tools, MCP servers, and streaming model +responses. + + + +The code excerpts in this guide come from the +[Google ADK samples](https://github.com/temporalio/samples-typescript/tree/main/google-adk-agents). Refer to the samples +for complete applications that run with a real model or an API-key-free test model. + +## 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 if you want to run the samples locally. + +## Install the Google ADK integration + +Install the Temporal 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 @google/genai +``` + +The Worker reads Gemini credentials from `GOOGLE_API_KEY` or `GEMINI_API_KEY`. Credentials stay in the Worker process +and are not stored in Workflow inputs or Event History. + +## Run an ADK agent in a Workflow + +Use the standard ADK `LlmAgent` and runner APIs in your Workflow, but configure the agent with `TemporalModel`. Each +call through `TemporalModel` becomes an Activity, while the runner and agent graph remain in deterministic Workflow +code. + + +[google-adk-agents/src/agent-chat/workflows.ts](https://github.com/temporalio/samples-typescript/blob/main/google-adk-agents/src/agent-chat/workflows.ts) +```ts +const agent = new LlmAgent({ + name: 'assistant', + model: new TemporalModel('gemini-2.5-flash'), + instruction: 'Continue the conversation using its prior context. Respond in one sentence.', +}); +const runner = new InMemoryRunner({ agent, appName: 'agent-chat' }); +``` + + +Register `GoogleAdkPlugin` on the Worker that executes the Workflow. The plugin installs the model Activities and the +Workflow bundler configuration required by Google ADK. + + +[google-adk-agents/src/agent-chat/worker.ts](https://github.com/temporalio/samples-typescript/blob/main/google-adk-agents/src/agent-chat/worker.ts) +```ts +const worker = await Worker.create({ + connection, + taskQueue: 'google-adk-agent-chat', + workflowsPath: require.resolve('./workflows'), + plugins: [ + new GoogleAdkPlugin(process.env.MODEL_PROVIDER === 'fake' ? { modelProvider: offlineModelProvider() } : {}), + ], +}); +await worker.run(); +``` + + +The default model provider uses Google ADK's model registry. You can pass a custom `modelProvider` to +`GoogleAdkPlugin` to configure another provider, route model names through a proxy, or supply a test double. Register +the plugin on the Worker; a Client plugin is not required. + +## 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` from `@temporalio/google-adk-agents/workflow` to expose an existing Activity to an agent. The tool +name identifies the registered Activity, and its Activity options control timeouts and retries. The +[tools sample](https://github.com/temporalio/samples-typescript/tree/main/google-adk-agents/src/tools) shows a +deterministic function tool and an Activity-backed weather tool in the same agent. + +For MCP, register a named toolset factory in `GoogleAdkPlugin` on the Worker, then use a `TemporalMCPToolset` with the +same name in Workflow code. Listing tools and calling them execute as Activities. The +[MCP sample](https://github.com/temporalio/samples-typescript/tree/main/google-adk-agents/src/mcp) shows this pairing +with a stateless filesystem server and an API-key-free test implementation. + +## Stream model responses + +Set `streamingTopic` in `TemporalModel` options to publish model response chunks through +[`@temporalio/workflow-streams`](https://www.npmjs.com/package/@temporalio/workflow-streams). Stream delivery is +at-least-once. The complete model response returned by the Activity is the deterministic value used by the Workflow. + +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 your agents + +The `@temporalio/google-adk-agents/testing` entry point provides `fakeModelProvider` and `mockMCPToolset`. Pass these +helpers to `GoogleAdkPlugin` to test an agent without model credentials or a live MCP server. This keeps model and tool +behavior controlled while exercising the real Worker plugin, Workflow bundle, and Activities. + +The Google ADK samples use the same testing APIs for their API-key-free execution path. For Workflow changes, also use +[replay testing](/develop/typescript/best-practices/testing-suite#replay) to verify that the current code +remains compatible with recorded Event Histories. + +## Add observability + +Compose `GoogleAdkPlugin` after `OpenTelemetryPlugin` from `@temporalio/interceptors-opentelemetry` to export ADK's +agent, model, and tool spans from the Workflow sandbox. The Workflow interceptor suppresses span export during 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. + +Model and MCP calls appear as Activities in Temporal Event History even when OpenTelemetry is not configured. ADK span +attributes can contain prompts and model responses, so send them only to an approved destination or remove sensitive +attributes in your span processor. + +## Reference and troubleshooting + +### Feature support + +The integration supports ADK agents and runners, Activity-backed model calls, deterministic function tools, +Activity-backed tools, MCP tool discovery and calls, server-sent event (SSE) model streaming, test doubles, and ADK +OpenTelemetry spans. + +Live bidirectional streaming through `BaseLlm.connect` isn't supported inside Workflows. ADK extension points that +perform I/O must run in Activities. `TemporalMCPToolset` accepts a list of tool names as its filter, but it doesn't +support ADK's `ToolPredicate` filter. + +### Composing with other plugins + +Register observability and governance plugins before `GoogleAdkPlugin`. For example, placing `OpenTelemetryPlugin` first +installs the Workflow tracer provider before ADK creates spans. + +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 `summary` callback run in Workflow code and must remain +deterministic. Model calls, MCP operations, and tools created with `activityAsTool` run as Activities. Their completed +results come from Event History during replay instead of executing again. + +Streaming publishes an at-least-once side channel, but the complete Activity result remains the deterministic value +returned to the Workflow. OpenTelemetry span export is also at-least-once: replay doesn't emit spans again, but a +Workflow Task retry can. + +### Configuration + +The following tables cover the integration-specific options. Options under `activity` accept the standard TypeScript SDK +`ActivityOptions` fields. + +| API | Option | Default | Behavior | +| ----------------- | ------------------------ | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | +| `GoogleAdkPlugin` | `modelProvider` | ADK `LLMRegistry` | Resolves a model name in the model Activities. Use it to configure another provider, a proxy, or a test double. | +| `GoogleAdkPlugin` | `mcpToolsets` | `{}` | Maps each name to an MCP factory and registers `-listTools` and `-callTool` Activities. | +| `TemporalModel` | `activity` | `startToCloseTimeout: '1 minute'` | Configures every model Activity. An explicit `startToCloseTimeout` overrides the default. | +| `TemporalModel` | `summary` | ADK agent name, then `adk.invokeModel ` | Sets the Activity summary. A function receives the model request and must be deterministic. This option 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 streaming 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 the listed tool names. Names are matched after applying `prefix`. | +| `TemporalMCPToolset` | `prefix` | None | Advertises each tool as `_` without changing its name on the MCP server. | +| `TemporalMCPToolset` | `activity` | `startToCloseTimeout: '1 minute'` | Configures both tool discovery and tool-call Activities. | +| `TemporalMCPToolset` | `connectionParams` | None | Creates a real MCP toolset only when used outside a Workflow. Worker-side MCP configuration belongs in `mcpToolsets`. | +| `activityAsTool` | `name` | Required | Names both 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 model name used by the test double. | +| `FakeLlm` | `responses` | One canned text response | Sets the responses yielded in order by the test double. | +| `fakeModelProvider` | `responses` | One canned text response | Creates a model provider that 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 open separate +sessions for discovery and the tool call. + +When `heartbeatTimeout` is set, model and MCP Activities heartbeat on a timer at half the timeout. Streaming model +Activities also heartbeat for each chunk. These heartbeats deliver cancellation and detect a stopped Worker, but they +don't detect a hung model or MCP call; `startToCloseTimeout` bounds that call. + +### 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 normally converts an error from an agent's model call into an event. The integration records that absorbed failure +and re-raises it after the Workflow or handler frame that ran the agent turn returns. To recover in an ADK +`onModelErrorCallback`, pass the received 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 + +- [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..715f2312a5 100644 --- a/src/components/IntegrationsGrid/integrations-data.json +++ b/src/components/IntegrationsGrid/integrations-data.json @@ -89,6 +89,15 @@ "sdk": "Go", "href": "/develop/go/integrations/google-adk" }, + { + "name": "Google ADK", + "description": "Run Google ADK agents as durable Temporal Workflows with the TypeScript SDK.", + "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.",