Skip to content

Commit f02fca3

Browse files
brianstrauchclaude
andcommitted
Merge branch 'main' into google-genai-plugin-samples
Resolve conflicts in .github/CODEOWNERS, README.md, and pyproject.toml by keeping both the google-genai-plugin and incoming AI SDK sample entries in alphabetical order, and regenerate uv.lock. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2 parents a7794d0 + f65112a commit f02fca3

47 files changed

Lines changed: 2370 additions & 98 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/CODEOWNERS‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,16 @@
1212
/tests/nexus*/ @temporalio/nexus
1313
/tests/*nexus/ @temporalio/nexus
1414

15-
/google_genai_plugin/ @temporalio/sdk @temporalio/ai-sdk
15+
# The AI SDK team owns the AI integration samples and their tests. We add
16+
# @temporalio/sdk too, so the SDK team can continue to manage repo-wide concerns.
17+
/google_adk_agents/ @temporalio/sdk @temporalio/ai-sdk
18+
/google_genai_plugin/ @temporalio/sdk @temporalio/ai-sdk
19+
/langgraph_plugin/ @temporalio/sdk @temporalio/ai-sdk
20+
/langsmith_tracing/ @temporalio/sdk @temporalio/ai-sdk
21+
/openai_agents/ @temporalio/sdk @temporalio/ai-sdk
22+
/strands_plugin/ @temporalio/sdk @temporalio/ai-sdk
23+
/tests/google_adk_agents/ @temporalio/sdk @temporalio/ai-sdk
1624
/tests/google_genai_plugin/ @temporalio/sdk @temporalio/ai-sdk
25+
/tests/langgraph_plugin/ @temporalio/sdk @temporalio/ai-sdk
26+
/tests/langsmith_tracing/ @temporalio/sdk @temporalio/ai-sdk
27+
/tests/strands_plugin/ @temporalio/sdk @temporalio/ai-sdk

‎README.md‎

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,32 +72,42 @@ Some examples require extra dependencies. See each sample's directory for specif
7272
* [external_storage](external_storage) - Offload large payloads to S3-compatible object storage, plus a codec server for the Web UI and CLI.
7373
* [external_storage_redis](external_storage_redis) - Redis driver for external storage
7474
* [gevent_async](gevent_async) - Combine gevent and Temporal.
75+
* [google_adk_agents](google_adk_agents) - Run Google ADK agents as durable Temporal workflows (model calls, tools, multi-agent, MCP, streaming).
7576
* [google_genai_plugin](google_genai_plugin) - Run the Google Gemini SDK inside durable Temporal workflows (generate_content, tools/AFC, streaming, chat, structured output, MCP, files, interactions, agents, Vertex AI).
77+
* [hello_nexus](hello_nexus) - Define a Nexus service, implement operation handlers, and call them from a workflow.
7678
* [hello_standalone_activity](hello_standalone_activity) - Use activities without using a workflow.
77-
* [langchain](langchain) - Orchestrate workflows for LangChain.
79+
* [lambda_worker](lambda_worker) - Run a Temporal Worker inside an AWS Lambda function.
7880
* [langgraph_plugin](langgraph_plugin) - Run LangGraph workflows as durable Temporal workflows (Graph API and Functional API).
81+
* [langsmith_tracing](langsmith_tracing) - Trace Temporal workflows with LangSmith via the LangSmith plugin.
7982
* [message_passing/introduction](message_passing/introduction/) - Introduction to queries, signals, and updates.
8083
* [message_passing/safe_message_handlers](message_passing/safe_message_handlers/) - Safely handling updates and signals.
8184
* [message_passing/update_with_start/lazy_initialization](message_passing/update_with_start/lazy_initialization/) - Use update-with-start to update a Shopping Cart, starting it if it does not exist.
85+
* [nexus_cancel](nexus_cancel) - Fan out concurrent Nexus operations, take the first result, and cancel the rest.
8286
* [Nexus Messaging](nexus_messaging): Demonstrates how send signal, update and query messages through Nexus.
8387
This contains two samples, one sending messages to an existing workflow and a second that creates a workflow through Nexus
8488
and sends messages to it.
89+
* [nexus_multiple_args](nexus_multiple_args) - Map a Nexus operation to a handler workflow that takes multiple arguments.
8590
* [nexus_standalone_operations](nexus_standalone_operations) - Execute Nexus operations directly from client code,
8691
without wrapping them in a workflow.
8792
* [open_telemetry](open_telemetry) - Trace workflows with OpenTelemetry.
93+
* [openai_agents](openai_agents) - Run OpenAI Agents SDK agents as durable Temporal workflows.
8894
* [patching](patching) - Alter workflows safely with `patch` and `deprecate_patch`.
8995
* [polling](polling) - Recommended implementation of an activity that needs to periodically poll an external resource waiting its successful completion.
9096
* [prometheus](prometheus) - Configure Prometheus metrics on clients/workers.
91-
* [workflow_streams](workflow_streams) - Workflow-hosted durable event stream via `temporalio.contrib.workflow_streams`. **Experimental**
9297
* [pydantic_converter](pydantic_converter) - Data converter for using Pydantic models.
98+
* [pydantic_converter_v1](pydantic_converter_v1) - Data converter for Pydantic v1 models (prefer pydantic_converter for v2).
99+
* [replay](replay) - Verify that workflow code changes are compatible with existing histories.
100+
* [resource_pool](resource_pool) - Allocate a pool of shared resources across workflows.
93101
* [schedules](schedules) - Demonstrates a Workflow Execution that occurs according to a schedule.
94102
* [sentry](sentry) - Report errors to Sentry.
103+
* [sleep_for_days](sleep_for_days) - A workflow that runs forever, sending an email every 30 days.
95104
* [strands_plugin](strands_plugin) - Run Strands Agents as durable Temporal workflows (model calls, tools, MCP, HITL).
96105
* [trio_async](trio_async) - Use asyncio Temporal in Trio-based environments.
97106
* [updatable_timer](updatable_timer) - A timer that can be updated while sleeping.
107+
* [worker_multiprocessing](worker_multiprocessing) - Leverage Python multiprocessing to parallelize workflow tasks and other CPU bound operations by running multiple workers.
98108
* [worker_specific_task_queues](worker_specific_task_queues) - Use unique task queues to ensure activities run on specific workers.
99109
* [worker_versioning](worker_versioning) - Use the Worker Versioning feature to more easily version your workflows & other code.
100-
* [worker_multiprocessing](worker_multiprocessing) - Leverage Python multiprocessing to parallelize workflow tasks and other CPU bound operations by running multiple workers.
110+
* [workflow_streams](workflow_streams) - Workflow-hosted durable event stream via `temporalio.contrib.workflow_streams`. **Experimental**
101111

102112
## Test
103113

‎google_adk_agents/README.md‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Temporal Google ADK Integration
2+
3+
⚠️ **Experimental** — This integration is experimental and its interfaces may
4+
change prior to General Availability.
5+
6+
This directory contains samples demonstrating how to run
7+
[Google ADK](https://google.github.io/adk-docs/) agents durably inside Temporal
8+
workflows using `temporalio.contrib.google_adk_agents`. Each scenario is a
9+
self-contained subdirectory with its own worker, workflow starter, workflow and
10+
activity packages, and README.
11+
12+
## Overview
13+
14+
The integration combines:
15+
16+
- **Temporal workflows** for durable orchestration of agent control flow
17+
- **Google ADK** for agent creation, model calls, tools, and MCP integration
18+
19+
`GoogleAdkPlugin` configures a Pydantic payload converter, sandbox passthrough
20+
for `google.adk` / `google.genai` / `mcp`, a deterministic ADK runtime, and the
21+
model activities. `TemporalModel` runs each LLM call as an activity, so every
22+
model turn is durable and observable.
23+
24+
## Prerequisites
25+
26+
- Temporal server [running locally](https://docs.temporal.io/cli/server#start-dev)
27+
- Dependencies installed via `uv sync --group google-adk`
28+
- Google API key set as an environment variable:
29+
`export GOOGLE_API_KEY=your_key_here`
30+
31+
All scenarios default to the `gemini-2.5-flash` model. ADK also supports other
32+
providers (for example, non-Gemini models via LiteLLM); swap the model name on
33+
`TemporalModel` to use one.
34+
35+
## Scenarios
36+
37+
Each directory contains a complete example with its own README:
38+
39+
| Scenario | What it shows |
40+
| --- | --- |
41+
| [basic](./basic/README.md) | A single ADK agent with `TemporalModel` and one model call — no tools. The minimal end-to-end example. |
42+
| [tools](./tools/README.md) | A Temporal activity wrapped as an ADK tool with `activity_tool`, so tool calls run as their own activities. |
43+
| [agent_patterns](./agent_patterns/README.md) | A coordinator `LlmAgent` with `sub_agents`, each a `TemporalModel` with a per-agent activity summary. |
44+
| [mcp](./mcp/README.md) | A local echo MCP toolset via `TemporalMcpToolSet` / `TemporalMcpToolSetProvider`, running MCP tools as activities. Self-contained, no Node required. |
45+
| [streaming](./streaming/README.md) | Token streaming via `TemporalModel(streaming_topic=...)` + `WorkflowStream`, consumed by a starter with `WorkflowStreamClient`. |
46+
47+
To run any scenario, start its worker in one terminal and its workflow starter
48+
in another:
49+
50+
```bash
51+
uv run python -m google_adk_agents.<scenario>.run_worker
52+
uv run python -m google_adk_agents.<scenario>.run_<name>_workflow
53+
```

‎google_adk_agents/__init__.py‎

Whitespace-only changes.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Agent Patterns — Multi-Agent Coordinator
2+
3+
A coordinator `LlmAgent` with `sub_agents=[researcher, writer]`. Each agent uses
4+
its own `TemporalModel` with an `ActivityConfig(summary=...)`, so the agents
5+
show up as named activities in workflow history. This demonstrates ADK's
6+
built-in `transfer_to_agent` handoff running durably, with per-agent activity
7+
summaries.
8+
9+
Before running, review the [prerequisites in the suite README](../README.md)
10+
(Temporal dev server, `uv sync --group google-adk`, and
11+
`export GOOGLE_API_KEY=...`).
12+
13+
## Running
14+
15+
Start the worker in one terminal:
16+
17+
```bash
18+
uv run python -m google_adk_agents.agent_patterns.run_worker
19+
```
20+
21+
Then start the workflow in another terminal:
22+
23+
```bash
24+
uv run python -m google_adk_agents.agent_patterns.run_multi_agent_workflow
25+
```
26+
27+
## What to expect
28+
29+
The starter asks for a haiku about the ocean. The coordinator delegates to the
30+
researcher and then the writer; the starter prints the final haiku.
31+
32+
## In the Temporal UI
33+
34+
Open the workflow `google-adk-agents-agent-patterns-workflow-id`. The
35+
`invoke_model` activities are labeled with their agent summaries —
36+
"Coordinator Agent", "Researcher Agent", "Writer Agent" — so you can follow the
37+
handoffs between agents directly in the history.

‎google_adk_agents/agent_patterns/__init__.py‎

Whitespace-only changes.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import asyncio
2+
3+
from temporalio.client import Client
4+
from temporalio.contrib.google_adk_agents import GoogleAdkPlugin
5+
6+
from google_adk_agents.agent_patterns.workflows.multi_agent_workflow import (
7+
MultiAgentWorkflow,
8+
)
9+
10+
11+
async def main():
12+
client = await Client.connect("localhost:7233", plugins=[GoogleAdkPlugin()])
13+
14+
result = await client.execute_workflow(
15+
MultiAgentWorkflow.run,
16+
"the ocean",
17+
id="google-adk-agents-agent-patterns-workflow-id",
18+
task_queue="google-adk-agents-agent-patterns",
19+
)
20+
print(f"Result: {result}")
21+
22+
23+
if __name__ == "__main__":
24+
asyncio.run(main())
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from __future__ import annotations
2+
3+
import asyncio
4+
5+
from temporalio.client import Client
6+
from temporalio.contrib.google_adk_agents import GoogleAdkPlugin
7+
from temporalio.worker import Worker
8+
9+
from google_adk_agents.agent_patterns.workflows.multi_agent_workflow import (
10+
MultiAgentWorkflow,
11+
)
12+
13+
14+
async def main():
15+
# Build the plugin once and give the same instance to the client and the
16+
# worker.
17+
plugin = GoogleAdkPlugin()
18+
19+
client = await Client.connect("localhost:7233", plugins=[plugin])
20+
21+
worker = Worker(
22+
client,
23+
task_queue="google-adk-agents-agent-patterns",
24+
workflows=[MultiAgentWorkflow],
25+
plugins=[plugin],
26+
)
27+
await worker.run()
28+
29+
30+
if __name__ == "__main__":
31+
asyncio.run(main())

‎google_adk_agents/agent_patterns/workflows/__init__.py‎

Whitespace-only changes.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
from datetime import timedelta
2+
3+
from google.adk.agents import LlmAgent
4+
from google.adk.runners import Runner
5+
from google.adk.sessions import InMemorySessionService
6+
from google.genai import types
7+
from temporalio import workflow
8+
from temporalio.contrib.google_adk_agents import TemporalModel
9+
from temporalio.workflow import ActivityConfig
10+
11+
12+
# @@@SNIPSTART google-adk-agents-agent-patterns-multi-agent-workflow
13+
@workflow.defn
14+
class MultiAgentWorkflow:
15+
@workflow.run
16+
async def run(self, topic: str) -> str:
17+
session_service = InMemorySessionService()
18+
session = await session_service.create_session(
19+
app_name="multi_agent_app", user_id="user"
20+
)
21+
22+
# Give each sub-agent its own TemporalModel with an ActivityConfig
23+
# summary, so its model turns show up as named activities in history.
24+
researcher = LlmAgent(
25+
name="researcher",
26+
model=TemporalModel(
27+
"gemini-2.5-flash",
28+
activity_config=ActivityConfig(summary="Researcher Agent"),
29+
),
30+
instruction="You are a researcher. Find information about the topic.",
31+
)
32+
33+
writer = LlmAgent(
34+
name="writer",
35+
model=TemporalModel(
36+
"gemini-2.5-flash",
37+
activity_config=ActivityConfig(summary="Writer Agent"),
38+
),
39+
instruction="You are a poet. Write a haiku based on the research.",
40+
)
41+
42+
# The coordinator hands off to the sub-agents using ADK's built-in
43+
# transfer_to_agent, which runs durably here.
44+
coordinator = LlmAgent(
45+
name="coordinator",
46+
model=TemporalModel(
47+
"gemini-2.5-flash",
48+
activity_config=ActivityConfig(
49+
start_to_close_timeout=timedelta(seconds=30),
50+
summary="Coordinator Agent",
51+
),
52+
),
53+
instruction="You are a coordinator. Delegate to researcher then writer.",
54+
sub_agents=[researcher, writer],
55+
)
56+
57+
runner = Runner(
58+
agent=coordinator,
59+
app_name="multi_agent_app",
60+
session_service=session_service,
61+
)
62+
63+
final_text = ""
64+
user_msg = types.Content(
65+
role="user",
66+
parts=[
67+
types.Part(
68+
text=f"Write a haiku about {topic}. First research it, then write it."
69+
)
70+
],
71+
)
72+
async for event in runner.run_async(
73+
user_id="user", session_id=session.id, new_message=user_msg
74+
):
75+
if event.content and event.content.parts and event.content.parts[0].text:
76+
final_text = event.content.parts[0].text
77+
78+
return final_text
79+
80+
81+
# @@@SNIPEND

0 commit comments

Comments
 (0)