@lobehub/lobehub 2.0.0-next.353 → 2.0.0-next.354

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 (119) hide show
  1. package/.agents/skills/add-provider-doc/SKILL.md +90 -0
  2. package/.agents/skills/add-setting-env/SKILL.md +106 -0
  3. package/.agents/skills/debug/SKILL.md +66 -0
  4. package/.agents/skills/desktop/SKILL.md +78 -0
  5. package/.agents/skills/desktop/references/feature-implementation.md +99 -0
  6. package/.agents/skills/desktop/references/local-tools.md +133 -0
  7. package/.agents/skills/desktop/references/menu-config.md +103 -0
  8. package/.agents/skills/desktop/references/window-management.md +143 -0
  9. package/.agents/skills/drizzle/SKILL.md +129 -0
  10. package/.agents/skills/drizzle/references/db-migrations.md +50 -0
  11. package/.agents/skills/hotkey/SKILL.md +90 -0
  12. package/{.cursor/rules/i18n.mdc → .agents/skills/i18n/SKILL.md} +14 -23
  13. package/.agents/skills/linear/SKILL.md +51 -0
  14. package/.agents/skills/microcopy/SKILL.md +83 -0
  15. package/.agents/skills/modal/SKILL.md +102 -0
  16. package/{.cursor/rules/project-structure.mdc → .agents/skills/project-overview/SKILL.md} +65 -37
  17. package/.agents/skills/react/SKILL.md +73 -0
  18. package/.agents/skills/react/references/layout-kit.md +100 -0
  19. package/.agents/skills/recent-data/SKILL.md +108 -0
  20. package/.agents/skills/testing/SKILL.md +89 -0
  21. package/.agents/skills/testing/references/agent-runtime-e2e.md +126 -0
  22. package/.agents/skills/testing/references/db-model-test.md +124 -0
  23. package/.agents/skills/testing/references/desktop-controller-test.md +124 -0
  24. package/.agents/skills/testing/references/electron-ipc-test.md +63 -0
  25. package/.agents/skills/testing/references/zustand-store-action-test.md +150 -0
  26. package/.agents/skills/typescript/SKILL.md +52 -0
  27. package/.agents/skills/zustand/SKILL.md +78 -0
  28. package/.agents/skills/zustand/references/action-patterns.md +125 -0
  29. package/.agents/skills/zustand/references/slice-organization.md +125 -0
  30. package/AGENTS.md +42 -55
  31. package/CHANGELOG.md +33 -0
  32. package/CLAUDE.md +57 -46
  33. package/GEMINI.md +47 -39
  34. package/changelog/v1.json +9 -0
  35. package/package.json +1 -1
  36. package/src/features/FileViewer/Renderer/PDF/index.tsx +2 -3
  37. package/src/features/ShareModal/SharePdf/PdfPreview.tsx +1 -2
  38. package/src/libs/pdfjs/index.tsx +25 -0
  39. package/src/store/test-coverage.md +5 -5
  40. package/.cursor/rules/add-provider-doc.mdc +0 -183
  41. package/.cursor/rules/add-setting-env.mdc +0 -175
  42. package/.cursor/rules/cursor-rules.mdc +0 -28
  43. package/.cursor/rules/db-migrations.mdc +0 -46
  44. package/.cursor/rules/debug-usage.mdc +0 -86
  45. package/.cursor/rules/desktop-controller-tests.mdc +0 -189
  46. package/.cursor/rules/desktop-feature-implementation.mdc +0 -155
  47. package/.cursor/rules/desktop-local-tools-implement.mdc +0 -81
  48. package/.cursor/rules/desktop-menu-configuration.mdc +0 -209
  49. package/.cursor/rules/desktop-window-management.mdc +0 -301
  50. package/.cursor/rules/drizzle-schema-style-guide.mdc +0 -218
  51. package/.cursor/rules/hotkey.mdc +0 -162
  52. package/.cursor/rules/linear.mdc +0 -53
  53. package/.cursor/rules/microcopy-cn.mdc +0 -158
  54. package/.cursor/rules/microcopy-en.mdc +0 -148
  55. package/.cursor/rules/modal-imperative.mdc +0 -162
  56. package/.cursor/rules/packages/react-layout-kit.mdc +0 -122
  57. package/.cursor/rules/project-introduce.mdc +0 -36
  58. package/.cursor/rules/react.mdc +0 -169
  59. package/.cursor/rules/recent-data-usage.mdc +0 -139
  60. package/.cursor/rules/rules-index.mdc +0 -44
  61. package/.cursor/rules/testing-guide/agent-runtime-e2e.mdc +0 -285
  62. package/.cursor/rules/testing-guide/db-model-test.mdc +0 -455
  63. package/.cursor/rules/testing-guide/electron-ipc-test.mdc +0 -80
  64. package/.cursor/rules/testing-guide/testing-guide.mdc +0 -534
  65. package/.cursor/rules/testing-guide/zustand-store-action-test.mdc +0 -574
  66. package/.cursor/rules/typescript.mdc +0 -55
  67. package/.cursor/rules/zustand-action-patterns.mdc +0 -328
  68. package/.cursor/rules/zustand-slice-organization.mdc +0 -308
  69. package/src/libs/pdfjs/pdf.worker.ts +0 -1
  70. package/src/libs/pdfjs/worker.ts +0 -12
  71. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/AGENTS.md +0 -0
  72. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/SKILL.md +0 -0
  73. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/advanced-event-handler-refs.md +0 -0
  74. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/advanced-use-latest.md +0 -0
  75. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-api-routes.md +0 -0
  76. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-defer-await.md +0 -0
  77. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-dependencies.md +0 -0
  78. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-parallel.md +0 -0
  79. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/async-suspense-boundaries.md +0 -0
  80. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-barrel-imports.md +0 -0
  81. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-conditional.md +0 -0
  82. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-defer-third-party.md +0 -0
  83. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-dynamic-imports.md +0 -0
  84. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/bundle-preload.md +0 -0
  85. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-event-listeners.md +0 -0
  86. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-localstorage-schema.md +0 -0
  87. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-passive-event-listeners.md +0 -0
  88. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/client-swr-dedup.md +0 -0
  89. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-batch-dom-css.md +0 -0
  90. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-cache-function-results.md +0 -0
  91. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-cache-property-access.md +0 -0
  92. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-cache-storage.md +0 -0
  93. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-combine-iterations.md +0 -0
  94. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-early-exit.md +0 -0
  95. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-hoist-regexp.md +0 -0
  96. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-index-maps.md +0 -0
  97. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-length-check-first.md +0 -0
  98. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-min-max-loop.md +0 -0
  99. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-set-map-lookups.md +0 -0
  100. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/js-tosorted-immutable.md +0 -0
  101. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-activity.md +0 -0
  102. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-animate-svg-wrapper.md +0 -0
  103. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-conditional-render.md +0 -0
  104. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-content-visibility.md +0 -0
  105. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-hoist-jsx.md +0 -0
  106. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-hydration-no-flicker.md +0 -0
  107. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rendering-svg-precision.md +0 -0
  108. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-defer-reads.md +0 -0
  109. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-dependencies.md +0 -0
  110. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-derived-state.md +0 -0
  111. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-functional-setstate.md +0 -0
  112. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-lazy-state-init.md +0 -0
  113. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-memo.md +0 -0
  114. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/rerender-transitions.md +0 -0
  115. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-after-nonblocking.md +0 -0
  116. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-cache-lru.md +0 -0
  117. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-cache-react.md +0 -0
  118. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-parallel-fetching.md +0 -0
  119. /package/.agents/{vercel-react-best-practices → skills/vercel-react-best-practices}/rules/server-serialization.md +0 -0
@@ -1,139 +0,0 @@
1
- # Recent Data 使用指南
2
-
3
- ## 概述
4
-
5
- Recent 数据(recentTopics, recentResources, recentPages)存储在 session store 中,可以在应用的任何地方访问。
6
-
7
- ## 数据初始化
8
-
9
- 在应用顶层(如 `RecentHydration.tsx`)中初始化所有 recent 数据:
10
-
11
- ```tsx
12
- import { useInitRecentPage } from '@/hooks/useInitRecentPage';
13
- import { useInitRecentResource } from '@/hooks/useInitRecentResource';
14
- import { useInitRecentTopic } from '@/hooks/useInitRecentTopic';
15
-
16
- const App = () => {
17
- // 初始化所有 recent 数据
18
- useInitRecentTopic();
19
- useInitRecentResource();
20
- useInitRecentPage();
21
-
22
- return <YourComponents />;
23
- };
24
- ```
25
-
26
- ## 使用方式
27
-
28
- ### 方式一:直接从 Store 读取(推荐用于多处使用)
29
-
30
- 在任何组件中直接访问 store 中的数据:
31
-
32
- ```tsx
33
- import { useSessionStore } from '@/store/session';
34
- import { recentSelectors } from '@/store/session/selectors';
35
-
36
- const Component = () => {
37
- // 读取数据
38
- const recentTopics = useSessionStore(recentSelectors.recentTopics);
39
- const isInit = useSessionStore(recentSelectors.isRecentTopicsInit);
40
-
41
- if (!isInit) return <div>Loading...</div>;
42
-
43
- return (
44
- <div>
45
- {recentTopics.map((topic) => (
46
- <div key={topic.id}>{topic.title}</div>
47
- ))}
48
- </div>
49
- );
50
- };
51
- ```
52
-
53
- ### 方式二:使用 Hook 返回的数据(用于单一组件)
54
-
55
- ```tsx
56
- import { useInitRecentTopic } from '@/hooks/useInitRecentTopic';
57
-
58
- const Component = () => {
59
- const { data: recentTopics, isLoading } = useInitRecentTopic();
60
-
61
- if (isLoading) return <div>Loading...</div>;
62
-
63
- return <div>{/* 使用 recentTopics */}</div>;
64
- };
65
- ```
66
-
67
- ## 可用的 Selectors
68
-
69
- ### Recent Topics (最近话题)
70
-
71
- ```tsx
72
- import { recentSelectors } from '@/store/session/selectors';
73
-
74
- // 数据
75
- const recentTopics = useSessionStore(recentSelectors.recentTopics);
76
- // 类型: RecentTopic[]
77
-
78
- // 初始化状态
79
- const isInit = useSessionStore(recentSelectors.isRecentTopicsInit);
80
- // 类型: boolean
81
- ```
82
-
83
- **RecentTopic 类型:**
84
-
85
- ```typescript
86
- interface RecentTopic {
87
- agent: {
88
- avatar: string | null;
89
- backgroundColor: string | null;
90
- id: string;
91
- title: string | null;
92
- } | null;
93
- id: string;
94
- title: string | null;
95
- updatedAt: Date;
96
- }
97
- ```
98
-
99
- ### Recent Resources (最近文件)
100
-
101
- ```tsx
102
- import { recentSelectors } from '@/store/session/selectors';
103
-
104
- // 数据
105
- const recentResources = useSessionStore(recentSelectors.recentResources);
106
- // 类型: FileListItem[]
107
-
108
- // 初始化状态
109
- const isInit = useSessionStore(recentSelectors.isRecentResourcesInit);
110
- // 类型: boolean
111
- ```
112
-
113
- ### Recent Pages (最近页面)
114
-
115
- ```tsx
116
- import { recentSelectors } from '@/store/session/selectors';
117
-
118
- // 数据
119
- const recentPages = useSessionStore(recentSelectors.recentPages);
120
- // 类型: any[]
121
-
122
- // 初始化状态
123
- const isInit = useSessionStore(recentSelectors.isRecentPagesInit);
124
- // 类型: boolean
125
- ```
126
-
127
- ## 特性
128
-
129
- 1. **自动登录检测**:只有在用户登录时才会加载数据
130
- 2. **数据缓存**:数据存储在 store 中,多处使用无需重复加载
131
- 3. **自动刷新**:使用 SWR,在用户重新聚焦时自动刷新(5分钟间隔)
132
- 4. **类型安全**:完整的 TypeScript 类型定义
133
-
134
- ## 最佳实践
135
-
136
- 1. **初始化位置**:在应用顶层统一初始化所有 recent 数据
137
- 2. **数据访问**:使用 selectors 从 store 读取数据
138
- 3. **多处使用**:同一数据在多个组件中使用时,推荐使用方式一(直接从 store 读取)
139
- 4. **性能优化**:使用 selector 确保只有相关数据变化时才重新渲染
@@ -1,44 +0,0 @@
1
- ---
2
- description:
3
- globs:
4
- alwaysApply: true
5
- ---
6
-
7
- # Available project rules index
8
-
9
- All following rules are saved under `.cursor/rules/` directory:
10
-
11
- ## Backend
12
-
13
- - `drizzle-schema-style-guide.mdc` – Style guide for defining Drizzle ORM schemas
14
-
15
- ## Frontend
16
-
17
- - `react.mdc` – React component style guide and conventions
18
- - `i18n.mdc` – Internationalization guide using react-i18next
19
- - `typescript.mdc` – TypeScript code style guide
20
- - `packages/react-layout-kit.mdc` – Usage guide for react-layout-kit
21
- - `modal-imperative.mdc` – Modal imperative API usage guide (createRawModal/createModal)
22
-
23
- ## State Management
24
-
25
- - `zustand-action-patterns.mdc` – Recommended patterns for organizing Zustand actions
26
- - `zustand-slice-organization.mdc` – Best practices for structuring Zustand slices
27
-
28
- ## Desktop (Electron)
29
-
30
- - `desktop-feature-implementation.mdc` – Implementing new Electron desktop features
31
- - `desktop-controller-tests.mdc` – Desktop controller unit testing guide
32
- - `desktop-local-tools-implement.mdc` – Workflow to add new desktop local tools
33
- - `desktop-menu-configuration.mdc` – Desktop menu configuration guide
34
- - `desktop-window-management.mdc` – Desktop window management guide
35
-
36
- ## Debugging
37
-
38
- - `debug-usage.mdc` – Using the debug package and namespace conventions
39
-
40
- ## Testing
41
-
42
- - `testing-guide/testing-guide.mdc` – Comprehensive testing guide for Vitest
43
- - `testing-guide/electron-ipc-test.mdc` – Electron IPC interface testing strategy
44
- - `testing-guide/db-model-test.mdc` – Database Model testing guide
@@ -1,285 +0,0 @@
1
- # Agent Runtime E2E 测试指南
2
-
3
- 本文档描述 Agent Runtime 端到端测试的核心原则和实施方法。
4
-
5
- ## 核心原则
6
-
7
- ### 1. 最小化 Mock 原则
8
-
9
- E2E 测试的目标是尽可能接近真实运行环境。因此,我们只 Mock **三个外部依赖**:
10
-
11
- | 依赖 | Mock 方式 | 说明 |
12
- | --- | --- | --- |
13
- | **Database** | PGLite | 使用 `@lobechat/database/test-utils` 提供的内存数据库 |
14
- | **Redis** | InMemoryAgentStateManager | Mock `AgentStateManager` 使用内存实现 |
15
- | **Redis** | InMemoryStreamEventManager | Mock `StreamEventManager` 使用内存实现 |
16
-
17
- **不 Mock 的部分:**
18
-
19
- - `model-bank` - 使用真实的模型配置数据
20
- - `Mecha` (AgentToolsEngine, ContextEngineering) - 使用真实逻辑
21
- - `AgentRuntimeService` - 使用真实逻辑
22
- - `AgentRuntimeCoordinator` - 使用真实逻辑
23
-
24
- ### 2. 使用 vi.spyOn 而非 vi.mock
25
-
26
- 不同测试场景需要不同的 LLM 响应。使用 `vi.spyOn` 可以:
27
-
28
- - 在每个测试中灵活控制返回值
29
- - 便于测试不同场景(纯文本、tool calls、错误等)
30
- - 避免全局 mock 导致的测试隔离问题
31
-
32
- ### 3. 默认模型使用 gpt-5
33
-
34
- - `model-bank` 中肯定有该模型的数据
35
- - 避免短期内因模型更新需要修改测试
36
-
37
- ## 技术实现
38
-
39
- ### 数据库设置
40
-
41
- ```typescript
42
- import { LobeChatDatabase } from '@lobechat/database';
43
- import { getTestDB } from '@lobechat/database/test-utils';
44
-
45
- let testDB: LobeChatDatabase;
46
-
47
- beforeEach(async () => {
48
- testDB = await getTestDB();
49
- });
50
- ```
51
-
52
- ### OpenAI Response Mock Helper
53
-
54
- 创建一个 helper 函数来生成 OpenAI 格式的流式响应:
55
-
56
- ```typescript
57
- /**
58
- * 创建 OpenAI 格式的流式响应
59
- */
60
- export const createOpenAIStreamResponse = (options: {
61
- content?: string;
62
- toolCalls?: Array<{
63
- id: string;
64
- name: string;
65
- arguments: string;
66
- }>;
67
- finishReason?: 'stop' | 'tool_calls';
68
- }) => {
69
- const { content, toolCalls, finishReason = 'stop' } = options;
70
-
71
- return new Response(
72
- new ReadableStream({
73
- start(controller) {
74
- const encoder = new TextEncoder();
75
-
76
- // 发送内容 chunk
77
- if (content) {
78
- const chunk = {
79
- id: 'chatcmpl-mock',
80
- object: 'chat.completion.chunk',
81
- model: 'gpt-5',
82
- choices: [
83
- {
84
- index: 0,
85
- delta: { content },
86
- finish_reason: null,
87
- },
88
- ],
89
- };
90
- controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}\n\n`));
91
- }
92
-
93
- // 发送 tool_calls chunk
94
- if (toolCalls) {
95
- for (const tool of toolCalls) {
96
- const chunk = {
97
- id: 'chatcmpl-mock',
98
- object: 'chat.completion.chunk',
99
- model: 'gpt-5',
100
- choices: [
101
- {
102
- index: 0,
103
- delta: {
104
- tool_calls: [
105
- {
106
- index: 0,
107
- id: tool.id,
108
- type: 'function',
109
- function: {
110
- name: tool.name,
111
- arguments: tool.arguments,
112
- },
113
- },
114
- ],
115
- },
116
- finish_reason: null,
117
- },
118
- ],
119
- };
120
- controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}\n\n`));
121
- }
122
- }
123
-
124
- // 发送完成 chunk
125
- const finishChunk = {
126
- id: 'chatcmpl-mock',
127
- object: 'chat.completion.chunk',
128
- model: 'gpt-5',
129
- choices: [
130
- {
131
- index: 0,
132
- delta: {},
133
- finish_reason: finishReason,
134
- },
135
- ],
136
- };
137
- controller.enqueue(encoder.encode(`data: ${JSON.stringify(finishChunk)}\n\n`));
138
- controller.enqueue(encoder.encode('data: [DONE]\n\n'));
139
- controller.close();
140
- },
141
- }),
142
- { headers: { 'content-type': 'text/event-stream' } },
143
- );
144
- };
145
- ```
146
-
147
- ### 内存状态管理
148
-
149
- 使用依赖注入替代 Redis:
150
-
151
- ```typescript
152
- import {
153
- InMemoryAgentStateManager,
154
- InMemoryStreamEventManager,
155
- } from '@/server/modules/AgentRuntime';
156
- import { AgentRuntimeService } from '@/server/services/agentRuntime';
157
-
158
- const stateManager = new InMemoryAgentStateManager();
159
- const streamEventManager = new InMemoryStreamEventManager();
160
-
161
- const service = new AgentRuntimeService(serverDB, userId, {
162
- coordinatorOptions: {
163
- stateManager,
164
- streamEventManager,
165
- },
166
- queueService: null, // 禁用 QStash 队列,使用 executeSync
167
- streamEventManager,
168
- });
169
- ```
170
-
171
- ### Mock OpenAI API
172
-
173
- 在测试中使用 `vi.spyOn` mock fetch:
174
-
175
- ```typescript
176
- import { vi } from 'vitest';
177
-
178
- // 在测试文件顶部或 beforeEach 中
179
- const fetchSpy = vi.spyOn(globalThis, 'fetch');
180
-
181
- // 在具体测试中设置返回值
182
- it('should handle text response', async () => {
183
- fetchSpy.mockResolvedValueOnce(createOpenAIStreamResponse({ content: '杭州今天天气晴朗' }));
184
-
185
- // ... 执行测试
186
- });
187
-
188
- it('should handle tool calls', async () => {
189
- fetchSpy.mockResolvedValueOnce(
190
- createOpenAIStreamResponse({
191
- toolCalls: [
192
- {
193
- id: 'call_123',
194
- name: 'lobe-web-browsing____search____builtin',
195
- arguments: JSON.stringify({ query: '杭州天气' }),
196
- },
197
- ],
198
- finishReason: 'tool_calls',
199
- }),
200
- );
201
-
202
- // ... 执行测试
203
- });
204
- ```
205
-
206
- ## 测试场景
207
-
208
- ### 1. 基本对话测试
209
-
210
- ```typescript
211
- describe('Basic Chat', () => {
212
- it('should complete a simple conversation', async () => {
213
- fetchSpy.mockResolvedValueOnce(
214
- createOpenAIStreamResponse({ content: 'Hello! How can I help you?' }),
215
- );
216
-
217
- const result = await service.createOperation({
218
- agentConfig: { model: 'gpt-5', provider: 'openai' },
219
- initialMessages: [{ role: 'user', content: 'Hi' }],
220
- // ...
221
- });
222
-
223
- const finalState = await service.executeSync(result.operationId);
224
- expect(finalState.status).toBe('done');
225
- });
226
- });
227
- ```
228
-
229
- ### 2. Tool 调用测试
230
-
231
- ```typescript
232
- describe('Tool Calls', () => {
233
- it('should execute web-browsing tool', async () => {
234
- // 第一次调用:LLM 返回 tool_calls
235
- fetchSpy.mockResolvedValueOnce(
236
- createOpenAIStreamResponse({
237
- toolCalls: [
238
- {
239
- id: 'call_123',
240
- name: 'lobe-web-browsing____search____builtin',
241
- arguments: JSON.stringify({ query: '杭州天气' }),
242
- },
243
- ],
244
- finishReason: 'tool_calls',
245
- }),
246
- );
247
-
248
- // 第二次调用:处理 tool 结果后的响应
249
- fetchSpy.mockResolvedValueOnce(
250
- createOpenAIStreamResponse({ content: '根据搜索结果,杭州今天...' }),
251
- );
252
-
253
- // ... 执行测试
254
- });
255
- });
256
- ```
257
-
258
- ### 3. 错误处理测试
259
-
260
- ```typescript
261
- describe('Error Handling', () => {
262
- it('should handle API errors gracefully', async () => {
263
- fetchSpy.mockRejectedValueOnce(new Error('API rate limit exceeded'));
264
-
265
- // ... 执行测试并验证错误处理
266
- });
267
- });
268
- ```
269
-
270
- ## 文件组织
271
-
272
- ```
273
- src/server/routers/lambda/__tests__/integration/
274
- ├── setup.ts # 测试设置工具
275
- ├── aiAgent.integration.test.ts # 现有集成测试
276
- ├── aiAgent.e2e.test.ts # E2E 测试
277
- └── helpers/
278
- └── openaiMock.ts # OpenAI mock helper
279
- ```
280
-
281
- ## 注意事项
282
-
283
- 1. **测试隔离**:每个测试后清理 `InMemoryAgentStateManager` 和 `InMemoryStreamEventManager`
284
- 2. **超时设置**:E2E 测试可能需要更长的超时时间
285
- 3. **调试**:使用 `DEBUG=lobe-server:*` 环境变量查看详细日志