@lobehub/lobehub 2.0.0-next.226 → 2.0.0-next.227
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 +25 -0
- package/changelog/v1.json +9 -0
- package/docs/self-hosting/server-database/docker-compose.mdx +11 -0
- package/docs/self-hosting/server-database/docker-compose.zh-CN.mdx +11 -0
- package/package.json +1 -1
- package/packages/model-bank/src/aiModels/lobehub.ts +28 -0
- package/src/business/client/hooks/useBusinessErrorAlertConfig.ts +9 -0
- package/src/business/client/hooks/useBusinessErrorContent.ts +13 -0
- package/src/business/server/lambda-routers/file.ts +12 -0
- package/src/features/Conversation/Error/index.tsx +13 -3
- package/src/server/routers/lambda/__tests__/file.test.ts +7 -7
- package/src/server/routers/lambda/file.ts +12 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.0.0-next.227](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.226...v2.0.0-next.227)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2026-01-06**</sup>
|
|
8
|
+
|
|
9
|
+
#### 🐛 Bug Fixes
|
|
10
|
+
|
|
11
|
+
- **misc**: Allow zero-byte files and add business hooks for error handling.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's fixed
|
|
19
|
+
|
|
20
|
+
- **misc**: Allow zero-byte files and add business hooks for error handling, closes [#11283](https://github.com/lobehub/lobe-chat/issues/11283) ([38f5b78](https://github.com/lobehub/lobe-chat/commit/38f5b78))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
5
30
|
## [Version 2.0.0-next.226](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.225...v2.0.0-next.226)
|
|
6
31
|
|
|
7
32
|
<sup>Released on **2026-01-06**</sup>
|
package/changelog/v1.json
CHANGED
|
@@ -440,6 +440,17 @@ Solutions:
|
|
|
440
440
|
|
|
441
441
|
- A straightforward troubleshooting method is to use the `curl` command in the LobeChat container terminal to access your authentication service at `https://auth.example.com/.well-known/openid-configuration`. If JSON format data is returned, it indicates your authentication service is functioning correctly.
|
|
442
442
|
|
|
443
|
+
#### OAuth Token Exchange Failures with Reverse Proxy
|
|
444
|
+
|
|
445
|
+
If OAuth authentication fails during the token exchange phase when using Docker behind a reverse proxy, this is typically caused by the default `MIDDLEWARE_REWRITE_THROUGH_LOCAL=1` setting which rewrites URLs to `127.0.0.1:3210`.
|
|
446
|
+
|
|
447
|
+
**Solution**: Set `MIDDLEWARE_REWRITE_THROUGH_LOCAL=0` in your `.env` file and restart Docker containers:
|
|
448
|
+
|
|
449
|
+
```bash
|
|
450
|
+
docker compose down
|
|
451
|
+
docker compose up -d
|
|
452
|
+
```
|
|
453
|
+
|
|
443
454
|
````markdown
|
|
444
455
|
## Extended Configuration
|
|
445
456
|
|
|
@@ -421,6 +421,17 @@ lobe-chat | [auth][error] TypeError: fetch failed
|
|
|
421
421
|
|
|
422
422
|
- 一个直接的排查方式,你可以在 LobeChat 容器的终端中,使用 `curl` 命令访问你的鉴权服务 `https://auth.example.com/.well-known/openid-configuration`,如果返回了 JSON 格式的数据,则说明你的鉴权服务正常运行。
|
|
423
423
|
|
|
424
|
+
#### 反向代理下 OAuth 令牌交换失败
|
|
425
|
+
|
|
426
|
+
如果在反向代理后使用 Docker 时 OAuth 认证在令牌交换阶段失败,这通常是由默认的 `MIDDLEWARE_REWRITE_THROUGH_LOCAL=1` 设置引起的,该设置会将 URL 重写为 `127.0.0.1:3210`。
|
|
427
|
+
|
|
428
|
+
**解决方案**: 在 `.env` 文件中设置 `MIDDLEWARE_REWRITE_THROUGH_LOCAL=0` 并重启 Docker 容器:
|
|
429
|
+
|
|
430
|
+
```bash
|
|
431
|
+
docker compose down
|
|
432
|
+
docker compose up -d
|
|
433
|
+
```
|
|
434
|
+
|
|
424
435
|
## 拓展配置
|
|
425
436
|
|
|
426
437
|
为了完善你的 LobeChat 服务,你可以根据你的需求进行以下拓展配置。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/lobehub",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.227",
|
|
4
4
|
"description": "LobeHub - an open-source,comprehensive AI Agent 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",
|
|
@@ -860,6 +860,34 @@ const lobehubChatModels: AIChatModelCard[] = [
|
|
|
860
860
|
releasedAt: '2025-08-26',
|
|
861
861
|
type: 'chat',
|
|
862
862
|
},
|
|
863
|
+
{
|
|
864
|
+
abilities: {
|
|
865
|
+
functionCall: true,
|
|
866
|
+
reasoning: true,
|
|
867
|
+
search: true,
|
|
868
|
+
vision: true,
|
|
869
|
+
},
|
|
870
|
+
contextWindowTokens: 256_000,
|
|
871
|
+
description:
|
|
872
|
+
'Our newest and strongest flagship model, excelling in NLP, math, and reasoning—an ideal all-rounder.',
|
|
873
|
+
displayName: 'Grok 4',
|
|
874
|
+
enabled: true,
|
|
875
|
+
id: 'grok-4',
|
|
876
|
+
pricing: {
|
|
877
|
+
units: [
|
|
878
|
+
{ name: 'textInput_cacheRead', rate: 0.75, strategy: 'fixed', unit: 'millionTokens' },
|
|
879
|
+
{ name: 'textInput', rate: 3, strategy: 'fixed', unit: 'millionTokens' },
|
|
880
|
+
{ name: 'textOutput', rate: 15, strategy: 'fixed', unit: 'millionTokens' },
|
|
881
|
+
],
|
|
882
|
+
},
|
|
883
|
+
releasedAt: '2025-07-09',
|
|
884
|
+
settings: {
|
|
885
|
+
// reasoning_effort is not supported by grok-4. Specifying reasoning_effort parameter will get an error response.
|
|
886
|
+
// extendParams: ['reasoningEffort'],
|
|
887
|
+
searchImpl: 'params',
|
|
888
|
+
},
|
|
889
|
+
type: 'chat',
|
|
890
|
+
},
|
|
863
891
|
{
|
|
864
892
|
abilities: {
|
|
865
893
|
imageOutput: true,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ErrorType } from '@lobechat/types';
|
|
2
|
+
import type { AlertProps } from '@lobehub/ui';
|
|
3
|
+
|
|
4
|
+
export default function useBusinessErrorAlertConfig(
|
|
5
|
+
// eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars
|
|
6
|
+
errorType?: ErrorType,
|
|
7
|
+
): AlertProps | undefined {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ErrorType } from '@lobechat/types';
|
|
2
|
+
|
|
3
|
+
export interface BusinessErrorContentResult {
|
|
4
|
+
errorType?: string;
|
|
5
|
+
hideMessage?: boolean;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export default function useBusinessErrorContent(
|
|
9
|
+
// eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars
|
|
10
|
+
errorType?: ErrorType | string,
|
|
11
|
+
): BusinessErrorContentResult {
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface BusinessFileUploadCheckParams {
|
|
2
|
+
actualSize: number;
|
|
3
|
+
clientIp?: string;
|
|
4
|
+
inputSize: number;
|
|
5
|
+
url: string;
|
|
6
|
+
userId: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function businessFileUploadCheck(
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
11
|
+
params: BusinessFileUploadCheckParams,
|
|
12
|
+
): Promise<void> {}
|
|
@@ -7,6 +7,8 @@ import dynamic from 'next/dynamic';
|
|
|
7
7
|
import { memo, useMemo } from 'react';
|
|
8
8
|
import { useTranslation } from 'react-i18next';
|
|
9
9
|
|
|
10
|
+
import useBusinessErrorAlertConfig from '@/business/client/hooks/useBusinessErrorAlertConfig';
|
|
11
|
+
import useBusinessErrorContent from '@/business/client/hooks/useBusinessErrorContent';
|
|
10
12
|
import useRenderBusinessChatErrorMessageExtra from '@/business/client/hooks/useRenderBusinessChatErrorMessageExtra';
|
|
11
13
|
import ErrorContent from '@/features/Conversation/ChatItem/components/ErrorContent';
|
|
12
14
|
import { useProviderName } from '@/hooks/useProviderName';
|
|
@@ -85,18 +87,26 @@ const getErrorAlertConfig = (
|
|
|
85
87
|
export const useErrorContent = (error: any) => {
|
|
86
88
|
const { t } = useTranslation('error');
|
|
87
89
|
const providerName = useProviderName(error?.body?.provider || '');
|
|
90
|
+
const businessAlertConfig = useBusinessErrorAlertConfig(error?.type);
|
|
91
|
+
const { errorType: businessErrorType, hideMessage } = useBusinessErrorContent(error?.type);
|
|
88
92
|
|
|
89
93
|
return useMemo<AlertProps | undefined>(() => {
|
|
90
94
|
if (!error) return;
|
|
91
95
|
const messageError = error;
|
|
92
96
|
|
|
93
|
-
|
|
97
|
+
// Use business alert config if provided, otherwise fall back to default
|
|
98
|
+
const alertConfig = businessAlertConfig ?? getErrorAlertConfig(messageError.type);
|
|
99
|
+
|
|
100
|
+
// Use business error type if provided, otherwise use original
|
|
101
|
+
const finalErrorType = businessErrorType ?? messageError.type;
|
|
94
102
|
|
|
95
103
|
return {
|
|
96
|
-
message:
|
|
104
|
+
message: hideMessage
|
|
105
|
+
? undefined
|
|
106
|
+
: t(`response.${finalErrorType}` as any, { provider: providerName }),
|
|
97
107
|
...alertConfig,
|
|
98
108
|
};
|
|
99
|
-
}, [error]);
|
|
109
|
+
}, [businessAlertConfig, businessErrorType, error, hideMessage, providerName, t]);
|
|
100
110
|
};
|
|
101
111
|
|
|
102
112
|
interface ErrorExtraProps {
|
|
@@ -273,7 +273,7 @@ describe('fileRouter', () => {
|
|
|
273
273
|
);
|
|
274
274
|
});
|
|
275
275
|
|
|
276
|
-
it('should throw error when getFileMetadata fails and input size is
|
|
276
|
+
it('should throw error when getFileMetadata fails and input size is negative', async () => {
|
|
277
277
|
mockFileModelCheckHash.mockResolvedValue({ isExist: false });
|
|
278
278
|
mockFileServiceGetFileMetadata.mockRejectedValue(new Error('File not found in S3'));
|
|
279
279
|
|
|
@@ -282,11 +282,11 @@ describe('fileRouter', () => {
|
|
|
282
282
|
hash: 'test-hash',
|
|
283
283
|
fileType: 'text',
|
|
284
284
|
name: 'test.txt',
|
|
285
|
-
size:
|
|
285
|
+
size: -1,
|
|
286
286
|
url: 'files/non-existent.txt',
|
|
287
287
|
metadata: {},
|
|
288
288
|
}),
|
|
289
|
-
).rejects.toThrow('File size
|
|
289
|
+
).rejects.toThrow('File size cannot be negative');
|
|
290
290
|
});
|
|
291
291
|
|
|
292
292
|
it('should use input size when getFileMetadata returns contentLength less than 1', async () => {
|
|
@@ -315,10 +315,10 @@ describe('fileRouter', () => {
|
|
|
315
315
|
);
|
|
316
316
|
});
|
|
317
317
|
|
|
318
|
-
it('should throw error when both getFileMetadata contentLength and input size are
|
|
318
|
+
it('should throw error when both getFileMetadata contentLength and input size are negative', async () => {
|
|
319
319
|
mockFileModelCheckHash.mockResolvedValue({ isExist: false });
|
|
320
320
|
mockFileServiceGetFileMetadata.mockResolvedValue({
|
|
321
|
-
contentLength:
|
|
321
|
+
contentLength: -1,
|
|
322
322
|
contentType: 'text/plain',
|
|
323
323
|
});
|
|
324
324
|
|
|
@@ -327,11 +327,11 @@ describe('fileRouter', () => {
|
|
|
327
327
|
hash: 'test-hash',
|
|
328
328
|
fileType: 'text',
|
|
329
329
|
name: 'test.txt',
|
|
330
|
-
size:
|
|
330
|
+
size: -1,
|
|
331
331
|
url: 'files/test.txt',
|
|
332
332
|
metadata: {},
|
|
333
333
|
}),
|
|
334
|
-
).rejects.toThrow('File size
|
|
334
|
+
).rejects.toThrow('File size cannot be negative');
|
|
335
335
|
});
|
|
336
336
|
});
|
|
337
337
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { TRPCError } from '@trpc/server';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
|
+
import { businessFileUploadCheck } from '@/business/server/lambda-routers/file';
|
|
4
5
|
import { checkFileStorageUsage } from '@/business/server/trpc-middlewares/lambda';
|
|
5
6
|
import { serverDBEnv } from '@/config/db';
|
|
6
7
|
import { AsyncTaskModel } from '@/database/models/asyncTask';
|
|
@@ -74,8 +75,16 @@ export const fileRouter = router({
|
|
|
74
75
|
// If metadata fetch fails, use original size from input
|
|
75
76
|
}
|
|
76
77
|
|
|
77
|
-
|
|
78
|
-
|
|
78
|
+
await businessFileUploadCheck({
|
|
79
|
+
actualSize,
|
|
80
|
+
clientIp: ctx.clientIp ?? undefined,
|
|
81
|
+
inputSize: input.size,
|
|
82
|
+
url: input.url,
|
|
83
|
+
userId: ctx.userId,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
if (actualSize < 0) {
|
|
87
|
+
throw new TRPCError({ code: 'BAD_REQUEST', message: 'File size cannot be negative' });
|
|
79
88
|
}
|
|
80
89
|
|
|
81
90
|
const { id } = await ctx.fileModel.create(
|
|
@@ -367,7 +376,7 @@ export const fileRouter = router({
|
|
|
367
376
|
|
|
368
377
|
if (!file) return;
|
|
369
378
|
|
|
370
|
-
//
|
|
379
|
+
// delete the file from S3 if it is not used by other files
|
|
371
380
|
await ctx.fileService.deleteFile(file.url!);
|
|
372
381
|
}),
|
|
373
382
|
|