mirror of
https://github.com/langchain-ai/langgraphjs.git
synced 2026-07-23 17:45:23 -04:00
feat(api,sdk): add typing and support for "checkpoints" and "tasks" stream mode (#1416)
This commit is contained in:
@@ -2,14 +2,14 @@
|
||||
"$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
|
||||
"changelog": "@changesets/cli/changelog",
|
||||
"commit": false,
|
||||
"fixed": [],
|
||||
"linked": [
|
||||
"fixed": [
|
||||
[
|
||||
"@langchain/langgraph-api",
|
||||
"@langchain/langgraph-cli",
|
||||
"@langchain/langgraph-ui"
|
||||
]
|
||||
],
|
||||
"linked": [],
|
||||
"access": "public",
|
||||
"baseBranch": "main",
|
||||
"updateInternalDependencies": "patch",
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@langchain/langgraph-api": patch
|
||||
---
|
||||
|
||||
feat(api): pass through "tasks" and "checkpoints" stream mode
|
||||
@@ -1,7 +1,5 @@
|
||||
---
|
||||
"@langchain/langgraph-cli": patch
|
||||
"@langchain/langgraph-api": patch
|
||||
"@langchain/langgraph-ui": patch
|
||||
---
|
||||
|
||||
feat(cli): add sysinfo command to obtain actual version
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@langchain/langgraph-sdk": patch
|
||||
---
|
||||
|
||||
feat(sdk): add typing for "tasks" and "checkpoints" stream mode
|
||||
@@ -212,6 +212,8 @@ export const RunCreate = z
|
||||
"messages-tuple",
|
||||
"updates",
|
||||
"events",
|
||||
"tasks",
|
||||
"checkpoints",
|
||||
"debug",
|
||||
"custom",
|
||||
])
|
||||
@@ -222,6 +224,8 @@ export const RunCreate = z
|
||||
"messages-tuple",
|
||||
"updates",
|
||||
"events",
|
||||
"tasks",
|
||||
"checkpoints",
|
||||
"debug",
|
||||
"custom",
|
||||
]),
|
||||
|
||||
@@ -2704,3 +2704,176 @@ it("resumable streams", { timeout: 10_000 }, async () => {
|
||||
|
||||
expect(join).toEqual(source);
|
||||
});
|
||||
|
||||
it("tasks / checkpoints stream mode", async () => {
|
||||
const assistant = await client.assistants.create({ graphId: "agent" });
|
||||
const thread = await client.threads.create();
|
||||
|
||||
const stream = await gatherIterator(
|
||||
client.runs.stream(thread.thread_id, assistant.assistant_id, {
|
||||
input: { messages: [{ role: "human", content: "input" }] },
|
||||
streamMode: ["tasks", "checkpoints"],
|
||||
config: globalConfig,
|
||||
})
|
||||
);
|
||||
|
||||
expect(stream).toMatchObject([
|
||||
{
|
||||
event: "metadata",
|
||||
data: { run_id: expect.any(String) },
|
||||
},
|
||||
{
|
||||
event: "checkpoints",
|
||||
data: {
|
||||
values: { messages: [] },
|
||||
metadata: { source: "input", step: -1 },
|
||||
next: ["__start__"],
|
||||
},
|
||||
},
|
||||
{
|
||||
event: "checkpoints",
|
||||
data: {
|
||||
values: { messages: [{ content: "input", type: "human" }] },
|
||||
metadata: { source: "loop", step: 0 },
|
||||
next: ["agent"],
|
||||
},
|
||||
},
|
||||
{
|
||||
event: "tasks",
|
||||
data: {
|
||||
name: "agent",
|
||||
input: { messages: [{ content: "input", type: "human" }] },
|
||||
triggers: ["branch:to:agent"],
|
||||
interrupts: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
event: "tasks",
|
||||
data: {
|
||||
name: "agent",
|
||||
result: expect.arrayContaining([
|
||||
[
|
||||
"messages",
|
||||
[expect.objectContaining({ content: "begin", type: "ai" })],
|
||||
],
|
||||
]),
|
||||
interrupts: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
event: "checkpoints",
|
||||
data: {
|
||||
values: {
|
||||
messages: [
|
||||
{ content: "input", type: "human" },
|
||||
{ content: "begin", type: "ai" },
|
||||
],
|
||||
},
|
||||
metadata: { source: "loop", step: 1 },
|
||||
next: ["tool"],
|
||||
},
|
||||
},
|
||||
{
|
||||
event: "tasks",
|
||||
data: {
|
||||
name: "tool",
|
||||
input: {
|
||||
messages: [
|
||||
{ content: "input", type: "human" },
|
||||
{ content: "begin", type: "ai" },
|
||||
],
|
||||
},
|
||||
triggers: ["branch:to:tool"],
|
||||
interrupts: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
event: "tasks",
|
||||
data: {
|
||||
name: "tool",
|
||||
result: expect.arrayContaining([
|
||||
[
|
||||
"messages",
|
||||
[
|
||||
expect.objectContaining({
|
||||
content: "tool_call__begin",
|
||||
tool_call_id: "tool_call_id",
|
||||
type: "tool",
|
||||
}),
|
||||
],
|
||||
],
|
||||
]),
|
||||
interrupts: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
event: "checkpoints",
|
||||
data: {
|
||||
values: {
|
||||
messages: [
|
||||
{ content: "input", type: "human" },
|
||||
{ content: "begin", type: "ai" },
|
||||
{
|
||||
content: "tool_call__begin",
|
||||
tool_call_id: "tool_call_id",
|
||||
type: "tool",
|
||||
},
|
||||
],
|
||||
},
|
||||
metadata: { source: "loop", step: 2 },
|
||||
next: ["agent"],
|
||||
},
|
||||
},
|
||||
{
|
||||
event: "tasks",
|
||||
data: {
|
||||
name: "agent",
|
||||
input: {
|
||||
messages: [
|
||||
{ content: "input", type: "human" },
|
||||
{ content: "begin", type: "ai" },
|
||||
{
|
||||
content: "tool_call__begin",
|
||||
tool_call_id: "tool_call_id",
|
||||
type: "tool",
|
||||
},
|
||||
],
|
||||
},
|
||||
triggers: ["branch:to:agent"],
|
||||
interrupts: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
event: "tasks",
|
||||
data: {
|
||||
name: "agent",
|
||||
result: expect.arrayContaining([
|
||||
[
|
||||
"messages",
|
||||
[expect.objectContaining({ content: "end", type: "ai" })],
|
||||
],
|
||||
]),
|
||||
interrupts: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
event: "checkpoints",
|
||||
data: {
|
||||
values: {
|
||||
messages: [
|
||||
{ content: "input", type: "human" },
|
||||
{ content: "begin", type: "ai" },
|
||||
{
|
||||
content: "tool_call__begin",
|
||||
tool_call_id: "tool_call_id",
|
||||
type: "tool",
|
||||
},
|
||||
{ content: "end", type: "ai" },
|
||||
],
|
||||
},
|
||||
metadata: { source: "loop", step: 3 },
|
||||
next: [],
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -33,6 +33,7 @@ import type {
|
||||
ThreadState,
|
||||
} from "../schema.js";
|
||||
import type {
|
||||
CheckpointsStreamEvent,
|
||||
CustomStreamEvent,
|
||||
DebugStreamEvent,
|
||||
ErrorStreamEvent,
|
||||
@@ -42,6 +43,7 @@ import type {
|
||||
MessagesTupleStreamEvent,
|
||||
MetadataStreamEvent,
|
||||
StreamMode,
|
||||
TasksStreamEvent,
|
||||
UpdatesStreamEvent,
|
||||
ValuesStreamEvent,
|
||||
} from "../types.stream.js";
|
||||
@@ -545,6 +547,18 @@ export interface UseStreamOptions<
|
||||
*/
|
||||
onDebugEvent?: (data: DebugStreamEvent["data"]) => void;
|
||||
|
||||
/**
|
||||
* Callback that is called when a checkpoints event is received.
|
||||
*/
|
||||
onCheckpointEvent?: (data: CheckpointsStreamEvent<StateType>["data"]) => void;
|
||||
|
||||
/**
|
||||
* Callback that is called when a tasks event is received.
|
||||
*/
|
||||
onTaskEvent?: (
|
||||
data: TasksStreamEvent<StateType, GetUpdateType<Bag, StateType>>["data"]
|
||||
) => void;
|
||||
|
||||
/**
|
||||
* Callback that is called when the stream is stopped by the user.
|
||||
* Provides a mutate function to update the stream state immediately
|
||||
@@ -814,6 +828,8 @@ export function useStream<
|
||||
| MessagesTupleStreamEvent
|
||||
| EventsStreamEvent
|
||||
| MetadataStreamEvent
|
||||
| CheckpointsStreamEvent<StateType>
|
||||
| TasksStreamEvent<StateType, UpdateType>
|
||||
| ErrorStreamEvent
|
||||
| FeedbackStreamEvent;
|
||||
|
||||
@@ -864,7 +880,15 @@ export function useStream<
|
||||
const abortRef = useRef<AbortController | null>(null);
|
||||
|
||||
const trackStreamModeRef = useRef<
|
||||
Array<"values" | "updates" | "events" | "custom" | "messages-tuple">
|
||||
Array<
|
||||
| "values"
|
||||
| "updates"
|
||||
| "events"
|
||||
| "custom"
|
||||
| "messages-tuple"
|
||||
| "checkpoints"
|
||||
| "tasks"
|
||||
>
|
||||
>([]);
|
||||
|
||||
const trackStreamMode = useCallback(
|
||||
@@ -883,18 +907,25 @@ export function useStream<
|
||||
const hasLangChainListener = options.onLangChainEvent != null;
|
||||
const hasDebugListener = options.onDebugEvent != null;
|
||||
|
||||
const hasCheckpointListener = options.onCheckpointEvent != null;
|
||||
const hasTaskListener = options.onTaskEvent != null;
|
||||
|
||||
const callbackStreamMode = useMemo(() => {
|
||||
const modes: Exclude<StreamMode, "messages">[] = [];
|
||||
if (hasUpdateListener) modes.push("updates");
|
||||
if (hasCustomListener) modes.push("custom");
|
||||
if (hasLangChainListener) modes.push("events");
|
||||
if (hasDebugListener) modes.push("debug");
|
||||
if (hasCheckpointListener) modes.push("checkpoints");
|
||||
if (hasTaskListener) modes.push("tasks");
|
||||
return modes;
|
||||
}, [
|
||||
hasUpdateListener,
|
||||
hasCustomListener,
|
||||
hasLangChainListener,
|
||||
hasDebugListener,
|
||||
hasCheckpointListener,
|
||||
hasTaskListener,
|
||||
]);
|
||||
|
||||
const clearCallbackRef = useRef<() => void>(null!);
|
||||
@@ -1040,6 +1071,8 @@ export function useStream<
|
||||
if (event === "metadata") options.onMetadataEvent?.(data);
|
||||
if (event === "events") options.onLangChainEvent?.(data);
|
||||
if (event === "debug") options.onDebugEvent?.(data);
|
||||
if (event === "checkpoints") options.onCheckpointEvent?.(data);
|
||||
if (event === "tasks") options.onTaskEvent?.(data);
|
||||
|
||||
if (event === "values") {
|
||||
if ("__interrupt__" in data) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Message } from "./types.messages.js";
|
||||
|
||||
import type { Interrupt, Metadata, Config, ThreadTask } from "./schema.js";
|
||||
/**
|
||||
import type { SubgraphCheckpointsStreamEvent } from "./types.stream.subgraph.js";
|
||||
* Stream modes
|
||||
* - "values": Stream only the state values.
|
||||
* - "messages": Stream complete messages.
|
||||
@@ -16,6 +17,8 @@ export type StreamMode =
|
||||
| "updates"
|
||||
| "events"
|
||||
| "debug"
|
||||
| "tasks"
|
||||
| "checkpoints"
|
||||
| "custom"
|
||||
| "messages-tuple";
|
||||
|
||||
@@ -120,6 +123,66 @@ type MessagesPartialStreamEvent = {
|
||||
data: Message[];
|
||||
};
|
||||
|
||||
type TasksStreamCreateEvent<StateType> = {
|
||||
id?: string;
|
||||
event: "tasks";
|
||||
data: {
|
||||
id: string;
|
||||
name: string;
|
||||
interrupts: Interrupt[];
|
||||
input: StateType;
|
||||
triggers: string[];
|
||||
};
|
||||
};
|
||||
|
||||
type TasksStreamResultEvent<UpdateType> = {
|
||||
id?: string;
|
||||
event: "tasks";
|
||||
data: {
|
||||
id: string;
|
||||
name: string;
|
||||
interrupts: Interrupt[];
|
||||
result: [string, UpdateType][];
|
||||
};
|
||||
};
|
||||
|
||||
type TasksStreamErrorEvent = {
|
||||
id?: string;
|
||||
event: "tasks";
|
||||
data: {
|
||||
id: string;
|
||||
name: string;
|
||||
interrupts: Interrupt[];
|
||||
error: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type TasksStreamEvent<StateType, UpdateType> =
|
||||
| TasksStreamCreateEvent<StateType>
|
||||
| TasksStreamResultEvent<UpdateType>
|
||||
| TasksStreamErrorEvent;
|
||||
|
||||
type SubgraphTasksStreamEvent<StateType, UpdateType> =
|
||||
| AsSubgraph<TasksStreamCreateEvent<StateType>>
|
||||
| AsSubgraph<TasksStreamResultEvent<UpdateType>>
|
||||
| AsSubgraph<TasksStreamErrorEvent>;
|
||||
|
||||
export type CheckpointsStreamEvent<StateType> = {
|
||||
id?: string;
|
||||
event: "checkpoints";
|
||||
data: {
|
||||
values: StateType;
|
||||
next: string[];
|
||||
config: Config;
|
||||
metadata: Metadata;
|
||||
tasks: ThreadTask[];
|
||||
};
|
||||
};
|
||||
|
||||
type SubgraphCheckpointsStreamEvent<StateType> = AsSubgraph<
|
||||
CheckpointsStreamEvent<StateType>
|
||||
>;
|
||||
|
||||
/**
|
||||
* Message stream event specific to LangGraph Server.
|
||||
* @deprecated Use `streamMode: "messages-tuple"` instead.
|
||||
@@ -194,6 +257,8 @@ type GetStreamModeMap<
|
||||
debug: DebugStreamEvent;
|
||||
messages: MessagesStreamEvent;
|
||||
"messages-tuple": MessagesTupleStreamEvent;
|
||||
tasks: TasksStreamEvent<TStateType, TUpdateType>;
|
||||
checkpoints: CheckpointsStreamEvent<TStateType>;
|
||||
events: EventsStreamEvent;
|
||||
}[TStreamMode extends StreamMode[] ? TStreamMode[number] : TStreamMode]
|
||||
| ErrorStreamEvent
|
||||
@@ -214,6 +279,8 @@ type GetSubgraphsStreamModeMap<
|
||||
messages: SubgraphMessagesStreamEvent;
|
||||
"messages-tuple": SubgraphMessagesTupleStreamEvent;
|
||||
events: SubgraphEventsStreamEvent;
|
||||
tasks: SubgraphTasksStreamEvent<TStateType, TUpdateType>;
|
||||
checkpoints: SubgraphCheckpointsStreamEvent<TStateType>;
|
||||
}[TStreamMode extends StreamMode[] ? TStreamMode[number] : TStreamMode]
|
||||
| SubgraphErrorStreamEvent
|
||||
| MetadataStreamEvent
|
||||
|
||||
Reference in New Issue
Block a user