@lobehub/chat 1.87.0 → 1.87.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,57 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 1.87.2](https://github.com/lobehub/lobe-chat/compare/v1.87.1...v1.87.2)
6
+
7
+ <sup>Released on **2025-05-16**</sup>
8
+
9
+ #### 💄 Styles
10
+
11
+ - **misc**: Support Doubao 1.5 Thinking Vision Pro model.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### Styles
19
+
20
+ - **misc**: Support Doubao 1.5 Thinking Vision Pro model, closes [#7784](https://github.com/lobehub/lobe-chat/issues/7784) ([9cf0d6f](https://github.com/lobehub/lobe-chat/commit/9cf0d6f))
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.87.1](https://github.com/lobehub/lobe-chat/compare/v1.87.0...v1.87.1)
31
+
32
+ <sup>Released on **2025-05-16**</sup>
33
+
34
+ #### 💄 Styles
35
+
36
+ - **misc**: Update internlm model list, add series, update Spark X1 model list & fix build-in search params.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### Styles
44
+
45
+ - **misc**: Update internlm model list, add series, closes [#7566](https://github.com/lobehub/lobe-chat/issues/7566) ([4eaddf4](https://github.com/lobehub/lobe-chat/commit/4eaddf4))
46
+ - **misc**: Update Spark X1 model list & fix build-in search params, closes [#7480](https://github.com/lobehub/lobe-chat/issues/7480) ([7050c81](https://github.com/lobehub/lobe-chat/commit/7050c81))
47
+
48
+ </details>
49
+
50
+ <div align="right">
51
+
52
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
53
+
54
+ </div>
55
+
5
56
  ## [Version 1.87.0](https://github.com/lobehub/lobe-chat/compare/v1.86.1...v1.87.0)
6
57
 
7
58
  <sup>Released on **2025-05-16**</sup>
package/Dockerfile CHANGED
@@ -215,7 +215,7 @@ ENV \
215
215
  # SiliconCloud
216
216
  SILICONCLOUD_API_KEY="" SILICONCLOUD_MODEL_LIST="" SILICONCLOUD_PROXY_URL="" \
217
217
  # Spark
218
- SPARK_API_KEY="" SPARK_MODEL_LIST="" \
218
+ SPARK_API_KEY="" SPARK_MODEL_LIST="" SPARK_PROXY_URL="" SPARK_SEARCH_MODE="" \
219
219
  # Stepfun
220
220
  STEPFUN_API_KEY="" STEPFUN_MODEL_LIST="" \
221
221
  # Taichu
@@ -259,7 +259,7 @@ ENV \
259
259
  # SiliconCloud
260
260
  SILICONCLOUD_API_KEY="" SILICONCLOUD_MODEL_LIST="" SILICONCLOUD_PROXY_URL="" \
261
261
  # Spark
262
- SPARK_API_KEY="" SPARK_MODEL_LIST="" \
262
+ SPARK_API_KEY="" SPARK_MODEL_LIST="" SPARK_PROXY_URL="" SPARK_SEARCH_MODE="" \
263
263
  # Stepfun
264
264
  STEPFUN_API_KEY="" STEPFUN_MODEL_LIST="" \
265
265
  # Taichu
package/Dockerfile.pglite CHANGED
@@ -213,7 +213,7 @@ ENV \
213
213
  # SiliconCloud
214
214
  SILICONCLOUD_API_KEY="" SILICONCLOUD_MODEL_LIST="" SILICONCLOUD_PROXY_URL="" \
215
215
  # Spark
216
- SPARK_API_KEY="" SPARK_MODEL_LIST="" \
216
+ SPARK_API_KEY="" SPARK_MODEL_LIST="" SPARK_PROXY_URL="" SPARK_SEARCH_MODE="" \
217
217
  # Stepfun
218
218
  STEPFUN_API_KEY="" STEPFUN_MODEL_LIST="" \
219
219
  # Taichu
package/changelog/v1.json CHANGED
@@ -1,4 +1,22 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "improvements": [
5
+ "Support Doubao 1.5 Thinking Vision Pro model."
6
+ ]
7
+ },
8
+ "date": "2025-05-16",
9
+ "version": "1.87.2"
10
+ },
11
+ {
12
+ "children": {
13
+ "improvements": [
14
+ "Update internlm model list, add series, update Spark X1 model list & fix build-in search params."
15
+ ]
16
+ },
17
+ "date": "2025-05-16",
18
+ "version": "1.87.1"
19
+ },
2
20
  {
3
21
  "children": {
4
22
  "features": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.87.0",
3
+ "version": "1.87.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",
@@ -27,7 +27,6 @@ const internlmChatModels: AIChatModelCard[] = [
27
27
  description:
28
28
  '我们仍在维护的老版本模型,经过多轮迭代有着极其优异且稳定的性能,包含 7B、20B 多种模型参数量可选,支持 1M 的上下文长度以及更强的指令跟随和工具调用能力。默认指向我们最新发布的 InternLM2.5 系列模型,当前指向 internlm2.5-20b-chat。',
29
29
  displayName: 'InternLM2.5',
30
- enabled: true,
31
30
  id: 'internlm2.5-latest',
32
31
  pricing: {
33
32
  input: 0,
@@ -37,12 +36,29 @@ const internlmChatModels: AIChatModelCard[] = [
37
36
  },
38
37
  {
39
38
  abilities: {
40
- functionCall: true,
39
+ vision: true,
41
40
  },
42
41
  contextWindowTokens: 32_768,
43
- description: 'InternLM2 版本最大的模型,专注于高度复杂的任务',
44
- displayName: 'InternLM2 Pro Chat',
45
- id: 'internlm2-pro-chat',
42
+ description:
43
+ '我们最新发布多模态大模型,具备更强的图文理解能力、长时序图片理解能力,性能比肩顶尖闭源模型。默认指向我们最新发布的 InternVL 系列模型,当前指向 internvl3-78b。',
44
+ displayName: 'InternVL3',
45
+ enabled: true,
46
+ id: 'internvl3-latest',
47
+ pricing: {
48
+ input: 0,
49
+ output: 0,
50
+ },
51
+ type: 'chat',
52
+ },
53
+ {
54
+ abilities: {
55
+ vision: true,
56
+ },
57
+ contextWindowTokens: 32_768,
58
+ description:
59
+ '我们仍在维护的 InternVL2.5 版本,具备优异且稳定的性能。默认指向我们最新发布的 InternVL2.5 系列模型,当前指向 internvl2.5-78b。',
60
+ displayName: 'InternVL2.5',
61
+ id: 'internvl2.5-latest',
46
62
  pricing: {
47
63
  input: 0,
48
64
  output: 0,
@@ -3,8 +3,21 @@ import { AIChatModelCard } from '@/types/aiModel';
3
3
  const sparkChatModels: AIChatModelCard[] = [
4
4
  {
5
5
  abilities: {
6
+ reasoning: true,
6
7
  search: true,
7
8
  },
9
+ contextWindowTokens: 32_768,
10
+ description:
11
+ 'Spark X1 模型将进一步升级,在原来数学任务国内领先基础上,推理、文本生成、语言理解等通用任务实现效果对标 OpenAI o1 和 DeepSeek R1。',
12
+ displayName: 'Spark X1',
13
+ id: 'x1',
14
+ maxOutput: 32_768,
15
+ settings: {
16
+ searchImpl: 'params',
17
+ },
18
+ type: 'chat',
19
+ },
20
+ {
8
21
  contextWindowTokens: 8192,
9
22
  description:
10
23
  'Spark Lite 是一款轻量级大语言模型,具备极低的延迟与高效的处理能力,完全免费开放,支持实时在线搜索功能。其快速响应的特性使其在低算力设备上的推理应用和模型微调中表现出色,为用户带来出色的成本效益和智能体验,尤其在知识问答、内容生成及搜索场景下表现不俗。',
@@ -12,9 +25,6 @@ const sparkChatModels: AIChatModelCard[] = [
12
25
  enabled: true,
13
26
  id: 'lite',
14
27
  maxOutput: 4096,
15
- settings: {
16
- searchImpl: 'internal',
17
- },
18
28
  type: 'chat',
19
29
  },
20
30
  {
@@ -29,24 +39,17 @@ const sparkChatModels: AIChatModelCard[] = [
29
39
  id: 'generalv3',
30
40
  maxOutput: 8192,
31
41
  settings: {
32
- searchImpl: 'internal',
42
+ searchImpl: 'params',
33
43
  },
34
44
  type: 'chat',
35
45
  },
36
46
  {
37
- abilities: {
38
- search: true,
39
- },
40
47
  contextWindowTokens: 131_072,
41
48
  description:
42
49
  'Spark Pro 128K 配置了特大上下文处理能力,能够处理多达128K的上下文信息,特别适合需通篇分析和长期逻辑关联处理的长文内容,可在复杂文本沟通中提供流畅一致的逻辑与多样的引用支持。',
43
50
  displayName: 'Spark Pro 128K',
44
- enabled: true,
45
51
  id: 'pro-128k',
46
52
  maxOutput: 4096,
47
- settings: {
48
- searchImpl: 'internal',
49
- },
50
53
  type: 'chat',
51
54
  },
52
55
  {
@@ -62,7 +65,7 @@ const sparkChatModels: AIChatModelCard[] = [
62
65
  id: 'generalv3.5',
63
66
  maxOutput: 8192,
64
67
  settings: {
65
- searchImpl: 'internal',
68
+ searchImpl: 'params',
66
69
  },
67
70
  type: 'chat',
68
71
  },
@@ -75,7 +78,6 @@ const sparkChatModels: AIChatModelCard[] = [
75
78
  description:
76
79
  'Spark Max 32K 配置了大上下文处理能力,更强的上下文理解和逻辑推理能力,支持32K tokens的文本输入,适用于长文档阅读、私有知识问答等场景',
77
80
  displayName: 'Spark Max 32K',
78
- enabled: true,
79
81
  id: 'max-32k',
80
82
  maxOutput: 8192,
81
83
  settings: {
@@ -96,7 +98,7 @@ const sparkChatModels: AIChatModelCard[] = [
96
98
  id: '4.0Ultra',
97
99
  maxOutput: 8192,
98
100
  settings: {
99
- searchImpl: 'internal',
101
+ searchImpl: 'params',
100
102
  },
101
103
  type: 'chat',
102
104
  },
@@ -4,6 +4,32 @@ import { AIChatModelCard } from '@/types/aiModel';
4
4
  // pricing https://console.volcengine.com/ark/region:ark+cn-beijing/openManagement
5
5
 
6
6
  const doubaoChatModels: AIChatModelCard[] = [
7
+ {
8
+ abilities: {
9
+ functionCall: true,
10
+ reasoning: true,
11
+ vision: true,
12
+ },
13
+ config: {
14
+ deploymentName: 'doubao-1-5-thinking-vision-pro-250428',
15
+ },
16
+ contextWindowTokens: 131_072,
17
+ description:
18
+ '全新视觉深度思考模型,具备更强的通用多模态理解和推理能力,在 59 个公开评测基准中的 37 个上取得 SOTA 表现。',
19
+ displayName: 'Doubao 1.5 Thinking Vision Pro',
20
+ enabled: true,
21
+ id: 'Doubao-1.5-thinking-vision-pro',
22
+ maxOutput: 16_000,
23
+ pricing: {
24
+ currency: 'CNY',
25
+ input: 3,
26
+ output: 9,
27
+ },
28
+ settings: {
29
+ extendParams: ['enableReasoning'],
30
+ },
31
+ type: 'chat',
32
+ },
7
33
  {
8
34
  abilities: {
9
35
  functionCall: true,
@@ -39,7 +65,6 @@ const doubaoChatModels: AIChatModelCard[] = [
39
65
  description:
40
66
  'Doubao-1.5全新深度思考模型 (m 版本自带原生多模态深度推理能力),在数学、编程、科学推理等专业领域及创意写作等通用任务中表现突出,在AIME 2024、Codeforces、GPQA等多项权威基准上达到或接近业界第一梯队水平。支持128k上下文窗口,16k输出。',
41
67
  displayName: 'Doubao 1.5 Thinking Pro M',
42
- enabled: true,
43
68
  id: 'Doubao-1.5-thinking-pro-m',
44
69
  maxOutput: 16_000,
45
70
  pricing: {
@@ -69,7 +69,11 @@ const Spark: ModelProviderCard = {
69
69
  modelsUrl: 'https://xinghuo.xfyun.cn/spark',
70
70
  name: 'Spark',
71
71
  settings: {
72
+ disableBrowserRequest: true,
72
73
  modelEditable: false,
74
+ proxyUrl: {
75
+ placeholder: 'https://spark-api-open.xf-yun.com/v1',
76
+ },
73
77
  sdkType: 'openai',
74
78
  showModelFetcher: false,
75
79
  smoothing: {
@@ -23,6 +23,10 @@ export const LobeInternLMAI = LobeOpenAICompatibleFactory({
23
23
  models: async ({ client }) => {
24
24
  const { LOBE_DEFAULT_MODEL_LIST } = await import('@/config/aiModels');
25
25
 
26
+ const functionCallKeywords = ['internlm']
27
+
28
+ const visionKeywords = ['internvl']
29
+
26
30
  const modelsPage = (await client.models.list()) as any;
27
31
  const modelList: InternLMModelCard[] = modelsPage.data;
28
32
 
@@ -36,10 +40,18 @@ export const LobeInternLMAI = LobeOpenAICompatibleFactory({
36
40
  contextWindowTokens: knownModel?.contextWindowTokens ?? undefined,
37
41
  displayName: knownModel?.displayName ?? undefined,
38
42
  enabled: knownModel?.enabled || false,
39
- functionCall: knownModel?.abilities?.functionCall || false,
43
+ functionCall:
44
+ functionCallKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) ||
45
+ knownModel?.abilities?.functionCall ||
46
+ false,
40
47
  id: model.id,
41
- reasoning: knownModel?.abilities?.reasoning || false,
42
- vision: knownModel?.abilities?.vision || false,
48
+ reasoning:
49
+ knownModel?.abilities?.reasoning ||
50
+ false,
51
+ vision:
52
+ visionKeywords.some(keyword => model.id.toLowerCase().includes(keyword)) ||
53
+ knownModel?.abilities?.vision ||
54
+ false,
43
55
  };
44
56
  })
45
57
  .filter(Boolean) as ChatModelCard[];
@@ -13,4 +13,7 @@ testProvider({
13
13
  defaultBaseURL,
14
14
  chatDebugEnv: 'DEBUG_SPARK_CHAT_COMPLETION',
15
15
  chatModel: 'spark',
16
+ test: {
17
+ skipAPICall: true,
18
+ },
16
19
  });
@@ -1,10 +1,32 @@
1
- import { ModelProvider } from '../types';
1
+ import { ChatStreamPayload, ModelProvider } from '../types';
2
2
  import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory';
3
3
  import { SparkAIStream, transformSparkResponseToStream } from '../utils/streams';
4
4
 
5
5
  export const LobeSparkAI = LobeOpenAICompatibleFactory({
6
6
  baseURL: 'https://spark-api-open.xf-yun.com/v1',
7
7
  chatCompletion: {
8
+ handlePayload: (payload: ChatStreamPayload) => {
9
+ const { enabledSearch, tools, ...rest } = payload;
10
+
11
+ const sparkTools = enabledSearch ? [
12
+ ...(tools || []),
13
+ {
14
+ type: "web_search",
15
+ web_search: {
16
+ enable: true,
17
+ search_mode: process.env.SPARK_SEARCH_MODE || "normal", // normal or deep
18
+ /*
19
+ show_ref_label: true,
20
+ */
21
+ },
22
+ }
23
+ ] : tools;
24
+
25
+ return {
26
+ ...rest,
27
+ tools: sparkTools,
28
+ } as any;
29
+ },
8
30
  handleStream: SparkAIStream,
9
31
  handleTransformResponseToStream: transformSparkResponseToStream,
10
32
  noUserId: true,
@@ -6,6 +6,72 @@ import { SparkAIStream, transformSparkResponseToStream } from './spark';
6
6
  describe('SparkAIStream', () => {
7
7
  beforeAll(() => {});
8
8
 
9
+ it('should handle reasoning content in stream', async () => {
10
+ const data = [
11
+ {
12
+ id: 'test-id',
13
+ object: 'chat.completion.chunk',
14
+ created: 1734395014,
15
+ model: 'x1',
16
+ choices: [
17
+ {
18
+ delta: {
19
+ reasoning_content: 'Hello',
20
+ role: 'assistant',
21
+ },
22
+ index: 0,
23
+ finish_reason: null,
24
+ },
25
+ ],
26
+ },
27
+ {
28
+ id: 'test-id',
29
+ object: 'chat.completion.chunk',
30
+ created: 1734395014,
31
+ model: 'x1',
32
+ choices: [
33
+ {
34
+ delta: {
35
+ reasoning_content: ' World',
36
+ role: 'assistant',
37
+ },
38
+ index: 0,
39
+ finish_reason: null,
40
+ },
41
+ ],
42
+ },
43
+ ]
44
+
45
+ const mockSparkStream = new ReadableStream({
46
+ start(controller) {
47
+ data.forEach((chunk) => {
48
+ controller.enqueue(chunk);
49
+ });
50
+
51
+ controller.close();
52
+ },
53
+ });
54
+
55
+ const protocolStream = SparkAIStream(mockSparkStream);
56
+
57
+ const decoder = new TextDecoder();
58
+ const chunks = [];
59
+
60
+ // @ts-ignore
61
+ for await (const chunk of protocolStream) {
62
+ chunks.push(decoder.decode(chunk, { stream: true }));
63
+ }
64
+
65
+ expect(chunks).toEqual([
66
+ 'id: test-id\n',
67
+ 'event: reasoning\n',
68
+ 'data: "Hello"\n\n',
69
+ 'id: test-id\n',
70
+ 'event: reasoning\n',
71
+ 'data: " World"\n\n',
72
+ ]);
73
+ });
74
+
9
75
  it('should transform non-streaming response to stream', async () => {
10
76
  const mockResponse = {
11
77
  id: 'cha000ceba6@dx193d200b580b8f3532',
@@ -11,11 +11,15 @@ import {
11
11
  generateToolCallId,
12
12
  } from './protocol';
13
13
 
14
+ import { convertUsage } from '../usageConverter';
15
+
14
16
  export function transformSparkResponseToStream(data: OpenAI.ChatCompletion) {
15
17
  return new ReadableStream({
16
18
  start(controller) {
19
+ const choices = data?.choices || [];
20
+
17
21
  const chunk: OpenAI.ChatCompletionChunk = {
18
- choices: data.choices.map((choice: OpenAI.ChatCompletion.Choice) => {
22
+ choices: choices.map((choice: OpenAI.ChatCompletion.Choice) => {
19
23
  const toolCallsArray = choice.message.tool_calls
20
24
  ? Array.isArray(choice.message.tool_calls)
21
25
  ? choice.message.tool_calls
@@ -49,7 +53,7 @@ export function transformSparkResponseToStream(data: OpenAI.ChatCompletion) {
49
53
  controller.enqueue(chunk);
50
54
 
51
55
  controller.enqueue({
52
- choices: data.choices.map((choice: OpenAI.ChatCompletion.Choice) => ({
56
+ choices: choices.map((choice: OpenAI.ChatCompletion.Choice) => ({
53
57
  delta: {
54
58
  content: null,
55
59
  role: choice.message.role,
@@ -106,7 +110,27 @@ export const transformSparkStream = (chunk: OpenAI.ChatCompletionChunk): StreamP
106
110
  return { data: item.finish_reason, id: chunk.id, type: 'stop' };
107
111
  }
108
112
 
113
+ if (
114
+ item.delta &&
115
+ 'reasoning_content' in item.delta &&
116
+ typeof item.delta.reasoning_content === 'string' &&
117
+ item.delta.reasoning_content !== ''
118
+ ) {
119
+ return { data: item.delta.reasoning_content, id: chunk.id, type: 'reasoning' };
120
+ }
121
+
109
122
  if (typeof item.delta?.content === 'string') {
123
+ /*
124
+ 处理 v1 endpoint usage,混合在最后一个 content 内容中
125
+ {"code":0,"message":"Success","sid":"cha000d05ef@dx196553ae415b80a432","id":"cha000d05ef@dx196553ae415b80a432","created":1745186655,"choices":[{"delta":{"role":"assistant","content":"😊"},"index":0}],"usage":{"prompt_tokens":1,"completion_tokens":418,"total_tokens":419}}
126
+ */
127
+ if (chunk.usage) {
128
+ return [
129
+ { data: item.delta.content, id: chunk.id, type: 'text' },
130
+ { data: convertUsage(chunk.usage), id: chunk.id, type: 'usage' },
131
+ ] as any;
132
+ }
133
+
110
134
  return { data: item.delta.content, id: chunk.id, type: 'text' };
111
135
  }
112
136
 
@@ -114,6 +138,11 @@ export const transformSparkStream = (chunk: OpenAI.ChatCompletionChunk): StreamP
114
138
  return { data: item.delta, id: chunk.id, type: 'data' };
115
139
  }
116
140
 
141
+ // 处理 v2 endpoint usage
142
+ if (chunk.usage) {
143
+ return { data: convertUsage(chunk.usage), id: chunk.id, type: 'usage' };
144
+ }
145
+
117
146
  return {
118
147
  data: { delta: item.delta, id: chunk.id, index: item.index },
119
148
  id: chunk.id,
@@ -3,6 +3,24 @@ import { LobeOpenAICompatibleFactory } from '../utils/openaiCompatibleFactory';
3
3
 
4
4
  export const LobeVolcengineAI = LobeOpenAICompatibleFactory({
5
5
  baseURL: 'https://ark.cn-beijing.volces.com/api/v3',
6
+ chatCompletion: {
7
+ handlePayload: (payload) => {
8
+ const { model, thinking, ...rest } = payload;
9
+
10
+ return {
11
+ ...rest,
12
+ model,
13
+ ...(['thinking-vision-pro'].some((keyword) => model.toLowerCase().includes(keyword))
14
+ ? {
15
+ thinking:
16
+ thinking !== undefined && thinking.type === 'enabled'
17
+ ? { type: 'enabled' }
18
+ : { type: 'disabled' },
19
+ }
20
+ : {}),
21
+ } as any;
22
+ },
23
+ },
6
24
  debug: {
7
25
  chatCompletion: () => process.env.DEBUG_VOLCENGINE_CHAT_COMPLETION === '1',
8
26
  },