UNPKG

@langgraph-js/sdk

Version:

The UI SDK for LangGraph - seamlessly integrate your AI agents with frontend interfaces

303 lines (302 loc) 11 kB
import { atom } from "nanostores"; import { LangGraphClient } from "../LangGraphClient.js"; import { debounce } from "ts-debounce"; import { ToolRenderData } from "../tool/ToolUI.js"; /** * @zh 格式化日期对象为时间字符串。 * @en Formats a Date object into a time string. */ export const formatTime = (date) => { return date.toLocaleTimeString("en-US"); }; /** * @zh 格式化数字为带千位分隔符的字符串。 * @en Formats a number into a string with thousand separators. */ export const formatTokens = (tokens) => { return tokens.toLocaleString("en"); }; /** * @zh 获取消息内容的文本表示,处理不同类型的消息内容。 * @en Gets the text representation of message content, handling different types of message content. */ export const getMessageContent = (content) => { if (typeof content === "string") return content; if (Array.isArray(content)) { return content .map((item) => { if (typeof item === "string") return item; if (item.type === "text") return item.text; if (item.type === "image_url") return `[图片]`; return JSON.stringify(item); }) .join(""); } return JSON.stringify(content); }; /** * @zh 获取历史记录中 Thread 内容的文本表示。 * @en Gets the text representation of Thread content in history. */ export const getHistoryContent = (thread) => { var _a, _b, _c; const content = (_c = (_b = (_a = thread === null || thread === void 0 ? void 0 : thread.values) === null || _a === void 0 ? void 0 : _a.messages) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.content; if (content && Array.isArray(content)) { return content.map((item) => { if (item.type === "text") { return item.text; } }); } else if (typeof content === "string") { return content; } else { return ""; } }; /** * @zh 创建一个用于聊天界面的状态管理器 (store)。 * @en Creates a state manager (store) for the chat interface. */ export const createChatStore = (initClientName, config, context = {}) => { var _a, _b; const client = atom(null); const renderMessages = atom([]); const userInput = atom(""); const loading = atom(false); const collapsedTools = atom([]); const inChatError = atom(null); const showHistory = atom((_a = context.showHistory) !== null && _a !== void 0 ? _a : false); const currentAgent = atom(initClientName); const currentChatId = atom(null); const currentNodeName = atom("__start__"); const tools = atom([]); const refreshTools = async () => { const c = client.get(); if (!c) return; c.tools.clearTools(); c.tools.bindTools(tools.get()); }; // 显示 langgraph 可视化图 const showGraph = atom((_b = context.showGraph) !== null && _b !== void 0 ? _b : false); const graphVisualize = atom(null); const refreshGraph = async () => { var _a; if (showGraph.get()) graphVisualize.set((await ((_a = client.get()) === null || _a === void 0 ? void 0 : _a.graphVisualize())) || null); }; const updateUI = debounce((newClient) => { const messages = newClient.renderMessage; const lastMessage = messages[messages.length - 1]; currentNodeName.set((lastMessage === null || lastMessage === void 0 ? void 0 : lastMessage.node_name) || (lastMessage === null || lastMessage === void 0 ? void 0 : lastMessage.name) || "__start__"); renderMessages.set(messages); }, 10); /** * @zh 初始化 LangGraph 客户端。 * @en Initializes the LangGraph client. */ async function initClient() { var _a; const newClient = new LangGraphClient(config); await newClient.initAssistant(currentAgent.get()); currentAgent.set(newClient.getCurrentAssistant().graph_id); // 不再需要创建,sendMessage 会自动创建 // await newClient.createThread(); inChatError.set(null); newClient.onStreamingUpdate((event) => { if (event.type === "start") loading.set(true); if (event.type === "thread" || event.type === "done") { // console.log(event.data); // 创建新流程时,默认为 __start__ currentNodeName.set("__start__"); if (event.type === "done") loading.set(false); // 创建新会话时,需要自动刷新历史面板 return refreshHistoryList(); } if (event.type === "error") { loading.set(false); inChatError.set(event.data); } // console.log(newClient.renderMessage); updateUI(newClient); }); (_a = context.onInit) === null || _a === void 0 ? void 0 : _a.call(context, newClient); newClient.graphState = {}; client.set(newClient); if (showGraph.get()) refreshGraph(); refreshTools(); return newClient; } /** * @zh 发送消息。 * @en Sends a message. */ const sendMessage = async (message, extraData) => { var _a; if ((!userInput.get().trim() && !(message === null || message === void 0 ? void 0 : message.length)) || loading.get() || !client.get()) return; loading.set(true); inChatError.set(null); await ((_a = client.get()) === null || _a === void 0 ? void 0 : _a.sendMessage(message || userInput.get(), extraData)); userInput.set(""); loading.set(false); }; /** * @zh 停止当前的消息生成。 * @en Stops the current message generation. */ const stopGeneration = () => { var _a; (_a = client.get()) === null || _a === void 0 ? void 0 : _a.cancelRun(); }; /** * @zh 切换工具消息的折叠状态。 * @en Toggles the collapsed state of a tool message. */ const toggleToolCollapse = (toolId) => { const prev = collapsedTools.get(); collapsedTools.set(prev.includes(toolId) ? prev.filter((id) => id !== toolId) : [...prev, toolId]); }; /** * @zh 切换历史记录面板的可见性。 * @en Toggles the visibility of the history panel. */ const toggleHistoryVisible = () => { showHistory.set(!showHistory.get()); if (showHistory.get()) { refreshHistoryList(); } }; const historyList = atom([]); /** * @zh 刷新历史记录列表。 * @en Refreshes the history list. */ const refreshHistoryList = async () => { var _a; if (!client.get() || !showHistory.get()) return; try { const response = await ((_a = client.get()) === null || _a === void 0 ? void 0 : _a.listThreads()); historyList.set(response || []); } catch (error) { console.error("Failed to fetch threads:", error); } }; /** * @zh 将一个 Thread 添加到历史记录列表的开头。 * @en Adds a Thread to the beginning of the history list. */ const addToHistory = (thread) => { const prev = historyList.get(); historyList.set([thread, ...prev]); }; const getToolUIRender = (tool_name) => { var _a; const toolsDefine = client.get().tools.getAllTools(); const tool = (_a = toolsDefine.find((i) => i.name === tool_name)) === null || _a === void 0 ? void 0 : _a.render; return tool ? (message) => tool(new ToolRenderData(message, client.get())) : null; }; return { data: { client, renderMessages, userInput, loading, inChatError, currentAgent, collapsedTools, showHistory, historyList, currentChatId, showGraph, graphVisualize, currentNodeName, tools, }, mutations: { refreshTools, setTools(new_tools) { tools.set(new_tools); refreshTools(); }, isFELocking() { var _a; return (_a = client.get()) === null || _a === void 0 ? void 0 : _a.isFELocking(renderMessages.get()); }, initClient, sendMessage, stopGeneration, toggleToolCollapse, toggleHistoryVisible, refreshHistoryList, addToHistory, /** * @zh 设置用户输入内容。 * @en Sets the user input content. */ setUserInput(input) { userInput.set(input); }, /** * @zh 设置当前的 Agent 并重新初始化客户端。 * @en Sets the current Agent and reinitializes the client. */ setCurrentAgent(agent) { currentAgent.set(agent); return initClient().then(() => { if (showHistory.get()) { refreshHistoryList(); } }); }, toggleGraphVisible() { showGraph.set(!showGraph.get()); if (showGraph.get()) { refreshGraph(); } }, refreshGraph, /** * @zh 创建一个新的聊天会话。 * @en Creates a new chat session. */ createNewChat() { var _a; (_a = client.get()) === null || _a === void 0 ? void 0 : _a.reset(); inChatError.set(null); loading.set(false); }, /** * @zh 切换到指定的历史聊天会话。 * @en Switches to the specified historical chat session. */ toHistoryChat(thread) { var _a, _b; inChatError.set(null); loading.set(false); (_a = client.get()) === null || _a === void 0 ? void 0 : _a.resetThread((_b = thread.metadata) === null || _b === void 0 ? void 0 : _b.graph_id, thread.thread_id); }, /** * @zh 删除指定的历史聊天会话。 * @en Deletes the specified historical chat session. */ async deleteHistoryChat(thread) { var _a; await ((_a = client.get()) === null || _a === void 0 ? void 0 : _a.threads.delete(thread.thread_id)); await refreshHistoryList(); }, getToolUIRender, }, }; };