@langgraph-js/sdk 1.1.5 → 1.1.8

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.
@@ -1,13 +1,17 @@
1
1
  import { ToolMessage } from "@langchain/langgraph-sdk";
2
- import { LangGraphClient } from "./LangGraphClient";
3
- import { CallToolResult, createJSONDefineTool, UnionTool } from "./tool/createTool";
2
+ import { LangGraphClient } from "./LangGraphClient.js";
3
+ import { CallToolResult, createJSONDefineTool, UnionTool } from "./tool/createTool.js";
4
4
 
5
+ /**
6
+ * @zh ToolManager 类用于管理和执行工具。
7
+ * @en The ToolManager class is used to manage and execute tools.
8
+ */
5
9
  export class ToolManager {
6
10
  private tools: Map<string, UnionTool<any>> = new Map();
7
11
 
8
12
  /**
9
- * 注册一个工具
10
- * @param tool 要注册的工具
13
+ * @zh 注册一个工具。
14
+ * @en Registers a tool.
11
15
  */
12
16
  bindTool(tool: UnionTool<any>) {
13
17
  if (this.tools.has(tool.name)) {
@@ -17,45 +21,49 @@ export class ToolManager {
17
21
  }
18
22
 
19
23
  /**
20
- * 注册多个工具
21
- * @param tools 要注册的工具数组
24
+ * @zh 注册多个工具。
25
+ * @en Registers multiple tools.
22
26
  */
23
27
  bindTools(tools: UnionTool<any>[]) {
24
28
  tools.forEach((tool) => this.bindTool(tool));
25
29
  }
26
30
 
27
31
  /**
28
- * 获取所有已注册的工具
29
- * @returns 工具数组
32
+ * @zh 获取所有已注册的工具。
33
+ * @en Gets all registered tools.
30
34
  */
31
35
  getAllTools(): UnionTool<any>[] {
32
36
  return Array.from(this.tools.values());
33
37
  }
34
38
 
35
39
  /**
36
- * 获取指定名称的工具
37
- * @param name 工具名称
38
- * @returns 工具实例或 undefined
40
+ * @zh 获取指定名称的工具。
41
+ * @en Gets the tool with the specified name.
39
42
  */
40
43
  getTool(name: string): UnionTool<any> | undefined {
41
44
  return this.tools.get(name);
42
45
  }
43
46
 
44
47
  /**
45
- * 移除指定名称的工具
46
- * @param name 工具名称
47
- * @returns 是否成功移除
48
+ * @zh 移除指定名称的工具。
49
+ * @en Removes the tool with the specified name.
48
50
  */
49
51
  removeTool(name: string): boolean {
50
52
  return this.tools.delete(name);
51
53
  }
52
54
 
53
55
  /**
54
- * 清空所有工具
56
+ * @zh 清空所有工具。
57
+ * @en Clears all tools.
55
58
  */
56
59
  clearTools() {
57
60
  this.tools.clear();
58
61
  }
62
+
63
+ /**
64
+ * @zh 调用指定名称的工具。
65
+ * @en Calls the tool with the specified name.
66
+ */
59
67
  async callTool(name: string, args: any, context: { client: LangGraphClient; message: ToolMessage }) {
60
68
  const tool = this.getTool(name);
61
69
  if (!tool) {
@@ -63,12 +71,22 @@ export class ToolManager {
63
71
  }
64
72
  return await tool.execute(args, context);
65
73
  }
74
+
75
+ /**
76
+ * @zh 将所有工具转换为 JSON 定义格式。
77
+ * @en Converts all tools to JSON definition format.
78
+ */
66
79
  toJSON() {
67
80
  return Array.from(this.tools.values()).map((i) => createJSONDefineTool(i));
68
81
  }
69
82
 
70
83
  // === 专门为前端设计的异步触发结构
71
84
  private waitingMap: Map<string, (value: CallToolResult) => void> = new Map();
85
+
86
+ /**
87
+ * @zh 标记指定 ID 的工具等待已完成,并传递结果。
88
+ * @en Marks the tool waiting with the specified ID as completed and passes the result.
89
+ */
72
90
  doneWaiting(id: string, value: CallToolResult) {
73
91
  if (this.waitingMap.has(id)) {
74
92
  this.waitingMap.get(id)!(value);
@@ -79,6 +97,11 @@ export class ToolManager {
79
97
  return false;
80
98
  }
81
99
  }
100
+
101
+ /**
102
+ * @zh 等待指定 ID 的工具完成。
103
+ * @en Waits for the tool with the specified ID to complete.
104
+ */
82
105
  waitForDone(id: string) {
83
106
  if (this.waitingMap.has(id)) {
84
107
  return this.waitingMap.get(id);
@@ -88,10 +111,10 @@ export class ToolManager {
88
111
  });
89
112
  return promise;
90
113
  }
91
- /** 等待用户输入
92
- * @example
93
- * // 继续 chat 流
94
- * client.tools.doneWaiting(message.id!, (e.target as any).value);
114
+
115
+ /**
116
+ * @zh 一个静态方法,用于在前端等待用户界面操作完成。
117
+ * @en A static method used in the frontend to wait for user interface operations to complete.
95
118
  */
96
119
  static waitForUIDone<T>(_: T, context: { client: LangGraphClient; message: ToolMessage }) {
97
120
  // console.log(context.message);
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
- export * from "./LangGraphClient";
2
- export * from "./tool";
1
+ export * from "./LangGraphClient.js";
2
+ export * from "./tool/index.js";
3
3
  export * from "@langchain/langgraph-sdk";
4
- export * from "./ui-store";
5
- export * from "./ToolManager";
4
+ export * from "./ui-store/index.js";
5
+ export * from "./ToolManager.js";
@@ -1,6 +1,6 @@
1
- import { actionParametersToJsonSchema, convertJsonSchemaToZodRawShape } from "./utils";
1
+ import { actionParametersToJsonSchema, convertJsonSchemaToZodRawShape } from "./utils.js";
2
2
  import { z, ZodRawShape, ZodTypeAny } from "zod";
3
- import { Action, Parameter } from "./copilotkit-actions";
3
+ import { Action, Parameter } from "./copilotkit-actions.js";
4
4
  import { zodToJsonSchema } from "zod-to-json-schema";
5
5
  import { Message } from "@langchain/langgraph-sdk";
6
6
 
package/src/tool/index.ts CHANGED
@@ -1,2 +1,2 @@
1
- export * from "./createTool";
2
- export * from "./copilotkit-actions";
1
+ export * from "./createTool.js";
2
+ export * from "./copilotkit-actions.js";
package/src/tool/utils.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * MIT License
6
6
  */
7
7
  import { z, ZodRawShape } from "zod";
8
- import { Parameter } from "./copilotkit-actions";
8
+ import { Parameter } from "./copilotkit-actions.js";
9
9
 
10
10
  export type JSONSchemaString = {
11
11
  type: "string";
@@ -1,8 +1,17 @@
1
- import { atom, PreinitializedWritableAtom, StoreValue } from "nanostores";
1
+ import { PreinitializedWritableAtom, StoreValue } from "nanostores";
2
+
3
+ /**
4
+ * @zh UnionStore 类型用于合并 store 的 data 和 mutations,使其可以直接访问。
5
+ * @en The UnionStore type is used to merge the data and mutations of a store, allowing direct access.
6
+ */
2
7
  export type UnionStore<T extends { data: Record<string, PreinitializedWritableAtom<any>>; mutations: Record<string, any> }> = {
3
8
  [k in keyof T["data"]]: StoreValue<T["data"][k]>;
4
9
  } & T["mutations"];
5
10
 
11
+ /**
12
+ * @zh useUnionStore Hook 用于将 nanostores 的 store 结构转换为更易于在 UI 组件中使用的扁平结构。
13
+ * @en The useUnionStore Hook is used to transform the nanostores store structure into a flatter structure that is easier to use in UI components.
14
+ */
6
15
  export const useUnionStore = <T extends { data: Record<string, any>; mutations: Record<string, any> }>(
7
16
  store: T,
8
17
  useStore: (store: PreinitializedWritableAtom<any>) => StoreValue<T["data"][keyof T["data"]]>
@@ -1,13 +1,27 @@
1
1
  import { atom } from "nanostores";
2
- import { LangGraphClient, LangGraphClientConfig, RenderMessage, SendMessageOptions } from "../LangGraphClient";
2
+ import { LangGraphClient, LangGraphClientConfig, RenderMessage, SendMessageOptions } from "../LangGraphClient.js";
3
3
  import { Message, Thread } from "@langchain/langgraph-sdk";
4
+
5
+ /**
6
+ * @zh 格式化日期对象为时间字符串。
7
+ * @en Formats a Date object into a time string.
8
+ */
4
9
  export const formatTime = (date: Date) => {
5
10
  return date.toLocaleTimeString("en-US");
6
11
  };
7
12
 
13
+ /**
14
+ * @zh 格式化数字为带千位分隔符的字符串。
15
+ * @en Formats a number into a string with thousand separators.
16
+ */
8
17
  export const formatTokens = (tokens: number) => {
9
18
  return tokens.toLocaleString("en");
10
19
  };
20
+
21
+ /**
22
+ * @zh 获取消息内容的文本表示,处理不同类型的消息内容。
23
+ * @en Gets the text representation of message content, handling different types of message content.
24
+ */
11
25
  export const getMessageContent = (content: any) => {
12
26
  if (typeof content === "string") return content;
13
27
  if (Array.isArray(content)) {
@@ -22,6 +36,30 @@ export const getMessageContent = (content: any) => {
22
36
  }
23
37
  return JSON.stringify(content);
24
38
  };
39
+
40
+ /**
41
+ * @zh 获取历史记录中 Thread 内容的文本表示。
42
+ * @en Gets the text representation of Thread content in history.
43
+ */
44
+ export const getHistoryContent = (thread: Thread) => {
45
+ const content = (thread?.values as any)?.messages?.[0]?.content;
46
+ if (content && Array.isArray(content)) {
47
+ return content.map((item: any) => {
48
+ if (item.type === "text") {
49
+ return item.text;
50
+ }
51
+ });
52
+ } else if (typeof content === "string") {
53
+ return content;
54
+ } else {
55
+ return "";
56
+ }
57
+ };
58
+
59
+ /**
60
+ * @zh 创建一个用于聊天界面的状态管理器 (store)。
61
+ * @en Creates a state manager (store) for the chat interface.
62
+ */
25
63
  export const createChatStore = (
26
64
  initClientName: string,
27
65
  config: LangGraphClientConfig,
@@ -39,12 +77,16 @@ export const createChatStore = (
39
77
  const currentAgent = atom<string>(initClientName);
40
78
  const currentChatId = atom<string | null>(null);
41
79
 
42
- const initClient = async () => {
80
+ /**
81
+ * @zh 初始化 LangGraph 客户端。
82
+ * @en Initializes the LangGraph client.
83
+ */
84
+ async function initClient() {
43
85
  const newClient = new LangGraphClient(config);
44
86
  await newClient.initAssistant(currentAgent.get());
45
87
  // 不再需要创建,sendMessage 会自动创建
46
88
  // await newClient.createThread();
47
-
89
+ inChatError.set(null);
48
90
  newClient.onStreamingUpdate((event) => {
49
91
  if (event.type === "thread" || event.type === "done") {
50
92
  // console.log(event.data);
@@ -53,17 +95,20 @@ export const createChatStore = (
53
95
  }
54
96
  if (event.type === "error") {
55
97
  loading.set(false);
56
- inChatError.set(event.data?.message || "发生错误");
98
+ inChatError.set(event.data);
57
99
  }
58
100
  // console.log(newClient.renderMessage);
59
101
  renderMessages.set(newClient.renderMessage);
60
102
  });
61
103
  context.onInit?.(newClient);
62
- // newClient.tools.bindTools([fileTool, askUserTool]);
63
104
  newClient.graphState = {};
64
105
  client.set(newClient);
65
106
  };
66
107
 
108
+ /**
109
+ * @zh 发送消息。
110
+ * @en Sends a message.
111
+ */
67
112
  const sendMessage = async (message?: Message[], extraData?: SendMessageOptions) => {
68
113
  if ((!userInput.get().trim() && !message?.length) || loading.get() || !client.get()) return;
69
114
 
@@ -76,21 +121,39 @@ export const createChatStore = (
76
121
  loading.set(false);
77
122
  };
78
123
 
124
+ /**
125
+ * @zh 停止当前的消息生成。
126
+ * @en Stops the current message generation.
127
+ */
79
128
  const stopGeneration = () => {
80
129
  client.get()?.cancelRun();
81
130
  };
82
131
 
132
+ /**
133
+ * @zh 切换工具消息的折叠状态。
134
+ * @en Toggles the collapsed state of a tool message.
135
+ */
83
136
  const toggleToolCollapse = (toolId: string) => {
84
137
  const prev = collapsedTools.get();
85
138
  collapsedTools.set(prev.includes(toolId) ? prev.filter((id) => id !== toolId) : [...prev, toolId]);
86
139
  };
87
140
 
141
+ /**
142
+ * @zh 切换历史记录面板的可见性。
143
+ * @en Toggles the visibility of the history panel.
144
+ */
88
145
  const toggleHistoryVisible = () => {
89
146
  showHistory.set(!showHistory.get());
90
147
  };
148
+
91
149
  const historyList = atom<Thread<{ messages: Message[] }>[]>([]);
150
+
151
+ /**
152
+ * @zh 刷新历史记录列表。
153
+ * @en Refreshes the history list.
154
+ */
92
155
  const refreshHistoryList = async () => {
93
- if (!client.get()) return;
156
+ if (!client.get() || !showHistory.get()) return;
94
157
  try {
95
158
  const response = await client.get()?.listThreads<{ messages: Message[] }>();
96
159
  historyList.set(response || []);
@@ -99,6 +162,10 @@ export const createChatStore = (
99
162
  }
100
163
  };
101
164
 
165
+ /**
166
+ * @zh 将一个 Thread 添加到历史记录列表的开头。
167
+ * @en Adds a Thread to the beginning of the history list.
168
+ */
102
169
  const addToHistory = (thread: Thread<{ messages: Message[] }>) => {
103
170
  const prev = historyList.get();
104
171
  historyList.set([thread, ...prev]);
@@ -125,18 +192,35 @@ export const createChatStore = (
125
192
  toggleHistoryVisible,
126
193
  refreshHistoryList,
127
194
  addToHistory,
195
+ /**
196
+ * @zh 设置用户输入内容。
197
+ * @en Sets the user input content.
198
+ */
128
199
  setUserInput(input: string) {
129
200
  userInput.set(input);
130
201
  },
202
+ /**
203
+ * @zh 设置当前的 Agent 并重新初始化客户端。
204
+ * @en Sets the current Agent and reinitializes the client.
205
+ */
131
206
  setCurrentAgent(agent: string) {
132
207
  currentAgent.set(agent);
133
208
  return initClient().then(() => {
134
209
  refreshHistoryList();
135
210
  });
136
211
  },
212
+ /**
213
+ * @zh 创建一个新的聊天会话。
214
+ * @en Creates a new chat session.
215
+ */
137
216
  createNewChat() {
138
217
  client.get()?.reset();
218
+ inChatError.set(null);
139
219
  },
220
+ /**
221
+ * @zh 切换到指定的历史聊天会话。
222
+ * @en Switches to the specified historical chat session.
223
+ */
140
224
  toHistoryChat(
141
225
  thread: Thread<{
142
226
  messages: Message[];
@@ -144,6 +228,10 @@ export const createChatStore = (
144
228
  ) {
145
229
  client.get()?.resetThread(thread.metadata?.graph_id as string, thread.thread_id);
146
230
  },
231
+ /**
232
+ * @zh 删除指定的历史聊天会话。
233
+ * @en Deletes the specified historical chat session.
234
+ */
147
235
  async deleteHistoryChat(thread: Thread<{ messages: Message[] }>) {
148
236
  await client.get()?.threads.delete(thread.thread_id);
149
237
  await refreshHistoryList();
@@ -1,2 +1,2 @@
1
- export * from "./createChatStore";
2
- export * from "./UnionStore";
1
+ export * from "./createChatStore.js";
2
+ export * from "./UnionStore.js";
package/tsconfig.json CHANGED
@@ -25,9 +25,9 @@
25
25
  // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
26
26
 
27
27
  /* Modules */
28
- "module": "ESNext" /* Specify what module code is generated. */,
28
+ "module": "NodeNext" /* Specify what module code is generated. */,
29
29
  // "rootDir": "./", /* Specify the root folder within your source files. */
30
- "moduleResolution": "node10" /* Specify how TypeScript looks up a file from a given module specifier. */,
30
+ "moduleResolution": "nodenext" /* Specify how TypeScript looks up a file from a given module specifier. */,
31
31
  // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
32
32
  // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
33
33
  // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
package/index.html DELETED
@@ -1,12 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Document</title>
7
- </head>
8
- <body>
9
- <div id="message"></div>
10
- </body>
11
- <script type="module" src="/ui/index.ts"></script>
12
- </html>
package/ui/index.ts DELETED
@@ -1,182 +0,0 @@
1
- import { LangGraphClient, RenderMessage } from "../src/LangGraphClient";
2
- /** @ts-ignore */
3
- import testResponse from "../test/testResponse.json?raw";
4
- import { createTools } from "./tool";
5
- const main = async () => {
6
- const client = new LangGraphClient({
7
- defaultHeaders: {},
8
- });
9
-
10
- await client.initAssistant("agent");
11
- await client.createThread();
12
- client.onStreamingUpdate((event) => {
13
- updateMessages(client.renderMessage);
14
- });
15
- client.tools.bindTools(createTools());
16
- const messages = await client.sendMessage("你有什么工具", {
17
- _debug: {
18
- // streamResponse: JSON.parse(testResponse),
19
- },
20
- });
21
- console.log(client.graphMessages);
22
- console.log(client.renderMessage);
23
- console.log(client.tokenCounter);
24
- };
25
-
26
- // 声明全局类型
27
- declare global {
28
- interface Window {
29
- main: () => Promise<void>;
30
- }
31
- }
32
-
33
- window.main = main;
34
- // main();
35
-
36
- function getMessageType(message: RenderMessage): string {
37
- if ("type" in message) {
38
- return message.type;
39
- }
40
- return "unknown";
41
- }
42
-
43
- function renderMessage(message: RenderMessage) {
44
- const messageDiv = document.createElement("div");
45
- const messageType = getMessageType(message);
46
- messageDiv.className = `message ${messageType}`;
47
-
48
- const contentDiv = document.createElement("div");
49
- contentDiv.className = "message-content";
50
-
51
- const messageContent = `
52
- <div class="message-header">
53
- <span class="message-role">${getRoleName(messageType)} </span>
54
- <span class="message-role">${
55
- /** @ts-ignore */
56
- message.name || ""
57
- }</span>
58
- ${renderTokenUsage(message.usage_metadata)}
59
- </div>
60
- <p class="message-body">
61
-
62
- <span>
63
- ${message.tool_input || ""}
64
- </span>
65
- <span>
66
- ${message.content}
67
- </span>
68
- </p>
69
- `;
70
-
71
- contentDiv.innerHTML = messageContent;
72
- messageDiv.appendChild(contentDiv);
73
- return messageDiv;
74
- }
75
-
76
- function getRoleName(type: string): string {
77
- switch (type) {
78
- case "human":
79
- return "You";
80
- case "ai":
81
- return "AI Assistant";
82
- case "system":
83
- return "System";
84
- default:
85
- return type;
86
- }
87
- }
88
-
89
- function renderTokenUsage(usage?: { input_tokens?: number; output_tokens?: number; total_tokens?: number }) {
90
- if (!usage) return "";
91
-
92
- return `
93
- <div class="token-usage">
94
- ${usage.input_tokens ? `<span class="token-count">Input: ${usage.input_tokens}</span>` : ""}
95
- ${usage.output_tokens ? `<span class="token-count">Output: ${usage.output_tokens}</span>` : ""}
96
- ${usage.total_tokens ? `<span class="token-count">Total: ${usage.total_tokens}</span>` : ""}
97
- </div>
98
- `;
99
- }
100
-
101
- function renderMessages(messages: RenderMessage[]) {
102
- const container = document.createElement("div");
103
- container.className = "messages-container";
104
-
105
- messages.forEach((message) => {
106
- container.appendChild(renderMessage(message));
107
- });
108
-
109
- return container;
110
- }
111
-
112
- export function updateMessages(messages: RenderMessage[]) {
113
- const messageDiv = document.getElementById("message");
114
- if (!messageDiv) return;
115
-
116
- messageDiv.innerHTML = "";
117
- messageDiv.appendChild(renderMessages(messages));
118
- }
119
-
120
- // 添加样式
121
- const style = document.createElement("style");
122
- style.textContent = `
123
- .messages-container {
124
- display: flex;
125
- flex-direction: column;
126
- gap: 1rem;
127
- padding: 1rem;
128
- }
129
-
130
- .message {
131
- max-width: 80%;
132
- padding: 0.75rem;
133
- border-radius: 0.5rem;
134
- }
135
-
136
- .message.human {
137
- align-self: flex-end;
138
- background-color: #e3f2fd;
139
- }
140
-
141
- .message.ai {
142
- align-self: flex-start;
143
- background-color: #f5f5f5;
144
- }
145
-
146
- .message.system {
147
- align-self: center;
148
- background-color: #fff3e0;
149
- font-style: italic;
150
- }
151
-
152
- .message-header {
153
- margin-bottom: 0.5rem;
154
- font-size: 0.875rem;
155
- color: #666;
156
- display: flex;
157
- justify-content: space-between;
158
- align-items: center;
159
- }
160
-
161
- .message-role {
162
- font-weight: 600;
163
- }
164
-
165
- .message-body {
166
- }
167
-
168
- .token-usage {
169
- display: flex;
170
- gap: 0.5rem;
171
- font-size: 0.75rem;
172
- color: #888;
173
- }
174
-
175
- .token-count {
176
- background-color: rgba(0, 0, 0, 0.05);
177
- padding: 0.125rem 0.375rem;
178
- border-radius: 0.25rem;
179
- }
180
- `;
181
-
182
- document.head.appendChild(style);
package/ui/tool.ts DELETED
@@ -1,55 +0,0 @@
1
- import { z } from "zod";
2
- import { createTool, createFETool } from "../src/tool/createTool";
3
-
4
- export function createTools() {
5
- // 文件操作工具
6
- const fileTool = createTool({
7
- name: "file_operation",
8
- description: "执行文件操作,包括读取和写入",
9
- parameters: {
10
- operation: z.enum(["read", "write"]).describe("操作类型:read-读取文件,write-写入文件"),
11
- filePath: z.string().describe("文件的完整路径"),
12
- content: z.string().optional().describe("写入文件时的内容,仅在 operation 为 write 时需要"),
13
- },
14
- async execute(args) {
15
- return [{ type: "text", text: "执行文件操作" }];
16
- },
17
- });
18
-
19
- // 用户交互工具
20
- const userInteractionTool = createFETool({
21
- name: "user_interaction",
22
- description: "与用户进行交互,包括确认和输入",
23
- parameters: [
24
- {
25
- name: "type",
26
- type: "string",
27
- description: "交互类型:confirm-确认,input-输入",
28
- },
29
- {
30
- name: "message",
31
- type: "string",
32
- description: "向用户展示的信息",
33
- },
34
- {
35
- name: "options",
36
- type: "string[]",
37
- description: "选项列表,仅在 type 为 confirm 时使用",
38
- optional: true,
39
- },
40
- ],
41
- handler: async (args) => {
42
- return {
43
- success: true,
44
- data: {
45
- type: args.type,
46
- message: args.message,
47
- options: args.options || ["确认", "取消"],
48
- },
49
- message: "等待用户响应",
50
- };
51
- },
52
- });
53
-
54
- return [fileTool, userInteractionTool];
55
- }