langgraph-api 0.0.26__py3-none-any.whl → 0.0.28rc1__py3-none-any.whl
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.
Potentially problematic release.
This version of langgraph-api might be problematic. Click here for more details.
- langgraph_api/api/__init__.py +2 -0
- langgraph_api/api/assistants.py +43 -13
- langgraph_api/api/meta.py +2 -1
- langgraph_api/api/runs.py +14 -1
- langgraph_api/api/ui.py +68 -0
- langgraph_api/asyncio.py +43 -4
- langgraph_api/auth/middleware.py +2 -2
- langgraph_api/cli.py +72 -57
- langgraph_api/config.py +23 -1
- langgraph_api/cron_scheduler.py +1 -1
- langgraph_api/graph.py +5 -0
- langgraph_api/http.py +24 -7
- langgraph_api/js/.gitignore +2 -0
- langgraph_api/js/build.mts +49 -3
- langgraph_api/js/client.mts +84 -40
- langgraph_api/js/global.d.ts +1 -0
- langgraph_api/js/package.json +15 -7
- langgraph_api/js/remote.py +662 -16
- langgraph_api/js/src/graph.mts +5 -4
- langgraph_api/js/sse.py +138 -0
- langgraph_api/js/tests/api.test.mts +28 -0
- langgraph_api/js/tests/compose-postgres.yml +2 -2
- langgraph_api/js/tests/graphs/agent.css +1 -0
- langgraph_api/js/tests/graphs/agent.ui.tsx +10 -0
- langgraph_api/js/tests/graphs/package.json +2 -2
- langgraph_api/js/tests/graphs/yarn.lock +13 -13
- langgraph_api/js/yarn.lock +710 -1187
- langgraph_api/lifespan.py +15 -5
- langgraph_api/logging.py +9 -0
- langgraph_api/metadata.py +5 -1
- langgraph_api/middleware/http_logger.py +1 -1
- langgraph_api/patch.py +2 -0
- langgraph_api/queue_entrypoint.py +63 -0
- langgraph_api/schema.py +2 -0
- langgraph_api/stream.py +1 -0
- langgraph_api/webhook.py +42 -0
- langgraph_api/{queue.py → worker.py} +52 -166
- {langgraph_api-0.0.26.dist-info → langgraph_api-0.0.28rc1.dist-info}/METADATA +8 -8
- {langgraph_api-0.0.26.dist-info → langgraph_api-0.0.28rc1.dist-info}/RECORD +49 -46
- langgraph_storage/database.py +8 -22
- langgraph_storage/inmem_stream.py +108 -0
- langgraph_storage/ops.py +80 -57
- langgraph_storage/queue.py +126 -103
- langgraph_storage/retry.py +5 -1
- langgraph_storage/store.py +5 -1
- openapi.json +3 -3
- langgraph_api/js/client.new.mts +0 -861
- langgraph_api/js/remote_new.py +0 -694
- langgraph_api/js/remote_old.py +0 -667
- langgraph_api/js/server_sent_events.py +0 -126
- {langgraph_api-0.0.26.dist-info → langgraph_api-0.0.28rc1.dist-info}/LICENSE +0 -0
- {langgraph_api-0.0.26.dist-info → langgraph_api-0.0.28rc1.dist-info}/WHEEL +0 -0
- {langgraph_api-0.0.26.dist-info → langgraph_api-0.0.28rc1.dist-info}/entry_points.txt +0 -0
langgraph_api/js/build.mts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
resolveGraph,
|
|
10
10
|
runGraphSchemaWorker,
|
|
11
11
|
} from "./src/graph.mts";
|
|
12
|
+
import { build } from "@langchain/langgraph-api/ui/bundler";
|
|
12
13
|
|
|
13
14
|
const __dirname = new URL(".", import.meta.url).pathname;
|
|
14
15
|
|
|
@@ -17,7 +18,7 @@ async function main() {
|
|
|
17
18
|
z.record(z.string()).parse(JSON.parse(process.env.LANGSERVE_GRAPHS))
|
|
18
19
|
);
|
|
19
20
|
|
|
20
|
-
const GRAPH_SCHEMAS: Record<string, Record<string, GraphSchema
|
|
21
|
+
const GRAPH_SCHEMAS: Record<string, Record<string, GraphSchema> | false> = {};
|
|
21
22
|
|
|
22
23
|
try {
|
|
23
24
|
await Promise.all(
|
|
@@ -29,9 +30,12 @@ async function main() {
|
|
|
29
30
|
|
|
30
31
|
try {
|
|
31
32
|
console.info(`[${graphId}]: Extracting schema`);
|
|
32
|
-
GRAPH_SCHEMAS[graphId] = await runGraphSchemaWorker(spec
|
|
33
|
+
GRAPH_SCHEMAS[graphId] = await runGraphSchemaWorker(spec, {
|
|
34
|
+
timeoutMs: 120_000,
|
|
35
|
+
});
|
|
33
36
|
} catch (error) {
|
|
34
37
|
console.error(`[${graphId}]: Error extracting schema: ${error}`);
|
|
38
|
+
GRAPH_SCHEMAS[graphId] = false;
|
|
35
39
|
}
|
|
36
40
|
})
|
|
37
41
|
);
|
|
@@ -46,7 +50,49 @@ async function main() {
|
|
|
46
50
|
process.exit(1);
|
|
47
51
|
}
|
|
48
52
|
|
|
49
|
-
|
|
53
|
+
const uiSpecs = z
|
|
54
|
+
.record(z.string())
|
|
55
|
+
.parse(JSON.parse(process.env.LANGGRAPH_UI || "{}"));
|
|
56
|
+
|
|
57
|
+
if (Object.keys(uiSpecs).length > 0) {
|
|
58
|
+
try {
|
|
59
|
+
const schemas: Record<string, { assets: string[]; name: string }> = {};
|
|
60
|
+
await Promise.all(
|
|
61
|
+
Object.entries(uiSpecs).map(async ([graphId, uiUserPath]) => {
|
|
62
|
+
console.info(`[${graphId}]: Building UI`);
|
|
63
|
+
const userPath = path.resolve(process.cwd(), uiUserPath);
|
|
64
|
+
const files = await build(graphId, userPath);
|
|
65
|
+
await Promise.all([
|
|
66
|
+
...files.map(async (item) => {
|
|
67
|
+
const folder = path.resolve(__dirname, "ui", graphId);
|
|
68
|
+
const source = path.resolve(folder, item.basename);
|
|
69
|
+
|
|
70
|
+
await fs.mkdir(path.dirname(source), { recursive: true });
|
|
71
|
+
await fs.writeFile(source, item.contents);
|
|
72
|
+
|
|
73
|
+
schemas[graphId] ??= { assets: [], name: graphId };
|
|
74
|
+
|
|
75
|
+
const relative = path.relative(
|
|
76
|
+
path.resolve(__dirname, "ui", graphId),
|
|
77
|
+
source
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
schemas[graphId].assets.push(relative);
|
|
81
|
+
}),
|
|
82
|
+
]);
|
|
83
|
+
})
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
await fs.writeFile(
|
|
87
|
+
path.resolve(__dirname, "client.ui.schemas.json"),
|
|
88
|
+
JSON.stringify(schemas),
|
|
89
|
+
{ encoding: "utf-8" }
|
|
90
|
+
);
|
|
91
|
+
} catch (error) {
|
|
92
|
+
console.error(`Error building UI: ${error}`);
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
50
96
|
}
|
|
51
97
|
|
|
52
98
|
main();
|
langgraph_api/js/client.mts
CHANGED
|
@@ -4,9 +4,9 @@ import { z } from "zod";
|
|
|
4
4
|
import { Context, Hono } from "hono";
|
|
5
5
|
import { serve } from "@hono/node-server";
|
|
6
6
|
import { zValidator } from "@hono/zod-validator";
|
|
7
|
-
import { streamSSE } from "hono/streaming";
|
|
7
|
+
import { streamSSE, stream } from "hono/streaming";
|
|
8
8
|
import { HTTPException } from "hono/http-exception";
|
|
9
|
-
import {
|
|
9
|
+
import { fetch } from "undici";
|
|
10
10
|
import pRetry from "p-retry";
|
|
11
11
|
import {
|
|
12
12
|
BaseStore,
|
|
@@ -43,6 +43,8 @@ import {
|
|
|
43
43
|
GraphSpec,
|
|
44
44
|
filterValidGraphSpecs,
|
|
45
45
|
} from "./src/graph.mts";
|
|
46
|
+
import { asyncExitHook, gracefulExit } from "exit-hook";
|
|
47
|
+
import { awaitAllCallbacks } from "@langchain/core/callbacks/promises";
|
|
46
48
|
|
|
47
49
|
const logger = createLogger({
|
|
48
50
|
level: "debug",
|
|
@@ -76,7 +78,7 @@ const logger = createLogger({
|
|
|
76
78
|
],
|
|
77
79
|
});
|
|
78
80
|
|
|
79
|
-
let GRAPH_SCHEMA: Record<string, Record<string, GraphSchema
|
|
81
|
+
let GRAPH_SCHEMA: Record<string, Record<string, GraphSchema> | false> = {};
|
|
80
82
|
const GRAPH_RESOLVED: Record<string, CompiledGraph<string>> = {};
|
|
81
83
|
const GRAPH_SPEC: Record<string, GraphSpec> = {};
|
|
82
84
|
|
|
@@ -92,9 +94,28 @@ async function getOrExtractSchema(graphId: string) {
|
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
if (!GRAPH_SCHEMA[graphId]) {
|
|
97
|
+
// This is only set during build phase
|
|
98
|
+
if (GRAPH_SCHEMA[graphId] === false) {
|
|
99
|
+
throw new Error(`Failed to locate schema for "${graphId}"`);
|
|
100
|
+
}
|
|
101
|
+
|
|
95
102
|
try {
|
|
96
103
|
const timer = logger.startTimer();
|
|
97
|
-
|
|
104
|
+
|
|
105
|
+
let timeoutMs: number | undefined = undefined;
|
|
106
|
+
try {
|
|
107
|
+
timeoutMs = Number.parseInt(
|
|
108
|
+
process.env.LANGGRAPH_SCHEMA_RESOLVE_TIMEOUT_MS || "30000",
|
|
109
|
+
10
|
|
110
|
+
);
|
|
111
|
+
if (Number.isNaN(timeoutMs) || timeoutMs <= 0) timeoutMs = undefined;
|
|
112
|
+
} catch {
|
|
113
|
+
// ignore
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
GRAPH_SCHEMA[graphId] = await runGraphSchemaWorker(GRAPH_SPEC[graphId], {
|
|
117
|
+
timeoutMs,
|
|
118
|
+
});
|
|
98
119
|
timer.done({ message: `Extracting schema for ${graphId} finished` });
|
|
99
120
|
} catch (error) {
|
|
100
121
|
throw new Error(`Failed to extract schema for "${graphId}": ${error}`);
|
|
@@ -104,11 +125,8 @@ async function getOrExtractSchema(graphId: string) {
|
|
|
104
125
|
return GRAPH_SCHEMA[graphId];
|
|
105
126
|
}
|
|
106
127
|
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
const remoteDispatcher = new Agent({
|
|
110
|
-
connect: { socketPath: REMOTE_SOCKET },
|
|
111
|
-
});
|
|
128
|
+
const GRAPH_PORT = 5556;
|
|
129
|
+
const REMOTE_PORT = 5555;
|
|
112
130
|
|
|
113
131
|
const RunnableConfigSchema = z.object({
|
|
114
132
|
tags: z.array(z.string()).optional(),
|
|
@@ -165,8 +183,7 @@ async function sendRecv<T = any>(
|
|
|
165
183
|
method: `${"checkpointer" | "store"}_${string}`,
|
|
166
184
|
data: unknown
|
|
167
185
|
): Promise<T> {
|
|
168
|
-
const res = await tryFetch(`http://
|
|
169
|
-
dispatcher: remoteDispatcher,
|
|
186
|
+
const res = await tryFetch(`http://localhost:${REMOTE_PORT}/${method}`, {
|
|
170
187
|
method: "POST",
|
|
171
188
|
headers: { "Content-Type": "application/json" },
|
|
172
189
|
body: JSON.stringify(data),
|
|
@@ -180,6 +197,7 @@ async function sendRecv<T = any>(
|
|
|
180
197
|
})) as T;
|
|
181
198
|
}
|
|
182
199
|
|
|
200
|
+
const HEARTBEAT_MS = 5_000;
|
|
183
201
|
const handleInvoke = <T extends z.ZodType<any>>(
|
|
184
202
|
name: string,
|
|
185
203
|
_schema: T,
|
|
@@ -188,8 +206,29 @@ const handleInvoke = <T extends z.ZodType<any>>(
|
|
|
188
206
|
return async (c: Context<any, any, { in: z.infer<T>; out: any }>) => {
|
|
189
207
|
const graphId = c.req.param("graphId");
|
|
190
208
|
const body = c.req.valid("json") as any;
|
|
191
|
-
|
|
192
|
-
|
|
209
|
+
|
|
210
|
+
// send heartbeat every HEARTBEAT_INTERVAL_MS
|
|
211
|
+
// to prevent connection from timing out
|
|
212
|
+
c.header("Content-Type", "application/json");
|
|
213
|
+
return stream(c, async (stream) => {
|
|
214
|
+
let resolved: Promise<any> = Promise.resolve();
|
|
215
|
+
const enqueueWrite = (payload: Uint8Array | string) => {
|
|
216
|
+
resolved = stream.write(payload);
|
|
217
|
+
return resolved;
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
// orjson.loads(...) does ignore the
|
|
221
|
+
// whitespace prefix, so we can use that
|
|
222
|
+
// as a heartbeat
|
|
223
|
+
let interval = setInterval(() => enqueueWrite(" "), HEARTBEAT_MS);
|
|
224
|
+
|
|
225
|
+
const response = JSON.stringify(
|
|
226
|
+
await handler({ graph_id: graphId, ...body })
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
clearInterval(interval);
|
|
230
|
+
await enqueueWrite(response);
|
|
231
|
+
});
|
|
193
232
|
};
|
|
194
233
|
};
|
|
195
234
|
|
|
@@ -202,25 +241,35 @@ const handleStream = <T extends z.ZodType<any>>(
|
|
|
202
241
|
const graphId = c.req.param("graphId");
|
|
203
242
|
const body = c.req.valid("json") as any;
|
|
204
243
|
return streamSSE(c, async (stream) => {
|
|
244
|
+
let resolve: Promise<any> = Promise.resolve();
|
|
245
|
+
let timer: NodeJS.Timeout | undefined;
|
|
246
|
+
const sendHeartbeat = () => {
|
|
247
|
+
clearTimeout(timer);
|
|
248
|
+
resolve = stream.writeln(": heartbeat");
|
|
249
|
+
timer = setTimeout(sendHeartbeat, HEARTBEAT_MS);
|
|
250
|
+
return resolve;
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const sendSSE = (event: string, data: unknown) => {
|
|
254
|
+
clearTimeout(timer);
|
|
255
|
+
resolve = stream.writeSSE({ data: serialiseAsDict(data), event });
|
|
256
|
+
timer = setTimeout(sendHeartbeat, HEARTBEAT_MS);
|
|
257
|
+
return resolve;
|
|
258
|
+
};
|
|
259
|
+
|
|
205
260
|
try {
|
|
206
261
|
for await (const data of handler({ graph_id: graphId, ...body })) {
|
|
207
|
-
await
|
|
208
|
-
data: Buffer.from(serialiseAsDict(data)).toString("base64"),
|
|
209
|
-
event: name,
|
|
210
|
-
});
|
|
262
|
+
await sendSSE(name, data);
|
|
211
263
|
}
|
|
212
264
|
} catch (error) {
|
|
213
265
|
// Still print out the error, as the stack
|
|
214
266
|
// trace is not carried over in Python
|
|
215
267
|
logger.error(error);
|
|
216
|
-
|
|
217
|
-
await stream.writeSSE({
|
|
218
|
-
event: "error",
|
|
219
|
-
data: Buffer.from(serialiseAsDict(serializeError(error))).toString(
|
|
220
|
-
"base64"
|
|
221
|
-
),
|
|
222
|
-
});
|
|
268
|
+
await sendSSE("error", serializeError(error));
|
|
223
269
|
}
|
|
270
|
+
|
|
271
|
+
clearTimeout(timer);
|
|
272
|
+
await resolve;
|
|
224
273
|
});
|
|
225
274
|
};
|
|
226
275
|
};
|
|
@@ -604,12 +653,12 @@ const GetGraphPayload = z.object({
|
|
|
604
653
|
async function getGraphRequest(rawPayload: z.infer<typeof GetGraphPayload>) {
|
|
605
654
|
const { graph_id: graphId, ...payload } = rawPayload;
|
|
606
655
|
const graph = getGraph(graphId);
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
656
|
+
|
|
657
|
+
const drawable = await graph.getGraphAsync({
|
|
658
|
+
...getRunnableConfig(payload.config),
|
|
659
|
+
xray: payload.xray ?? undefined,
|
|
660
|
+
});
|
|
661
|
+
return drawable.toJSON();
|
|
613
662
|
}
|
|
614
663
|
|
|
615
664
|
const GetSubgraphsPayload = z.object({
|
|
@@ -630,7 +679,7 @@ async function getSubgraphsRequest(
|
|
|
630
679
|
|
|
631
680
|
if (!rootGraphId) throw new Error("Failed to find root graph");
|
|
632
681
|
|
|
633
|
-
for (const [name] of graph.
|
|
682
|
+
for await (const [name] of graph.getSubgraphsAsync(
|
|
634
683
|
payload.namespace ?? undefined,
|
|
635
684
|
payload.recurse ?? undefined
|
|
636
685
|
)) {
|
|
@@ -818,20 +867,15 @@ async function main() {
|
|
|
818
867
|
return c.text(message || "Internal server error", 500);
|
|
819
868
|
});
|
|
820
869
|
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
{
|
|
824
|
-
fetch: app.fetch,
|
|
825
|
-
hostname: "localhost",
|
|
826
|
-
port: GRAPH_SOCKET as any,
|
|
827
|
-
},
|
|
828
|
-
(c) => logger.info(`Listening to ${c}`)
|
|
870
|
+
serve({ fetch: app.fetch, hostname: "localhost", port: GRAPH_PORT }, (c) =>
|
|
871
|
+
logger.info(`Listening to ${c.address}:${c.port}`)
|
|
829
872
|
);
|
|
830
873
|
}
|
|
831
874
|
|
|
832
875
|
process.on("uncaughtExceptionMonitor", (error) => {
|
|
833
876
|
logger.error(error);
|
|
834
|
-
|
|
877
|
+
gracefulExit();
|
|
835
878
|
});
|
|
836
879
|
|
|
880
|
+
asyncExitHook(() => awaitAllCallbacks(), { wait: 3_000 });
|
|
837
881
|
main();
|
langgraph_api/js/global.d.ts
CHANGED
langgraph_api/js/package.json
CHANGED
|
@@ -9,27 +9,35 @@
|
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"@hono/node-server": "^1.12.0",
|
|
11
11
|
"@hono/zod-validator": "^0.2.2",
|
|
12
|
-
"@langchain/core": "^0.3.
|
|
13
|
-
"@langchain/langgraph": "^0.2.
|
|
12
|
+
"@langchain/core": "^0.3.40",
|
|
13
|
+
"@langchain/langgraph": "^0.2.49",
|
|
14
|
+
"@langchain/langgraph-checkpoint": "^0.0.15",
|
|
14
15
|
"@types/json-schema": "^7.0.15",
|
|
15
16
|
"@typescript/vfs": "^1.6.0",
|
|
16
17
|
"dedent": "^1.5.3",
|
|
18
|
+
"exit-hook": "^4.0.0",
|
|
17
19
|
"hono": "^4.5.4",
|
|
18
20
|
"p-queue": "^8.0.1",
|
|
19
21
|
"p-retry": "^6.2.0",
|
|
20
|
-
"tsx": "^4.19.
|
|
22
|
+
"tsx": "^4.19.3",
|
|
21
23
|
"typescript": "^5.5.4",
|
|
22
24
|
"undici": "^6.21.1",
|
|
23
25
|
"uuid": "^10.0.0",
|
|
24
|
-
"winston": "^3.
|
|
25
|
-
"
|
|
26
|
+
"winston": "^3.17.0",
|
|
27
|
+
"@langchain/langgraph-api": "~0.0.14",
|
|
26
28
|
"zod": "^3.23.8"
|
|
27
29
|
},
|
|
30
|
+
"resolutions": {
|
|
31
|
+
"esbuild": "^0.25.0"
|
|
32
|
+
},
|
|
28
33
|
"devDependencies": {
|
|
29
34
|
"@langchain/langgraph-sdk": "^0.0.31",
|
|
35
|
+
"@types/react": "^19.0.8",
|
|
36
|
+
"@types/react-dom": "^19.0.3",
|
|
30
37
|
"@types/node": "^22.2.0",
|
|
31
38
|
"postgres": "^3.4.4",
|
|
32
39
|
"prettier": "^3.3.3",
|
|
33
40
|
"vitest": "^3.0.4"
|
|
34
|
-
}
|
|
35
|
-
|
|
41
|
+
},
|
|
42
|
+
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
|
43
|
+
}
|