@lobehub/chat 1.92.2 → 1.92.3
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 +33 -0
- package/changelog/v1.json +12 -0
- package/package.json +2 -2
- package/src/config/aiModels/openrouter.ts +44 -0
- package/src/libs/model-runtime/openrouter/index.ts +55 -43
- package/src/services/file/ClientS3/index.test.ts +8 -8
- package/src/services/file/ClientS3/index.ts +2 -1
package/CHANGELOG.md
CHANGED
@@ -2,6 +2,39 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
### [Version 1.92.3](https://github.com/lobehub/lobe-chat/compare/v1.92.2...v1.92.3)
|
6
|
+
|
7
|
+
<sup>Released on **2025-06-08**</sup>
|
8
|
+
|
9
|
+
#### 🐛 Bug Fixes
|
10
|
+
|
11
|
+
- **misc**: Fix client s3 getObject throw error.
|
12
|
+
|
13
|
+
#### 💄 Styles
|
14
|
+
|
15
|
+
- **misc**: Support OpenRouter Claude 4 reasoning.
|
16
|
+
|
17
|
+
<br/>
|
18
|
+
|
19
|
+
<details>
|
20
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
21
|
+
|
22
|
+
#### What's fixed
|
23
|
+
|
24
|
+
- **misc**: Fix client s3 getObject throw error, closes [#8009](https://github.com/lobehub/lobe-chat/issues/8009) ([b91ca8c](https://github.com/lobehub/lobe-chat/commit/b91ca8c))
|
25
|
+
|
26
|
+
#### Styles
|
27
|
+
|
28
|
+
- **misc**: Support OpenRouter Claude 4 reasoning, closes [#8087](https://github.com/lobehub/lobe-chat/issues/8087) ([039be1d](https://github.com/lobehub/lobe-chat/commit/039be1d))
|
29
|
+
|
30
|
+
</details>
|
31
|
+
|
32
|
+
<div align="right">
|
33
|
+
|
34
|
+
[](#readme-top)
|
35
|
+
|
36
|
+
</div>
|
37
|
+
|
5
38
|
### [Version 1.92.2](https://github.com/lobehub/lobe-chat/compare/v1.92.1...v1.92.2)
|
6
39
|
|
7
40
|
<sup>Released on **2025-06-07**</sup>
|
package/changelog/v1.json
CHANGED
@@ -1,4 +1,16 @@
|
|
1
1
|
[
|
2
|
+
{
|
3
|
+
"children": {
|
4
|
+
"fixes": [
|
5
|
+
"Fix client s3 getObject throw error."
|
6
|
+
],
|
7
|
+
"improvements": [
|
8
|
+
"Support OpenRouter Claude 4 reasoning."
|
9
|
+
]
|
10
|
+
},
|
11
|
+
"date": "2025-06-08",
|
12
|
+
"version": "1.92.3"
|
13
|
+
},
|
2
14
|
{
|
3
15
|
"children": {
|
4
16
|
"improvements": [
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.92.
|
3
|
+
"version": "1.92.3",
|
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",
|
@@ -121,7 +121,7 @@
|
|
121
121
|
"dependencies": {
|
122
122
|
"@ant-design/icons": "^5.6.1",
|
123
123
|
"@ant-design/pro-components": "^2.8.7",
|
124
|
-
"@anthropic-ai/sdk": "^0.
|
124
|
+
"@anthropic-ai/sdk": "^0.53.0",
|
125
125
|
"@auth/core": "^0.38.0",
|
126
126
|
"@aws-sdk/client-bedrock-runtime": "^3.821.0",
|
127
127
|
"@aws-sdk/client-s3": "^3.821.0",
|
@@ -660,6 +660,50 @@ const openrouterChatModels: AIChatModelCard[] = [
|
|
660
660
|
},
|
661
661
|
type: 'chat',
|
662
662
|
},
|
663
|
+
{
|
664
|
+
abilities: {
|
665
|
+
functionCall: true,
|
666
|
+
reasoning: true,
|
667
|
+
vision: true,
|
668
|
+
},
|
669
|
+
contextWindowTokens: 200_000,
|
670
|
+
description:
|
671
|
+
'Claude Sonnet 4 可以产生近乎即时的响应或延长的逐步思考,用户可以清晰地看到这些过程。API 用户还可以对模型思考的时间进行细致的控制',
|
672
|
+
displayName: 'Claude Sonnet 4',
|
673
|
+
id: 'anthropic/claude-sonnet-4',
|
674
|
+
maxOutput: 64_000,
|
675
|
+
pricing: {
|
676
|
+
input: 3,
|
677
|
+
output: 15,
|
678
|
+
},
|
679
|
+
releasedAt: '2025-05-23',
|
680
|
+
settings: {
|
681
|
+
extendParams: ['enableReasoning', 'reasoningBudgetToken'],
|
682
|
+
},
|
683
|
+
type: 'chat',
|
684
|
+
},
|
685
|
+
{
|
686
|
+
abilities: {
|
687
|
+
functionCall: true,
|
688
|
+
reasoning: true,
|
689
|
+
vision: true,
|
690
|
+
},
|
691
|
+
contextWindowTokens: 200_000,
|
692
|
+
description:
|
693
|
+
'Claude Opus 4 是 Anthropic 用于处理高度复杂任务的最强大模型。它在性能、智能、流畅性和理解力方面表现卓越。',
|
694
|
+
displayName: 'Claude Opus 4',
|
695
|
+
id: 'anthropic/claude-opus-4',
|
696
|
+
maxOutput: 32_000,
|
697
|
+
pricing: {
|
698
|
+
input: 15,
|
699
|
+
output: 75,
|
700
|
+
},
|
701
|
+
releasedAt: '2025-05-23',
|
702
|
+
settings: {
|
703
|
+
extendParams: ['enableReasoning', 'reasoningBudgetToken'],
|
704
|
+
},
|
705
|
+
type: 'chat',
|
706
|
+
},
|
663
707
|
{
|
664
708
|
abilities: {
|
665
709
|
functionCall: true,
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import OpenRouterModels from '@/config/aiModels/openrouter';
|
1
2
|
import type { ChatModelCard } from '@/types/llm';
|
2
3
|
|
3
4
|
import { ModelProvider } from '../types';
|
@@ -14,12 +15,27 @@ export const LobeOpenRouterAI = createOpenAICompatibleRuntime({
|
|
14
15
|
baseURL: 'https://openrouter.ai/api/v1',
|
15
16
|
chatCompletion: {
|
16
17
|
handlePayload: (payload) => {
|
17
|
-
const { thinking } = payload;
|
18
|
+
const { thinking, model, max_tokens } = payload;
|
18
19
|
|
19
20
|
let reasoning: OpenRouterReasoning = {};
|
21
|
+
|
20
22
|
if (thinking?.type === 'enabled') {
|
23
|
+
const modelConfig = OpenRouterModels.find((m) => m.id === model);
|
24
|
+
const defaultMaxOutput = modelConfig?.maxOutput;
|
25
|
+
|
26
|
+
// 配置优先级:用户设置 > 模型配置 > 硬编码默认值
|
27
|
+
const getMaxTokens = () => {
|
28
|
+
if (max_tokens) return max_tokens;
|
29
|
+
if (defaultMaxOutput) return defaultMaxOutput;
|
30
|
+
return undefined;
|
31
|
+
};
|
32
|
+
|
33
|
+
const maxTokens = getMaxTokens() || 32_000; // Claude Opus 4 has minimum maxOutput
|
34
|
+
|
21
35
|
reasoning = {
|
22
|
-
max_tokens: thinking
|
36
|
+
max_tokens: thinking?.budget_tokens
|
37
|
+
? Math.min(thinking.budget_tokens, maxTokens - 1)
|
38
|
+
: 1024,
|
23
39
|
};
|
24
40
|
}
|
25
41
|
|
@@ -43,7 +59,7 @@ export const LobeOpenRouterAI = createOpenAICompatibleRuntime({
|
|
43
59
|
models: async ({ client }) => {
|
44
60
|
const modelsPage = (await client.models.list()) as any;
|
45
61
|
const modelList: OpenRouterModelCard[] = modelsPage.data;
|
46
|
-
|
62
|
+
|
47
63
|
const modelsExtraInfo: OpenRouterModelExtraInfo[] = [];
|
48
64
|
try {
|
49
65
|
const response = await fetch('https://openrouter.ai/api/frontend/models');
|
@@ -54,49 +70,45 @@ export const LobeOpenRouterAI = createOpenAICompatibleRuntime({
|
|
54
70
|
} catch (error) {
|
55
71
|
console.error('Failed to fetch OpenRouter frontend models:', error);
|
56
72
|
}
|
57
|
-
|
73
|
+
|
58
74
|
// 解析模型能力
|
59
75
|
const baseModels = await processMultiProviderModelList(modelList);
|
60
|
-
|
76
|
+
|
61
77
|
// 合并 OpenRouter 获取的模型信息
|
62
|
-
return baseModels
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
baseModel.reasoning ||
|
91
|
-
|
92
|
-
false,
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
model.architecture.modality.includes('image') ||
|
97
|
-
false,
|
98
|
-
};
|
99
|
-
}).filter(Boolean) as ChatModelCard[];
|
78
|
+
return baseModels
|
79
|
+
.map((baseModel) => {
|
80
|
+
const model = modelList.find((m) => m.id === baseModel.id);
|
81
|
+
const extraInfo = modelsExtraInfo.find(
|
82
|
+
(m) => m.slug.toLowerCase() === baseModel.id.toLowerCase(),
|
83
|
+
);
|
84
|
+
|
85
|
+
if (!model) return baseModel;
|
86
|
+
|
87
|
+
return {
|
88
|
+
...baseModel,
|
89
|
+
contextWindowTokens: model.context_length,
|
90
|
+
description: model.description,
|
91
|
+
displayName: model.name,
|
92
|
+
functionCall:
|
93
|
+
baseModel.functionCall ||
|
94
|
+
model.description.includes('function calling') ||
|
95
|
+
model.description.includes('tools') ||
|
96
|
+
extraInfo?.endpoint?.supports_tool_parameters ||
|
97
|
+
false,
|
98
|
+
maxTokens:
|
99
|
+
typeof model.top_provider.max_completion_tokens === 'number'
|
100
|
+
? model.top_provider.max_completion_tokens
|
101
|
+
: undefined,
|
102
|
+
pricing: {
|
103
|
+
input: formatPrice(model.pricing.prompt),
|
104
|
+
output: formatPrice(model.pricing.completion),
|
105
|
+
},
|
106
|
+
reasoning: baseModel.reasoning || extraInfo?.endpoint?.supports_reasoning || false,
|
107
|
+
releasedAt: new Date(model.created * 1000).toISOString().split('T')[0],
|
108
|
+
vision: baseModel.vision || model.architecture.modality.includes('image') || false,
|
109
|
+
};
|
110
|
+
})
|
111
|
+
.filter(Boolean) as ChatModelCard[];
|
100
112
|
},
|
101
113
|
provider: ModelProvider.OpenRouter,
|
102
114
|
});
|
@@ -84,14 +84,14 @@ describe('BrowserS3Storage', () => {
|
|
84
84
|
expect(result).toBeUndefined();
|
85
85
|
});
|
86
86
|
|
87
|
-
it('should throw error when get operation fails', async () => {
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
});
|
87
|
+
// it('should throw error when get operation fails', async () => {
|
88
|
+
// const mockError = new Error('Storage error');
|
89
|
+
// (get as any).mockRejectedValue(mockError);
|
90
|
+
//
|
91
|
+
// await expect(storage.getObject('test-key')).rejects.toThrow(
|
92
|
+
// 'Failed to get object (key=test-key): Storage error',
|
93
|
+
// );
|
94
|
+
// });
|
95
95
|
});
|
96
96
|
|
97
97
|
describe('deleteObject', () => {
|
@@ -38,7 +38,8 @@ export class BrowserS3Storage {
|
|
38
38
|
|
39
39
|
return new File([res.data], res!.name, { type: res?.type });
|
40
40
|
} catch (e) {
|
41
|
-
|
41
|
+
console.log(`Failed to get object (key=${key}):`, e);
|
42
|
+
return undefined;
|
42
43
|
}
|
43
44
|
};
|
44
45
|
|