@lobehub/chat 1.14.11 → 1.15.0

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 (50) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/Dockerfile +2 -0
  3. package/Dockerfile.database +2 -0
  4. package/locales/ar/file.json +2 -1
  5. package/locales/bg-BG/file.json +2 -1
  6. package/locales/de-DE/file.json +2 -1
  7. package/locales/en-US/file.json +2 -1
  8. package/locales/es-ES/file.json +2 -1
  9. package/locales/fr-FR/file.json +2 -1
  10. package/locales/it-IT/file.json +2 -1
  11. package/locales/ja-JP/file.json +2 -1
  12. package/locales/ko-KR/file.json +2 -1
  13. package/locales/nl-NL/file.json +2 -1
  14. package/locales/pl-PL/file.json +2 -1
  15. package/locales/pt-BR/file.json +2 -1
  16. package/locales/ru-RU/file.json +2 -1
  17. package/locales/tr-TR/file.json +2 -1
  18. package/locales/vi-VN/file.json +2 -1
  19. package/locales/zh-CN/file.json +2 -1
  20. package/locales/zh-TW/file.json +2 -1
  21. package/package.json +1 -1
  22. package/src/app/(main)/settings/llm/ProviderList/providers.tsx +2 -0
  23. package/src/app/api/chat/agentRuntime.ts +7 -0
  24. package/src/components/FileParsingStatus/index.tsx +0 -2
  25. package/src/config/llm.ts +6 -0
  26. package/src/config/modelProviders/index.ts +4 -0
  27. package/src/config/modelProviders/moonshot.ts +6 -6
  28. package/src/config/modelProviders/upstage.ts +45 -0
  29. package/src/config/modelProviders/zeroone.ts +30 -29
  30. package/src/config/modelProviders/zhipu.ts +8 -8
  31. package/src/const/settings/llm.ts +5 -0
  32. package/src/const/url.ts +3 -0
  33. package/src/features/Conversation/Error/APIKeyForm/ProviderAvatar.tsx +5 -0
  34. package/src/features/FileViewer/NotSupport/index.tsx +15 -4
  35. package/src/features/FileViewer/Renderer/Image/index.tsx +36 -0
  36. package/src/features/FileViewer/Renderer/MSDoc/index.tsx +65 -0
  37. package/src/features/FileViewer/Renderer/index.ts +5 -0
  38. package/src/features/FileViewer/index.tsx +14 -14
  39. package/src/libs/agent-runtime/AgentRuntime.ts +7 -0
  40. package/src/libs/agent-runtime/types/type.ts +1 -0
  41. package/src/libs/agent-runtime/upstage/index.test.ts +255 -0
  42. package/src/libs/agent-runtime/upstage/index.ts +10 -0
  43. package/src/locales/default/file.ts +2 -1
  44. package/src/server/globalConfig/index.ts +3 -0
  45. package/src/types/user/settings/keyVaults.ts +1 -0
  46. /package/src/features/FileViewer/{PDFViewer → Renderer/PDF}/HighlightLayer.tsx +0 -0
  47. /package/src/features/FileViewer/{PDFViewer → Renderer/PDF}/index.tsx +0 -0
  48. /package/src/features/FileViewer/{PDFViewer → Renderer/PDF}/style.ts +0 -0
  49. /package/src/features/FileViewer/{PDFViewer → Renderer/PDF}/useResizeObserver.ts +0 -0
  50. /package/src/features/FileViewer/{TXTViewer → Renderer/TXT}/index.tsx +0 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,57 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 1.15.0](https://github.com/lobehub/lobe-chat/compare/v1.14.12...v1.15.0)
6
+
7
+ <sup>Released on **2024-08-30**</sup>
8
+
9
+ #### ✨ Features
10
+
11
+ - **misc**: Add Upstage model provider support.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's improved
19
+
20
+ - **misc**: Add Upstage model provider support, closes [#3670](https://github.com/lobehub/lobe-chat/issues/3670) ([4b8591b](https://github.com/lobehub/lobe-chat/commit/4b8591b))
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
+
30
+ ### [Version 1.14.12](https://github.com/lobehub/lobe-chat/compare/v1.14.11...v1.14.12)
31
+
32
+ <sup>Released on **2024-08-30**</sup>
33
+
34
+ #### 💄 Styles
35
+
36
+ - **misc**: Fix ms doc file preview, Update the sorting of each provider model.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### Styles
44
+
45
+ - **misc**: Fix ms doc file preview, closes [#3686](https://github.com/lobehub/lobe-chat/issues/3686) ([2cd78cf](https://github.com/lobehub/lobe-chat/commit/2cd78cf))
46
+ - **misc**: Update the sorting of each provider model, closes [#3688](https://github.com/lobehub/lobe-chat/issues/3688) ([2630bbc](https://github.com/lobehub/lobe-chat/commit/2630bbc))
47
+
48
+ </details>
49
+
50
+ <div align="right">
51
+
52
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
53
+
54
+ </div>
55
+
5
56
  ### [Version 1.14.11](https://github.com/lobehub/lobe-chat/compare/v1.14.10...v1.14.11)
6
57
 
7
58
  <sup>Released on **2024-08-30**</sup>
package/Dockerfile CHANGED
@@ -146,6 +146,8 @@ ENV \
146
146
  TAICHU_API_KEY="" \
147
147
  # TogetherAI
148
148
  TOGETHERAI_API_KEY="" TOGETHERAI_MODEL_LIST="" \
149
+ # Upstage
150
+ UPSTAGE_API_KEY="" \
149
151
  # 01.AI
150
152
  ZEROONE_API_KEY="" \
151
153
  # Zhipu
@@ -178,6 +178,8 @@ ENV \
178
178
  TAICHU_API_KEY="" \
179
179
  # TogetherAI
180
180
  TOGETHERAI_API_KEY="" TOGETHERAI_MODEL_LIST="" \
181
+ # Upstage
182
+ UPSTAGE_API_KEY="" \
181
183
  # 01.AI
182
184
  ZEROONE_API_KEY="" \
183
185
  # Zhipu
@@ -56,7 +56,8 @@
56
56
  "title": "الوضع الحالي للنشر لا يدعم إدارة الملفات"
57
57
  },
58
58
  "preview": {
59
- "unsupportedFile": "هذا التنسيق غير مدعوم للمعاينة عبر الإنترنت"
59
+ "downloadFile": "تحميل الملف",
60
+ "unsupportedFileAndContact": "هذا التنسيق من الملفات غير مدعوم للمعاينة عبر الإنترنت، إذا كان لديك طلب للمعاينة، فلا تتردد في <1>إبلاغنا</1>"
60
61
  },
61
62
  "searchFilePlaceholder": "بحث عن ملف",
62
63
  "tab": {
@@ -56,7 +56,8 @@
56
56
  "title": "Текущият режим на инсталация не поддържа управление на файлове"
57
57
  },
58
58
  "preview": {
59
- "unsupportedFile": "Този файлов формат не поддържа онлайн преглед"
59
+ "downloadFile": "Изтеглете файла",
60
+ "unsupportedFileAndContact": "Този формат на файла не поддържа онлайн преглед. Ако имате нужда от преглед, моля, <1>свържете се с нас</1>."
60
61
  },
61
62
  "searchFilePlaceholder": "Търсене на файл",
62
63
  "tab": {
@@ -56,7 +56,8 @@
56
56
  "title": "Der aktuelle Bereitstellungsmodus unterstützt keine Dateiverwaltung"
57
57
  },
58
58
  "preview": {
59
- "unsupportedFile": "Dieses Dateiformat wird nicht für die Online-Vorschau unterstützt"
59
+ "downloadFile": "Datei herunterladen",
60
+ "unsupportedFileAndContact": "Dieses Dateiformat wird derzeit nicht für die Online-Vorschau unterstützt. Wenn Sie eine Vorschau wünschen, können Sie uns gerne <1>Feedback geben</1>."
60
61
  },
61
62
  "searchFilePlaceholder": "Datei suchen",
62
63
  "tab": {
@@ -56,7 +56,8 @@
56
56
  "title": "The current deployment mode does not support file management"
57
57
  },
58
58
  "preview": {
59
- "unsupportedFile": "This file format does not support online preview"
59
+ "downloadFile": "Download File",
60
+ "unsupportedFileAndContact": "This file format is not currently supported for online preview. If you have a request for previewing, feel free to <1>contact us</1>."
60
61
  },
61
62
  "searchFilePlaceholder": "Search Files",
62
63
  "tab": {
@@ -56,7 +56,8 @@
56
56
  "title": "El modo de despliegue actual no soporta la gestión de archivos"
57
57
  },
58
58
  "preview": {
59
- "unsupportedFile": "Este formato de archivo no es compatible con la vista previa en línea"
59
+ "downloadFile": "Descargar archivo",
60
+ "unsupportedFileAndContact": "Este formato de archivo no es compatible con la vista previa en línea. Si desea solicitar una vista previa, no dude en <1>contactarnos</1>."
60
61
  },
61
62
  "searchFilePlaceholder": "Buscar archivo",
62
63
  "tab": {
@@ -56,7 +56,8 @@
56
56
  "title": "Le mode de déploiement actuel ne prend pas en charge la gestion des fichiers"
57
57
  },
58
58
  "preview": {
59
- "unsupportedFile": "Ce format de fichier ne prend pas en charge l'aperçu en ligne"
59
+ "downloadFile": "Télécharger le fichier",
60
+ "unsupportedFileAndContact": "Ce format de fichier n'est pas encore pris en charge pour l'aperçu en ligne. Si vous souhaitez un aperçu, n'hésitez pas à <1>nous contacter</1>."
60
61
  },
61
62
  "searchFilePlaceholder": "Rechercher un fichier",
62
63
  "tab": {
@@ -56,7 +56,8 @@
56
56
  "title": "La modalità di distribuzione attuale non supporta la gestione dei file"
57
57
  },
58
58
  "preview": {
59
- "unsupportedFile": "Questo formato di file non supporta l'anteprima online"
59
+ "downloadFile": "Scarica file",
60
+ "unsupportedFileAndContact": "Questo formato di file non è attualmente supportato per la visualizzazione online. Se hai bisogno di una visualizzazione, ti preghiamo di <1>contattarci</1>."
60
61
  },
61
62
  "searchFilePlaceholder": "Cerca file",
62
63
  "tab": {
@@ -56,7 +56,8 @@
56
56
  "title": "現在のデプロイメントモードはファイル管理をサポートしていません"
57
57
  },
58
58
  "preview": {
59
- "unsupportedFile": "このファイル形式はオンラインプレビューをサポートしていません"
59
+ "downloadFile": "ファイルをダウンロード",
60
+ "unsupportedFileAndContact": "このファイル形式はオンラインプレビューをサポートしていません。プレビューのリクエストがある場合は、ぜひ<1>ご連絡ください</1>。"
60
61
  },
61
62
  "searchFilePlaceholder": "ファイルを検索",
62
63
  "tab": {
@@ -56,7 +56,8 @@
56
56
  "title": "현재 배포 모드는 파일 관리를 지원하지 않습니다."
57
57
  },
58
58
  "preview": {
59
- "unsupportedFile": "파일 형식은 온라인 미리보기를 지원하지 않습니다."
59
+ "downloadFile": "파일 다운로드",
60
+ "unsupportedFileAndContact": "이 파일 형식은 온라인 미리보기를 지원하지 않습니다. 미리보기가 필요하신 경우, <1>저희에게 피드백을 주시기 바랍니다</1>."
60
61
  },
61
62
  "searchFilePlaceholder": "파일 검색",
62
63
  "tab": {
@@ -56,7 +56,8 @@
56
56
  "title": "De huidige implementatiemodus ondersteunt geen bestandsbeheer"
57
57
  },
58
58
  "preview": {
59
- "unsupportedFile": "Dit bestandsformaat wordt niet ondersteund voor online preview"
59
+ "downloadFile": "Bestand downloaden",
60
+ "unsupportedFileAndContact": "Dit bestandsformaat wordt momenteel niet ondersteund voor online preview. Als u een preview wilt, neem dan gerust <1>contact met ons op</1>."
60
61
  },
61
62
  "searchFilePlaceholder": "Zoek bestand",
62
63
  "tab": {
@@ -56,7 +56,8 @@
56
56
  "title": "Obecny tryb wdrożenia nie obsługuje zarządzania plikami"
57
57
  },
58
58
  "preview": {
59
- "unsupportedFile": "Ten format pliku nie jest obsługiwany w podglądzie online"
59
+ "downloadFile": "Pobierz plik",
60
+ "unsupportedFileAndContact": "Ten format pliku nie jest obecnie obsługiwany w podglądzie online. Jeśli chcesz uzyskać podgląd, zachęcamy do <1>skontaktowania się z nami</1>."
60
61
  },
61
62
  "searchFilePlaceholder": "Szukaj pliku",
62
63
  "tab": {
@@ -56,7 +56,8 @@
56
56
  "title": "O modo de implantação atual não suporta gerenciamento de arquivos"
57
57
  },
58
58
  "preview": {
59
- "unsupportedFile": "Este formato de arquivo não suporta visualização online"
59
+ "downloadFile": "Baixar arquivo",
60
+ "unsupportedFileAndContact": "Este formato de arquivo não é suportado para visualização online. Se você tiver interesse em visualizar, sinta-se à vontade para <1>nos enviar um feedback</1>."
60
61
  },
61
62
  "searchFilePlaceholder": "Pesquisar arquivo",
62
63
  "tab": {
@@ -56,7 +56,8 @@
56
56
  "title": "Текущий режим развертывания не поддерживает управление файлами"
57
57
  },
58
58
  "preview": {
59
- "unsupportedFile": "Этот формат файла не поддерживает онлайн-просмотр"
59
+ "downloadFile": "Скачать файл",
60
+ "unsupportedFileAndContact": "Этот формат файла в настоящее время не поддерживает онлайн-просмотр. Если у вас есть запрос на просмотр, пожалуйста, <1>сообщите нам</1>."
60
61
  },
61
62
  "searchFilePlaceholder": "Поиск файла",
62
63
  "tab": {
@@ -56,7 +56,8 @@
56
56
  "title": "Mevcut dağıtım modu dosya yönetimini desteklemiyor"
57
57
  },
58
58
  "preview": {
59
- "unsupportedFile": "Bu dosya formatı çevrimiçi önizleme desteklenmiyor"
59
+ "downloadFile": "Dosyayı İndir",
60
+ "unsupportedFileAndContact": "Bu dosya formatı çevrimiçi önizleme için desteklenmiyor. Önizleme talebiniz varsa, lütfen <1>bize geri bildirimde bulunun</1>."
60
61
  },
61
62
  "searchFilePlaceholder": "Dosya Ara",
62
63
  "tab": {
@@ -56,7 +56,8 @@
56
56
  "title": "Chế độ triển khai hiện tại không hỗ trợ quản lý tệp"
57
57
  },
58
58
  "preview": {
59
- "unsupportedFile": "Định dạng tệp này không hỗ trợ xem trước trực tuyến"
59
+ "downloadFile": "Tải tệp",
60
+ "unsupportedFileAndContact": "Định dạng tệp này hiện không hỗ trợ xem trước trực tuyến. Nếu bạn có yêu cầu xem trước, vui lòng <1>phản hồi cho chúng tôi</1>"
60
61
  },
61
62
  "searchFilePlaceholder": "Tìm kiếm tệp",
62
63
  "tab": {
@@ -56,7 +56,8 @@
56
56
  "title": "当前部署模式不支持文件管理"
57
57
  },
58
58
  "preview": {
59
- "unsupportedFile": "此文件格式不支持在线预览"
59
+ "downloadFile": "下载文件",
60
+ "unsupportedFileAndContact": "此文件格式暂不支持在线预览,如有预览诉求,欢迎<1>反馈给我们</1>"
60
61
  },
61
62
  "searchFilePlaceholder": "搜索文件",
62
63
  "tab": {
@@ -56,7 +56,8 @@
56
56
  "title": "當前部署模式不支持檔案管理"
57
57
  },
58
58
  "preview": {
59
- "unsupportedFile": "此檔案格式不支持在線預覽"
59
+ "downloadFile": "下載檔案",
60
+ "unsupportedFileAndContact": "此檔案格式暫不支援線上預覽,如有預覽需求,歡迎<1>反饋給我們</1>"
60
61
  },
61
62
  "searchFilePlaceholder": "搜索檔案",
62
63
  "tab": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.14.11",
3
+ "version": "1.15.0",
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",
@@ -18,6 +18,7 @@ import {
18
18
  StepfunProviderCard,
19
19
  TaichuProviderCard,
20
20
  TogetherAIProviderCard,
21
+ UpstageProviderCard,
21
22
  ZeroOneProviderCard,
22
23
  ZhiPuProviderCard,
23
24
  } from '@/config/modelProviders';
@@ -59,6 +60,7 @@ export const useProviderList = (): ProviderItem[] => {
59
60
  TaichuProviderCard,
60
61
  Ai360ProviderCard,
61
62
  SiliconCloudProviderCard,
63
+ UpstageProviderCard,
62
64
  ],
63
65
  [AzureProvider, OllamaProvider, OpenAIProvider, BedrockProvider],
64
66
  );
@@ -208,6 +208,13 @@ const getLlmOptionsFromPayload = (provider: string, payload: JWTPayload) => {
208
208
 
209
209
  return { apiKey, baseURL };
210
210
  }
211
+ case ModelProvider.Upstage: {
212
+ const { UPSTAGE_API_KEY } = getLLMConfig();
213
+
214
+ const apiKey = apiKeyManager.pick(payload?.apiKey || UPSTAGE_API_KEY);
215
+
216
+ return { apiKey };
217
+ }
211
218
  }
212
219
  };
213
220
 
@@ -104,8 +104,6 @@ const FileParsingStatus = memo<FileParsingStatusProps>(
104
104
  }
105
105
 
106
106
  case AsyncTaskStatus.Success: {
107
- console.log(embeddingStatus);
108
-
109
107
  // if no embedding status, it means that the embedding is not started
110
108
  if (!embeddingStatus || preparingEmbedding)
111
109
  return (
package/src/config/llm.ts CHANGED
@@ -93,6 +93,9 @@ export const getLLMConfig = () => {
93
93
  SILICONCLOUD_API_KEY: z.string().optional(),
94
94
  SILICONCLOUD_MODEL_LIST: z.string().optional(),
95
95
  SILICONCLOUD_PROXY_URL: z.string().optional(),
96
+
97
+ ENABLED_UPSTAGE: z.boolean(),
98
+ UPSTAGE_API_KEY: z.string().optional(),
96
99
  },
97
100
  runtimeEnv: {
98
101
  API_KEY_SELECT_MODE: process.env.API_KEY_SELECT_MODE,
@@ -183,6 +186,9 @@ export const getLLMConfig = () => {
183
186
  SILICONCLOUD_API_KEY: process.env.SILICONCLOUD_API_KEY,
184
187
  SILICONCLOUD_MODEL_LIST: process.env.SILICONCLOUD_MODEL_LIST,
185
188
  SILICONCLOUD_PROXY_URL: process.env.SILICONCLOUD_PROXY_URL,
189
+
190
+ ENABLED_UPSTAGE: !!process.env.UPSTAGE_API_KEY,
191
+ UPSTAGE_API_KEY: process.env.UPSTAGE_API_KEY,
186
192
  },
187
193
  });
188
194
  };
@@ -21,6 +21,7 @@ import SiliconCloudProvider from './siliconcloud';
21
21
  import StepfunProvider from './stepfun';
22
22
  import TaichuProvider from './taichu';
23
23
  import TogetherAIProvider from './togetherai';
24
+ import UpstageProvider from './upstage';
24
25
  import ZeroOneProvider from './zeroone';
25
26
  import ZhiPuProvider from './zhipu';
26
27
 
@@ -47,6 +48,7 @@ export const LOBE_DEFAULT_MODEL_LIST: ChatModelCard[] = [
47
48
  TaichuProvider.chatModels,
48
49
  Ai360Provider.chatModels,
49
50
  SiliconCloudProvider.chatModels,
51
+ UpstageProvider.chatModels,
50
52
  ].flat();
51
53
 
52
54
  export const DEFAULT_MODEL_PROVIDER_LIST = [
@@ -73,6 +75,7 @@ export const DEFAULT_MODEL_PROVIDER_LIST = [
73
75
  TaichuProvider,
74
76
  Ai360Provider,
75
77
  SiliconCloudProvider,
78
+ UpstageProvider,
76
79
  ];
77
80
 
78
81
  export const filterEnabledModels = (provider: ModelProviderCard) => {
@@ -105,5 +108,6 @@ export { default as SiliconCloudProviderCard } from './siliconcloud';
105
108
  export { default as StepfunProviderCard } from './stepfun';
106
109
  export { default as TaichuProviderCard } from './taichu';
107
110
  export { default as TogetherAIProviderCard } from './togetherai';
111
+ export { default as UpstageProviderCard } from './upstage';
108
112
  export { default as ZeroOneProviderCard } from './zeroone';
109
113
  export { default as ZhiPuProviderCard } from './zhipu';
@@ -4,11 +4,11 @@ import { ModelProviderCard } from '@/types/llm';
4
4
  const Moonshot: ModelProviderCard = {
5
5
  chatModels: [
6
6
  {
7
- displayName: 'Moonshot V1 8K',
7
+ displayName: 'Moonshot V1 128K',
8
8
  enabled: true,
9
9
  functionCall: true,
10
- id: 'moonshot-v1-8k',
11
- tokens: 8192,
10
+ id: 'moonshot-v1-128k',
11
+ tokens: 128_000,
12
12
  },
13
13
  {
14
14
  displayName: 'Moonshot V1 32K',
@@ -18,11 +18,11 @@ const Moonshot: ModelProviderCard = {
18
18
  tokens: 32_768,
19
19
  },
20
20
  {
21
- displayName: 'Moonshot V1 128K',
21
+ displayName: 'Moonshot V1 8K',
22
22
  enabled: true,
23
23
  functionCall: true,
24
- id: 'moonshot-v1-128k',
25
- tokens: 128_000,
24
+ id: 'moonshot-v1-8k',
25
+ tokens: 8192,
26
26
  },
27
27
  ],
28
28
  checkModel: 'moonshot-v1-8k',
@@ -0,0 +1,45 @@
1
+ import { ModelProviderCard } from '@/types/llm';
2
+
3
+ // ref https://developers.upstage.ai/docs/getting-started/models
4
+ const Upstage: ModelProviderCard = {
5
+ chatModels: [
6
+ {
7
+ description: 'A compact LLM offering superior performance to GPT-3.5, with robust multilingual capabilities for both English and Korean, delivering high efficiency in a smaller package. solar-1-mini-chat is alias for our latest solar-1-mini-chat model. (Currently solar-1-mini-chat-240612)',
8
+ displayName: 'Solar 1 Mini Chat',
9
+ enabled: true,
10
+ functionCall: true,
11
+ id: 'solar-1-mini-chat',
12
+ tokens: 32_768,
13
+ },
14
+ {
15
+ description: 'A compact LLM that extends the capabilities of solar-mini-chat with specialization in Japanese, while maintaining high efficiency and performance in English and Korean. solar-1-mini-chat-ja is alias for our latest solar-1-mini-chat-ja model.(Currently solar-1-mini-chat-ja-240612)',
16
+ displayName: 'Solar 1 Mini Chat Ja',
17
+ enabled: true,
18
+ functionCall: false,
19
+ id: 'solar-1-mini-chat-ja',
20
+ tokens: 32_768,
21
+ },
22
+ {
23
+ description: 'English-to-Korean translation specialized model based on the solar-mini. Maximum context length is 32k tokens. solar-1-mini-translate-enko is alias for our latest solar-1-mini-translate-enko model. (Currently solar-1-mini-translate-enko-240507)',
24
+ displayName: 'Solar 1 Mini Translate EnKo',
25
+ enabled: false,
26
+ functionCall: false,
27
+ id: 'solar-1-mini-translate-enko',
28
+ tokens: 32_768,
29
+ },
30
+ {
31
+ description: 'Korean-to-English translation specialized model based on the solar-mini. Maximum context length is 32k tokens. solar-1-mini-translate-koen is alias for our latest solar-1-mini-translate-koen model. (Currently solar-1-mini-translate-koen-240507)',
32
+ displayName: 'Solar 1 Mini Translate KoEn',
33
+ enabled: false,
34
+ functionCall: false,
35
+ id: 'solar-1-mini-translate-koen',
36
+ tokens: 32_768,
37
+ },
38
+ ],
39
+ checkModel: 'solar-1-mini-chat',
40
+ id: 'upstage',
41
+ modelList: { showModelFetcher: true },
42
+ name: 'Upstage',
43
+ };
44
+
45
+ export default Upstage;
@@ -11,23 +11,39 @@ const ZeroOne: ModelProviderCard = {
11
11
  tokens: 32_768,
12
12
  },
13
13
  {
14
- description: '中型尺寸模型升级微调,能力均衡,性价比高。深度优化指令遵循能力。',
15
- displayName: 'Yi Medium',
14
+ description:
15
+ ' yi-large 模型的基础上支持并强化了工具调用的能力,适用于各种需要搭建 agent 或 workflow 的业务场景。',
16
+ displayName: 'Yi Large FC',
16
17
  enabled: true,
17
- id: 'yi-medium',
18
+ functionCall: true,
19
+ id: 'yi-large-fc',
20
+ tokens: 32_768,
21
+ },
22
+ {
23
+ description:
24
+ '基于 yi-large 超强模型的高阶服务,结合检索与生成技术提供精准答案,实时全网检索信息服务。',
25
+ displayName: 'Yi Large RAG',
26
+ enabled: true,
27
+ id: 'yi-large-rag',
18
28
  tokens: 16_384,
19
29
  },
20
30
  {
21
- description: '复杂视觉任务模型,提供高性能图片理解、分析能力。',
22
- displayName: 'Yi Vision',
31
+ description: '超高性价比、卓越性能。根据性能和推理速度、成本,进行平衡性高精度调优。',
32
+ displayName: 'Yi Large Turbo',
23
33
  enabled: true,
24
- id: 'yi-vision',
34
+ id: 'yi-large-turbo',
35
+ tokens: 16_384,
36
+ },
37
+ {
38
+ description: '中型尺寸模型升级微调,能力均衡,性价比高。深度优化指令遵循能力。',
39
+ displayName: 'Yi Medium',
40
+ enabled: true,
41
+ id: 'yi-medium',
25
42
  tokens: 16_384,
26
- vision: true,
27
43
  },
28
44
  {
29
45
  description: '200K 超长上下文窗口,提供长文本深度理解和生成能力。',
30
- displayName: 'Yi 200K',
46
+ displayName: 'Yi Medium 200K',
31
47
  enabled: true,
32
48
  id: 'yi-medium-200k',
33
49
  tokens: 200_000,
@@ -35,32 +51,17 @@ const ZeroOne: ModelProviderCard = {
35
51
  {
36
52
  description: '小而精悍,轻量极速模型。提供强化数学运算和代码编写能力。',
37
53
  displayName: 'Yi Spark',
38
- id: 'yi-spark',
39
- tokens: 16_384,
40
- },
41
- {
42
- description:
43
- '基于 yi-large 超强模型的高阶服务,结合检索与生成技术提供精准答案,实时全网检索信息服务。',
44
- displayName: 'Yi Large RAG',
45
54
  enabled: true,
46
- id: 'yi-large-rag',
55
+ id: 'yi-spark',
47
56
  tokens: 16_384,
48
57
  },
49
58
  {
50
- description:
51
- ' yi-large 模型的基础上支持并强化了工具调用的能力,适用于各种需要搭建 agent 或 workflow 的业务场景。',
52
- displayName: 'Yi Large FC',
53
- enabled: true,
54
- functionCall: true,
55
- id: 'yi-large-fc',
56
- tokens: 32_768,
57
- },
58
- {
59
- description: '超高性价比、卓越性能。根据性能和推理速度、成本,进行平衡性高精度调优。',
60
- displayName: 'Yi Large Turbo',
59
+ description: '复杂视觉任务模型,提供高性能图片理解、分析能力。',
60
+ displayName: 'Yi Vision',
61
61
  enabled: true,
62
- id: 'yi-large-turbo',
62
+ id: 'yi-vision',
63
63
  tokens: 16_384,
64
+ vision: true,
64
65
  },
65
66
  {
66
67
  description: '初期版本,推荐使用 yi-large(新版本)',
@@ -69,7 +70,7 @@ const ZeroOne: ModelProviderCard = {
69
70
  tokens: 16_384,
70
71
  },
71
72
  ],
72
- checkModel: 'yi-large',
73
+ checkModel: 'yi-spark',
73
74
  id: 'zeroone',
74
75
  name: '01.AI',
75
76
  };
@@ -5,14 +5,6 @@ import { ModelProviderCard } from '@/types/llm';
5
5
  // ref https://open.bigmodel.cn/modelcenter/square
6
6
  const ZhiPu: ModelProviderCard = {
7
7
  chatModels: [
8
- {
9
- description: '超长输入:专为处理超长文本和记忆型任务设计',
10
- displayName: 'GLM-4-Long',
11
- enabled: true,
12
- functionCall: true,
13
- id: 'glm-4-long',
14
- tokens: 1_024_000,
15
- },
16
8
  {
17
9
  description:
18
10
  'GLM-4-AllTools 是专门为支持智能体和相关任务而进一步优化的模型版本。它能够自主理解用户的意图,规划复杂的指令,并能够调用一个或多个工具(例如网络浏览器、代码解释器和文本生图像)以完成复杂的任务。',
@@ -60,6 +52,14 @@ const ZhiPu: ModelProviderCard = {
60
52
  id: 'glm-4-airx',
61
53
  tokens: 8192,
62
54
  },
55
+ {
56
+ description: '超长输入:专为处理超长文本和记忆型任务设计',
57
+ displayName: 'GLM-4-Long',
58
+ enabled: true,
59
+ functionCall: true,
60
+ id: 'glm-4-long',
61
+ tokens: 1_024_000,
62
+ },
63
63
  {
64
64
  description: '适用简单任务,速度最快,价格最实惠的版本',
65
65
  displayName: 'GLM-4-Flash',
@@ -19,6 +19,7 @@ import {
19
19
  StepfunProviderCard,
20
20
  TaichuProviderCard,
21
21
  TogetherAIProviderCard,
22
+ UpstageProviderCard,
22
23
  ZeroOneProviderCard,
23
24
  ZhiPuProviderCard,
24
25
  filterEnabledModels,
@@ -111,6 +112,10 @@ export const DEFAULT_LLM_CONFIG: UserModelProviderConfig = {
111
112
  enabled: false,
112
113
  enabledModels: filterEnabledModels(TogetherAIProviderCard),
113
114
  },
115
+ upstage: {
116
+ enabled: false,
117
+ enabledModels: filterEnabledModels(UpstageProviderCard),
118
+ },
114
119
  zeroone: {
115
120
  enabled: false,
116
121
  enabledModels: filterEnabledModels(ZeroOneProviderCard),
package/src/const/url.ts CHANGED
@@ -47,6 +47,9 @@ export const PLUGINS_INDEX_URL = 'https://chat-plugins.lobehub.com';
47
47
  export const MORE_MODEL_PROVIDER_REQUEST_URL =
48
48
  'https://github.com/lobehub/lobe-chat/discussions/1284';
49
49
 
50
+ export const MORE_FILE_PREVIEW_REQUEST_URL =
51
+ 'https://github.com/lobehub/lobe-chat/discussions/3684';
52
+
50
53
  export const AGENTS_INDEX_GITHUB = 'https://github.com/lobehub/lobe-chat-agents';
51
54
  export const AGENTS_INDEX_GITHUB_ISSUE = urlJoin(AGENTS_INDEX_GITHUB, 'issues/new');
52
55
 
@@ -16,6 +16,7 @@ import {
16
16
  Stepfun,
17
17
  Together,
18
18
  Tongyi,
19
+ Upstage,
19
20
  ZeroOne,
20
21
  Zhipu,
21
22
  } from '@lobehub/icons';
@@ -103,6 +104,10 @@ const ProviderAvatar = memo<ProviderAvatarProps>(({ provider }) => {
103
104
  return <Ai360 color={Ai360.colorPrimary} size={56} />;
104
105
  }
105
106
 
107
+ case ModelProvider.Upstage: {
108
+ return <Upstage color={Upstage.colorPrimary} size={56} />;
109
+ }
110
+
106
111
  default:
107
112
  case ModelProvider.OpenAI: {
108
113
  return <OpenAI color={theme.colorText} size={64} />;
@@ -1,10 +1,13 @@
1
1
  import { IDocument } from '@cyntler/react-doc-viewer';
2
+ import { FluentEmoji } from '@lobehub/ui';
2
3
  import { Button } from 'antd';
3
4
  import { createStyles } from 'antd-style';
5
+ import Link from 'next/link';
4
6
  import React, { ComponentType, useState } from 'react';
5
- import { useTranslation } from 'react-i18next';
7
+ import { Trans, useTranslation } from 'react-i18next';
6
8
  import { Center, Flexbox } from 'react-layout-kit';
7
9
 
10
+ import { MORE_FILE_PREVIEW_REQUEST_URL } from '@/const/url';
8
11
  import { downloadFile } from '@/utils/client/downloadFile';
9
12
 
10
13
  const useStyles = createStyles(({ css, token }) => ({
@@ -25,7 +28,7 @@ const NotSupport: ComponentType<{
25
28
  }> = ({ fileName, document: doc }) => {
26
29
  const { styles } = useStyles();
27
30
 
28
- const { t } = useTranslation(['file', 'common']);
31
+ const { t } = useTranslation('file');
29
32
 
30
33
  const [loading, setLoading] = useState(false);
31
34
 
@@ -33,7 +36,15 @@ const NotSupport: ComponentType<{
33
36
  <Flexbox className={styles.page} id="txt-renderer">
34
37
  <Center height={'100%'}>
35
38
  <Flexbox align={'center'} gap={12}>
36
- {t('preview.unsupportedFile')}
39
+ <FluentEmoji emoji={'👀'} size={64} />
40
+ <Flexbox style={{ textAlign: 'center' }}>
41
+ <Trans i18nKey="preview.unsupportedFileAndContact" ns={'file'}>
42
+ 此文件格式暂不支持在线预览,如有预览诉求,欢迎
43
+ <Link aria-label={'todo'} href={MORE_FILE_PREVIEW_REQUEST_URL} target="_blank">
44
+ 反馈给我们
45
+ </Link>
46
+ </Trans>
47
+ </Flexbox>
37
48
  <Button
38
49
  loading={loading}
39
50
  onClick={async () => {
@@ -43,7 +54,7 @@ const NotSupport: ComponentType<{
43
54
  setLoading(false);
44
55
  }}
45
56
  >
46
- {t('download', { ns: 'common' })}
57
+ {t('preview.downloadFile')}
47
58
  </Button>
48
59
  </Flexbox>
49
60
  </Center>
@@ -0,0 +1,36 @@
1
+ import { DocRenderer } from '@cyntler/react-doc-viewer';
2
+ import { Center } from 'react-layout-kit';
3
+
4
+ const ImageRenderer: DocRenderer = ({ mainState: { currentDocument } }) => {
5
+ const { uri, fileName } = currentDocument || {};
6
+
7
+ return (
8
+ <Center height={'100%'} width={'100%'}>
9
+ <img
10
+ alt={fileName}
11
+ height={'100%'}
12
+ src={uri}
13
+ style={{ objectFit: 'contain', overflow: 'hidden' }}
14
+ width={'100%'}
15
+ />
16
+ </Center>
17
+ );
18
+ };
19
+
20
+ export default ImageRenderer;
21
+
22
+ ImageRenderer.fileTypes = [
23
+ 'jpg',
24
+ 'jpeg',
25
+ 'image/jpg',
26
+ 'image/jpeg',
27
+ 'png',
28
+ 'image/png',
29
+ 'webp',
30
+ 'image/webp',
31
+ 'gif',
32
+ 'image/gif',
33
+ 'bmp',
34
+ 'image/bmp',
35
+ ];
36
+ ImageRenderer.weight = 0;
@@ -0,0 +1,65 @@
1
+ import { DocRenderer } from '@cyntler/react-doc-viewer';
2
+ import { css, cx } from 'antd-style';
3
+ import { Flexbox } from 'react-layout-kit';
4
+
5
+ const container = css`
6
+ position: relative;
7
+ overflow: hidden;
8
+ border-radius: 4px;
9
+ `;
10
+
11
+ const content = css`
12
+ position: absolute;
13
+ inset-block: -1px -1px;
14
+ inset-inline-start: -1px;
15
+
16
+ width: calc(100% + 2px);
17
+ height: calc(100% + 2px);
18
+
19
+ border: 0;
20
+ `;
21
+
22
+ const MSDocRenderer: DocRenderer = ({ mainState: { currentDocument } }) => {
23
+ if (!currentDocument) return null;
24
+
25
+ return (
26
+ <Flexbox className={cx(container)} height={'100%'} id="msdoc-renderer" width={'100%'}>
27
+ <iframe
28
+ className={cx(content)}
29
+ id="msdoc-iframe"
30
+ src={`https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(
31
+ currentDocument.uri,
32
+ )}`}
33
+ title="msdoc-iframe"
34
+ />
35
+ </Flexbox>
36
+ );
37
+ };
38
+
39
+ export default MSDocRenderer;
40
+
41
+ const MSDocFTMaps = {
42
+ doc: ['doc', 'application/msword'],
43
+ docx: [
44
+ 'docx',
45
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
46
+ 'application/octet-stream',
47
+ ],
48
+ odt: ['odt', 'application/vnd.oasis.opendocument.text'],
49
+ ppt: ['ppt', 'application/vnd.ms-powerpoint'],
50
+ pptx: ['pptx', 'application/vnd.openxmlformats-officedocument.presentationml.presentation'],
51
+ xls: ['xls', 'application/vnd.ms-excel'],
52
+ xlsx: ['xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],
53
+ };
54
+
55
+ MSDocRenderer.fileTypes = [
56
+ ...MSDocFTMaps.odt,
57
+ ...MSDocFTMaps.doc,
58
+ ...MSDocFTMaps.docx,
59
+ ...MSDocFTMaps.xls,
60
+ ...MSDocFTMaps.xlsx,
61
+ ...MSDocFTMaps.ppt,
62
+ ...MSDocFTMaps.pptx,
63
+ ];
64
+ MSDocRenderer.weight = 0;
65
+ MSDocRenderer.fileLoader = ({ fileLoaderComplete }) => fileLoaderComplete();
@@ -0,0 +1,5 @@
1
+ import ImageRenderer from './Image';
2
+ import MSDocRenderer from './MSDoc';
3
+ import TXTRenderer from './TXT';
4
+
5
+ export const FileViewRenderers = [TXTRenderer, ImageRenderer, MSDocRenderer];
@@ -1,21 +1,22 @@
1
1
  'use client';
2
2
 
3
- import DocViewer, { MSDocRenderer } from '@cyntler/react-doc-viewer';
4
- import { createStyles } from 'antd-style';
3
+ import DocViewer from '@cyntler/react-doc-viewer';
4
+ import { css, cx } from 'antd-style';
5
5
  import { CSSProperties, memo } from 'react';
6
6
 
7
7
  import { FileListItem } from '@/types/files';
8
8
 
9
9
  import NotSupport from './NotSupport';
10
- import PDFViewer from './PDFViewer';
11
- import TXTViewer from './TXTViewer';
10
+ import { FileViewRenderers } from './Renderer';
11
+ import PDFRenderer from './Renderer/PDF';
12
12
 
13
- const useStyles = createStyles(({ css, token }) => ({
14
- container: css`
15
- padding: 12px;
16
- background: ${token.colorBgLayout} !important;
17
- `,
18
- }));
13
+ const container = css`
14
+ background: transparent !important;
15
+
16
+ #proxy-renderer {
17
+ height: 100%;
18
+ }
19
+ `;
19
20
 
20
21
  interface FileViewerProps extends FileListItem {
21
22
  className?: string;
@@ -23,20 +24,19 @@ interface FileViewerProps extends FileListItem {
23
24
  }
24
25
 
25
26
  const FileViewer = memo<FileViewerProps>(({ id, style, fileType, url, name }) => {
26
- const { styles } = useStyles();
27
27
  if (fileType === 'pdf' || name.endsWith('.pdf')) {
28
- return <PDFViewer fileId={id} url={url} />;
28
+ return <PDFRenderer fileId={id} url={url} />;
29
29
  }
30
30
 
31
31
  return (
32
32
  <DocViewer
33
- className={styles.container}
33
+ className={cx(container)}
34
34
  config={{
35
35
  header: { disableHeader: true },
36
36
  noRenderer: { overrideComponent: NotSupport },
37
37
  }}
38
38
  documents={[{ fileName: name, fileType, uri: url }]}
39
- pluginRenderers={[TXTViewer, MSDocRenderer]}
39
+ pluginRenderers={FileViewRenderers}
40
40
  style={style}
41
41
  />
42
42
  );
@@ -24,6 +24,7 @@ import { LobeSiliconCloudAI } from './siliconcloud';
24
24
  import { LobeStepfunAI } from './stepfun';
25
25
  import { LobeTaichuAI } from './taichu';
26
26
  import { LobeTogetherAI } from './togetherai';
27
+ import { LobeUpstageAI } from './upstage';
27
28
  import {
28
29
  ChatCompetitionOptions,
29
30
  ChatStreamPayload,
@@ -134,6 +135,7 @@ class AgentRuntime {
134
135
  stepfun: Partial<ClientOptions>;
135
136
  taichu: Partial<ClientOptions>;
136
137
  togetherai: Partial<ClientOptions>;
138
+ upstage: Partial<ClientOptions>;
137
139
  zeroone: Partial<ClientOptions>;
138
140
  zhipu: Partial<ClientOptions>;
139
141
  }>,
@@ -261,6 +263,11 @@ class AgentRuntime {
261
263
  runtimeModel = new LobeSiliconCloudAI(params.siliconcloud ?? {});
262
264
  break;
263
265
  }
266
+
267
+ case ModelProvider.Upstage: {
268
+ runtimeModel = new LobeUpstageAI(params.upstage);
269
+ break
270
+ }
264
271
  }
265
272
 
266
273
  return new AgentRuntime(runtimeModel);
@@ -43,6 +43,7 @@ export enum ModelProvider {
43
43
  Stepfun = 'stepfun',
44
44
  Taichu = 'taichu',
45
45
  TogetherAI = 'togetherai',
46
+ Upstage = 'upstage',
46
47
  ZeroOne = 'zeroone',
47
48
  ZhiPu = 'zhipu',
48
49
  }
@@ -0,0 +1,255 @@
1
+ // @vitest-environment node
2
+ import OpenAI from 'openai';
3
+ import { Mock, afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
4
+
5
+ import {
6
+ ChatStreamCallbacks,
7
+ LobeOpenAICompatibleRuntime,
8
+ ModelProvider,
9
+ } from '@/libs/agent-runtime';
10
+
11
+ import * as debugStreamModule from '../utils/debugStream';
12
+ import { LobeUpstageAI } from './index';
13
+
14
+ const provider = ModelProvider.Upstage;
15
+ const defaultBaseURL = 'https://api.upstage.ai/v1/solar';
16
+
17
+ const bizErrorType = 'ProviderBizError';
18
+ const invalidErrorType = 'InvalidProviderAPIKey';
19
+
20
+ // Mock the console.error to avoid polluting test output
21
+ vi.spyOn(console, 'error').mockImplementation(() => {});
22
+
23
+ let instance: LobeOpenAICompatibleRuntime;
24
+
25
+ beforeEach(() => {
26
+ instance = new LobeUpstageAI({ apiKey: 'test' });
27
+
28
+ // 使用 vi.spyOn 来模拟 chat.completions.create 方法
29
+ vi.spyOn(instance['client'].chat.completions, 'create').mockResolvedValue(
30
+ new ReadableStream() as any,
31
+ );
32
+ });
33
+
34
+ afterEach(() => {
35
+ vi.clearAllMocks();
36
+ });
37
+
38
+ describe('LobeUpstageAI', () => {
39
+ describe('init', () => {
40
+ it('should correctly initialize with an API key', async () => {
41
+ const instance = new LobeUpstageAI({ apiKey: 'test_api_key' });
42
+ expect(instance).toBeInstanceOf(LobeUpstageAI);
43
+ expect(instance.baseURL).toEqual(defaultBaseURL);
44
+ });
45
+ });
46
+
47
+ describe('chat', () => {
48
+ describe('Error', () => {
49
+ it('should return OpenAIBizError with an openai error response when OpenAI.APIError is thrown', async () => {
50
+ // Arrange
51
+ const apiError = new OpenAI.APIError(
52
+ 400,
53
+ {
54
+ status: 400,
55
+ error: {
56
+ message: 'Bad Request',
57
+ },
58
+ },
59
+ 'Error message',
60
+ {},
61
+ );
62
+
63
+ vi.spyOn(instance['client'].chat.completions, 'create').mockRejectedValue(apiError);
64
+
65
+ // Act
66
+ try {
67
+ await instance.chat({
68
+ messages: [{ content: 'Hello', role: 'user' }],
69
+ model: 'solar-1-mini-chat',
70
+ temperature: 0,
71
+ });
72
+ } catch (e) {
73
+ expect(e).toEqual({
74
+ endpoint: defaultBaseURL,
75
+ error: {
76
+ error: { message: 'Bad Request' },
77
+ status: 400,
78
+ },
79
+ errorType: bizErrorType,
80
+ provider,
81
+ });
82
+ }
83
+ });
84
+
85
+ it('should throw AgentRuntimeError with NoOpenAIAPIKey if no apiKey is provided', async () => {
86
+ try {
87
+ new LobeUpstageAI({});
88
+ } catch (e) {
89
+ expect(e).toEqual({ errorType: invalidErrorType });
90
+ }
91
+ });
92
+
93
+ it('should return OpenAIBizError with the cause when OpenAI.APIError is thrown with cause', async () => {
94
+ // Arrange
95
+ const errorInfo = {
96
+ stack: 'abc',
97
+ cause: {
98
+ message: 'api is undefined',
99
+ },
100
+ };
101
+ const apiError = new OpenAI.APIError(400, errorInfo, 'module error', {});
102
+
103
+ vi.spyOn(instance['client'].chat.completions, 'create').mockRejectedValue(apiError);
104
+
105
+ // Act
106
+ try {
107
+ await instance.chat({
108
+ messages: [{ content: 'Hello', role: 'user' }],
109
+ model: 'solar-1-mini-chat',
110
+ temperature: 0,
111
+ });
112
+ } catch (e) {
113
+ expect(e).toEqual({
114
+ endpoint: defaultBaseURL,
115
+ error: {
116
+ cause: { message: 'api is undefined' },
117
+ stack: 'abc',
118
+ },
119
+ errorType: bizErrorType,
120
+ provider,
121
+ });
122
+ }
123
+ });
124
+
125
+ it('should return OpenAIBizError with an cause response with desensitize Url', async () => {
126
+ // Arrange
127
+ const errorInfo = {
128
+ stack: 'abc',
129
+ cause: { message: 'api is undefined' },
130
+ };
131
+ const apiError = new OpenAI.APIError(400, errorInfo, 'module error', {});
132
+
133
+ instance = new LobeUpstageAI({
134
+ apiKey: 'test',
135
+
136
+ baseURL: 'https://api.abc.com/v1',
137
+ });
138
+
139
+ vi.spyOn(instance['client'].chat.completions, 'create').mockRejectedValue(apiError);
140
+
141
+ // Act
142
+ try {
143
+ await instance.chat({
144
+ messages: [{ content: 'Hello', role: 'user' }],
145
+ model: 'solar-1-mini-chat',
146
+ temperature: 0,
147
+ });
148
+ } catch (e) {
149
+ expect(e).toEqual({
150
+ endpoint: 'https://api.***.com/v1',
151
+ error: {
152
+ cause: { message: 'api is undefined' },
153
+ stack: 'abc',
154
+ },
155
+ errorType: bizErrorType,
156
+ provider,
157
+ });
158
+ }
159
+ });
160
+
161
+ it('should throw an InvalidUpstageAPIKey error type on 401 status code', async () => {
162
+ // Mock the API call to simulate a 401 error
163
+ const error = new Error('Unauthorized') as any;
164
+ error.status = 401;
165
+ vi.mocked(instance['client'].chat.completions.create).mockRejectedValue(error);
166
+
167
+ try {
168
+ await instance.chat({
169
+ messages: [{ content: 'Hello', role: 'user' }],
170
+ model: 'solar-1-mini-chat',
171
+ temperature: 0,
172
+ });
173
+ } catch (e) {
174
+ // Expect the chat method to throw an error with InvalidUpstageAPIKey
175
+ expect(e).toEqual({
176
+ endpoint: defaultBaseURL,
177
+ error: new Error('Unauthorized'),
178
+ errorType: invalidErrorType,
179
+ provider,
180
+ });
181
+ }
182
+ });
183
+
184
+ it('should return AgentRuntimeError for non-OpenAI errors', async () => {
185
+ // Arrange
186
+ const genericError = new Error('Generic Error');
187
+
188
+ vi.spyOn(instance['client'].chat.completions, 'create').mockRejectedValue(genericError);
189
+
190
+ // Act
191
+ try {
192
+ await instance.chat({
193
+ messages: [{ content: 'Hello', role: 'user' }],
194
+ model: 'solar-1-mini-chat',
195
+ temperature: 0,
196
+ });
197
+ } catch (e) {
198
+ expect(e).toEqual({
199
+ endpoint: defaultBaseURL,
200
+ errorType: 'AgentRuntimeError',
201
+ provider,
202
+ error: {
203
+ name: genericError.name,
204
+ cause: genericError.cause,
205
+ message: genericError.message,
206
+ stack: genericError.stack,
207
+ },
208
+ });
209
+ }
210
+ });
211
+ });
212
+
213
+ describe('DEBUG', () => {
214
+ it('should call debugStream and return StreamingTextResponse when DEBUG_UPSTAGE_CHAT_COMPLETION is 1', async () => {
215
+ // Arrange
216
+ const mockProdStream = new ReadableStream() as any; // 模拟的 prod 流
217
+ const mockDebugStream = new ReadableStream({
218
+ start(controller) {
219
+ controller.enqueue('Debug stream content');
220
+ controller.close();
221
+ },
222
+ }) as any;
223
+ mockDebugStream.toReadableStream = () => mockDebugStream; // 添加 toReadableStream 方法
224
+
225
+ // 模拟 chat.completions.create 返回值,包括模拟的 tee 方法
226
+ (instance['client'].chat.completions.create as Mock).mockResolvedValue({
227
+ tee: () => [mockProdStream, { toReadableStream: () => mockDebugStream }],
228
+ });
229
+
230
+ // 保存原始环境变量值
231
+ const originalDebugValue = process.env.DEBUG_UPSTAGE_CHAT_COMPLETION;
232
+
233
+ // 模拟环境变量
234
+ process.env.DEBUG_UPSTAGE_CHAT_COMPLETION = '1';
235
+ vi.spyOn(debugStreamModule, 'debugStream').mockImplementation(() => Promise.resolve());
236
+
237
+ // 执行测试
238
+ // 运行你的测试函数,确保它会在条件满足时调用 debugStream
239
+ // 假设的测试函数调用,你可能需要根据实际情况调整
240
+ await instance.chat({
241
+ messages: [{ content: 'Hello', role: 'user' }],
242
+ model: 'solar-1-mini-chat',
243
+ stream: true,
244
+ temperature: 0,
245
+ });
246
+
247
+ // 验证 debugStream 被调用
248
+ expect(debugStreamModule.debugStream).toHaveBeenCalled();
249
+
250
+ // 恢复原始环境变量值
251
+ process.env.DEBUG_UPSTAGE_CHAT_COMPLETION = originalDebugValue;
252
+ });
253
+ });
254
+ });
255
+ });
@@ -0,0 +1,10 @@
1
+ import { ModelProvider } from '../types';
2
+ import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory';
3
+
4
+ export const LobeUpstageAI = LobeOpenAICompatibleFactory({
5
+ baseURL: 'https://api.upstage.ai/v1/solar',
6
+ debug: {
7
+ chatCompletion: () => process.env.DEBUG_UPSTAGE_CHAT_COMPLETION === '1',
8
+ },
9
+ provider: ModelProvider.Upstage,
10
+ });
@@ -57,7 +57,8 @@ export default {
57
57
  title: '当前部署模式不支持文件管理',
58
58
  },
59
59
  preview: {
60
- unsupportedFile: '此文件格式不支持在线预览',
60
+ downloadFile: '下载文件',
61
+ unsupportedFileAndContact: '此文件格式暂不支持在线预览,如有预览诉求,欢迎<1>反馈给我们</1>',
61
62
  },
62
63
  searchFilePlaceholder: '搜索文件',
63
64
  tab: {
@@ -47,6 +47,8 @@ export const getServerGlobalConfig = () => {
47
47
  ENABLED_SILICONCLOUD,
48
48
  SILICONCLOUD_MODEL_LIST,
49
49
 
50
+ ENABLED_UPSTAGE,
51
+
50
52
  ENABLED_AZURE_OPENAI,
51
53
  AZURE_MODEL_LIST,
52
54
 
@@ -138,6 +140,7 @@ export const getServerGlobalConfig = () => {
138
140
  modelString: TOGETHERAI_MODEL_LIST,
139
141
  }),
140
142
  },
143
+ upstage: { enabled: ENABLED_UPSTAGE },
141
144
  zeroone: { enabled: ENABLED_ZEROONE },
142
145
  zhipu: {
143
146
  enabled: ENABLED_ZHIPU,
@@ -39,6 +39,7 @@ export interface UserKeyVaults {
39
39
  stepfun?: OpenAICompatibleKeyVault;
40
40
  taichu?: OpenAICompatibleKeyVault;
41
41
  togetherai?: OpenAICompatibleKeyVault;
42
+ upstage?: OpenAICompatibleKeyVault;
42
43
  zeroone?: OpenAICompatibleKeyVault;
43
44
  zhipu?: OpenAICompatibleKeyVault;
44
45
  }