@lobehub/chat 1.128.7 → 1.128.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.
Files changed (30) hide show
  1. package/.cursor/rules/debug-usage.mdc +2 -2
  2. package/.github/workflows/test.yml +1 -0
  3. package/CHANGELOG.md +25 -0
  4. package/apps/desktop/package.json +1 -1
  5. package/changelog/v1.json +9 -0
  6. package/docs/development/database-schema.dbml +2 -0
  7. package/package.json +2 -1
  8. package/packages/agent-runtime/examples/tools-calling.ts +303 -0
  9. package/packages/agent-runtime/package.json +15 -0
  10. package/packages/agent-runtime/src/core/__tests__/runtime.test.ts +1046 -0
  11. package/packages/agent-runtime/src/core/index.ts +1 -0
  12. package/packages/agent-runtime/src/core/runtime.ts +656 -0
  13. package/packages/agent-runtime/src/index.ts +2 -0
  14. package/packages/agent-runtime/src/types/event.ts +121 -0
  15. package/packages/agent-runtime/src/types/index.ts +5 -0
  16. package/packages/agent-runtime/src/types/instruction.ts +125 -0
  17. package/packages/agent-runtime/src/types/runtime.ts +18 -0
  18. package/packages/agent-runtime/src/types/state.ts +108 -0
  19. package/packages/agent-runtime/src/types/usage.ts +128 -0
  20. package/packages/agent-runtime/vitest.config.mts +12 -0
  21. package/packages/database/migrations/0031_add_agent_index.sql +2 -0
  22. package/packages/database/migrations/meta/0031_snapshot.json +6447 -0
  23. package/packages/database/migrations/meta/_journal.json +7 -0
  24. package/packages/database/src/core/migrations.json +9 -0
  25. package/packages/database/src/models/session.ts +30 -27
  26. package/packages/database/src/schemas/agent.ts +3 -0
  27. package/src/features/ChatInput/ActionBar/History/Controls.tsx +12 -2
  28. package/src/features/ChatInput/ActionBar/History/index.tsx +18 -1
  29. package/src/features/ChatInput/ActionBar/Search/index.tsx +19 -1
  30. package/src/features/ChatInput/ActionBar/components/Action.tsx +5 -1
@@ -1,6 +1,6 @@
1
1
  ---
2
- description: 包含添加 debug 日志请求时
3
- globs:
2
+ description: 包含添加 console.log 日志请求时
3
+ globs:
4
4
  alwaysApply: false
5
5
  ---
6
6
  # Debug 包使用指南
@@ -19,6 +19,7 @@ jobs:
19
19
  - electron-server-ipc
20
20
  - utils
21
21
  - context-engine
22
+ - agent-runtime
22
23
 
23
24
  name: Test package ${{ matrix.package }}
24
25
 
package/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 1.128.8](https://github.com/lobehub/lobe-chat/compare/v1.128.7...v1.128.8)
6
+
7
+ <sup>Released on **2025-09-15**</sup>
8
+
9
+ #### 💄 Styles
10
+
11
+ - **misc**: Enable toggling search on/off via search button click & historyCount button.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### Styles
19
+
20
+ - **misc**: Enable toggling search on/off via search button click & historyCount button, closes [#9173](https://github.com/lobehub/lobe-chat/issues/9173) ([240c7b7](https://github.com/lobehub/lobe-chat/commit/240c7b7))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
5
30
  ### [Version 1.128.7](https://github.com/lobehub/lobe-chat/compare/v1.128.6...v1.128.7)
6
31
 
7
32
  <sup>Released on **2025-09-14**</sup>
@@ -52,7 +52,7 @@
52
52
  "@typescript/native-preview": "7.0.0-dev.20250711.1",
53
53
  "consola": "^3.1.0",
54
54
  "cookie": "^1.0.2",
55
- "electron": "^37.4.0",
55
+ "electron": "^38.0.0",
56
56
  "electron-builder": "^26.0.12",
57
57
  "electron-is": "^3.0.0",
58
58
  "electron-log": "^5.3.3",
package/changelog/v1.json CHANGED
@@ -1,4 +1,13 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "improvements": [
5
+ "Enable toggling search on/off via search button click & historyCount button."
6
+ ]
7
+ },
8
+ "date": "2025-09-15",
9
+ "version": "1.128.8"
10
+ },
2
11
  {
3
12
  "children": {},
4
13
  "date": "2025-09-14",
@@ -24,6 +24,8 @@ table agents {
24
24
 
25
25
  indexes {
26
26
  (client_id, user_id) [name: 'client_id_user_id_unique', unique]
27
+ title [name: 'agents_title_idx']
28
+ description [name: 'agents_description_idx']
27
29
  }
28
30
  }
29
31
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.128.7",
3
+ "version": "1.128.8",
4
4
  "description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -143,6 +143,7 @@
143
143
  "@icons-pack/react-simple-icons": "9.6.0",
144
144
  "@khmyznikov/pwa-install": "0.3.9",
145
145
  "@langchain/community": "^0.3.55",
146
+ "@lobechat/agent-runtime": "workspace:*",
146
147
  "@lobechat/const": "workspace:*",
147
148
  "@lobechat/context-engine": "workspace:*",
148
149
  "@lobechat/database": "workspace:*",
@@ -0,0 +1,303 @@
1
+ // @ts-nocheck
2
+ import OpenAI from 'openai';
3
+
4
+ import { AgentRuntime } from '../src';
5
+ import type { Agent, AgentState, RuntimeContext } from '../src';
6
+
7
+ // OpenAI 模型运行时
8
+ async function* openaiRuntime(payload: any) {
9
+ const openai = new OpenAI({
10
+ apiKey: process.env.OPENAI_API_KEY || '',
11
+ });
12
+
13
+ const { messages, tools } = payload;
14
+
15
+ const stream = await openai.chat.completions.create({
16
+ messages,
17
+ model: 'gpt-4.1-mini',
18
+ stream: true,
19
+ tools,
20
+ });
21
+
22
+ let content = '';
23
+ let toolCalls: any[] = [];
24
+
25
+ for await (const chunk of stream) {
26
+ const delta = chunk.choices[0]?.delta;
27
+
28
+ if (delta?.content) {
29
+ content += delta.content;
30
+ yield { content: delta.content };
31
+ }
32
+
33
+ if (delta?.tool_calls) {
34
+ for (const toolCall of delta.tool_calls) {
35
+ if (!toolCalls[toolCall.index]) {
36
+ toolCalls[toolCall.index] = {
37
+ function: { arguments: '', name: '' },
38
+ id: toolCall.id,
39
+ type: 'function',
40
+ };
41
+ }
42
+ if (toolCall.function?.name) {
43
+ toolCalls[toolCall.index].function.name += toolCall.function.name;
44
+ }
45
+ if (toolCall.function?.arguments) {
46
+ toolCalls[toolCall.index].function.arguments += toolCall.function.arguments;
47
+ }
48
+ }
49
+ }
50
+ }
51
+
52
+ if (toolCalls.length > 0) {
53
+ yield { tool_calls: toolCalls.filter(Boolean) };
54
+ }
55
+ }
56
+
57
+ // 简单的 Agent 实现
58
+ class SimpleAgent implements Agent {
59
+ private conversationState: 'waiting_user' | 'processing_llm' | 'executing_tools' | 'done' =
60
+ 'waiting_user';
61
+ private pendingToolCalls: any[] = [];
62
+
63
+ // Agent 拥有自己的模型运行时
64
+ modelRuntime = openaiRuntime;
65
+
66
+ // 定义可用工具
67
+ tools = {
68
+ calculate: async ({ expression }: { expression: string }) => {
69
+ try {
70
+ // 注意:实际应用中应使用安全的数学解析器
71
+ const result = new Function(`"use strict"; return (${expression})`)();
72
+ return { expression, result };
73
+ } catch {
74
+ return { error: 'Invalid expression', expression };
75
+ }
76
+ },
77
+
78
+ get_time: async () => {
79
+ return {
80
+ current_time: new Date().toISOString(),
81
+ formatted_time: new Date().toLocaleString(),
82
+ };
83
+ },
84
+ };
85
+
86
+ // 获取工具定义
87
+ private getToolDefinitions() {
88
+ return [
89
+ {
90
+ function: {
91
+ description: 'Get current date and time',
92
+ name: 'get_time',
93
+ parameters: { properties: {}, type: 'object' },
94
+ },
95
+ type: 'function' as const,
96
+ },
97
+ {
98
+ function: {
99
+ description: 'Calculate mathematical expressions',
100
+ name: 'calculate',
101
+ parameters: {
102
+ properties: {
103
+ expression: { description: 'Math expression', type: 'string' },
104
+ },
105
+ required: ['expression'],
106
+ type: 'object',
107
+ },
108
+ },
109
+ type: 'function' as const,
110
+ },
111
+ ];
112
+ }
113
+
114
+ // Agent 决策逻辑 - 基于执行阶段和上下文
115
+ async runner(context: RuntimeContext, state: AgentState) {
116
+ console.log(`[${context.phase}] 对话状态: ${this.conversationState}`);
117
+
118
+ switch (context.phase) {
119
+ case 'init': {
120
+ // 初始化阶段
121
+ this.conversationState = 'waiting_user';
122
+ return { reason: 'No action needed', type: 'finish' as const };
123
+ }
124
+
125
+ case 'user_input': {
126
+ // 用户输入阶段
127
+ const userPayload = context.payload as { isFirstMessage: boolean; message: any };
128
+ console.log(`👤 用户消息: ${userPayload.message.content}`);
129
+
130
+ // 只有在等待用户输入状态时才处理
131
+ if (this.conversationState === 'waiting_user') {
132
+ this.conversationState = 'processing_llm';
133
+ return {
134
+ payload: {
135
+ messages: state.messages,
136
+ tools: this.getToolDefinitions(),
137
+ },
138
+ type: 'call_llm' as const,
139
+ };
140
+ }
141
+
142
+ // 其他状态下不处理用户输入,结束对话
143
+ console.log(`⚠️ 忽略用户输入,当前状态: ${this.conversationState}`);
144
+ return {
145
+ reason: `Not in waiting_user state: ${this.conversationState}`,
146
+ type: 'finish' as const,
147
+ };
148
+ }
149
+
150
+ case 'llm_result': {
151
+ // LLM 结果阶段,检查是否需要工具调用
152
+ const llmPayload = context.payload as { hasToolCalls: boolean; result: any };
153
+
154
+ // 手动添加 assistant 消息到状态中(修复 Runtime 的问题)
155
+ const assistantMessage: any = {
156
+ content: llmPayload.result.content || null,
157
+ role: 'assistant',
158
+ };
159
+
160
+ if (llmPayload.hasToolCalls) {
161
+ const toolCalls = llmPayload.result.tool_calls;
162
+ assistantMessage.tool_calls = toolCalls;
163
+ this.pendingToolCalls = toolCalls;
164
+ this.conversationState = 'executing_tools';
165
+
166
+ console.log(
167
+ '🔧 需要执行工具:',
168
+ toolCalls.map((call: any) => call.function.name),
169
+ );
170
+
171
+ // 添加包含 tool_calls 的 assistant 消息
172
+ state.messages.push(assistantMessage);
173
+
174
+ // 执行第一个工具调用
175
+ return {
176
+ toolCall: toolCalls[0],
177
+ type: 'call_tool' as const,
178
+ };
179
+ }
180
+
181
+ // 没有工具调用,添加普通 assistant 消息
182
+ state.messages.push(assistantMessage);
183
+ this.conversationState = 'done';
184
+ return { reason: 'LLM response completed', type: 'finish' as const };
185
+ }
186
+
187
+ case 'tool_result': {
188
+ // 工具执行结果阶段
189
+ const toolPayload = context.payload as { result: any; toolMessage: any };
190
+ console.log(`🛠️ 工具执行完成: ${JSON.stringify(toolPayload.result)}`);
191
+
192
+ // 移除已执行的工具
193
+ this.pendingToolCalls = this.pendingToolCalls.slice(1);
194
+
195
+ // 如果还有未执行的工具,继续执行
196
+ if (this.pendingToolCalls.length > 0) {
197
+ return {
198
+ toolCall: this.pendingToolCalls[0],
199
+ type: 'call_tool' as const,
200
+ };
201
+ }
202
+
203
+ // 所有工具执行完成,调用 LLM 处理结果
204
+ this.conversationState = 'processing_llm';
205
+ return {
206
+ payload: {
207
+ messages: state.messages,
208
+ tools: this.getToolDefinitions(),
209
+ },
210
+ type: 'call_llm' as const,
211
+ };
212
+ }
213
+
214
+ case 'human_response': {
215
+ // 人机交互响应阶段(简化示例中不使用)
216
+ return { reason: 'Human interaction not supported', type: 'finish' as const };
217
+ }
218
+
219
+ case 'error': {
220
+ // 错误阶段
221
+ const errorPayload = context.payload as { error: any };
222
+ console.error('❌ 错误状态:', errorPayload.error);
223
+ return { reason: 'Error occurred', type: 'finish' as const };
224
+ }
225
+
226
+ default: {
227
+ return { reason: 'Unknown phase', type: 'finish' as const };
228
+ }
229
+ }
230
+ }
231
+ }
232
+
233
+ // 主函数
234
+ async function main() {
235
+ console.log('🚀 简单的 OpenAI Tools Agent 示例\n');
236
+
237
+ if (!process.env.OPENAI_API_KEY) {
238
+ console.error('❌ 请设置 OPENAI_API_KEY 环境变量');
239
+ return;
240
+ }
241
+
242
+ // 创建 Agent 和 Runtime
243
+ const agent = new SimpleAgent();
244
+ const runtime = new AgentRuntime(agent); // modelRuntime 现在在 Agent 中
245
+
246
+ // 测试消息
247
+ const testMessage = process.argv[2] || 'What time is it? Also calculate 15 * 8 + 7';
248
+ console.log(`💬 用户: ${testMessage}\n`);
249
+
250
+ // 创建初始状态
251
+ let state = AgentRuntime.createInitialState({
252
+ maxSteps: 10,
253
+ messages: [{ content: testMessage, role: 'user' }],
254
+ sessionId: 'simple-test',
255
+ });
256
+
257
+ console.log('🤖 AI: ');
258
+
259
+ // 执行对话循环
260
+ let nextContext: RuntimeContext | undefined = undefined;
261
+
262
+ while (state.status !== 'done' && state.status !== 'error') {
263
+ const result = await runtime.step(state, nextContext);
264
+
265
+ // 处理事件
266
+ for (const event of result.events) {
267
+ switch (event.type) {
268
+ case 'llm_stream': {
269
+ if ((event as any).chunk.content) {
270
+ process.stdout.write((event as any).chunk.content);
271
+ }
272
+ break;
273
+ }
274
+ case 'llm_result': {
275
+ if ((event as any).result.tool_calls) {
276
+ console.log('\n\n🔧 需要调用工具...');
277
+ }
278
+ break;
279
+ }
280
+ case 'tool_result': {
281
+ console.log(`\n🛠️ 工具执行结果:`, event.result);
282
+ console.log('\n🤖 AI: ');
283
+ break;
284
+ }
285
+ case 'done': {
286
+ console.log('\n\n✅ 对话完成');
287
+ break;
288
+ }
289
+ case 'error': {
290
+ console.error('\n❌ 错误:', event.error);
291
+ break;
292
+ }
293
+ }
294
+ }
295
+
296
+ state = result.newState;
297
+ nextContext = result.nextContext; // 使用返回的 nextContext
298
+ }
299
+
300
+ console.log(`\n📊 总共执行了 ${state.stepCount} 个步骤`);
301
+ }
302
+
303
+ main().catch(console.error);
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "@lobechat/agent-runtime",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "main": "./src/index.ts",
6
+ "scripts": {
7
+ "simple": "tsx examples/tools-calling.ts",
8
+ "test": "vitest",
9
+ "test:coverage": "vitest --coverage"
10
+ },
11
+ "dependencies": {},
12
+ "devDependencies": {
13
+ "openai": "^4.0.0"
14
+ }
15
+ }