@langchain/langgraph-api 1.1.8 → 1.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/assistants.d.mts +3 -0
- package/dist/api/assistants.mjs +193 -0
- package/dist/api/meta.d.mts +3 -0
- package/dist/api/meta.mjs +65 -0
- package/dist/api/runs.d.mts +3 -0
- package/dist/api/runs.mjs +324 -0
- package/dist/api/store.d.mts +3 -0
- package/dist/api/store.mjs +111 -0
- package/dist/api/threads.d.mts +3 -0
- package/dist/api/threads.mjs +143 -0
- package/dist/auth/custom.d.mts +9 -0
- package/dist/auth/custom.mjs +32 -0
- package/dist/auth/index.d.mts +43 -0
- package/dist/auth/index.mjs +163 -0
- package/dist/cli/entrypoint.d.mts +1 -0
- package/dist/cli/entrypoint.mjs +41 -0
- package/dist/cli/spawn.d.mts +42 -0
- package/dist/cli/spawn.mjs +47 -0
- package/dist/cli/utils/ipc/client.d.mts +5 -0
- package/dist/cli/utils/ipc/client.mjs +47 -0
- package/dist/cli/utils/ipc/utils/get-pipe-path.d.mts +1 -0
- package/dist/cli/utils/ipc/utils/get-pipe-path.mjs +29 -0
- package/dist/cli/utils/ipc/utils/temporary-directory.d.mts +5 -0
- package/dist/cli/utils/ipc/utils/temporary-directory.mjs +40 -0
- package/dist/command.d.mts +11 -0
- package/dist/command.mjs +15 -0
- package/dist/experimental/embed.d.mts +42 -0
- package/dist/experimental/embed.mjs +299 -0
- package/dist/graph/api.d.mts +1 -0
- package/dist/graph/api.mjs +2 -0
- package/dist/graph/load.d.mts +19 -0
- package/dist/graph/load.hooks.d.mts +2 -0
- package/dist/graph/load.hooks.mjs +52 -0
- package/dist/graph/load.mjs +96 -0
- package/dist/graph/load.utils.d.mts +22 -0
- package/dist/graph/load.utils.mjs +49 -0
- package/dist/graph/parser/index.d.mts +23 -0
- package/dist/graph/parser/index.mjs +58 -0
- package/dist/graph/parser/parser.d.mts +77 -0
- package/dist/graph/parser/parser.mjs +429 -0
- package/dist/graph/parser/parser.worker.d.mts +1 -0
- package/dist/graph/parser/parser.worker.mjs +7 -0
- package/dist/graph/parser/schema/types.d.mts +154 -0
- package/dist/graph/parser/schema/types.mjs +1496 -0
- package/dist/graph/parser/schema/types.template.d.mts +1 -0
- package/dist/graph/parser/schema/types.template.mts +92 -0
- package/dist/http/custom.d.mts +6 -0
- package/dist/http/custom.mjs +10 -0
- package/dist/http/middleware.d.mts +11 -0
- package/dist/http/middleware.mjs +57 -0
- package/dist/logging.d.mts +10 -0
- package/dist/logging.mjs +115 -0
- package/dist/loopback.d.mts +4 -0
- package/dist/loopback.mjs +10 -0
- package/dist/preload.d.mts +1 -0
- package/dist/preload.mjs +29 -0
- package/dist/queue.d.mts +2 -0
- package/dist/queue.mjs +119 -0
- package/dist/schemas.d.mts +1552 -0
- package/dist/schemas.mjs +492 -0
- package/dist/semver/index.d.mts +15 -0
- package/dist/semver/index.mjs +46 -0
- package/dist/server.d.mts +175 -0
- package/dist/server.mjs +181 -0
- package/dist/state.d.mts +3 -0
- package/dist/state.mjs +30 -0
- package/dist/storage/checkpoint.d.mts +19 -0
- package/dist/storage/checkpoint.mjs +127 -0
- package/dist/storage/context.d.mts +3 -0
- package/dist/storage/context.mjs +11 -0
- package/dist/storage/importMap.d.mts +55 -0
- package/dist/storage/importMap.mjs +55 -0
- package/dist/storage/ops.d.mts +169 -0
- package/dist/storage/ops.mjs +1262 -0
- package/dist/storage/persist.d.mts +18 -0
- package/dist/storage/persist.mjs +81 -0
- package/dist/storage/store.d.mts +17 -0
- package/dist/storage/store.mjs +41 -0
- package/dist/storage/types.d.mts +301 -0
- package/dist/storage/types.mjs +1 -0
- package/dist/stream.d.mts +43 -0
- package/dist/stream.mjs +235 -0
- package/dist/ui/load.d.mts +8 -0
- package/dist/ui/load.mjs +53 -0
- package/dist/utils/abort.d.mts +1 -0
- package/dist/utils/abort.mjs +8 -0
- package/dist/utils/hono.d.mts +5 -0
- package/dist/utils/hono.mjs +24 -0
- package/dist/utils/importMap.d.mts +55 -0
- package/dist/utils/importMap.mjs +55 -0
- package/dist/utils/runnableConfig.d.mts +3 -0
- package/dist/utils/runnableConfig.mjs +45 -0
- package/dist/utils/serde.d.mts +5 -0
- package/dist/utils/serde.mjs +20 -0
- package/dist/vitest.config.d.ts +2 -0
- package/dist/vitest.config.js +12 -0
- package/dist/webhook.d.mts +11 -0
- package/dist/webhook.mjs +30 -0
- package/package.json +1 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// MIT License
|
|
2
|
+
//
|
|
3
|
+
// Copyright (c) Hiroki Osame <hiroki.osame@gmail.com>
|
|
4
|
+
//
|
|
5
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
// in the Software without restriction, including without limitation the rights
|
|
8
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
// furnished to do so, subject to the following conditions:
|
|
11
|
+
//
|
|
12
|
+
// The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
// copies or substantial portions of the Software.
|
|
14
|
+
//
|
|
15
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
// SOFTWARE.
|
|
22
|
+
//
|
|
23
|
+
// https://github.com/privatenumber/tsx/tree/28a3e7d2b8fd72b683aab8a98dd1fcee4624e4cb
|
|
24
|
+
import net from "node:net";
|
|
25
|
+
import { getPipePath } from "./utils/get-pipe-path.mjs";
|
|
26
|
+
export const connectToServer = (processId = process.ppid) => new Promise((resolve) => {
|
|
27
|
+
const pipePath = getPipePath(processId);
|
|
28
|
+
const socket = net.createConnection(pipePath, () => {
|
|
29
|
+
const sendToParent = (data) => {
|
|
30
|
+
const messageBuffer = Buffer.from(JSON.stringify(data));
|
|
31
|
+
const lengthBuffer = Buffer.alloc(4);
|
|
32
|
+
lengthBuffer.writeInt32BE(messageBuffer.length, 0);
|
|
33
|
+
socket.write(Buffer.concat([lengthBuffer, messageBuffer]));
|
|
34
|
+
};
|
|
35
|
+
resolve(sendToParent);
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* Ignore error when:
|
|
39
|
+
* - Called as a loader and there is no server
|
|
40
|
+
* - Nested process when using --test and the ppid is incorrect
|
|
41
|
+
*/
|
|
42
|
+
socket.on("error", () => {
|
|
43
|
+
resolve(undefined);
|
|
44
|
+
});
|
|
45
|
+
// Prevent Node from waiting for this socket to close before exiting
|
|
46
|
+
socket.unref();
|
|
47
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const getPipePath: (processId: number) => string;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// MIT License
|
|
2
|
+
//
|
|
3
|
+
// Copyright (c) Hiroki Osame <hiroki.osame@gmail.com>
|
|
4
|
+
//
|
|
5
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
// in the Software without restriction, including without limitation the rights
|
|
8
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
// furnished to do so, subject to the following conditions:
|
|
11
|
+
//
|
|
12
|
+
// The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
// copies or substantial portions of the Software.
|
|
14
|
+
//
|
|
15
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
// SOFTWARE.
|
|
22
|
+
//
|
|
23
|
+
// https://github.com/privatenumber/tsx/tree/28a3e7d2b8fd72b683aab8a98dd1fcee4624e4cb
|
|
24
|
+
import path from "node:path";
|
|
25
|
+
import { tmpdir } from "./temporary-directory.mjs";
|
|
26
|
+
export const getPipePath = (processId) => {
|
|
27
|
+
const pipePath = path.join(tmpdir, `${processId}.pipe`);
|
|
28
|
+
return process.platform === "win32" ? `\\\\?\\pipe\\${pipePath}` : pipePath;
|
|
29
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// MIT License
|
|
2
|
+
//
|
|
3
|
+
// Copyright (c) Hiroki Osame <hiroki.osame@gmail.com>
|
|
4
|
+
//
|
|
5
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
// in the Software without restriction, including without limitation the rights
|
|
8
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
// furnished to do so, subject to the following conditions:
|
|
11
|
+
//
|
|
12
|
+
// The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
// copies or substantial portions of the Software.
|
|
14
|
+
//
|
|
15
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
// SOFTWARE.
|
|
22
|
+
//
|
|
23
|
+
// https://github.com/privatenumber/tsx/tree/28a3e7d2b8fd72b683aab8a98dd1fcee4624e4cb
|
|
24
|
+
import path from "node:path";
|
|
25
|
+
import os from "node:os";
|
|
26
|
+
/**
|
|
27
|
+
* Cache directory is based on the user's identifier
|
|
28
|
+
* to avoid permission issues when accessed by a different user
|
|
29
|
+
*/
|
|
30
|
+
const { geteuid } = process;
|
|
31
|
+
const userId = geteuid
|
|
32
|
+
? // For Linux users with virtual users on CI (e.g. Docker)
|
|
33
|
+
geteuid()
|
|
34
|
+
: // Use username on Windows because it doesn't have id
|
|
35
|
+
os.userInfo().username;
|
|
36
|
+
/**
|
|
37
|
+
* This ensures that the cache directory is unique per user
|
|
38
|
+
* and has the appropriate permissions
|
|
39
|
+
*/
|
|
40
|
+
export const tmpdir = path.join(os.tmpdir(), `tsx-${userId}`);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Command } from "@langchain/langgraph";
|
|
2
|
+
export interface RunSend {
|
|
3
|
+
node: string;
|
|
4
|
+
input?: unknown;
|
|
5
|
+
}
|
|
6
|
+
export interface RunCommand {
|
|
7
|
+
goto?: string | RunSend | Array<RunSend | string>;
|
|
8
|
+
update?: Record<string, unknown> | [string, unknown][];
|
|
9
|
+
resume?: unknown;
|
|
10
|
+
}
|
|
11
|
+
export declare const getLangGraphCommand: (command: RunCommand) => Command<unknown, Record<string, unknown>, string>;
|
package/dist/command.mjs
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Command, Send } from "@langchain/langgraph";
|
|
2
|
+
export const getLangGraphCommand = (command) => {
|
|
3
|
+
let goto = command.goto != null && !Array.isArray(command.goto)
|
|
4
|
+
? [command.goto]
|
|
5
|
+
: command.goto;
|
|
6
|
+
return new Command({
|
|
7
|
+
goto: goto?.map((item) => {
|
|
8
|
+
if (typeof item !== "string")
|
|
9
|
+
return new Send(item.node, item.input);
|
|
10
|
+
return item;
|
|
11
|
+
}),
|
|
12
|
+
update: command.update,
|
|
13
|
+
resume: command.resume,
|
|
14
|
+
});
|
|
15
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { BaseCheckpointSaver, BaseStore, Pregel } from "@langchain/langgraph";
|
|
2
|
+
import { Hono } from "hono";
|
|
3
|
+
import type { Metadata } from "../storage/types.mjs";
|
|
4
|
+
type AnyPregel = Pregel<any, any, any, any, any>;
|
|
5
|
+
interface Thread {
|
|
6
|
+
thread_id: string;
|
|
7
|
+
metadata: Metadata;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Interface for storing and retrieving threads used by `createEmbedServer`.
|
|
11
|
+
* @experimental Does not follow semver.
|
|
12
|
+
*/
|
|
13
|
+
export interface ThreadSaver {
|
|
14
|
+
get: (id: string) => Promise<Thread>;
|
|
15
|
+
set: (id: string, options: {
|
|
16
|
+
kind: "put" | "patch";
|
|
17
|
+
metadata?: Metadata;
|
|
18
|
+
}) => Promise<Thread>;
|
|
19
|
+
delete: (id: string) => Promise<void>;
|
|
20
|
+
search?: (options: {
|
|
21
|
+
metadata?: Metadata;
|
|
22
|
+
limit: number;
|
|
23
|
+
offset: number;
|
|
24
|
+
sortBy: "created_at" | "updated_at";
|
|
25
|
+
sortOrder: "asc" | "desc";
|
|
26
|
+
}) => AsyncGenerator<{
|
|
27
|
+
thread: Thread;
|
|
28
|
+
total: number;
|
|
29
|
+
}>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Create a Hono server with a subset of LangGraph Platform routes.
|
|
33
|
+
*
|
|
34
|
+
* @experimental Does not follow semver.
|
|
35
|
+
*/
|
|
36
|
+
export declare function createEmbedServer(options: {
|
|
37
|
+
graph: Record<string, AnyPregel>;
|
|
38
|
+
threads: ThreadSaver;
|
|
39
|
+
checkpointer: BaseCheckpointSaver;
|
|
40
|
+
store?: BaseStore;
|
|
41
|
+
}): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
42
|
+
export {};
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import { zValidator } from "@hono/zod-validator";
|
|
3
|
+
import { streamSSE } from "hono/streaming";
|
|
4
|
+
import { v4 as uuidv4 } from "uuid";
|
|
5
|
+
import * as schemas from "../schemas.mjs";
|
|
6
|
+
import { z } from "zod/v3";
|
|
7
|
+
import { streamState } from "../stream.mjs";
|
|
8
|
+
import { serialiseAsDict, serializeError } from "../utils/serde.mjs";
|
|
9
|
+
import { getDisconnectAbortSignal, jsonExtra } from "../utils/hono.mjs";
|
|
10
|
+
import { stateSnapshotToThreadState } from "../state.mjs";
|
|
11
|
+
import { ensureContentType } from "../http/middleware.mjs";
|
|
12
|
+
function createStubRun(threadId, payload) {
|
|
13
|
+
const now = new Date();
|
|
14
|
+
const runId = uuidv4();
|
|
15
|
+
let streamMode = Array.isArray(payload.stream_mode)
|
|
16
|
+
? payload.stream_mode
|
|
17
|
+
: payload.stream_mode
|
|
18
|
+
? [payload.stream_mode]
|
|
19
|
+
: undefined;
|
|
20
|
+
if (streamMode == null || streamMode.length === 0)
|
|
21
|
+
streamMode = ["values"];
|
|
22
|
+
const config = Object.assign({}, payload.config ?? {}, {
|
|
23
|
+
configurable: {
|
|
24
|
+
run_id: runId,
|
|
25
|
+
thread_id: threadId,
|
|
26
|
+
graph_id: payload.assistant_id,
|
|
27
|
+
...(payload.checkpoint_id
|
|
28
|
+
? { checkpoint_id: payload.checkpoint_id }
|
|
29
|
+
: null),
|
|
30
|
+
...payload.checkpoint,
|
|
31
|
+
...(payload.langsmith_tracer
|
|
32
|
+
? {
|
|
33
|
+
langsmith_project: payload.langsmith_tracer.project_name,
|
|
34
|
+
langsmith_example_id: payload.langsmith_tracer.example_id,
|
|
35
|
+
}
|
|
36
|
+
: null),
|
|
37
|
+
},
|
|
38
|
+
}, { metadata: payload.metadata ?? {} });
|
|
39
|
+
return {
|
|
40
|
+
run_id: runId,
|
|
41
|
+
thread_id: threadId,
|
|
42
|
+
assistant_id: payload.assistant_id,
|
|
43
|
+
metadata: payload.metadata ?? {},
|
|
44
|
+
status: "running",
|
|
45
|
+
kwargs: {
|
|
46
|
+
input: payload.input,
|
|
47
|
+
command: payload.command,
|
|
48
|
+
config,
|
|
49
|
+
context: payload.context,
|
|
50
|
+
stream_mode: streamMode,
|
|
51
|
+
interrupt_before: payload.interrupt_before,
|
|
52
|
+
interrupt_after: payload.interrupt_after,
|
|
53
|
+
feedback_keys: payload.feedback_keys,
|
|
54
|
+
subgraphs: payload.stream_subgraphs,
|
|
55
|
+
temporary: false,
|
|
56
|
+
},
|
|
57
|
+
multitask_strategy: "reject",
|
|
58
|
+
created_at: now,
|
|
59
|
+
updated_at: now,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Create a Hono server with a subset of LangGraph Platform routes.
|
|
64
|
+
*
|
|
65
|
+
* @experimental Does not follow semver.
|
|
66
|
+
*/
|
|
67
|
+
export function createEmbedServer(options) {
|
|
68
|
+
async function getGraph(graphId) {
|
|
69
|
+
const targetGraph = options.graph[graphId];
|
|
70
|
+
targetGraph.store = options.store;
|
|
71
|
+
targetGraph.checkpointer = options.checkpointer;
|
|
72
|
+
return targetGraph;
|
|
73
|
+
}
|
|
74
|
+
const api = new Hono();
|
|
75
|
+
api.use(ensureContentType());
|
|
76
|
+
api.post("/threads", zValidator("json", schemas.ThreadCreate), async (c) => {
|
|
77
|
+
// create a new thread
|
|
78
|
+
const payload = c.req.valid("json");
|
|
79
|
+
const threadId = payload.thread_id || uuidv4();
|
|
80
|
+
return jsonExtra(c, await options.threads.set(threadId, {
|
|
81
|
+
kind: "put",
|
|
82
|
+
metadata: payload.metadata,
|
|
83
|
+
}));
|
|
84
|
+
});
|
|
85
|
+
api.get("/threads/:thread_id", zValidator("param", z.object({ thread_id: z.string().uuid() })), async (c) => {
|
|
86
|
+
// Get Thread
|
|
87
|
+
const { thread_id } = c.req.valid("param");
|
|
88
|
+
return jsonExtra(c, await options.threads.get(thread_id));
|
|
89
|
+
});
|
|
90
|
+
api.patch("/threads/:thread_id", zValidator("param", z.object({ thread_id: z.string().uuid() })), zValidator("json", schemas.ThreadCreate), async (c) => {
|
|
91
|
+
// Update Thread
|
|
92
|
+
const { thread_id } = c.req.valid("param");
|
|
93
|
+
const payload = c.req.valid("json");
|
|
94
|
+
return jsonExtra(c, await options.threads.set(thread_id, {
|
|
95
|
+
kind: "patch",
|
|
96
|
+
metadata: payload.metadata,
|
|
97
|
+
}));
|
|
98
|
+
});
|
|
99
|
+
api.delete("/threads/:thread_id", zValidator("param", z.object({ thread_id: z.string().uuid() })), async (c) => {
|
|
100
|
+
// Delete Thread
|
|
101
|
+
const { thread_id } = c.req.valid("param");
|
|
102
|
+
await options.threads.delete(thread_id);
|
|
103
|
+
return new Response(null, { status: 204 });
|
|
104
|
+
});
|
|
105
|
+
api.post("/threads/search", zValidator("json", schemas.ThreadSearchRequest), async (c) => {
|
|
106
|
+
const payload = c.req.valid("json");
|
|
107
|
+
const result = [];
|
|
108
|
+
if (!options.threads.search)
|
|
109
|
+
return c.json({ error: "Threads search not implemented" }, 422);
|
|
110
|
+
const sortBy = payload.sort_by === "created_at" || payload.sort_by === "updated_at"
|
|
111
|
+
? payload.sort_by
|
|
112
|
+
: "created_at";
|
|
113
|
+
let total = 0;
|
|
114
|
+
for await (const item of options.threads.search({
|
|
115
|
+
metadata: payload.metadata,
|
|
116
|
+
limit: payload.limit ?? 10,
|
|
117
|
+
offset: payload.offset ?? 0,
|
|
118
|
+
sortBy,
|
|
119
|
+
sortOrder: payload.sort_order ?? "desc",
|
|
120
|
+
})) {
|
|
121
|
+
result.push(item.thread);
|
|
122
|
+
// Only set total if it's the first item
|
|
123
|
+
if (total === 0)
|
|
124
|
+
total = item.total;
|
|
125
|
+
}
|
|
126
|
+
c.res.headers.set("X-Pagination-Total", total.toString());
|
|
127
|
+
return jsonExtra(c, result);
|
|
128
|
+
});
|
|
129
|
+
api.get("/threads/:thread_id/state", zValidator("param", z.object({ thread_id: z.string().uuid() })), zValidator("query", z.object({ subgraphs: schemas.coercedBoolean.optional() })), async (c) => {
|
|
130
|
+
// Get Latest Thread State
|
|
131
|
+
const { thread_id } = c.req.valid("param");
|
|
132
|
+
const { subgraphs } = c.req.valid("query");
|
|
133
|
+
const thread = await options.threads.get(thread_id);
|
|
134
|
+
const graphId = thread.metadata?.graph_id;
|
|
135
|
+
const graph = graphId ? await getGraph(graphId) : undefined;
|
|
136
|
+
if (graph == null) {
|
|
137
|
+
return jsonExtra(c, stateSnapshotToThreadState({
|
|
138
|
+
values: {},
|
|
139
|
+
next: [],
|
|
140
|
+
config: {},
|
|
141
|
+
metadata: undefined,
|
|
142
|
+
createdAt: undefined,
|
|
143
|
+
parentConfig: undefined,
|
|
144
|
+
tasks: [],
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
147
|
+
const config = { configurable: { thread_id } };
|
|
148
|
+
const result = await graph.getState(config, { subgraphs });
|
|
149
|
+
return jsonExtra(c, stateSnapshotToThreadState(result));
|
|
150
|
+
});
|
|
151
|
+
api.post("/threads/:thread_id/state", zValidator("param", z.object({ thread_id: z.string().uuid() })), zValidator("json", schemas.ThreadStateUpdate), async (c) => {
|
|
152
|
+
// Update Thread State
|
|
153
|
+
const { thread_id } = c.req.valid("param");
|
|
154
|
+
const payload = c.req.valid("json");
|
|
155
|
+
const config = { configurable: { thread_id } };
|
|
156
|
+
config.configurable ??= {};
|
|
157
|
+
if (payload.checkpoint_id) {
|
|
158
|
+
config.configurable.checkpoint_id = payload.checkpoint_id;
|
|
159
|
+
}
|
|
160
|
+
if (payload.checkpoint) {
|
|
161
|
+
Object.assign(config.configurable, payload.checkpoint);
|
|
162
|
+
}
|
|
163
|
+
const thread = await options.threads.get(thread_id);
|
|
164
|
+
const graphId = thread.metadata?.graph_id;
|
|
165
|
+
const graph = graphId ? await getGraph(graphId) : undefined;
|
|
166
|
+
if (graph == null)
|
|
167
|
+
return c.json({ error: "Graph not found" }, 404);
|
|
168
|
+
const result = await graph.updateState(config, payload.values, payload.as_node);
|
|
169
|
+
return jsonExtra(c, { checkpoint: result.configurable });
|
|
170
|
+
});
|
|
171
|
+
// get thread state at checkpoint
|
|
172
|
+
api.get("/threads/:thread_id/state/:checkpoint_id", zValidator("param", z.object({
|
|
173
|
+
thread_id: z.string().uuid(),
|
|
174
|
+
checkpoint_id: z.string().uuid(),
|
|
175
|
+
})), zValidator("query", z.object({ subgraphs: schemas.coercedBoolean.optional() })), async (c) => {
|
|
176
|
+
// Get Thread State At Checkpoint
|
|
177
|
+
const { thread_id, checkpoint_id } = c.req.valid("param");
|
|
178
|
+
const { subgraphs } = c.req.valid("query");
|
|
179
|
+
const thread = await options.threads.get(thread_id);
|
|
180
|
+
const graphId = thread.metadata?.graph_id;
|
|
181
|
+
const graph = graphId ? await getGraph(graphId) : undefined;
|
|
182
|
+
if (graph == null)
|
|
183
|
+
return c.json({ error: "Graph not found" }, 404);
|
|
184
|
+
const result = await graph.getState({ configurable: { thread_id, checkpoint_id } }, { subgraphs });
|
|
185
|
+
return jsonExtra(c, stateSnapshotToThreadState(result));
|
|
186
|
+
});
|
|
187
|
+
api.post("/threads/:thread_id/state/checkpoint", zValidator("param", z.object({ thread_id: z.string().uuid() })), zValidator("json", z.object({
|
|
188
|
+
subgraphs: schemas.coercedBoolean.optional(),
|
|
189
|
+
checkpoint: schemas.CheckpointSchema.nullish(),
|
|
190
|
+
})), async (c) => {
|
|
191
|
+
// Get Thread State At Checkpoint post
|
|
192
|
+
const { thread_id } = c.req.valid("param");
|
|
193
|
+
const { checkpoint, subgraphs } = c.req.valid("json");
|
|
194
|
+
const thread = await options.threads.get(thread_id);
|
|
195
|
+
const graphId = thread.metadata?.graph_id;
|
|
196
|
+
const graph = graphId ? await getGraph(graphId) : undefined;
|
|
197
|
+
if (graph == null)
|
|
198
|
+
return c.json({ error: "Graph not found" }, 404);
|
|
199
|
+
const result = await graph.getState({ configurable: { thread_id, ...checkpoint } }, { subgraphs });
|
|
200
|
+
return jsonExtra(c, stateSnapshotToThreadState(result));
|
|
201
|
+
});
|
|
202
|
+
api.post("/threads/:thread_id/history", zValidator("param", z.object({ thread_id: z.string().uuid() })), zValidator("json", schemas.ThreadHistoryRequest), async (c) => {
|
|
203
|
+
// Get Thread History Post
|
|
204
|
+
const { thread_id } = c.req.valid("param");
|
|
205
|
+
const { limit, before, metadata, checkpoint } = c.req.valid("json");
|
|
206
|
+
const thread = await options.threads.get(thread_id);
|
|
207
|
+
const graphId = thread.metadata?.graph_id;
|
|
208
|
+
const graph = graphId ? await getGraph(graphId) : undefined;
|
|
209
|
+
if (graph == null)
|
|
210
|
+
return jsonExtra(c, []);
|
|
211
|
+
const config = { configurable: { thread_id, ...checkpoint } };
|
|
212
|
+
const result = [];
|
|
213
|
+
const beforeConfig = typeof before === "string"
|
|
214
|
+
? { configurable: { checkpoint_id: before } }
|
|
215
|
+
: before;
|
|
216
|
+
for await (const state of graph.getStateHistory(config, {
|
|
217
|
+
limit,
|
|
218
|
+
before: beforeConfig,
|
|
219
|
+
filter: metadata,
|
|
220
|
+
})) {
|
|
221
|
+
result.push(stateSnapshotToThreadState(state));
|
|
222
|
+
}
|
|
223
|
+
return jsonExtra(c, result);
|
|
224
|
+
});
|
|
225
|
+
api.post("/threads/:thread_id/runs/stream", zValidator("param", z.object({ thread_id: z.string().uuid() })), zValidator("json", schemas.RunCreate), async (c) => {
|
|
226
|
+
// Stream Run
|
|
227
|
+
const { thread_id } = c.req.valid("param");
|
|
228
|
+
const payload = c.req.valid("json");
|
|
229
|
+
const thread = await options.threads.get(thread_id);
|
|
230
|
+
if (thread == null)
|
|
231
|
+
return c.json({ error: "Thread not found" }, 404);
|
|
232
|
+
return streamSSE(c, async (stream) => {
|
|
233
|
+
const signal = getDisconnectAbortSignal(c, stream);
|
|
234
|
+
const run = createStubRun(thread_id, payload);
|
|
235
|
+
await options.threads.set(thread_id, {
|
|
236
|
+
kind: "patch",
|
|
237
|
+
metadata: {
|
|
238
|
+
graph_id: payload.assistant_id,
|
|
239
|
+
assistant_id: payload.assistant_id,
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
try {
|
|
243
|
+
for await (const { event, data } of streamState(run, {
|
|
244
|
+
attempt: 1,
|
|
245
|
+
getGraph,
|
|
246
|
+
signal,
|
|
247
|
+
})) {
|
|
248
|
+
await stream.writeSSE({ data: serialiseAsDict(data), event });
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
await stream.writeSSE({
|
|
253
|
+
data: serialiseAsDict(serializeError(error)),
|
|
254
|
+
event: "error",
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
api.post("/runs/stream", zValidator("json", schemas.RunCreate), async (c) => {
|
|
260
|
+
// Stream Stateless Run
|
|
261
|
+
return streamSSE(c, async (stream) => {
|
|
262
|
+
const payload = c.req.valid("json");
|
|
263
|
+
const signal = getDisconnectAbortSignal(c, stream);
|
|
264
|
+
const threadId = uuidv4();
|
|
265
|
+
await options.threads.set(threadId, {
|
|
266
|
+
kind: "put",
|
|
267
|
+
metadata: {
|
|
268
|
+
graph_id: payload.assistant_id,
|
|
269
|
+
assistant_id: payload.assistant_id,
|
|
270
|
+
},
|
|
271
|
+
});
|
|
272
|
+
try {
|
|
273
|
+
const run = createStubRun(threadId, payload);
|
|
274
|
+
try {
|
|
275
|
+
for await (const { event, data } of streamState(run, {
|
|
276
|
+
attempt: 1,
|
|
277
|
+
getGraph,
|
|
278
|
+
signal,
|
|
279
|
+
})) {
|
|
280
|
+
await stream.writeSSE({ data: serialiseAsDict(data), event });
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
await stream.writeSSE({
|
|
285
|
+
data: serialiseAsDict(serializeError(error)),
|
|
286
|
+
event: "error",
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
finally {
|
|
291
|
+
await options.threads.delete(threadId);
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
api.notFound((c) => {
|
|
296
|
+
return c.json({ error: `${c.req.method} ${c.req.path} not implemented` }, 404);
|
|
297
|
+
});
|
|
298
|
+
return api;
|
|
299
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { assertGraphExists, getAssistantId, getGraph, getGraphKeys, } from "./load.mjs";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AssistantsRepo } from "../storage/types.mjs";
|
|
2
|
+
import type { BaseCheckpointSaver, BaseStore, CompiledGraph, LangGraphRunnableConfig } from "@langchain/langgraph";
|
|
3
|
+
import { type CompiledGraphFactory } from "./load.utils.mjs";
|
|
4
|
+
import type { GraphSchema, GraphSpec } from "./parser/index.mjs";
|
|
5
|
+
export declare const GRAPHS: Record<string, CompiledGraph<string> | CompiledGraphFactory<string>>;
|
|
6
|
+
export declare const GRAPH_SPEC: Record<string, GraphSpec>;
|
|
7
|
+
export declare const GRAPH_SCHEMA: Record<string, Record<string, GraphSchema>>;
|
|
8
|
+
export declare const NAMESPACE_GRAPH: Uint8Array<ArrayBufferLike>;
|
|
9
|
+
export declare const getAssistantId: (graphId: string) => string;
|
|
10
|
+
export declare function registerFromEnv(assistants: AssistantsRepo, specs: Record<string, string>, options: {
|
|
11
|
+
cwd: string;
|
|
12
|
+
}): Promise<(CompiledGraph<string, any, any, Record<string, any>, any, any, unknown, unknown, any> | CompiledGraphFactory<string>)[]>;
|
|
13
|
+
export declare function getGraph(graphId: string, config: LangGraphRunnableConfig | undefined, options?: {
|
|
14
|
+
checkpointer?: BaseCheckpointSaver | null;
|
|
15
|
+
store?: BaseStore;
|
|
16
|
+
}): Promise<CompiledGraph<string, any, any, Record<string, any>, any, any, unknown, unknown, any>>;
|
|
17
|
+
export declare function assertGraphExists(graphId: string): void;
|
|
18
|
+
export declare function getGraphKeys(): string[];
|
|
19
|
+
export declare function getCachedStaticGraphSchema(graphId: string): Promise<Record<string, GraphSchema>>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// This module hook is used to ensure that @langchain/langgraph package
|
|
2
|
+
// is imported from a consistent location.
|
|
3
|
+
// Accepts `{ parentURL: string }` as argument when registering the hook.
|
|
4
|
+
const OVERRIDE_RESOLVE = [
|
|
5
|
+
// Override `@langchain/langgraph` or `@langchain/langgraph/prebuilt`,
|
|
6
|
+
// but not `@langchain/langgraph-sdk`
|
|
7
|
+
new RegExp(`^@langchain\/langgraph(\/.+)?$`),
|
|
8
|
+
new RegExp(`^@langchain\/langgraph-checkpoint(\/.+)?$`),
|
|
9
|
+
];
|
|
10
|
+
let parentURL;
|
|
11
|
+
let langgraphPackageURL;
|
|
12
|
+
export async function initialize(args) {
|
|
13
|
+
parentURL = args.parentURL;
|
|
14
|
+
}
|
|
15
|
+
export async function resolve(specifier, context, nextResolve) {
|
|
16
|
+
// HACK: @tailwindcss/node internally uses an ESM loader cache, which does not play nicely with `tsx`.
|
|
17
|
+
// Node.js crashes with "TypeError [ERR_INVALID_URL_SCHEME]: The URL must be of scheme file".
|
|
18
|
+
// As it already is a valid URI, we can just short-circuit the resolution and avoid `tsx`.
|
|
19
|
+
if (specifier.includes("@tailwindcss/node/dist/esm-cache.loader") &&
|
|
20
|
+
specifier.startsWith("file://")) {
|
|
21
|
+
return {
|
|
22
|
+
shortCircuit: true,
|
|
23
|
+
// Node 18.x will for some reason attempt to load `.mts` instead of `.mjs`
|
|
24
|
+
url: specifier.replace(".mts", ".mjs"),
|
|
25
|
+
format: "module",
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if (specifier === "@langchain/langgraph-checkpoint") {
|
|
29
|
+
// resolve relative to @langchain/langgraph package instead
|
|
30
|
+
// This is done to avoid adding a direct dependency on @langchain/langgraph-checkpoint
|
|
31
|
+
// in project, which if not present will cause `pnpm` to not find the package.
|
|
32
|
+
if (!langgraphPackageURL) {
|
|
33
|
+
const main = await nextResolve("@langchain/langgraph", {
|
|
34
|
+
...context,
|
|
35
|
+
parentURL,
|
|
36
|
+
});
|
|
37
|
+
langgraphPackageURL = main.url.toString();
|
|
38
|
+
}
|
|
39
|
+
return await nextResolve(specifier, {
|
|
40
|
+
...context,
|
|
41
|
+
parentURL: langgraphPackageURL,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
if (OVERRIDE_RESOLVE.some((regex) => regex.test(specifier))) {
|
|
45
|
+
const resolved = await nextResolve(specifier, { ...context, parentURL });
|
|
46
|
+
// If @langchain/langgraph is resolved first, cache it!
|
|
47
|
+
if (specifier === "@langchain/langgraph" && !langgraphPackageURL) {
|
|
48
|
+
langgraphPackageURL = resolved.url.toString();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return nextResolve(specifier, context);
|
|
52
|
+
}
|