Skip to content

Commit 97fd586

Browse files
authored
chore(sdk): add example for React (#1690)
1 parent c046b2f commit 97fd586

31 files changed

+567
-2
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
pnpm-debug.log*
8+
lerna-debug.log*
9+
10+
node_modules
11+
dist
12+
dist-ssr
13+
*.local
14+
15+
# Editor directories and files
16+
.vscode/*
17+
!.vscode/extensions.json
18+
.idea
19+
.DS_Store
20+
*.suo
21+
*.ntvs*
22+
*.njsproj
23+
*.sln
24+
*.sw?
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
8+
<title>Vite + React + TS</title>
9+
</head>
10+
<body>
11+
<div id="root"></div>
12+
<script type="module" src="/src/client.tsx"></script>
13+
</body>
14+
</html>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"name": "ui-react-transport",
3+
"private": true,
4+
"type": "module",
5+
"scripts": {
6+
"dev": "turbo dev:client dev:server",
7+
"dev:client": "vite",
8+
"dev:server": "tsx watch --env-file=.env --clear-screen=false src/server.mts",
9+
"build:internal": "tsc -b && vite build",
10+
"format": "prettier --write src",
11+
"lint": "prettier --check src",
12+
"preview": "vite preview"
13+
},
14+
"dependencies": {
15+
"@hono/node-server": "^1.12.0",
16+
"@langchain/core": "^1.0.0-alpha",
17+
"@langchain/langgraph": "workspace:*",
18+
"@langchain/langgraph-sdk": "workspace:*",
19+
"@langchain/openai": "^0.4.4",
20+
"hono": "^4.8.2",
21+
"react": "^19.0.0",
22+
"react-dom": "^19.0.0",
23+
"zod": "^3.23.8"
24+
},
25+
"devDependencies": {
26+
"@types/react": "^19.0.8",
27+
"@types/react-dom": "^19.0.3",
28+
"@vitejs/plugin-react": "^4.4.1",
29+
"prettier": "^2.8.3",
30+
"tsx": "^4.19.3",
31+
"typescript": "^5.4.5",
32+
"vite": "^6.0.0"
33+
}
34+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { StrictMode } from "react";
2+
import { createRoot } from "react-dom/client";
3+
4+
import {
5+
useStream,
6+
FetchStreamTransport,
7+
} from "@langchain/langgraph-sdk/react";
8+
9+
export function App() {
10+
const stream = useStream({
11+
transport: new FetchStreamTransport({ apiUrl: "/api/stream" }),
12+
});
13+
14+
return (
15+
<div className="max-w-xl mx-auto">
16+
<div className="flex flex-col gap-2">
17+
{stream.messages.map((message) => (
18+
<div key={message.id} className="whitespace-pre-wrap">
19+
{message.content as string}
20+
</div>
21+
))}
22+
</div>
23+
<form
24+
className="grid grid-cols-[1fr_auto] gap-2"
25+
onSubmit={(e) => {
26+
e.preventDefault();
27+
28+
const form = e.target as HTMLFormElement;
29+
const formData = new FormData(form);
30+
const content = formData.get("content") as string;
31+
32+
form.reset();
33+
stream.submit({ messages: [{ content, type: "human" }] });
34+
}}
35+
>
36+
<textarea
37+
name="content"
38+
className="field-sizing-content"
39+
onKeyDown={(e) => {
40+
const target = e.target as HTMLTextAreaElement;
41+
42+
if (e.key === "Enter" && !e.shiftKey) {
43+
e.preventDefault();
44+
target.form?.requestSubmit();
45+
}
46+
}}
47+
/>
48+
<button type="submit">Submit</button>
49+
</form>
50+
</div>
51+
);
52+
}
53+
54+
createRoot(document.getElementById("root")!).render(
55+
<StrictMode>
56+
<App />
57+
</StrictMode>
58+
);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import type { BaseMessage } from "@langchain/core/messages";
2+
import { StateGraph, MessagesZodMeta, START } from "@langchain/langgraph";
3+
import { registry } from "@langchain/langgraph/zod";
4+
import { ChatOpenAI } from "@langchain/openai";
5+
import { z } from "zod/v4";
6+
7+
import { serve } from "@hono/node-server";
8+
import { Hono } from "hono";
9+
10+
const llm = new ChatOpenAI({ model: "gpt-4o-mini" });
11+
12+
const schema = z.object({
13+
messages: z.custom<BaseMessage[]>().register(registry, MessagesZodMeta),
14+
});
15+
16+
const graph = new StateGraph(schema)
17+
.addNode("agent", async ({ messages }) => ({
18+
messages: await llm.invoke(messages),
19+
}))
20+
.addEdge(START, "agent")
21+
.compile();
22+
23+
export type GraphType = typeof graph;
24+
25+
const app = new Hono();
26+
27+
app.post("/api/stream", async (c) => {
28+
const { input } = z.object({ input: schema }).parse(await c.req.json());
29+
30+
const stream = await graph.stream(input, {
31+
encoding: "text/event-stream",
32+
streamMode: ["values", "messages", "updates"],
33+
});
34+
35+
return new Response(stream, {
36+
headers: { "Content-Type": "text/event-stream" },
37+
});
38+
});
39+
40+
serve({ fetch: app.fetch, port: 9123 }, (c) => {
41+
console.log(`Server running at ${c.address}:${c.port}`);
42+
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/// <reference types="vite/client" />
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"compilerOptions": {
3+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
4+
"target": "ES2022",
5+
"useDefineForClassFields": true,
6+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
7+
"module": "ESNext",
8+
"skipLibCheck": true,
9+
10+
/* Bundler mode */
11+
"moduleResolution": "bundler",
12+
"allowImportingTsExtensions": true,
13+
"verbatimModuleSyntax": true,
14+
"moduleDetection": "force",
15+
"noEmit": true,
16+
"jsx": "react-jsx",
17+
18+
/* Linting */
19+
"strict": true,
20+
"noUnusedLocals": true,
21+
"noUnusedParameters": true,
22+
"erasableSyntaxOnly": true,
23+
"noFallthroughCasesInSwitch": true,
24+
"noUncheckedSideEffectImports": true
25+
},
26+
"include": ["src"]
27+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"files": [],
3+
"references": [
4+
{ "path": "./tsconfig.app.json" },
5+
{ "path": "./tsconfig.node.json" }
6+
]
7+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"compilerOptions": {
3+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
4+
"target": "ES2023",
5+
"lib": ["ES2023"],
6+
"module": "ESNext",
7+
"skipLibCheck": true,
8+
9+
/* Bundler mode */
10+
"moduleResolution": "bundler",
11+
"allowImportingTsExtensions": true,
12+
"verbatimModuleSyntax": true,
13+
"moduleDetection": "force",
14+
"noEmit": true,
15+
16+
/* Linting */
17+
"strict": true,
18+
"noUnusedLocals": true,
19+
"noUnusedParameters": true,
20+
"erasableSyntaxOnly": true,
21+
"noFallthroughCasesInSwitch": true,
22+
"noUncheckedSideEffectImports": true
23+
},
24+
"include": ["vite.config.ts"]
25+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"extends": [
3+
"//"
4+
],
5+
"tasks": {
6+
"build": {
7+
"outputs": [
8+
"**/dist/**"
9+
]
10+
},
11+
"build:internal": {
12+
"dependsOn": [
13+
"^build:internal"
14+
],
15+
"outputs": [
16+
"**/dist/**"
17+
]
18+
},
19+
"dev:client": {
20+
"cache": false,
21+
"persistent": true
22+
},
23+
"dev:server": {
24+
"cache": false,
25+
"persistent": true
26+
}
27+
}
28+
}

0 commit comments

Comments
 (0)