@lobehub/chat 1.47.3 → 1.47.5

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,48 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 1.47.5](https://github.com/lobehub/lobe-chat/compare/v1.47.4...v1.47.5)
6
+
7
+ <sup>Released on **2025-01-20**</sup>
8
+
9
+ #### ♻ Code Refactoring
10
+
11
+ - **misc**: Improve ai provider code.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### Code refactoring
19
+
20
+ - **misc**: Improve ai provider code, closes [#5514](https://github.com/lobehub/lobe-chat/issues/5514) ([92789cd](https://github.com/lobehub/lobe-chat/commit/92789cd))
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.47.4](https://github.com/lobehub/lobe-chat/compare/v1.47.3...v1.47.4)
31
+
32
+ <sup>Released on **2025-01-18**</sup>
33
+
34
+ <br/>
35
+
36
+ <details>
37
+ <summary><kbd>Improvements and Fixes</kbd></summary>
38
+
39
+ </details>
40
+
41
+ <div align="right">
42
+
43
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
44
+
45
+ </div>
46
+
5
47
  ### [Version 1.47.3](https://github.com/lobehub/lobe-chat/compare/v1.47.2...v1.47.3)
6
48
 
7
49
  <sup>Released on **2025-01-18**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,18 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "improvements": [
5
+ "Improve ai provider code."
6
+ ]
7
+ },
8
+ "date": "2025-01-20",
9
+ "version": "1.47.5"
10
+ },
11
+ {
12
+ "children": {},
13
+ "date": "2025-01-18",
14
+ "version": "1.47.4"
15
+ },
2
16
  {
3
17
  "children": {
4
18
  "fixes": [
package/next.config.ts CHANGED
@@ -165,8 +165,8 @@ const nextConfig: NextConfig = {
165
165
  source: '/welcome',
166
166
  },
167
167
  ],
168
- serverExternalPackages: ['@electric-sql/pglite', 'sharp'],
169
-
168
+ // when external packages in dev mode with turbopack, this config will lead to bundle error
169
+ serverExternalPackages: isProd ? ['@electric-sql/pglite'] : undefined,
170
170
  transpilePackages: ['pdfjs-dist', 'mermaid'],
171
171
 
172
172
  webpack(config) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.47.3",
3
+ "version": "1.47.5",
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",
@@ -5,7 +5,7 @@ import { createStyles } from 'antd-style';
5
5
  import Link from 'next/link';
6
6
  import { usePathname } from 'next/navigation';
7
7
  import { memo } from 'react';
8
- import { Flexbox } from 'react-layout-kit';
8
+ import { Center, Flexbox } from 'react-layout-kit';
9
9
 
10
10
  import { AiProviderListItem, AiProviderSourceEnum } from '@/types/aiProvider';
11
11
 
@@ -62,7 +62,16 @@ const ProviderItem = memo<AiProviderListItem>(({ id, name, source, enabled, logo
62
62
  )}
63
63
  {name}
64
64
  </Flexbox>
65
- {enabled && <Badge status="success" />}
65
+ <Flexbox horizontal>
66
+ {enabled && (
67
+ <Center width={24}>
68
+ <Badge status="success" />
69
+ </Center>
70
+ )}
71
+ {/* cloud slot */}
72
+
73
+ {/* cloud slot */}
74
+ </Flexbox>
66
75
  </Link>
67
76
  );
68
77
  });
@@ -0,0 +1,46 @@
1
+ import { Skeleton } from 'antd';
2
+ import { createStyles } from 'antd-style';
3
+ import { FC } from 'react';
4
+
5
+ import InstantSwitch from '@/components/InstantSwitch';
6
+ import { aiProviderSelectors, useAiInfraStore } from '@/store/aiInfra';
7
+
8
+ const useStyles = createStyles(({ css }) => ({
9
+ switchLoading: css`
10
+ width: 44px !important;
11
+ min-width: 44px !important;
12
+ height: 22px !important;
13
+ border-radius: 12px !important;
14
+ `,
15
+ }));
16
+
17
+ interface SwitchProps {
18
+ Component?: FC<{ id: string }>;
19
+ id: string;
20
+ }
21
+
22
+ const Switch = ({ id, Component }: SwitchProps) => {
23
+ const { styles } = useStyles();
24
+
25
+ const [toggleProviderEnabled, enabled, isLoading] = useAiInfraStore((s) => [
26
+ s.toggleProviderEnabled,
27
+ aiProviderSelectors.isProviderEnabled(id)(s),
28
+ aiProviderSelectors.isAiProviderConfigLoading(id)(s),
29
+ ]);
30
+
31
+ if (isLoading) return <Skeleton.Button active className={styles.switchLoading} />;
32
+
33
+ // slot for cloud
34
+ if (Component) return <Component id={id} />;
35
+
36
+ return (
37
+ <InstantSwitch
38
+ enabled={enabled}
39
+ onChange={async (enabled) => {
40
+ await toggleProviderEnabled(id as any, enabled);
41
+ }}
42
+ />
43
+ );
44
+ };
45
+
46
+ export default Switch;
@@ -12,7 +12,6 @@ import { Trans, useTranslation } from 'react-i18next';
12
12
  import { Center, Flexbox } from 'react-layout-kit';
13
13
  import urlJoin from 'url-join';
14
14
 
15
- import InstantSwitch from '@/components/InstantSwitch';
16
15
  import { FORM_STYLE } from '@/const/layoutTokens';
17
16
  import { AES_GCM_URL, BASE_PROVIDER_DOC_URL } from '@/const/url';
18
17
  import { isServerMode } from '@/const/version';
@@ -26,6 +25,7 @@ import {
26
25
  import { KeyVaultsConfigKey, LLMProviderApiTokenKey, LLMProviderBaseUrlKey } from '../../const';
27
26
  import Checker from './Checker';
28
27
  import { SkeletonInput } from './SkeletonInput';
28
+ import EnableSwitch from './EnableSwitch';
29
29
  import UpdateProviderInfo from './UpdateProviderInfo';
30
30
 
31
31
  const useStyles = createStyles(({ css, prefixCls, responsive, token }) => ({
@@ -131,7 +131,6 @@ const ProviderConfig = memo<ProviderConfigProps>(
131
131
  const { cx, styles, theme } = useStyles();
132
132
 
133
133
  const [
134
- toggleProviderEnabled,
135
134
  useFetchAiProviderItem,
136
135
  updateAiProviderConfig,
137
136
  enabled,
@@ -141,7 +140,6 @@ const ProviderConfig = memo<ProviderConfigProps>(
141
140
  isProviderEndpointNotEmpty,
142
141
  isProviderApiKeyNotEmpty,
143
142
  ] = useAiInfraStore((s) => [
144
- s.toggleProviderEnabled,
145
143
  s.useFetchAiProviderItem,
146
144
  s.updateAiProviderConfig,
147
145
  aiProviderSelectors.isProviderEnabled(id)(s),
@@ -285,16 +283,7 @@ const ProviderConfig = memo<ProviderConfigProps>(
285
283
  {extra}
286
284
 
287
285
  {isCustom && <UpdateProviderInfo />}
288
- {isLoading ? (
289
- <Skeleton.Button active className={styles.switchLoading} />
290
- ) : (
291
- <InstantSwitch
292
- enabled={enabled}
293
- onChange={async (enabled) => {
294
- await toggleProviderEnabled(id as any, enabled);
295
- }}
296
- />
297
- )}
286
+ <EnableSwitch id={id} />
298
287
  </Flexbox>
299
288
  ),
300
289
  title: (
@@ -1,4 +1,4 @@
1
- import type { PgliteDatabase } from 'drizzle-orm/pglite';
1
+ import { PgliteDatabase, drizzle } from 'drizzle-orm/pglite';
2
2
  import { Md5 } from 'ts-md5';
3
3
 
4
4
  import { ClientDBLoadingProgress, DatabaseLoadingState } from '@/types/clientDB';
@@ -28,6 +28,12 @@ export class DatabaseManager {
28
28
  private static WASM_CDN_URL =
29
29
  'https://registry.npmmirror.com/@electric-sql/pglite/0.2.13/files/dist/postgres.wasm';
30
30
 
31
+ private static FSBUNDLER_CDN_URL =
32
+ 'https://registry.npmmirror.com/@electric-sql/pglite/0.2.13/files/dist/postgres.data';
33
+
34
+ private static VECTOR_CDN_URL =
35
+ 'https://registry.npmmirror.com/@electric-sql/pglite/0.2.13/files/dist/vector.tar.gz';
36
+
31
37
  private constructor() {}
32
38
 
33
39
  static getInstance() {
@@ -88,6 +94,12 @@ export class DatabaseManager {
88
94
  return WebAssembly.compile(wasmBytes);
89
95
  }
90
96
 
97
+ private fetchFsBundle = async () => {
98
+ const res = await fetch(DatabaseManager.FSBUNDLER_CDN_URL);
99
+
100
+ return await res.blob();
101
+ };
102
+
91
103
  // 异步加载 PGlite 相关依赖
92
104
  private async loadDependencies() {
93
105
  const start = Date.now();
@@ -100,7 +112,7 @@ export class DatabaseManager {
100
112
  PGlite: m.PGlite,
101
113
  })),
102
114
  import('@electric-sql/pglite/vector'),
103
- import('drizzle-orm/pglite'),
115
+ this.fetchFsBundle(),
104
116
  ];
105
117
 
106
118
  let loaded = 0;
@@ -125,9 +137,9 @@ export class DatabaseManager {
125
137
  });
126
138
 
127
139
  // @ts-ignore
128
- const [{ PGlite, IdbFs, MemoryFS }, { vector }, { drizzle }] = results;
140
+ const [{ PGlite, IdbFs, MemoryFS }, { vector }, fsBundle] = results;
129
141
 
130
- return { IdbFs, MemoryFS, PGlite, drizzle, vector };
142
+ return { IdbFs, MemoryFS, PGlite, fsBundle, vector };
131
143
  }
132
144
 
133
145
  // 数据库迁移方法
@@ -177,17 +189,34 @@ export class DatabaseManager {
177
189
  this.callbacks?.onStateChange?.(DatabaseLoadingState.Initializing);
178
190
 
179
191
  // 加载依赖
180
- const { PGlite, vector, drizzle, IdbFs, MemoryFS } = await this.loadDependencies();
192
+ const { fsBundle, PGlite, MemoryFS, IdbFs, vector } = await this.loadDependencies();
181
193
 
182
194
  // 加载并编译 WASM 模块
183
195
  const wasmModule = await this.loadWasmModule();
184
196
 
185
- const db = new PGlite({
186
- extensions: { vector },
187
- fs: typeof window === 'undefined' ? new MemoryFS('lobechat') : new IdbFs('lobechat'),
188
- relaxedDurability: true,
189
- wasmModule,
190
- });
197
+ const { initPgliteWorker } = await import('./pglite');
198
+
199
+ let db: typeof PGlite;
200
+
201
+ const dbName = 'lobechat';
202
+
203
+ // make db as web worker if worker is available
204
+ if (typeof Worker !== 'undefined') {
205
+ db = await initPgliteWorker({
206
+ dbName,
207
+ fsBundle: fsBundle as Blob,
208
+ vectorBundlePath: DatabaseManager.VECTOR_CDN_URL,
209
+ wasmModule,
210
+ });
211
+ } else {
212
+ // in edge runtime or test runtime, we don't have worker
213
+ db = new PGlite({
214
+ extensions: { vector },
215
+ fs: typeof window === 'undefined' ? new MemoryFS(dbName) : new IdbFs(dbName),
216
+ relaxedDurability: true,
217
+ wasmModule,
218
+ });
219
+ }
191
220
 
192
221
  this.dbInstance = drizzle({ client: db, schema });
193
222
 
@@ -210,6 +239,8 @@ export class DatabaseManager {
210
239
  name: error.name,
211
240
  stack: error.stack,
212
241
  });
242
+
243
+ console.error(error);
213
244
  throw error;
214
245
  }
215
246
  })();
@@ -0,0 +1,17 @@
1
+ import { PGliteWorker } from '@electric-sql/pglite/worker';
2
+
3
+ import { InitMeta } from './type';
4
+
5
+ export const initPgliteWorker = async (meta: InitMeta) => {
6
+ const worker = await PGliteWorker.create(
7
+ new Worker(new URL('pglite.worker.ts', import.meta.url)),
8
+ { meta },
9
+ );
10
+
11
+ // 监听 worker 状态变化
12
+ worker.onLeaderChange(() => {
13
+ console.log('Worker leader changed, isLeader:', worker?.isLeader);
14
+ });
15
+
16
+ return worker as PGliteWorker;
17
+ };
@@ -0,0 +1,25 @@
1
+ import { worker } from '@electric-sql/pglite/worker';
2
+
3
+ import { InitMeta } from './type';
4
+
5
+ worker({
6
+ async init(options) {
7
+ const { wasmModule, fsBundle, vectorBundlePath, dbName } = options.meta as InitMeta;
8
+ const { PGlite } = await import('@electric-sql/pglite');
9
+
10
+ return new PGlite({
11
+ dataDir: `idb://${dbName}`,
12
+ extensions: {
13
+ vector: {
14
+ name: 'pgvector',
15
+ setup: async (pglite, options) => {
16
+ return { bundlePath: new URL(vectorBundlePath), options };
17
+ },
18
+ },
19
+ },
20
+ fsBundle,
21
+ relaxedDurability: true,
22
+ wasmModule,
23
+ });
24
+ },
25
+ });
@@ -0,0 +1,6 @@
1
+ export interface InitMeta {
2
+ dbName: string;
3
+ fsBundle: Blob;
4
+ vectorBundlePath: string;
5
+ wasmModule: WebAssembly.Module;
6
+ }