@lobehub/chat 1.88.0 → 1.88.2

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,64 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 1.88.2](https://github.com/lobehub/lobe-chat/compare/v1.88.1...v1.88.2)
6
+
7
+ <sup>Released on **2025-05-24**</sup>
8
+
9
+ #### 💄 Styles
10
+
11
+ - **misc**: Add live search support for xAI.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### Styles
19
+
20
+ - **misc**: Add live search support for xAI, closes [#7907](https://github.com/lobehub/lobe-chat/issues/7907) ([dff4b7b](https://github.com/lobehub/lobe-chat/commit/dff4b7b))
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.88.1](https://github.com/lobehub/lobe-chat/compare/v1.88.0...v1.88.1)
31
+
32
+ <sup>Released on **2025-05-24**</sup>
33
+
34
+ #### 🐛 Bug Fixes
35
+
36
+ - **misc**: User nickName & username selector in desktop.
37
+
38
+ #### 💄 Styles
39
+
40
+ - **misc**: Support Gemini 2.5 thought reasoning.
41
+
42
+ <br/>
43
+
44
+ <details>
45
+ <summary><kbd>Improvements and Fixes</kbd></summary>
46
+
47
+ #### What's fixed
48
+
49
+ - **misc**: User nickName & username selector in desktop, closes [#7899](https://github.com/lobehub/lobe-chat/issues/7899) ([bf51746](https://github.com/lobehub/lobe-chat/commit/bf51746))
50
+
51
+ #### Styles
52
+
53
+ - **misc**: Support Gemini 2.5 thought reasoning, closes [#7686](https://github.com/lobehub/lobe-chat/issues/7686) ([f34c4de](https://github.com/lobehub/lobe-chat/commit/f34c4de))
54
+
55
+ </details>
56
+
57
+ <div align="right">
58
+
59
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
60
+
61
+ </div>
62
+
5
63
  ## [Version 1.88.0](https://github.com/lobehub/lobe-chat/compare/v1.87.9...v1.88.0)
6
64
 
7
65
  <sup>Released on **2025-05-23**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,25 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "improvements": [
5
+ "Add live search support for xAI."
6
+ ]
7
+ },
8
+ "date": "2025-05-24",
9
+ "version": "1.88.2"
10
+ },
11
+ {
12
+ "children": {
13
+ "fixes": [
14
+ "User nickName & username selector in desktop."
15
+ ],
16
+ "improvements": [
17
+ "Support Gemini 2.5 thought reasoning."
18
+ ]
19
+ },
20
+ "date": "2025-05-24",
21
+ "version": "1.88.1"
22
+ },
2
23
  {
3
24
  "children": {
4
25
  "features": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.88.0",
3
+ "version": "1.88.2",
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",
@@ -285,7 +285,7 @@ const googleChatModels: AIChatModelCard[] = [
285
285
  contextWindowTokens: 2_008_192,
286
286
  description:
287
287
  'Gemini 1.5 Pro 002 是最新的生产就绪模型,提供更高质量的输出,特别在数学、长上下文和视觉任务方面有显著提升。',
288
- displayName: 'Gemini 1.5 Pro 002',
288
+ displayName: 'Gemini 1.5 Pro 002 (Paid)',
289
289
  id: 'gemini-1.5-pro-002', // Deprecated on 2025-09-24
290
290
  maxOutput: 8192,
291
291
  pricing: {
@@ -303,7 +303,7 @@ const googleChatModels: AIChatModelCard[] = [
303
303
  },
304
304
  contextWindowTokens: 2_008_192,
305
305
  description: 'Gemini 1.5 Pro 001 是可扩展的多模态AI解决方案,支持广泛的复杂任务。',
306
- displayName: 'Gemini 1.5 Pro 001',
306
+ displayName: 'Gemini 1.5 Pro 001 (Paid)',
307
307
  id: 'gemini-1.5-pro-001', // Deprecated on 2025-05-27
308
308
  maxOutput: 8192,
309
309
  pricing: {
@@ -5,91 +5,91 @@ const xaiChatModels: AIChatModelCard[] = [
5
5
  {
6
6
  abilities: {
7
7
  functionCall: true,
8
+ search: true,
8
9
  },
9
10
  contextWindowTokens: 131_072,
10
11
  description:
11
12
  '旗舰级模型,擅长数据提取、编程和文本摘要等企业级应用,拥有金融、医疗、法律和科学等领域的深厚知识。',
12
- displayName: 'Grok 3 Beta',
13
+ displayName: 'Grok 3',
13
14
  enabled: true,
14
- id: 'grok-3-beta',
15
+ id: 'grok-3',
15
16
  pricing: {
16
17
  input: 3,
17
18
  output: 15,
18
19
  },
19
20
  releasedAt: '2025-04-03',
21
+ settings: {
22
+ searchImpl: 'params',
23
+ },
20
24
  type: 'chat',
21
25
  },
22
26
  {
23
27
  abilities: {
24
28
  functionCall: true,
29
+ search: true,
25
30
  },
26
31
  contextWindowTokens: 131_072,
27
32
  description:
28
33
  '旗舰级模型,擅长数据提取、编程和文本摘要等企业级应用,拥有金融、医疗、法律和科学等领域的深厚知识。',
29
- displayName: 'Grok 3 Beta (Fast mode)',
30
- id: 'grok-3-fast-beta',
34
+ displayName: 'Grok 3 (Fast mode)',
35
+ id: 'grok-3-fast',
31
36
  pricing: {
32
37
  input: 5,
33
38
  output: 25,
34
39
  },
35
40
  releasedAt: '2025-04-03',
41
+ settings: {
42
+ searchImpl: 'params',
43
+ },
36
44
  type: 'chat',
37
45
  },
38
46
  {
39
47
  abilities: {
40
48
  functionCall: true,
41
49
  reasoning: true,
50
+ search: true,
42
51
  },
43
52
  contextWindowTokens: 131_072,
44
53
  description:
45
54
  '轻量级模型,回话前会先思考。运行快速、智能,适用于不需要深层领域知识的逻辑任务,并能获取原始的思维轨迹。',
46
- displayName: 'Grok 3 Mini Beta',
55
+ displayName: 'Grok 3 Mini',
47
56
  enabled: true,
48
- id: 'grok-3-mini-beta',
57
+ id: 'grok-3-mini',
49
58
  pricing: {
50
59
  input: 0.3,
51
60
  output: 0.5,
52
61
  },
53
62
  releasedAt: '2025-04-03',
63
+ settings: {
64
+ searchImpl: 'params',
65
+ },
54
66
  type: 'chat',
55
67
  },
56
68
  {
57
69
  abilities: {
58
70
  functionCall: true,
59
71
  reasoning: true,
72
+ search: true,
60
73
  },
61
74
  contextWindowTokens: 131_072,
62
75
  description:
63
76
  '轻量级模型,回话前会先思考。运行快速、智能,适用于不需要深层领域知识的逻辑任务,并能获取原始的思维轨迹。',
64
- displayName: 'Grok 3 Mini Beta (Fast mode)',
65
- id: 'grok-3-mini-fast-beta',
77
+ displayName: 'Grok 3 Mini (Fast mode)',
78
+ id: 'grok-3-mini-fast',
66
79
  pricing: {
67
80
  input: 0.6,
68
81
  output: 4,
69
82
  },
70
83
  releasedAt: '2025-04-03',
71
- type: 'chat',
72
- },
73
- {
74
- abilities: {
75
- functionCall: true,
76
- vision: true,
77
- },
78
- contextWindowTokens: 32_768,
79
- description: '该模型在准确性、指令遵循和多语言能力方面有所改进。',
80
- displayName: 'Grok 2 Vision 1212',
81
- enabled: true,
82
- id: 'grok-2-vision-1212',
83
- pricing: {
84
- input: 2,
85
- output: 10,
84
+ settings: {
85
+ searchImpl: 'params',
86
86
  },
87
- releasedAt: '2024-12-12',
88
87
  type: 'chat',
89
88
  },
90
89
  {
91
90
  abilities: {
92
91
  functionCall: true,
92
+ search: true,
93
93
  },
94
94
  contextWindowTokens: 131_072,
95
95
  description: '该模型在准确性、指令遵循和多语言能力方面有所改进。',
@@ -100,34 +100,29 @@ const xaiChatModels: AIChatModelCard[] = [
100
100
  output: 10,
101
101
  },
102
102
  releasedAt: '2024-12-12',
103
- type: 'chat',
104
- },
105
- {
106
- abilities: {
107
- functionCall: true,
108
- },
109
- contextWindowTokens: 131_072,
110
- description: '拥有与 Grok 2 相当的性能,但具有更高的效率、速度和功能。',
111
- displayName: 'Grok Beta',
112
- id: 'grok-beta', // legacy
113
- pricing: {
114
- input: 5,
115
- output: 15,
103
+ settings: {
104
+ searchImpl: 'params',
116
105
  },
117
106
  type: 'chat',
118
107
  },
119
108
  {
120
109
  abilities: {
121
110
  functionCall: true,
111
+ search: true,
122
112
  vision: true,
123
113
  },
124
- contextWindowTokens: 8192,
125
- description: '最新的图像理解模型,可以处理各种各样的视觉信息,包括文档、图表、截图和照片等。',
126
- displayName: 'Grok Vision Beta',
127
- id: 'grok-vision-beta', // legacy
114
+ contextWindowTokens: 32_768,
115
+ description: '该模型在准确性、指令遵循和多语言能力方面有所改进。',
116
+ displayName: 'Grok 2 Vision 1212',
117
+ enabled: true,
118
+ id: 'grok-2-vision-1212',
128
119
  pricing: {
129
- input: 5,
130
- output: 15,
120
+ input: 2,
121
+ output: 10,
122
+ },
123
+ releasedAt: '2024-12-12',
124
+ settings: {
125
+ searchImpl: 'params',
131
126
  },
132
127
  type: 'chat',
133
128
  },
@@ -82,6 +82,10 @@ interface LobeGoogleAIParams {
82
82
  isVertexAi?: boolean;
83
83
  }
84
84
 
85
+ interface GoogleAIThinkingConfig {
86
+ includeThoughts?: boolean;
87
+ }
88
+
85
89
  export class LobeGoogleAI implements LobeRuntimeAI {
86
90
  private client: GoogleGenerativeAI;
87
91
  private isVertexAi: boolean;
@@ -106,6 +110,10 @@ export class LobeGoogleAI implements LobeRuntimeAI {
106
110
  const payload = this.buildPayload(rawPayload);
107
111
  const model = payload.model;
108
112
 
113
+ const thinkingConfig: GoogleAIThinkingConfig = {
114
+ includeThoughts: true,
115
+ };
116
+
109
117
  const contents = await this.buildGoogleMessages(payload.messages);
110
118
 
111
119
  const inputStartAt = Date.now();
@@ -117,6 +125,7 @@ export class LobeGoogleAI implements LobeRuntimeAI {
117
125
  // @ts-expect-error - Google SDK 0.24.0 doesn't have this property for now with
118
126
  response_modalities: modelsWithModalities.has(model) ? ['Text', 'Image'] : undefined,
119
127
  temperature: payload.temperature,
128
+ thinkingConfig,
120
129
  topP: payload.top_p,
121
130
  },
122
131
  model,
@@ -76,6 +76,15 @@ const transformGoogleGenerativeAIStream = (
76
76
  const text = chunk.text?.();
77
77
 
78
78
  if (candidate) {
79
+ // 首先检查是否为 reasoning 内容 (thought: true)
80
+ if (Array.isArray(candidate.content.parts) && candidate.content.parts.length > 0) {
81
+ for (const part of candidate.content.parts) {
82
+ if (part && part.text && (part as any).thought === true) {
83
+ return { data: part.text, id: context.id, type: 'reasoning' };
84
+ }
85
+ }
86
+ }
87
+
79
88
  // return the grounding
80
89
  if (candidate.groundingMetadata) {
81
90
  const { webSearchQueries, groundingChunks } = candidate.groundingMetadata;
@@ -10,8 +10,4 @@ testProvider({
10
10
  defaultBaseURL: 'https://api.x.ai/v1',
11
11
  chatDebugEnv: 'DEBUG_XAI_CHAT_COMPLETION',
12
12
  chatModel: 'grok',
13
-
14
- test: {
15
- skipAPICall: true,
16
- },
17
13
  });
@@ -10,16 +10,36 @@ export interface XAIModelCard {
10
10
  export const LobeXAI = LobeOpenAICompatibleFactory({
11
11
  baseURL: 'https://api.x.ai/v1',
12
12
  chatCompletion: {
13
- // xAI API does not support stream_options: { include_usage: true }
14
- excludeUsage: true,
15
13
  handlePayload: (payload) => {
16
- const { frequency_penalty, model, presence_penalty, ...rest } = payload;
14
+ const { enabledSearch, frequency_penalty, model, presence_penalty, ...rest } = payload;
17
15
 
18
16
  return {
19
17
  ...rest,
20
18
  frequency_penalty: model.includes('grok-3-mini') ? undefined : frequency_penalty,
21
19
  model,
22
20
  presence_penalty: model.includes('grok-3-mini') ? undefined : presence_penalty,
21
+ stream: true,
22
+ ...(enabledSearch && {
23
+ search_parameters: {
24
+ max_search_results: Math.min(Math.max(parseInt(process.env.XAI_MAX_SEARCH_RESULTS ?? '15', 10), 1), 30),
25
+ mode: 'auto',
26
+ return_citations: true,
27
+ sources: [
28
+ {
29
+ safe_search: process.env.XAI_SAFE_SEARCH === '1',
30
+ type: 'news',
31
+ },
32
+ /*
33
+ { type: 'rss' },
34
+ */
35
+ {
36
+ safe_search: process.env.XAI_SAFE_SEARCH === '1',
37
+ type: 'web',
38
+ },
39
+ { type: 'x' },
40
+ ],
41
+ },
42
+ }),
23
43
  } as any;
24
44
  },
25
45
  },
@@ -11,6 +11,7 @@ vi.mock('i18next', () => ({
11
11
 
12
12
  // 定义一个变量来存储 enableAuth 的值
13
13
  let enableAuth = true;
14
+ let isDesktop = false;
14
15
 
15
16
  // 模拟 @/const/auth 模块
16
17
  vi.mock('@/const/auth', () => ({
@@ -19,14 +20,23 @@ vi.mock('@/const/auth', () => ({
19
20
  },
20
21
  }));
21
22
 
23
+ // 模拟 @/const/version 模块
24
+ vi.mock('@/const/version', () => ({
25
+ get isDesktop() {
26
+ return isDesktop;
27
+ },
28
+ }));
29
+
22
30
  afterEach(() => {
23
31
  enableAuth = true;
32
+ isDesktop = false;
24
33
  });
25
34
 
26
35
  describe('userProfileSelectors', () => {
27
36
  describe('nickName', () => {
28
- it('should return default nickname when auth is disabled', () => {
37
+ it('should return default nickname when auth is disabled and not desktop', () => {
29
38
  enableAuth = false;
39
+ isDesktop = false;
30
40
 
31
41
  const store: UserStore = {
32
42
  isSignedIn: false,
@@ -38,6 +48,19 @@ describe('userProfileSelectors', () => {
38
48
  expect(t).toHaveBeenCalledWith('userPanel.defaultNickname', { ns: 'common' });
39
49
  });
40
50
 
51
+ it('should return user fullName when auth is disabled and is desktop', () => {
52
+ enableAuth = false;
53
+ isDesktop = true;
54
+
55
+ const store: UserStore = {
56
+ isSignedIn: false,
57
+ user: { fullName: 'John Doe' },
58
+ enableAuth: () => false,
59
+ } as unknown as UserStore;
60
+
61
+ expect(userProfileSelectors.nickName(store)).toBe('John Doe');
62
+ });
63
+
41
64
  it('should return user fullName when signed in', () => {
42
65
  enableAuth = true;
43
66
 
@@ -75,8 +98,9 @@ describe('userProfileSelectors', () => {
75
98
  });
76
99
 
77
100
  describe('username', () => {
78
- it('should return default username when auth is disabled', () => {
101
+ it('should return default username when auth is disabled and not desktop', () => {
79
102
  enableAuth = false;
103
+ isDesktop = false;
80
104
 
81
105
  const store: UserStore = {
82
106
  isSignedIn: false,
@@ -87,6 +111,19 @@ describe('userProfileSelectors', () => {
87
111
  expect(userProfileSelectors.username(store)).toBe('LobeChat');
88
112
  });
89
113
 
114
+ it('should return user username when auth is disabled and is desktop', () => {
115
+ enableAuth = false;
116
+ isDesktop = true;
117
+
118
+ const store: UserStore = {
119
+ isSignedIn: false,
120
+ user: { username: 'johndoe' },
121
+ enableAuth: () => false,
122
+ } as unknown as UserStore;
123
+
124
+ expect(userProfileSelectors.username(store)).toBe('johndoe');
125
+ });
126
+
90
127
  it('should return user username when signed in', () => {
91
128
  const store: UserStore = {
92
129
  isSignedIn: true,
@@ -4,19 +4,31 @@ import { enableAuth, enableClerk, enableNextAuth } from '@/const/auth';
4
4
  import { BRANDING_NAME } from '@/const/branding';
5
5
  import { UserStore } from '@/store/user';
6
6
  import { LobeUser } from '@/types/user';
7
+ import { isDesktop } from '@/const/version';
7
8
 
8
9
  const DEFAULT_USERNAME = BRANDING_NAME;
9
10
 
10
11
  const nickName = (s: UserStore) => {
11
- if (!enableAuth) return t('userPanel.defaultNickname', { ns: 'common' });
12
+ const defaultNickName = s.user?.fullName || s.user?.username;
13
+ if (!enableAuth) {
14
+ if (isDesktop) {
15
+ return defaultNickName;
16
+ }
17
+ return t('userPanel.defaultNickname', { ns: 'common' });
18
+ }
12
19
 
13
- if (s.isSignedIn) return s.user?.fullName || s.user?.username;
20
+ if (s.isSignedIn) return defaultNickName;
14
21
 
15
22
  return t('userPanel.anonymousNickName', { ns: 'common' });
16
23
  };
17
24
 
18
25
  const username = (s: UserStore) => {
19
- if (!enableAuth) return DEFAULT_USERNAME;
26
+ if (!enableAuth) {
27
+ if (isDesktop) {
28
+ return s.user?.username;
29
+ }
30
+ return DEFAULT_USERNAME;
31
+ }
20
32
 
21
33
  if (s.isSignedIn) return s.user?.username;
22
34