Skip to content

Commit 1acf172

Browse files
bloveclaude
andauthored
feat(examples/chat): announce the research subagent's stream identity (#874)
Adopts threadplane-middleware 0.0.2's announce_subagent in the canonical demo's research @tool: the child's tools:<uuid> stream namespace is bound to the dispatching call_* id via one custom event, so @threadplane/langgraph (0.0.62) attributes the stream exactly instead of positionally. Sequenced deliberately after both packages published — this is the first consumer resolving announce_subagent from PyPI rather than from source. e2e 54/54 (research-subagent @drift spec included), uv.lock resolves threadplane-middleware 0.0.2 from the registry. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent 179c6a8 commit 1acf172

3 files changed

Lines changed: 18 additions & 7 deletions

File tree

examples/chat/python/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ dependencies = [
77
"langchain-openai>=0.3",
88
"langgraph-api>=0.8.7",
99
"python-dotenv>=1.0",
10-
"threadplane-middleware>=0.0.1",
10+
"threadplane-middleware>=0.0.2",
1111
]
1212

1313
[tool.uv]

examples/chat/python/src/graph.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,12 @@
3838
ToolMessage,
3939
)
4040
from langchain_core.runnables import RunnableConfig
41+
from langchain_core.tools import InjectedToolCallId
4142
from langchain_core.tools import tool
4243
from langgraph_sdk import get_client
4344
from langsmith import traceable
4445

45-
from threadplane.middleware.langgraph import bind_client_tools, client_tool_names
46+
from threadplane.middleware.langgraph import announce_subagent, bind_client_tools, client_tool_names
4647

4748
from src.streaming.a2ui_partial_handler import A2uiPartialHandler
4849
from src.streaming.envelope_tool import render_a2ui_surface
@@ -310,7 +311,12 @@ async def research_node(state: ResearchState) -> dict:
310311

311312

312313
@tool
313-
async def research(topic: str, subagent_type: str = "research") -> str:
314+
async def research(
315+
topic: str,
316+
subagent_type: str = "research",
317+
tool_call_id: Annotated[str, InjectedToolCallId] = None,
318+
config: RunnableConfig = None,
319+
) -> str:
314320
"""Dispatch a research subagent to gather facts on a focused topic.
315321
The subagent returns a concise summary; pass that summary back to
316322
the user, citing it with the inline citation syntax if appropriate.
@@ -324,6 +330,11 @@ async def research(topic: str, subagent_type: str = "research") -> str:
324330
# it travels in the tool call args so the SubagentTracker can
325331
# register the dispatch and surface a card while the child graph runs.
326332
del subagent_type
333+
# Bind the child's `tools:<uuid>` stream namespace to this tool-call id.
334+
# The namespace is a checkpoint id with no wire-level link to `call_*`;
335+
# announcing the pair lets the frontend attribute the stream exactly —
336+
# including under parallel fan-out, where guessing is unsound.
337+
announce_subagent(config, tool_call_id)
327338
result = await research_subgraph.ainvoke({"topic": topic, "messages": []})
328339
msgs = result.get("messages") if isinstance(result, dict) else None
329340
if not msgs:

examples/chat/python/uv.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)