@lobehub/chat 1.134.6 → 1.134.7

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,39 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 1.134.7](https://github.com/lobehub/lobe-chat/compare/v1.134.6...v1.134.7)
6
+
7
+ <sup>Released on **2025-10-06**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **security**: Sanitize Azure provider error responses to prevent API key exposure.
12
+
13
+ #### 💄 Styles
14
+
15
+ - **misc**: Update i18n.
16
+
17
+ <br/>
18
+
19
+ <details>
20
+ <summary><kbd>Improvements and Fixes</kbd></summary>
21
+
22
+ #### What's fixed
23
+
24
+ - **security**: Sanitize Azure provider error responses to prevent API key exposure, closes [#9583](https://github.com/lobehub/lobe-chat/issues/9583) ([af59bfe](https://github.com/lobehub/lobe-chat/commit/af59bfe))
25
+
26
+ #### Styles
27
+
28
+ - **misc**: Update i18n, closes [#9580](https://github.com/lobehub/lobe-chat/issues/9580) ([c0974ea](https://github.com/lobehub/lobe-chat/commit/c0974ea))
29
+
30
+ </details>
31
+
32
+ <div align="right">
33
+
34
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
35
+
36
+ </div>
37
+
5
38
  ### [Version 1.134.6](https://github.com/lobehub/lobe-chat/compare/v1.134.5...v1.134.6)
6
39
 
7
40
  <sup>Released on **2025-10-05**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,13 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "improvements": [
5
+ "Update i18n."
6
+ ]
7
+ },
8
+ "date": "2025-10-06",
9
+ "version": "1.134.7"
10
+ },
2
11
  {
3
12
  "children": {},
4
13
  "date": "2025-10-05",
@@ -42,7 +42,7 @@ export type ChatMessage = {
42
42
 
43
43
  ```ts
44
44
  // src/store/chatStore.ts
45
- import create from 'zustand';
45
+ import { create } from 'zustand';
46
46
 
47
47
  type ChatState = {
48
48
  messages: ChatMessage[];
@@ -4,37 +4,88 @@ The directory structure of LobeChat is as follows:
4
4
 
5
5
  ```bash
6
6
  src
7
- ├── app # Main logic and state management related code for the application
7
+ ├── app # Next.js App Router implementation with route groups and API routes
8
8
  ├── components # Reusable UI components
9
9
  ├── config # Application configuration files, including client-side and server-side environment variables
10
- ├── const # Used to define constants, such as action types, route names, etc.
11
10
  ├── features # Function modules related to business functions, such as agent settings, plugin development pop-ups, etc.
12
11
  ├── hooks # Custom utility hooks reused throughout the application
13
12
  ├── layout # Application layout components, such as navigation bars, sidebars, etc.
13
+ ├── libs # Third-party integrations (analytics, OIDC, etc.)
14
14
  ├── locales # Internationalization language files
15
+ ├── server # Server-side modules and services
15
16
  ├── services # Encapsulated backend service interfaces, such as HTTP requests
16
17
  ├── store # Zustand store for state management
18
+ ├── styles # Global styles and CSS-in-JS configurations
17
19
  ├── types # TypeScript type definition files
18
20
  └── utils # Common utility functions
19
21
  ```
20
22
 
21
23
  ## app
22
24
 
23
- In the `app` folder, we organize each route page according to the app router's [Route Groups](https://nextjs.org/docs/app/building-your-application/routing/route-groups) to separately handle the implementation of desktop and mobile code. Taking the file structure of the `welcome` page as an example:
25
+ The `app` directory follows Next.js 13+ App Router conventions with a sophisticated architecture using [Route Groups](https://nextjs.org/docs/app/building-your-application/routing/route-groups) to organize backend services, platform variants, and application routes:
24
26
 
25
27
  ```bash
26
- welcome
27
- ├── (desktop) # Desktop implementation
28
- │ ├── features # Desktop-specific features
29
- │ ├── index.tsx # Main entry file for desktop
30
- │ └── layout.desktop.tsx # Desktop layout component
31
- ├── (mobile) # Mobile implementation
32
- │ ├── features # Mobile-specific features
33
- │ ├── index.tsx # Main entry file for mobile
34
- └── layout.mobile.tsx # Mobile layout component
35
- ├── features # This folder contains features code shared by both desktop and mobile, such as the Banner component
36
- └── Banner
37
- └── page.tsx # This is the main entry file for the page, used to load desktop or mobile code based on the device type
28
+ app
29
+ ├── (backend)/ # Backend API routes and services
30
+ │ ├── api/ # REST API endpoints
31
+ ├── auth/ # Authentication routes
32
+ └── webhooks/ # Webhook handlers
33
+ ├── middleware/ # Request middleware
34
+ │ ├── oidc/ # OpenID Connect routes
35
+ │ ├── trpc/ # tRPC API endpoints
36
+ │ ├── async/ # Async tRPC routes
37
+ │ │ ├── desktop/ # Desktop-specific tRPC routes
38
+ │ ├── edge/ # Edge runtime tRPC routes
39
+ │ │ ├── lambda/ # Lambda tRPC routes
40
+ │ │ └── tools/ # Tools tRPC routes
41
+ │ └── webapi/ # Web API endpoints
42
+ │ ├── chat/ # Chat-related APIs
43
+ │ ├── models/ # Model management APIs
44
+ │ ├── tts/ # Text-to-speech APIs
45
+ │ └── ...
46
+ ├── [variants]/ # Platform and device variants
47
+ │ ├── (auth)/ # Authentication pages
48
+ │ │ ├── login/
49
+ │ │ ├── signup/
50
+ │ │ └── next-auth/
51
+ │ ├── (main)/ # Main application routes
52
+ │ │ ├── (mobile)/ # Mobile-specific routes
53
+ │ │ │ └── me/ # Mobile profile pages
54
+ │ │ ├── _layout/ # Layout components
55
+ │ │ ├── chat/ # Chat interface
56
+ │ │ ├── discover/ # Discovery pages
57
+ │ │ ├── files/ # File management
58
+ │ │ ├── image/ # Image generation
59
+ │ │ ├── profile/ # User profile
60
+ │ │ ├── repos/ # Repository management
61
+ │ │ └── settings/ # Application settings
62
+ │ └── @modal/ # Parallel modal routes
63
+ │ ├── (.)changelog/
64
+ │ └── _layout/
65
+ ├── desktop/ # Desktop-specific routes
66
+ │ └── devtools/
67
+ ├── manifest.ts # PWA manifest
68
+ ├── robots.tsx # Robots.txt generation
69
+ ├── sitemap.tsx # Sitemap generation
70
+ └── sw.ts # Service worker
38
71
  ```
39
72
 
40
- In this way, we can clearly distinguish and manage desktop and mobile code, while also easily reusing code required on both devices, thereby improving development efficiency and maintaining code cleanliness and maintainability.
73
+ ### Architecture Explanation
74
+
75
+ **Route Groups:**
76
+ - `(backend)` - Contains all server-side API routes, middleware, and backend services
77
+ - `[variants]` - Dynamic route group handling different platform variants and main application pages
78
+ - `@modal` - Parallel routes for modal dialogs using Next.js parallel routing
79
+
80
+ **Platform Organization:**
81
+ - The architecture supports multiple platforms (web, desktop, mobile) through route organization
82
+ - Desktop-specific routes are in the `desktop/` directory
83
+ - Mobile-specific routes are organized under `(main)/(mobile)/`
84
+ - Shared layouts and components are in `_layout/` directories
85
+
86
+ **API Architecture:**
87
+ - REST APIs in `(backend)/api/` and `(backend)/webapi/`
88
+ - tRPC endpoints organized by runtime environment (edge, lambda, async, desktop)
89
+ - Authentication and OIDC handling in dedicated route groups
90
+
91
+ This architecture provides clear separation of concerns while maintaining flexibility for different deployment targets and runtime environments.
@@ -4,37 +4,88 @@ LobeChat 的文件夹目录架构如下:
4
4
 
5
5
  ```bash
6
6
  src
7
- ├── app # 应用主要逻辑和状态管理相关的代码
7
+ ├── app # Next.js App Router 实现,包含路由组和 API 路由
8
8
  ├── components # 可复用的 UI 组件
9
9
  ├── config # 应用的配置文件,包含客户端环境变量与服务端环境变量
10
- ├── const # 用于定义常量,如 action 类型、路由名等
11
10
  ├── features # 与业务功能相关的功能模块,如 Agent 设置、插件开发弹窗等
12
11
  ├── hooks # 全应用复用自定义的工具 Hooks
13
12
  ├── layout # 应用的布局组件,如导航栏、侧边栏等
13
+ ├── libs # 第三方集成(分析、OIDC 等)
14
14
  ├── locales # 国际化的语言文件
15
+ ├── server # 服务端模块和服务
15
16
  ├── services # 封装的后端服务接口,如 HTTP 请求
16
17
  ├── store # 用于状态管理的 zustand store
18
+ ├── styles # 全局样式和 CSS-in-JS 配置
17
19
  ├── types # TypeScript 的类型定义文件
18
20
  └── utils # 通用的工具函数
19
21
  ```
20
22
 
21
23
  ## app
22
24
 
23
- `app` 文件夹中,我们将每个路由页面按照 app router [Route Groups](https://nextjs.org/docs/app/building-your-application/routing/route-groups) 进行组织,以此来分别处理桌面端和移动端的代码实现。以 `welcome` 页面的文件结构为例:
25
+ `app` 目录遵循 Next.js 13+ App Router 约定,采用复杂的架构,使用 [路由组](https://nextjs.org/docs/app/building-your-application/routing/route-groups) 来组织后端服务、平台变体和应用路由:
24
26
 
25
27
  ```bash
26
- welcome
27
- ├── (desktop) # 桌面端实现
28
- │ ├── features # 桌面端特有的功能
29
- │ ├── index.tsx # 桌面端的主入口文件
30
- │ └── layout.desktop.tsx # 桌面端的布局组件
31
- ├── (mobile) # 移动端实现
32
- │ ├── features # 移动端特有的功能
33
- │ ├── index.tsx # 移动端的主入口文件
34
- └── layout.mobile.tsx # 移动端的布局组件
35
- ├── features # 此文件夹包含双端共享的特性代码,如 Banner 组件
36
- └── Banner
37
- └── page.tsx # 此为页面的主入口文件,用于根据设备类型选择加载桌面端或移动端的代码
28
+ app
29
+ ├── (backend)/ # 后端 API 路由和服务
30
+ │ ├── api/ # REST API 端点
31
+ ├── auth/ # 身份验证路由
32
+ └── webhooks/ # Webhook 处理器
33
+ ├── middleware/ # 请求中间件
34
+ │ ├── oidc/ # OpenID Connect 路由
35
+ │ ├── trpc/ # tRPC API 端点
36
+ │ ├── async/ # 异步 tRPC 路由
37
+ │ │ ├── desktop/ # 桌面端专用 tRPC 路由
38
+ │ ├── edge/ # Edge 运行时 tRPC 路由
39
+ │ │ ├── lambda/ # Lambda tRPC 路由
40
+ │ │ └── tools/ # 工具 tRPC 路由
41
+ │ └── webapi/ # Web API 端点
42
+ │ ├── chat/ # 聊天相关 API
43
+ │ ├── models/ # 模型管理 API
44
+ │ ├── tts/ # 文本转语音 API
45
+ │ └── ...
46
+ ├── [variants]/ # 平台和设备变体
47
+ │ ├── (auth)/ # 身份验证页面
48
+ │ │ ├── login/
49
+ │ │ ├── signup/
50
+ │ │ └── next-auth/
51
+ │ ├── (main)/ # 主应用路由
52
+ │ │ ├── (mobile)/ # 移动端专用路由
53
+ │ │ │ └── me/ # 移动端个人资料页面
54
+ │ │ ├── _layout/ # 布局组件
55
+ │ │ ├── chat/ # 聊天界面
56
+ │ │ ├── discover/ # 发现页面
57
+ │ │ ├── files/ # 文件管理
58
+ │ │ ├── image/ # 图像生成
59
+ │ │ ├── profile/ # 用户资料
60
+ │ │ ├── repos/ # 仓库管理
61
+ │ │ └── settings/ # 应用设置
62
+ │ └── @modal/ # 并行模态框路由
63
+ │ ├── (.)changelog/
64
+ │ └── _layout/
65
+ ├── desktop/ # 桌面端专用路由
66
+ │ └── devtools/
67
+ ├── manifest.ts # PWA 清单
68
+ ├── robots.tsx # Robots.txt 生成
69
+ ├── sitemap.tsx # 站点地图生成
70
+ └── sw.ts # Service Worker
38
71
  ```
39
72
 
40
- 通过这种方式,我们可以清晰地区分和管理桌面端和移动端的代码,同时也能方便地复用在两种设备上都需要的代码,从而提高开发效率并保持代码的整洁和可维护性。
73
+ ### 架构说明
74
+
75
+ **路由组:**
76
+ - `(backend)` - 包含所有服务端 API 路由、中间件和后端服务
77
+ - `[variants]` - 处理不同平台变体和主应用页面的动态路由组
78
+ - `@modal` - 使用 Next.js 并行路由的模态框对话框并行路由
79
+
80
+ **平台组织:**
81
+ - 架构通过路由组织支持多个平台(Web、桌面端、移动端)
82
+ - 桌面端专用路由位于 `desktop/` 目录中
83
+ - 移动端专用路由组织在 `(main)/(mobile)/` 下
84
+ - 共享布局和组件位于 `_layout/` 目录中
85
+
86
+ **API 架构:**
87
+ - `(backend)/api/` 和 `(backend)/webapi/` 中的 REST API
88
+ - 按运行时环境组织的 tRPC 端点(edge、lambda、async、desktop)
89
+ - 专用路由组中的身份验证和 OIDC 处理
90
+
91
+ 这种架构在保持不同部署目标和运行时环境灵活性的同时,提供了清晰的关注点分离。
@@ -294,6 +294,21 @@
294
294
  "title": "أقصى نافذة سياق",
295
295
  "unlimited": "غير محدود"
296
296
  },
297
+ "type": {
298
+ "extra": "أنواع النماذج المختلفة تمتلك سيناريوهات استخدام وقدرات مميزة",
299
+ "options": {
300
+ "chat": "محادثة",
301
+ "embedding": "تضمين",
302
+ "image": "توليد الصور",
303
+ "realtime": "محادثة فورية",
304
+ "stt": "تحويل الصوت إلى نص",
305
+ "text2music": "تحويل النص إلى موسيقى",
306
+ "text2video": "تحويل النص إلى فيديو",
307
+ "tts": "تحويل النص إلى كلام"
308
+ },
309
+ "placeholder": "يرجى اختيار نوع النموذج",
310
+ "title": "نوع النموذج"
311
+ },
297
312
  "vision": {
298
313
  "extra": "سيؤدي هذا التكوين إلى فتح إعدادات تحميل الصور في التطبيق، ما إذا كان يدعم التعرف يعتمد بالكامل على النموذج نفسه، يرجى اختبار قابلية استخدام التعرف البصري لهذا النموذج بنفسك",
299
314
  "title": "دعم التعرف البصري"
@@ -294,6 +294,21 @@
294
294
  "title": "Максимален контекстуален прозорец",
295
295
  "unlimited": "Без ограничения"
296
296
  },
297
+ "type": {
298
+ "extra": "Различните типове модели имат различни сценарии на използване и възможности",
299
+ "options": {
300
+ "chat": "Чат",
301
+ "embedding": "Векторизация",
302
+ "image": "Генериране на изображения",
303
+ "realtime": "Реално време чат",
304
+ "stt": "Гласов текст",
305
+ "text2music": "Текст към музика",
306
+ "text2video": "Текст към видео",
307
+ "tts": "Гласово синтезиране"
308
+ },
309
+ "placeholder": "Моля, изберете тип модел",
310
+ "title": "Тип модел"
311
+ },
297
312
  "vision": {
298
313
  "extra": "Тази конфигурация ще активира само конфигурацията за качване на изображения в приложението, дали поддържа разпознаване зависи изцяло от самия модел, моля, тествайте наличността на визуалната разпознаваемост на този модел.",
299
314
  "title": "Поддръжка на визуално разпознаване"
@@ -294,6 +294,21 @@
294
294
  "title": "Maximales Kontextfenster",
295
295
  "unlimited": "Unbegrenzt"
296
296
  },
297
+ "type": {
298
+ "extra": "Verschiedene Modelltypen haben unterschiedliche Anwendungsbereiche und Fähigkeiten",
299
+ "options": {
300
+ "chat": "Chat",
301
+ "embedding": "Vektorisierung",
302
+ "image": "Bildgenerierung",
303
+ "realtime": "Echtzeit-Chat",
304
+ "stt": "Spracherkennung",
305
+ "text2music": "Text zu Musik",
306
+ "text2video": "Text zu Video",
307
+ "tts": "Sprachsynthese"
308
+ },
309
+ "placeholder": "Bitte Modelltyp auswählen",
310
+ "title": "Modelltyp"
311
+ },
297
312
  "vision": {
298
313
  "extra": "Diese Konfiguration aktiviert nur die Bild-Upload-Funktionalität in der Anwendung. Ob die Erkennung unterstützt wird, hängt vollständig vom Modell selbst ab. Bitte testen Sie die Verwendbarkeit der visuellen Erkennungsfähigkeit des Modells selbst.",
299
314
  "title": "Visuelle Erkennung unterstützen"
@@ -294,6 +294,21 @@
294
294
  "title": "Maximum Context Window",
295
295
  "unlimited": "Unlimited"
296
296
  },
297
+ "type": {
298
+ "extra": "Different model types have distinct use cases and capabilities",
299
+ "options": {
300
+ "chat": "Chat",
301
+ "embedding": "Embedding",
302
+ "image": "Image Generation",
303
+ "realtime": "Real-time Chat",
304
+ "stt": "Speech-to-Text",
305
+ "text2music": "Text-to-Music",
306
+ "text2video": "Text-to-Video",
307
+ "tts": "Text-to-Speech"
308
+ },
309
+ "placeholder": "Please select a model type",
310
+ "title": "Model Type"
311
+ },
297
312
  "vision": {
298
313
  "extra": "This configuration will only enable image upload capabilities in the application. Whether recognition is supported depends entirely on the model itself. Please test the visual recognition capabilities of the model yourself.",
299
314
  "title": "Support Vision"
@@ -294,6 +294,21 @@
294
294
  "title": "Máximo de ventana de contexto",
295
295
  "unlimited": "Sin límite"
296
296
  },
297
+ "type": {
298
+ "extra": "Diferentes tipos de modelos tienen escenarios de uso y capacidades diferenciadas",
299
+ "options": {
300
+ "chat": "Chat",
301
+ "embedding": "Vectorización",
302
+ "image": "Generación de imágenes",
303
+ "realtime": "Chat en tiempo real",
304
+ "stt": "Reconocimiento de voz a texto",
305
+ "text2music": "Texto a música",
306
+ "text2video": "Texto a video",
307
+ "tts": "Síntesis de voz"
308
+ },
309
+ "placeholder": "Por favor, seleccione el tipo de modelo",
310
+ "title": "Tipo de modelo"
311
+ },
297
312
  "vision": {
298
313
  "extra": "Esta configuración solo habilitará la configuración de carga de imágenes en la aplicación, si se admite el reconocimiento depende completamente del modelo en sí, prueba la disponibilidad de la capacidad de reconocimiento visual de este modelo.",
299
314
  "title": "Soporte para reconocimiento visual"
@@ -294,6 +294,21 @@
294
294
  "title": "حداکثر پنجره زمینه",
295
295
  "unlimited": "بدون محدودیت"
296
296
  },
297
+ "type": {
298
+ "extra": "انواع مختلف مدل‌ها دارای سناریوها و قابلیت‌های متفاوتی هستند",
299
+ "options": {
300
+ "chat": "گفتگو",
301
+ "embedding": "بردار‌سازی",
302
+ "image": "تولید تصویر",
303
+ "realtime": "گفتگوی زنده",
304
+ "stt": "تبدیل گفتار به متن",
305
+ "text2music": "تبدیل متن به موسیقی",
306
+ "text2video": "تبدیل متن به ویدئو",
307
+ "tts": "تبدیل متن به گفتار"
308
+ },
309
+ "placeholder": "لطفاً نوع مدل را انتخاب کنید",
310
+ "title": "نوع مدل"
311
+ },
297
312
  "vision": {
298
313
  "extra": "این پیکربندی تنها قابلیت بارگذاری تصویر در برنامه را فعال می‌کند، اینکه آیا شناسایی پشتیبانی می‌شود به خود مدل بستگی دارد، لطفاً قابلیت استفاده از شناسایی بصری این مدل را آزمایش کنید",
299
314
  "title": "پشتیبانی از شناسایی بصری"
@@ -294,6 +294,21 @@
294
294
  "title": "Fenêtre de contexte maximale",
295
295
  "unlimited": "Illimité"
296
296
  },
297
+ "type": {
298
+ "extra": "Différents types de modèles possèdent des scénarios d'utilisation et des capacités distincts",
299
+ "options": {
300
+ "chat": "Conversation",
301
+ "embedding": "Vectorisation",
302
+ "image": "Génération d'images",
303
+ "realtime": "Dialogue en temps réel",
304
+ "stt": "Reconnaissance vocale",
305
+ "text2music": "Texte en musique",
306
+ "text2video": "Texte en vidéo",
307
+ "tts": "Synthèse vocale"
308
+ },
309
+ "placeholder": "Veuillez sélectionner un type de modèle",
310
+ "title": "Type de modèle"
311
+ },
297
312
  "vision": {
298
313
  "extra": "Cette configuration n'activera que la configuration de téléchargement d'images dans l'application, la prise en charge de la reconnaissance dépend entièrement du modèle lui-même, veuillez tester la disponibilité des capacités de reconnaissance visuelle de ce modèle.",
299
314
  "title": "Reconnaissance visuelle prise en charge"
@@ -294,6 +294,21 @@
294
294
  "title": "Finestra di contesto massima",
295
295
  "unlimited": "Illimitato"
296
296
  },
297
+ "type": {
298
+ "extra": "Diversi tipi di modelli hanno scenari d'uso e capacità differenziate",
299
+ "options": {
300
+ "chat": "Conversazione",
301
+ "embedding": "Vettorializzazione",
302
+ "image": "Generazione di immagini",
303
+ "realtime": "Conversazione in tempo reale",
304
+ "stt": "Trascrizione vocale",
305
+ "text2music": "Testo in musica",
306
+ "text2video": "Testo in video",
307
+ "tts": "Sintesi vocale"
308
+ },
309
+ "placeholder": "Seleziona il tipo di modello",
310
+ "title": "Tipo di modello"
311
+ },
297
312
  "vision": {
298
313
  "extra": "Questa configurazione abiliterà solo la configurazione di caricamento immagini nell'app, la disponibilità di riconoscimento dipende interamente dal modello stesso, testare autonomamente la disponibilità di riconoscimento visivo di questo modello.",
299
314
  "title": "Supporto per riconoscimento visivo"
@@ -294,6 +294,21 @@
294
294
  "title": "最大コンテキストウィンドウ",
295
295
  "unlimited": "無制限"
296
296
  },
297
+ "type": {
298
+ "extra": "異なるモデルタイプは、それぞれ異なる使用シーンと能力を持っています",
299
+ "options": {
300
+ "chat": "チャット",
301
+ "embedding": "ベクトル化",
302
+ "image": "画像生成",
303
+ "realtime": "リアルタイムチャット",
304
+ "stt": "音声からテキストへ",
305
+ "text2music": "テキストから音楽へ",
306
+ "text2video": "テキストから動画へ",
307
+ "tts": "音声合成"
308
+ },
309
+ "placeholder": "モデルタイプを選択してください",
310
+ "title": "モデルタイプ"
311
+ },
297
312
  "vision": {
298
313
  "extra": "この設定はアプリ内の画像アップロード設定のみを有効にします。認識のサポートはモデル自体に依存しますので、そのモデルの視覚認識機能の可用性を自分でテストしてください",
299
314
  "title": "視覚認識をサポート"
@@ -294,6 +294,21 @@
294
294
  "title": "최대 컨텍스트 창",
295
295
  "unlimited": "제한 없음"
296
296
  },
297
+ "type": {
298
+ "extra": "다양한 모델 유형은 차별화된 사용 시나리오와 기능을 제공합니다",
299
+ "options": {
300
+ "chat": "대화",
301
+ "embedding": "벡터화",
302
+ "image": "이미지 생성",
303
+ "realtime": "실시간 대화",
304
+ "stt": "음성 인식",
305
+ "text2music": "텍스트에서 음악으로",
306
+ "text2video": "텍스트에서 비디오로",
307
+ "tts": "음성 합성"
308
+ },
309
+ "placeholder": "모델 유형을 선택하세요",
310
+ "title": "모델 유형"
311
+ },
297
312
  "vision": {
298
313
  "extra": "이 설정은 애플리케이션 내에서 이미지 업로드 기능만 활성화합니다. 인식 지원 여부는 모델 자체에 따라 다르므로, 해당 모델의 시각 인식 가능성을 스스로 테스트하세요.",
299
314
  "title": "시각 인식 지원"
@@ -294,6 +294,21 @@
294
294
  "title": "Maximale contextvenster",
295
295
  "unlimited": "Onbeperkt"
296
296
  },
297
+ "type": {
298
+ "extra": "Verschillende modeltypen hebben verschillende toepassingsscenario's en mogelijkheden",
299
+ "options": {
300
+ "chat": "Gesprek",
301
+ "embedding": "Vectorisatie",
302
+ "image": "Afbeeldingsgeneratie",
303
+ "realtime": "Realtime gesprek",
304
+ "stt": "Spraak naar tekst",
305
+ "text2music": "Tekst naar muziek",
306
+ "text2video": "Tekst naar video",
307
+ "tts": "Spraaksynthese"
308
+ },
309
+ "placeholder": "Selecteer een modeltype",
310
+ "title": "Modeltype"
311
+ },
297
312
  "vision": {
298
313
  "extra": "Deze configuratie zal alleen de afbeeldinguploadcapaciteit in de applicatie inschakelen, of herkenning wordt ondersteund hangt volledig af van het model zelf, test de beschikbaarheid van de visuele herkenningscapaciteit van dit model zelf.",
299
314
  "title": "Ondersteuning voor visuele herkenning"
@@ -294,6 +294,21 @@
294
294
  "title": "Maksymalne okno kontekstu",
295
295
  "unlimited": "Bez ograniczeń"
296
296
  },
297
+ "type": {
298
+ "extra": "Różne typy modeli mają różne scenariusze użycia i możliwości",
299
+ "options": {
300
+ "chat": "Czat",
301
+ "embedding": "Wektoryzacja",
302
+ "image": "Generowanie obrazów",
303
+ "realtime": "Czat w czasie rzeczywistym",
304
+ "stt": "Rozpoznawanie mowy",
305
+ "text2music": "Tekst na muzykę",
306
+ "text2video": "Tekst na wideo",
307
+ "tts": "Synteza mowy"
308
+ },
309
+ "placeholder": "Wybierz typ modelu",
310
+ "title": "Typ modelu"
311
+ },
297
312
  "vision": {
298
313
  "extra": "Ta konfiguracja włączy tylko możliwość przesyłania obrazów w aplikacji, czy model obsługuje rozpoznawanie zależy od samego modelu, proszę samodzielnie przetestować dostępność rozpoznawania wizualnego tego modelu.",
299
314
  "title": "Wsparcie dla rozpoznawania wizualnego"
@@ -294,6 +294,21 @@
294
294
  "title": "Janela de contexto máxima",
295
295
  "unlimited": "Ilimitado"
296
296
  },
297
+ "type": {
298
+ "extra": "Diferentes tipos de modelos possuem cenários de uso e capacidades diferenciadas",
299
+ "options": {
300
+ "chat": "Conversa",
301
+ "embedding": "Vetorização",
302
+ "image": "Geração de imagem",
303
+ "realtime": "Conversa em tempo real",
304
+ "stt": "Reconhecimento de voz para texto",
305
+ "text2music": "Texto para música",
306
+ "text2video": "Texto para vídeo",
307
+ "tts": "Síntese de voz"
308
+ },
309
+ "placeholder": "Por favor, selecione o tipo de modelo",
310
+ "title": "Tipo de modelo"
311
+ },
297
312
  "vision": {
298
313
  "extra": "Esta configuração apenas habilitará a configuração de upload de imagens no aplicativo, se o reconhecimento for suportado depende do modelo em si, teste a capacidade de reconhecimento visual desse modelo.",
299
314
  "title": "Suporte a Reconhecimento Visual"
@@ -294,6 +294,21 @@
294
294
  "title": "Максимальное окно контекста",
295
295
  "unlimited": "Без ограничений"
296
296
  },
297
+ "type": {
298
+ "extra": "Различные типы моделей имеют разные сценарии использования и возможности",
299
+ "options": {
300
+ "chat": "Диалог",
301
+ "embedding": "Векторизация",
302
+ "image": "Генерация изображений",
303
+ "realtime": "Реальное время",
304
+ "stt": "Распознавание речи",
305
+ "text2music": "Текст в музыку",
306
+ "text2video": "Текст в видео",
307
+ "tts": "Синтез речи"
308
+ },
309
+ "placeholder": "Пожалуйста, выберите тип модели",
310
+ "title": "Тип модели"
311
+ },
297
312
  "vision": {
298
313
  "extra": "Эта настройка только активирует возможность загрузки изображений в приложении, поддержка распознавания полностью зависит от самой модели, пожалуйста, протестируйте доступность визуального распознавания этой модели.",
299
314
  "title": "Поддержка визуального распознавания"
@@ -294,6 +294,21 @@
294
294
  "title": "Maksimum bağlam penceresi",
295
295
  "unlimited": "Sınırsız"
296
296
  },
297
+ "type": {
298
+ "extra": "Farklı model türleri, farklı kullanım senaryoları ve yeteneklere sahiptir",
299
+ "options": {
300
+ "chat": "Sohbet",
301
+ "embedding": "Vektörleştirme",
302
+ "image": "Görüntü oluşturma",
303
+ "realtime": "Gerçek zamanlı sohbet",
304
+ "stt": "Ses metne dönüştürme",
305
+ "text2music": "Metinden müziğe",
306
+ "text2video": "Metinden videoya",
307
+ "tts": "Ses sentezi"
308
+ },
309
+ "placeholder": "Lütfen model türünü seçin",
310
+ "title": "Model Türü"
311
+ },
297
312
  "vision": {
298
313
  "extra": "Bu yapılandırma yalnızca uygulamadaki resim yükleme yapılandırmasını açacaktır, tanıma desteği tamamen modele bağlıdır, lütfen bu modelin görsel tanıma yeteneğini test edin.",
299
314
  "title": "Görsel Tanımayı Destekle"
@@ -294,6 +294,21 @@
294
294
  "title": "Cửa sổ ngữ cảnh tối đa",
295
295
  "unlimited": "Không giới hạn"
296
296
  },
297
+ "type": {
298
+ "extra": "Các loại mô hình khác nhau có các kịch bản sử dụng và khả năng khác biệt",
299
+ "options": {
300
+ "chat": "Đối thoại",
301
+ "embedding": "Vector hóa",
302
+ "image": "Tạo hình ảnh",
303
+ "realtime": "Đối thoại thời gian thực",
304
+ "stt": "Chuyển giọng nói thành văn bản",
305
+ "text2music": "Chuyển văn bản thành nhạc",
306
+ "text2video": "Chuyển văn bản thành video",
307
+ "tts": "Tổng hợp giọng nói"
308
+ },
309
+ "placeholder": "Vui lòng chọn loại mô hình",
310
+ "title": "Loại mô hình"
311
+ },
297
312
  "vision": {
298
313
  "extra": "Cấu hình này chỉ mở khả năng tải lên hình ảnh trong ứng dụng, việc hỗ trợ nhận diện hoàn toàn phụ thuộc vào mô hình, xin hãy tự kiểm tra khả năng nhận diện hình ảnh của mô hình này.",
299
314
  "title": "Hỗ trợ nhận diện hình ảnh"
@@ -294,6 +294,21 @@
294
294
  "title": "最大上下文窗口",
295
295
  "unlimited": "无限制"
296
296
  },
297
+ "type": {
298
+ "extra": "不同模型类型拥有差异化的使用场景与能力",
299
+ "options": {
300
+ "chat": "对话",
301
+ "embedding": "向量化",
302
+ "image": "图片生成",
303
+ "realtime": "实时对话",
304
+ "stt": "语音转文本",
305
+ "text2music": "文本转音乐",
306
+ "text2video": "文本转视频",
307
+ "tts": "语音合成"
308
+ },
309
+ "placeholder": "请选择模型类型",
310
+ "title": "模型类型"
311
+ },
297
312
  "vision": {
298
313
  "extra": "此配置将仅开启应用中的图片上传配置,是否支持识别完全取决于模型本身,请自行测试该模型的视觉识别能力可用性",
299
314
  "title": "支持视觉识别"
@@ -294,6 +294,21 @@
294
294
  "title": "最大上下文窗口",
295
295
  "unlimited": "無限制"
296
296
  },
297
+ "type": {
298
+ "extra": "不同模型類型擁有差異化的使用場景與能力",
299
+ "options": {
300
+ "chat": "對話",
301
+ "embedding": "向量化",
302
+ "image": "圖片生成",
303
+ "realtime": "即時對話",
304
+ "stt": "語音轉文字",
305
+ "text2music": "文本轉音樂",
306
+ "text2video": "文本轉影片",
307
+ "tts": "語音合成"
308
+ },
309
+ "placeholder": "請選擇模型類型",
310
+ "title": "模型類型"
311
+ },
297
312
  "vision": {
298
313
  "extra": "此配置將僅開啟應用中的圖片上傳配置,是否支持識別完全取決於模型本身,請自行測試該模型的視覺識別能力可用性",
299
314
  "title": "支持視覺識別"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.134.6",
3
+ "version": "1.134.7",
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",
@@ -20,6 +20,7 @@ import { AgentRuntimeError } from '../../utils/createError';
20
20
  import { debugStream } from '../../utils/debugStream';
21
21
  import { convertImageUrlToFile, convertOpenAIMessages } from '../../utils/openaiHelpers';
22
22
  import { StreamingResponse } from '../../utils/response';
23
+ import { sanitizeError } from '../../utils/sanitizeError';
23
24
 
24
25
  const azureImageLogger = debug('lobe-image:azure');
25
26
  export class LobeAzureOpenAI implements LobeRuntimeAI {
@@ -253,9 +254,12 @@ export class LobeAzureOpenAI implements LobeRuntimeAI {
253
254
  ? AgentRuntimeErrorType.ProviderBizError
254
255
  : AgentRuntimeErrorType.AgentRuntimeError;
255
256
 
257
+ // Sanitize error to remove sensitive information like API keys from headers
258
+ const sanitizedError = sanitizeError(error);
259
+
256
260
  throw AgentRuntimeError.chat({
257
261
  endpoint: this.maskSensitiveUrl(this.baseURL),
258
- error,
262
+ error: sanitizedError,
259
263
  errorType,
260
264
  provider: ModelProvider.Azure,
261
265
  });
@@ -12,6 +12,7 @@ import { AgentRuntimeErrorType } from '../../types/error';
12
12
  import { AgentRuntimeError } from '../../utils/createError';
13
13
  import { debugStream } from '../../utils/debugStream';
14
14
  import { StreamingResponse } from '../../utils/response';
15
+ import { sanitizeError } from '../../utils/sanitizeError';
15
16
 
16
17
  interface AzureAIParams {
17
18
  apiKey?: string;
@@ -112,9 +113,12 @@ export class LobeAzureAI implements LobeRuntimeAI {
112
113
  ? AgentRuntimeErrorType.ProviderBizError
113
114
  : AgentRuntimeErrorType.AgentRuntimeError;
114
115
 
116
+ // Sanitize error to remove sensitive information like API keys from headers
117
+ const sanitizedError = sanitizeError(error);
118
+
115
119
  throw AgentRuntimeError.chat({
116
120
  endpoint: this.maskSensitiveUrl(this.baseURL),
117
- error,
121
+ error: sanitizedError,
118
122
  errorType,
119
123
  provider: ModelProvider.Azure,
120
124
  });
@@ -0,0 +1,109 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { sanitizeError } from './sanitizeError';
4
+
5
+ describe('sanitizeError', () => {
6
+ it('should remove sensitive request headers', () => {
7
+ const errorWithHeaders = {
8
+ message: 'API Error',
9
+ code: 401,
10
+ request: {
11
+ headers: {
12
+ authorization: 'Bearer sk-1234567890',
13
+ 'content-type': 'application/json',
14
+ 'ocp-apim-subscription-key': 'azure-key-123',
15
+ },
16
+ url: 'https://api.example.com',
17
+ },
18
+ };
19
+
20
+ const sanitized = sanitizeError(errorWithHeaders);
21
+
22
+ expect(sanitized).toEqual({
23
+ message: 'API Error',
24
+ code: 401,
25
+ });
26
+ expect(sanitized.request).toBeUndefined();
27
+ });
28
+
29
+ it('should remove sensitive fields at any level', () => {
30
+ const errorWithNestedSensitive = {
31
+ message: 'Error',
32
+ data: {
33
+ config: {
34
+ apikey: 'secret-key',
35
+ headers: {
36
+ authorization: 'Bearer token',
37
+ },
38
+ },
39
+ response: {
40
+ status: 401,
41
+ data: 'Unauthorized',
42
+ },
43
+ },
44
+ };
45
+
46
+ const sanitized = sanitizeError(errorWithNestedSensitive);
47
+
48
+ expect(sanitized).toEqual({
49
+ message: 'Error',
50
+ data: {
51
+ response: {
52
+ status: 401,
53
+ data: 'Unauthorized',
54
+ },
55
+ },
56
+ });
57
+ expect(sanitized.data.config).toBeUndefined();
58
+ });
59
+
60
+ it('should handle primitive values', () => {
61
+ expect(sanitizeError('string')).toBe('string');
62
+ expect(sanitizeError(123)).toBe(123);
63
+ expect(sanitizeError(true)).toBe(true);
64
+ expect(sanitizeError(null)).toBe(null);
65
+ expect(sanitizeError(undefined)).toBe(undefined);
66
+ });
67
+
68
+ it('should handle arrays', () => {
69
+ const errorArray = [
70
+ { message: 'Error 1', apikey: 'secret' },
71
+ { message: 'Error 2', status: 500 },
72
+ ];
73
+
74
+ const sanitized = sanitizeError(errorArray);
75
+
76
+ expect(sanitized).toEqual([{ message: 'Error 1' }, { message: 'Error 2', status: 500 }]);
77
+ });
78
+
79
+ it('should be case insensitive for sensitive field detection', () => {
80
+ const errorWithMixedCase = {
81
+ message: 'Error',
82
+ Authorization: 'Bearer token',
83
+ 'API-KEY': 'secret',
84
+ Headers: { token: 'secret' },
85
+ };
86
+
87
+ const sanitized = sanitizeError(errorWithMixedCase);
88
+
89
+ expect(sanitized).toEqual({
90
+ message: 'Error',
91
+ });
92
+ });
93
+
94
+ it('should preserve safe nested structures', () => {
95
+ const errorWithSafeNested = {
96
+ message: 'Error occurred',
97
+ status: 401,
98
+ details: {
99
+ code: 'UNAUTHORIZED',
100
+ timestamp: '2024-01-01T00:00:00Z',
101
+ path: '/api/chat',
102
+ },
103
+ };
104
+
105
+ const sanitized = sanitizeError(errorWithSafeNested);
106
+
107
+ expect(sanitized).toEqual(errorWithSafeNested);
108
+ });
109
+ });
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Sanitizes error objects by removing sensitive information that could expose API keys or other credentials.
3
+ * This is particularly important for errors from Azure/OpenAI SDKs that may include request headers.
4
+ */
5
+ export function sanitizeError(error: any): any {
6
+ if (!error || typeof error !== 'object') {
7
+ return error;
8
+ }
9
+
10
+ // Handle array of errors
11
+ if (Array.isArray(error)) {
12
+ return error.map(sanitizeError);
13
+ }
14
+
15
+ // Create a sanitized copy
16
+ const sanitized: any = {};
17
+
18
+ // List of sensitive fields that should be removed or masked
19
+ const sensitiveFields = [
20
+ 'request',
21
+ 'headers',
22
+ 'authorization',
23
+ 'apikey',
24
+ 'api-key',
25
+ 'ocp-apim-subscription-key',
26
+ 'x-api-key',
27
+ 'bearer',
28
+ 'token',
29
+ 'auth',
30
+ 'credential',
31
+ 'key',
32
+ 'secret',
33
+ 'password',
34
+ 'config',
35
+ 'options',
36
+ ];
37
+
38
+ // Copy safe fields and recursively sanitize nested objects
39
+ for (const key in error) {
40
+ if (error.hasOwnProperty(key)) {
41
+ const value = error[key];
42
+ const lowerKey = key.toLowerCase();
43
+
44
+ // Skip sensitive fields entirely
45
+ if (sensitiveFields.indexOf(lowerKey) !== -1) {
46
+ continue;
47
+ }
48
+
49
+ // Recursively sanitize nested objects
50
+ if (value && typeof value === 'object') {
51
+ sanitized[key] = sanitizeError(value);
52
+ } else {
53
+ sanitized[key] = value;
54
+ }
55
+ }
56
+ }
57
+
58
+ return sanitized;
59
+ }