@langchain/langgraph-api 0.0.13 → 0.0.14-experimental.0

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.
@@ -5,6 +5,7 @@ export declare function spawnServer(args: {
5
5
  }, context: {
6
6
  config: {
7
7
  graphs: Record<string, string>;
8
+ ui?: Record<string, string>;
8
9
  };
9
10
  env: NodeJS.ProcessEnv;
10
11
  hostUrl: string;
@@ -28,6 +28,7 @@ For production use, please use LangGraph Cloud.
28
28
  nWorkers: Number.parseInt(args.nJobsPerWorker, 10),
29
29
  host: args.host,
30
30
  graphs: context.config.graphs,
31
+ ui: context.config.ui,
31
32
  cwd: options.projectCwd,
32
33
  }),
33
34
  ], {
@@ -65,7 +65,9 @@ export async function getGraphSchema(graphId) {
65
65
  GRAPH_SCHEMA[graphId] = await runGraphSchemaWorker(GRAPH_SPEC[graphId]);
66
66
  }
67
67
  catch (error) {
68
- throw new Error(`Failed to extract schema for "${graphId}": ${error}`);
68
+ throw new Error(`Failed to extract schema for "${graphId}"`, {
69
+ cause: error,
70
+ });
69
71
  }
70
72
  }
71
73
  return GRAPH_SCHEMA[graphId];
package/dist/server.mjs CHANGED
@@ -6,6 +6,7 @@ import runs from "./api/runs.mjs";
6
6
  import threads from "./api/threads.mjs";
7
7
  import assistants from "./api/assistants.mjs";
8
8
  import store from "./api/store.mjs";
9
+ import ui, { registerGraphUi } from "./ui/load.mjs";
9
10
  import { truncate, conn as opsConn } from "./storage/ops.mjs";
10
11
  import { zValidator } from "@hono/zod-validator";
11
12
  import { z } from "zod";
@@ -31,6 +32,7 @@ app.route("/", assistants);
31
32
  app.route("/", runs);
32
33
  app.route("/", threads);
33
34
  app.route("/", store);
35
+ app.route("/", ui);
34
36
  app.get("/info", (c) => c.json({ flags: { assistants: true, crons: false } }));
35
37
  app.post("/internal/truncate", zValidator("json", z.object({
36
38
  runs: z.boolean().optional(),
@@ -49,6 +51,7 @@ export const StartServerSchema = z.object({
49
51
  host: z.string(),
50
52
  cwd: z.string(),
51
53
  graphs: z.record(z.string()),
54
+ ui: z.record(z.string()).optional(),
52
55
  });
53
56
  export async function startServer(options) {
54
57
  logger.info(`Initializing storage...`);
@@ -63,6 +66,10 @@ export async function startServer(options) {
63
66
  };
64
67
  logger.info(`Registering graphs from ${options.cwd}`);
65
68
  await registerFromEnv(options.graphs, { cwd: options.cwd });
69
+ if (options.ui) {
70
+ logger.info(`Registering UI from ${options.cwd}`);
71
+ await registerGraphUi(options.ui, { cwd: options.cwd });
72
+ }
66
73
  logger.info(`Starting ${options.nWorkers} workers`);
67
74
  for (let i = 0; i < options.nWorkers; i++)
68
75
  queue();
@@ -0,0 +1,104 @@
1
+ import * as path from "node:path";
2
+ import * as url from "node:url";
3
+ import * as fs from "node:fs";
4
+ import { z } from "zod";
5
+ import { Hono } from "hono";
6
+ import { getMimeType } from "hono/utils/mime";
7
+ import { zValidator } from "@hono/zod-validator";
8
+ import { context } from "esbuild";
9
+ import tailwind from "esbuild-plugin-tailwindcss";
10
+ const GRAPH_UI = {};
11
+ export async function registerGraphUi(defs, options) {
12
+ const textEncoder = new TextEncoder();
13
+ const renderTemplate = await fs.promises.readFile(path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), "./render.mts"), "utf-8");
14
+ const projectDir = options.cwd;
15
+ const result = await Promise.all(Object.entries(defs).map(async ([agentName, uiUserPath]) => {
16
+ const ctx = await context({
17
+ entryPoints: ["entrypoint"],
18
+ outdir: path.resolve(projectDir, "dist"),
19
+ bundle: true,
20
+ platform: "browser",
21
+ target: "es2020",
22
+ jsx: "automatic",
23
+ external: ["react", "react-dom", "@langchain/langgraph-sdk"],
24
+ plugins: [
25
+ {
26
+ name: "entrypoint",
27
+ setup(build) {
28
+ build.onResolve({ filter: /^entrypoint$/ }, (args) => ({
29
+ path: path.resolve(projectDir, "ui.entrypoint.tsx"),
30
+ namespace: "entrypoint-ns",
31
+ }));
32
+ build.onLoad({ filter: /.*/, namespace: "entrypoint-ns" }, () => ({
33
+ resolveDir: projectDir,
34
+ contents: [
35
+ `import ui from "${uiUserPath}"`,
36
+ renderTemplate,
37
+ `export const render = createRenderer(ui)`,
38
+ ].join("\n"),
39
+ loader: "tsx",
40
+ }));
41
+ },
42
+ },
43
+ tailwind(),
44
+ {
45
+ name: "require-transform",
46
+ setup(build) {
47
+ build.onEnd(async (result) => {
48
+ const newResult = [];
49
+ for (const item of result.outputFiles ?? []) {
50
+ let basename = path.basename(item.path);
51
+ let contents = item.contents;
52
+ if (basename === "entrypoint.js") {
53
+ contents = textEncoder.encode(item.text.replaceAll(`typeof require !== "undefined" ? require`, `typeof globalThis[Symbol.for("LGUI_REQUIRE")] !== "undefined" ? globalThis[Symbol.for("LGUI_REQUIRE")]`));
54
+ }
55
+ newResult.push({ basename, contents });
56
+ }
57
+ if (newResult.length > 0) {
58
+ GRAPH_UI[agentName] = newResult;
59
+ }
60
+ });
61
+ },
62
+ },
63
+ ],
64
+ write: false,
65
+ globalName: `__LGUI_${agentName}`,
66
+ });
67
+ await ctx.watch();
68
+ return [agentName, ctx];
69
+ }));
70
+ return Object.fromEntries(result);
71
+ }
72
+ const api = new Hono();
73
+ api.post("/ui/:agent", zValidator("json", z.object({ name: z.string(), shadowRootId: z.string() })), async (c) => {
74
+ const agent = c.req.param("agent");
75
+ const host = c.req.header("host");
76
+ const body = await c.req.valid("json");
77
+ const files = GRAPH_UI[agent];
78
+ if (!files?.length)
79
+ return c.text(`UI not found for agent "${agent}"`, 404);
80
+ const strName = JSON.stringify(body.name);
81
+ const strRootId = JSON.stringify(body.shadowRootId);
82
+ const result = [];
83
+ for (const css of files.filter((i) => path.extname(i.basename) === ".css")) {
84
+ result.push(`<link rel="stylesheet" href="http://${host}/ui/${agent}/${css.basename}" />`);
85
+ }
86
+ const js = files.find((i) => path.extname(i.basename) === ".js");
87
+ if (js) {
88
+ result.push(`<script src="http://${host}/ui/${agent}/${js.basename}" onload='__LGUI_${agent}.render(${strName}, ${strRootId})'></script>`);
89
+ }
90
+ return c.text(result.join("\n"), {
91
+ headers: { "Content-Type": "text/html" },
92
+ });
93
+ });
94
+ api.get("/ui/:agent/:basename", async (c) => {
95
+ const agent = c.req.param("agent");
96
+ const basename = c.req.param("basename");
97
+ const file = GRAPH_UI[agent]?.find((item) => item.basename === basename);
98
+ if (!file)
99
+ return c.text("File not found", 404);
100
+ return c.body(file.contents, {
101
+ headers: { "Content-Type": getMimeType(file.basename) ?? "text/plain" },
102
+ });
103
+ });
104
+ export default api;
@@ -0,0 +1,27 @@
1
+ import type { ComponentClass, FunctionComponent } from "react";
2
+
3
+ const STORE_SYMBOL = Symbol.for("LGUI_EXT_STORE");
4
+
5
+ declare global {
6
+ interface Window {
7
+ [STORE_SYMBOL]: {
8
+ respond: (
9
+ shadowRootId: string,
10
+ component: FunctionComponent | ComponentClass,
11
+ renderEl: HTMLElement,
12
+ ) => void;
13
+ };
14
+ }
15
+ }
16
+
17
+ // @ts-ignore
18
+ function createRenderer(
19
+ components: Record<string, FunctionComponent | ComponentClass>,
20
+ ) {
21
+ return (name: string, shadowRootId: string) => {
22
+ const root = document.getElementById(shadowRootId)!.shadowRoot;
23
+ const renderEl = document.createElement("div");
24
+ root!.appendChild(renderEl);
25
+ window[STORE_SYMBOL].respond(shadowRootId, components[name], renderEl);
26
+ };
27
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/langgraph-api",
3
- "version": "0.0.13",
3
+ "version": "0.0.14-experimental.0",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": "^18.19.0 || >=20.16.0"
@@ -29,6 +29,8 @@
29
29
  "stacktrace-parser": "^0.1.10",
30
30
  "superjson": "^2.2.2",
31
31
  "tsx": "^4.19.2",
32
+ "esbuild": "^0.23.1",
33
+ "esbuild-plugin-tailwindcss": "^2.0.1",
32
34
  "uuid": "^10.0.0",
33
35
  "winston": "^3.17.0",
34
36
  "winston-console-format": "^1.0.8",
@@ -43,6 +45,8 @@
43
45
  "devDependencies": {
44
46
  "@langchain/langgraph-sdk": "^0.0.33",
45
47
  "@types/babel__code-frame": "^7.0.6",
48
+ "@types/react": "^19.0.8",
49
+ "@types/react-dom": "^19.0.3",
46
50
  "@types/node": "^22.2.0",
47
51
  "@types/uuid": "^10.0.0",
48
52
  "postgres": "^3.4.5",