@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.
- package/.vscode/settings.json +2 -3
- package/CHANGELOG.md +33 -0
- package/changelog/v1.json +12 -0
- package/package.json +1 -4
- package/packages/database/src/models/__tests__/generationBatch.test.ts +47 -1
- package/packages/database/src/models/generationBatch.ts +8 -1
- package/packages/model-bank/src/aiModels/aihubmix.ts +1 -1
- package/packages/model-bank/src/aiModels/google.ts +4 -4
- package/packages/model-bank/src/aiModels/openrouter.ts +2 -2
- package/packages/model-bank/src/aiModels/qwen.ts +3 -1
- package/packages/model-bank/src/aiModels/siliconcloud.ts +6 -0
- package/packages/model-bank/src/aiModels/vertexai.ts +2 -2
- package/packages/model-runtime/src/google/createImage.ts +52 -24
- package/packages/model-runtime/src/qwen/index.ts +1 -1
- package/packages/model-runtime/src/siliconcloud/index.ts +1 -1
- package/src/app/[variants]/(main)/(mobile)/me/settings/features/useCategory.tsx +2 -16
- package/src/app/[variants]/(main)/chat/@session/_layout/Desktop/SessionHeader.tsx +1 -3
- package/src/app/[variants]/(main)/chat/@session/_layout/Mobile/SessionHeader.tsx +1 -3
- package/src/app/[variants]/(main)/settings/hooks/useCategory.tsx +3 -21
- package/src/config/featureFlags/schema.test.ts +1 -2
- package/src/config/featureFlags/schema.ts +0 -6
- package/src/config/featureFlags/utils/parser.test.ts +7 -7
- package/src/database/_deprecated/core/index.ts +0 -1
- package/src/database/_deprecated/core/model.ts +4 -38
- package/src/database/_deprecated/models/message.ts +1 -1
- package/src/layout/GlobalProvider/StoreInitialization.tsx +0 -3
- package/src/store/serverConfig/selectors.test.ts +0 -1
- package/src/store/user/initialState.ts +1 -4
- package/src/store/user/selectors.ts +0 -1
- package/src/store/user/store.ts +1 -4
- package/docs/self-hosting/advanced/webrtc.mdx +0 -86
- package/docs/self-hosting/advanced/webrtc.zh-CN.mdx +0 -80
- package/src/app/[variants]/(main)/settings/sync/features/Alert.tsx +0 -53
- package/src/app/[variants]/(main)/settings/sync/features/DeviceInfo/Card.tsx +0 -42
- package/src/app/[variants]/(main)/settings/sync/features/DeviceInfo/DeviceName.tsx +0 -62
- package/src/app/[variants]/(main)/settings/sync/features/DeviceInfo/SystemIcon.tsx +0 -31
- package/src/app/[variants]/(main)/settings/sync/features/DeviceInfo/index.tsx +0 -103
- package/src/app/[variants]/(main)/settings/sync/features/WebRTC/ChannelNameInput.tsx +0 -45
- package/src/app/[variants]/(main)/settings/sync/features/WebRTC/SyncSwitch/index.css +0 -238
- package/src/app/[variants]/(main)/settings/sync/features/WebRTC/SyncSwitch/index.tsx +0 -79
- package/src/app/[variants]/(main)/settings/sync/features/WebRTC/generateRandomRoomName.ts +0 -4
- package/src/app/[variants]/(main)/settings/sync/features/WebRTC/index.tsx +0 -103
- package/src/app/[variants]/(main)/settings/sync/index.tsx +0 -17
- package/src/app/[variants]/(main)/settings/sync/page.tsx +0 -29
- package/src/database/_deprecated/core/sync.ts +0 -321
- package/src/features/SyncStatusInspector/DisableSync.tsx +0 -79
- package/src/features/SyncStatusInspector/EnableSync.tsx +0 -132
- package/src/features/SyncStatusInspector/EnableTag.tsx +0 -66
- package/src/features/SyncStatusInspector/index.tsx +0 -27
- package/src/hooks/useSyncData.ts +0 -50
- package/src/services/__tests__/sync.test.ts +0 -56
- package/src/services/sync.ts +0 -19
- package/src/store/user/slices/sync/action.test.ts +0 -164
- package/src/store/user/slices/sync/action.ts +0 -101
- package/src/store/user/slices/sync/initialState.ts +0 -13
- package/src/store/user/slices/sync/selectors.ts +0 -20
package/.vscode/settings.json
CHANGED
@@ -34,7 +34,7 @@
|
|
34
34
|
// make stylelint work with tsx antd-style css template string
|
35
35
|
"typescriptreact"
|
36
36
|
],
|
37
|
-
"vitest.maximumConfigs":
|
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/
|
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
|
+
[](#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.
|
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 {
|
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: '
|
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
|
-
'
|
200
|
-
displayName: '
|
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: '
|
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
|
-
'
|
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: '
|
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: '
|
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
|
-
'
|
130
|
-
displayName: '
|
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
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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 {
|
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 {
|
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 {
|
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 {
|
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
|
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 {
|
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,
|
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
|
-
|
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('+
|
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 = '+
|
19
|
+
const input = '+api_key_manage,-openai_api_key,+another_feature';
|
20
20
|
|
21
21
|
expect(parseFeatureFlag(input)).toEqual({
|
22
|
-
|
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 = '+
|
56
|
+
const input = '+api_key_manage,-openai_api_key';
|
57
57
|
|
58
58
|
expect(parseFeatureFlag(input)).toEqual({
|
59
|
-
|
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 = ' +
|
65
|
+
const input = ' +api_key_manage , -openai_api_key ';
|
66
66
|
|
67
67
|
expect(parseFeatureFlag(input)).toEqual({
|
68
|
-
|
68
|
+
api_key_manage: true,
|
69
69
|
openai_api_key: false,
|
70
70
|
});
|
71
71
|
});
|