Skip to content

Commit 3a817fc

Browse files
committed
Merge remote-tracking branch 'origin/main' into fix-mcp-dependency-declarations
# Conflicts: # pyproject.toml # uv.lock
2 parents d40d3be + a9cb676 commit 3a817fc

18 files changed

Lines changed: 413 additions & 146 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ Some examples require extra dependencies. See each sample's directory for specif
9393
This contains two samples, one sending messages to an existing workflow and a second that creates a workflow through Nexus
9494
and sends messages to it.
9595
* [nexus_multiple_args](nexus_multiple_args) - Map a Nexus operation to a handler workflow that takes multiple arguments.
96+
* [nexus_standalone_activity](nexus_standalone_activity) - Back a Nexus operation with a standalone Activity.
9697
* [nexus_standalone_operations](nexus_standalone_operations) - Execute Nexus operations directly from client code,
9798
without wrapping them in a workflow.
9899
* [open_telemetry](open_telemetry) - Trace workflows with OpenTelemetry.

google_adk_agents/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Each directory contains a complete example with its own README:
4343
| --- | --- |
4444
| [basic](./basic/README.md) | A single ADK agent with `TemporalModel` and one model call — no tools. The minimal end-to-end example. |
4545
| [chatbot](./chatbot/README.md) | A multi-turn conversation over one persisted ADK session, with each turn driven by a workflow Update handler that returns the assistant's reply. |
46-
| [tools](./tools/README.md) | A Temporal activity wrapped as an ADK tool with `activity_tool`, so tool calls run as their own activities. |
46+
| [tools](./tools/README.md) | A Temporal activity wrapped as an ADK tool with `activity_as_tool`, so tool calls run as their own activities. |
4747
| [agent_patterns](./agent_patterns/README.md) | A coordinator `LlmAgent` with `sub_agents`, each a `TemporalModel` with a per-agent activity summary. |
4848
| [mcp](./mcp/README.md) | A local echo MCP toolset via `TemporalMcpToolSet` / `TemporalMcpToolSetProvider`, running MCP tools as activities. Self-contained, no Node required. |
4949
| [streaming](./streaming/README.md) | Token streaming via `TemporalModel(streaming_topic=...)` + `WorkflowStream`, consumed by a starter with `WorkflowStreamClient`. |

google_adk_agents/tools/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Tools — Temporal Activities as ADK Tools
22

33
A weather agent whose `get_weather` Temporal activity is wrapped as an ADK tool
4-
with `activity_tool(...)`. The model decides to call the tool; the tool runs as
4+
with `activity_as_tool(...)`. The model decides to call the tool; the tool runs as
55
its own Temporal activity — retryable and observable — rather than inline in the
66
workflow. This demonstrates the activity boundary for tool calls.
77

google_adk_agents/tools/workflows/weather_workflow.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
class WeatherAgentWorkflow:
1717
@workflow.run
1818
async def run(self, prompt: str) -> str:
19-
# activity_tool runs the tool call as a real Temporal activity, so it's
19+
# activity_as_tool runs the tool call as a real Temporal activity, so it's
2020
# retryable and shows up in history.
21-
weather_tool = temporalio.contrib.google_adk_agents.workflow.activity_tool(
21+
weather_tool = temporalio.contrib.google_adk_agents.workflow.activity_as_tool(
2222
get_weather, start_to_close_timeout=timedelta(seconds=60)
2323
)
2424

lambda_worker/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ authors = [{ name = "Temporal Technologies Inc", email = "sdk@temporal.io" }]
66
requires-python = ">=3.10"
77
readme = "README.md"
88
license = "MIT"
9-
dependencies = ["temporalio[lambda-worker-otel]>=1.26.0,<2"]
9+
dependencies = ["temporalio[lambda-worker-otel]>=1.32.0,<2"]
1010

1111
[dependency-groups]
1212
dev = [

lambda_worker/uv.lock

Lines changed: 11 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Nexus operation backed by a standalone Activity
2+
3+
This sample shows how to implement a `TemporalOperationHandler` that starts a
4+
standalone Activity as the backing execution for a Nexus operation. When the Activity
5+
finishes, Temporal delivers its result to the Nexus caller. The default handler
6+
cancellation implementation also forwards Nexus cancellation to the Activity.
7+
8+
The APIs used by this sample are experimental and may change incompatibly.
9+
10+
### Sample structure
11+
12+
- [service.py](./service.py) defines the Nexus service shared by caller and handler.
13+
- [activity.py](./activity.py) defines the standalone Activity.
14+
- [handler.py](./handler.py) implements `TemporalOperationHandler.start_operation`.
15+
- [worker.py](./worker.py) hosts the Nexus handler and Activity.
16+
- [starter.py](./starter.py) executes the Nexus operation from client code.
17+
18+
## Run locally
19+
20+
This sample requires the [Temporal dev server build that supports standalone Nexus operations](https://docs.temporal.io/standalone-nexus-operation#temporal-cli-support) and Activity
21+
callbacks enabled.
22+
23+
1. Start the server with caller and handler namespaces:
24+
25+
```bash
26+
./temporal server start-dev \
27+
--dynamic-config-value activity.enableCallbacks=true \
28+
--namespace nexus-standalone-activity-caller \
29+
--namespace nexus-standalone-activity-handler
30+
```
31+
32+
2. Create an endpoint targeting the handler namespace and task queue:
33+
34+
```bash
35+
./temporal operator nexus endpoint create \
36+
--name nexus-standalone-activity-endpoint \
37+
--target-namespace nexus-standalone-activity-handler \
38+
--target-task-queue nexus-standalone-activity-handler
39+
```
40+
41+
3. Start the handler Worker:
42+
43+
```bash
44+
TEMPORAL_NAMESPACE=nexus-standalone-activity-handler \
45+
uv run nexus_standalone_activity/worker.py
46+
```
47+
48+
4. Execute the operation from the caller namespace:
49+
50+
```bash
51+
TEMPORAL_NAMESPACE=nexus-standalone-activity-caller \
52+
uv run nexus_standalone_activity/starter.py
53+
```
54+
55+
Expected output:
56+
57+
```text
58+
Hello, World!
59+
```
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Nexus operation backed by a standalone Activity sample."""
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
"""Activity used as the backing execution for the Nexus operation."""
2+
3+
from temporalio import activity
4+
5+
from nexus_standalone_activity.service import GreetingInput, GreetingOutput
6+
7+
8+
@activity.defn
9+
async def create_greeting(input: GreetingInput) -> GreetingOutput:
10+
return GreetingOutput(message=f"Hello, {input.name}!")
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""Temporal operation handler that starts a standalone Activity."""
2+
3+
from datetime import timedelta
4+
5+
import nexusrpc.handler
6+
from temporalio import nexus
7+
8+
from nexus_standalone_activity.activity import create_greeting
9+
from nexus_standalone_activity.service import (
10+
GreetingInput,
11+
GreetingOutput,
12+
GreetingService,
13+
)
14+
15+
16+
def get_activity_id(input: GreetingInput) -> str:
17+
return f"greeting-{input.name}"
18+
19+
20+
@nexusrpc.handler.service_handler(service=GreetingService)
21+
class GreetingServiceHandler:
22+
@nexus.temporal_operation
23+
async def greet(
24+
self,
25+
_ctx: nexus.TemporalStartOperationContext,
26+
client: nexus.TemporalNexusClient,
27+
input: GreetingInput,
28+
) -> nexus.TemporalOperationResult[GreetingOutput]:
29+
# The standalone Activity becomes the asynchronous backing execution for
30+
# this Nexus operation. Omitting task_queue uses the Nexus Worker's queue.
31+
return await client.start_activity(
32+
create_greeting,
33+
input,
34+
id=get_activity_id(input),
35+
start_to_close_timeout=timedelta(seconds=10),
36+
)

0 commit comments

Comments
 (0)