Skip to content

Add dynamic node/task summaries to LangGraph plugin#1612

Open
DABH wants to merge 2 commits into
mainfrom
langgraph-dynamic-summaries
Open

Add dynamic node/task summaries to LangGraph plugin#1612
DABH wants to merge 2 commits into
mainfrom
langgraph-dynamic-summaries

Conversation

@DABH

@DABH DABH commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

What

Adds dynamic, runtime-computed summaries to the LangGraph plugin, matching the summary_fn capability the Google ADK integration already exposes.

  • New per-node / per-task summary_fn(args, kwargs) -> str | None, settable via Graph API node metadata or Functional activity_options[name], plus a plugin-wide default_summary_fn (overridable per node).
  • execute_in="activity" nodes: the result is set as the activity summary (carried in user_metadata on each ActivityTaskScheduledEvent, visible in UI/CLI/history).
  • execute_in="workflow" nodes: there is no activity, so the result updates the workflow's current details via workflow.set_current_details (last-writer-wins).
  • The static summary activity option already flowed through to execute_activity; this is now documented. Setting both a static summary and a summary_fn on the same node raises ValueError.
  • summary_fn runs in workflow context, so it must be deterministic and must not raise. It is replay-safe: summaries ride in user_metadata, which is not part of command/history matching.

Test plan

  • New tests/contrib/langgraph/test_summary_fn.py (11 tests): activity summary in history; dynamic/None/empty variants; default_summary_fn + per-node override; static-suppresses-default; static+summary_fn raises; summary_fn not leaked into node config["metadata"]; workflow-node current_details via __temporal_workflow_metadata; replay safety.
  • tests/contrib/langgraph suite: 44 passed. The lone failure, test_continue_as_new, is a pre-existing flake (an asyncio "in select" teardown timing issue that reproduces on main independently of this change).
  • ruff, pyright, basedpyright, mypy, pydocstyle clean on the touched files.
  • Ran the LangGraph samples in temporalio/samples-python (tests/langgraph_plugin/) against this branch vs. the released SDK — per-test outcomes identical (9 passed; the 2 human_in_the_loop failures occur in both runs solely from a missing ANTHROPIC_API_KEY).

Adds a per-node/per-task summary_fn(args, kwargs) -> str | None (and a
plugin-wide default_summary_fn) that computes a Temporal summary at
runtime from the node's input.

- execute_in="activity" nodes: the result sets the activity summary
  (user_metadata, shown on each scheduled-activity event).
- execute_in="workflow" nodes: the result updates the workflow's current
  details via workflow.set_current_details (last-writer-wins).

A static summary activity option already flowed through to
execute_activity; this is now documented. Setting both a static summary
and summary_fn on the same node raises ValueError. summary_fn runs in
workflow context (must be deterministic and must not raise) and is
replay-safe, since summaries ride in user_metadata.
@DABH DABH requested review from a team as code owners June 22, 2026 19:53
"""Prepare a node or task to execute as an activity or inline in the workflow."""
opts = kwargs or {}
execute_in = opts.pop("execute_in")
node_summary_fn = opts.pop("summary_fn", None)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why pop from the opts if we're already passing the opts? Should this be a get()?

@DABH DABH Jun 23, 2026

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.

So I believe it has to be pop, not get: a few lines down the leftover opts is splatted into wrap_execute_activity(..., **opts), which forwards them to workflow.execute_activity(...). summary_fn isn't a valid execute_activity kwarg, and we also pass it explicitly as summary_fn=summary_fn, so leaving it in opts would give some error about an unexpected/duplicate keyword. It's the same reason execute_in is popped on the line right above.

Pushed a comment to make that explicit in case that's helpful!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hmm, is summary a valid kwarg? I wonder if we could normalize summary and summary_fn into the relevant kwarg

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 runtime-computed node/task summaries to the temporalio.contrib.langgraph plugin, aligning it with existing dynamic-summary capabilities in other contrib integrations and improving observability in Temporal UI/CLI.

Changes:

  • Introduces per-node/per-task summary_fn(args, kwargs) -> str | None plus plugin-wide default_summary_fn, with validation against using static summary and summary_fn together.
  • Plumbs computed summaries into activity scheduling (execute_in="activity") and into workflow current details (execute_in="workflow").
  • Adds documentation and a dedicated test suite covering activity-history summaries, defaults/overrides, metadata stripping, workflow current details, and replay behavior.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/contrib/langgraph/test_summary_fn.py New tests validating summary_fn behavior for activity- and workflow-executed nodes, plus replay safety.
temporalio/contrib/langgraph/README.md Documents static summaries and new dynamic summary_fn/default_summary_fn behavior.
temporalio/contrib/langgraph/_workflow.py Adds workflow-side summary_fn support via workflow.set_current_details.
temporalio/contrib/langgraph/_plugin.py Adds default_summary_fn, plumbs per-node/task summary_fn, and strips it from node metadata.
temporalio/contrib/langgraph/_activity.py Computes dynamic activity summary on schedule path and passes it to workflow.execute_activity.

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

Comment on lines +33 to +42
Workflow-side nodes have no activity to carry a summary, so a
truthy ``summary_fn`` result updates the workflow's current details
via :func:`temporalio.workflow.set_current_details` (last-writer-wins).
"""

async def wrapper(*args: Any, **kwargs: Any) -> Any:
if summary_fn is not None:
summary = summary_fn(args, kwargs)
if summary:
workflow.set_current_details(summary)
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.

3 participants