Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 164 additions & 8 deletions apps/website/content/docs/agent/api/api-docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
},
{
"name": "createQueuedRun",
"signature": "createQueuedRun(assistantId: string, threadId: string, payload: unknown, signal: AbortSignal)",
"signature": "createQueuedRun(assistantId: string, threadId: string, payload: unknown, signal: AbortSignal, options: LangGraphSubmitOptions)",
"description": "Create a pending server-side run using LangGraph's enqueue strategy.",
"params": [
{
Expand All @@ -75,6 +75,12 @@
"type": "AbortSignal",
"description": "",
"optional": false
},
{
"name": "options",
"type": "LangGraphSubmitOptions",
"description": "",
"optional": true
}
]
},
Expand Down Expand Up @@ -130,7 +136,7 @@
},
{
"name": "stream",
"signature": "stream(assistantId: string, threadId: string | null, payload: unknown, signal: AbortSignal)",
"signature": "stream(assistantId: string, threadId: string | null, payload: unknown, signal: AbortSignal, options: LangGraphSubmitOptions)",
"description": "Open a streaming connection, creating a thread if needed.",
"params": [
{
Expand All @@ -156,6 +162,12 @@
"type": "AbortSignal",
"description": "",
"optional": false
},
{
"name": "options",
"type": "LangGraphSubmitOptions",
"description": "",
"optional": true
}
]
}
Expand Down Expand Up @@ -206,6 +218,12 @@
"type": "object[]",
"description": "",
"optional": false
},
{
"name": "streams",
"type": "object[]",
"description": "",
"optional": false
}
],
"methods": [
Expand Down Expand Up @@ -242,7 +260,7 @@
},
{
"name": "createQueuedRun",
"signature": "createQueuedRun(_assistantId: string, threadId: string, payload: unknown, signal: AbortSignal)",
"signature": "createQueuedRun(_assistantId: string, threadId: string, payload: unknown, signal: AbortSignal, options: LangGraphSubmitOptions)",
"description": "Optional: create a server-side queued run without joining it immediately.",
"params": [
{
Expand All @@ -268,6 +286,12 @@
"type": "AbortSignal",
"description": "",
"optional": false
},
{
"name": "options",
"type": "LangGraphSubmitOptions",
"description": "",
"optional": true
}
]
},
Expand Down Expand Up @@ -361,7 +385,7 @@
},
{
"name": "stream",
"signature": "stream(_assistantId: string, _threadId: string | null, _payload: unknown, signal: AbortSignal)",
"signature": "stream(_assistantId: string, _threadId: string | null, _payload: unknown, signal: AbortSignal, options: LangGraphSubmitOptions)",
"description": "Open a streaming connection to an agent and yield events.",
"params": [
{
Expand All @@ -387,6 +411,12 @@
"type": "AbortSignal",
"description": "",
"optional": false
},
{
"name": "options",
"type": "LangGraphSubmitOptions",
"description": "",
"optional": true
}
]
}
Expand Down Expand Up @@ -918,7 +948,7 @@
{
"name": "submit",
"type": "object",
"description": "",
"description": "Submit input, resume commands, checkpoint forks, or other LangGraph run options.",
"optional": false
},
{
Expand Down Expand Up @@ -953,17 +983,143 @@
"kind": "interface",
"description": "Options accepted by LangGraph-backed submit calls.",
"properties": [
{
"name": "afterSeconds",
"type": "number",
"description": "",
"optional": true
},
{
"name": "checkpoint",
"type": "Omit<Checkpoint, \"thread_id\"> | null",
"description": "",
"optional": true
},
{
"name": "checkpointDuring",
"type": "boolean",
"description": "",
"optional": true
},
{
"name": "checkpointId",
"type": "string",
"description": "",
"optional": true
},
{
"name": "command",
"type": "Command",
"description": "",
"optional": true
},
{
"name": "config",
"type": "Config",
"description": "",
"optional": true
},
{
"name": "context",
"type": "unknown",
"description": "",
"optional": true
},
{
"name": "durability",
"type": "LangGraphDurability",
"description": "",
"optional": true
},
{
"name": "feedbackKeys",
"type": "string[]",
"description": "",
"optional": true
},
{
"name": "ifNotExists",
"type": "\"create\" | \"reject\"",
"description": "",
"optional": true
},
{
"name": "interruptAfter",
"type": "string[] | \"*\"",
"description": "",
"optional": true
},
{
"name": "interruptBefore",
"type": "string[] | \"*\"",
"description": "",
"optional": true
},
{
"name": "metadata",
"type": "Metadata",
"description": "",
"optional": true
},
{
"name": "multitaskStrategy",
"type": "LangGraphMultitaskStrategy",
"description": "Strategy for handling concurrent runs on the same thread.",
"optional": true
},
{
"name": "onCompletion",
"type": "LangGraphOnCompletion",
"description": "",
"optional": true
},
{
"name": "onDisconnect",
"type": "LangGraphOnDisconnect",
"description": "",
"optional": true
},
{
"name": "onRunCreated",
"type": "object",
"description": "",
"optional": true
},
{
"name": "resume",
"type": "unknown",
"description": "Convenience alias normalized to `command.resume` before invoking LangGraph.",
"optional": true
},
{
"name": "signal",
"type": "AbortSignal",
"description": "",
"optional": true
},
{
"name": "streamMode",
"type": "StreamMode[]",
"description": "",
"optional": true
},
{
"name": "streamResumable",
"type": "boolean",
"description": "",
"optional": true
},
{
"name": "streamSubgraphs",
"type": "boolean",
"description": "",
"optional": true
},
{
"name": "webhook",
"type": "string",
"description": "",
"optional": true
}
],
"examples": []
Expand Down Expand Up @@ -1150,7 +1306,7 @@
{
"name": "submit",
"type": "object",
"description": "",
"description": "Submit input, resume commands, checkpoint forks, or other LangGraph run options.",
"optional": false
},
{
Expand Down Expand Up @@ -1205,7 +1361,7 @@
},
{
"name": "type",
"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\"",
"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\"",
"description": "Event type identifier (e.g., 'values', 'messages', 'error', 'interrupt').",
"optional": false
}
Expand All @@ -1231,7 +1387,7 @@
},
{
"name": "status",
"type": "Signal<\"running\" | \"error\" | \"pending\" | \"complete\">",
"type": "Signal<\"running\" | \"error\" | \"complete\" | \"pending\">",
"description": "Current execution status of the subagent.",
"optional": false
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -593,12 +593,12 @@ export class DebugTimelineComponent {

selectedState = computed(() => {
const id = this.currentCheckpoint();
return this.history().find(c => c.id === id)?.state;
return this.history().find(c => c.id === id)?.values;
});

timeTravel(checkpointId: string) {
this.currentCheckpoint.set(checkpointId);
this.agent.submit(null, { checkpoint: checkpointId });
this.agent.submit(null, { checkpointId });
}
}
```
Expand Down
30 changes: 30 additions & 0 deletions libs/langgraph/src/lib/agent.fn.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,36 @@ describe('agent', () => {
expect(Array.isArray(rawHist)).toBe(true);
});

it('normalizes resume submit options into a LangGraph command', async () => {
const seen: Array<{ payload: unknown; options: unknown }> = [];
const transport = new MockAgentTransport();
transport.stream = async function* (
_assistantId: string,
_threadId: string | null,
payload: unknown,
_signal: AbortSignal,
options?: unknown,
) {
seen.push({ payload, options });
yield* [];
};

const ref = withInjectionContext(() =>
agent({ apiUrl: '', assistantId: 'a', transport, threadId: 'thread-1', throttle: false })
);

await ref.submit(null, { resume: { approved: true } });

expect(seen).toEqual([
{
payload: null,
options: expect.objectContaining({
command: { resume: { approved: true } },
}),
},
]);
});

it('experimentalBranchTree() exposes a branch tree derived from LangGraph history', async () => {
const root = threadState('root');
const left = threadState('left', 'root');
Expand Down
41 changes: 37 additions & 4 deletions libs/langgraph/src/lib/agent.fn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,9 @@ export function agent<
subagents: subagentsNeutral,
events$,
history: historyNeutral,
submit: (input: AgentSubmitInput, opts?: AgentSubmitOptions & LangGraphSubmitOptions) => {
manager.submit(buildSubmitPayload(input), opts);
submit: (input: AgentSubmitInput | null | undefined, opts?: AgentSubmitOptions & LangGraphSubmitOptions) => {
const request = buildSubmitRequest(input, opts);
manager.submit(request.payload, request.options);
return Promise.resolve();
},
stop: () => manager.stop(),
Expand Down Expand Up @@ -389,8 +390,19 @@ function getToolCallIds(msg: CoreAIMessage): string[] {
.filter((id): id is string => id != null);
}

function buildSubmitPayload(input: AgentSubmitInput): unknown {
if (input.resume !== undefined) return { __resume__: input.resume };
function buildSubmitRequest(
input: AgentSubmitInput | null | undefined,
opts?: AgentSubmitOptions & LangGraphSubmitOptions,
): { payload: unknown; options?: AgentSubmitOptions & LangGraphSubmitOptions } {
return {
payload: buildSubmitPayload(input),
options: normalizeSubmitOptions(input, opts),
};
}

function buildSubmitPayload(input: AgentSubmitInput | null | undefined): unknown {
if (input == null) return null;
if (input.resume !== undefined) return null;
if (input.message !== undefined) {
const content = typeof input.message === 'string'
? input.message
Expand All @@ -400,6 +412,27 @@ function buildSubmitPayload(input: AgentSubmitInput): unknown {
return input.state ?? {};
}

function normalizeSubmitOptions(
input: AgentSubmitInput | null | undefined,
opts?: AgentSubmitOptions & LangGraphSubmitOptions,
): (AgentSubmitOptions & LangGraphSubmitOptions) | undefined {
const inputResume = input?.resume;
const optionResume = opts?.resume;
const resume = inputResume !== undefined ? inputResume : optionResume;
if (resume === undefined) return opts;

const next = { ...(opts ?? {}) };
delete next.resume;
const command = next.command;
return {
...next,
command: {
...command,
resume,
},
};
}

function randomId(): string {
return Math.random().toString(36).slice(2);
}
Expand Down
Loading
Loading