@templmf/temp-solf-lmf 0.0.136 → 0.0.137
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/.tiktoken_cache/gpt2.json +1 -0
- package/demo.js +77 -0
- package/package.json +1 -1
- package/server.js +0 -58
package/demo.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
|
|
2
|
+
// server.js
|
|
3
|
+
process.env.TIKTOKEN_CACHE_DIR = "./tiktoken_cache"; // 必须第一行
|
|
4
|
+
|
|
5
|
+
import { tool } from "@langchain/core/tools";
|
|
6
|
+
import { ToolNode } from "@langchain/langgraph/prebuilt";
|
|
7
|
+
import { ChatOpenAI } from "@langchain/openai";
|
|
8
|
+
import { StateGraph, MessagesAnnotation } from "@langchain/langgraph";
|
|
9
|
+
import { HumanMessage, AIMessage } from "@langchain/core/messages";
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
import express from "express";
|
|
12
|
+
import cors from "cors";
|
|
13
|
+
|
|
14
|
+
const app = express();
|
|
15
|
+
app.use(cors());
|
|
16
|
+
app.use(express.json());
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
// ---- LangGraph 图 ----
|
|
21
|
+
const tools = [];
|
|
22
|
+
const toolNode = new ToolNode(tools);
|
|
23
|
+
|
|
24
|
+
const llm = new ChatOpenAI({
|
|
25
|
+
model: "gpt-4o",
|
|
26
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
27
|
+
streaming: true,
|
|
28
|
+
}).bindTools(tools);
|
|
29
|
+
|
|
30
|
+
async function callLLM(state) {
|
|
31
|
+
const response = await llm.invoke(state.messages);
|
|
32
|
+
return { messages: [response] };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function shouldUseTool(state) {
|
|
36
|
+
const lastMsg = state.messages.at(-1);
|
|
37
|
+
return lastMsg.tool_calls?.length > 0 ? "call_tools" : "__end__";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const graph = new StateGraph(MessagesAnnotation)
|
|
41
|
+
.addNode("llm", callLLM)
|
|
42
|
+
.addNode("call_tools", toolNode)
|
|
43
|
+
.addEdge("__start__", "llm")
|
|
44
|
+
.addConditionalEdges("llm", shouldUseTool)
|
|
45
|
+
.addEdge("call_tools", "llm")
|
|
46
|
+
.compile();
|
|
47
|
+
|
|
48
|
+
// ---- SSE 接口 ----
|
|
49
|
+
app.post("/chat/stream", async (req, res) => {
|
|
50
|
+
const { messages } = req.body;
|
|
51
|
+
|
|
52
|
+
res.setHeader("Content-Type", "text/event-stream");
|
|
53
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
54
|
+
res.setHeader("Connection", "keep-alive");
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const stream = await graph.stream(
|
|
58
|
+
{ messages },
|
|
59
|
+
{ streamMode: "messages" }
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
for await (const [chunk, metadata] of stream) {
|
|
63
|
+
// 只输出 llm 节点的 token,过滤 tool 执行结果
|
|
64
|
+
if (metadata?.langgraph_node === "llm" && chunk.content) {
|
|
65
|
+
res.write(`data: ${JSON.stringify({ token: chunk.content })}\n\n`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
res.write("data: [DONE]\n\n");
|
|
70
|
+
} catch (err) {
|
|
71
|
+
res.write(`data: ${JSON.stringify({ error: err.message })}\n\n`);
|
|
72
|
+
} finally {
|
|
73
|
+
res.end();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
app.listen(3000, () => console.log("http://localhost:3000"));
|
package/package.json
CHANGED
package/server.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import express from "express";
|
|
2
|
-
import cors from "cors";
|
|
3
|
-
import { ChatAnthropic } from "@langchain/anthropic";
|
|
4
|
-
import { StateGraph, MessagesAnnotation } from "@langchain/langgraph";
|
|
5
|
-
import { HumanMessage, AIMessage } from "@langchain/core/messages";
|
|
6
|
-
|
|
7
|
-
const app = express();
|
|
8
|
-
app.use(cors());
|
|
9
|
-
app.use(express.json());
|
|
10
|
-
|
|
11
|
-
const llm = new ChatAnthropic({
|
|
12
|
-
model: "claude-3-5-sonnet-20241022",
|
|
13
|
-
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
const graph = new StateGraph(MessagesAnnotation)
|
|
17
|
-
.addNode("chat", async (state) => ({
|
|
18
|
-
messages: [await llm.invoke(state.messages)],
|
|
19
|
-
}))
|
|
20
|
-
.addEdge("__start__", "chat")
|
|
21
|
-
.addEdge("chat", "__end__")
|
|
22
|
-
.compile();
|
|
23
|
-
|
|
24
|
-
app.post("/chat/stream", async (req, res) => {
|
|
25
|
-
const { messages } = req.body;
|
|
26
|
-
// messages 格式: [{ role: "user"|"assistant", content: "..." }]
|
|
27
|
-
|
|
28
|
-
// SSE headers
|
|
29
|
-
res.setHeader("Content-Type", "text/event-stream");
|
|
30
|
-
res.setHeader("Cache-Control", "no-cache");
|
|
31
|
-
res.setHeader("Connection", "keep-alive");
|
|
32
|
-
|
|
33
|
-
// 将前端传来的历史消息转换为 LangChain 格式
|
|
34
|
-
const history = messages.map((m) =>
|
|
35
|
-
m.role === "user" ? new HumanMessage(m.content) : new AIMessage(m.content)
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
try {
|
|
39
|
-
const stream = await graph.stream(
|
|
40
|
-
{ messages: history },
|
|
41
|
-
{ streamMode: "messages" }
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
for await (const [chunk, metadata] of stream) {
|
|
45
|
-
if (metadata?.langgraph_node === "chat" && chunk.content) {
|
|
46
|
-
res.write(`data: ${JSON.stringify({ token: chunk.content })}\n\n`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
res.write(`data: [DONE]\n\n`);
|
|
51
|
-
} catch (err) {
|
|
52
|
-
res.write(`data: ${JSON.stringify({ error: err.message })}\n\n`);
|
|
53
|
-
} finally {
|
|
54
|
-
res.end();
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
app.listen(3000, () => console.log("Server running on http://localhost:3000"));
|