@langchain/langgraph-api 0.0.20 → 0.0.22
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.mjs +38 -36
- package/dist/api/meta.mjs +5 -0
- package/dist/api/runs.mjs +72 -30
- package/dist/api/store.mjs +28 -0
- package/dist/api/threads.mjs +13 -16
- package/dist/auth/custom.mjs +58 -0
- package/dist/auth/index.d.mts +40 -0
- package/dist/auth/index.mjs +120 -0
- package/dist/cli/spawn.d.mts +21 -0
- package/dist/cli/spawn.mjs +2 -0
- package/dist/graph/load.mjs +2 -2
- package/dist/http/custom.mjs +10 -0
- package/dist/http/middleware.mjs +38 -0
- package/dist/queue.mjs +21 -4
- package/dist/schemas.mjs +3 -2
- package/dist/server.mjs +72 -34
- package/dist/storage/ops.mjs +318 -115
- package/dist/webhook.mjs +30 -0
- package/package.json +14 -3
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { HTTPException } from "hono/http-exception";
|
|
2
|
+
import * as url from "node:url";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
let CUSTOM_AUTH;
|
|
5
|
+
let DISABLE_STUDIO_AUTH = false;
|
|
6
|
+
export const isAuthRegistered = () => CUSTOM_AUTH != null;
|
|
7
|
+
export const isStudioAuthDisabled = () => DISABLE_STUDIO_AUTH;
|
|
8
|
+
function convertError(error) {
|
|
9
|
+
const isHTTPAuthException = (error) => {
|
|
10
|
+
return (typeof error === "object" &&
|
|
11
|
+
error != null &&
|
|
12
|
+
"status" in error &&
|
|
13
|
+
"headers" in error);
|
|
14
|
+
};
|
|
15
|
+
if (isHTTPAuthException(error)) {
|
|
16
|
+
throw new HTTPException(error.status, {
|
|
17
|
+
message: error.message,
|
|
18
|
+
res: new Response(error.message || "Unauthorized", {
|
|
19
|
+
status: error.status,
|
|
20
|
+
headers: error.headers,
|
|
21
|
+
}),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
export async function authorize(payload) {
|
|
27
|
+
// find filters and execute them
|
|
28
|
+
const handlers = CUSTOM_AUTH?.["~handlerCache"];
|
|
29
|
+
if (!handlers)
|
|
30
|
+
return { filters: undefined, value: payload.value };
|
|
31
|
+
const cbKey = [
|
|
32
|
+
`${payload.resource}:${payload.action}`,
|
|
33
|
+
`${payload.resource}`,
|
|
34
|
+
`*:${payload.action}`,
|
|
35
|
+
`*`,
|
|
36
|
+
].find((priority) => handlers.callbacks?.[priority]);
|
|
37
|
+
const handler = cbKey ? handlers.callbacks?.[cbKey] : undefined;
|
|
38
|
+
if (!handler || !payload.context) {
|
|
39
|
+
return { filters: undefined, value: payload.value };
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const result = await handler({
|
|
43
|
+
event: `${payload.resource}:${payload.action}`,
|
|
44
|
+
resource: payload.resource,
|
|
45
|
+
action: payload.action,
|
|
46
|
+
value: payload.value,
|
|
47
|
+
permissions: payload.context?.scopes,
|
|
48
|
+
user: payload.context?.user,
|
|
49
|
+
});
|
|
50
|
+
if (result == null || result == true) {
|
|
51
|
+
return { filters: undefined, value: payload.value };
|
|
52
|
+
}
|
|
53
|
+
if (result === false)
|
|
54
|
+
throw new HTTPException(403);
|
|
55
|
+
if (typeof result !== "object") {
|
|
56
|
+
throw new HTTPException(500, {
|
|
57
|
+
message: `Auth handler returned invalid result. Expected filter object, null, undefined or boolean. Got "${typeof result}" instead.`,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return { filters: result, value: payload.value };
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
throw convertError(error);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export async function authenticate(request) {
|
|
67
|
+
const handlers = CUSTOM_AUTH?.["~handlerCache"];
|
|
68
|
+
if (!handlers?.authenticate)
|
|
69
|
+
return undefined;
|
|
70
|
+
try {
|
|
71
|
+
const response = await handlers.authenticate(request);
|
|
72
|
+
// normalize auth response
|
|
73
|
+
const { scopes, user } = (() => {
|
|
74
|
+
if (typeof response === "string") {
|
|
75
|
+
return {
|
|
76
|
+
scopes: [],
|
|
77
|
+
user: {
|
|
78
|
+
permissions: [],
|
|
79
|
+
identity: response,
|
|
80
|
+
display_name: response,
|
|
81
|
+
is_authenticated: true,
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if ("identity" in response && typeof response.identity === "string") {
|
|
86
|
+
const scopes = "permissions" in response && Array.isArray(response.permissions)
|
|
87
|
+
? response.permissions
|
|
88
|
+
: [];
|
|
89
|
+
return {
|
|
90
|
+
scopes,
|
|
91
|
+
user: {
|
|
92
|
+
...response,
|
|
93
|
+
permissions: scopes,
|
|
94
|
+
is_authenticated: response.is_authenticated ?? true,
|
|
95
|
+
display_name: response.display_name ?? response.identity,
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
throw new Error("Invalid auth response received. Make sure to either return a `string` or an object with `identity` property.");
|
|
100
|
+
})();
|
|
101
|
+
return { scopes, user };
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
throw convertError(error);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
export async function registerAuth(auth, options) {
|
|
108
|
+
if (!auth.path)
|
|
109
|
+
return;
|
|
110
|
+
// TODO: handle options.auth.disable_studio_auth
|
|
111
|
+
const [userFile, exportSymbol] = auth.path.split(":", 2);
|
|
112
|
+
const sourceFile = path.resolve(options.cwd, userFile);
|
|
113
|
+
const module = (await import(url.pathToFileURL(sourceFile).toString()).then((module) => module[exportSymbol || "default"]));
|
|
114
|
+
if (!module)
|
|
115
|
+
throw new Error(`Failed to load auth: ${auth.path}`);
|
|
116
|
+
if (!("~handlerCache" in module))
|
|
117
|
+
throw new Error(`Auth must be an instance of Auth: ${auth.path}`);
|
|
118
|
+
CUSTOM_AUTH = module;
|
|
119
|
+
DISABLE_STUDIO_AUTH = auth.disable_studio_auth ?? false;
|
|
120
|
+
}
|
package/dist/cli/spawn.d.mts
CHANGED
|
@@ -9,6 +9,27 @@ export declare function spawnServer(args: {
|
|
|
9
9
|
ui_config?: {
|
|
10
10
|
shared?: string[];
|
|
11
11
|
};
|
|
12
|
+
auth?: {
|
|
13
|
+
path?: string;
|
|
14
|
+
disable_studio_auth?: boolean;
|
|
15
|
+
};
|
|
16
|
+
http?: {
|
|
17
|
+
app?: string;
|
|
18
|
+
disable_assistants?: boolean;
|
|
19
|
+
disable_threads?: boolean;
|
|
20
|
+
disable_runs?: boolean;
|
|
21
|
+
disable_store?: boolean;
|
|
22
|
+
disable_meta?: boolean;
|
|
23
|
+
cors?: {
|
|
24
|
+
allow_origins?: string[];
|
|
25
|
+
allow_methods?: string[];
|
|
26
|
+
allow_headers?: string[];
|
|
27
|
+
allow_credentials?: boolean;
|
|
28
|
+
allow_origin_regex?: string;
|
|
29
|
+
expose_headers?: string[];
|
|
30
|
+
max_age?: number;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
12
33
|
};
|
|
13
34
|
env: NodeJS.ProcessEnv;
|
|
14
35
|
hostUrl: string;
|
package/dist/cli/spawn.mjs
CHANGED
|
@@ -30,9 +30,11 @@ For production use, please use LangGraph Cloud.
|
|
|
30
30
|
nWorkers: Number.parseInt(args.nJobsPerWorker, 10),
|
|
31
31
|
host: args.host,
|
|
32
32
|
graphs: context.config.graphs,
|
|
33
|
+
auth: context.config.auth,
|
|
33
34
|
ui: context.config.ui,
|
|
34
35
|
ui_config: context.config.ui_config,
|
|
35
36
|
cwd: options.projectCwd,
|
|
37
|
+
http: context.config.http,
|
|
36
38
|
}),
|
|
37
39
|
], {
|
|
38
40
|
stdio: ["inherit", "inherit", "inherit", "ipc"],
|
package/dist/graph/load.mjs
CHANGED
|
@@ -10,7 +10,7 @@ export const GRAPHS = {};
|
|
|
10
10
|
export const GRAPH_SPEC = {};
|
|
11
11
|
export const GRAPH_SCHEMA = {};
|
|
12
12
|
export const NAMESPACE_GRAPH = uuid.parse("6ba7b821-9dad-11d1-80b4-00c04fd430c8");
|
|
13
|
-
const ConfigSchema = z.record(z.unknown());
|
|
13
|
+
const ConfigSchema = z.record(z.record(z.unknown()));
|
|
14
14
|
export const getAssistantId = (graphId) => {
|
|
15
15
|
if (graphId in GRAPHS)
|
|
16
16
|
return uuid.v5(graphId, NAMESPACE_GRAPH);
|
|
@@ -37,7 +37,7 @@ export async function registerFromEnv(specs, options) {
|
|
|
37
37
|
config: config ?? {},
|
|
38
38
|
if_exists: "do_nothing",
|
|
39
39
|
name: graphId,
|
|
40
|
-
});
|
|
40
|
+
}, undefined);
|
|
41
41
|
return resolved;
|
|
42
42
|
}));
|
|
43
43
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import * as url from "node:url";
|
|
3
|
+
export async function registerHttp(appPath, options) {
|
|
4
|
+
const [userFile, exportSymbol] = appPath.split(":", 2);
|
|
5
|
+
const sourceFile = path.resolve(options.cwd, userFile);
|
|
6
|
+
const user = (await import(url.pathToFileURL(sourceFile).toString()).then((module) => module[exportSymbol || "default"]));
|
|
7
|
+
if (!user)
|
|
8
|
+
throw new Error(`Failed to load HTTP app: ${appPath}`);
|
|
9
|
+
return { api: user };
|
|
10
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { cors as honoCors } from "hono/cors";
|
|
2
|
+
export const cors = (cors) => {
|
|
3
|
+
if (cors == null)
|
|
4
|
+
return honoCors();
|
|
5
|
+
const originRegex = cors.allow_origin_regex
|
|
6
|
+
? new RegExp(cors.allow_origin_regex)
|
|
7
|
+
: undefined;
|
|
8
|
+
const origin = originRegex
|
|
9
|
+
? (origin) => {
|
|
10
|
+
originRegex.lastIndex = 0; // reset regex in case it's a global regex
|
|
11
|
+
if (originRegex.test(origin))
|
|
12
|
+
return origin;
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
: (cors.allow_origins ?? []);
|
|
16
|
+
// TODO: handle `cors.allow_credentials`
|
|
17
|
+
return honoCors({
|
|
18
|
+
origin,
|
|
19
|
+
maxAge: cors.max_age,
|
|
20
|
+
allowMethods: cors.allow_methods,
|
|
21
|
+
allowHeaders: cors.allow_headers,
|
|
22
|
+
credentials: cors.allow_credentials,
|
|
23
|
+
exposeHeaders: cors.expose_headers,
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
// This is used to match the behavior of the original LangGraph API
|
|
27
|
+
// where the content-type is not being validated. Might be nice
|
|
28
|
+
// to warn about this in the future and throw an error instead.
|
|
29
|
+
export const ensureContentType = () => {
|
|
30
|
+
return async (c, next) => {
|
|
31
|
+
if (c.req.header("content-type")?.startsWith("text/plain") &&
|
|
32
|
+
c.req.method !== "GET" &&
|
|
33
|
+
c.req.method !== "OPTIONS") {
|
|
34
|
+
c.req.raw.headers.set("content-type", "application/json");
|
|
35
|
+
}
|
|
36
|
+
await next();
|
|
37
|
+
};
|
|
38
|
+
};
|
package/dist/queue.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { Runs, Threads } from "./storage/ops.mjs";
|
|
|
2
2
|
import { streamState, } from "./stream.mjs";
|
|
3
3
|
import { logError, logger } from "./logging.mjs";
|
|
4
4
|
import { serializeError } from "./utils/serde.mjs";
|
|
5
|
+
import { callWebhook } from "./webhook.mjs";
|
|
5
6
|
const MAX_RETRY_ATTEMPTS = 3;
|
|
6
7
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
7
8
|
export const queue = async () => {
|
|
@@ -15,9 +16,12 @@ export const queue = async () => {
|
|
|
15
16
|
};
|
|
16
17
|
const worker = async (run, attempt, abortSignal) => {
|
|
17
18
|
const startedAt = new Date();
|
|
19
|
+
let endedAt = undefined;
|
|
18
20
|
let checkpoint = undefined;
|
|
19
21
|
let exception = undefined;
|
|
22
|
+
let status = undefined;
|
|
20
23
|
const temporary = run.kwargs.temporary;
|
|
24
|
+
const webhook = run.kwargs.webhook;
|
|
21
25
|
logger.info("Starting background run", {
|
|
22
26
|
run_id: run.run_id,
|
|
23
27
|
run_attempt: attempt,
|
|
@@ -54,7 +58,7 @@ const worker = async (run, attempt, abortSignal) => {
|
|
|
54
58
|
await Runs.Stream.publish(run.run_id, "error", serializeError(error));
|
|
55
59
|
throw error;
|
|
56
60
|
}
|
|
57
|
-
|
|
61
|
+
endedAt = new Date();
|
|
58
62
|
logger.info("Background run succeeded", {
|
|
59
63
|
run_id: run.run_id,
|
|
60
64
|
run_attempt: attempt,
|
|
@@ -63,10 +67,11 @@ const worker = async (run, attempt, abortSignal) => {
|
|
|
63
67
|
run_ended_at: endedAt,
|
|
64
68
|
run_exec_ms: endedAt.valueOf() - startedAt.valueOf(),
|
|
65
69
|
});
|
|
66
|
-
|
|
70
|
+
status = "success";
|
|
71
|
+
await Runs.setStatus(run.run_id, status);
|
|
67
72
|
}
|
|
68
73
|
catch (error) {
|
|
69
|
-
|
|
74
|
+
endedAt = new Date();
|
|
70
75
|
if (error instanceof Error)
|
|
71
76
|
exception = error;
|
|
72
77
|
logError(error, {
|
|
@@ -80,14 +85,26 @@ const worker = async (run, attempt, abortSignal) => {
|
|
|
80
85
|
run_exec_ms: endedAt.valueOf() - startedAt.valueOf(),
|
|
81
86
|
},
|
|
82
87
|
});
|
|
88
|
+
status = "error";
|
|
83
89
|
await Runs.setStatus(run.run_id, "error");
|
|
84
90
|
}
|
|
85
91
|
finally {
|
|
86
92
|
if (temporary) {
|
|
87
|
-
await Threads.delete(run.thread_id);
|
|
93
|
+
await Threads.delete(run.thread_id, undefined);
|
|
88
94
|
}
|
|
89
95
|
else {
|
|
90
96
|
await Threads.setStatus(run.thread_id, { checkpoint, exception });
|
|
91
97
|
}
|
|
98
|
+
if (webhook) {
|
|
99
|
+
await callWebhook({
|
|
100
|
+
checkpoint,
|
|
101
|
+
status,
|
|
102
|
+
exception,
|
|
103
|
+
run,
|
|
104
|
+
webhook,
|
|
105
|
+
run_started_at: startedAt,
|
|
106
|
+
run_ended_at: endedAt,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
92
109
|
}
|
|
93
110
|
};
|
package/dist/schemas.mjs
CHANGED
|
@@ -45,6 +45,7 @@ export const AssistantPatch = z
|
|
|
45
45
|
.object({
|
|
46
46
|
graph_id: z.string().describe("The graph to use.").optional(),
|
|
47
47
|
config: AssistantConfig.optional(),
|
|
48
|
+
name: z.string().optional(),
|
|
48
49
|
metadata: z
|
|
49
50
|
.object({})
|
|
50
51
|
.catchall(z.any())
|
|
@@ -88,7 +89,7 @@ export const CronCreate = z
|
|
|
88
89
|
.describe("Metadata for the run.")
|
|
89
90
|
.optional(),
|
|
90
91
|
config: AssistantConfig.optional(),
|
|
91
|
-
webhook: z.string().
|
|
92
|
+
webhook: z.string().optional(),
|
|
92
93
|
interrupt_before: z.union([z.enum(["*"]), z.array(z.string())]).optional(),
|
|
93
94
|
interrupt_after: z.union([z.enum(["*"]), z.array(z.string())]).optional(),
|
|
94
95
|
multitask_strategy: z
|
|
@@ -171,7 +172,7 @@ export const RunCreate = z
|
|
|
171
172
|
.describe("Metadata for the run.")
|
|
172
173
|
.optional(),
|
|
173
174
|
config: AssistantConfig.optional(),
|
|
174
|
-
webhook: z.string().
|
|
175
|
+
webhook: z.string().optional(),
|
|
175
176
|
interrupt_before: z.union([z.enum(["*"]), z.array(z.string())]).optional(),
|
|
176
177
|
interrupt_after: z.union([z.enum(["*"]), z.array(z.string())]).optional(),
|
|
177
178
|
on_disconnect: z
|
package/dist/server.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { serve } from "@hono/node-server";
|
|
2
2
|
import { Hono } from "hono";
|
|
3
|
-
import { cors } from "hono/cors";
|
|
4
3
|
import { registerFromEnv } from "./graph/load.mjs";
|
|
5
4
|
import runs from "./api/runs.mjs";
|
|
6
5
|
import threads from "./api/threads.mjs";
|
|
7
6
|
import assistants from "./api/assistants.mjs";
|
|
8
7
|
import store from "./api/store.mjs";
|
|
8
|
+
import meta from "./api/meta.mjs";
|
|
9
9
|
import { truncate, conn as opsConn } from "./storage/ops.mjs";
|
|
10
10
|
import { zValidator } from "@hono/zod-validator";
|
|
11
11
|
import { z } from "zod";
|
|
@@ -13,44 +13,46 @@ import { queue } from "./queue.mjs";
|
|
|
13
13
|
import { logger, requestLogger } from "./logging.mjs";
|
|
14
14
|
import { checkpointer } from "./storage/checkpoint.mjs";
|
|
15
15
|
import { store as graphStore } from "./storage/store.mjs";
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (c.req.header("content-type")?.startsWith("text/plain") &&
|
|
22
|
-
c.req.method !== "GET" &&
|
|
23
|
-
c.req.method !== "OPTIONS") {
|
|
24
|
-
c.req.raw.headers.set("content-type", "application/json");
|
|
25
|
-
}
|
|
26
|
-
await next();
|
|
27
|
-
});
|
|
28
|
-
app.use(cors());
|
|
29
|
-
app.use(requestLogger());
|
|
30
|
-
app.route("/", assistants);
|
|
31
|
-
app.route("/", runs);
|
|
32
|
-
app.route("/", threads);
|
|
33
|
-
app.route("/", store);
|
|
34
|
-
app.get("/info", (c) => c.json({ flags: { assistants: true, crons: false } }));
|
|
35
|
-
app.post("/internal/truncate", zValidator("json", z.object({
|
|
36
|
-
runs: z.boolean().optional(),
|
|
37
|
-
threads: z.boolean().optional(),
|
|
38
|
-
assistants: z.boolean().optional(),
|
|
39
|
-
checkpointer: z.boolean().optional(),
|
|
40
|
-
store: z.boolean().optional(),
|
|
41
|
-
})), (c) => {
|
|
42
|
-
const { runs, threads, assistants, checkpointer, store } = c.req.valid("json");
|
|
43
|
-
truncate({ runs, threads, assistants, checkpointer, store });
|
|
44
|
-
return c.json({ ok: true });
|
|
45
|
-
});
|
|
16
|
+
import { auth } from "./auth/custom.mjs";
|
|
17
|
+
import { registerAuth } from "./auth/index.mjs";
|
|
18
|
+
import { registerHttp } from "./http/custom.mjs";
|
|
19
|
+
import { cors, ensureContentType } from "./http/middleware.mjs";
|
|
20
|
+
import { bindLoopbackFetch } from "./webhook.mjs";
|
|
46
21
|
export const StartServerSchema = z.object({
|
|
47
22
|
port: z.number(),
|
|
48
23
|
nWorkers: z.number(),
|
|
49
24
|
host: z.string(),
|
|
50
25
|
cwd: z.string(),
|
|
51
26
|
graphs: z.record(z.string()),
|
|
27
|
+
auth: z
|
|
28
|
+
.object({
|
|
29
|
+
path: z.string().optional(),
|
|
30
|
+
disable_studio_auth: z.boolean().default(false),
|
|
31
|
+
})
|
|
32
|
+
.optional(),
|
|
52
33
|
ui: z.record(z.string()).optional(),
|
|
53
34
|
ui_config: z.object({ shared: z.array(z.string()).optional() }).optional(),
|
|
35
|
+
http: z
|
|
36
|
+
.object({
|
|
37
|
+
app: z.string().optional(),
|
|
38
|
+
disable_assistants: z.boolean().default(false),
|
|
39
|
+
disable_threads: z.boolean().default(false),
|
|
40
|
+
disable_runs: z.boolean().default(false),
|
|
41
|
+
disable_store: z.boolean().default(false),
|
|
42
|
+
disable_meta: z.boolean().default(false),
|
|
43
|
+
cors: z
|
|
44
|
+
.object({
|
|
45
|
+
allow_origins: z.array(z.string()).optional(),
|
|
46
|
+
allow_methods: z.array(z.string()).optional(),
|
|
47
|
+
allow_headers: z.array(z.string()).optional(),
|
|
48
|
+
allow_credentials: z.boolean().optional(),
|
|
49
|
+
allow_origin_regex: z.string().optional(),
|
|
50
|
+
expose_headers: z.array(z.string()).optional(),
|
|
51
|
+
max_age: z.number().optional(),
|
|
52
|
+
})
|
|
53
|
+
.optional(),
|
|
54
|
+
})
|
|
55
|
+
.optional(),
|
|
54
56
|
});
|
|
55
57
|
export async function startServer(options) {
|
|
56
58
|
logger.info(`Initializing storage...`);
|
|
@@ -65,16 +67,52 @@ export async function startServer(options) {
|
|
|
65
67
|
};
|
|
66
68
|
logger.info(`Registering graphs from ${options.cwd}`);
|
|
67
69
|
await registerFromEnv(options.graphs, { cwd: options.cwd });
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
const app = new Hono();
|
|
71
|
+
if (options.auth?.path) {
|
|
72
|
+
logger.info(`Loading auth from ${options.auth.path}`);
|
|
73
|
+
await registerAuth(options.auth, { cwd: options.cwd });
|
|
74
|
+
app.use(auth());
|
|
75
|
+
}
|
|
76
|
+
if (options.http?.app) {
|
|
77
|
+
logger.info(`Loading HTTP app from ${options.http.app}`);
|
|
78
|
+
const { api } = await registerHttp(options.http.app, { cwd: options.cwd });
|
|
71
79
|
app.route("/", api);
|
|
80
|
+
}
|
|
81
|
+
app.use(cors(options.http?.cors));
|
|
82
|
+
app.use(requestLogger());
|
|
83
|
+
app.use(ensureContentType());
|
|
84
|
+
app.post("/internal/truncate", zValidator("json", z.object({
|
|
85
|
+
runs: z.boolean().optional(),
|
|
86
|
+
threads: z.boolean().optional(),
|
|
87
|
+
assistants: z.boolean().optional(),
|
|
88
|
+
checkpointer: z.boolean().optional(),
|
|
89
|
+
store: z.boolean().optional(),
|
|
90
|
+
})), (c) => {
|
|
91
|
+
const { runs, threads, assistants, checkpointer, store } = c.req.valid("json");
|
|
92
|
+
truncate({ runs, threads, assistants, checkpointer, store });
|
|
93
|
+
return c.json({ ok: true });
|
|
94
|
+
});
|
|
95
|
+
if (!options.http?.disable_meta)
|
|
96
|
+
app.route("/", meta);
|
|
97
|
+
if (!options.http?.disable_assistants)
|
|
98
|
+
app.route("/", assistants);
|
|
99
|
+
if (!options.http?.disable_runs)
|
|
100
|
+
app.route("/", runs);
|
|
101
|
+
if (!options.http?.disable_threads)
|
|
102
|
+
app.route("/", threads);
|
|
103
|
+
if (!options.http?.disable_store)
|
|
104
|
+
app.route("/", store);
|
|
105
|
+
if (options.ui) {
|
|
72
106
|
logger.info(`Registering UI from ${options.cwd}`);
|
|
107
|
+
const { api, registerGraphUi } = await import("./ui/load.mjs");
|
|
73
108
|
await registerGraphUi(options.ui, {
|
|
74
109
|
cwd: options.cwd,
|
|
75
110
|
config: options.ui_config,
|
|
76
111
|
});
|
|
112
|
+
app.route("/", api);
|
|
77
113
|
}
|
|
114
|
+
// Loopback fetch used by webhooks
|
|
115
|
+
bindLoopbackFetch(app);
|
|
78
116
|
logger.info(`Starting ${options.nWorkers} workers`);
|
|
79
117
|
for (let i = 0; i < options.nWorkers; i++)
|
|
80
118
|
queue();
|