LangGraph plugin samples#289
Conversation
Seven samples demonstrating the Temporal LangGraph plugin across both the Graph API and Functional API: - Human-in-the-loop: interrupt() + Temporal signals for chatbot approval - Continue-as-new: task result caching across workflow boundaries - ReAct agent: tool-calling loop with conditional edges / while loop - Control flow (Functional API only): parallel, for-loop, if/else Related SDK PR: temporalio/sdk-python#1448
|
|
… merge) so that CI otherwise passes
There was a problem hiding this comment.
Pull request overview
Adds a new langgraph_plugin/ sample suite to demonstrate running LangGraph workflows as durable Temporal workflows via the Temporal LangGraph plugin, covering both LangGraph Graph API and Functional API patterns.
Changes:
- Introduces 7 new LangGraph+Temporal sample workflows (human-in-the-loop, continue-as-new w/ caching, ReAct agent, control-flow).
- Adds a new
langgraphdependency group and includeslanggraph_pluginin the build packages list. - Updates repository documentation to link to the new sample suite and provides a dedicated README for running the samples.
Reviewed changes
Copilot reviewed 24 out of 34 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| pyproject.toml | Adds langgraph dependency group, includes langgraph_plugin in wheel packages, adjusts lint/test tasks, and adds mypy override. |
| README.md | Links the new langgraph_plugin samples from the repo root README. |
| langgraph_plugin/README.md | Adds usage documentation and a sample matrix for the new suite. |
| langgraph_plugin/init.py | Declares the new top-level sample package. |
| langgraph_plugin/graph_api/init.py | Declares Graph API samples namespace. |
| langgraph_plugin/graph_api/react_agent/init.py | Declares Graph API ReAct agent package. |
| langgraph_plugin/graph_api/react_agent/workflow.py | Implements Graph API ReAct agent workflow and graph definition. |
| langgraph_plugin/graph_api/react_agent/run_worker.py | Worker wiring for Graph API ReAct agent sample. |
| langgraph_plugin/graph_api/react_agent/run_workflow.py | Starter script for Graph API ReAct agent sample. |
| langgraph_plugin/graph_api/human_in_the_loop/init.py | Declares Graph API human-in-the-loop package. |
| langgraph_plugin/graph_api/human_in_the_loop/workflow.py | Implements Graph API interrupt + Temporal signal/query human approval flow. |
| langgraph_plugin/graph_api/human_in_the_loop/run_worker.py | Worker wiring for Graph API human-in-the-loop sample. |
| langgraph_plugin/graph_api/human_in_the_loop/run_workflow.py | Starter script for Graph API human-in-the-loop sample. |
| langgraph_plugin/graph_api/continue_as_new/init.py | Declares Graph API continue-as-new package. |
| langgraph_plugin/graph_api/continue_as_new/workflow.py | Implements Graph API pipeline w/ continue-as-new + cache handoff. |
| langgraph_plugin/graph_api/continue_as_new/run_worker.py | Worker wiring for Graph API continue-as-new sample. |
| langgraph_plugin/graph_api/continue_as_new/run_workflow.py | Starter script for Graph API continue-as-new sample. |
| langgraph_plugin/functional_api/init.py | Declares Functional API samples namespace. |
| langgraph_plugin/functional_api/react_agent/init.py | Declares Functional API ReAct agent package. |
| langgraph_plugin/functional_api/react_agent/workflow.py | Implements Functional API ReAct agent via @task + @entrypoint. |
| langgraph_plugin/functional_api/react_agent/run_worker.py | Worker wiring for Functional API ReAct agent sample. |
| langgraph_plugin/functional_api/react_agent/run_workflow.py | Starter script for Functional API ReAct agent sample. |
| langgraph_plugin/functional_api/human_in_the_loop/init.py | Declares Functional API human-in-the-loop package. |
| langgraph_plugin/functional_api/human_in_the_loop/workflow.py | Implements Functional API interrupt + signal/query human approval flow. |
| langgraph_plugin/functional_api/human_in_the_loop/run_worker.py | Worker wiring for Functional API human-in-the-loop sample. |
| langgraph_plugin/functional_api/human_in_the_loop/run_workflow.py | Starter script for Functional API human-in-the-loop sample. |
| langgraph_plugin/functional_api/control_flow/init.py | Declares Functional API control-flow package. |
| langgraph_plugin/functional_api/control_flow/workflow.py | Implements Functional API sample demonstrating parallelism + branching. |
| langgraph_plugin/functional_api/control_flow/run_worker.py | Worker wiring for Functional API control-flow sample. |
| langgraph_plugin/functional_api/control_flow/run_workflow.py | Starter script for Functional API control-flow sample. |
| langgraph_plugin/functional_api/continue_as_new/init.py | Declares Functional API continue-as-new package. |
| langgraph_plugin/functional_api/continue_as_new/workflow.py | Implements Functional API pipeline w/ continue-as-new + cache handoff. |
| langgraph_plugin/functional_api/continue_as_new/run_worker.py | Worker wiring for Functional API continue-as-new sample. |
| langgraph_plugin/functional_api/continue_as_new/run_workflow.py | Starter script for Functional API continue-as-new sample. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Combines LangGraphPlugin (durable execution) with LangSmithPlugin (observability) for full tracing of LLM calls through Temporal workflows.
- graphs parameter is now a list, not a dict - No graph()/entrypoint() lookup functions — reference objects directly - No entrypoints parameter on LangGraphPlugin - Use set_cache()/get_cache() for continue-as-new caching - Update tests and READMEs accordingly
# Conflicts: # pyproject.toml # uv.lock
The plugin API has evolved past PR #1448: - LangGraphPlugin(graphs=...) now takes dict[str, StateGraph], not list - Same for entrypoints (dict[str, Pregel]) - Each node/task must declare execute_in: "activity" | "workflow" in metadata - Workflows look up registered graphs/entrypoints via graph(name) / entrypoint(name) instead of importing them directly - get_cache/set_cache replaced by cache() and graph(name, cache=...) / entrypoint(name, cache=...) Other changes: - State schemas converted from primitives (str, int) to TypedDict, since langgraph's StateT is bound to TypedDict / dataclass / pydantic models - Module-level graph instances replaced with make_<name>_graph() factories because LangGraphPlugin mutates node metadata in-place at construction time, which prevented reuse across tests - langgraph dep group now pulls langchain, langchain-anthropic, and temporalio[langgraph,langsmith] so the langsmith_tracing samples and human_in_the_loop's RunnableConfig usage actually resolve
These features rely on contextvars propagation through asyncio.create_task(), which is only available in Python 3.11+. On 3.10 they hang rather than fail cleanly, so CI sat for an hour before today's fix landed. Mirrors the pytestmark skipif used in sdk-python's own contrib/langgraph tests.
- Drop cross-references between Functional API and Graph API READMEs so each stands alone (review comment 1) - Combine run_worker.py + run_workflow.py into a single main.py for the langsmith_tracing samples to reduce setup steps (review comment 2) - Fix workflow -> Workflow casing (review comment 3) - Alias temporalio's entrypoint and graph helpers as temporal_entrypoint / temporal_graph at import sites to avoid the langgraph.func.entrypoint collision (review comment 4) - Demonstrate @Traceable in three places in both langsmith_tracing samples: on the Activity/task itself, on a helper called from the Activity, and on a helper called from the Workflow (review comment 5) - Drop unnecessary single-task list comprehension for activity_options in hello_world and langsmith_tracing functional samples (review comment 6) - Spell out 'temporal server start-dev' in every sample README's prerequisites (review comment 7) - Fix stale README references to get_cache() now that the API is cache() (review comment 8) - Rename arbitrarily-named ETL pipeline functions in continue_as_new to honest math names (double / add_50 / triple) per review comment 9
| # (see [tool.uv.sources] in pyproject.toml). Drop this step once the SDK | ||
| # ships a release that includes the LangGraph plugin. |
There was a problem hiding this comment.
Can we just leave this step? I assume we'll need to constantly add and remove it as we write new plugins + samples.
| @@ -0,0 +1,36 @@ | |||
| # Continue-as-New with Caching (Functional API) | |||
|
|
|||
| Demonstrates combining Temporal's continue-as-new with LangGraph's task result caching to avoid re-executing completed `@task` functions across workflow boundaries. | |||
There was a problem hiding this comment.
It's actually OUR task result caching, right?
| activity_options = { | ||
| t.func.__name__: { | ||
| "execute_in": "activity", | ||
| "start_to_close_timeout": timedelta(seconds=30), | ||
| } | ||
| for t in all_tasks | ||
| } |
There was a problem hiding this comment.
It feels like we're hacking around our own limitations here. Let's just be explicit everywhere or make this more user friendly.
There was a problem hiding this comment.
(My vote is to be more explicit, that was the point of making this change anyways)
| @@ -0,0 +1,37 @@ | |||
| # Human-in-the-Loop Chatbot (Functional API) | |||
|
|
|||
| Demonstrates using LangGraph's `interrupt()` to pause an entrypoint for human review, combined with Temporal signals and queries for asynchronous feedback, using the imperative `@task`/`@entrypoint` style. | |||
There was a problem hiding this comment.
Signals and Queries are important to mention here, but the real star of the show is wait_condition which can wait indefinitely
| activity_options = { | ||
| t.func.__name__: { | ||
| "execute_in": "activity", | ||
| "start_to_close_timeout": timedelta(seconds=30), | ||
| } | ||
| for t in all_tasks | ||
| } |
| @@ -0,0 +1,37 @@ | |||
| # LangSmith Tracing (Functional API) | |||
|
|
|||
| Demonstrates combining `LangGraphPlugin` (durable task execution) with Temporal's `LangSmithPlugin` (observability) for full tracing of LLM calls through Temporal workflows, using LangGraph's `@task` and `@entrypoint` decorators. | |||
There was a problem hiding this comment.
Redundant, tracing = observability
| Demonstrates combining `LangGraphPlugin` (durable task execution) with Temporal's `LangSmithPlugin` (observability) for full tracing of LLM calls through Temporal workflows, using LangGraph's `@task` and `@entrypoint` decorators. | |
| Demonstrates combining `LangGraphPlugin` (durable task execution) with Temporal's `LangSmithPlugin` for full tracing of LLM calls through Temporal workflows, using LangGraph's `@task` and `@entrypoint` decorators. |
Summary
Add samples demonstrating the Temporal LangGraph plugin, which runs LangGraph workflows as durable Temporal workflows. Each graph node or
@taskexecutes as a Temporal activity with automatic retries, timeouts, and crash recovery.Samples are provided for both the Graph API (declarative
StateGraphwith nodes and edges) and the Functional API (imperative@task/@entrypointdecorators).interrupt()to pause for human approval, Temporal signals to receive feedback, and queries to expose the pending draft.continue-as-newwith task result caching so previously-completed stages are not re-executed.add_conditional_edges; Functional API uses awhileloop.forloops, andif/elsebranching — patterns that are natural in the Functional API.LangGraphPluginwith Temporal'sLangSmithPluginfor durable execution + full LLM observability. Requires API keys.Key Features Demonstrated
@taskruns as a Temporal activity with configurable timeouts and retry policies.interrupt()pauses the graph; Temporal signals deliver human input; queries expose pending state to UIs.get_cache()captures completed task results; passing the cache to the next execution avoids re-running them.add_conditional_edgesand Functional API's nativeif/else/whilefor agent loops.LangSmithPluginon the client +LangGraphPluginon the worker for full trace propagation through Temporal workflows.Related PRs
Test plan
uv run langgraph_plugin/<api>/<sample>/run_worker.py+run_workflow.pyANTHROPIC_API_KEYandLANGCHAIN_API_KEY