@langgraph-js/sdk 1.1.6 → 1.1.9

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/src/SpendTime.ts CHANGED
@@ -1,13 +1,30 @@
1
+ /**
2
+ * @zh SpendTime 类用于计算和记录操作的耗时。
3
+ * @en The SpendTime class is used to calculate and record the time spent on operations.
4
+ */
1
5
  export class SpendTime {
2
6
  private timeCounter = new Map<string, [Date, Date] | [Date]>();
3
7
 
8
+ /**
9
+ * @zh 开始计时。
10
+ * @en Starts timing.
11
+ */
4
12
  start(key: string) {
5
13
  this.timeCounter.set(key, [new Date()]);
6
14
  }
7
15
 
16
+ /**
17
+ * @zh 结束计时。
18
+ * @en Ends timing.
19
+ */
8
20
  end(key: string) {
9
21
  this.timeCounter.set(key, [this.timeCounter.get(key)?.[0] || new Date(), new Date()]);
10
22
  }
23
+
24
+ /**
25
+ * @zh 设置或更新指定键的耗时记录。如果键已存在,则更新结束时间;否则,开始新的计时。
26
+ * @en Sets or updates the time spent record for the specified key. If the key already exists, updates the end time; otherwise, starts a new timing.
27
+ */
11
28
  setSpendTime(key: string) {
12
29
  if (this.timeCounter.has(key)) {
13
30
  this.end(key);
@@ -15,13 +32,27 @@ export class SpendTime {
15
32
  this.start(key);
16
33
  }
17
34
  }
35
+
36
+ /**
37
+ * @zh 获取指定键的开始时间。
38
+ * @en Gets the start time for the specified key.
39
+ */
18
40
  getStartTime(key: string) {
19
41
  return this.timeCounter.get(key)?.[0] || new Date();
20
42
  }
43
+
44
+ /**
45
+ * @zh 获取指定键的结束时间。
46
+ * @en Gets the end time for the specified key.
47
+ */
21
48
  getEndTime(key: string) {
22
49
  return this.timeCounter.get(key)?.[1] || new Date();
23
50
  }
24
51
 
52
+ /**
53
+ * @zh 获取指定键的耗时(毫秒)。
54
+ * @en Gets the time spent (in milliseconds) for the specified key.
55
+ */
25
56
  getSpendTime(key: string) {
26
57
  const [start, end = new Date()] = this.timeCounter.get(key) || [new Date(), new Date()];
27
58
  return end.getTime() - start.getTime();
@@ -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,28 @@
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
+ import { rafDebounce } from "./rafDebounce.js";
5
+
6
+ /**
7
+ * @zh 格式化日期对象为时间字符串。
8
+ * @en Formats a Date object into a time string.
9
+ */
4
10
  export const formatTime = (date: Date) => {
5
11
  return date.toLocaleTimeString("en-US");
6
12
  };
7
13
 
14
+ /**
15
+ * @zh 格式化数字为带千位分隔符的字符串。
16
+ * @en Formats a number into a string with thousand separators.
17
+ */
8
18
  export const formatTokens = (tokens: number) => {
9
19
  return tokens.toLocaleString("en");
10
20
  };
21
+
22
+ /**
23
+ * @zh 获取消息内容的文本表示,处理不同类型的消息内容。
24
+ * @en Gets the text representation of message content, handling different types of message content.
25
+ */
11
26
  export const getMessageContent = (content: any) => {
12
27
  if (typeof content === "string") return content;
13
28
  if (Array.isArray(content)) {
@@ -22,6 +37,11 @@ export const getMessageContent = (content: any) => {
22
37
  }
23
38
  return JSON.stringify(content);
24
39
  };
40
+
41
+ /**
42
+ * @zh 获取历史记录中 Thread 内容的文本表示。
43
+ * @en Gets the text representation of Thread content in history.
44
+ */
25
45
  export const getHistoryContent = (thread: Thread) => {
26
46
  const content = (thread?.values as any)?.messages?.[0]?.content;
27
47
  if (content && Array.isArray(content)) {
@@ -36,6 +56,11 @@ export const getHistoryContent = (thread: Thread) => {
36
56
  return "";
37
57
  }
38
58
  };
59
+
60
+ /**
61
+ * @zh 创建一个用于聊天界面的状态管理器 (store)。
62
+ * @en Creates a state manager (store) for the chat interface.
63
+ */
39
64
  export const createChatStore = (
40
65
  initClientName: string,
41
66
  config: LangGraphClientConfig,
@@ -53,12 +78,20 @@ export const createChatStore = (
53
78
  const currentAgent = atom<string>(initClientName);
54
79
  const currentChatId = atom<string | null>(null);
55
80
 
56
- const initClient = async () => {
81
+ const updateUI = rafDebounce((newClient: LangGraphClient) => {
82
+ renderMessages.set(newClient.renderMessage);
83
+ });
84
+ /**
85
+ * @zh 初始化 LangGraph 客户端。
86
+ * @en Initializes the LangGraph client.
87
+ */
88
+ async function initClient() {
57
89
  const newClient = new LangGraphClient(config);
58
90
  await newClient.initAssistant(currentAgent.get());
91
+ currentAgent.set(newClient.getCurrentAssistant()!.graph_id);
59
92
  // 不再需要创建,sendMessage 会自动创建
60
93
  // await newClient.createThread();
61
-
94
+ inChatError.set(null);
62
95
  newClient.onStreamingUpdate((event) => {
63
96
  if (event.type === "thread" || event.type === "done") {
64
97
  // console.log(event.data);
@@ -67,17 +100,21 @@ export const createChatStore = (
67
100
  }
68
101
  if (event.type === "error") {
69
102
  loading.set(false);
70
- inChatError.set(event.data?.message || "发生错误");
103
+ inChatError.set(event.data);
71
104
  }
72
105
  // console.log(newClient.renderMessage);
73
- renderMessages.set(newClient.renderMessage);
106
+
107
+ updateUI(newClient);
74
108
  });
75
109
  context.onInit?.(newClient);
76
- // newClient.tools.bindTools([fileTool, askUserTool]);
77
110
  newClient.graphState = {};
78
111
  client.set(newClient);
79
- };
112
+ }
80
113
 
114
+ /**
115
+ * @zh 发送消息。
116
+ * @en Sends a message.
117
+ */
81
118
  const sendMessage = async (message?: Message[], extraData?: SendMessageOptions) => {
82
119
  if ((!userInput.get().trim() && !message?.length) || loading.get() || !client.get()) return;
83
120
 
@@ -90,21 +127,39 @@ export const createChatStore = (
90
127
  loading.set(false);
91
128
  };
92
129
 
130
+ /**
131
+ * @zh 停止当前的消息生成。
132
+ * @en Stops the current message generation.
133
+ */
93
134
  const stopGeneration = () => {
94
135
  client.get()?.cancelRun();
95
136
  };
96
137
 
138
+ /**
139
+ * @zh 切换工具消息的折叠状态。
140
+ * @en Toggles the collapsed state of a tool message.
141
+ */
97
142
  const toggleToolCollapse = (toolId: string) => {
98
143
  const prev = collapsedTools.get();
99
144
  collapsedTools.set(prev.includes(toolId) ? prev.filter((id) => id !== toolId) : [...prev, toolId]);
100
145
  };
101
146
 
147
+ /**
148
+ * @zh 切换历史记录面板的可见性。
149
+ * @en Toggles the visibility of the history panel.
150
+ */
102
151
  const toggleHistoryVisible = () => {
103
152
  showHistory.set(!showHistory.get());
104
153
  };
154
+
105
155
  const historyList = atom<Thread<{ messages: Message[] }>[]>([]);
156
+
157
+ /**
158
+ * @zh 刷新历史记录列表。
159
+ * @en Refreshes the history list.
160
+ */
106
161
  const refreshHistoryList = async () => {
107
- if (!client.get()) return;
162
+ if (!client.get() || !showHistory.get()) return;
108
163
  try {
109
164
  const response = await client.get()?.listThreads<{ messages: Message[] }>();
110
165
  historyList.set(response || []);
@@ -113,6 +168,10 @@ export const createChatStore = (
113
168
  }
114
169
  };
115
170
 
171
+ /**
172
+ * @zh 将一个 Thread 添加到历史记录列表的开头。
173
+ * @en Adds a Thread to the beginning of the history list.
174
+ */
116
175
  const addToHistory = (thread: Thread<{ messages: Message[] }>) => {
117
176
  const prev = historyList.get();
118
177
  historyList.set([thread, ...prev]);
@@ -139,18 +198,37 @@ export const createChatStore = (
139
198
  toggleHistoryVisible,
140
199
  refreshHistoryList,
141
200
  addToHistory,
201
+ /**
202
+ * @zh 设置用户输入内容。
203
+ * @en Sets the user input content.
204
+ */
142
205
  setUserInput(input: string) {
143
206
  userInput.set(input);
144
207
  },
208
+ /**
209
+ * @zh 设置当前的 Agent 并重新初始化客户端。
210
+ * @en Sets the current Agent and reinitializes the client.
211
+ */
145
212
  setCurrentAgent(agent: string) {
146
213
  currentAgent.set(agent);
147
214
  return initClient().then(() => {
148
- refreshHistoryList();
215
+ if (showHistory.get()) {
216
+ refreshHistoryList();
217
+ }
149
218
  });
150
219
  },
220
+ /**
221
+ * @zh 创建一个新的聊天会话。
222
+ * @en Creates a new chat session.
223
+ */
151
224
  createNewChat() {
152
225
  client.get()?.reset();
226
+ inChatError.set(null);
153
227
  },
228
+ /**
229
+ * @zh 切换到指定的历史聊天会话。
230
+ * @en Switches to the specified historical chat session.
231
+ */
154
232
  toHistoryChat(
155
233
  thread: Thread<{
156
234
  messages: Message[];
@@ -158,6 +236,10 @@ export const createChatStore = (
158
236
  ) {
159
237
  client.get()?.resetThread(thread.metadata?.graph_id as string, thread.thread_id);
160
238
  },
239
+ /**
240
+ * @zh 删除指定的历史聊天会话。
241
+ * @en Deletes the specified historical chat session.
242
+ */
161
243
  async deleteHistoryChat(thread: Thread<{ messages: Message[] }>) {
162
244
  await client.get()?.threads.delete(thread.thread_id);
163
245
  await refreshHistoryList();
@@ -1,2 +1,2 @@
1
- export * from "./createChatStore";
2
- export * from "./UnionStore";
1
+ export * from "./createChatStore.js";
2
+ export * from "./UnionStore.js";
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Creates a debounced function that executes once per animation frame
3
+ * @param callback - The function to debounce
4
+ * @returns A function that executes the callback on the next animation frame
5
+ */
6
+ export function rafDebounce<T extends (...args: any[]) => any>(callback: T): (...args: Parameters<T>) => void {
7
+ let rafId: number | null = null;
8
+ let lastArgs: Parameters<T> | null = null;
9
+
10
+ // Return the debounced function
11
+ return function (...args: Parameters<T>): void {
12
+ // Store the most recent arguments
13
+ lastArgs = args;
14
+
15
+ // Cancel any pending animation frame
16
+ if (rafId !== null) {
17
+ cancelAnimationFrame(rafId);
18
+ }
19
+
20
+ // Schedule execution on the next animation frame
21
+ rafId = requestAnimationFrame(() => {
22
+ if (lastArgs !== null) {
23
+ callback(...lastArgs);
24
+ lastArgs = null;
25
+ }
26
+ rafId = null;
27
+ });
28
+ };
29
+ }
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>