Skip to content

Commit ccd2790

Browse files
authored
fix(langgraph): forward submit run options
1 parent b3239a8 commit ccd2790

10 files changed

Lines changed: 488 additions & 36 deletions

File tree

apps/website/content/docs/agent/api/api-docs.json

Lines changed: 164 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
},
5050
{
5151
"name": "createQueuedRun",
52-
"signature": "createQueuedRun(assistantId: string, threadId: string, payload: unknown, signal: AbortSignal)",
52+
"signature": "createQueuedRun(assistantId: string, threadId: string, payload: unknown, signal: AbortSignal, options: LangGraphSubmitOptions)",
5353
"description": "Create a pending server-side run using LangGraph's enqueue strategy.",
5454
"params": [
5555
{
@@ -75,6 +75,12 @@
7575
"type": "AbortSignal",
7676
"description": "",
7777
"optional": false
78+
},
79+
{
80+
"name": "options",
81+
"type": "LangGraphSubmitOptions",
82+
"description": "",
83+
"optional": true
7884
}
7985
]
8086
},
@@ -130,7 +136,7 @@
130136
},
131137
{
132138
"name": "stream",
133-
"signature": "stream(assistantId: string, threadId: string | null, payload: unknown, signal: AbortSignal)",
139+
"signature": "stream(assistantId: string, threadId: string | null, payload: unknown, signal: AbortSignal, options: LangGraphSubmitOptions)",
134140
"description": "Open a streaming connection, creating a thread if needed.",
135141
"params": [
136142
{
@@ -156,6 +162,12 @@
156162
"type": "AbortSignal",
157163
"description": "",
158164
"optional": false
165+
},
166+
{
167+
"name": "options",
168+
"type": "LangGraphSubmitOptions",
169+
"description": "",
170+
"optional": true
159171
}
160172
]
161173
}
@@ -206,6 +218,12 @@
206218
"type": "object[]",
207219
"description": "",
208220
"optional": false
221+
},
222+
{
223+
"name": "streams",
224+
"type": "object[]",
225+
"description": "",
226+
"optional": false
209227
}
210228
],
211229
"methods": [
@@ -242,7 +260,7 @@
242260
},
243261
{
244262
"name": "createQueuedRun",
245-
"signature": "createQueuedRun(_assistantId: string, threadId: string, payload: unknown, signal: AbortSignal)",
263+
"signature": "createQueuedRun(_assistantId: string, threadId: string, payload: unknown, signal: AbortSignal, options: LangGraphSubmitOptions)",
246264
"description": "Optional: create a server-side queued run without joining it immediately.",
247265
"params": [
248266
{
@@ -268,6 +286,12 @@
268286
"type": "AbortSignal",
269287
"description": "",
270288
"optional": false
289+
},
290+
{
291+
"name": "options",
292+
"type": "LangGraphSubmitOptions",
293+
"description": "",
294+
"optional": true
271295
}
272296
]
273297
},
@@ -361,7 +385,7 @@
361385
},
362386
{
363387
"name": "stream",
364-
"signature": "stream(_assistantId: string, _threadId: string | null, _payload: unknown, signal: AbortSignal)",
388+
"signature": "stream(_assistantId: string, _threadId: string | null, _payload: unknown, signal: AbortSignal, options: LangGraphSubmitOptions)",
365389
"description": "Open a streaming connection to an agent and yield events.",
366390
"params": [
367391
{
@@ -387,6 +411,12 @@
387411
"type": "AbortSignal",
388412
"description": "",
389413
"optional": false
414+
},
415+
{
416+
"name": "options",
417+
"type": "LangGraphSubmitOptions",
418+
"description": "",
419+
"optional": true
390420
}
391421
]
392422
}
@@ -918,7 +948,7 @@
918948
{
919949
"name": "submit",
920950
"type": "object",
921-
"description": "",
951+
"description": "Submit input, resume commands, checkpoint forks, or other LangGraph run options.",
922952
"optional": false
923953
},
924954
{
@@ -953,17 +983,143 @@
953983
"kind": "interface",
954984
"description": "Options accepted by LangGraph-backed submit calls.",
955985
"properties": [
986+
{
987+
"name": "afterSeconds",
988+
"type": "number",
989+
"description": "",
990+
"optional": true
991+
},
992+
{
993+
"name": "checkpoint",
994+
"type": "Omit<Checkpoint, \"thread_id\"> | null",
995+
"description": "",
996+
"optional": true
997+
},
998+
{
999+
"name": "checkpointDuring",
1000+
"type": "boolean",
1001+
"description": "",
1002+
"optional": true
1003+
},
1004+
{
1005+
"name": "checkpointId",
1006+
"type": "string",
1007+
"description": "",
1008+
"optional": true
1009+
},
1010+
{
1011+
"name": "command",
1012+
"type": "Command",
1013+
"description": "",
1014+
"optional": true
1015+
},
1016+
{
1017+
"name": "config",
1018+
"type": "Config",
1019+
"description": "",
1020+
"optional": true
1021+
},
1022+
{
1023+
"name": "context",
1024+
"type": "unknown",
1025+
"description": "",
1026+
"optional": true
1027+
},
1028+
{
1029+
"name": "durability",
1030+
"type": "LangGraphDurability",
1031+
"description": "",
1032+
"optional": true
1033+
},
1034+
{
1035+
"name": "feedbackKeys",
1036+
"type": "string[]",
1037+
"description": "",
1038+
"optional": true
1039+
},
1040+
{
1041+
"name": "ifNotExists",
1042+
"type": "\"create\" | \"reject\"",
1043+
"description": "",
1044+
"optional": true
1045+
},
1046+
{
1047+
"name": "interruptAfter",
1048+
"type": "string[] | \"*\"",
1049+
"description": "",
1050+
"optional": true
1051+
},
1052+
{
1053+
"name": "interruptBefore",
1054+
"type": "string[] | \"*\"",
1055+
"description": "",
1056+
"optional": true
1057+
},
1058+
{
1059+
"name": "metadata",
1060+
"type": "Metadata",
1061+
"description": "",
1062+
"optional": true
1063+
},
9561064
{
9571065
"name": "multitaskStrategy",
9581066
"type": "LangGraphMultitaskStrategy",
9591067
"description": "Strategy for handling concurrent runs on the same thread.",
9601068
"optional": true
9611069
},
1070+
{
1071+
"name": "onCompletion",
1072+
"type": "LangGraphOnCompletion",
1073+
"description": "",
1074+
"optional": true
1075+
},
1076+
{
1077+
"name": "onDisconnect",
1078+
"type": "LangGraphOnDisconnect",
1079+
"description": "",
1080+
"optional": true
1081+
},
1082+
{
1083+
"name": "onRunCreated",
1084+
"type": "object",
1085+
"description": "",
1086+
"optional": true
1087+
},
1088+
{
1089+
"name": "resume",
1090+
"type": "unknown",
1091+
"description": "Convenience alias normalized to `command.resume` before invoking LangGraph.",
1092+
"optional": true
1093+
},
9621094
{
9631095
"name": "signal",
9641096
"type": "AbortSignal",
9651097
"description": "",
9661098
"optional": true
1099+
},
1100+
{
1101+
"name": "streamMode",
1102+
"type": "StreamMode[]",
1103+
"description": "",
1104+
"optional": true
1105+
},
1106+
{
1107+
"name": "streamResumable",
1108+
"type": "boolean",
1109+
"description": "",
1110+
"optional": true
1111+
},
1112+
{
1113+
"name": "streamSubgraphs",
1114+
"type": "boolean",
1115+
"description": "",
1116+
"optional": true
1117+
},
1118+
{
1119+
"name": "webhook",
1120+
"type": "string",
1121+
"description": "",
1122+
"optional": true
9671123
}
9681124
],
9691125
"examples": []
@@ -1150,7 +1306,7 @@
11501306
{
11511307
"name": "submit",
11521308
"type": "object",
1153-
"description": "",
1309+
"description": "Submit input, resume commands, checkpoint forks, or other LangGraph run options.",
11541310
"optional": false
11551311
},
11561312
{
@@ -1205,7 +1361,7 @@
12051361
},
12061362
{
12071363
"name": "type",
1208-
"type": "\"error\" | \"interrupt\" | \"values\" | `values|${string}` | \"messages\" | `messages|${string}` | `messages/${string}` | `messages/${string}|${string}` | \"updates\" | `updates|${string}` | \"tools\" | `tools|${string}` | \"custom\" | `custom|${string}` | `error|${string}` | \"metadata\" | \"checkpoints\" | `checkpoints|${string}` | \"tasks\" | `tasks|${string}` | \"debug\" | `debug|${string}` | \"events\" | `events|${string}` | \"interrupts\"",
1364+
"type": "\"error\" | \"values\" | \"messages\" | \"updates\" | \"events\" | \"debug\" | \"tasks\" | \"checkpoints\" | \"custom\" | \"tools\" | \"interrupt\" | `values|${string}` | `messages|${string}` | `messages/${string}` | `messages/${string}|${string}` | `updates|${string}` | `tools|${string}` | `custom|${string}` | `error|${string}` | \"metadata\" | `checkpoints|${string}` | `tasks|${string}` | `debug|${string}` | `events|${string}` | \"interrupts\"",
12091365
"description": "Event type identifier (e.g., 'values', 'messages', 'error', 'interrupt').",
12101366
"optional": false
12111367
}
@@ -1231,7 +1387,7 @@
12311387
},
12321388
{
12331389
"name": "status",
1234-
"type": "Signal<\"running\" | \"error\" | \"pending\" | \"complete\">",
1390+
"type": "Signal<\"running\" | \"error\" | \"complete\" | \"pending\">",
12351391
"description": "Current execution status of the subagent.",
12361392
"optional": false
12371393
},

apps/website/content/docs/agent/concepts/agent-architecture.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -593,12 +593,12 @@ export class DebugTimelineComponent {
593593

594594
selectedState = computed(() => {
595595
const id = this.currentCheckpoint();
596-
return this.history().find(c => c.id === id)?.state;
596+
return this.history().find(c => c.id === id)?.values;
597597
});
598598

599599
timeTravel(checkpointId: string) {
600600
this.currentCheckpoint.set(checkpointId);
601-
this.agent.submit(null, { checkpoint: checkpointId });
601+
this.agent.submit(null, { checkpointId });
602602
}
603603
}
604604
```

libs/langgraph/src/lib/agent.fn.spec.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,36 @@ describe('agent', () => {
209209
expect(Array.isArray(rawHist)).toBe(true);
210210
});
211211

212+
it('normalizes resume submit options into a LangGraph command', async () => {
213+
const seen: Array<{ payload: unknown; options: unknown }> = [];
214+
const transport = new MockAgentTransport();
215+
transport.stream = async function* (
216+
_assistantId: string,
217+
_threadId: string | null,
218+
payload: unknown,
219+
_signal: AbortSignal,
220+
options?: unknown,
221+
) {
222+
seen.push({ payload, options });
223+
yield* [];
224+
};
225+
226+
const ref = withInjectionContext(() =>
227+
agent({ apiUrl: '', assistantId: 'a', transport, threadId: 'thread-1', throttle: false })
228+
);
229+
230+
await ref.submit(null, { resume: { approved: true } });
231+
232+
expect(seen).toEqual([
233+
{
234+
payload: null,
235+
options: expect.objectContaining({
236+
command: { resume: { approved: true } },
237+
}),
238+
},
239+
]);
240+
});
241+
212242
it('experimentalBranchTree() exposes a branch tree derived from LangGraph history', async () => {
213243
const root = threadState('root');
214244
const left = threadState('left', 'root');

libs/langgraph/src/lib/agent.fn.ts

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,9 @@ export function agent<
227227
subagents: subagentsNeutral,
228228
events$,
229229
history: historyNeutral,
230-
submit: (input: AgentSubmitInput, opts?: AgentSubmitOptions & LangGraphSubmitOptions) => {
231-
manager.submit(buildSubmitPayload(input), opts);
230+
submit: (input: AgentSubmitInput | null | undefined, opts?: AgentSubmitOptions & LangGraphSubmitOptions) => {
231+
const request = buildSubmitRequest(input, opts);
232+
manager.submit(request.payload, request.options);
232233
return Promise.resolve();
233234
},
234235
stop: () => manager.stop(),
@@ -389,8 +390,19 @@ function getToolCallIds(msg: CoreAIMessage): string[] {
389390
.filter((id): id is string => id != null);
390391
}
391392

392-
function buildSubmitPayload(input: AgentSubmitInput): unknown {
393-
if (input.resume !== undefined) return { __resume__: input.resume };
393+
function buildSubmitRequest(
394+
input: AgentSubmitInput | null | undefined,
395+
opts?: AgentSubmitOptions & LangGraphSubmitOptions,
396+
): { payload: unknown; options?: AgentSubmitOptions & LangGraphSubmitOptions } {
397+
return {
398+
payload: buildSubmitPayload(input),
399+
options: normalizeSubmitOptions(input, opts),
400+
};
401+
}
402+
403+
function buildSubmitPayload(input: AgentSubmitInput | null | undefined): unknown {
404+
if (input == null) return null;
405+
if (input.resume !== undefined) return null;
394406
if (input.message !== undefined) {
395407
const content = typeof input.message === 'string'
396408
? input.message
@@ -400,6 +412,27 @@ function buildSubmitPayload(input: AgentSubmitInput): unknown {
400412
return input.state ?? {};
401413
}
402414

415+
function normalizeSubmitOptions(
416+
input: AgentSubmitInput | null | undefined,
417+
opts?: AgentSubmitOptions & LangGraphSubmitOptions,
418+
): (AgentSubmitOptions & LangGraphSubmitOptions) | undefined {
419+
const inputResume = input?.resume;
420+
const optionResume = opts?.resume;
421+
const resume = inputResume !== undefined ? inputResume : optionResume;
422+
if (resume === undefined) return opts;
423+
424+
const next = { ...(opts ?? {}) };
425+
delete next.resume;
426+
const command = next.command;
427+
return {
428+
...next,
429+
command: {
430+
...command,
431+
resume,
432+
},
433+
};
434+
}
435+
403436
function randomId(): string {
404437
return Math.random().toString(36).slice(2);
405438
}

0 commit comments

Comments
 (0)