@langgraph-js/pure-graph 1.0.2 → 1.3.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.
- package/.prettierrc +11 -0
- package/README.md +104 -10
- package/bun.lock +209 -0
- package/dist/adapter/hono/assistants.js +3 -9
- package/dist/adapter/hono/endpoint.js +1 -2
- package/dist/adapter/hono/runs.js +23 -39
- package/dist/adapter/hono/threads.js +5 -46
- package/dist/adapter/nextjs/endpoint.d.ts +1 -0
- package/dist/adapter/nextjs/endpoint.js +2 -0
- package/dist/adapter/nextjs/index.d.ts +1 -0
- package/dist/adapter/nextjs/index.js +2 -0
- package/dist/adapter/nextjs/router.d.ts +5 -0
- package/dist/adapter/nextjs/router.js +179 -0
- package/dist/adapter/nextjs/zod.d.ts +203 -0
- package/dist/adapter/nextjs/zod.js +60 -0
- package/dist/adapter/zod.d.ts +584 -0
- package/dist/adapter/zod.js +127 -0
- package/dist/createEndpoint.d.ts +1 -2
- package/dist/createEndpoint.js +4 -3
- package/dist/global.d.ts +6 -4
- package/dist/global.js +10 -5
- package/dist/graph/stream.d.ts +1 -1
- package/dist/graph/stream.js +18 -10
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/queue/stream_queue.d.ts +5 -3
- package/dist/queue/stream_queue.js +4 -2
- package/dist/storage/index.d.ts +9 -4
- package/dist/storage/index.js +38 -3
- package/dist/storage/memory/threads.d.ts +3 -2
- package/dist/storage/memory/threads.js +29 -2
- package/dist/storage/redis/queue.d.ts +39 -0
- package/dist/storage/redis/queue.js +130 -0
- package/dist/storage/sqlite/DB.d.ts +3 -0
- package/dist/storage/sqlite/DB.js +14 -0
- package/dist/storage/sqlite/checkpoint.d.ts +18 -0
- package/dist/storage/sqlite/checkpoint.js +374 -0
- package/dist/storage/sqlite/threads.d.ts +44 -0
- package/dist/storage/sqlite/threads.js +300 -0
- package/dist/storage/sqlite/type.d.ts +15 -0
- package/dist/storage/sqlite/type.js +1 -0
- package/dist/threads/index.d.ts +3 -2
- package/dist/threads/index.js +1 -26
- package/dist/types.d.ts +2 -1
- package/dist/utils/createEntrypointGraph.d.ts +14 -0
- package/dist/utils/createEntrypointGraph.js +11 -0
- package/dist/utils/getGraph.js +3 -3
- package/examples/nextjs/README.md +36 -0
- package/examples/nextjs/app/api/langgraph/[...path]/route.ts +10 -0
- package/examples/nextjs/app/favicon.ico +0 -0
- package/examples/nextjs/app/globals.css +26 -0
- package/examples/nextjs/app/layout.tsx +34 -0
- package/examples/nextjs/app/page.tsx +211 -0
- package/examples/nextjs/next.config.ts +26 -0
- package/examples/nextjs/package.json +24 -0
- package/examples/nextjs/postcss.config.mjs +5 -0
- package/examples/nextjs/tsconfig.json +27 -0
- package/package.json +9 -4
- package/packages/agent-graph/demo.json +35 -0
- package/packages/agent-graph/package.json +18 -0
- package/packages/agent-graph/src/index.ts +47 -0
- package/packages/agent-graph/src/tools/tavily.ts +9 -0
- package/packages/agent-graph/src/tools.ts +38 -0
- package/packages/agent-graph/src/types.ts +42 -0
- package/pnpm-workspace.yaml +4 -0
- package/src/adapter/hono/assistants.ts +16 -33
- package/src/adapter/hono/endpoint.ts +1 -2
- package/src/adapter/hono/runs.ts +42 -51
- package/src/adapter/hono/threads.ts +15 -70
- package/src/adapter/nextjs/endpoint.ts +2 -0
- package/src/adapter/nextjs/index.ts +2 -0
- package/src/adapter/nextjs/router.ts +206 -0
- package/src/adapter/{hono → nextjs}/zod.ts +22 -5
- package/src/adapter/zod.ts +144 -0
- package/src/createEndpoint.ts +12 -5
- package/src/e.d.ts +3 -0
- package/src/global.ts +11 -6
- package/src/graph/stream.ts +20 -10
- package/src/index.ts +1 -0
- package/src/queue/stream_queue.ts +6 -5
- package/src/storage/index.ts +42 -4
- package/src/storage/memory/threads.ts +30 -1
- package/src/storage/redis/queue.ts +148 -0
- package/src/storage/sqlite/DB.ts +16 -0
- package/src/storage/sqlite/checkpoint.ts +502 -0
- package/src/storage/sqlite/threads.ts +405 -0
- package/src/storage/sqlite/type.ts +12 -0
- package/src/threads/index.ts +11 -25
- package/src/types.ts +2 -0
- package/src/utils/createEntrypointGraph.ts +20 -0
- package/src/utils/getGraph.ts +3 -3
- package/test/graph/entrypoint.ts +21 -0
- package/test/graph/index.ts +45 -6
- package/test/hono.ts +5 -0
- package/test/test.ts +0 -10
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
|
|
5
|
+
export default function Home() {
|
|
6
|
+
const [result, setResult] = useState<string>("");
|
|
7
|
+
const [loading, setLoading] = useState(false);
|
|
8
|
+
|
|
9
|
+
const testAPI = async (
|
|
10
|
+
endpoint: string,
|
|
11
|
+
method: string = "GET",
|
|
12
|
+
body?: any
|
|
13
|
+
) => {
|
|
14
|
+
setLoading(true);
|
|
15
|
+
setResult("");
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
const options: RequestInit = {
|
|
19
|
+
method,
|
|
20
|
+
headers: {
|
|
21
|
+
"Content-Type": "application/json",
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
if (body && method !== "GET") {
|
|
26
|
+
options.body = JSON.stringify(body);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const response = await fetch(`/api${endpoint}`, options);
|
|
30
|
+
const data = await response.text();
|
|
31
|
+
|
|
32
|
+
setResult(`状态: ${response.status}\n响应: ${data}`);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
setResult(`错误: ${error}`);
|
|
35
|
+
} finally {
|
|
36
|
+
setLoading(false);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const testStream = async () => {
|
|
41
|
+
setLoading(true);
|
|
42
|
+
setResult("");
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
// 先创建一个线程
|
|
46
|
+
const threadResponse = await fetch("/api/threads", {
|
|
47
|
+
method: "POST",
|
|
48
|
+
headers: { "Content-Type": "application/json" },
|
|
49
|
+
body: JSON.stringify({}),
|
|
50
|
+
});
|
|
51
|
+
const thread = await threadResponse.json();
|
|
52
|
+
|
|
53
|
+
// 测试流式接口
|
|
54
|
+
const response = await fetch(
|
|
55
|
+
`/api/threads/${thread.thread_id}/runs/stream`,
|
|
56
|
+
{
|
|
57
|
+
method: "POST",
|
|
58
|
+
headers: { "Content-Type": "application/json" },
|
|
59
|
+
body: JSON.stringify({
|
|
60
|
+
assistant_id: "test-assistant",
|
|
61
|
+
input: { message: "Hello" },
|
|
62
|
+
}),
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
if (response.body) {
|
|
67
|
+
const reader = response.body.getReader();
|
|
68
|
+
const decoder = new TextDecoder();
|
|
69
|
+
let result = "流式响应:\n";
|
|
70
|
+
|
|
71
|
+
while (true) {
|
|
72
|
+
const { done, value } = await reader.read();
|
|
73
|
+
if (done) break;
|
|
74
|
+
|
|
75
|
+
const chunk = decoder.decode(value);
|
|
76
|
+
result += chunk;
|
|
77
|
+
setResult(result);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
} catch (error) {
|
|
81
|
+
setResult(`错误: ${error}`);
|
|
82
|
+
} finally {
|
|
83
|
+
setLoading(false);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<div className="font-sans min-h-screen p-8">
|
|
89
|
+
<main className="max-w-4xl mx-auto">
|
|
90
|
+
<h1 className="text-3xl font-bold mb-8">
|
|
91
|
+
Pure Graph Next.js 统一路由适配器演示
|
|
92
|
+
</h1>
|
|
93
|
+
|
|
94
|
+
<div className="grid gap-6">
|
|
95
|
+
<div className="border rounded-lg p-6">
|
|
96
|
+
<h2 className="text-xl font-semibold mb-4">API 测试</h2>
|
|
97
|
+
<div className="grid gap-4">
|
|
98
|
+
<div className="flex gap-2">
|
|
99
|
+
<button
|
|
100
|
+
onClick={() =>
|
|
101
|
+
testAPI("/assistants/search", "POST", {
|
|
102
|
+
limit: 10,
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
disabled={loading}
|
|
106
|
+
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 disabled:opacity-50">
|
|
107
|
+
搜索助手
|
|
108
|
+
</button>
|
|
109
|
+
<button
|
|
110
|
+
onClick={() =>
|
|
111
|
+
testAPI("/threads", "POST", {})
|
|
112
|
+
}
|
|
113
|
+
disabled={loading}
|
|
114
|
+
className="px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600 disabled:opacity-50">
|
|
115
|
+
创建线程
|
|
116
|
+
</button>
|
|
117
|
+
<button
|
|
118
|
+
onClick={() =>
|
|
119
|
+
testAPI("/threads/search", "POST", {
|
|
120
|
+
limit: 5,
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
disabled={loading}
|
|
124
|
+
className="px-4 py-2 bg-purple-500 text-white rounded hover:bg-purple-600 disabled:opacity-50">
|
|
125
|
+
搜索线程
|
|
126
|
+
</button>
|
|
127
|
+
</div>
|
|
128
|
+
<button
|
|
129
|
+
onClick={testStream}
|
|
130
|
+
disabled={loading}
|
|
131
|
+
className="px-4 py-2 bg-orange-500 text-white rounded hover:bg-orange-600 disabled:opacity-50">
|
|
132
|
+
测试流式接口 (SSE)
|
|
133
|
+
</button>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
<div className="border rounded-lg p-6">
|
|
138
|
+
<h2 className="text-xl font-semibold mb-4">API 响应</h2>
|
|
139
|
+
<pre className="bg-gray-100 dark:bg-gray-800 p-4 rounded text-sm overflow-auto max-h-96 whitespace-pre-wrap">
|
|
140
|
+
{loading
|
|
141
|
+
? "加载中..."
|
|
142
|
+
: result || "点击上方按钮测试 API"}
|
|
143
|
+
</pre>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<div className="border rounded-lg p-6">
|
|
147
|
+
<h2 className="text-xl font-semibold mb-4">
|
|
148
|
+
统一路由 API 端点 (所有请求都通过 /api/[...path])
|
|
149
|
+
</h2>
|
|
150
|
+
<div className="grid gap-2 text-sm">
|
|
151
|
+
<div>
|
|
152
|
+
<code className="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded">
|
|
153
|
+
POST /api/assistants/search
|
|
154
|
+
</code>{" "}
|
|
155
|
+
- 搜索助手
|
|
156
|
+
</div>
|
|
157
|
+
<div>
|
|
158
|
+
<code className="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded">
|
|
159
|
+
GET /api/assistants/[id]/graph
|
|
160
|
+
</code>{" "}
|
|
161
|
+
- 获取助手图谱
|
|
162
|
+
</div>
|
|
163
|
+
<div>
|
|
164
|
+
<code className="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded">
|
|
165
|
+
POST /api/threads
|
|
166
|
+
</code>{" "}
|
|
167
|
+
- 创建线程
|
|
168
|
+
</div>
|
|
169
|
+
<div>
|
|
170
|
+
<code className="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded">
|
|
171
|
+
POST /api/threads/search
|
|
172
|
+
</code>{" "}
|
|
173
|
+
- 搜索线程
|
|
174
|
+
</div>
|
|
175
|
+
<div>
|
|
176
|
+
<code className="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded">
|
|
177
|
+
GET /api/threads/[id]
|
|
178
|
+
</code>{" "}
|
|
179
|
+
- 获取线程
|
|
180
|
+
</div>
|
|
181
|
+
<div>
|
|
182
|
+
<code className="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded">
|
|
183
|
+
DELETE /api/threads/[id]
|
|
184
|
+
</code>{" "}
|
|
185
|
+
- 删除线程
|
|
186
|
+
</div>
|
|
187
|
+
<div>
|
|
188
|
+
<code className="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded">
|
|
189
|
+
GET /api/threads/[id]/runs
|
|
190
|
+
</code>{" "}
|
|
191
|
+
- 获取运行列表
|
|
192
|
+
</div>
|
|
193
|
+
<div>
|
|
194
|
+
<code className="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded">
|
|
195
|
+
POST /api/threads/[id]/runs/stream
|
|
196
|
+
</code>{" "}
|
|
197
|
+
- 流式运行 (SSE)
|
|
198
|
+
</div>
|
|
199
|
+
<div>
|
|
200
|
+
<code className="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded">
|
|
201
|
+
POST /api/threads/[id]/runs/[run_id]/cancel
|
|
202
|
+
</code>{" "}
|
|
203
|
+
- 取消运行
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
</div>
|
|
208
|
+
</main>
|
|
209
|
+
</div>
|
|
210
|
+
);
|
|
211
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { NextConfig } from "next";
|
|
2
|
+
|
|
3
|
+
const nextConfig: NextConfig = {
|
|
4
|
+
async headers() {
|
|
5
|
+
return [
|
|
6
|
+
{
|
|
7
|
+
// matching all API routes
|
|
8
|
+
source: "/api/:path*",
|
|
9
|
+
headers: [
|
|
10
|
+
{ key: "Access-Control-Allow-Credentials", value: "true" },
|
|
11
|
+
{ key: "Access-Control-Allow-Origin", value: "*" }, // replace this your actual origin
|
|
12
|
+
{
|
|
13
|
+
key: "Access-Control-Allow-Methods",
|
|
14
|
+
value: "GET,DELETE,PATCH,POST,PUT",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
key: "Access-Control-Allow-Headers",
|
|
18
|
+
value: "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, Authorization",
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default nextConfig;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nextjs",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "next dev --turbopack",
|
|
7
|
+
"build": "next build --turbopack",
|
|
8
|
+
"start": "next start"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@langgraph-js/pure-graph": "workspace:^*",
|
|
12
|
+
"next": "15.5.4",
|
|
13
|
+
"react": "19.1.0",
|
|
14
|
+
"react-dom": "19.1.0"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@tailwindcss/postcss": "^4",
|
|
18
|
+
"@types/node": "^20",
|
|
19
|
+
"@types/react": "^19",
|
|
20
|
+
"@types/react-dom": "^19",
|
|
21
|
+
"tailwindcss": "^4",
|
|
22
|
+
"typescript": "^5"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2017",
|
|
4
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"module": "esnext",
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"jsx": "preserve",
|
|
15
|
+
"incremental": true,
|
|
16
|
+
"plugins": [
|
|
17
|
+
{
|
|
18
|
+
"name": "next"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"paths": {
|
|
22
|
+
"@/*": ["./*"]
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
26
|
+
"exclude": ["node_modules"]
|
|
27
|
+
}
|
package/package.json
CHANGED
|
@@ -1,32 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@langgraph-js/pure-graph",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"keywords": [],
|
|
7
7
|
"author": "",
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@langchain/core": "^0.3.
|
|
10
|
+
"@langchain/core": "^0.3.78",
|
|
11
11
|
"@langchain/langgraph": "^0.4.9",
|
|
12
12
|
"@langchain/langgraph-checkpoint": "^0.1.1",
|
|
13
|
+
"@langchain/langgraph-checkpoint-sqlite": "^0.2.1",
|
|
13
14
|
"@langgraph-js/pro": "^1.8.1",
|
|
15
|
+
"better-sqlite3": "^12.4.1",
|
|
14
16
|
"eventemitter3": "^5.0.1",
|
|
15
17
|
"zod": "^3"
|
|
16
18
|
},
|
|
17
19
|
"optionalDependencies": {
|
|
20
|
+
"@langchain/langgraph-checkpoint-redis": "^0.0.2",
|
|
18
21
|
"@hono/zod-validator": "^0.7.3",
|
|
19
|
-
"hono": "^4.9.9"
|
|
22
|
+
"hono": "^4.9.9",
|
|
23
|
+
"redis": "^5.8.3"
|
|
20
24
|
},
|
|
21
25
|
"devDependencies": {
|
|
22
26
|
"@langchain/langgraph-sdk": "^0.1.7",
|
|
27
|
+
"@langchain/openai": "^0.6.14",
|
|
23
28
|
"@langgraph-js/sdk": "^2.0.1",
|
|
24
29
|
"@types/node": "^22.13.5",
|
|
25
30
|
"typescript": "^5.9.2"
|
|
26
31
|
},
|
|
27
32
|
"scripts": {
|
|
28
33
|
"dev": "bun run test/hono.ts",
|
|
29
|
-
"build": "
|
|
34
|
+
"build": " tsc",
|
|
30
35
|
"prepublish": "pnpm build"
|
|
31
36
|
}
|
|
32
37
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"agent_protocol": {
|
|
3
|
+
"id": "novel-writing-assistant",
|
|
4
|
+
"protocolVersion": "1.0.0",
|
|
5
|
+
"name": "小说创作助手",
|
|
6
|
+
"description": "专业的AI小说创作助手,提供情节构思、人物塑造、世界观构建和写作指导的全方位服务",
|
|
7
|
+
"url": "https://api.novelai.net/ai/generate",
|
|
8
|
+
"iconUrl": "https://img.icons8.com/fluency/96/000000/book.png",
|
|
9
|
+
"version": "2.0.1",
|
|
10
|
+
"documentationUrl": "https://docs.novelai.net/",
|
|
11
|
+
"llm": [
|
|
12
|
+
{
|
|
13
|
+
"provider": "openai",
|
|
14
|
+
"model": "gpt-4o-mini"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"provider": "openai",
|
|
18
|
+
"model": "qwen-plus"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"systemPrompt": "你是一位经验丰富的小说家和创意写作导师,精通各种文学体裁和叙事技巧。你善于分析经典作品的结构,帮助创作者构建引人入胜的情节、塑造立体的人物形象,并提供专业的写作建议和灵感启发。",
|
|
22
|
+
"tools": [
|
|
23
|
+
{
|
|
24
|
+
"tool_type": "mcp",
|
|
25
|
+
"url": "https://mcp.exa.ai/mcp?exaApiKey=ce1249b7-6b78-4ec8-8be9-41823b5d2365",
|
|
26
|
+
"headers": {}
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"tool_type": "builtin",
|
|
30
|
+
"name": "tavily_search"
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"subAgents": []
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "packages",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [],
|
|
10
|
+
"author": "",
|
|
11
|
+
"license": "ISC",
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@langchain/mcp-adapters": "^0.6.0",
|
|
14
|
+
"@langchain/openai": "^0.6.14",
|
|
15
|
+
"@langchain/tavily": "^0.1.5",
|
|
16
|
+
"@langgraph-js/pure-graph": "workspace:^*"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Annotation, entrypoint } from '@langchain/langgraph';
|
|
2
|
+
import { createState } from '@langgraph-js/pro';
|
|
3
|
+
import { AgentProtocol } from './types';
|
|
4
|
+
import { createReactAgent, createReactAgentAnnotation } from '@langchain/langgraph/prebuilt';
|
|
5
|
+
import { ChatOpenAI } from '@langchain/openai';
|
|
6
|
+
import { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
7
|
+
import { createTools } from './tools';
|
|
8
|
+
import { createEntrypointGraph } from '@langgraph-js/pure-graph';
|
|
9
|
+
const AgentProtocolState = createState().build({
|
|
10
|
+
agent_protocol: Annotation<AgentProtocol>(),
|
|
11
|
+
model_name: Annotation<string>(),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const AgentGraphState = createState(AgentProtocolState, createReactAgentAnnotation()).build({});
|
|
15
|
+
|
|
16
|
+
export const createLLM = async (protocol: AgentProtocol, model_name?: string): Promise<BaseChatModel> => {
|
|
17
|
+
if (!model_name) {
|
|
18
|
+
model_name = protocol.llm[0].model;
|
|
19
|
+
} else {
|
|
20
|
+
const llm = protocol.llm.find((i) => i.model === model_name);
|
|
21
|
+
if (!llm) {
|
|
22
|
+
throw new Error(`Model ${model_name} not found`);
|
|
23
|
+
}
|
|
24
|
+
model_name = llm.model;
|
|
25
|
+
}
|
|
26
|
+
return new ChatOpenAI({
|
|
27
|
+
model: model_name,
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const graph = createEntrypointGraph({
|
|
32
|
+
stateSchema: AgentGraphState,
|
|
33
|
+
graph: entrypoint({ name: 'agent-graph' }, async (state: typeof AgentGraphState.State) => {
|
|
34
|
+
const protocol = state.agent_protocol;
|
|
35
|
+
|
|
36
|
+
const tools = await createTools(protocol);
|
|
37
|
+
|
|
38
|
+
const agent = createReactAgent({
|
|
39
|
+
llm: await createLLM(protocol, state.model_name),
|
|
40
|
+
tools,
|
|
41
|
+
prompt: protocol.systemPrompt,
|
|
42
|
+
stateSchema: AgentGraphState,
|
|
43
|
+
});
|
|
44
|
+
const response = await agent.invoke(state);
|
|
45
|
+
return response;
|
|
46
|
+
}),
|
|
47
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { TavilyExtract, TavilySearch } from '@langchain/tavily';
|
|
2
|
+
export const tavily_search = new TavilySearch({
|
|
3
|
+
maxResults: 5,
|
|
4
|
+
apiBaseUrl: process.env.TAVILY_HOST,
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
export const tavily_extract = new TavilyExtract({
|
|
8
|
+
apiBaseUrl: process.env.TAVILY_HOST,
|
|
9
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { AgentProtocol } from './types';
|
|
2
|
+
import { StructuredTool } from '@langchain/core/tools';
|
|
3
|
+
import { MultiServerMCPClient } from '@langchain/mcp-adapters';
|
|
4
|
+
import * as tavily from './tools/tavily';
|
|
5
|
+
const prebuiltTools: Record<string, StructuredTool> = {
|
|
6
|
+
...tavily,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const createPrebuiltTools = async (protocol: AgentProtocol): Promise<StructuredTool[]> => {
|
|
10
|
+
const PrebuiltConfigs = protocol.tools.filter((i) => i.tool_type === 'builtin');
|
|
11
|
+
if (PrebuiltConfigs.length === 0) {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
return PrebuiltConfigs.map((i) => prebuiltTools[i.name]);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const createMCPTools = async (protocol: AgentProtocol): Promise<StructuredTool[]> => {
|
|
18
|
+
const MCPConfigs = protocol.tools.filter((i) => i.tool_type === 'mcp');
|
|
19
|
+
if (MCPConfigs.length === 0) {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
const client = new MultiServerMCPClient({
|
|
23
|
+
mcpServers: Object.fromEntries(
|
|
24
|
+
MCPConfigs.map((i) => [
|
|
25
|
+
i.name,
|
|
26
|
+
{
|
|
27
|
+
url: i.url,
|
|
28
|
+
headers: i.headers,
|
|
29
|
+
},
|
|
30
|
+
]),
|
|
31
|
+
),
|
|
32
|
+
});
|
|
33
|
+
return client.getTools();
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const createTools = async (protocol: AgentProtocol): Promise<StructuredTool[]> => {
|
|
37
|
+
return [...(await createMCPTools(protocol)), ...(await createPrebuiltTools(protocol))];
|
|
38
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export interface AgentTool {
|
|
2
|
+
name: string;
|
|
3
|
+
tool_type: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface MCPTool extends AgentTool {
|
|
8
|
+
tool_type: 'mcp';
|
|
9
|
+
type: string;
|
|
10
|
+
url: string;
|
|
11
|
+
headers?: Record<string, string>;
|
|
12
|
+
}
|
|
13
|
+
export interface BuiltinTool extends AgentTool {
|
|
14
|
+
tool_type: 'builtin';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface InnerTool extends AgentTool {
|
|
18
|
+
tool_type: 'inner';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface SubAgent {
|
|
22
|
+
protocolId: string;
|
|
23
|
+
protocol?: AgentProtocol;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface AgentProtocol {
|
|
27
|
+
id: string;
|
|
28
|
+
protocolVersion: string;
|
|
29
|
+
name: string;
|
|
30
|
+
description: string;
|
|
31
|
+
url: string;
|
|
32
|
+
iconUrl?: string;
|
|
33
|
+
version: string;
|
|
34
|
+
documentationUrl?: string;
|
|
35
|
+
llm: {
|
|
36
|
+
provider?: string;
|
|
37
|
+
model: string;
|
|
38
|
+
}[];
|
|
39
|
+
systemPrompt: string;
|
|
40
|
+
tools: (BuiltinTool | InnerTool | MCPTool)[];
|
|
41
|
+
subAgents: SubAgent[];
|
|
42
|
+
}
|
|
@@ -1,41 +1,24 @@
|
|
|
1
1
|
import { zValidator } from '@hono/zod-validator';
|
|
2
2
|
import { Hono } from 'hono';
|
|
3
|
-
import { z } from 'zod';
|
|
4
3
|
import { client } from './endpoint';
|
|
5
|
-
import {
|
|
4
|
+
import { AssistantsSearchSchema, AssistantGraphQuerySchema } from '../zod';
|
|
6
5
|
const api = new Hono();
|
|
7
6
|
|
|
8
|
-
api.post(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
offset: z.number().int().optional(),
|
|
17
|
-
}),
|
|
18
|
-
),
|
|
19
|
-
async (c) => {
|
|
20
|
-
// Search Assistants
|
|
21
|
-
const payload = c.req.valid('json');
|
|
22
|
-
let total = 0;
|
|
23
|
-
const data = await client.assistants.search(payload);
|
|
24
|
-
c.res.headers.set('X-Pagination-Total', total.toString());
|
|
25
|
-
return c.json(data);
|
|
26
|
-
},
|
|
27
|
-
);
|
|
7
|
+
api.post('/assistants/search', zValidator('json', AssistantsSearchSchema), async (c) => {
|
|
8
|
+
// Search Assistants
|
|
9
|
+
const payload = c.req.valid('json');
|
|
10
|
+
let total = 0;
|
|
11
|
+
const data = await client.assistants.search(payload);
|
|
12
|
+
c.res.headers.set('X-Pagination-Total', total.toString());
|
|
13
|
+
return c.json(data);
|
|
14
|
+
});
|
|
28
15
|
|
|
29
|
-
api.get(
|
|
30
|
-
'
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
});
|
|
37
|
-
return c.json(data);
|
|
38
|
-
},
|
|
39
|
-
);
|
|
16
|
+
api.get('/assistants/:assistant_id/graph', zValidator('query', AssistantGraphQuerySchema), async (c) => {
|
|
17
|
+
const xray = c.req.valid('query').xray;
|
|
18
|
+
const data = await client.assistants.getGraph(c.req.param('assistant_id'), {
|
|
19
|
+
xray: xray !== undefined ? xray === 'true' : undefined,
|
|
20
|
+
});
|
|
21
|
+
return c.json(data);
|
|
22
|
+
});
|
|
40
23
|
|
|
41
24
|
export default api;
|