@lobehub/lobehub 2.0.0-next.321 → 2.0.0-next.322

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,31 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 2.0.0-next.322](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.321...v2.0.0-next.322)
6
+
7
+ <sup>Released on **2026-01-20**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **memory-user-memory**: Should fallback to server configured provider & model.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's fixed
19
+
20
+ - **memory-user-memory**: Should fallback to server configured provider & model, closes [#11643](https://github.com/lobehub/lobe-chat/issues/11643) ([af446d9](https://github.com/lobehub/lobe-chat/commit/af446d9))
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
+
5
30
  ## [Version 2.0.0-next.321](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.320...v2.0.0-next.321)
6
31
 
7
32
  <sup>Released on **2026-01-20**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,9 @@
1
1
  [
2
+ {
3
+ "children": {},
4
+ "date": "2026-01-20",
5
+ "version": "2.0.0-next.322"
6
+ },
2
7
  {
3
8
  "children": {},
4
9
  "date": "2026-01-20",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/lobehub",
3
- "version": "2.0.0-next.321",
3
+ "version": "2.0.0-next.322",
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",
@@ -8,6 +8,7 @@ import AgentBuilder from '@/features/AgentBuilder';
8
8
  import WideScreenContainer from '@/features/WideScreenContainer';
9
9
  import { useAgentStore } from '@/store/agent';
10
10
  import { agentSelectors } from '@/store/agent/selectors';
11
+ import { StyleSheet } from '@/utils/styles';
11
12
 
12
13
  import Header from './features/Header';
13
14
  import ProfileEditor from './features/ProfileEditor';
@@ -15,13 +16,25 @@ import ProfileHydration from './features/ProfileHydration';
15
16
  import ProfileProvider from './features/ProfileProvider';
16
17
  import { useProfileStore } from './features/store';
17
18
 
19
+ const styles = StyleSheet.create({
20
+ contentWrapper: {
21
+ cursor: 'text',
22
+ display: 'flex',
23
+ overflowY: 'auto',
24
+ position: 'relative',
25
+ },
26
+ profileArea: {
27
+ minWidth: 0,
28
+ },
29
+ });
30
+
18
31
  const ProfileArea = memo(() => {
19
32
  const editor = useProfileStore((s) => s.editor);
20
33
  const isAgentConfigLoading = useAgentStore(agentSelectors.isAgentConfigLoading);
21
34
 
22
35
  return (
23
36
  <>
24
- <Flexbox flex={1} height={'100%'}>
37
+ <Flexbox flex={1} height={'100%'} style={styles.profileArea}>
25
38
  {isAgentConfigLoading ? (
26
39
  <Loading debugId="ProfileArea" />
27
40
  ) : (
@@ -33,7 +46,7 @@ const ProfileArea = memo(() => {
33
46
  onClick={() => {
34
47
  editor?.focus();
35
48
  }}
36
- style={{ cursor: 'text', display: 'flex', overflowY: 'auto', position: 'relative' }}
49
+ style={styles.contentWrapper}
37
50
  width={'100%'}
38
51
  >
39
52
  <WideScreenContainer>
@@ -15,6 +15,7 @@ import { builtinAgentSelectors } from '@/store/agent/selectors';
15
15
  import { useDocumentStore } from '@/store/document';
16
16
  import { editorSelectors } from '@/store/document/slices/editor';
17
17
  import { usePageStore } from '@/store/page';
18
+ import { StyleSheet } from '@/utils/styles';
18
19
 
19
20
  import Copilot from './Copilot';
20
21
  import EditorCanvas from './EditorCanvas';
@@ -25,6 +26,22 @@ import PageTitle from './PageTitle';
25
26
  import TitleSection from './TitleSection';
26
27
  import { usePageEditorStore } from './store';
27
28
 
29
+ const styles = StyleSheet.create({
30
+ contentWrapper: {
31
+ display: 'flex',
32
+ overflowY: 'auto',
33
+ position: 'relative',
34
+ },
35
+ editorContainer: {
36
+ minWidth: 0,
37
+ position: 'relative',
38
+ },
39
+ editorContent: {
40
+ overflowY: 'auto',
41
+ position: 'relative',
42
+ },
43
+ });
44
+
28
45
  interface PageEditorProps {
29
46
  emoji?: string;
30
47
  knowledgeBaseId?: string;
@@ -77,16 +94,11 @@ const PageEditorCanvas = memo(() => {
77
94
  style={{ backgroundColor: cssVar.colorBgContainer }}
78
95
  width={'100%'}
79
96
  >
80
- <Flexbox flex={1} height={'100%'} style={{ position: 'relative' }}>
97
+ <Flexbox flex={1} height={'100%'} style={styles.editorContainer}>
81
98
  <Header />
82
- <Flexbox
83
- height={'100%'}
84
- horizontal
85
- style={{ display: 'flex', overflowY: 'auto', position: 'relative' }}
86
- width={'100%'}
87
- >
99
+ <Flexbox height={'100%'} horizontal style={styles.contentWrapper} width={'100%'}>
88
100
  <WideScreenContainer onClick={() => editor?.focus()} wrapperStyle={{ cursor: 'text' }}>
89
- <Flexbox flex={1} style={{ overflowY: 'auto', position: 'relative' }}>
101
+ <Flexbox flex={1} style={styles.editorContent}>
90
102
  <TitleSection />
91
103
  <EditorCanvas />
92
104
  </Flexbox>
@@ -89,8 +89,9 @@ describe('MemoryExtractionExecutor.resolveRuntimeKeyVaults', () => {
89
89
  });
90
90
  });
91
91
 
92
- it('throws when no provider can satisfy an embedding model', () => {
92
+ it('warns and falls back to server provider when no enabled provider satisfies embedding model', () => {
93
93
  const executor = createExecutor();
94
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
94
95
 
95
96
  const runtimeState = createRuntimeState(
96
97
  [
@@ -106,6 +107,15 @@ describe('MemoryExtractionExecutor.resolveRuntimeKeyVaults', () => {
106
107
  },
107
108
  );
108
109
 
109
- expect(() => (executor as any).resolveRuntimeKeyVaults(runtimeState)).toThrow(/embedding/i);
110
+ const keyVaults = (executor as any).resolveRuntimeKeyVaults(runtimeState);
111
+
112
+ expect(keyVaults).toMatchObject({
113
+ 'provider-b': { apiKey: 'b-key' },
114
+ 'provider-l': { apiKey: 'l-key' },
115
+ });
116
+ expect(keyVaults).not.toHaveProperty('provider-e');
117
+ expect(warnSpy).toHaveBeenCalled();
118
+
119
+ warnSpy.mockRestore();
110
120
  });
111
121
  });
@@ -1660,6 +1660,7 @@ export class MemoryExtractionExecutor {
1660
1660
  config,
1661
1661
  ]),
1662
1662
  );
1663
+
1663
1664
  const providerModels = runtimeState.enabledAiModels.reduce<Record<string, Set<string>>>(
1664
1665
  (acc, model) => {
1665
1666
  const providerId = normalizeProvider(model.providerId);
@@ -1692,15 +1693,23 @@ export class MemoryExtractionExecutor {
1692
1693
  for (const providerId of providerOrder) {
1693
1694
  const models = providerModels[providerId];
1694
1695
  if (!models) continue;
1695
-
1696
1696
  if (models.has(modelId)) return providerId;
1697
1697
 
1698
1698
  const preferredMatch = candidateModels.find((preferredModel) => models.has(preferredModel));
1699
1699
  if (preferredMatch) return providerId;
1700
1700
  }
1701
+ if (fallbackProvider) {
1702
+ console.warn(
1703
+ `[memory-extraction] no enabled provider found for ${label || 'model'} "${modelId}"`,
1704
+ `(preferred ${preferredProviders}), falling back to server-configured provider "${fallbackProvider}".`,
1705
+ );
1706
+
1707
+ return normalizeProvider(fallbackProvider);
1708
+ }
1701
1709
 
1702
1710
  throw new Error(
1703
- `Unable to resolve provider for ${label || 'model'} "${modelId}". Check preferred providers/models configuration.`,
1711
+ `Unable to resolve provider for ${label || 'model'} "${modelId}". ` +
1712
+ `Check preferred providers/models configuration.`,
1704
1713
  );
1705
1714
  };
1706
1715
 
@@ -0,0 +1,10 @@
1
+ import type { CSSProperties } from 'react';
2
+
3
+ export const StyleSheet = {
4
+ compose: (...styles: Array<CSSProperties | undefined | null | false>): CSSProperties => {
5
+ return Object.assign({}, ...styles.filter(Boolean));
6
+ },
7
+ create: (styles: Record<string, CSSProperties>) => {
8
+ return styles;
9
+ },
10
+ };