@lobehub/chat 1.119.2 → 1.120.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 (56) hide show
  1. package/.vscode/settings.json +2 -3
  2. package/CHANGELOG.md +33 -0
  3. package/changelog/v1.json +12 -0
  4. package/package.json +1 -4
  5. package/packages/database/src/models/__tests__/generationBatch.test.ts +47 -1
  6. package/packages/database/src/models/generationBatch.ts +8 -1
  7. package/packages/model-bank/src/aiModels/aihubmix.ts +1 -1
  8. package/packages/model-bank/src/aiModels/google.ts +4 -4
  9. package/packages/model-bank/src/aiModels/openrouter.ts +2 -2
  10. package/packages/model-bank/src/aiModels/qwen.ts +3 -1
  11. package/packages/model-bank/src/aiModels/siliconcloud.ts +6 -0
  12. package/packages/model-bank/src/aiModels/vertexai.ts +2 -2
  13. package/packages/model-runtime/src/google/createImage.ts +52 -24
  14. package/packages/model-runtime/src/qwen/index.ts +1 -1
  15. package/packages/model-runtime/src/siliconcloud/index.ts +1 -1
  16. package/src/app/[variants]/(main)/(mobile)/me/settings/features/useCategory.tsx +2 -16
  17. package/src/app/[variants]/(main)/chat/@session/_layout/Desktop/SessionHeader.tsx +1 -3
  18. package/src/app/[variants]/(main)/chat/@session/_layout/Mobile/SessionHeader.tsx +1 -3
  19. package/src/app/[variants]/(main)/settings/hooks/useCategory.tsx +3 -21
  20. package/src/config/featureFlags/schema.test.ts +1 -2
  21. package/src/config/featureFlags/schema.ts +0 -6
  22. package/src/config/featureFlags/utils/parser.test.ts +7 -7
  23. package/src/database/_deprecated/core/index.ts +0 -1
  24. package/src/database/_deprecated/core/model.ts +4 -38
  25. package/src/database/_deprecated/models/message.ts +1 -1
  26. package/src/layout/GlobalProvider/StoreInitialization.tsx +0 -3
  27. package/src/store/serverConfig/selectors.test.ts +0 -1
  28. package/src/store/user/initialState.ts +1 -4
  29. package/src/store/user/selectors.ts +0 -1
  30. package/src/store/user/store.ts +1 -4
  31. package/docs/self-hosting/advanced/webrtc.mdx +0 -86
  32. package/docs/self-hosting/advanced/webrtc.zh-CN.mdx +0 -80
  33. package/src/app/[variants]/(main)/settings/sync/features/Alert.tsx +0 -53
  34. package/src/app/[variants]/(main)/settings/sync/features/DeviceInfo/Card.tsx +0 -42
  35. package/src/app/[variants]/(main)/settings/sync/features/DeviceInfo/DeviceName.tsx +0 -62
  36. package/src/app/[variants]/(main)/settings/sync/features/DeviceInfo/SystemIcon.tsx +0 -31
  37. package/src/app/[variants]/(main)/settings/sync/features/DeviceInfo/index.tsx +0 -103
  38. package/src/app/[variants]/(main)/settings/sync/features/WebRTC/ChannelNameInput.tsx +0 -45
  39. package/src/app/[variants]/(main)/settings/sync/features/WebRTC/SyncSwitch/index.css +0 -238
  40. package/src/app/[variants]/(main)/settings/sync/features/WebRTC/SyncSwitch/index.tsx +0 -79
  41. package/src/app/[variants]/(main)/settings/sync/features/WebRTC/generateRandomRoomName.ts +0 -4
  42. package/src/app/[variants]/(main)/settings/sync/features/WebRTC/index.tsx +0 -103
  43. package/src/app/[variants]/(main)/settings/sync/index.tsx +0 -17
  44. package/src/app/[variants]/(main)/settings/sync/page.tsx +0 -29
  45. package/src/database/_deprecated/core/sync.ts +0 -321
  46. package/src/features/SyncStatusInspector/DisableSync.tsx +0 -79
  47. package/src/features/SyncStatusInspector/EnableSync.tsx +0 -132
  48. package/src/features/SyncStatusInspector/EnableTag.tsx +0 -66
  49. package/src/features/SyncStatusInspector/index.tsx +0 -27
  50. package/src/hooks/useSyncData.ts +0 -50
  51. package/src/services/__tests__/sync.test.ts +0 -56
  52. package/src/services/sync.ts +0 -19
  53. package/src/store/user/slices/sync/action.test.ts +0 -164
  54. package/src/store/user/slices/sync/action.ts +0 -101
  55. package/src/store/user/slices/sync/initialState.ts +0 -13
  56. package/src/store/user/slices/sync/selectors.ts +0 -20
@@ -34,7 +34,7 @@
34
34
  // make stylelint work with tsx antd-style css template string
35
35
  "typescriptreact"
36
36
  ],
37
- "vitest.maximumConfigs": 10,
37
+ "vitest.maximumConfigs": 20,
38
38
  "workbench.editor.customLabels.patterns": {
39
39
  "**/app/**/[[]*[]]/[[]*[]]/page.tsx": "${dirname(2)}/${dirname(1)}/${dirname} • page component",
40
40
  "**/app/**/[[]*[]]/page.tsx": "${dirname(1)}/${dirname} • page component",
@@ -81,8 +81,7 @@
81
81
  "**/src/store/*/slices/*/reducer.ts": "${dirname(2)}/${dirname} • reducer",
82
82
 
83
83
  "**/src/config/modelProviders/*.ts": "${filename} • provider",
84
- "**/src/config/aiModels/*.ts": "${filename} • model",
85
- "**/src/config/paramsSchemas/*/*.json": "${dirname(1)}/${filename} • params",
84
+ "**/packages/model-bank/src/aiModels/aiModels/*.ts": "${filename} • model",
86
85
  "**/packages/model-runtime/src/*/index.ts": "${dirname} • runtime",
87
86
 
88
87
  "**/src/server/services/*/index.ts": "${dirname} • server/service",
package/CHANGELOG.md CHANGED
@@ -2,6 +2,39 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 1.120.0](https://github.com/lobehub/lobe-chat/compare/v1.119.2...v1.120.0)
6
+
7
+ <sup>Released on **2025-08-30**</sup>
8
+
9
+ #### ♻ Code Refactoring
10
+
11
+ - **misc**: Remove webrtc sync feature flag.
12
+
13
+ #### ✨ Features
14
+
15
+ - **misc**: Rename Gemini 2.5 flash image to Nano Banana.
16
+
17
+ <br/>
18
+
19
+ <details>
20
+ <summary><kbd>Improvements and Fixes</kbd></summary>
21
+
22
+ #### Code refactoring
23
+
24
+ - **misc**: Remove webrtc sync feature flag, closes [#9002](https://github.com/lobehub/lobe-chat/issues/9002) ([0924d98](https://github.com/lobehub/lobe-chat/commit/0924d98))
25
+
26
+ #### What's improved
27
+
28
+ - **misc**: Rename Gemini 2.5 flash image to Nano Banana, closes [#9004](https://github.com/lobehub/lobe-chat/issues/9004) ([dac5a6f](https://github.com/lobehub/lobe-chat/commit/dac5a6f))
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.119.2](https://github.com/lobehub/lobe-chat/compare/v1.119.1...v1.119.2)
6
39
 
7
40
  <sup>Released on **2025-08-30**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,16 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "improvements": [
5
+ "Remove webrtc sync feature flag."
6
+ ],
7
+ "features": [
8
+ "Rename Gemini 2.5 flash image to Nano Banana."
9
+ ]
10
+ },
11
+ "date": "2025-08-30",
12
+ "version": "1.120.0"
13
+ },
2
14
  {
3
15
  "children": {},
4
16
  "date": "2025-08-30",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.119.2",
3
+ "version": "1.120.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",
@@ -275,10 +275,7 @@
275
275
  "use-merge-value": "^1.2.0",
276
276
  "uuid": "^11.1.0",
277
277
  "ws": "^8.18.3",
278
- "y-protocols": "^1.0.6",
279
- "y-webrtc": "^10.3.0",
280
278
  "yaml": "^2.8.1",
281
- "yjs": "^13.6.27",
282
279
  "zod": "^3.25.76",
283
280
  "zustand": "5.0.4",
284
281
  "zustand-utils": "^2.1.0"
@@ -2,7 +2,7 @@
2
2
  import { eq } from 'drizzle-orm';
3
3
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
4
4
 
5
- import { LobeChatDatabase } from '../../type';import { AsyncTaskStatus } from '@/types/asyncTask';
5
+ import { AsyncTaskStatus } from '@/types/asyncTask';
6
6
  import { GenerationConfig } from '@/types/generation';
7
7
 
8
8
  import {
@@ -12,6 +12,7 @@ import {
12
12
  generations,
13
13
  users,
14
14
  } from '../../schemas';
15
+ import { LobeChatDatabase } from '../../type';
15
16
  import { GenerationBatchModel } from '../generationBatch';
16
17
  import { getTestDB } from './_util';
17
18
 
@@ -367,6 +368,51 @@ describe('GenerationBatchModel', () => {
367
368
  });
368
369
  });
369
370
 
371
+ it('should transform single config imageUrl through FileService', async () => {
372
+ const [createdBatch] = await serverDB
373
+ .insert(generationBatches)
374
+ .values({
375
+ ...testBatch,
376
+ userId,
377
+ config: { imageUrl: 'single-image.jpg', prompt: 'test prompt' },
378
+ })
379
+ .returning();
380
+
381
+ const results = await generationBatchModel.queryGenerationBatchesByTopicIdWithGenerations(
382
+ testTopic.id,
383
+ );
384
+
385
+ expect(results[0].config).toEqual({
386
+ imageUrl: 'https://example.com/single-image.jpg',
387
+ prompt: 'test prompt',
388
+ });
389
+ });
390
+
391
+ it('should transform both imageUrl and imageUrls when both are present', async () => {
392
+ const [createdBatch] = await serverDB
393
+ .insert(generationBatches)
394
+ .values({
395
+ ...testBatch,
396
+ userId,
397
+ config: {
398
+ imageUrl: 'single-image.jpg',
399
+ imageUrls: ['url1.jpg', 'url2.jpg'],
400
+ prompt: 'test prompt',
401
+ },
402
+ })
403
+ .returning();
404
+
405
+ const results = await generationBatchModel.queryGenerationBatchesByTopicIdWithGenerations(
406
+ testTopic.id,
407
+ );
408
+
409
+ expect(results[0].config).toEqual({
410
+ imageUrl: 'https://example.com/single-image.jpg',
411
+ imageUrls: ['https://example.com/url1.jpg', 'https://example.com/url2.jpg'],
412
+ prompt: 'test prompt',
413
+ });
414
+ });
415
+
370
416
  it('should handle config without imageUrls', async () => {
371
417
  const [createdBatch] = await serverDB
372
418
  .insert(generationBatches)
@@ -1,7 +1,6 @@
1
1
  import debug from 'debug';
2
2
  import { and, eq } from 'drizzle-orm';
3
3
 
4
- import { LobeChatDatabase } from '../type';
5
4
  import { FileService } from '@/server/services/file';
6
5
  import { Generation, GenerationAsset, GenerationBatch, GenerationConfig } from '@/types/generation';
7
6
 
@@ -11,6 +10,7 @@ import {
11
10
  NewGenerationBatch,
12
11
  generationBatches,
13
12
  } from '../schemas/generation';
13
+ import { LobeChatDatabase } from '../type';
14
14
  import { GenerationModel } from './generation';
15
15
 
16
16
  const log = debug('lobe-image:generation-batch-model');
@@ -121,6 +121,13 @@ export class GenerationBatchModel {
121
121
  // Transform config
122
122
  (async () => {
123
123
  const config = batch.config as GenerationConfig;
124
+
125
+ // Handle single imageUrl
126
+ if (config.imageUrl) {
127
+ config.imageUrl = await this.fileService.getFullFileUrl(config.imageUrl);
128
+ }
129
+
130
+ // Handle imageUrls array
124
131
  if (Array.isArray(config.imageUrls)) {
125
132
  config.imageUrls = await Promise.all(
126
133
  config.imageUrls.map((url) => this.fileService.getFullFileUrl(url)),
@@ -700,7 +700,7 @@ const aihubmixModels: AIChatModelCard[] = [
700
700
  },
701
701
  contextWindowTokens: 32_768 + 8192,
702
702
  description: 'Gemini 2.5 Flash 实验模型,支持图像生成',
703
- displayName: 'Gemini 2.5 Flash Image Preview',
703
+ displayName: 'Nano Banana',
704
704
  id: 'gemini-2.5-flash-image-preview',
705
705
  maxOutput: 8192,
706
706
  pricing: {
@@ -196,8 +196,8 @@ const googleChatModels: AIChatModelCard[] = [
196
196
  },
197
197
  contextWindowTokens: 32_768 + 8192,
198
198
  description:
199
- 'Gemini 2.5 Flash Image Preview 是 Google 最新、最快、最高效的原生多模态模型,它允许您通过对话生成和编辑图像。',
200
- displayName: 'Gemini 2.5 Flash Image Preview',
199
+ 'Nano Banana 是 Google 最新、最快、最高效的原生多模态模型,它允许您通过对话生成和编辑图像。',
200
+ displayName: 'Nano Banana',
201
201
  enabled: true,
202
202
  id: 'gemini-2.5-flash-image-preview',
203
203
  maxOutput: 8192,
@@ -610,12 +610,12 @@ const imagenBaseParameters: ModelParamsSchema = {
610
610
  /* eslint-disable sort-keys-fix/sort-keys-fix */
611
611
  const googleImageModels: AIImageModelCard[] = [
612
612
  {
613
- displayName: 'Gemini 2.5 Flash Image Preview',
613
+ displayName: 'Nano Banana',
614
614
  id: 'gemini-2.5-flash-image-preview:image',
615
615
  enabled: true,
616
616
  type: 'image',
617
617
  description:
618
- 'Gemini 2.5 Flash Image Preview 是 Google 最新、最快、最高效的原生多模态模型,它允许您通过对话生成和编辑图像。',
618
+ 'Nano Banana 是 Google 最新、最快、最高效的原生多模态模型,它允许您通过对话生成和编辑图像。',
619
619
  releasedAt: '2025-08-26',
620
620
  parameters: CHAT_MODEL_IMAGE_GENERATION_PARAMS,
621
621
  pricing: {
@@ -37,7 +37,7 @@ const openrouterChatModels: AIChatModelCard[] = [
37
37
  },
38
38
  contextWindowTokens: 32_768 + 8192,
39
39
  description: 'Gemini 2.5 Flash 实验模型,支持图像生成',
40
- displayName: 'Gemini 2.5 Flash Image Preview',
40
+ displayName: 'Nano Banana',
41
41
  id: 'google/gemini-2.5-flash-image-preview',
42
42
  maxOutput: 8192,
43
43
  pricing: {
@@ -57,7 +57,7 @@ const openrouterChatModels: AIChatModelCard[] = [
57
57
  },
58
58
  contextWindowTokens: 32_768 + 8192,
59
59
  description: 'Gemini 2.5 Flash 实验模型,支持图像生成',
60
- displayName: 'Gemini 2.5 Flash Image Preview (free)',
60
+ displayName: 'Nano Banana (free)',
61
61
  id: 'google/gemini-2.5-flash-image-preview:free',
62
62
  maxOutput: 8192,
63
63
  releasedAt: '2025-08-26',
@@ -5,7 +5,6 @@ import { AIChatModelCard, AIImageModelCard } from '../types/aiModel';
5
5
  const qwenChatModels: AIChatModelCard[] = [
6
6
  {
7
7
  abilities: {
8
- functionCall: true,
9
8
  reasoning: true,
10
9
  },
11
10
  contextWindowTokens: 131_072,
@@ -20,6 +19,9 @@ const qwenChatModels: AIChatModelCard[] = [
20
19
  { name: 'textOutput', rate: 12, strategy: 'fixed', unit: 'millionTokens' },
21
20
  ],
22
21
  },
22
+ settings: {
23
+ extendParams: ['enableReasoning', 'reasoningBudgetToken'],
24
+ },
23
25
  type: 'chat',
24
26
  },
25
27
  {
@@ -20,6 +20,9 @@ const siliconcloudChatModels: AIChatModelCard[] = [
20
20
  { name: 'textOutput', rate: 12, strategy: 'fixed', unit: 'millionTokens' },
21
21
  ],
22
22
  },
23
+ settings: {
24
+ extendParams: ['enableReasoning', 'reasoningBudgetToken'],
25
+ },
23
26
  type: 'chat',
24
27
  },
25
28
  {
@@ -39,6 +42,9 @@ const siliconcloudChatModels: AIChatModelCard[] = [
39
42
  { name: 'textOutput', rate: 12, strategy: 'fixed', unit: 'millionTokens' },
40
43
  ],
41
44
  },
45
+ settings: {
46
+ extendParams: ['enableReasoning', 'reasoningBudgetToken'],
47
+ },
42
48
  type: 'chat',
43
49
  },
44
50
  {
@@ -126,8 +126,8 @@ const vertexaiChatModels: AIChatModelCard[] = [
126
126
  },
127
127
  contextWindowTokens: 32_768 + 8192,
128
128
  description:
129
- 'Gemini 2.5 Flash Image Preview 是 Google 最新、最快、最高效的原生多模态模型,它允许您通过对话生成和编辑图像。',
130
- displayName: 'Gemini 2.5 Flash Image Preview',
129
+ 'Nano Banana 是 Google 最新、最快、最高效的原生多模态模型,它允许您通过对话生成和编辑图像。',
130
+ displayName: 'Nano Banana',
131
131
  enabled: true,
132
132
  id: 'gemini-2.5-flash-image-preview',
133
133
  maxOutput: 8192,
@@ -6,6 +6,40 @@ import { parseGoogleErrorMessage } from '../utils/googleErrorParser';
6
6
  import { imageUrlToBase64 } from '../utils/imageToBase64';
7
7
  import { parseDataUri } from '../utils/uriParser';
8
8
 
9
+ // Maximum number of images allowed for processing
10
+ const MAX_IMAGE_COUNT = 10;
11
+
12
+ /**
13
+ * Process a single image URL and convert it to Google AI Part format
14
+ */
15
+ async function processImageForParts(imageUrl: string): Promise<Part> {
16
+ const { mimeType, base64, type } = parseDataUri(imageUrl);
17
+
18
+ if (type === 'base64') {
19
+ if (!base64) {
20
+ throw new TypeError("Image URL doesn't contain base64 data");
21
+ }
22
+
23
+ return {
24
+ inlineData: {
25
+ data: base64,
26
+ mimeType: mimeType || 'image/png',
27
+ },
28
+ };
29
+ } else if (type === 'url') {
30
+ const { base64: urlBase64, mimeType: urlMimeType } = await imageUrlToBase64(imageUrl);
31
+
32
+ return {
33
+ inlineData: {
34
+ data: urlBase64,
35
+ mimeType: urlMimeType,
36
+ },
37
+ };
38
+ } else {
39
+ throw new TypeError(`currently we don't support image url: ${imageUrl}`);
40
+ }
41
+ }
42
+
9
43
  /**
10
44
  * Extract image data from generateContent response
11
45
  */
@@ -71,36 +105,30 @@ async function generateImageByChatModel(
71
105
  const { model, params } = payload;
72
106
  const actualModel = model.replace(':image', '');
73
107
 
108
+ // Check for conflicting image parameters
109
+ if (params.imageUrl && params.imageUrls && params.imageUrls.length > 0) {
110
+ throw new TypeError('Cannot provide both imageUrl and imageUrls parameters simultaneously');
111
+ }
112
+
74
113
  // Build content parts
75
114
  const parts: Part[] = [{ text: params.prompt }];
76
115
 
77
116
  // Add image for editing if provided
78
117
  if (params.imageUrl && params.imageUrl !== null) {
79
- const { mimeType, base64, type } = parseDataUri(params.imageUrl);
80
-
81
- if (type === 'base64') {
82
- if (!base64) {
83
- throw new TypeError("Image URL doesn't contain base64 data");
84
- }
85
-
86
- parts.push({
87
- inlineData: {
88
- data: base64,
89
- mimeType: mimeType || 'image/png',
90
- },
91
- });
92
- } else if (type === 'url') {
93
- const { base64: urlBase64, mimeType: urlMimeType } = await imageUrlToBase64(params.imageUrl);
94
-
95
- parts.push({
96
- inlineData: {
97
- data: urlBase64,
98
- mimeType: urlMimeType,
99
- },
100
- });
101
- } else {
102
- throw new TypeError(`currently we don't support image url: ${params.imageUrl}`);
118
+ const imagePart = await processImageForParts(params.imageUrl);
119
+ parts.push(imagePart);
120
+ }
121
+
122
+ // Add multiple images for editing if provided
123
+ if (params.imageUrls && Array.isArray(params.imageUrls) && params.imageUrls.length > 0) {
124
+ if (params.imageUrls.length > MAX_IMAGE_COUNT) {
125
+ throw new TypeError(`Too many images provided. Maximum ${MAX_IMAGE_COUNT} images allowed`);
103
126
  }
127
+
128
+ const imageParts = await Promise.all(
129
+ params.imageUrls.map((imageUrl) => processImageForParts(imageUrl)),
130
+ );
131
+ parts.push(...imageParts);
104
132
  }
105
133
 
106
134
  const contents: Content[] = [
@@ -35,7 +35,7 @@ export const LobeQwenAI = createOpenAICompatibleRuntime({
35
35
  thinking_budget:
36
36
  thinking?.budget_tokens === 0 ? 0 : thinking?.budget_tokens || undefined,
37
37
  }
38
- : ['qwen3', 'qwen-turbo', 'qwen-plus'].some((keyword) =>
38
+ : ['qwen3', 'qwen-turbo', 'qwen-plus', 'deepseek-v3.1'].some((keyword) =>
39
39
  model.toLowerCase().includes(keyword),
40
40
  )
41
41
  ? {
@@ -45,7 +45,7 @@ export const LobeSiliconCloudAI = createOpenAICompatibleRuntime({
45
45
 
46
46
  return {
47
47
  ...rest,
48
- ...(['qwen3'].some((keyword) => model.toLowerCase().includes(keyword))
48
+ ...(['qwen3', 'deepseek-v3.1'].some((keyword) => model.toLowerCase().includes(keyword))
49
49
  ? {
50
50
  enable_thinking: thinking !== undefined ? thinking.type === 'enabled' : false,
51
51
  thinking_budget:
@@ -1,8 +1,6 @@
1
- import { Tag } from '@lobehub/ui';
2
- import { Bot, Brain, Cloudy, Info, Mic2, Settings2, Sparkles } from 'lucide-react';
1
+ import { Bot, Brain, Info, Mic2, Settings2, Sparkles } from 'lucide-react';
3
2
  import { useRouter } from 'next/navigation';
4
3
  import { useTranslation } from 'react-i18next';
5
- import { Flexbox } from 'react-layout-kit';
6
4
  import urlJoin from 'url-join';
7
5
 
8
6
  import { CellProps } from '@/components/Cell';
@@ -13,7 +11,7 @@ import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfi
13
11
  export const useCategory = () => {
14
12
  const router = useRouter();
15
13
  const { t } = useTranslation('setting');
16
- const { enableWebrtc, showLLM } = useServerConfigStore(featureFlagsSelectors);
14
+ const { showLLM } = useServerConfigStore(featureFlagsSelectors);
17
15
 
18
16
  const items: CellProps[] = [
19
17
  {
@@ -26,18 +24,6 @@ export const useCategory = () => {
26
24
  key: SettingsTabs.SystemAgent,
27
25
  label: t('tab.system-agent'),
28
26
  },
29
- enableWebrtc && {
30
- icon: Cloudy,
31
- key: SettingsTabs.Sync,
32
- label: (
33
- <Flexbox align={'center'} gap={8} horizontal>
34
- {t('tab.sync')}
35
- <Tag bordered={false} color={'warning'}>
36
- {t('tab.experiment')}
37
- </Tag>
38
- </Flexbox>
39
- ),
40
- },
41
27
  showLLM &&
42
28
  (isDeprecatedEdition
43
29
  ? {
@@ -9,7 +9,6 @@ import { Flexbox } from 'react-layout-kit';
9
9
 
10
10
  import { ProductLogo } from '@/components/Branding';
11
11
  import { DESKTOP_HEADER_ICON_SIZE } from '@/const/layoutTokens';
12
- import SyncStatusTag from '@/features/SyncStatusInspector';
13
12
  import { useActionSWR } from '@/libs/swr';
14
13
  import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
15
14
  import { useSessionStore } from '@/store/session';
@@ -33,7 +32,7 @@ const Header = memo(() => {
33
32
  const { styles } = useStyles();
34
33
  const { t } = useTranslation('chat');
35
34
  const [createSession] = useSessionStore((s) => [s.createSession]);
36
- const { enableWebrtc, showCreateSession } = useServerConfigStore(featureFlagsSelectors);
35
+ const { showCreateSession } = useServerConfigStore(featureFlagsSelectors);
37
36
 
38
37
  const { mutate, isValidating } = useActionSWR('session.createSession', () => createSession());
39
38
 
@@ -50,7 +49,6 @@ const Header = memo(() => {
50
49
  }}
51
50
  >
52
51
  <ProductLogo className={styles.logo} size={36} type={'text'} />
53
- {enableWebrtc && <SyncStatusTag />}
54
52
  </Flexbox>
55
53
  <Flexbox align={'center'} gap={4} horizontal>
56
54
  <TogglePanelButton />
@@ -9,7 +9,6 @@ import { Flexbox } from 'react-layout-kit';
9
9
 
10
10
  import { ProductLogo } from '@/components/Branding';
11
11
  import { MOBILE_HEADER_ICON_SIZE } from '@/const/layoutTokens';
12
- import SyncStatusInspector from '@/features/SyncStatusInspector';
13
12
  import UserAvatar from '@/features/User/UserAvatar';
14
13
  import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
15
14
  import { useSessionStore } from '@/store/session';
@@ -18,7 +17,7 @@ import { mobileHeaderSticky } from '@/styles/mobileHeader';
18
17
  const Header = memo(() => {
19
18
  const [createSession] = useSessionStore((s) => [s.createSession]);
20
19
  const router = useRouter();
21
- const { enableWebrtc, showCreateSession } = useServerConfigStore(featureFlagsSelectors);
20
+ const { showCreateSession } = useServerConfigStore(featureFlagsSelectors);
22
21
 
23
22
  return (
24
23
  <ChatHeader
@@ -26,7 +25,6 @@ const Header = memo(() => {
26
25
  <Flexbox align={'center'} gap={8} horizontal style={{ marginLeft: 8 }}>
27
26
  <UserAvatar onClick={() => router.push('/me')} size={32} />
28
27
  <ProductLogo type={'text'} />
29
- {enableWebrtc && <SyncStatusInspector placement={'bottom'} />}
30
28
  </Flexbox>
31
29
  }
32
30
  right={
@@ -1,8 +1,7 @@
1
- import { Icon, Tag } from '@lobehub/ui';
1
+ import { Icon } from '@lobehub/ui';
2
2
  import {
3
3
  Bot,
4
4
  Brain,
5
- Cloudy,
6
5
  Database,
7
6
  EthernetPort,
8
7
  Info,
@@ -14,7 +13,6 @@ import {
14
13
  import Link from 'next/link';
15
14
  import { useMemo } from 'react';
16
15
  import { useTranslation } from 'react-i18next';
17
- import { Flexbox } from 'react-layout-kit';
18
16
 
19
17
  import type { MenuProps } from '@/components/Menu';
20
18
  import { isDeprecatedEdition, isDesktop } from '@/const/version';
@@ -24,8 +22,7 @@ import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfi
24
22
  export const useCategory = () => {
25
23
  const { t } = useTranslation('setting');
26
24
  const mobile = useServerConfigStore((s) => s.isMobile);
27
- const { enableWebrtc, showLLM, enableSTT, hideDocs } =
28
- useServerConfigStore(featureFlagsSelectors);
25
+ const { showLLM, enableSTT, hideDocs } = useServerConfigStore(featureFlagsSelectors);
29
26
 
30
27
  const cateItems: MenuProps['items'] = useMemo(
31
28
  () =>
@@ -48,21 +45,6 @@ export const useCategory = () => {
48
45
  </Link>
49
46
  ),
50
47
  },
51
- // TODO: remove in V2
52
- enableWebrtc && {
53
- icon: <Icon icon={Cloudy} />,
54
- key: SettingsTabs.Sync,
55
- label: (
56
- <Link href={'/settings/sync'} onClick={(e) => e.preventDefault()}>
57
- <Flexbox align={'center'} gap={8} horizontal>
58
- {t('tab.sync')}
59
- <Tag bordered={false} color={'warning'}>
60
- {t('tab.experiment')}
61
- </Tag>
62
- </Flexbox>
63
- </Link>
64
- ),
65
- },
66
48
  !mobile && {
67
49
  icon: <Icon icon={KeyboardIcon} />,
68
50
  key: SettingsTabs.Hotkey,
@@ -146,7 +128,7 @@ export const useCategory = () => {
146
128
  ),
147
129
  },
148
130
  ].filter(Boolean) as MenuProps['items'],
149
- [t, enableWebrtc, showLLM],
131
+ [t, showLLM],
150
132
  );
151
133
 
152
134
  return cateItems;
@@ -20,7 +20,7 @@ describe('FeatureFlagsSchema', () => {
20
20
 
21
21
  it('should reject invalid feature flags', () => {
22
22
  const result = FeatureFlagsSchema.safeParse({
23
- webrtc_sync: 'yes', // Invalid type, should be boolean
23
+ edit_agent: 'yes', // Invalid type, should be boolean
24
24
  });
25
25
 
26
26
  expect(result.success).toBe(false);
@@ -43,7 +43,6 @@ describe('mapFeatureFlagsEnvToState', () => {
43
43
  };
44
44
 
45
45
  const expectedState = {
46
- enableWebrtc: true,
47
46
  isAgentEditable: false,
48
47
  showCreateSession: true,
49
48
  showLLM: false,
@@ -2,10 +2,6 @@
2
2
  import { z } from 'zod';
3
3
 
4
4
  export const FeatureFlagsSchema = z.object({
5
- /**
6
- * Enable WebRTC sync
7
- */
8
- webrtc_sync: z.boolean().optional(),
9
5
  check_updates: z.boolean().optional(),
10
6
  pin_list: z.boolean().optional(),
11
7
 
@@ -51,7 +47,6 @@ export const FeatureFlagsSchema = z.object({
51
47
  export type IFeatureFlags = z.infer<typeof FeatureFlagsSchema>;
52
48
 
53
49
  export const DEFAULT_FEATURE_FLAGS: IFeatureFlags = {
54
- webrtc_sync: false,
55
50
  pin_list: false,
56
51
 
57
52
  language_model_settings: true,
@@ -93,7 +88,6 @@ export const DEFAULT_FEATURE_FLAGS: IFeatureFlags = {
93
88
 
94
89
  export const mapFeatureFlagsEnvToState = (config: IFeatureFlags) => {
95
90
  return {
96
- enableWebrtc: config.webrtc_sync,
97
91
  isAgentEditable: config.edit_agent,
98
92
 
99
93
  showCreateSession: config.create_session,
@@ -8,7 +8,7 @@ describe('parseFeatureFlag', () => {
8
8
  });
9
9
 
10
10
  it('should enable a feature when prefixed with +', () => {
11
- expect(parseFeatureFlag('+webrtc_sync')).toEqual({ webrtc_sync: true });
11
+ expect(parseFeatureFlag('+api_key_manage')).toEqual({ api_key_manage: true });
12
12
  });
13
13
 
14
14
  it('should disable a feature when prefixed with -', () => {
@@ -16,10 +16,10 @@ describe('parseFeatureFlag', () => {
16
16
  });
17
17
 
18
18
  it('should handle multiple flags separated by commas', () => {
19
- const input = '+webrtc_sync,-openai_api_key,+another_feature';
19
+ const input = '+api_key_manage,-openai_api_key,+another_feature';
20
20
 
21
21
  expect(parseFeatureFlag(input)).toEqual({
22
- webrtc_sync: true,
22
+ api_key_manage: true,
23
23
  openai_api_key: false,
24
24
  });
25
25
  });
@@ -53,19 +53,19 @@ describe('parseFeatureFlag', () => {
53
53
  });
54
54
 
55
55
  it('should handle flags separated by Chinese commas', () => {
56
- const input = '+webrtc_sync,-openai_api_key';
56
+ const input = '+api_key_manage,-openai_api_key';
57
57
 
58
58
  expect(parseFeatureFlag(input)).toEqual({
59
- webrtc_sync: true,
59
+ api_key_manage: true,
60
60
  openai_api_key: false,
61
61
  });
62
62
  });
63
63
 
64
64
  it('should ignore whitespace around flags', () => {
65
- const input = ' +webrtc_sync , -openai_api_key ';
65
+ const input = ' +api_key_manage , -openai_api_key ';
66
66
 
67
67
  expect(parseFeatureFlag(input)).toEqual({
68
- webrtc_sync: true,
68
+ api_key_manage: true,
69
69
  openai_api_key: false,
70
70
  });
71
71
  });
@@ -1,3 +1,2 @@
1
1
  export { browserDB } from './db';
2
2
  export * from './model';
3
- export { dataSync } from './sync';