@lobehub/chat 0.122.9 → 0.123.1

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 (231) hide show
  1. package/.env.example +22 -2
  2. package/.eslintrc.js +1 -0
  3. package/CHANGELOG.md +59 -0
  4. package/Dockerfile +7 -1
  5. package/README.md +12 -10
  6. package/README.zh-CN.md +12 -10
  7. package/docs/Deployment/Docker-Deployment.md +1 -1
  8. package/docs/Deployment/Docker-Deployment.zh-CN.md +1 -1
  9. package/docs/Deployment/Environment-Variable.md +65 -23
  10. package/docs/Deployment/Environment-Variable.zh-CN.md +64 -22
  11. package/locales/ar/chat.json +12 -1
  12. package/locales/ar/common.json +16 -0
  13. package/locales/ar/error.json +35 -6
  14. package/locales/ar/setting.json +53 -6
  15. package/locales/de-DE/chat.json +12 -1
  16. package/locales/de-DE/common.json +16 -0
  17. package/locales/de-DE/error.json +35 -6
  18. package/locales/de-DE/setting.json +53 -6
  19. package/locales/en-US/chat.json +13 -2
  20. package/locales/en-US/common.json +16 -0
  21. package/locales/en-US/error.json +35 -6
  22. package/locales/en-US/setting.json +53 -6
  23. package/locales/es-ES/chat.json +12 -1
  24. package/locales/es-ES/common.json +16 -0
  25. package/locales/es-ES/error.json +35 -6
  26. package/locales/es-ES/setting.json +53 -6
  27. package/locales/fr-FR/chat.json +13 -2
  28. package/locales/fr-FR/common.json +16 -0
  29. package/locales/fr-FR/error.json +35 -6
  30. package/locales/fr-FR/setting.json +53 -6
  31. package/locales/it-IT/chat.json +12 -1
  32. package/locales/it-IT/common.json +16 -0
  33. package/locales/it-IT/error.json +35 -6
  34. package/locales/it-IT/setting.json +53 -6
  35. package/locales/ja-JP/chat.json +12 -1
  36. package/locales/ja-JP/common.json +16 -0
  37. package/locales/ja-JP/error.json +35 -6
  38. package/locales/ja-JP/setting.json +53 -6
  39. package/locales/ko-KR/chat.json +12 -1
  40. package/locales/ko-KR/common.json +16 -0
  41. package/locales/ko-KR/error.json +35 -6
  42. package/locales/ko-KR/setting.json +53 -6
  43. package/locales/nl-NL/chat.json +12 -1
  44. package/locales/nl-NL/common.json +16 -0
  45. package/locales/nl-NL/error.json +35 -6
  46. package/locales/nl-NL/setting.json +53 -6
  47. package/locales/pl-PL/chat.json +12 -1
  48. package/locales/pl-PL/common.json +16 -0
  49. package/locales/pl-PL/error.json +35 -6
  50. package/locales/pl-PL/setting.json +53 -6
  51. package/locales/pt-BR/chat.json +12 -1
  52. package/locales/pt-BR/common.json +16 -0
  53. package/locales/pt-BR/error.json +35 -6
  54. package/locales/pt-BR/setting.json +53 -6
  55. package/locales/ru-RU/chat.json +12 -1
  56. package/locales/ru-RU/common.json +16 -0
  57. package/locales/ru-RU/error.json +35 -6
  58. package/locales/ru-RU/setting.json +53 -6
  59. package/locales/tr-TR/chat.json +12 -1
  60. package/locales/tr-TR/common.json +16 -0
  61. package/locales/tr-TR/error.json +35 -6
  62. package/locales/tr-TR/setting.json +53 -6
  63. package/locales/vi-VN/chat.json +12 -1
  64. package/locales/vi-VN/common.json +16 -0
  65. package/locales/vi-VN/error.json +35 -6
  66. package/locales/vi-VN/setting.json +53 -6
  67. package/locales/zh-CN/chat.json +12 -1
  68. package/locales/zh-CN/common.json +16 -0
  69. package/locales/zh-CN/error.json +34 -5
  70. package/locales/zh-CN/setting.json +59 -12
  71. package/locales/zh-TW/chat.json +12 -1
  72. package/locales/zh-TW/common.json +16 -0
  73. package/locales/zh-TW/error.json +35 -6
  74. package/locales/zh-TW/setting.json +53 -6
  75. package/next.config.mjs +8 -2
  76. package/package.json +8 -2
  77. package/src/app/api/chat/[provider]/agentRuntime.ts +129 -0
  78. package/src/app/api/chat/[provider]/route.ts +64 -0
  79. package/src/app/api/chat/auth.ts +42 -0
  80. package/src/app/api/chat/google/route.ts +86 -0
  81. package/src/app/api/config/route.ts +6 -1
  82. package/src/app/api/config.ts +3 -3
  83. package/src/app/api/errorResponse.test.ts +8 -8
  84. package/src/app/api/errorResponse.ts +43 -6
  85. package/src/app/api/openai/createBizOpenAI/createAzureOpenai.ts +1 -1
  86. package/src/app/api/openai/createBizOpenAI/createOpenai.ts +1 -1
  87. package/src/app/api/openai/createBizOpenAI/index.ts +2 -2
  88. package/src/app/api/plugin/gateway/route.ts +4 -1
  89. package/src/app/chat/(desktop)/features/ChatHeader.tsx +6 -4
  90. package/src/app/chat/(desktop)/features/ChatInput/Footer/index.tsx +13 -3
  91. package/src/app/chat/(desktop)/features/ChatInput/Header/index.tsx +16 -24
  92. package/src/app/chat/(desktop)/features/SideBar/index.tsx +1 -0
  93. package/src/app/chat/features/ChatHeader/ShareButton/Preview.tsx +3 -3
  94. package/src/app/chat/features/SessionListContent/List/Item/index.tsx +2 -3
  95. package/src/app/settings/llm/Azure/index.tsx +145 -0
  96. package/src/app/settings/llm/Bedrock/index.tsx +109 -0
  97. package/src/app/settings/llm/{LLM/Checker.tsx → Checker.tsx} +11 -14
  98. package/src/app/settings/llm/Google/index.tsx +74 -0
  99. package/src/app/settings/llm/{LLM → OpenAI}/index.tsx +26 -44
  100. package/src/app/settings/llm/Zhipu/index.tsx +78 -0
  101. package/src/app/settings/llm/const.ts +13 -0
  102. package/src/app/settings/llm/page.tsx +42 -2
  103. package/src/app/settings/llm/useSyncSettings.ts +23 -0
  104. package/src/chains/__tests__/langDetect.test.ts +2 -2
  105. package/src/chains/__tests__/pickEmoji.test.ts +2 -2
  106. package/src/chains/langDetect.ts +2 -2
  107. package/src/chains/pickEmoji.ts +2 -2
  108. package/src/chains/summaryAgentName.ts +2 -2
  109. package/src/chains/summaryDescription.ts +2 -2
  110. package/src/chains/summaryTags.ts +2 -2
  111. package/src/chains/summaryTitle.ts +2 -2
  112. package/src/chains/translate.ts +2 -2
  113. package/src/components/ModelIcon/index.tsx +37 -0
  114. package/src/components/ModelProviderIcon/index.tsx +44 -0
  115. package/src/components/ModelSelect/index.tsx +133 -0
  116. package/src/components/ModelTag/ModelIcon.tsx +35 -0
  117. package/src/components/ModelTag/index.tsx +13 -0
  118. package/src/config/modelProviders/bedrock.ts +43 -0
  119. package/src/config/modelProviders/google.ts +20 -0
  120. package/src/config/modelProviders/index.ts +18 -0
  121. package/src/config/modelProviders/openai.ts +110 -0
  122. package/src/config/modelProviders/zhipu.ts +34 -0
  123. package/src/config/{server.ts → server/app.ts} +1 -24
  124. package/src/config/server/index.ts +13 -0
  125. package/src/config/server/provider.ts +78 -0
  126. package/src/const/auth.ts +30 -0
  127. package/src/const/fetch.ts +3 -0
  128. package/src/const/settings.ts +24 -4
  129. package/src/database/core/migrations/migrateSettingsToUser/index.test.ts +2 -1
  130. package/src/database/core/migrations/migrateSettingsToUser/index.ts +1 -18
  131. package/src/database/core/migrations/migrateSettingsToUser/type.ts +100 -0
  132. package/src/database/models/message.ts +3 -1
  133. package/src/database/schemas/message.ts +1 -0
  134. package/src/database/schemas/session.ts +1 -0
  135. package/src/features/AgentSetting/AgentConfig/ModelSelect.tsx +71 -0
  136. package/src/features/AgentSetting/AgentConfig/index.tsx +171 -161
  137. package/src/features/AgentSetting/AgentConfig/useSyncConfig.ts +23 -0
  138. package/src/features/AgentSetting/AgentPrompt/TokenTag.tsx +39 -0
  139. package/src/features/AgentSetting/AgentPrompt/index.tsx +4 -26
  140. package/src/features/AgentSetting/store/index.ts +10 -2
  141. package/src/features/ChatInput/ActionBar/FileUpload.tsx +15 -5
  142. package/src/features/ChatInput/ActionBar/ModelSwitch.tsx +56 -14
  143. package/src/features/ChatInput/ActionBar/Token/TokenTag.tsx +42 -7
  144. package/src/features/ChatInput/ActionBar/Token/index.tsx +5 -2
  145. package/src/features/ChatInput/ActionBar/Tools/index.tsx +11 -1
  146. package/src/features/ChatInput/useChatInput.ts +5 -1
  147. package/src/features/Conversation/Error/APIKeyForm/Bedrock.tsx +80 -0
  148. package/src/features/Conversation/Error/APIKeyForm/Google.tsx +61 -0
  149. package/src/features/Conversation/Error/APIKeyForm/OpenAI.tsx +63 -0
  150. package/src/features/Conversation/Error/APIKeyForm/Zhipu.tsx +62 -0
  151. package/src/features/Conversation/Error/APIKeyForm/index.tsx +72 -0
  152. package/src/features/Conversation/Error/AccessCodeForm.tsx +63 -0
  153. package/src/features/Conversation/Error/ErrorJsonViewer.tsx +26 -0
  154. package/src/features/Conversation/Error/InvalidAPIKey.tsx +16 -0
  155. package/src/features/Conversation/Error/InvalidAccessCode.tsx +50 -0
  156. package/src/features/Conversation/Error/OpenAiBizError.tsx +9 -17
  157. package/src/features/Conversation/Error/{Plugin/PluginSettings.tsx → PluginSettings.tsx} +8 -3
  158. package/src/features/Conversation/Error/index.tsx +75 -70
  159. package/src/features/Conversation/Error/style.tsx +9 -3
  160. package/src/features/Conversation/Extras/Assistant.tsx +2 -3
  161. package/src/features/Conversation/Plugins/Inspector/index.tsx +8 -2
  162. package/src/features/Conversation/components/ChatItem/index.tsx +9 -24
  163. package/src/features/Conversation/types/index.tsx +2 -5
  164. package/src/hooks/_header.ts +35 -0
  165. package/src/libs/agent-runtime/BaseAI.ts +9 -0
  166. package/src/libs/agent-runtime/azureOpenai/index.ts +72 -0
  167. package/src/libs/agent-runtime/bedrock/index.ts +134 -0
  168. package/src/libs/agent-runtime/error.ts +26 -0
  169. package/src/libs/agent-runtime/google/index.ts +160 -0
  170. package/src/libs/agent-runtime/index.ts +9 -0
  171. package/src/libs/agent-runtime/openai/index.test.ts +176 -0
  172. package/src/libs/agent-runtime/openai/index.ts +107 -0
  173. package/src/libs/agent-runtime/types/chat.ts +119 -0
  174. package/src/libs/agent-runtime/types/index.ts +2 -0
  175. package/src/libs/agent-runtime/types/type.ts +34 -0
  176. package/src/libs/agent-runtime/utils/createError.ts +10 -0
  177. package/src/libs/agent-runtime/utils/debugStream.ts +18 -0
  178. package/src/libs/agent-runtime/utils/env.ts +1 -0
  179. package/src/libs/agent-runtime/utils/handleOpenAIError.ts +39 -0
  180. package/src/libs/agent-runtime/utils/uriParser.ts +16 -0
  181. package/src/libs/agent-runtime/zhipu/authToken.ts +22 -0
  182. package/src/libs/agent-runtime/zhipu/index.ts +125 -0
  183. package/src/locales/default/chat.ts +12 -2
  184. package/src/locales/default/common.ts +16 -0
  185. package/src/locales/default/error.ts +42 -5
  186. package/src/locales/default/setting.ts +61 -12
  187. package/src/services/__tests__/chat.test.ts +8 -2
  188. package/src/services/__tests__/message.test.ts +1 -1
  189. package/src/services/_auth.ts +106 -0
  190. package/src/services/_header.ts +1 -0
  191. package/src/services/_url.ts +0 -2
  192. package/src/services/chat.ts +34 -14
  193. package/src/store/chat/slices/message/action.ts +9 -6
  194. package/src/store/global/slices/common/action.ts +8 -2
  195. package/src/store/global/slices/settings/action.test.ts +2 -2
  196. package/src/store/global/slices/settings/action.ts +11 -4
  197. package/src/store/global/slices/settings/selectors/__snapshots__/modelProvider.test.ts.snap +24 -92
  198. package/src/store/global/slices/settings/selectors/__snapshots__/selectors.test.ts.snap +2 -0
  199. package/src/store/global/slices/settings/selectors/modelProvider.test.ts +127 -50
  200. package/src/store/global/slices/settings/selectors/modelProvider.ts +145 -43
  201. package/src/store/session/slices/agent/selectors.test.ts +0 -48
  202. package/src/store/session/slices/agent/selectors.ts +7 -12
  203. package/src/types/agent/index.ts +6 -2
  204. package/src/types/fetch.ts +4 -2
  205. package/src/types/files.ts +3 -3
  206. package/src/types/llm.ts +34 -0
  207. package/src/types/message/index.ts +7 -3
  208. package/src/types/openai/chat.ts +6 -11
  209. package/src/types/settings/index.ts +1 -0
  210. package/src/types/settings/modelProvider.ts +37 -1
  211. package/src/utils/__snapshots__/parseModels.test.ts.snap +63 -0
  212. package/src/utils/parseModels.test.ts +24 -0
  213. package/src/utils/parseModels.ts +37 -0
  214. package/src/app/api/openai/chat/createChatCompletion.test.ts +0 -166
  215. package/src/app/api/openai/chat/createChatCompletion.ts +0 -76
  216. package/src/app/api/openai/chat/route.ts +0 -19
  217. package/src/app/api/openai/models/route.ts +0 -17
  218. package/src/app/settings/llm/LLM/ModelList.tsx +0 -51
  219. package/src/app/settings/llm/LLM/getModelList.ts +0 -9
  220. package/src/app/settings/llm/index.tsx +0 -36
  221. package/src/const/llm.ts +0 -32
  222. package/src/features/Conversation/Error/ApiKeyForm.tsx +0 -87
  223. package/src/features/Conversation/Error/ErrorActionContainer.tsx +0 -23
  224. package/src/features/Conversation/Error/InvalidAccess.tsx +0 -90
  225. package/src/features/Conversation/Error/OpenAPIKey.tsx +0 -13
  226. package/src/features/Conversation/Error/Plugin/PluginError.tsx +0 -30
  227. package/src/services/modelList.ts +0 -15
  228. /package/src/app/chat/(desktop)/features/ChatInput/{Header → Footer}/DragUpload.tsx +0 -0
  229. /package/src/{app/api/openai/chat → libs/agent-runtime/utils}/desensitizeUrl.test.ts +0 -0
  230. /package/src/{app/api/openai/chat → libs/agent-runtime/utils}/desensitizeUrl.ts +0 -0
  231. /package/src/types/{translate.ts → message/translate.ts} +0 -0
@@ -1,166 +0,0 @@
1
- import OpenAI, { APIError } from 'openai';
2
- import { Mock, afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
-
4
- import { createChatCompletion } from './createChatCompletion';
5
-
6
- // Mock the console.error to avoid polluting test output
7
- vi.spyOn(console, 'error').mockImplementation(() => {});
8
-
9
- describe('createChatCompletion', () => {
10
- let openaiInstance: OpenAI;
11
-
12
- beforeEach(() => {
13
- openaiInstance = new OpenAI({ apiKey: 'test', dangerouslyAllowBrowser: true });
14
-
15
- // 使用 vi.spyOn 来模拟 chat.completions.create 方法
16
- vi.spyOn(openaiInstance.chat.completions, 'create').mockResolvedValue(
17
- new ReadableStream() as any,
18
- );
19
- });
20
-
21
- afterEach(() => {
22
- vi.clearAllMocks();
23
- });
24
-
25
- it('should return a StreamingTextResponse on successful API call', async () => {
26
- // Arrange
27
- const mockStream = new ReadableStream();
28
- const mockResponse = Promise.resolve(mockStream);
29
-
30
- (openaiInstance.chat.completions.create as Mock).mockResolvedValue(mockResponse);
31
-
32
- // Act
33
- const result = await createChatCompletion({
34
- openai: openaiInstance,
35
- payload: {
36
- messages: [{ content: 'Hello', role: 'user' }],
37
- model: 'text-davinci-003',
38
- temperature: 0,
39
- },
40
- });
41
-
42
- // Assert
43
- expect(result).toBeInstanceOf(Response); // 注意这里的改动
44
- });
45
-
46
- it('should return an openai error response when OpenAI.APIError is thrown', async () => {
47
- // Arrange
48
- const apiError = new OpenAI.APIError(
49
- 400,
50
- {
51
- status: 400,
52
- error: {
53
- message: 'Bad Request',
54
- },
55
- },
56
- 'Error message',
57
- {},
58
- );
59
-
60
- vi.spyOn(openaiInstance.chat.completions, 'create').mockRejectedValue(apiError);
61
-
62
- // Act
63
- const result = await createChatCompletion({
64
- openai: openaiInstance,
65
- payload: {
66
- messages: [{ content: 'Hello', role: 'user' }],
67
- model: 'text-davinci-003',
68
- temperature: 0,
69
- },
70
- });
71
-
72
- // Assert
73
- expect(result).toBeInstanceOf(Response);
74
- expect(result.status).toBe(577); // Your custom error status code
75
- });
76
-
77
- it('should return an cause response when OpenAI.APIError is thrown with cause', async () => {
78
- // Arrange
79
- const errorInfo = {
80
- stack: 'abc',
81
- cause: {
82
- message: 'api is undefined',
83
- },
84
- };
85
- const apiError = new OpenAI.APIError(400, errorInfo, 'module error', {});
86
-
87
- vi.spyOn(openaiInstance.chat.completions, 'create').mockRejectedValue(apiError);
88
-
89
- // Act
90
- const result = await createChatCompletion({
91
- openai: openaiInstance,
92
- payload: {
93
- messages: [{ content: 'Hello', role: 'user' }],
94
- model: 'text-davinci-003',
95
- temperature: 0,
96
- },
97
- });
98
-
99
- // Assert
100
- expect(result).toBeInstanceOf(Response);
101
- expect(result.status).toBe(577); // Your custom error status code
102
-
103
- const content = await result.json();
104
- expect(content.body).toHaveProperty('endpoint');
105
- expect(content.body.endpoint).toEqual('https://api.openai.com/v1');
106
- expect(content.body.error).toEqual(errorInfo);
107
- });
108
-
109
- it('should return an cause response with desensitize Url', async () => {
110
- // Arrange
111
- const errorInfo = {
112
- stack: 'abc',
113
- cause: { message: 'api is undefined' },
114
- };
115
- const apiError = new OpenAI.APIError(400, errorInfo, 'module error', {});
116
-
117
- openaiInstance = new OpenAI({
118
- apiKey: 'test',
119
- dangerouslyAllowBrowser: true,
120
- baseURL: 'https://api.abc.com/v1',
121
- });
122
-
123
- vi.spyOn(openaiInstance.chat.completions, 'create').mockRejectedValue(apiError);
124
-
125
- // Act
126
- const result = await createChatCompletion({
127
- openai: openaiInstance,
128
- payload: {
129
- messages: [{ content: 'Hello', role: 'user' }],
130
- model: 'gpt-3.5-turbo',
131
- temperature: 0,
132
- },
133
- });
134
-
135
- // Assert
136
- expect(result).toBeInstanceOf(Response);
137
- expect(result.status).toBe(577); // Your custom error status code
138
-
139
- const content = await result.json();
140
- expect(content.body.endpoint).toEqual('https://api.***.com/v1');
141
- expect(content.body.error).toEqual(errorInfo);
142
- });
143
-
144
- it('should return a 500 error response for non-OpenAI errors', async () => {
145
- // Arrange
146
- const genericError = new Error('Generic Error');
147
-
148
- vi.spyOn(openaiInstance.chat.completions, 'create').mockRejectedValue(genericError);
149
-
150
- // Act
151
- const result = await createChatCompletion({
152
- openai: openaiInstance,
153
- payload: {
154
- messages: [{ content: 'Hello', role: 'user' }],
155
- model: 'text-davinci-003',
156
- temperature: 0,
157
- },
158
- });
159
-
160
- // Assert
161
- expect(result.status).toBe(500);
162
- const content = await result.json();
163
- expect(content.body).toHaveProperty('endpoint');
164
- expect(content.body).toHaveProperty('error');
165
- });
166
- });
@@ -1,76 +0,0 @@
1
- import { OpenAIStream, StreamingTextResponse } from 'ai';
2
- import OpenAI from 'openai';
3
-
4
- import { ChatErrorType } from '@/types/fetch';
5
- import { OpenAIChatStreamPayload } from '@/types/openai/chat';
6
-
7
- import { createErrorResponse } from '../../errorResponse';
8
- import { desensitizeUrl } from './desensitizeUrl';
9
-
10
- interface CreateChatCompletionOptions {
11
- openai: OpenAI;
12
- payload: OpenAIChatStreamPayload;
13
- }
14
-
15
- export const createChatCompletion = async ({ payload, openai }: CreateChatCompletionOptions) => {
16
- // ============ 1. preprocess messages ============ //
17
- const { messages, ...params } = payload;
18
-
19
- // ============ 2. send api ============ //
20
-
21
- try {
22
- const response = await openai.chat.completions.create(
23
- {
24
- messages,
25
- ...params,
26
- stream: true,
27
- } as unknown as OpenAI.ChatCompletionCreateParamsStreaming,
28
- { headers: { Accept: '*/*' } },
29
- );
30
- const stream = OpenAIStream(response);
31
- return new StreamingTextResponse(stream);
32
- } catch (error) {
33
- let desensitizedEndpoint = openai.baseURL;
34
-
35
- // refs: https://github.com/lobehub/lobe-chat/issues/842
36
- if (openai.baseURL !== 'https://api.openai.com/v1') {
37
- desensitizedEndpoint = desensitizeUrl(openai.baseURL);
38
- }
39
-
40
- // Check if the error is an OpenAI APIError
41
- if (error instanceof OpenAI.APIError) {
42
- let errorResult: any;
43
-
44
- // if error is definitely OpenAI APIError, there will be an error object
45
- if (error.error) {
46
- errorResult = error.error;
47
- }
48
- // Or if there is a cause, we use error cause
49
- // This often happened when there is a bug of the `openai` package.
50
- else if (error.cause) {
51
- errorResult = error.cause;
52
- }
53
- // if there is no other request error, the error object is a Response like object
54
- else {
55
- errorResult = { headers: error.headers, stack: error.stack, status: error.status };
56
- }
57
-
58
- // track the error at server side
59
- console.error(errorResult);
60
-
61
- return createErrorResponse(ChatErrorType.OpenAIBizError, {
62
- endpoint: desensitizedEndpoint,
63
- error: errorResult,
64
- });
65
- }
66
-
67
- // track the non-openai error
68
- console.error(error);
69
-
70
- // return as a GatewayTimeout error
71
- return createErrorResponse(ChatErrorType.InternalServerError, {
72
- endpoint: desensitizedEndpoint,
73
- error: JSON.stringify(error),
74
- });
75
- }
76
- };
@@ -1,19 +0,0 @@
1
- import { OpenAIChatStreamPayload } from '@/types/openai/chat';
2
-
3
- import { getPreferredRegion } from '../../config';
4
- import { createBizOpenAI } from '../createBizOpenAI';
5
- import { createChatCompletion } from './createChatCompletion';
6
-
7
- export const runtime = 'edge';
8
- export const preferredRegion = getPreferredRegion();
9
-
10
- export const POST = async (req: Request) => {
11
- const payload = (await req.json()) as OpenAIChatStreamPayload;
12
-
13
- const openaiOrErrResponse = createBizOpenAI(req, payload.model);
14
-
15
- // if resOrOpenAI is a Response, it means there is an error,just return it
16
- if (openaiOrErrResponse instanceof Response) return openaiOrErrResponse;
17
-
18
- return createChatCompletion({ openai: openaiOrErrResponse, payload });
19
- };
@@ -1,17 +0,0 @@
1
- import { getOpenAIAuthFromRequest } from '@/const/fetch';
2
-
3
- import { createOpenai } from '../createBizOpenAI/createOpenai';
4
-
5
- export const runtime = 'edge';
6
-
7
- export const POST = async (req: Request) => {
8
- const { apiKey, endpoint } = getOpenAIAuthFromRequest(req);
9
-
10
- const openAI = createOpenai(apiKey, endpoint);
11
-
12
- const res = await openAI.models.list();
13
-
14
- const modelList = res.data.map((i) => i.id);
15
-
16
- return new Response(JSON.stringify(modelList));
17
- };
@@ -1,51 +0,0 @@
1
- import { ActionIcon } from '@lobehub/ui';
2
- import { Button, Dropdown, Skeleton } from 'antd';
3
- import { MenuItemType } from 'antd/lib/menu/hooks/useItems';
4
- import { RotateCwIcon } from 'lucide-react';
5
- import { memo } from 'react';
6
- import { useTranslation } from 'react-i18next';
7
- import { Flexbox } from 'react-layout-kit';
8
-
9
- import { getModelList } from './getModelList';
10
-
11
- interface ModelListProps {
12
- value?: string[];
13
- }
14
- const ModelList = memo<ModelListProps>(({ value }) => {
15
- const { t } = useTranslation('setting');
16
-
17
- const isLoading = !value;
18
-
19
- if (isLoading)
20
- return (
21
- <Skeleton active paragraph={false} title={{ style: { marginBottom: 0 }, width: '50%' }} />
22
- );
23
-
24
- const isEmpty = value?.length === 0;
25
-
26
- return isEmpty ? (
27
- <Button
28
- onClick={() => {
29
- getModelList('openAI');
30
- }}
31
- >
32
- {t('llm.OpenAI.models.fetch')}
33
- </Button>
34
- ) : (
35
- <Dropdown
36
- menu={{
37
- items: (value || []).map<MenuItemType>((v) => ({
38
- key: v,
39
- label: v,
40
- })),
41
- }}
42
- >
43
- <Flexbox align={'center'} gap={8} horizontal>
44
- {t('llm.OpenAI.models.count', { count: value.length })}
45
- <ActionIcon icon={RotateCwIcon} size={'small'} title={t('llm.OpenAI.models.refetch')} />
46
- </Flexbox>
47
- </Dropdown>
48
- );
49
- });
50
-
51
- export default ModelList;
@@ -1,9 +0,0 @@
1
- import { fetchModelList } from '@/services/modelList';
2
- import { useGlobalStore } from '@/store/global';
3
-
4
- export const getModelList = async (brand = 'openAI') => {
5
- const setSettings = useGlobalStore.getState().setSettings;
6
- const models = await fetchModelList();
7
-
8
- setSettings({ languageModel: { [brand]: { models } } });
9
- };
@@ -1,36 +0,0 @@
1
- 'use client';
2
-
3
- import Link from 'next/link';
4
- import { memo } from 'react';
5
- import { Trans, useTranslation } from 'react-i18next';
6
-
7
- import Footer from '@/app/settings/features/Footer';
8
- import PageTitle from '@/components/PageTitle';
9
- import { useSwitchSideBarOnInit } from '@/store/global/hooks/useSwitchSettingsOnInit';
10
- import { SettingsTabs } from '@/store/global/initialState';
11
-
12
- import LLM from './LLM';
13
-
14
- export default memo(() => {
15
- useSwitchSideBarOnInit(SettingsTabs.LLM);
16
- const { t } = useTranslation('setting');
17
- return (
18
- <>
19
- <PageTitle title={t('tab.llm')} />
20
- <LLM />
21
- <Footer>
22
- <Trans i18nKey="llm.waitingForMore" ns={'setting'}>
23
- 更多模型正在
24
- <Link
25
- aria-label={'todo'}
26
- href="https://github.com/lobehub/lobe-chat/issues/151"
27
- target="_blank"
28
- >
29
- 计划接入
30
- </Link>
31
- 中 ,敬请期待 ✨
32
- </Trans>
33
- </Footer>
34
- </>
35
- );
36
- });
package/src/const/llm.ts DELETED
@@ -1,32 +0,0 @@
1
- import { LanguageModel } from '@/types/llm';
2
-
3
- /**
4
- * A white list of language models that are allowed to display and be used in the app.
5
- */
6
- export const LanguageModelWhiteList = [
7
- // OpenAI
8
- 'gpt-3.5-turbo',
9
- 'gpt-3.5-turbo-16k',
10
- 'gpt-4',
11
- 'gpt-4-32k',
12
- ];
13
-
14
- export const DEFAULT_OPENAI_MODEL_LIST: string[] = Object.values(LanguageModel);
15
-
16
- // vision model white list, these models will change the content from string to array
17
- // refs: https://github.com/lobehub/lobe-chat/issues/790
18
- export const isVisionModel = (model?: string) => !!model && model.includes('vision');
19
-
20
- // refs: https://github.com/lobehub/lobe-chat/issues/837
21
- export const GPT4_VISION_MODEL_DEFAULT_MAX_TOKENS = 2048;
22
-
23
- // refs to: https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo
24
- export const ModelTokens: Record<LanguageModel, number> = {
25
- [LanguageModel.GPT3_5]: 4096,
26
- [LanguageModel.GPT3_5_1106]: 16_385,
27
- [LanguageModel.GPT3_5_16K]: 16_385,
28
- [LanguageModel.GPT4]: 8196,
29
- [LanguageModel.GPT4_PREVIEW]: 128_000,
30
- [LanguageModel.GPT4_VISION_PREVIEW]: 128_000,
31
- [LanguageModel.GPT4_32K]: 32_768,
32
- };
@@ -1,87 +0,0 @@
1
- import { Icon } from '@lobehub/ui';
2
- import { Button, Input } from 'antd';
3
- import { Network } from 'lucide-react';
4
- import { memo, useState } from 'react';
5
- import { useTranslation } from 'react-i18next';
6
- import { Center, Flexbox } from 'react-layout-kit';
7
-
8
- import { useChatStore } from '@/store/chat';
9
- import { useGlobalStore } from '@/store/global';
10
- import { modelProviderSelectors } from '@/store/global/selectors';
11
-
12
- import { FormAction } from './style';
13
-
14
- const APIKeyForm = memo<{ id: string }>(({ id }) => {
15
- const { t } = useTranslation('error');
16
- const [showProxy, setShow] = useState(false);
17
-
18
- const [apiKey, proxyUrl, setConfig] = useGlobalStore((s) => [
19
- modelProviderSelectors.openAIAPI(s),
20
- modelProviderSelectors.openAIProxyUrl(s),
21
- s.setOpenAIConfig,
22
- ]);
23
-
24
- const [resend, deleteMessage] = useChatStore((s) => [s.resendMessage, s.deleteMessage]);
25
-
26
- return (
27
- <Center gap={16} style={{ maxWidth: 300 }}>
28
- <FormAction
29
- avatar={'🔑'}
30
- description={t('unlock.apikey.description')}
31
- title={t('unlock.apikey.title')}
32
- >
33
- <Input.Password
34
- autoComplete={'new-password'}
35
- onChange={(e) => {
36
- setConfig({ OPENAI_API_KEY: e.target.value });
37
- }}
38
- placeholder={'sk-*****************************************'}
39
- type={'block'}
40
- value={apiKey}
41
- />
42
- {showProxy ? (
43
- <Input
44
- onChange={(e) => {
45
- setConfig({ endpoint: e.target.value });
46
- }}
47
- placeholder={'https://api.openai.com/v1'}
48
- type={'block'}
49
- value={proxyUrl}
50
- />
51
- ) : (
52
- <Button
53
- icon={<Icon icon={Network} />}
54
- onClick={() => {
55
- setShow(true);
56
- }}
57
- type={'text'}
58
- >
59
- {t('unlock.apikey.addProxyUrl')}
60
- </Button>
61
- )}
62
- </FormAction>
63
- <Flexbox gap={12} width={'100%'}>
64
- <Button
65
- block
66
- onClick={() => {
67
- resend(id);
68
- deleteMessage(id);
69
- }}
70
- style={{ marginTop: 8 }}
71
- type={'primary'}
72
- >
73
- {t('unlock.confirm')}
74
- </Button>
75
- <Button
76
- onClick={() => {
77
- deleteMessage(id);
78
- }}
79
- >
80
- {t('unlock.closeMessage')}
81
- </Button>
82
- </Flexbox>
83
- </Center>
84
- );
85
- });
86
-
87
- export default APIKeyForm;
@@ -1,23 +0,0 @@
1
- import { createStyles } from 'antd-style';
2
- import { ReactNode, memo } from 'react';
3
- import { Center } from 'react-layout-kit';
4
-
5
- const useStyles = createStyles(({ css, token }) => ({
6
- container: css`
7
- background: ${token.colorBgContainer};
8
- border: 1px solid ${token.colorSplit};
9
- border-radius: 8px;
10
- `,
11
- }));
12
-
13
- const ErrorActionContainer = memo<{ children: ReactNode }>(({ children }) => {
14
- const { styles } = useStyles();
15
-
16
- return (
17
- <Center className={styles.container} gap={24} padding={24}>
18
- {children}
19
- </Center>
20
- );
21
- });
22
-
23
- export default ErrorActionContainer;
@@ -1,90 +0,0 @@
1
- import { Icon } from '@lobehub/ui';
2
- import { Button, Input, Segmented } from 'antd';
3
- import { AsteriskSquare, KeySquare } from 'lucide-react';
4
- import { memo, useState } from 'react';
5
- import { useTranslation } from 'react-i18next';
6
- import { Flexbox } from 'react-layout-kit';
7
-
8
- import { useChatStore } from '@/store/chat';
9
- import { useGlobalStore } from '@/store/global';
10
- import { settingsSelectors } from '@/store/global/selectors';
11
-
12
- import { RenderErrorMessage } from '../types';
13
- import APIKeyForm from './ApiKeyForm';
14
- import { ErrorActionContainer, FormAction } from './style';
15
-
16
- enum Tab {
17
- Api = 'api',
18
- Password = 'password',
19
- }
20
-
21
- const InvalidAccess: RenderErrorMessage['Render'] = memo(({ id }) => {
22
- const { t } = useTranslation('error');
23
- const [mode, setMode] = useState<Tab>(Tab.Password);
24
- const [password, setSettings] = useGlobalStore((s) => [
25
- settingsSelectors.currentSettings(s).password,
26
- s.setSettings,
27
- ]);
28
- const [resend, deleteMessage] = useChatStore((s) => [s.resendMessage, s.deleteMessage]);
29
-
30
- return (
31
- <ErrorActionContainer>
32
- <Segmented
33
- block
34
- onChange={(value) => setMode(value as Tab)}
35
- options={[
36
- {
37
- icon: <Icon icon={AsteriskSquare} />,
38
- label: t('password', { ns: 'common' }),
39
- value: Tab.Password,
40
- },
41
- { icon: <Icon icon={KeySquare} />, label: 'OpenAI API Key', value: Tab.Api },
42
- ]}
43
- style={{ width: '100%' }}
44
- value={mode}
45
- />
46
- <Flexbox gap={24}>
47
- {mode === Tab.Password && (
48
- <>
49
- <FormAction
50
- avatar={'🗳'}
51
- description={t('unlock.password.description')}
52
- title={t('unlock.password.title')}
53
- >
54
- <Input.Password
55
- autoComplete={'new-password'}
56
- onChange={(e) => {
57
- setSettings({ password: e.target.value });
58
- }}
59
- placeholder={t('unlock.password.placeholder')}
60
- type={'block'}
61
- value={password}
62
- />
63
- </FormAction>
64
- <Flexbox gap={12}>
65
- <Button
66
- onClick={() => {
67
- resend(id);
68
- deleteMessage(id);
69
- }}
70
- type={'primary'}
71
- >
72
- {t('unlock.confirm')}
73
- </Button>
74
- <Button
75
- onClick={() => {
76
- deleteMessage(id);
77
- }}
78
- >
79
- {t('unlock.closeMessage')}
80
- </Button>
81
- </Flexbox>
82
- </>
83
- )}
84
- {mode === Tab.Api && <APIKeyForm id={id} />}
85
- </Flexbox>
86
- </ErrorActionContainer>
87
- );
88
- });
89
-
90
- export default InvalidAccess;
@@ -1,13 +0,0 @@
1
- import { memo } from 'react';
2
-
3
- import { RenderErrorMessage } from '../types';
4
- import APIKeyForm from './ApiKeyForm';
5
- import { ErrorActionContainer } from './style';
6
-
7
- const OpenAPIKey: RenderErrorMessage['Render'] = memo(({ id }) => (
8
- <ErrorActionContainer>
9
- <APIKeyForm id={id} />
10
- </ErrorActionContainer>
11
- ));
12
-
13
- export default OpenAPIKey;
@@ -1,30 +0,0 @@
1
- import { Highlighter } from '@lobehub/ui';
2
- import { memo } from 'react';
3
- import { Flexbox } from 'react-layout-kit';
4
-
5
- import { RenderErrorMessage } from '../../types';
6
-
7
- interface OpenAIError {
8
- code: 'invalid_api_key' | string;
9
- message: string;
10
- param?: any;
11
- type: string;
12
- }
13
-
14
- interface OpenAIErrorResponse {
15
- error: OpenAIError;
16
- }
17
-
18
- const PluginError: RenderErrorMessage['Render'] = memo(({ error, id }) => {
19
- const errorBody: OpenAIErrorResponse = (error as any)?.body;
20
-
21
- return (
22
- <Flexbox id={id} style={{ maxWidth: 600 }}>
23
- <Highlighter copyButtonSize={'small'} language={'json'} type={'pure'}>
24
- {JSON.stringify(errorBody, null, 2)}
25
- </Highlighter>
26
- </Flexbox>
27
- );
28
- });
29
-
30
- export default PluginError;
@@ -1,15 +0,0 @@
1
- import { LanguageModelWhiteList } from '@/const/llm';
2
-
3
- import { createHeaderWithOpenAI } from './_header';
4
- import { OPENAI_URLS } from './_url';
5
-
6
- export const fetchModelList = async (): Promise<string[]> => {
7
- const res = await fetch(OPENAI_URLS.models, {
8
- headers: createHeaderWithOpenAI(),
9
- method: 'POST',
10
- });
11
-
12
- const modelList: string[] = await res.json();
13
-
14
- return LanguageModelWhiteList.filter((i) => modelList.includes(i));
15
- };