@lobehub/chat 1.77.16 → 1.77.18

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 (145) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/contributing/Basic/Architecture.md +1 -1
  4. package/contributing/Basic/Architecture.zh-CN.md +1 -1
  5. package/contributing/Basic/Chat-API.md +326 -108
  6. package/contributing/Basic/Chat-API.zh-CN.md +313 -133
  7. package/contributing/Basic/Contributing-Guidelines.md +7 -4
  8. package/contributing/Basic/Contributing-Guidelines.zh-CN.md +7 -6
  9. package/contributing/Home.md +5 -5
  10. package/contributing/State-Management/State-Management-Intro.md +1 -1
  11. package/contributing/State-Management/State-Management-Intro.zh-CN.md +1 -1
  12. package/docker-compose/local/docker-compose.yml +2 -1
  13. package/locales/ar/components.json +4 -0
  14. package/locales/ar/modelProvider.json +1 -0
  15. package/locales/ar/models.json +8 -5
  16. package/locales/ar/tool.json +21 -1
  17. package/locales/bg-BG/components.json +4 -0
  18. package/locales/bg-BG/modelProvider.json +1 -0
  19. package/locales/bg-BG/models.json +8 -5
  20. package/locales/bg-BG/tool.json +21 -1
  21. package/locales/de-DE/components.json +4 -0
  22. package/locales/de-DE/modelProvider.json +1 -0
  23. package/locales/de-DE/models.json +8 -5
  24. package/locales/de-DE/tool.json +21 -1
  25. package/locales/en-US/components.json +4 -0
  26. package/locales/en-US/modelProvider.json +1 -0
  27. package/locales/en-US/models.json +8 -5
  28. package/locales/en-US/tool.json +21 -1
  29. package/locales/es-ES/components.json +4 -0
  30. package/locales/es-ES/modelProvider.json +1 -0
  31. package/locales/es-ES/models.json +7 -4
  32. package/locales/es-ES/tool.json +21 -1
  33. package/locales/fa-IR/components.json +4 -0
  34. package/locales/fa-IR/modelProvider.json +1 -0
  35. package/locales/fa-IR/models.json +7 -4
  36. package/locales/fa-IR/tool.json +21 -1
  37. package/locales/fr-FR/components.json +4 -0
  38. package/locales/fr-FR/modelProvider.json +1 -0
  39. package/locales/fr-FR/models.json +8 -5
  40. package/locales/fr-FR/tool.json +21 -1
  41. package/locales/it-IT/components.json +4 -0
  42. package/locales/it-IT/modelProvider.json +1 -0
  43. package/locales/it-IT/models.json +7 -4
  44. package/locales/it-IT/tool.json +21 -1
  45. package/locales/ja-JP/components.json +4 -0
  46. package/locales/ja-JP/modelProvider.json +1 -0
  47. package/locales/ja-JP/models.json +8 -5
  48. package/locales/ja-JP/tool.json +21 -1
  49. package/locales/ko-KR/components.json +4 -0
  50. package/locales/ko-KR/modelProvider.json +1 -0
  51. package/locales/ko-KR/models.json +8 -5
  52. package/locales/ko-KR/tool.json +21 -1
  53. package/locales/nl-NL/components.json +4 -0
  54. package/locales/nl-NL/modelProvider.json +1 -0
  55. package/locales/nl-NL/models.json +8 -5
  56. package/locales/nl-NL/tool.json +21 -1
  57. package/locales/pl-PL/components.json +4 -0
  58. package/locales/pl-PL/modelProvider.json +1 -0
  59. package/locales/pl-PL/models.json +8 -5
  60. package/locales/pl-PL/tool.json +21 -1
  61. package/locales/pt-BR/components.json +4 -0
  62. package/locales/pt-BR/modelProvider.json +1 -0
  63. package/locales/pt-BR/models.json +7 -4
  64. package/locales/pt-BR/tool.json +21 -1
  65. package/locales/ru-RU/components.json +4 -0
  66. package/locales/ru-RU/modelProvider.json +1 -0
  67. package/locales/ru-RU/models.json +7 -4
  68. package/locales/ru-RU/tool.json +21 -1
  69. package/locales/tr-TR/components.json +4 -0
  70. package/locales/tr-TR/modelProvider.json +1 -0
  71. package/locales/tr-TR/models.json +8 -5
  72. package/locales/tr-TR/tool.json +21 -1
  73. package/locales/vi-VN/components.json +4 -0
  74. package/locales/vi-VN/modelProvider.json +1 -0
  75. package/locales/vi-VN/models.json +8 -5
  76. package/locales/vi-VN/tool.json +21 -1
  77. package/locales/zh-CN/components.json +4 -0
  78. package/locales/zh-CN/modelProvider.json +1 -0
  79. package/locales/zh-CN/models.json +9 -6
  80. package/locales/zh-CN/tool.json +30 -1
  81. package/locales/zh-TW/components.json +4 -0
  82. package/locales/zh-TW/modelProvider.json +1 -0
  83. package/locales/zh-TW/models.json +7 -4
  84. package/locales/zh-TW/tool.json +21 -1
  85. package/package.json +1 -1
  86. package/src/app/(backend)/webapi/models/[provider]/pull/route.ts +34 -0
  87. package/src/app/(backend)/webapi/{chat/models → models}/[provider]/route.ts +1 -2
  88. package/src/app/[variants]/(main)/settings/llm/ProviderList/Ollama/index.tsx +0 -7
  89. package/src/app/[variants]/(main)/settings/provider/(detail)/ollama/CheckError.tsx +1 -1
  90. package/src/components/FormAction/index.tsx +1 -1
  91. package/src/database/models/__tests__/aiProvider.test.ts +100 -0
  92. package/src/database/models/aiProvider.ts +11 -1
  93. package/src/features/Conversation/Error/OllamaBizError/InvalidOllamaModel.tsx +43 -0
  94. package/src/features/Conversation/Error/OllamaDesktopSetupGuide/index.tsx +61 -0
  95. package/src/features/Conversation/Error/index.tsx +7 -0
  96. package/src/features/DevPanel/SystemInspector/ServerConfig.tsx +18 -2
  97. package/src/features/DevPanel/SystemInspector/index.tsx +25 -6
  98. package/src/features/OllamaModelDownloader/index.tsx +149 -0
  99. package/src/libs/agent-runtime/AgentRuntime.ts +6 -0
  100. package/src/libs/agent-runtime/BaseAI.ts +7 -0
  101. package/src/libs/agent-runtime/ollama/index.ts +84 -2
  102. package/src/libs/agent-runtime/openrouter/__snapshots__/index.test.ts.snap +24 -3263
  103. package/src/libs/agent-runtime/openrouter/fixtures/frontendModels.json +25 -0
  104. package/src/libs/agent-runtime/openrouter/fixtures/models.json +0 -3353
  105. package/src/libs/agent-runtime/openrouter/index.test.ts +56 -1
  106. package/src/libs/agent-runtime/openrouter/index.ts +9 -4
  107. package/src/libs/agent-runtime/types/index.ts +1 -0
  108. package/src/libs/agent-runtime/types/model.ts +44 -0
  109. package/src/libs/agent-runtime/utils/streams/index.ts +1 -0
  110. package/src/libs/agent-runtime/utils/streams/model.ts +110 -0
  111. package/src/locales/default/components.ts +4 -0
  112. package/src/locales/default/modelProvider.ts +1 -0
  113. package/src/locales/default/tool.ts +30 -1
  114. package/src/server/modules/SearXNG.ts +10 -2
  115. package/src/server/routers/tools/__test__/search.test.ts +3 -1
  116. package/src/server/routers/tools/search.ts +10 -2
  117. package/src/services/__tests__/models.test.ts +21 -0
  118. package/src/services/_url.ts +4 -1
  119. package/src/services/chat.ts +1 -1
  120. package/src/services/models.ts +153 -7
  121. package/src/services/search.ts +2 -2
  122. package/src/store/aiInfra/slices/aiModel/action.ts +1 -1
  123. package/src/store/aiInfra/slices/aiProvider/action.ts +2 -1
  124. package/src/store/chat/slices/builtinTool/actions/searXNG.test.ts +28 -8
  125. package/src/store/chat/slices/builtinTool/actions/searXNG.ts +22 -5
  126. package/src/store/user/slices/modelList/action.test.ts +2 -2
  127. package/src/store/user/slices/modelList/action.ts +1 -1
  128. package/src/tools/web-browsing/Portal/Search/index.tsx +1 -1
  129. package/src/tools/web-browsing/Render/Search/SearchQuery/SearchView.tsx +1 -1
  130. package/src/tools/web-browsing/Render/Search/SearchQuery/index.tsx +1 -1
  131. package/src/tools/web-browsing/Render/Search/SearchResult/index.tsx +1 -1
  132. package/src/tools/web-browsing/components/CategoryAvatar.tsx +27 -0
  133. package/src/tools/web-browsing/components/SearchBar.tsx +84 -4
  134. package/src/tools/web-browsing/const.ts +26 -0
  135. package/src/tools/web-browsing/index.ts +58 -28
  136. package/src/tools/web-browsing/systemRole.ts +62 -1
  137. package/src/types/tool/search.ts +10 -1
  138. package/src/app/[variants]/(main)/settings/llm/ProviderList/Ollama/Checker.tsx +0 -73
  139. package/src/app/[variants]/(main)/settings/provider/(detail)/ollama/OllamaModelDownloader/index.tsx +0 -127
  140. package/src/features/Conversation/Error/OllamaBizError/InvalidOllamaModel/index.tsx +0 -154
  141. package/src/features/Conversation/Error/OllamaBizError/InvalidOllamaModel/useDownloadMonitor.ts +0 -29
  142. package/src/helpers/url.ts +0 -17
  143. package/src/services/__tests__/ollama.test.ts +0 -28
  144. package/src/services/ollama.ts +0 -83
  145. /package/src/{app/[variants]/(main)/settings/provider/(detail)/ollama → features}/OllamaModelDownloader/useDownloadMonitor.ts +0 -0
@@ -1,174 +1,354 @@
1
- # 会话 API 实现逻辑
1
+ # Lobe Chat API 前后端交互逻辑
2
2
 
3
- LobeChat 的大模型 AI 实现主要依赖于 OpenAI 的 API,包括后端的核心会话 API 和前端的集成 API。接下来,我们将分别介绍后端和前端的实现思路和代码。
3
+ 本文档说明了 Lobe Chat API 在前后端交互中的实现逻辑,包括事件序列和涉及的核心组件。
4
4
 
5
5
  #### TOC
6
6
 
7
- - [后端实现](#后端实现)
8
- - [核心会话 API](#核心会话-api)
9
- - [会话结果处理](#会话结果处理)
10
- - [前端实现](#前端实现)
11
- - [前端集成](#前端集成)
12
- - [使用流式获取结果](#使用流式获取结果)
7
+ - [交互时序图](#交互时序图)
8
+ - [主要步骤说明](#主要步骤说明)
9
+ - [AgentRuntime 说明](#agentruntime-说明)
13
10
 
14
- ## 后端实现
11
+ ## 交互时序图
15
12
 
16
- 以下代码中移除了鉴权、错误处理等逻辑,仅保留了核心的主要功能逻辑。
13
+ ```mermaid
14
+ sequenceDiagram
15
+ participant Client as 前端客户端
16
+ participant ChatService as 前端 ChatService
17
+ participant ChatAPI as 后端 Chat API
18
+ participant AgentRuntime as AgentRuntime
19
+ participant ModelProvider as 模型提供商 API
20
+ participant PluginGateway as 插件网关
17
21
 
18
- ### 核心会话 API
22
+ Client->>ChatService: 调用 createAssistantMessage
23
+ Note over ChatService: 处理消息、工具和参数
19
24
 
20
- `src/app/api/openai/chat/route.ts` 中,定义了一个处理 POST 请求的方法,主要负责从请求体中提取 `OpenAIChatStreamPayload` 类型的 payload,并使用 `createBizOpenAI` 函数根据请求和模型信息创建 OpenAI 实例。随后,该方法调用 `createChatCompletion` 来处理实际的会话,并返回响应结果。如果创建 OpenAI 实例过程中出现错误,则直接返回错误响应。
25
+ ChatService->>ChatService: 调用 getChatCompletion
26
+ Note over ChatService: 准备请求参数
21
27
 
22
- ```ts
23
- export const POST = async (req: Request) => {
24
- const payload = (await req.json()) as OpenAIChatStreamPayload;
28
+ ChatService->>ChatAPI: 发送 POST 请求到 /webapi/chat/[provider]
25
29
 
26
- const openaiOrErrResponse = createBizOpenAI(req, payload.model);
30
+ ChatAPI->>AgentRuntime: 初始化 AgentRuntime
31
+ Note over AgentRuntime: 通过 provider 和 用户配置创建运行时
27
32
 
28
- // if resOrOpenAI is a Response, it means there is an error,just return it
29
- if (openaiOrErrResponse instanceof Response) return openaiOrErrResponse;
33
+ ChatAPI->>AgentRuntime: 调用 chat 方法
34
+ AgentRuntime->>ModelProvider: 发送 chat completion 请求
30
35
 
31
- return createChatCompletion({ openai: openaiOrErrResponse, payload });
32
- };
33
- ```
36
+ ModelProvider-->>AgentRuntime: 返回流式响应
37
+ AgentRuntime-->>ChatAPI: 处理响应并返回 stream
34
38
 
35
- ### 会话结果处理
36
-
37
- 而在 `src/app/api/openai/chat/createChatCompletion.ts` 文件中,`createChatCompletion` 方法主要负责与 OpenAI API 进行交互,处理会话请求。它首先对 payload 中的消息进行预处理,然后通过 `openai.chat.completions.create` 方法发送 API 请求,并使用 `OpenAIStream` 将返回的响应转换为流式格式。如果在 API 调用过程中出现错误,方法将生成并处理相应的错误响应。
38
-
39
- ```ts
40
- import { OpenAIStream, StreamingTextResponse } from 'ai';
41
-
42
- export const createChatCompletion = async ({ payload, openai }: CreateChatCompletionOptions) => {
43
- // 预处理消息
44
- const { messages, ...params } = payload;
45
- // 发送 API 请求
46
- try {
47
- const response = await openai.chat.completions.create(
48
- {
49
- messages,
50
- ...params,
51
- stream: true,
52
- } as unknown as OpenAI.ChatCompletionCreateParamsStreaming,
53
- { headers: { Accept: '*/*' } },
54
- );
55
- const stream = OpenAIStream(response);
56
- return new StreamingTextResponse(stream);
57
- } catch (error) {
58
- // 检查错误是否为 OpenAI APIError
59
- if (error instanceof OpenAI.APIError) {
60
- let errorResult: any;
61
- // 如果错误是 OpenAI APIError,那么会有一个 error 对象
62
- if (error.error) {
63
- errorResult = error.error;
64
- } else if (error.cause) {
65
- errorResult = error.cause;
66
- }
67
- // 如果没有其他请求错误,错误对象是一个类似 Response 的对象
68
- else {
69
- errorResult = { headers: error.headers, stack: error.stack, status: error.status };
70
- }
71
- console.error(errorResult);
72
- // 返回错误响应
73
- return createErrorResponse(ChatErrorType.OpenAIBizError, {
74
- endpoint: openai.baseURL,
75
- error: errorResult,
76
- });
77
- }
78
- console.error(error);
79
- return createErrorResponse(ChatErrorType.InternalServerError, {
80
- endpoint: openai.baseURL,
81
- error: JSON.stringify(error),
82
- });
83
- }
84
- };
39
+ ChatAPI-->>ChatService: 流式返回 SSE 响应
40
+
41
+ ChatService->>ChatService: 使用 fetchSSE 处理流式响应
42
+ Note over ChatService: 通过 fetchEventSource 处理事件流
43
+
44
+ loop 对于每个数据块
45
+ ChatService->>ChatService: 处理不同类型的事件 (text, tool_calls, reasoning 等)
46
+ ChatService-->>Client: 通过 onMessageHandle 回调返回当前块
47
+ end
48
+
49
+ ChatService-->>Client: 通过 onFinish 回调返回完整结果
50
+
51
+ Note over ChatService,ModelProvider: 插件调用场景
52
+ ModelProvider-->>ChatService: 返回包含 tool_calls 的响应
53
+ ChatService->>ChatService: 解析工具调用
54
+ ChatService->>ChatService: 调用 runPluginApi
55
+ ChatService->>PluginGateway: 发送插件请求到网关
56
+ PluginGateway-->>ChatService: 返回插件执行结果
57
+ ChatService->>ModelProvider: 将插件结果返回给模型
58
+ ModelProvider-->>ChatService: 基于插件结果生成最终响应
59
+
60
+ Note over ChatService,ModelProvider: 预设任务场景
61
+ Client->>ChatService: 触发预设任务(如自动翻译、搜索等)
62
+ ChatService->>ChatService: 调用 fetchPresetTaskResult
63
+ ChatService->>ChatAPI: 发送预设任务请求
64
+ ChatAPI-->>ChatService: 返回任务结果
65
+ ChatService-->>Client: 通过回调函数返回结果
85
66
  ```
86
67
 
87
- ## 前端实现
68
+ ## 主要步骤说明
88
69
 
89
- ### 前端集成
70
+ 1. **客户端发起请求**:客户端调用前端 ChatService 的 createAssistantMessage 方法。
90
71
 
91
- 在 `src/services/chat.ts` 文件中,我们定义了 `ChatService` 类。这个类提供了一些方法来处理与 OpenAI 聊天 API 的交互。
72
+ 2. **前端处理请求**:
92
73
 
93
- `createAssistantMessage` 方法用于创建一个新的助手消息。它接收一个包含插件、消息和其他参数的对象,以及一个可选的 `FetchOptions` 对象。这个方法会合并默认的代理配置和传入的参数,预处理消息和工具,然后调用 `getChatCompletion` 方法获取聊天完成任务。
74
+ - `src/services/chat.ts` 对消息、工具和参数进行预处理
75
+ - 调用 getChatCompletion 准备请求参数
76
+ - 使用 `src/utils/fetch/fetchSSE.ts` 发送请求到后端 API
94
77
 
95
- `getChatCompletion` 方法用于获取聊天完成任务。它接收一个 `OpenAIChatStreamPayload` 对象和一个可选的 `FetchOptions` 对象。这个方法会合并默认的代理配置和传入的参数,然后发送 POST 请求到 OpenAI 的聊天 API。
78
+ 3. **后端处理请求**:
96
79
 
97
- `runPluginApi` 方法用于运行插件 API 并获取结果。它接收一个 `PluginRequestPayload` 对象和一个可选的 `FetchOptions` 对象。这个方法会从工具存储中获取状态,通过插件标识符获取插件设置和清单,然后发送 POST 请求到插件的网关 URL。
80
+ - `src/app/(backend)/webapi/chat/[provider]/route.ts` 接收请求
81
+ - 初始化 AgentRuntime
82
+ - 根据用户配置和提供商创建相应的模型实例
98
83
 
99
- `fetchPresetTaskResult` 方法用于获取预设任务的结果。它使用 `fetchAIFactory` 工厂函数创建一个新的函数,这个函数接收一个聊天完成任务的参数,并返回一个 Promise。当 Promise 解析时,返回的结果是聊天完成任务的结果。
84
+ 4. **模型调用**:
100
85
 
101
- `processMessages` 方法用于处理聊天消息。它接收一个聊天消息数组,一个可选的模型名称,和一个可选的工具数组。这个方法会处理消息内容,将输入的 `messages` 数组映射为 `OpenAIChatMessage` 类型的数组,如果存在启用的工具,将工具的系统角色添加到系统消息中。
86
+ - `src/libs/agent-runtime/AgentRuntime.ts` 调用相应模型提供商的 API
87
+ - 返回流式响应
102
88
 
103
- ```ts
104
- class ChatService {
105
- // 创建一个新的助手消息
106
- createAssistantMessage(params: object, fetchOptions?: FetchOptions) {
107
- // 实现细节...
108
- }
89
+ 5. **处理响应**:
90
+
91
+ - 后端将模型响应转换为 Stream 返回
92
+ - 前端通过 fetchSSE 和 [fetchEventSource](https://github.com/Azure/fetch-event-source) 处理流式响应
93
+ - 对不同类型的事件(文本、工具调用、推理等)进行处理
94
+ - 通过回调函数将结果传递回客户端
109
95
 
110
- // 获取聊天完成任务
111
- getChatCompletion(payload: OpenAIChatStreamPayload, fetchOptions?: FetchOptions) {
112
- // 实现细节...
113
- }
96
+ 6. **插件调用场景**:
114
97
 
115
- // 运行插件 API 并获取结果
116
- runPluginApi(payload: PluginRequestPayload, fetchOptions?: FetchOptions) {
117
- // 实现细节...
118
- }
98
+ AI 模型在响应中返回 `tool_calls` 字段时,会触发插件调用流程:
119
99
 
120
- // 获取预设任务的结果
121
- fetchPresetTaskResult() {
122
- // 实现细节...
123
- }
100
+ - AI 模型返回包含 `tool_calls` 的响应,表明需要调用工具
101
+ - 前端通过 `internal_callPluginApi` 方法处理工具调用
102
+ - 调用 `runPluginApi` 方法执行插件功能,包括获取插件设置和清单、创建认证请求头、发送请求到插件网关
103
+ - 插件执行完成后,结果返回给 AI 模型,模型基于结果生成最终响应
124
104
 
125
- // 处理聊天消息
126
- processMessages(messages: ChatMessage[], modelName?: string, tools?: Tool[]) {
127
- // 实现细节...
128
- }
129
- }
130
- ```
105
+ **实际应用示例**:
106
+
107
+ - **搜索插件**:当用户需要获取实时信息时,AI 会调用网页搜索插件来获取最新数据
108
+ - **DALL-E 插件**:用户要求生成图片时,AI 调用 DALL-E 插件创建图像
109
+ - **Midjourney 插件**:提供更高质量的图像生成能力,通过 API 调用 Midjourney 服务
131
110
 
132
- ### 使用流式获取结果
111
+ 7. **预设任务处理**:
133
112
 
134
- `src/utils/fetch.ts` 文件中,我们定义了 `fetchSSE` 方法,该方法使用流式方法获取数据,当读取到新的数据块时,会调用 `onMessageHandle` 回调函数处理数据块,进而实现打字机输出效果。
113
+ 预设任务是指系统预定义的特定功能任务,通常在用户执行特定操作时触发(而非常规聊天流程的一部分)。这些任务使用 `fetchPresetTaskResult` 方法执行,该方法与正常聊天流程类似,但会使用专门设计的提示词(prompt chain)。
135
114
 
136
- ```ts
137
- export const fetchSSE = async (fetchFn: () => Promise<Response>, options: FetchSSEOptions = {}) => {
138
- const response = await fetchFn();
115
+ **执行时机**:预设任务主要在以下场景被触发:
139
116
 
140
- // 如果不 ok 说明有请求错误
141
- if (!response.ok) {
142
- const chatMessageError = await getMessageError(response);
117
+ 1. **角色信息自动生成**:当用户创建或编辑角色时触发
143
118
 
144
- options.onErrorHandle?.(chatMessageError);
145
- return;
146
- }
119
+ - 角色头像生成(通过 `autoPickEmoji` 方法)
120
+ - 角色描述生成(通过 `autocompleteAgentDescription` 方法)
121
+ - 角色标签生成(通过 `autocompleteAgentTags` 方法)
122
+ - 角色标题生成(通过 `autocompleteAgentTitle` 方法)
147
123
 
148
- const returnRes = response.clone();
124
+ 2. **消息翻译**:用户手动点击翻译按钮时触发(通过 `translateMessage` 方法)
149
125
 
150
- const data = response.body;
126
+ 3. **网页搜索**:当启用搜索但模型不支持工具调用时,通过 `fetchPresetTaskResult` 实现搜索功能
151
127
 
152
- if (!data) return;
153
- let output = '';
154
- const reader = data.getReader();
155
- const decoder = new TextDecoder();
128
+ **实际代码示例**:
156
129
 
157
- let done = false;
130
+ 角色头像自动生成实现:
158
131
 
159
- while (!done) {
160
- const { value, done: doneReading } = await reader.read();
161
- done = doneReading;
162
- const chunkValue = decoder.decode(value, { stream: true });
132
+ ```typescript
133
+ // src/features/AgentSetting/store/action.ts
134
+ autoPickEmoji: async () => {
135
+ const { config, meta, dispatchMeta } = get();
136
+ const systemRole = config.systemRole;
137
+
138
+ chatService.fetchPresetTaskResult({
139
+ onFinish: async (emoji) => {
140
+ dispatchMeta({ type: 'update', value: { avatar: emoji } });
141
+ },
142
+ onLoadingChange: (loading) => {
143
+ get().updateLoadingState('avatar', loading);
144
+ },
145
+ params: merge(
146
+ get().internal_getSystemAgentForMeta(),
147
+ chainPickEmoji([meta.title, meta.description, systemRole].filter(Boolean).join(',')),
148
+ ),
149
+ trace: get().getCurrentTracePayload({ traceName: TraceNameMap.EmojiPicker }),
150
+ });
151
+ };
152
+ ```
153
+
154
+ 翻译功能实现:
155
+
156
+ ```typescript
157
+ // src/store/chat/slices/translate/action.ts
158
+ translateMessage: async (id, targetLang) => {
159
+ // ...省略部分代码...
160
+
161
+ // 检测语言
162
+ chatService.fetchPresetTaskResult({
163
+ onFinish: async (data) => {
164
+ if (data && supportLocales.includes(data)) from = data;
165
+ await updateMessageTranslate(id, { content, from, to: targetLang });
166
+ },
167
+ params: merge(translationSetting, chainLangDetect(message.content)),
168
+ trace: get().getCurrentTracePayload({ traceName: TraceNameMap.LanguageDetect }),
169
+ });
170
+
171
+ // 执行翻译
172
+ chatService.fetchPresetTaskResult({
173
+ onMessageHandle: (chunk) => {
174
+ if (chunk.type === 'text') {
175
+ content = chunk.text;
176
+ internal_dispatchMessage({
177
+ id,
178
+ type: 'updateMessageTranslate',
179
+ value: { content, from, to: targetLang },
180
+ });
181
+ }
182
+ },
183
+ onFinish: async () => {
184
+ await updateMessageTranslate(id, { content, from, to: targetLang });
185
+ internal_toggleChatLoading(false, id, n('translateMessage(end)', { id }) as string);
186
+ },
187
+ params: merge(translationSetting, chainTranslate(message.content, targetLang)),
188
+ trace: get().getCurrentTracePayload({ traceName: TraceNameMap.Translation }),
189
+ });
190
+ };
191
+ ```
192
+
193
+ 8. **完成**:
194
+ - 当流结束时,调用 onFinish 回调,提供完整的响应结果
195
+
196
+ ## AgentRuntime 说明
197
+
198
+ AgentRuntime 是 Lobe Chat 中的一个核心抽象层,它封装了与不同 AI 模型提供商交互的统一接口。其主要职责和特点包括:
199
+
200
+ 1. **统一抽象层**:AgentRuntime 提供了一个统一的接口,隐藏了不同 AI 提供商 API 的实现细节差异(如 OpenAI、Anthropic、Bedrock 等)。
201
+
202
+ 2. **模型初始化**:通过 `initializeWithProvider` 静态方法,根据指定的提供商和配置参数初始化对应的运行时实例。
203
+
204
+ 3. **能力封装**:
205
+
206
+ - `chat` 方法:处理聊天流式请求
207
+ - `models` 方法:获取模型列表
208
+ - 支持文本嵌入、文本到图像、文本到语音等功能(如果模型提供商支持)
209
+
210
+ 4. **插件化架构**:通过 `src/libs/agent-runtime/runtimeMap.ts` 映射表,实现了可扩展的插件化架构,方便添加新的模型提供商。目前支持超过 40 个不同的模型提供商:
211
+
212
+ ```typescript
213
+ export const providerRuntimeMap = {
214
+ openai: LobeOpenAI,
215
+ anthropic: LobeAnthropicAI,
216
+ google: LobeGoogleAI,
217
+ azure: LobeAzureOpenAI,
218
+ bedrock: LobeBedrockAI,
219
+ ollama: LobeOllamaAI,
220
+ // ...其他40多个模型提供商
221
+ };
222
+ ```
223
+
224
+ 5. **适配器模式**:在内部使用适配器模式,将不同提供商的 API 适配到统一的 `src/libs/agent-runtime/BaseAI.ts` 接口:
225
+
226
+ ```typescript
227
+ export interface LobeRuntimeAI {
228
+ baseURL?: string;
229
+ chat(payload: ChatStreamPayload, options?: ChatCompetitionOptions): Promise<Response>;
230
+ embeddings?(payload: EmbeddingsPayload, options?: EmbeddingsOptions): Promise<Embeddings[]>;
231
+ models?(): Promise<any>;
232
+ textToImage?: (payload: TextToImagePayload) => Promise<string[]>;
233
+ textToSpeech?: (
234
+ payload: TextToSpeechPayload,
235
+ options?: TextToSpeechOptions,
236
+ ) => Promise<ArrayBuffer>;
237
+ }
238
+ ```
239
+
240
+ **适配器实现示例**:
241
+
242
+ 1. **OpenRouter 适配器**:
243
+ OpenRouter 是一个统一 API,可以通过它访问多个模型提供商的 AI 模型。Lobe Chat 通过适配器实现对 OpenRouter 的支持:
244
+
245
+ ```typescript
246
+ // OpenRouter 适配器实现
247
+ class LobeOpenRouterAI implements LobeRuntimeAI {
248
+ client: OpenAI;
249
+ baseURL: string;
250
+
251
+ constructor(options: OpenAICompatibleOptions) {
252
+ // 初始化 OpenRouter 客户端,使用 OpenAI 兼容的 API
253
+ this.client = new OpenAI({
254
+ apiKey: options.apiKey,
255
+ baseURL: OPENROUTER_BASE_URL,
256
+ defaultHeaders: {
257
+ 'HTTP-Referer': 'https://github.com/lobehub/lobe-chat',
258
+ 'X-Title': 'LobeChat',
259
+ },
260
+ });
261
+ this.baseURL = OPENROUTER_BASE_URL;
262
+ }
263
+
264
+ // 实现聊天功能
265
+ async chat(payload: ChatCompletionCreateParamsBase, options?: RequestOptions) {
266
+ // 将 Lobe Chat 的请求格式转换为 OpenRouter 格式
267
+ // 处理模型映射、消息格式等
268
+ return this.client.chat.completions.create(
269
+ {
270
+ ...payload,
271
+ model: payload.model || 'openai/gpt-4-turbo', // 默认模型
272
+ },
273
+ options,
274
+ );
275
+ }
276
+
277
+ // 实现其他 LobeRuntimeAI 接口方法
278
+ }
279
+ ```
280
+
281
+ 2. **Google Gemini 适配器**:
282
+ Gemini 是 Google 的大语言模型,Lobe Chat 通过专门的适配器支持 Gemini 系列模型:
283
+
284
+ ```typescript
285
+ import { GoogleGenerativeAI } from '@google/generative-ai';
286
+
287
+ // Gemini 适配器实现
288
+ class LobeGoogleAI implements LobeRuntimeAI {
289
+ client: GoogleGenerativeAI;
290
+ baseURL: string;
291
+ apiKey: string;
292
+
293
+ constructor(options: GoogleAIOptions) {
294
+ // 初始化 Google Generative AI 客户端
295
+ this.client = new GoogleGenerativeAI(options.apiKey);
296
+ this.apiKey = options.apiKey;
297
+ this.baseURL = options.baseURL || GOOGLE_AI_BASE_URL;
298
+ }
299
+
300
+ // 实现聊天功能
301
+ async chat(payload: ChatCompletionCreateParamsBase, options?: RequestOptions) {
302
+ // 选择合适的模型(支持 Gemini Pro、Gemini Flash 等)
303
+ const modelName = payload.model || 'gemini-pro';
304
+ const model = this.client.getGenerativeModel({ model: modelName });
305
+
306
+ // 处理多模态输入(如图像)
307
+ const contents = this.processMessages(payload.messages);
308
+
309
+ // 设置生成参数
310
+ const generationConfig = {
311
+ temperature: payload.temperature,
312
+ topK: payload.top_k,
313
+ topP: payload.top_p,
314
+ maxOutputTokens: payload.max_tokens,
315
+ };
316
+
317
+ // 创建聊天会话并获取响应
318
+ const chat = model.startChat({
319
+ generationConfig,
320
+ history: contents.slice(0, -1),
321
+ safetySettings: this.getSafetySettings(payload),
322
+ });
323
+
324
+ // 处理流式响应
325
+ return this.handleStreamResponse(chat, contents, options?.signal);
326
+ }
327
+
328
+ // 实现其他处理方法
329
+ private processMessages(messages) {
330
+ /* ... */
331
+ }
332
+ private getSafetySettings(payload) {
333
+ /* ... */
334
+ }
335
+ private handleStreamResponse(chat, contents, signal) {
336
+ /* ... */
337
+ }
338
+ }
339
+ ```
163
340
 
164
- output += chunkValue;
165
- options.onMessageHandle?.(chunkValue);
166
- }
341
+ **不同模型的适配实现**:
167
342
 
168
- await options?.onFinish?.(output);
343
+ - `src/libs/agent-runtime/openai/index.ts` - OpenAI 实现
344
+ - `src/libs/agent-runtime/anthropic/index.ts` - Anthropic 实现
345
+ - `src/libs/agent-runtime/google/index.ts` - Google 实现
346
+ - `src/libs/agent-runtime/openrouter/index.ts` - OpenRouter 实现
169
347
 
170
- return returnRes;
171
- };
172
- ```
348
+ 详细实现可以查看:
173
349
 
174
- 以上就是 LobeChat 会话 API 的核心实现。在理解了这些核心代码的基础上,便可以进一步扩展和优化 LobeChat 的 AI 功能。
350
+ - `src/libs/agent-runtime/AgentRuntime.ts` - 核心运行时类
351
+ - `src/libs/agent-runtime/BaseAI.ts` - 定义基础接口
352
+ - `src/libs/agent-runtime/runtimeMap.ts` - 提供商映射表
353
+ - `src/libs/agent-runtime/UniformRuntime/index.ts` - 处理多模型统一运行时
354
+ - `src/libs/agent-runtime/utils/openaiCompatibleFactory/index.ts` - OpenAI 兼容适配器工厂
@@ -9,6 +9,7 @@ Welcome to the Code Style and Contribution Guidelines for LobeChat. This guide w
9
9
  - [Prettier](#prettier)
10
10
  - [remarklint](#remarklint)
11
11
  - [stylelint](#stylelint)
12
+ - [Style Checking](#style-checking)
12
13
  - [Contribution Process](#contribution-process)
13
14
  - [Gitmoji](#gitmoji)
14
15
  - [Semantic Release](#semantic-release)
@@ -17,7 +18,7 @@ Welcome to the Code Style and Contribution Guidelines for LobeChat. This guide w
17
18
 
18
19
  ## Code Style
19
20
 
20
- In LobeChat, we use the `@lobehub/lint` package to maintain a unified code style. This package incorporates configurations for `ESLint`, `Prettier`, `remarklint`, and `stylelint` to ensure that our JavaScript, Markdown, and CSS files adhere to the same coding standards.
21
+ In LobeChat, we use the [@lobehub/lint](https://github.com/lobehub/lobe-lint) package to maintain a unified code style. This package incorporates configurations for `ESLint`, `Prettier`, `remarklint`, and `stylelint` to ensure that our JavaScript, Markdown, and CSS files adhere to the same coding standards.
21
22
 
22
23
  ### ESLint
23
24
 
@@ -29,7 +30,7 @@ To ensure your code aligns with the project's standards, run ESLint before commi
29
30
 
30
31
  Prettier is responsible for code formatting to maintain consistency. Our Prettier configuration can be found in `.prettierrc.js`, imported from `@lobehub/lint`.
31
32
 
32
- It's recommended to configure your editor to run Prettier automatically upon saving files or manually run it before committing.
33
+ It's recommended to configure your editor to run Prettier automatically when saving files.
33
34
 
34
35
  ### remarklint
35
36
 
@@ -39,7 +40,9 @@ For Markdown files, we use remarklint to ensure consistent document formatting.
39
40
 
40
41
  We utilize stylelint to standardize the style of our CSS code. In the configuration file for stylelint, we have made some custom rule adjustments based on `@lobehub/lint` configuration.
41
42
 
42
- Ensure that your style code passes stylelint checks before committing.
43
+ ### Style Checking
44
+
45
+ You don't need to manually run these checks. The project is configured with husky to automatically run lint-staged when you commit code, which will check if your committed files comply with the above standards.
43
46
 
44
47
  ## Contribution Process
45
48
 
@@ -51,7 +54,7 @@ When committing code, please use gitmoji to label your commit messages. This hel
51
54
 
52
55
  Gitmoji commit messages use specific emojis to represent the type or intent of the commit. Here's an example:
53
56
 
54
- ```
57
+ ```markdown
55
58
  📝 Update README with contribution guidelines
56
59
 
57
60
  - Added section about code style preferences
@@ -9,6 +9,7 @@
9
9
  - [Prettier](#prettier)
10
10
  - [remarklint](#remarklint)
11
11
  - [stylelint](#stylelint)
12
+ - [风格检查](#风格检查)
12
13
  - [贡献流程](#贡献流程)
13
14
  - [Gitmoji](#gitmoji)
14
15
  - [Semantic Release](#semantic-release)
@@ -17,7 +18,7 @@
17
18
 
18
19
  ## 代码风格
19
20
 
20
- 在 LobeChat 中,我们使用 `@lobehub/lint` 程序包来统一代码风格。该程序包内置了 `ESLint`、`Prettier`、`remarklint` 和 `stylelint` 的配置,以确保我们的 JavaScript、Markdown 和 CSS 文件遵循相同的编码标准。
21
+ 在 LobeChat 中,我们使用 [@lobehub/lint](https://github.com/lobehub/lobe-lint) 程序包来统一代码风格。该程序包内置了 `ESLint`、`Prettier`、`remarklint` 和 `stylelint` 的配置,以确保我们的 JavaScript、Markdown 和 CSS 文件遵循相同的编码标准。
21
22
 
22
23
  ### ESLint
23
24
 
@@ -25,13 +26,11 @@
25
26
 
26
27
  为了与 Next.js 框架兼容,我们在配置中添加了 `plugin:@next/next/recommended`。此外,我们禁用了一些规则,以适应我们项目的特定需求。
27
28
 
28
- 请在提交代码前运行 ESLint,以确保您的代码符合项目规范。
29
-
30
29
  ### Prettier
31
30
 
32
31
  Prettier 负责代码格式化,以保证代码的一致性。您可以在 `.prettierrc.js` 中找到我们的 Prettier 配置,它是从 `@lobehub/lint` 导入的。
33
32
 
34
- 在保存文件时,建议您配置您的编辑器以自动运行 Prettier,或者在提交前手动运行它。
33
+ 在保存文件时,建议您配置您的编辑器以自动运行 Prettier
35
34
 
36
35
  ### remarklint
37
36
 
@@ -41,7 +40,9 @@ Prettier 负责代码格式化,以保证代码的一致性。您可以在 `.pr
41
40
 
42
41
  我们使用 stylelint 来规范 CSS 代码的风格。在 `stylelint` 的配置文件中,我们基于 `@lobehub/lint` 的配置进行了一些自定义规则的调整。
43
42
 
44
- 确保您的样式代码在提交前通过了 stylelint 的检查。
43
+ ### 风格检查
44
+
45
+ 你不需要手动运行这些检查,项目配置了 husky 会在您提交代码时自动运行 lint-staged 检查你提交的文件是否符合以上规范。
45
46
 
46
47
  ## 贡献流程
47
48
 
@@ -53,7 +54,7 @@ LobeChat 采用 gitmoji 和 semantic release 作为我们的代码提交和发
53
54
 
54
55
  Gitmoji commit messages 使用特定的 emoji 来表示提交的类型或意图。以下是一个示例:
55
56
 
56
- ```
57
+ ```markdown
57
58
  📝 Update README with contribution guidelines
58
59
 
59
60
  - Added section about code style preferences
@@ -19,16 +19,16 @@ LobeChat is an open-source, extensible ([Function Calling][fc-url]), high-perfor
19
19
  ### 🤯 Basic
20
20
 
21
21
  - [Architecture Design](https://github.com/lobehub/lobe-chat/wiki/Architecture) | [架构设计](https://github.com/lobehub/lobe-chat/wiki/Architecture.zh-CN)
22
+ - [Development Environment Setup Guide](https://github.com/lobehub/lobe-chat/wiki/Setup-Development) | [配置开发环境指南](https://github.com/lobehub/lobe-chat/wiki/Setup-Development.zh-CN)
23
+ - [Project Directory Structure](https://github.com/lobehub/lobe-chat/wiki/Folder-Structure) | [项目目录架构](https://github.com/lobehub/lobe-chat/wiki/Folder-Structure.zh-CN)
24
+ - [Technical Development Getting Started Guide](https://github.com/lobehub/lobe-chat/wiki/Intro) | [技术开发上手指南](https://github.com/lobehub/lobe-chat/wiki/Intro.zh-CN)
22
25
  - [Code Style and Contribution Guidelines](https://github.com/lobehub/lobe-chat/wiki/Contributing-Guidelines) | [代码风格与贡献指南](https://github.com/lobehub/lobe-chat/wiki/Contributing-Guidelines.zh-CN)
26
+ - [How to Develop a New Feature](https://github.com/lobehub/lobe-chat/wiki/Feature-Development-Frontend) | [如何开发一个新功能:前端实现](https://github.com/lobehub/lobe-chat/wiki/Feature-Development-Frontend.zh-CN)
23
27
  - [Complete Guide to LobeChat Feature Development](https://github.com/lobehub/lobe-chat/wiki/Feature-Development) | [LobeChat 功能开发完全指南](https://github.com/lobehub/lobe-chat/wiki/Feature-Development.zh-CN)
24
28
  - [Conversation API Implementation Logic](https://github.com/lobehub/lobe-chat/wiki/Chat-API) | [会话 API 实现逻辑](https://github.com/lobehub/lobe-chat/wiki/Chat-API.zh-CN)
25
- - [Directory Structure](https://github.com/lobehub/lobe-chat/wiki/Folder-Structure) | [目录架构](https://github.com/lobehub/lobe-chat/wiki/Folder-Structure.zh-CN)
26
- - [Environment Setup Guide](https://github.com/lobehub/lobe-chat/wiki/Setup-Development) | [环境设置指南](https://github.com/lobehub/lobe-chat/wiki/Setup-Development.zh-CN)
27
- - [How to Develop a New Feature](https://github.com/lobehub/lobe-chat/wiki/Feature-Development-Frontend) | [如何开发一个新功能:前端实现](https://github.com/lobehub/lobe-chat/wiki/Feature-Development-Frontend.zh-CN)
28
29
  - [New Authentication Provider Guide](https://github.com/lobehub/lobe-chat/wiki/Add-New-Authentication-Providers) | [新身份验证方式开发指南](https://github.com/lobehub/lobe-chat/wiki/Add-New-Authentication-Providers.zh-CN)
29
- - [Resources and References](https://github.com/lobehub/lobe-chat/wiki/Resources) | [资源与参考](https://github.com/lobehub/lobe-chat/wiki/Resources.zh-CN)
30
- - [Technical Development Getting Started Guide](https://github.com/lobehub/lobe-chat/wiki/Intro) | [技术开发上手指南](https://github.com/lobehub/lobe-chat/wiki/Intro.zh-CN)
31
30
  - [Testing Guide](https://github.com/lobehub/lobe-chat/wiki/Test) | [测试指南](https://github.com/lobehub/lobe-chat/wiki/Test.zh-CN)
31
+ - [Resources and References](https://github.com/lobehub/lobe-chat/wiki/Resources) | [资源与参考](https://github.com/lobehub/lobe-chat/wiki/Resources.zh-CN)
32
32
 
33
33
  <br/>
34
34
 
@@ -190,7 +190,7 @@ export const useSessionStore = createWithEqualityFn<SessionStore>()(
190
190
 
191
191
  ```
192
192
 
193
- In this `store.ts` file, we create a `useSessionStore` hook that uses the `zustand` library to create a global state manager. We merge the initialState and the state and actions of each Slice to create a complete SessionStore.
193
+ In this `store.ts` file, we create a `useSessionStore` hook that uses the `zustand` library to create a global state manager. We merge the initialState with the actions from each Slice to create a complete SessionStore.
194
194
 
195
195
  #### slices/session/action.ts
196
196
 
@@ -182,7 +182,7 @@ export const useSessionStore = createWithEqualityFn<SessionStore>()(
182
182
 
183
183
  ```
184
184
 
185
- 在这个 `store.ts` 文件中,我们创建了一个 `useSessionStore` 钩子,它使用 `zustand` 库来创建一个全局状态管理器。我们将 initialState 和每个 Slice 的状态和动作合并,以创建完整的 SessionStore。
185
+ 在这个 `store.ts` 文件中,我们创建了一个 `useSessionStore` 钩子,它使用 `zustand` 库来创建一个全局状态管理器。我们将 initialState 和每个 Slice 的动作合并,以创建完整的 SessionStore。
186
186
 
187
187
  #### slices/session/action.ts
188
188
 
@@ -58,8 +58,9 @@ services:
58
58
  wait \$MINIO_PID
59
59
  "
60
60
 
61
+ # version lock ref: https://github.com/lobehub/lobe-chat/pull/7331
61
62
  casdoor:
62
- image: casbin/casdoor
63
+ image: casbin/casdoor:v1.843.0
63
64
  container_name: lobe-casdoor
64
65
  entrypoint: /bin/sh -c './server --createDatabase=true'
65
66
  network_mode: 'service:network-service'
@@ -91,6 +91,10 @@
91
91
  "provider": "مزود"
92
92
  },
93
93
  "OllamaSetupGuide": {
94
+ "action": {
95
+ "close": "إغلاق الإشعار",
96
+ "start": "تم التثبيت والتشغيل، ابدأ المحادثة"
97
+ },
94
98
  "cors": {
95
99
  "description": "بسبب قيود أمان المتصفح، تحتاج إلى تكوين CORS لـ Ollama لاستخدامه بشكل صحيح.",
96
100
  "linux": {