@langchain/langgraph-sdk
Version:
Client library for interacting with the LangGraph API
266 lines (265 loc) • 11 kB
text/typescript
import { Message } from "../types.messages.cjs";
import { CheckpointsStreamEvent, CustomStreamEvent, DebugStreamEvent, ErrorStreamEvent, EventsStreamEvent, FeedbackStreamEvent, MessagesTupleStreamEvent, MetadataStreamEvent, TasksStreamEvent, ToolsStreamEvent, UpdatesStreamEvent, ValuesStreamEvent } from "../types.stream.cjs";
import { BagTemplate } from "../types.template.cjs";
import { SubagentStreamInterface } from "./types.cjs";
import { MessageTupleManager } from "./messages.cjs";
import { BaseMessage } from "@langchain/core/messages";
//#region src/ui/manager.d.ts
type GetUpdateType<Bag extends BagTemplate, StateType extends Record<string, unknown>> = Bag extends {
UpdateType: unknown;
} ? Bag["UpdateType"] : Partial<StateType>;
type GetCustomEventType<Bag extends BagTemplate> = Bag extends {
CustomEventType: unknown;
} ? Bag["CustomEventType"] : unknown;
type EventStreamMap<StateType, UpdateType, CustomType> = {
values: ValuesStreamEvent<StateType>;
updates: UpdatesStreamEvent<UpdateType>;
custom: CustomStreamEvent<CustomType>;
debug: DebugStreamEvent;
messages: MessagesTupleStreamEvent;
events: EventsStreamEvent;
metadata: MetadataStreamEvent;
checkpoints: CheckpointsStreamEvent<StateType>;
tasks: TasksStreamEvent<StateType, UpdateType>;
error: ErrorStreamEvent;
feedback: FeedbackStreamEvent;
tools: ToolsStreamEvent;
};
type EventStreamEvent<StateType, UpdateType, CustomType> = EventStreamMap<StateType, UpdateType, CustomType>[keyof EventStreamMap<StateType, UpdateType, CustomType>];
interface StreamManagerEventCallbacks<StateType extends Record<string, unknown>, Bag extends BagTemplate = BagTemplate> {
onUpdateEvent?: (data: UpdatesStreamEvent<GetUpdateType<Bag, StateType>>["data"], options: {
namespace: string[] | undefined;
mutate: (update: Partial<StateType> | ((prev: StateType) => Partial<StateType>)) => void;
}) => void;
onCustomEvent?: (data: GetCustomEventType<Bag>, options: {
namespace: string[] | undefined;
mutate: (update: Partial<StateType> | ((prev: StateType) => Partial<StateType>)) => void;
}) => void;
onMetadataEvent?: (data: MetadataStreamEvent["data"]) => void;
onLangChainEvent?: (data: EventsStreamEvent["data"]) => void;
onDebugEvent?: (data: DebugStreamEvent["data"], options: {
namespace: string[] | undefined;
}) => void;
onCheckpointEvent?: (data: CheckpointsStreamEvent<StateType>["data"], options: {
namespace: string[] | undefined;
}) => void;
onTaskEvent?: (data: TasksStreamEvent<StateType, GetUpdateType<Bag, StateType>>["data"], options: {
namespace: string[] | undefined;
}) => void;
onToolEvent?: (data: ToolsStreamEvent["data"], options: {
namespace: string[] | undefined;
mutate: (update: Partial<StateType> | ((prev: StateType) => Partial<StateType>)) => void;
}) => void;
}
/**
* Options for StreamManager constructor.
*/
interface StreamManagerOptions {
/**
* Throttle the stream updates.
* If a number is provided, updates are throttled to the given milliseconds.
* If `true`, updates are batched in a single macrotask.
* If `false`, updates are not throttled.
*/
throttle: number | boolean;
/**
* Tool names that indicate subagent invocation.
*
* When an AI message contains tool calls with these names, they are
* automatically tracked as subagent executions. This enables the
* `subagents`, `activeSubagents`, `getSubagent()`, and `getSubagentsByType()`
* properties on the stream.
*
* @default ["task"]
*
* @example
* ```typescript
* // Track both "task" and "delegate" as subagent tools
* subagentToolNames: ["task", "delegate", "spawn_agent"]
* ```
*/
subagentToolNames?: string[];
/**
* Filter out messages from subagent streams in the main messages array.
*
* When enabled, messages from subagraph executions (those with a `tools:` namespace)
* are excluded from `stream.messages`. Instead, these messages are tracked
* per-subagent and accessible via `stream.subagents.get(id).messages`.
*
* This is useful for deep agent architectures where you want to display
* the main conversation separately from subagent activity.
*
* @default false
*
* @example
* ```typescript
* const stream = useStream({
* assistantId: "my-agent",
* filterSubagentMessages: true,
* });
*
* // Main thread messages only (no subagent messages)
* stream.messages
*
* // Access subagent messages individually
* stream.subagents.get("call_xyz").messages
* ```
*/
filterSubagentMessages?: boolean;
/**
* Converts a @langchain/core BaseMessage to the desired output format.
*
* Defaults to `toMessageDict` which produces plain Message objects.
* Framework SDKs pass `toMessageClass` (identity) to keep class instances.
*/
toMessage?: (chunk: BaseMessage) => Message | BaseMessage;
}
declare class StreamManager<StateType extends Record<string, unknown>, Bag extends BagTemplate = BagTemplate> {
private abortRef;
private messages;
private subagentManager;
private listeners;
private throttle;
private filterSubagentMessages;
private toMessage;
private queue;
private queueSize;
private state;
constructor(messages: MessageTupleManager, options: StreamManagerOptions);
/**
* Increment version counter to trigger React re-renders.
* Called when subagent state changes.
*/
private bumpVersion;
/**
* Get all subagents as a Map.
*/
getSubagents(): Map<string, SubagentStreamInterface>;
/**
* Get all currently running subagents.
*/
getActiveSubagents(): SubagentStreamInterface[];
/**
* Get a specific subagent by tool call ID.
*/
getSubagent(toolCallId: string): SubagentStreamInterface | undefined;
/**
* Get all subagents of a specific type.
*/
getSubagentsByType(type: string): SubagentStreamInterface[];
/**
* Get all subagents triggered by a specific AI message.
*/
getSubagentsByMessage(messageId: string): SubagentStreamInterface[];
/**
* Reconstruct subagent state from historical messages.
*
* This method should be called when loading thread history to restore
* subagent visualization after:
* - Page refresh (when stream has already completed)
* - Loading thread history
* - Navigating between threads
*
* @param messages - Array of messages from thread history
* @param options - Optional configuration
* @param options.skipIfPopulated - If true, skip reconstruction if subagents already exist
*/
reconstructSubagents(messages: Message[], options?: {
skipIfPopulated?: boolean;
}): void;
/**
* Fetch and restore internal messages for reconstructed subagents from their
* subgraph checkpoints. Should be called after `reconstructSubagents` to
* restore the full subagent conversation after a page refresh.
*
* Subagent messages are persisted in the LangGraph checkpointer under a
* subgraph-specific `checkpoint_ns` (e.g. `tools:<uuid>`). This method
* discovers the correct namespace by inspecting the main thread's intermediate
* history checkpoints, where each pending task's `checkpoint.checkpoint_ns`
* identifies the subgraph. Tasks are matched to tool calls by their Send index
* (`task.path[1]`), which corresponds to the order of tool calls in the AI
* message — no deepagent-specific metadata required.
*
* @param threads - Client with a `getHistory` method (e.g. `client.threads`)
* @param threadId - The parent thread ID
* @param options - Optional configuration
* @param options.messagesKey - Key in state values containing messages (default: "messages")
* @param options.signal - AbortSignal to cancel in-flight requests on effect cleanup
*/
fetchSubagentHistory(threads: {
getHistory<V extends Record<string, unknown>>(threadId: string, options?: {
limit?: number;
checkpoint?: {
checkpoint_ns?: string;
};
signal?: AbortSignal;
}): Promise<Array<{
values: V;
tasks?: Array<{
id: string;
name: string;
path?: unknown[];
checkpoint?: {
checkpoint_ns?: string;
} | null;
}>;
}>>;
}, threadId: string, options?: {
messagesKey?: string;
historyLimit?: number;
signal?: AbortSignal;
}): Promise<void>;
/**
* Check if any subagents are currently tracked.
*/
hasSubagents(): boolean;
private setState;
private notifyListeners;
subscribe: (listener: () => void) => () => void;
getSnapshot: () => {
isLoading: boolean;
values: [values: StateType, kind: "stop" | "stream"] | null;
error: unknown; /** Version counter to force React re-renders on subagent changes */
version: number;
};
get isLoading(): boolean;
get values(): StateType | null;
get error(): unknown;
setStreamValues: (values: StateType | ((prev: StateType | null, kind: "stop" | "stream") => StateType | null) | null, kind?: "stop" | "stream") => void;
private getMutateFn;
private matchEventType;
protected enqueue: (action: (signal: AbortSignal) => Promise<AsyncGenerator<EventStreamEvent<StateType, GetUpdateType<Bag, StateType>, GetCustomEventType<Bag>>, any, any>>, options: {
getMessages: (values: StateType) => Message[];
setMessages: (current: StateType, messages: Message[]) => StateType;
initialValues: StateType;
callbacks: StreamManagerEventCallbacks<StateType, Bag>;
onSuccess: () => void | StateType | Promise<void | StateType | null | undefined> | null | undefined;
onError: (error: unknown) => void | Promise<void>;
onFinish?: (() => void) | undefined;
}) => Promise<void>;
start: (action: (signal: AbortSignal) => Promise<AsyncGenerator<EventStreamEvent<StateType, GetUpdateType<Bag, StateType>, GetCustomEventType<Bag>>, any, any>>, options: {
getMessages: (values: StateType) => Message[];
setMessages: (current: StateType, messages: Message[]) => StateType;
initialValues: StateType;
callbacks: StreamManagerEventCallbacks<StateType, Bag>;
onSuccess: () => void | StateType | Promise<void | StateType | null | undefined> | null | undefined;
onError: (error: unknown) => void | Promise<void>;
onFinish?: (() => void) | undefined;
}, startOptions?: {
/**
* If true, abort any currently running stream before starting this one.
* Used for multitask_strategy: "interrupt" and "rollback" to unblock
* the queue so the new run request can proceed immediately.
*/
abortPrevious?: boolean | undefined;
} | undefined) => Promise<void>;
stop: (historyValues: StateType, options: {
onStop?: ((options: {
mutate: (update: Partial<StateType> | ((prev: StateType) => Partial<StateType>)) => void;
}) => void) | undefined;
}) => Promise<void>;
clear: () => void;
}
//#endregion
export { EventStreamEvent, StreamManager };
//# sourceMappingURL=manager.d.cts.map