@lobehub/chat 1.108.1 → 1.108.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/.cursor/rules/testing-guide/testing-guide.mdc +18 -0
- package/CHANGELOG.md +25 -0
- package/changelog/v1.json +9 -0
- package/package.json +2 -2
- package/src/app/[variants]/(main)/settings/provider/features/ProviderConfig/Checker.tsx +15 -2
- package/src/app/[variants]/(main)/settings/provider/features/ProviderConfig/index.tsx +30 -3
- package/src/components/Analytics/LobeAnalyticsProvider.tsx +10 -13
- package/src/components/Analytics/LobeAnalyticsProviderWrapper.tsx +16 -4
- package/src/libs/model-runtime/RouterRuntime/createRuntime.test.ts +538 -0
- package/src/libs/model-runtime/RouterRuntime/createRuntime.ts +50 -13
- package/src/libs/model-runtime/RouterRuntime/index.ts +1 -1
- package/src/libs/model-runtime/aihubmix/index.ts +10 -5
- package/src/libs/model-runtime/ppio/index.test.ts +3 -6
- package/src/libs/model-runtime/utils/openaiCompatibleFactory/index.ts +8 -6
- package/src/server/globalConfig/genServerAiProviderConfig.test.ts +22 -25
- package/src/server/globalConfig/genServerAiProviderConfig.ts +34 -22
- package/src/server/globalConfig/index.ts +1 -1
- package/src/server/services/discover/index.ts +11 -2
- package/src/services/chat.ts +1 -1
- package/src/store/aiInfra/slices/aiProvider/__tests__/action.test.ts +211 -0
- package/src/store/aiInfra/slices/aiProvider/action.ts +46 -35
- package/src/store/user/slices/modelList/action.test.ts +5 -5
- package/src/store/user/slices/modelList/action.ts +4 -4
- package/src/utils/getFallbackModelProperty.test.ts +52 -45
- package/src/utils/getFallbackModelProperty.ts +4 -3
- package/src/utils/parseModels.test.ts +107 -98
- package/src/utils/parseModels.ts +10 -8
@@ -430,6 +430,22 @@ expect(result!.status).toBe('success');
|
|
430
430
|
// ✅ 使用 any 类型简化复杂的 Mock 设置
|
431
431
|
const mockStream = new ReadableStream() as any;
|
432
432
|
mockStream.toReadableStream = () => mockStream;
|
433
|
+
|
434
|
+
// ✅ 使用中括号访问私有属性和方法(推荐)
|
435
|
+
class MyClass {
|
436
|
+
private _cache = new Map();
|
437
|
+
private getFromCache(key: string) { /* ... */ }
|
438
|
+
}
|
439
|
+
|
440
|
+
const instance = new MyClass();
|
441
|
+
|
442
|
+
// 推荐:使用中括号访问私有成员
|
443
|
+
await instance['getFromCache']('test-key');
|
444
|
+
expect(instance['_cache'].size).toBe(1);
|
445
|
+
|
446
|
+
// 避免:使用 as any 访问私有成员
|
447
|
+
await (instance as any).getFromCache('test-key'); // ❌ 不推荐
|
448
|
+
expect((instance as any)._cache.size).toBe(1); // ❌ 不推荐
|
433
449
|
```
|
434
450
|
|
435
451
|
#### 🎯 适用场景
|
@@ -437,11 +453,13 @@ mockStream.toReadableStream = () => mockStream;
|
|
437
453
|
- **Mock 对象**: 对于测试用的 Mock 数据,使用 `as any` 避免复杂的类型定义
|
438
454
|
- **第三方库**: 处理复杂的第三方库类型时,适当使用 `any` 提高效率
|
439
455
|
- **测试断言**: 在确定对象存在的测试场景中,使用 `!` 非空断言
|
456
|
+
- **私有成员访问**: 优先使用中括号 `instance['privateMethod']()` 而不是 `(instance as any).privateMethod()`
|
440
457
|
- **临时调试**: 快速编写测试时,先用 `any` 保证功能,后续可选择性地优化类型
|
441
458
|
|
442
459
|
#### ⚠️ 注意事项
|
443
460
|
|
444
461
|
- **适度使用**: 不要过度依赖 `any`,核心业务逻辑的类型仍应保持严格
|
462
|
+
- **私有成员访问优先级**: 中括号访问 > `as any` 转换,保持更好的类型安全性
|
445
463
|
- **文档说明**: 对于使用 `any` 的复杂场景,添加注释说明原因
|
446
464
|
- **测试覆盖**: 确保即使使用了 `any`,测试仍能有效验证功能正确性
|
447
465
|
|
package/CHANGELOG.md
CHANGED
@@ -2,6 +2,31 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
### [Version 1.108.2](https://github.com/lobehub/lobe-chat/compare/v1.108.1...v1.108.2)
|
6
|
+
|
7
|
+
<sup>Released on **2025-08-05**</sup>
|
8
|
+
|
9
|
+
#### 🐛 Bug Fixes
|
10
|
+
|
11
|
+
- **misc**: Provider config checker uses outdated API key.
|
12
|
+
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<details>
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
17
|
+
|
18
|
+
#### What's fixed
|
19
|
+
|
20
|
+
- **misc**: Provider config checker uses outdated API key, closes [#8666](https://github.com/lobehub/lobe-chat/issues/8666) ([3a3e73e](https://github.com/lobehub/lobe-chat/commit/3a3e73e))
|
21
|
+
|
22
|
+
</details>
|
23
|
+
|
24
|
+
<div align="right">
|
25
|
+
|
26
|
+
[](#readme-top)
|
27
|
+
|
28
|
+
</div>
|
29
|
+
|
5
30
|
### [Version 1.108.1](https://github.com/lobehub/lobe-chat/compare/v1.108.0...v1.108.1)
|
6
31
|
|
7
32
|
<sup>Released on **2025-08-05**</sup>
|
package/changelog/v1.json
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.108.
|
3
|
+
"version": "1.108.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",
|
@@ -146,7 +146,7 @@
|
|
146
146
|
"@lobechat/electron-server-ipc": "workspace:*",
|
147
147
|
"@lobechat/file-loaders": "workspace:*",
|
148
148
|
"@lobechat/web-crawler": "workspace:*",
|
149
|
-
"@lobehub/analytics": "^1.
|
149
|
+
"@lobehub/analytics": "^1.6.0",
|
150
150
|
"@lobehub/charts": "^2.0.0",
|
151
151
|
"@lobehub/chat-plugin-sdk": "^1.32.4",
|
152
152
|
"@lobehub/chat-plugins-gateway": "^1.9.0",
|
@@ -46,11 +46,13 @@ export type CheckErrorRender = (props: {
|
|
46
46
|
interface ConnectionCheckerProps {
|
47
47
|
checkErrorRender?: CheckErrorRender;
|
48
48
|
model: string;
|
49
|
+
onAfterCheck: () => Promise<void>;
|
50
|
+
onBeforeCheck: () => Promise<void>;
|
49
51
|
provider: string;
|
50
52
|
}
|
51
53
|
|
52
54
|
const Checker = memo<ConnectionCheckerProps>(
|
53
|
-
({ model, provider, checkErrorRender: CheckErrorRender }) => {
|
55
|
+
({ model, provider, checkErrorRender: CheckErrorRender, onBeforeCheck, onAfterCheck }) => {
|
54
56
|
const { t } = useTranslation('setting');
|
55
57
|
|
56
58
|
const isProviderConfigUpdating = useAiInfraStore(
|
@@ -152,7 +154,18 @@ const Checker = memo<ConnectionCheckerProps>(
|
|
152
154
|
value={checkModel}
|
153
155
|
virtual
|
154
156
|
/>
|
155
|
-
<Button
|
157
|
+
<Button
|
158
|
+
disabled={isProviderConfigUpdating}
|
159
|
+
loading={loading}
|
160
|
+
onClick={async () => {
|
161
|
+
await onBeforeCheck();
|
162
|
+
try {
|
163
|
+
await checkConnection();
|
164
|
+
} finally {
|
165
|
+
await onAfterCheck();
|
166
|
+
}
|
167
|
+
}}
|
168
|
+
>
|
156
169
|
{t('llm.checker.button')}
|
157
170
|
</Button>
|
158
171
|
</Flexbox>
|
@@ -14,7 +14,7 @@ import { Skeleton, Switch } from 'antd';
|
|
14
14
|
import { createStyles } from 'antd-style';
|
15
15
|
import { Loader2Icon, LockIcon } from 'lucide-react';
|
16
16
|
import Link from 'next/link';
|
17
|
-
import { ReactNode, memo, useLayoutEffect } from 'react';
|
17
|
+
import { ReactNode, memo, useCallback, useLayoutEffect, useRef } from 'react';
|
18
18
|
import { Trans, useTranslation } from 'react-i18next';
|
19
19
|
import { Center, Flexbox } from 'react-layout-kit';
|
20
20
|
import urlJoin from 'url-join';
|
@@ -173,7 +173,24 @@ const ProviderConfig = memo<ProviderConfigProps>(
|
|
173
173
|
form.setFieldsValue(data);
|
174
174
|
}, [isLoading, id, data]);
|
175
175
|
|
176
|
-
|
176
|
+
// 标记是否正在进行连接测试
|
177
|
+
const isCheckingConnection = useRef(false);
|
178
|
+
|
179
|
+
const handleValueChange = useCallback(
|
180
|
+
(...params: Parameters<typeof updateAiProviderConfig>) => {
|
181
|
+
// 虽然 debouncedHandleValueChange 早于 onBeforeCheck 执行,
|
182
|
+
// 但是由于 debouncedHandleValueChange 因为 debounce 的原因,本来就会晚 500ms 执行
|
183
|
+
// 所以 isCheckingConnection.current 这时候已经更新了
|
184
|
+
// 测试链接时已经出发一次了 updateAiProviderConfig , 不应该重复更新
|
185
|
+
if (isCheckingConnection.current) return;
|
186
|
+
|
187
|
+
updateAiProviderConfig(...params);
|
188
|
+
},
|
189
|
+
[updateAiProviderConfig],
|
190
|
+
);
|
191
|
+
const { run: debouncedHandleValueChange } = useDebounceFn(handleValueChange, {
|
192
|
+
wait: 500,
|
193
|
+
});
|
177
194
|
|
178
195
|
const isCustom = source === AiProviderSourceEnum.Custom;
|
179
196
|
|
@@ -318,6 +335,16 @@ const ProviderConfig = memo<ProviderConfigProps>(
|
|
318
335
|
<Checker
|
319
336
|
checkErrorRender={checkErrorRender}
|
320
337
|
model={data?.checkModel || checkModel!}
|
338
|
+
onAfterCheck={async () => {
|
339
|
+
// 重置连接测试状态,允许后续的 onValuesChange 更新
|
340
|
+
isCheckingConnection.current = false;
|
341
|
+
}}
|
342
|
+
onBeforeCheck={async () => {
|
343
|
+
// 设置连接测试状态,阻止 onValuesChange 的重复请求
|
344
|
+
isCheckingConnection.current = true;
|
345
|
+
// 主动保存表单最新值,确保 fetchAiProviderRuntimeState 获取最新数据
|
346
|
+
await updateAiProviderConfig(id, form.getFieldsValue());
|
347
|
+
}}
|
321
348
|
provider={id}
|
322
349
|
/>
|
323
350
|
),
|
@@ -389,7 +416,7 @@ const ProviderConfig = memo<ProviderConfigProps>(
|
|
389
416
|
form={form}
|
390
417
|
items={[model]}
|
391
418
|
onValuesChange={(_, values) => {
|
392
|
-
|
419
|
+
debouncedHandleValueChange(id, values);
|
393
420
|
}}
|
394
421
|
variant={'borderless'}
|
395
422
|
{...FORM_STYLE}
|
@@ -1,6 +1,10 @@
|
|
1
1
|
'use client';
|
2
2
|
|
3
|
-
import {
|
3
|
+
import {
|
4
|
+
GoogleAnalyticsProviderConfig,
|
5
|
+
PostHogProviderAnalyticsConfig,
|
6
|
+
createSingletonAnalytics,
|
7
|
+
} from '@lobehub/analytics';
|
4
8
|
import { AnalyticsProvider } from '@lobehub/analytics/react';
|
5
9
|
import { ReactNode, memo, useMemo } from 'react';
|
6
10
|
|
@@ -10,16 +14,14 @@ import { isDev } from '@/utils/env';
|
|
10
14
|
|
11
15
|
type Props = {
|
12
16
|
children: ReactNode;
|
13
|
-
|
14
|
-
|
15
|
-
posthogHost: string;
|
16
|
-
posthogToken: string;
|
17
|
+
ga4Config: GoogleAnalyticsProviderConfig;
|
18
|
+
postHogConfig: PostHogProviderAnalyticsConfig;
|
17
19
|
};
|
18
20
|
|
19
21
|
let analyticsInstance: ReturnType<typeof createSingletonAnalytics> | null = null;
|
20
22
|
|
21
23
|
export const LobeAnalyticsProvider = memo(
|
22
|
-
({ children,
|
24
|
+
({ children, ga4Config, postHogConfig }: Props) => {
|
23
25
|
const analytics = useMemo(() => {
|
24
26
|
if (analyticsInstance) {
|
25
27
|
return analyticsInstance;
|
@@ -29,13 +31,8 @@ export const LobeAnalyticsProvider = memo(
|
|
29
31
|
business: BUSINESS_LINE,
|
30
32
|
debug: isDev,
|
31
33
|
providers: {
|
32
|
-
|
33
|
-
|
34
|
-
enabled: posthogEnabled,
|
35
|
-
host: posthogHost,
|
36
|
-
key: posthogToken,
|
37
|
-
person_profiles: 'always',
|
38
|
-
},
|
34
|
+
ga4: ga4Config,
|
35
|
+
posthog: postHogConfig,
|
39
36
|
},
|
40
37
|
});
|
41
38
|
|
@@ -2,6 +2,7 @@ import { ReactNode, memo } from 'react';
|
|
2
2
|
|
3
3
|
import { LobeAnalyticsProvider } from '@/components/Analytics/LobeAnalyticsProvider';
|
4
4
|
import { analyticsEnv } from '@/config/analytics';
|
5
|
+
import { isDev } from '@/utils/env';
|
5
6
|
|
6
7
|
type Props = {
|
7
8
|
children: ReactNode;
|
@@ -10,10 +11,21 @@ type Props = {
|
|
10
11
|
export const LobeAnalyticsProviderWrapper = memo<Props>(({ children }) => {
|
11
12
|
return (
|
12
13
|
<LobeAnalyticsProvider
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
ga4Config={{
|
15
|
+
debug: isDev,
|
16
|
+
enabled: analyticsEnv.ENABLE_GOOGLE_ANALYTICS,
|
17
|
+
gtagConfig: {
|
18
|
+
debug_mode: isDev,
|
19
|
+
},
|
20
|
+
measurementId: analyticsEnv.GOOGLE_ANALYTICS_MEASUREMENT_ID ?? '',
|
21
|
+
}}
|
22
|
+
postHogConfig={{
|
23
|
+
debug: analyticsEnv.DEBUG_POSTHOG_ANALYTICS,
|
24
|
+
enabled: analyticsEnv.ENABLED_POSTHOG_ANALYTICS,
|
25
|
+
host: analyticsEnv.POSTHOG_HOST,
|
26
|
+
key: analyticsEnv.POSTHOG_KEY ?? '',
|
27
|
+
person_profiles: 'always',
|
28
|
+
}}
|
17
29
|
>
|
18
30
|
{children}
|
19
31
|
</LobeAnalyticsProvider>
|