Skip to content

Add Deep Agents plugin samples#328

Draft
DABH wants to merge 2 commits into
mainfrom
deepagents-plugin-samples
Draft

Add Deep Agents plugin samples#328
DABH wants to merge 2 commits into
mainfrom
deepagents-plugin-samples

Conversation

@DABH

@DABH DABH commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

What

Samples for the upcoming Temporal ↔ LangChain Deep Agents integration (temporalio.contrib.deepagents): eight scenarios under deepagents_plugin/, each a runnable worker/starter pair with its own README, plus offline tests (fake model provider, no API keys) under tests/deepagents_plugin/.

  • hello_world — vanilla create_deep_agent(...).ainvoke(...) inside a workflow; the only change is plugins=[DeepAgentsPlugin()], and every model call becomes a Temporal Activity.
  • react_agent — the explicit per-tool Workflow-vs-Activity choice: activity_as_tool (surface an existing @activity.defn) and tool_as_activity (route an I/O LangChain tool through an activity), with an explicit TemporalModel.
  • human_in_the_loop — the native LangGraph interrupt/resume protocol mapped to a Temporal Query + Update; no custom shim.
  • continue_as_newrun_deep_agent(..., continue_as_new_after=N) carrying the conversation and the model/tool result cache across continue-as-new.
  • filesystem_backendTemporalBackend(FilesystemBackend(...)): the agent's built-in file tools execute as durable deepagents.backend_op activities instead of doing I/O in workflow code.
  • subagents — sub-agents inherit the durable model automatically; delegation needs no per-sub-agent wiring.
  • streamingstreaming_topic=... publishes chunk batches to a workflow-streams topic for live subscribers while the durable result stays identical to the non-streaming path.
  • langsmith_tracing — composing DeepAgentsPlugin with LangSmithPlugin for tracing (no test; needs real API keys).

Repo registration: a deepagents dependency group (gated on Python ≥ 3.11), the wheel packages entry, and the root README row.

Status

Draft until the plugin lands in sdk-python. The plugin distribution is experimental and not yet published, so the dependency group deliberately does not include it (an unresolvable dep would break uv lock/uv sync for everyone), and tests/deepagents_plugin/ skips collection when the plugin isn't importable — CI stays green with no plugin present. pyproject.toml carries an inline note with the one-line group change to make when the plugin publishes.

How to run / test evidence

Interim install (documented in deepagents_plugin/README.md): install the plugin into the repo venv, then

uv run --no-sync pytest tests/deepagents_plugin

Current result against the in-development plugin: 8 passed, with ruff check / ruff format --check / mypy clean on the new directories.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new deepagents_plugin/ sample suite demonstrating how the upcoming temporalio.contrib.deepagents integration makes LangChain Deep Agents durable (model/tool/backend calls executed as Temporal Activities while the agent loop replays in Workflow code). It also introduces an optional deepagents dependency group (Python ≥ 3.11) and a guarded offline test suite under tests/deepagents_plugin/.

Changes:

  • Add eight runnable Deep Agents plugin sample scenarios under deepagents_plugin/ (worker/starter pairs + per-scenario READMEs).
  • Add offline pytest coverage for the scenarios (skipped at collection time when the plugin isn’t importable / Python < 3.11).
  • Register the sample package in pyproject.toml, update root README, and update uv.lock for the new dependency group resolution.

Reviewed changes

Copilot reviewed 43 out of 53 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
uv.lock Locks new deepagents group deps and associated transitive packages.
pyproject.toml Adds deepagents dependency group (Python ≥ 3.11) and registers deepagents_plugin as a package.
README.md Adds deepagents_plugin to the root sample list.
deepagents_plugin/init.py Package marker for the Deep Agents samples.
deepagents_plugin/README.md Suite-level documentation, prerequisites, and run instructions.
deepagents_plugin/hello_world/workflow.py Minimal durable agent workflow example.
deepagents_plugin/hello_world/run_worker.py Worker wiring with DeepAgentsPlugin.
deepagents_plugin/hello_world/run_workflow.py Starter for hello world scenario.
deepagents_plugin/hello_world/README.md Scenario documentation.
deepagents_plugin/hello_world/init.py Package marker.
deepagents_plugin/react_agent/workflow.py Tool loop example showing activity_as_tool vs tool_as_activity + explicit TemporalModel.
deepagents_plugin/react_agent/run_worker.py Worker wiring + registers the user activity tool.
deepagents_plugin/react_agent/run_workflow.py Starter for react-agent scenario.
deepagents_plugin/react_agent/README.md Scenario documentation.
deepagents_plugin/react_agent/init.py Package marker.
deepagents_plugin/human_in_the_loop/workflow.py Interrupt/resume mapping to Temporal Query + Update.
deepagents_plugin/human_in_the_loop/run_worker.py Worker wiring with plugin.
deepagents_plugin/human_in_the_loop/run_workflow.py Starter demonstrating query polling + update resume.
deepagents_plugin/human_in_the_loop/README.md Scenario documentation.
deepagents_plugin/human_in_the_loop/init.py Package marker.
deepagents_plugin/continue_as_new/workflow.py run_deep_agent continue-as-new contract example.
deepagents_plugin/continue_as_new/run_worker.py Worker wiring with plugin.
deepagents_plugin/continue_as_new/run_workflow.py Starter for continue-as-new scenario.
deepagents_plugin/continue_as_new/README.md Scenario documentation.
deepagents_plugin/continue_as_new/init.py Package marker.
deepagents_plugin/filesystem_backend/workflow.py TemporalBackend(FilesystemBackend(...)) durable file I/O example.
deepagents_plugin/filesystem_backend/run_worker.py Worker wiring with plugin.
deepagents_plugin/filesystem_backend/run_workflow.py Starter for filesystem-backend scenario.
deepagents_plugin/filesystem_backend/README.md Scenario documentation.
deepagents_plugin/filesystem_backend/init.py Package marker.
deepagents_plugin/subagents/workflow.py Sub-agent durability inheritance example.
deepagents_plugin/subagents/run_worker.py Worker wiring with plugin.
deepagents_plugin/subagents/run_workflow.py Starter for subagents scenario.
deepagents_plugin/subagents/README.md Scenario documentation.
deepagents_plugin/subagents/init.py Package marker.
deepagents_plugin/streaming/workflow.py Streaming model chunks to workflow-streams topic example.
deepagents_plugin/streaming/run_worker.py Worker wiring enabling streaming dispatch.
deepagents_plugin/streaming/run_workflow.py Starter subscribing to streamed chunks and printing live output.
deepagents_plugin/streaming/README.md Scenario documentation.
deepagents_plugin/streaming/init.py Package marker.
deepagents_plugin/langsmith_tracing/workflow.py Durable agent workflow used for tracing scenario.
deepagents_plugin/langsmith_tracing/main.py Single-process driver composing LangSmithPlugin + DeepAgentsPlugin.
deepagents_plugin/langsmith_tracing/README.md Scenario documentation (no offline test).
deepagents_plugin/langsmith_tracing/init.py Package marker.
tests/deepagents_plugin/conftest.py Collection-time guard to skip tests when plugin isn’t available / Python < 3.11.
tests/deepagents_plugin/hello_world_test.py Offline test for hello world scenario using mock_model_provider.
tests/deepagents_plugin/react_agent_test.py Offline test for tool loop scenario.
tests/deepagents_plugin/human_in_the_loop_test.py Offline test for interrupt/resume scenario.
tests/deepagents_plugin/continue_as_new_test.py Offline tests for run_deep_agent contract + continue-as-new carry behavior.
tests/deepagents_plugin/filesystem_backend_test.py Offline test for durable filesystem backend ops.
tests/deepagents_plugin/subagents_test.py Offline test for sub-agent durability inheritance.
tests/deepagents_plugin/streaming_test.py Offline test for streaming topic publishing.
tests/deepagents_plugin/init.py Test package marker.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +78 to +82
@workflow.update
async def resume(self, decision: str) -> None:
"""Resume the paused agent with ``"approve"`` or ``"reject"``."""
self._decision = decision
self._resumed = True

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch on both points — fixed in 2e22662. _pending is now cleared as soon as the resume update unblocks the workflow, so the query honors its documented contract of returning None when not paused. resume now has an update validator rejecting anything other than "approve"/"reject": with interrupt_on={"book_trip": True} the middleware raises on unexpected decision types inside the workflow, which would fail the workflow task, so rejecting invalid input before it is accepted into history is the right mapping — and mirrors the validator pattern in message_passing/introduction.

Comment thread tests/deepagents_plugin/conftest.py Outdated
Comment on lines +19 to +27
import importlib.util
import sys

collect_ignore_glob: list[str] = []

if sys.version_info < (3, 11) or (
importlib.util.find_spec("temporalio.contrib.deepagents") is None
):
collect_ignore_glob = ["*_test.py"]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — a guarded import is the stronger check here; fixed in 2e22662. In this setup the failure mode is real: the plugin subpackage ships inside an out-of-band temporalio build while its runtime deps (deepagents/langchain) come from the separate deepagents dependency group, so the module can exist on disk while its imports would fail. The conftest now attempts the actual import (after the Python 3.11 gate, so it is never tried on unsupported interpreters) and skips collection on ImportError. We kept the except clause narrow so a genuinely broken install still fails loudly rather than silently skipping the suite.

Comment on lines +45 to +65
async def consume() -> None:
stream = WorkflowStreamClient.create(client, workflow_id)
async for item in stream.subscribe(
[STREAMING_TOPIC],
from_offset=0,
result_type=dict,
poll_cooldown=timedelta(milliseconds=10),
):
text = getattr(load(item.data), "content", "")
if text:
chunks.append(text)

consume_task = asyncio.create_task(consume())
result = await handle.result()
# The workflow has completed; give the subscriber a beat to drain, then stop.
await asyncio.sleep(0.5)
consume_task.cancel()
try:
await consume_task
except asyncio.CancelledError:
pass

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, the fixed sleep was timing-dependent — fixed in 2e22662. The subscriber now returns as soon as it has observed the full streamed answer, and the test awaits it with asyncio.wait_for(..., timeout=10.0) — the same pattern the strands and google_adk streaming tests use. No fixed sleeps remain; the timeout only bounds a regression instead of gating the happy path. We also hoisted the expected string into a local so the mock model, the drain condition, and the assertions share one definition.

…test

- human_in_the_loop: clear the pending-approval prompt on resume so the
  query honors its documented contract, and add an update validator that
  rejects decisions other than approve/reject before they enter history
- tests conftest: replace find_spec with a guarded import so collection
  is skipped when the plugin package exists but its runtime deps do not
- streaming test: replace the fixed sleep-then-cancel drain with a
  condition-based subscriber awaited via wait_for, matching the other
  streaming tests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants