@lobehub/chat 1.46.6 → 1.47.0
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 +51 -0
- package/changelog/v1.json +18 -0
- package/netlify.toml +1 -1
- package/package.json +2 -1
- package/src/app/(main)/discover/(detail)/provider/[slug]/features/ProviderConfig.tsx +2 -2
- package/src/app/(main)/settings/hooks/useCategory.tsx +3 -3
- package/src/app/(main)/settings/provider/(detail)/[id]/ClientMode.tsx +25 -0
- package/src/app/(main)/settings/provider/(detail)/[id]/page.tsx +2 -1
- package/src/app/(main)/settings/provider/ProviderMenu/SortProviderModal/index.tsx +0 -1
- package/src/const/settings/knowledge.ts +1 -1
- package/src/database/client/migrations.json +11 -0
- package/src/database/repositories/tableViewer/index.test.ts +256 -0
- package/src/database/repositories/tableViewer/index.ts +251 -0
- package/src/database/server/models/aiProvider.ts +2 -2
- package/src/features/DevPanel/FloatPanel.tsx +136 -0
- package/src/features/DevPanel/PostgresViewer/DataTable/Table.tsx +157 -0
- package/src/features/DevPanel/PostgresViewer/DataTable/TableCell.tsx +34 -0
- package/src/features/DevPanel/PostgresViewer/DataTable/index.tsx +67 -0
- package/src/features/DevPanel/PostgresViewer/Schema.tsx +196 -0
- package/src/features/DevPanel/PostgresViewer/TableColumns.tsx +67 -0
- package/src/features/DevPanel/PostgresViewer/index.tsx +19 -0
- package/src/features/DevPanel/PostgresViewer/useTableColumns.ts +13 -0
- package/src/features/DevPanel/index.tsx +12 -0
- package/src/features/ModelSwitchPanel/index.tsx +4 -2
- package/src/hooks/useEnabledChatModels.ts +2 -2
- package/src/hooks/useModelContextWindowTokens.ts +2 -2
- package/src/hooks/useModelHasContextWindowToken.ts +2 -2
- package/src/hooks/useModelSupportToolUse.ts +2 -2
- package/src/hooks/useModelSupportVision.ts +2 -2
- package/src/layout/GlobalProvider/index.tsx +2 -2
- package/src/server/globalConfig/parseFilesConfig.test.ts +122 -3
- package/src/server/globalConfig/parseFilesConfig.ts +19 -13
- package/src/server/modules/S3/index.ts +3 -0
- package/src/services/_auth.ts +2 -2
- package/src/services/aiModel/client.ts +60 -0
- package/src/services/aiModel/index.test.ts +10 -0
- package/src/services/aiModel/index.ts +5 -0
- package/src/services/aiModel/server.ts +47 -0
- package/src/services/aiModel/type.ts +30 -0
- package/src/services/aiProvider/client.ts +64 -0
- package/src/services/aiProvider/index.test.ts +10 -0
- package/src/services/aiProvider/index.ts +5 -0
- package/src/services/aiProvider/server.ts +43 -0
- package/src/services/aiProvider/type.ts +26 -0
- package/src/services/chat.ts +5 -5
- package/src/services/tableViewer/client.ts +16 -0
- package/src/services/tableViewer/index.ts +3 -0
- package/src/store/aiInfra/slices/aiProvider/action.ts +2 -2
- package/src/types/knowledgeBase/index.ts +1 -1
- package/src/types/serverConfig.ts +6 -0
- package/src/types/tableViewer.ts +30 -0
- package/src/types/user/settings/filesConfig.ts +1 -1
- package/tests/utils.tsx +46 -0
- package/src/features/DebugUI/Content.tsx +0 -34
- package/src/features/DebugUI/index.tsx +0 -20
- package/src/services/aiModel.ts +0 -52
- package/src/services/aiProvider.ts +0 -47
@@ -0,0 +1,196 @@
|
|
1
|
+
import { Icon } from '@lobehub/ui';
|
2
|
+
import { createStyles } from 'antd-style';
|
3
|
+
import { ChevronDown, ChevronRight, Database, Table as TableIcon } from 'lucide-react';
|
4
|
+
import React, { useState } from 'react';
|
5
|
+
import { Flexbox } from 'react-layout-kit';
|
6
|
+
import useSWR from 'swr';
|
7
|
+
|
8
|
+
import { tableViewerService } from '@/services/tableViewer';
|
9
|
+
import { useGlobalStore } from '@/store/global';
|
10
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
11
|
+
|
12
|
+
import TableColumns from './TableColumns';
|
13
|
+
|
14
|
+
// 样式定义
|
15
|
+
const useStyles = createStyles(({ token, css }) => ({
|
16
|
+
button: css`
|
17
|
+
cursor: pointer;
|
18
|
+
|
19
|
+
display: flex;
|
20
|
+
gap: 4px;
|
21
|
+
align-items: center;
|
22
|
+
|
23
|
+
padding-block: ${token.paddingXS}px;
|
24
|
+
padding-inline: ${token.padding}px;
|
25
|
+
border: none;
|
26
|
+
border-radius: ${token.borderRadius}px;
|
27
|
+
|
28
|
+
color: ${token.colorText};
|
29
|
+
|
30
|
+
background: ${token.colorFillSecondary};
|
31
|
+
|
32
|
+
transition: all ${token.motionDurationMid};
|
33
|
+
|
34
|
+
&:hover {
|
35
|
+
background: ${token.colorFillTertiary};
|
36
|
+
}
|
37
|
+
`,
|
38
|
+
count: css`
|
39
|
+
font-size: 12px;
|
40
|
+
color: ${token.colorTextTertiary};
|
41
|
+
`,
|
42
|
+
dataPanel: css`
|
43
|
+
overflow: hidden;
|
44
|
+
display: flex;
|
45
|
+
flex: 1;
|
46
|
+
flex-direction: column;
|
47
|
+
|
48
|
+
height: 100%;
|
49
|
+
|
50
|
+
background: ${token.colorBgContainer};
|
51
|
+
`,
|
52
|
+
schema: css`
|
53
|
+
font-family: ${token.fontFamilyCode};
|
54
|
+
font-size: 12px;
|
55
|
+
font-weight: normal;
|
56
|
+
color: ${token.colorTextTertiary};
|
57
|
+
`,
|
58
|
+
schemaHeader: css`
|
59
|
+
display: flex;
|
60
|
+
gap: 8px;
|
61
|
+
align-items: center;
|
62
|
+
|
63
|
+
padding-block: 12px;
|
64
|
+
padding-inline: 16px;
|
65
|
+
|
66
|
+
font-weight: ${token.fontWeightStrong};
|
67
|
+
color: ${token.colorText};
|
68
|
+
`,
|
69
|
+
schemaPanel: css`
|
70
|
+
overflow: scroll;
|
71
|
+
|
72
|
+
width: 280px;
|
73
|
+
height: 100%;
|
74
|
+
border-inline-end: 1px solid ${token.colorBorderSecondary};
|
75
|
+
|
76
|
+
background: ${token.colorBgContainer};
|
77
|
+
`,
|
78
|
+
selected: css`
|
79
|
+
background: ${token.colorFillSecondary};
|
80
|
+
`,
|
81
|
+
table: css`
|
82
|
+
overflow: hidden;
|
83
|
+
flex: 1;
|
84
|
+
|
85
|
+
table {
|
86
|
+
border-collapse: collapse;
|
87
|
+
width: 100%;
|
88
|
+
}
|
89
|
+
|
90
|
+
th {
|
91
|
+
position: sticky;
|
92
|
+
z-index: 1;
|
93
|
+
inset-block-start: 0;
|
94
|
+
|
95
|
+
padding: ${token.padding}px;
|
96
|
+
border-block-end: 1px solid ${token.colorBorderSecondary};
|
97
|
+
|
98
|
+
font-weight: ${token.fontWeightStrong};
|
99
|
+
text-align: start;
|
100
|
+
|
101
|
+
background: ${token.colorFillQuaternary};
|
102
|
+
}
|
103
|
+
|
104
|
+
td {
|
105
|
+
padding: ${token.padding}px;
|
106
|
+
border-block-end: 1px solid ${token.colorBorderSecondary};
|
107
|
+
transition: all ${token.motionDurationMid};
|
108
|
+
|
109
|
+
&:hover {
|
110
|
+
background: ${token.colorFillQuaternary};
|
111
|
+
}
|
112
|
+
}
|
113
|
+
`,
|
114
|
+
tableItem: css`
|
115
|
+
cursor: pointer;
|
116
|
+
|
117
|
+
display: flex;
|
118
|
+
gap: 8px;
|
119
|
+
align-items: center;
|
120
|
+
|
121
|
+
margin-inline: 8px;
|
122
|
+
padding: 8px;
|
123
|
+
border-radius: ${token.borderRadius}px;
|
124
|
+
|
125
|
+
color: ${token.colorText};
|
126
|
+
|
127
|
+
&:hover {
|
128
|
+
background: ${token.colorFillSecondary};
|
129
|
+
}
|
130
|
+
`,
|
131
|
+
}));
|
132
|
+
|
133
|
+
interface SchemaPanelProps {
|
134
|
+
onTableSelect: (tableName: string) => void;
|
135
|
+
selectedTable?: string;
|
136
|
+
}
|
137
|
+
const SchemaPanel = ({ onTableSelect, selectedTable }: SchemaPanelProps) => {
|
138
|
+
const { styles, cx } = useStyles();
|
139
|
+
const [expandedTables, setExpandedTables] = useState(new Set());
|
140
|
+
|
141
|
+
const isDBInited = useGlobalStore(systemStatusSelectors.isDBInited);
|
142
|
+
|
143
|
+
const { data, isLoading } = useSWR(isDBInited ? 'fetch-tables' : null, () =>
|
144
|
+
tableViewerService.getAllTables(),
|
145
|
+
);
|
146
|
+
|
147
|
+
const toggleTable = (tableName: string) => {
|
148
|
+
const newExpanded = new Set(expandedTables);
|
149
|
+
if (newExpanded.has(tableName)) {
|
150
|
+
newExpanded.delete(tableName);
|
151
|
+
} else {
|
152
|
+
newExpanded.add(tableName);
|
153
|
+
}
|
154
|
+
setExpandedTables(newExpanded);
|
155
|
+
};
|
156
|
+
|
157
|
+
return (
|
158
|
+
<div className={styles.schemaPanel}>
|
159
|
+
<div className={styles.schemaHeader}>
|
160
|
+
<Database size={16} />
|
161
|
+
<Flexbox align={'center'} horizontal justify={'space-between'}>
|
162
|
+
<span>Tables {data?.length}</span>
|
163
|
+
<span className={styles.schema}>public</span>
|
164
|
+
</Flexbox>
|
165
|
+
</div>
|
166
|
+
{isLoading ? (
|
167
|
+
<div>Loading...</div>
|
168
|
+
) : (
|
169
|
+
<Flexbox>
|
170
|
+
{data?.map((table) => (
|
171
|
+
<div key={table.name}>
|
172
|
+
<Flexbox
|
173
|
+
className={cx(styles.tableItem, selectedTable === table.name && styles.selected)}
|
174
|
+
horizontal
|
175
|
+
onClick={() => {
|
176
|
+
toggleTable(table.name);
|
177
|
+
onTableSelect(table.name);
|
178
|
+
}}
|
179
|
+
>
|
180
|
+
<Icon icon={expandedTables.has(table.name) ? ChevronDown : ChevronRight} />
|
181
|
+
<TableIcon size={16} />
|
182
|
+
<Flexbox align={'center'} horizontal justify={'space-between'}>
|
183
|
+
<span>{table.name}</span>
|
184
|
+
<span className={styles.count}>{table.count}</span>
|
185
|
+
</Flexbox>
|
186
|
+
</Flexbox>
|
187
|
+
{expandedTables.has(table.name) && <TableColumns tableName={table.name} />}
|
188
|
+
</div>
|
189
|
+
))}
|
190
|
+
</Flexbox>
|
191
|
+
)}
|
192
|
+
</div>
|
193
|
+
);
|
194
|
+
};
|
195
|
+
|
196
|
+
export default SchemaPanel;
|
@@ -0,0 +1,67 @@
|
|
1
|
+
import { Tag } from 'antd';
|
2
|
+
import { createStyles } from 'antd-style';
|
3
|
+
import React from 'react';
|
4
|
+
import { Flexbox } from 'react-layout-kit';
|
5
|
+
|
6
|
+
import { useTableColumns } from './useTableColumns';
|
7
|
+
|
8
|
+
const useStyles = createStyles(({ token, css }) => ({
|
9
|
+
container: css`
|
10
|
+
margin-inline: 40px 4px;
|
11
|
+
font-size: ${token.fontSizeSM}px;
|
12
|
+
color: ${token.colorTextSecondary};
|
13
|
+
`,
|
14
|
+
item: css`
|
15
|
+
padding-block: 4px;
|
16
|
+
padding-inline: 0;
|
17
|
+
font-family: ${token.fontFamilyCode};
|
18
|
+
`,
|
19
|
+
type: css`
|
20
|
+
font-size: 10px;
|
21
|
+
color: ${token.red9};
|
22
|
+
`,
|
23
|
+
}));
|
24
|
+
|
25
|
+
interface TableColumnsProps {
|
26
|
+
tableName: string;
|
27
|
+
}
|
28
|
+
|
29
|
+
const TableColumns = ({ tableName }: TableColumnsProps) => {
|
30
|
+
const { styles } = useStyles();
|
31
|
+
|
32
|
+
const { data, isLoading } = useTableColumns(tableName);
|
33
|
+
|
34
|
+
return (
|
35
|
+
<div className={styles.container}>
|
36
|
+
{isLoading ? (
|
37
|
+
<div>Loading...</div>
|
38
|
+
) : (
|
39
|
+
<Flexbox>
|
40
|
+
{data?.map((column) => (
|
41
|
+
<Flexbox
|
42
|
+
align={'center'}
|
43
|
+
className={styles.item}
|
44
|
+
horizontal
|
45
|
+
justify={'space-between'}
|
46
|
+
key={column.name}
|
47
|
+
>
|
48
|
+
<Flexbox>
|
49
|
+
<Flexbox>{column.name}</Flexbox>
|
50
|
+
<span className={styles.type}>{column.type}</span>
|
51
|
+
</Flexbox>
|
52
|
+
{column.isPrimaryKey && (
|
53
|
+
<div>
|
54
|
+
<Tag bordered={false} color={'cyan'}>
|
55
|
+
Primary
|
56
|
+
</Tag>
|
57
|
+
</div>
|
58
|
+
)}
|
59
|
+
</Flexbox>
|
60
|
+
))}
|
61
|
+
</Flexbox>
|
62
|
+
)}
|
63
|
+
</div>
|
64
|
+
);
|
65
|
+
};
|
66
|
+
|
67
|
+
export default TableColumns;
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import React, { useState } from 'react';
|
2
|
+
import { Flexbox } from 'react-layout-kit';
|
3
|
+
|
4
|
+
import DataTable from './DataTable';
|
5
|
+
import SchemaPanel from './Schema';
|
6
|
+
|
7
|
+
// Main Database Panel Component
|
8
|
+
const DatabasePanel = () => {
|
9
|
+
const [selectedTable, setSelectedTable] = useState<string>('');
|
10
|
+
|
11
|
+
return (
|
12
|
+
<Flexbox height={'100%'} horizontal>
|
13
|
+
<SchemaPanel onTableSelect={setSelectedTable} selectedTable={selectedTable} />
|
14
|
+
<DataTable tableName={selectedTable} />
|
15
|
+
</Flexbox>
|
16
|
+
);
|
17
|
+
};
|
18
|
+
|
19
|
+
export default DatabasePanel;
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import useSWR from 'swr';
|
2
|
+
|
3
|
+
import { tableViewerService } from '@/services/tableViewer';
|
4
|
+
import { useGlobalStore } from '@/store/global';
|
5
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
6
|
+
|
7
|
+
export const useTableColumns = (tableName?: string) => {
|
8
|
+
const isDBInited = useGlobalStore(systemStatusSelectors.isDBInited);
|
9
|
+
|
10
|
+
return useSWR(isDBInited && tableName ? ['fetch-table-columns', tableName] : null, ([, table]) =>
|
11
|
+
tableViewerService.getTableDetails(table),
|
12
|
+
);
|
13
|
+
};
|
@@ -9,7 +9,7 @@ import { useTranslation } from 'react-i18next';
|
|
9
9
|
import { Flexbox } from 'react-layout-kit';
|
10
10
|
|
11
11
|
import { ModelItemRender, ProviderItemRender } from '@/components/ModelSelect';
|
12
|
-
import {
|
12
|
+
import { isDeprecatedEdition } from '@/const/version';
|
13
13
|
import { useEnabledChatModels } from '@/hooks/useEnabledChatModels';
|
14
14
|
import { useIsMobile } from '@/hooks/useIsMobile';
|
15
15
|
import { useAgentStore } from '@/store/agent';
|
@@ -76,7 +76,9 @@ const ModelSwitchPanel = memo<PropsWithChildren>(({ children }) => {
|
|
76
76
|
</Flexbox>
|
77
77
|
),
|
78
78
|
onClick: () => {
|
79
|
-
router.push(
|
79
|
+
router.push(
|
80
|
+
isDeprecatedEdition ? '/settings/llm' : `/settings/provider/${provider.id}`,
|
81
|
+
);
|
80
82
|
},
|
81
83
|
},
|
82
84
|
];
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import isEqual from 'fast-deep-equal';
|
2
2
|
|
3
|
-
import {
|
3
|
+
import { isDeprecatedEdition } from '@/const/version';
|
4
4
|
import { useAiInfraStore } from '@/store/aiInfra';
|
5
5
|
import { useUserStore } from '@/store/user';
|
6
6
|
import { modelProviderSelectors } from '@/store/user/selectors';
|
@@ -10,7 +10,7 @@ export const useEnabledChatModels = (): EnabledProviderWithModels[] => {
|
|
10
10
|
const enabledList = useUserStore(modelProviderSelectors.modelProviderListForModelSelect, isEqual);
|
11
11
|
const enabledChatModelList = useAiInfraStore((s) => s.enabledChatModelList, isEqual);
|
12
12
|
|
13
|
-
if (
|
13
|
+
if (isDeprecatedEdition) {
|
14
14
|
return enabledList;
|
15
15
|
}
|
16
16
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import {
|
1
|
+
import { isDeprecatedEdition } from '@/const/version';
|
2
2
|
import { aiModelSelectors, useAiInfraStore } from '@/store/aiInfra';
|
3
3
|
import { useUserStore } from '@/store/user';
|
4
4
|
import { modelProviderSelectors } from '@/store/user/selectors';
|
@@ -8,7 +8,7 @@ export const useModelContextWindowTokens = (model: string, provider: string) =>
|
|
8
8
|
|
9
9
|
// TODO: remove this in V2.0
|
10
10
|
const oldValue = useUserStore(modelProviderSelectors.modelMaxToken(model));
|
11
|
-
if (
|
11
|
+
if (isDeprecatedEdition) return oldValue;
|
12
12
|
//
|
13
13
|
|
14
14
|
return newValue as number;
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import {
|
1
|
+
import { isDeprecatedEdition } from '@/const/version';
|
2
2
|
import { useAgentStore } from '@/store/agent';
|
3
3
|
import { agentSelectors } from '@/store/agent/slices/chat';
|
4
4
|
import { aiModelSelectors, useAiInfraStore } from '@/store/aiInfra';
|
@@ -12,7 +12,7 @@ export const useModelHasContextWindowToken = () => {
|
|
12
12
|
|
13
13
|
// TODO: remove this in V2.0
|
14
14
|
const oldValue = useUserStore(modelProviderSelectors.isModelHasMaxToken(model));
|
15
|
-
if (
|
15
|
+
if (isDeprecatedEdition) return oldValue;
|
16
16
|
//
|
17
17
|
|
18
18
|
return newValue;
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import {
|
1
|
+
import { isDeprecatedEdition } from '@/const/version';
|
2
2
|
import { aiModelSelectors, useAiInfraStore } from '@/store/aiInfra';
|
3
3
|
import { useUserStore } from '@/store/user';
|
4
4
|
import { modelProviderSelectors } from '@/store/user/selectors';
|
@@ -8,7 +8,7 @@ export const useModelSupportToolUse = (model: string, provider: string) => {
|
|
8
8
|
|
9
9
|
// TODO: remove this in V2.0
|
10
10
|
const oldValue = useUserStore(modelProviderSelectors.isModelEnabledFunctionCall(model));
|
11
|
-
if (
|
11
|
+
if (isDeprecatedEdition) return oldValue;
|
12
12
|
//
|
13
13
|
|
14
14
|
return newValue;
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import {
|
1
|
+
import { isDeprecatedEdition } from '@/const/version';
|
2
2
|
import { aiModelSelectors, useAiInfraStore } from '@/store/aiInfra';
|
3
3
|
import { useUserStore } from '@/store/user';
|
4
4
|
import { modelProviderSelectors } from '@/store/user/selectors';
|
@@ -8,7 +8,7 @@ export const useModelSupportVision = (model: string, provider: string) => {
|
|
8
8
|
|
9
9
|
// TODO: remove this in V2.0
|
10
10
|
const oldValue = useUserStore(modelProviderSelectors.isModelEnabledVision(model));
|
11
|
-
if (
|
11
|
+
if (isDeprecatedEdition) return oldValue;
|
12
12
|
//
|
13
13
|
|
14
14
|
return newValue;
|
@@ -9,7 +9,7 @@ import {
|
|
9
9
|
LOBE_THEME_NEUTRAL_COLOR,
|
10
10
|
LOBE_THEME_PRIMARY_COLOR,
|
11
11
|
} from '@/const/theme';
|
12
|
-
import
|
12
|
+
import DevPanel from '@/features/DevPanel';
|
13
13
|
import { getServerGlobalConfig } from '@/server/globalConfig';
|
14
14
|
import { ServerConfigStoreProvider } from '@/store/serverConfig';
|
15
15
|
import { getAntdLocale, parseBrowserLanguage } from '@/utils/locale';
|
@@ -65,9 +65,9 @@ const GlobalLayout = async ({ children }: PropsWithChildren) => {
|
|
65
65
|
<Suspense>
|
66
66
|
<StoreInitialization />
|
67
67
|
<ReactScan />
|
68
|
+
{process.env.NODE_ENV === 'development' && <DevPanel />}
|
68
69
|
</Suspense>
|
69
70
|
</ServerConfigStoreProvider>
|
70
|
-
<DebugUI />
|
71
71
|
</AppTheme>
|
72
72
|
<AntdV5MonkeyPatch />
|
73
73
|
</Locale>
|
@@ -3,15 +3,134 @@ import { describe, expect, it } from 'vitest';
|
|
3
3
|
import { parseFilesConfig } from './parseFilesConfig';
|
4
4
|
|
5
5
|
describe('parseFilesConfig', () => {
|
6
|
+
it('parses full configuration correctly', () => {
|
7
|
+
const envStr =
|
8
|
+
'embedding_model=openai/embedding-text-3-small,reranker_model=cohere/rerank-english-v3.0,query_mode=full_text';
|
9
|
+
const expected = {
|
10
|
+
embeddingModel: { provider: 'openai', model: 'embedding-text-3-small' },
|
11
|
+
rerankerModel: { provider: 'cohere', model: 'rerank-english-v3.0' },
|
12
|
+
queryMode: 'full_text',
|
13
|
+
};
|
14
|
+
expect(parseFilesConfig(envStr)).toEqual(expected);
|
15
|
+
});
|
16
|
+
|
6
17
|
// 测试embeddings配置是否被正确解析
|
7
18
|
it('parses embeddings configuration correctly', () => {
|
8
|
-
const envStr =
|
9
|
-
'embedding_model=openai/embedding-text-3-large,reranker_model=cohere/rerank-english-v3.0,query_model=full_text';
|
19
|
+
const envStr = 'embedding_model=openai/embedding-text-3-large';
|
10
20
|
const expected = {
|
11
21
|
embeddingModel: { provider: 'openai', model: 'embedding-text-3-large' },
|
22
|
+
};
|
23
|
+
expect(parseFilesConfig(envStr)).toEqual(expected);
|
24
|
+
});
|
25
|
+
|
26
|
+
it('parses rerank configuration correctly', () => {
|
27
|
+
const envStr = 'reranker_model=cohere/rerank-english-v3.0';
|
28
|
+
const expected = {
|
29
|
+
rerankerModel: { provider: 'cohere', model: 'rerank-english-v3.0' },
|
30
|
+
};
|
31
|
+
expect(parseFilesConfig(envStr)).toEqual(expected);
|
32
|
+
});
|
33
|
+
|
34
|
+
it('parses queryMode configuration correctly', () => {
|
35
|
+
const envStr = 'query_mode=full_text';
|
36
|
+
const expected = {
|
37
|
+
queryMode: 'full_text',
|
38
|
+
};
|
39
|
+
expect(parseFilesConfig(envStr)).toEqual(expected);
|
40
|
+
});
|
41
|
+
|
42
|
+
it('parses queryMode rerank configuration correctly', () => {
|
43
|
+
const envStr = 'reranker_model=cohere/rerank-english-v3.0,query_mode=full_text';
|
44
|
+
const expected = {
|
45
|
+
queryMode: 'full_text',
|
46
|
+
rerankerModel: { provider: 'cohere', model: 'rerank-english-v3.0' },
|
47
|
+
};
|
48
|
+
expect(parseFilesConfig(envStr)).toEqual(expected);
|
49
|
+
});
|
50
|
+
|
51
|
+
it('parses queryMode embeddings configuration correctly', () => {
|
52
|
+
const envStr = 'embedding_model=openai/embedding-text-3-small,query_mode=full_text';
|
53
|
+
const expected = {
|
54
|
+
queryMode: 'full_text',
|
55
|
+
embeddingModel: { provider: 'openai', model: 'embedding-text-3-small' },
|
56
|
+
};
|
57
|
+
expect(parseFilesConfig(envStr)).toEqual(expected);
|
58
|
+
});
|
59
|
+
|
60
|
+
it('parses rerank embeddings configuration correctly', () => {
|
61
|
+
const envStr =
|
62
|
+
'reranker_model=cohere/rerank-english-v3.0,embedding_model=openai/embedding-text-3-small';
|
63
|
+
const expected = {
|
64
|
+
embeddingModel: { provider: 'openai', model: 'embedding-text-3-small' },
|
12
65
|
rerankerModel: { provider: 'cohere', model: 'rerank-english-v3.0' },
|
13
|
-
queryModel: 'full_text',
|
14
66
|
};
|
15
67
|
expect(parseFilesConfig(envStr)).toEqual(expected);
|
16
68
|
});
|
69
|
+
|
70
|
+
it('should throw an error for invalid embedding_model format', () => {
|
71
|
+
const envStr =
|
72
|
+
'reranker_model=cohere/rerank-english-v3.0,embedding_model=/embedding-text-3-small';
|
73
|
+
expect(() => {
|
74
|
+
parseFilesConfig(envStr);
|
75
|
+
}).toThrow(
|
76
|
+
new Error(
|
77
|
+
'Invalid environment variable format. expected of the form embedding_model=provider/model',
|
78
|
+
),
|
79
|
+
);
|
80
|
+
});
|
81
|
+
|
82
|
+
it('should throw an error for invalid embedding_model format', () => {
|
83
|
+
const envStr = 'reranker_model=cohere/rerank-english-v3.0,embedding_model=openai';
|
84
|
+
expect(() => {
|
85
|
+
parseFilesConfig(envStr);
|
86
|
+
}).toThrow(
|
87
|
+
new Error(
|
88
|
+
'Invalid environment variable format. expected of the form embedding_model=provider/model',
|
89
|
+
),
|
90
|
+
);
|
91
|
+
});
|
92
|
+
|
93
|
+
it('should throw an error for invalid embedding_model format', () => {
|
94
|
+
const envStr = 'reranker_model=cohere/rerank-english-v3.0,embedding_model=';
|
95
|
+
expect(() => {
|
96
|
+
parseFilesConfig(envStr);
|
97
|
+
}).toThrowError(new Error('Invalid environment variable format.'));
|
98
|
+
});
|
99
|
+
|
100
|
+
it('should throw an error for invalid reranker_model format', () => {
|
101
|
+
const envStr =
|
102
|
+
'reranker_model=/rerank-english-v3.0,embedding_model=openai/embedding-text-3-small';
|
103
|
+
expect(() => {
|
104
|
+
parseFilesConfig(envStr);
|
105
|
+
}).toThrow(
|
106
|
+
new Error(
|
107
|
+
'Invalid environment variable format. expected of the form reranker_model=provider/model',
|
108
|
+
),
|
109
|
+
);
|
110
|
+
});
|
111
|
+
|
112
|
+
it('should throw an error for invalid reranker_model format', () => {
|
113
|
+
const envStr = 'reranker_model=cohere/,embedding_model=openai/embedding-text-3-small';
|
114
|
+
expect(() => {
|
115
|
+
parseFilesConfig(envStr);
|
116
|
+
}).toThrow(
|
117
|
+
new Error(
|
118
|
+
'Invalid environment variable format. expected of the form reranker_model=provider/model',
|
119
|
+
),
|
120
|
+
);
|
121
|
+
});
|
122
|
+
|
123
|
+
it('should throw an error for invalid reranker_model format', () => {
|
124
|
+
const envStr = 'reranker_model=,embedding_model=openai/embedding-text-3-small';
|
125
|
+
expect(() => {
|
126
|
+
parseFilesConfig(envStr);
|
127
|
+
}).toThrow(new Error('Invalid environment variable format.'));
|
128
|
+
});
|
129
|
+
|
130
|
+
it('should throw an error for invalid query_mode format', () => {
|
131
|
+
const envStr = 'query_mode=';
|
132
|
+
expect(() => {
|
133
|
+
parseFilesConfig(envStr);
|
134
|
+
}).toThrow(new Error('Invalid environment variable format.'));
|
135
|
+
});
|
17
136
|
});
|
@@ -4,7 +4,7 @@ import { FilesConfig } from '@/types/user/settings/filesConfig';
|
|
4
4
|
|
5
5
|
const protectedKeys = Object.keys({
|
6
6
|
embedding_model: null,
|
7
|
-
|
7
|
+
query_mode: null,
|
8
8
|
reranker_model: null,
|
9
9
|
});
|
10
10
|
|
@@ -24,34 +24,40 @@ export const parseFilesConfig = (envString: string = ''): SystemEmbeddingConfig
|
|
24
24
|
const [provider, ...modelParts] = value.split('/');
|
25
25
|
const model = modelParts.join('/');
|
26
26
|
|
27
|
-
if ((!provider || !model) && key !== 'query_model') {
|
28
|
-
throw new Error('Missing model or provider value');
|
29
|
-
}
|
30
|
-
|
31
|
-
if (key === 'query_model' && value === '') {
|
32
|
-
throw new Error('Missing query mode value');
|
33
|
-
}
|
34
|
-
|
35
27
|
if (protectedKeys.includes(key)) {
|
36
28
|
switch (key) {
|
37
29
|
case 'embedding_model': {
|
30
|
+
if (!provider || !model) {
|
31
|
+
throw new Error(
|
32
|
+
'Invalid environment variable format. expected of the form embedding_model=provider/model',
|
33
|
+
);
|
34
|
+
}
|
38
35
|
config.embeddingModel = { model: model.trim(), provider: provider.trim() };
|
39
36
|
break;
|
40
37
|
}
|
41
38
|
case 'reranker_model': {
|
39
|
+
if (!provider || !model) {
|
40
|
+
throw new Error(
|
41
|
+
'Invalid environment variable format. expected of the form reranker_model=provider/model',
|
42
|
+
);
|
43
|
+
}
|
42
44
|
config.rerankerModel = { model: model.trim(), provider: provider.trim() };
|
43
45
|
break;
|
44
46
|
}
|
45
|
-
case '
|
46
|
-
config.
|
47
|
+
case 'query_mode': {
|
48
|
+
config.queryMode = value;
|
47
49
|
break;
|
48
50
|
}
|
51
|
+
default: {
|
52
|
+
throw new Error(
|
53
|
+
'Invalid environment variable format. expected one of embedding_model, reranker_model, query_mode',
|
54
|
+
);
|
55
|
+
}
|
49
56
|
}
|
50
57
|
}
|
51
58
|
} else {
|
52
|
-
throw new Error('Invalid environment variable format');
|
59
|
+
throw new Error('Invalid environment variable format.');
|
53
60
|
}
|
54
61
|
}
|
55
|
-
|
56
62
|
return config;
|
57
63
|
};
|
@@ -44,6 +44,9 @@ export class S3 {
|
|
44
44
|
endpoint: fileEnv.S3_ENDPOINT,
|
45
45
|
forcePathStyle: fileEnv.S3_ENABLE_PATH_STYLE,
|
46
46
|
region: fileEnv.S3_REGION || DEFAULT_S3_REGION,
|
47
|
+
// refs: https://github.com/lobehub/lobe-chat/pull/5479
|
48
|
+
requestChecksumCalculation: 'WHEN_REQUIRED',
|
49
|
+
responseChecksumValidation: 'WHEN_REQUIRED',
|
47
50
|
});
|
48
51
|
}
|
49
52
|
|
package/src/services/_auth.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { JWTPayload, LOBE_CHAT_AUTH_HEADER } from '@/const/auth';
|
2
|
-
import {
|
2
|
+
import { isDeprecatedEdition } from '@/const/version';
|
3
3
|
import { ModelProvider } from '@/libs/agent-runtime';
|
4
4
|
import { aiProviderSelectors, useAiInfraStore } from '@/store/aiInfra';
|
5
5
|
import { useUserStore } from '@/store/user';
|
@@ -94,7 +94,7 @@ export const createPayloadWithKeyVaults = (provider: string) => {
|
|
94
94
|
let keyVaults = {};
|
95
95
|
|
96
96
|
// TODO: remove this condition in V2.0
|
97
|
-
if (
|
97
|
+
if (isDeprecatedEdition) {
|
98
98
|
keyVaults = keyVaultsConfigSelectors.getVaultByProvider(provider as any)(
|
99
99
|
useUserStore.getState(),
|
100
100
|
);
|