[PR #1903] [MERGED] feat(sdk): add multi-subagent tracking to useStream #1830

Closed
opened 2026-02-15 20:16:58 -05:00 by yindo · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/langchain-ai/langgraphjs/pull/1903
Author: @christian-bromann
Created: 1/15/2026
Status: Merged
Merged: 2/5/2026
Merged by: @christian-bromann

Base: mainHead: cb/subagent-streaming


📝 Commits (10+)

  • e80f8b2 feat(sdk): add multi-subagent tracking to useStream
  • a7620f1 Update changeset for langchain SDK versioning
  • 643f11b fix types after dep update
  • f6d02f8 fix(sdk): persist subagent state after stream completion and page refresh
  • 7ec09d6 align UseStream and SubAgentStream
  • 8c5d6f7 add example with subagents using tool calls
  • 9d65f87 seperate in use case streams
  • 84d542a format
  • 6b0e6d3 use fixtures for lg
  • 1f466e6 more type fixes

📊 Changes

46 files changed (+7083 additions, -2309 deletions)

View changed files

.changeset/tall-parrots-agree.md (+5 -0)
📝 examples/ui-react-transport/src/server.mts (+8 -17)
📝 examples/ui-react/langgraph.json (+2 -1)
📝 examples/ui-react/package.json (+1 -0)
📝 examples/ui-react/src/components/Layout.tsx (+2 -1)
📝 examples/ui-react/src/examples/custom-streaming/agent.ts (+23 -20)
examples/ui-react/src/examples/deepagent-tools/agent.ts (+350 -0)
examples/ui-react/src/examples/deepagent-tools/components/SubagentPipeline.tsx (+100 -0)
examples/ui-react/src/examples/deepagent-tools/components/SubagentStreamCard.tsx (+291 -0)
examples/ui-react/src/examples/deepagent-tools/components/SubagentToolCallCard.tsx (+326 -0)
examples/ui-react/src/examples/deepagent-tools/index.tsx (+238 -0)
examples/ui-react/src/examples/deepagent-tools/types.ts (+47 -0)
examples/ui-react/src/examples/deepagent/agent.ts (+384 -0)
examples/ui-react/src/examples/deepagent/components/SubagentCard.tsx (+255 -0)
examples/ui-react/src/examples/deepagent/components/SubagentPipeline.tsx (+80 -0)
examples/ui-react/src/examples/deepagent/index.tsx (+282 -0)
examples/ui-react/src/examples/multi-step-graph/agent.ts (+0 -265)
examples/ui-react/src/examples/multi-step-graph/index.tsx (+0 -384)
📝 examples/ui-react/src/examples/parallel-research/agent.ts (+17 -14)
📝 internal/build/package.json (+11 -11)

...and 26 more files

📄 Description

This PR adds comprehensive streaming enhancements to the useStream hook, enabling developers to:

  • Track node executions in any graph
  • Monitor tool calls with typed arguments in agents
  • Display multiple concurrent subagent executions in deep agent scenarios

Stream Type Hierarchy

The PR introduces a layered type system that automatically resolves based on your agent/graph type:

UseStreamOptions / BaseStream              ← Original useStream (unchanged)
    │
    ├── UseAgentStreamOptions / UseAgentStream
    │       └── Extends UseGraphStream
    │       Input:  (no new options)
    │       Output: + toolCalls, getToolCalls
    │
    └── UseDeepAgentStreamOptions / UseDeepAgentStream
            └── Extends UseAgentStream
            Input:  + subagentToolNames, filterSubagentMessages
            Output: + subagents, activeSubagents, getSubagent, getSubagentsByType

Usage automatically resolves to the correct interface:

useStream<typeof graph>()        // → BaseStream
useStream<typeof agent>()        // → UseAgentStream (with toolCalls)
useStream<typeof deepAgent>()    // → UseDeepAgentStream (with subagents)

Tool Call Tracking (for createAgent)

Track tool calls with full type inference from your agent's tools:

const stream = useStream<typeof agent>({
  assistantId: "my-agent",
  apiUrl: "http://localhost:2024",
});

// All tool calls paired with results
stream.toolCalls.forEach(({ call, result }) => {
  if (call.name === "search") {
    // call.args is typed as { query: string }
    console.log("Searched for:", call.args.query);
  }
});

// Get tool calls for a specific AI message
stream.getToolCalls(aiMessage)

Subagent Streaming (for createDeepAgent)

Track multiple concurrent subagent executions:

const stream = useStream<typeof agent>({
  assistantId: "vacation-planner",
  apiUrl: "http://localhost:2024",
  subagentToolNames: ["task"],       // Tool names that spawn subagents
  filterSubagentMessages: true,       // Keep main messages clean
});

// All subagent executions (Map<string, SubagentStream>)
stream.subagents

// Currently running subagents
stream.activeSubagents

// Get specific subagent by tool call ID
stream.getSubagent("call_abc123")

// Get all subagents of a specific type (with typed state)
stream.getSubagentsByType("weather-scout")

SubagentStream interface:

interface SubagentStream<StateType, ToolCall> {
  id: string;                    // Tool call ID
  toolCall: {
    id: string;
    name: string;
    args: {
      description?: string;
      subagent_type?: string;
    };
  };
  status: "pending" | "running" | "complete" | "error";
  result: string | null;         // Final result when complete
  error: string | null;          // Error message if failed
  namespace: string[];           // LangGraph namespace path
  messages: Message<ToolCall>[]; // Subagent's message history
  values: StateType;             // Subagent's state (typed per-subagent)
  parentId: string | null;       // For nested subagents
  depth: number;                 // Nesting level (0 = direct child)
  startedAt: Date | null;
  completedAt: Date | null;
}

filterSubagentMessages option:

When enabled, subagent messages are filtered from the main stream.messages array and only accessible via SubagentStream.messages. This allows clean separation of main agent and subagent streams in the UI.

Usage Example

function DeepAgentUI() {
  const stream = useStream<typeof agent>({
    assistantId: "vacation-planner",
    apiUrl: "http://localhost:2024",
    filterSubagentMessages: true,
  });

  return (
    <div>
      {/* Main agent messages */}
      <MessageList messages={stream.messages} />
      
      {/* Subagent cards */}
      <div className="grid grid-cols-3 gap-4">
        {[...stream.subagents.values()].map((subagent) => (
          <SubagentCard 
            key={subagent.id}
            type={subagent.toolCall.args.subagent_type}
            status={subagent.status}
            content={subagent.messages
              .filter(m => m.type === "ai")
              .map(m => m.content)
              .join("")}
          />
        ))}
      </div>
    </div>
  );
}

Implementation Details

New Managers

  • SubagentManager class handles all subagent state tracking

Key Features

  • Namespace mapping: Connects LangGraph's internal pregel task IDs to the original tool call IDs via description matching
  • Per-node/subagent MessageTupleManager: Properly concatenates streaming message chunks
  • Status-based filtering: Only shows subagents once they're actually running (filters out partial streaming artifacts)
  • Type inference: Full TypeScript inference for node return types, tool call args, and subagent states

Added Examples

Added deepagent examples displaying individual subagent streams:

Screenshot 2026-01-15 at 5 00 13 PM

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/langchain-ai/langgraphjs/pull/1903 **Author:** [@christian-bromann](https://github.com/christian-bromann) **Created:** 1/15/2026 **Status:** ✅ Merged **Merged:** 2/5/2026 **Merged by:** [@christian-bromann](https://github.com/christian-bromann) **Base:** `main` ← **Head:** `cb/subagent-streaming` --- ### 📝 Commits (10+) - [`e80f8b2`](https://github.com/langchain-ai/langgraphjs/commit/e80f8b2f002e73816aa668894489c42e951d73d6) feat(sdk): add multi-subagent tracking to useStream - [`a7620f1`](https://github.com/langchain-ai/langgraphjs/commit/a7620f1bce2795f8f8ff5c0730cc7e29e463d259) Update changeset for langchain SDK versioning - [`643f11b`](https://github.com/langchain-ai/langgraphjs/commit/643f11bf027e91c5fbe8654a0c7aa2173653f93c) fix types after dep update - [`f6d02f8`](https://github.com/langchain-ai/langgraphjs/commit/f6d02f8f294fe892c27ce9417381311012f7a2e4) fix(sdk): persist subagent state after stream completion and page refresh - [`7ec09d6`](https://github.com/langchain-ai/langgraphjs/commit/7ec09d6025775555714ab88657a3db975bd63ce2) align UseStream and SubAgentStream - [`8c5d6f7`](https://github.com/langchain-ai/langgraphjs/commit/8c5d6f762a7216bebe2c407f053629a2a460c091) add example with subagents using tool calls - [`9d65f87`](https://github.com/langchain-ai/langgraphjs/commit/9d65f87cf26792ddca092e1becc713dbd27bceb9) seperate in use case streams - [`84d542a`](https://github.com/langchain-ai/langgraphjs/commit/84d542a75af586b5c22c9220cb1f4c37d4c7df14) format - [`6b0e6d3`](https://github.com/langchain-ai/langgraphjs/commit/6b0e6d301be117bbffccc3fc10f640984117e7ab) use fixtures for lg - [`1f466e6`](https://github.com/langchain-ai/langgraphjs/commit/1f466e6cb2fd2f2437ee5a0e3849d5a2168d5c40) more type fixes ### 📊 Changes **46 files changed** (+7083 additions, -2309 deletions) <details> <summary>View changed files</summary> ➕ `.changeset/tall-parrots-agree.md` (+5 -0) 📝 `examples/ui-react-transport/src/server.mts` (+8 -17) 📝 `examples/ui-react/langgraph.json` (+2 -1) 📝 `examples/ui-react/package.json` (+1 -0) 📝 `examples/ui-react/src/components/Layout.tsx` (+2 -1) 📝 `examples/ui-react/src/examples/custom-streaming/agent.ts` (+23 -20) ➕ `examples/ui-react/src/examples/deepagent-tools/agent.ts` (+350 -0) ➕ `examples/ui-react/src/examples/deepagent-tools/components/SubagentPipeline.tsx` (+100 -0) ➕ `examples/ui-react/src/examples/deepagent-tools/components/SubagentStreamCard.tsx` (+291 -0) ➕ `examples/ui-react/src/examples/deepagent-tools/components/SubagentToolCallCard.tsx` (+326 -0) ➕ `examples/ui-react/src/examples/deepagent-tools/index.tsx` (+238 -0) ➕ `examples/ui-react/src/examples/deepagent-tools/types.ts` (+47 -0) ➕ `examples/ui-react/src/examples/deepagent/agent.ts` (+384 -0) ➕ `examples/ui-react/src/examples/deepagent/components/SubagentCard.tsx` (+255 -0) ➕ `examples/ui-react/src/examples/deepagent/components/SubagentPipeline.tsx` (+80 -0) ➕ `examples/ui-react/src/examples/deepagent/index.tsx` (+282 -0) ➖ `examples/ui-react/src/examples/multi-step-graph/agent.ts` (+0 -265) ➖ `examples/ui-react/src/examples/multi-step-graph/index.tsx` (+0 -384) 📝 `examples/ui-react/src/examples/parallel-research/agent.ts` (+17 -14) 📝 `internal/build/package.json` (+11 -11) _...and 26 more files_ </details> ### 📄 Description This PR adds comprehensive streaming enhancements to the `useStream` hook, enabling developers to: - Track **node executions** in any graph - Monitor **tool calls** with typed arguments in agents - Display **multiple concurrent subagent executions** in deep agent scenarios ## Stream Type Hierarchy The PR introduces a layered type system that automatically resolves based on your agent/graph type: ``` UseStreamOptions / BaseStream ← Original useStream (unchanged) │ ├── UseAgentStreamOptions / UseAgentStream │ └── Extends UseGraphStream │ Input: (no new options) │ Output: + toolCalls, getToolCalls │ └── UseDeepAgentStreamOptions / UseDeepAgentStream └── Extends UseAgentStream Input: + subagentToolNames, filterSubagentMessages Output: + subagents, activeSubagents, getSubagent, getSubagentsByType ``` Usage automatically resolves to the correct interface: ```typescript useStream<typeof graph>() // → BaseStream useStream<typeof agent>() // → UseAgentStream (with toolCalls) useStream<typeof deepAgent>() // → UseDeepAgentStream (with subagents) ``` ## Tool Call Tracking (for `createAgent`) Track tool calls with full type inference from your agent's tools: ```typescript const stream = useStream<typeof agent>({ assistantId: "my-agent", apiUrl: "http://localhost:2024", }); // All tool calls paired with results stream.toolCalls.forEach(({ call, result }) => { if (call.name === "search") { // call.args is typed as { query: string } console.log("Searched for:", call.args.query); } }); // Get tool calls for a specific AI message stream.getToolCalls(aiMessage) ``` ## Subagent Streaming (for `createDeepAgent`) Track multiple concurrent subagent executions: ```typescript const stream = useStream<typeof agent>({ assistantId: "vacation-planner", apiUrl: "http://localhost:2024", subagentToolNames: ["task"], // Tool names that spawn subagents filterSubagentMessages: true, // Keep main messages clean }); // All subagent executions (Map<string, SubagentStream>) stream.subagents // Currently running subagents stream.activeSubagents // Get specific subagent by tool call ID stream.getSubagent("call_abc123") // Get all subagents of a specific type (with typed state) stream.getSubagentsByType("weather-scout") ``` ### `SubagentStream` interface: ```typescript interface SubagentStream<StateType, ToolCall> { id: string; // Tool call ID toolCall: { id: string; name: string; args: { description?: string; subagent_type?: string; }; }; status: "pending" | "running" | "complete" | "error"; result: string | null; // Final result when complete error: string | null; // Error message if failed namespace: string[]; // LangGraph namespace path messages: Message<ToolCall>[]; // Subagent's message history values: StateType; // Subagent's state (typed per-subagent) parentId: string | null; // For nested subagents depth: number; // Nesting level (0 = direct child) startedAt: Date | null; completedAt: Date | null; } ``` ### `filterSubagentMessages` option: When enabled, subagent messages are filtered from the main `stream.messages` array and only accessible via `SubagentStream.messages`. This allows clean separation of main agent and subagent streams in the UI. ## Usage Example ```tsx function DeepAgentUI() { const stream = useStream<typeof agent>({ assistantId: "vacation-planner", apiUrl: "http://localhost:2024", filterSubagentMessages: true, }); return ( <div> {/* Main agent messages */} <MessageList messages={stream.messages} /> {/* Subagent cards */} <div className="grid grid-cols-3 gap-4"> {[...stream.subagents.values()].map((subagent) => ( <SubagentCard key={subagent.id} type={subagent.toolCall.args.subagent_type} status={subagent.status} content={subagent.messages .filter(m => m.type === "ai") .map(m => m.content) .join("")} /> ))} </div> </div> ); } ``` ## Implementation Details ### New Managers - **`SubagentManager`** class handles all subagent state tracking ### Key Features - **Namespace mapping**: Connects LangGraph's internal pregel task IDs to the original tool call IDs via description matching - **Per-node/subagent `MessageTupleManager`**: Properly concatenates streaming message chunks - **Status-based filtering**: Only shows subagents once they're actually running (filters out partial streaming artifacts) - **Type inference**: Full TypeScript inference for node return types, tool call args, and subagent states ## Added Examples Added deepagent examples displaying individual subagent streams: <img width="1649" height="1152" alt="Screenshot 2026-01-15 at 5 00 13 PM" src="https://github.com/user-attachments/assets/00b9548a-702f-48cd-8ee7-538f9a09d99b" /> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
yindo added the pull-request label 2026-02-15 20:16:58 -05:00
yindo closed this issue 2026-02-15 20:16:59 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraphjs#1830