@lobehub/chat 0.147.7 → 0.147.8
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/README.md +2 -2
- package/package.json +1 -1
- package/src/app/api/config/__snapshots__/route.test.ts.snap +45 -43
- package/src/app/api/config/parseDefaultAgent.test.ts +1 -1
- package/src/app/api/config/route.test.ts +2 -10
- package/src/app/api/config/route.ts +37 -16
- package/src/app/api/openai/createBizOpenAI/index.ts +3 -13
- package/src/app/api/openai/images/route.ts +1 -1
- package/src/app/api/openai/stt/route.ts +1 -1
- package/src/app/api/openai/tts/route.ts +1 -1
- package/src/app/chat/(desktop)/features/ChatHeader/Tags.tsx +1 -1
- package/src/app/chat/(desktop)/features/ChatInput/Footer/index.tsx +1 -1
- package/src/app/settings/llm/Azure/index.tsx +2 -2
- package/src/app/settings/llm/components/ProviderModelList/ModelFetcher.tsx +7 -3
- package/src/app/settings/llm/components/ProviderModelList/Option.tsx +4 -1
- package/src/app/settings/llm/components/ProviderModelList/index.tsx +4 -4
- package/src/config/__tests__/server.test.ts +0 -12
- package/src/config/server/provider.ts +12 -8
- package/src/features/AgentSetting/AgentConfig/ModelSelect.tsx +5 -2
- package/src/features/AgentSetting/AgentPrompt/TokenTag.tsx +1 -1
- package/src/features/ChatInput/ActionBar/FileUpload.tsx +2 -2
- package/src/features/ChatInput/ActionBar/Token/TokenTag.tsx +1 -1
- package/src/features/ChatInput/ActionBar/Token/index.tsx +1 -1
- package/src/features/ChatInput/ActionBar/Tools/index.tsx +1 -1
- package/src/features/ChatInput/useChatInput.ts +1 -1
- package/src/features/Conversation/Error/APIKeyForm/ProviderApiKeyForm.tsx +3 -3
- package/src/features/ModelSwitchPanel/index.tsx +5 -2
- package/src/migrations/FromV3ToV4/fixtures/openai-output-v4.json +1 -3
- package/src/migrations/FromV3ToV4/fixtures/openrouter-output-v4.json +2 -6
- package/src/migrations/FromV3ToV4/index.ts +8 -2
- package/src/services/_auth.ts +1 -3
- package/src/services/chat.ts +4 -5
- package/src/store/global/slices/settings/actions/llm.test.ts +2 -2
- package/src/store/global/slices/settings/actions/llm.ts +3 -3
- package/src/store/global/slices/settings/selectors/modelConfig.test.ts +0 -88
- package/src/store/global/slices/settings/selectors/modelConfig.ts +17 -81
- package/src/store/global/slices/settings/selectors/modelProvider.test.ts +99 -15
- package/src/store/global/slices/settings/selectors/modelProvider.ts +94 -30
- package/src/store/global/slices/settings/selectors/settings.ts +7 -1
- package/src/types/serverConfig.ts +1 -0
- package/src/types/settings/modelProvider.ts +0 -2
- package/src/utils/parseModels.test.ts +146 -0
- package/src/utils/parseModels.ts +67 -15
|
@@ -46,7 +46,7 @@ const Tools = memo(() => {
|
|
|
46
46
|
const { styles } = useStyles();
|
|
47
47
|
|
|
48
48
|
const model = useSessionStore(agentSelectors.currentAgentModel);
|
|
49
|
-
const enableFC = useGlobalStore(modelProviderSelectors.
|
|
49
|
+
const enableFC = useGlobalStore(modelProviderSelectors.isModelEnabledFunctionCall(model));
|
|
50
50
|
|
|
51
51
|
return (
|
|
52
52
|
<>
|
|
@@ -15,7 +15,7 @@ export const useChatInput = () => {
|
|
|
15
15
|
const onSend = useSendMessage();
|
|
16
16
|
|
|
17
17
|
const model = useSessionStore(agentSelectors.currentAgentModel);
|
|
18
|
-
const canUpload = useGlobalStore(modelProviderSelectors.
|
|
18
|
+
const canUpload = useGlobalStore(modelProviderSelectors.isModelEnabledUpload(model));
|
|
19
19
|
|
|
20
20
|
const [loading, value, onInput, onStop] = useChatStore((s) => [
|
|
21
21
|
!!s.chatLoadingId,
|
|
@@ -5,7 +5,7 @@ import { ReactNode, memo, useState } from 'react';
|
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
|
|
7
7
|
import { useGlobalStore } from '@/store/global';
|
|
8
|
-
import {
|
|
8
|
+
import { settingsSelectors } from '@/store/global/selectors';
|
|
9
9
|
import { GlobalLLMProviderKey } from '@/types/settings';
|
|
10
10
|
|
|
11
11
|
import { FormAction } from '../style';
|
|
@@ -24,8 +24,8 @@ const ProviderApiKeyForm = memo<ProviderApiKeyFormProps>(
|
|
|
24
24
|
const [showProxy, setShow] = useState(false);
|
|
25
25
|
|
|
26
26
|
const [apiKey, proxyUrl, setConfig] = useGlobalStore((s) => [
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
settingsSelectors.providerConfig(provider)(s)?.apiKey,
|
|
28
|
+
settingsSelectors.providerConfig(provider)(s)?.endpoint,
|
|
29
29
|
s.setModelProviderConfig,
|
|
30
30
|
]);
|
|
31
31
|
|
|
@@ -10,7 +10,7 @@ import { Flexbox } from 'react-layout-kit';
|
|
|
10
10
|
|
|
11
11
|
import { ModelItemRender, ProviderItemRender } from '@/components/ModelSelect';
|
|
12
12
|
import { useGlobalStore } from '@/store/global';
|
|
13
|
-
import {
|
|
13
|
+
import { modelProviderSelectors } from '@/store/global/selectors';
|
|
14
14
|
import { useSessionStore } from '@/store/session';
|
|
15
15
|
import { agentSelectors } from '@/store/session/selectors';
|
|
16
16
|
import { ModelProviderCard } from '@/types/llm';
|
|
@@ -44,7 +44,10 @@ const ModelSwitchPanel = memo<PropsWithChildren>(({ children }) => {
|
|
|
44
44
|
const updateAgentConfig = useSessionStore((s) => s.updateAgentConfig);
|
|
45
45
|
|
|
46
46
|
const router = useRouter();
|
|
47
|
-
const enabledList = useGlobalStore(
|
|
47
|
+
const enabledList = useGlobalStore(
|
|
48
|
+
modelProviderSelectors.modelProviderListForModelSelect,
|
|
49
|
+
isEqual,
|
|
50
|
+
);
|
|
48
51
|
|
|
49
52
|
const items = useMemo(() => {
|
|
50
53
|
const getModelItems = (provider: ModelProviderCard) => {
|
|
@@ -48,16 +48,12 @@
|
|
|
48
48
|
{
|
|
49
49
|
"displayName": "01-ai/yi-34b-chat",
|
|
50
50
|
"enabled": true,
|
|
51
|
-
"
|
|
52
|
-
"id": "01-ai/yi-34b-chat",
|
|
53
|
-
"vision": true
|
|
51
|
+
"id": "01-ai/yi-34b-chat"
|
|
54
52
|
},
|
|
55
53
|
{
|
|
56
54
|
"displayName": "huggingfaceh4/zephyr-7b-beta",
|
|
57
55
|
"enabled": true,
|
|
58
|
-
"
|
|
59
|
-
"id": "huggingfaceh4/zephyr-7b-beta",
|
|
60
|
-
"vision": true
|
|
56
|
+
"id": "huggingfaceh4/zephyr-7b-beta"
|
|
61
57
|
}
|
|
62
58
|
]
|
|
63
59
|
},
|
|
@@ -62,7 +62,10 @@ export class MigrationV3ToV4 implements Migration {
|
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
const customModelCards = transformToChatModelCards(
|
|
65
|
+
const customModelCards = transformToChatModelCards({
|
|
66
|
+
defaultChatModels: [],
|
|
67
|
+
modelString: openai.customModelName,
|
|
68
|
+
});
|
|
66
69
|
|
|
67
70
|
return {
|
|
68
71
|
azure: {
|
|
@@ -81,7 +84,10 @@ export class MigrationV3ToV4 implements Migration {
|
|
|
81
84
|
};
|
|
82
85
|
|
|
83
86
|
static migrateProvider = (provider: V3LegacyConfig): V4ProviderConfig => {
|
|
84
|
-
const customModelCards = transformToChatModelCards(
|
|
87
|
+
const customModelCards = transformToChatModelCards({
|
|
88
|
+
defaultChatModels: [],
|
|
89
|
+
modelString: provider.customModelName,
|
|
90
|
+
});
|
|
85
91
|
|
|
86
92
|
return {
|
|
87
93
|
apiKey: provider.apiKey,
|
package/src/services/_auth.ts
CHANGED
|
@@ -35,9 +35,7 @@ export const getProviderAuthPayload = (provider: string) => {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
default: {
|
|
38
|
-
const config =
|
|
39
|
-
useGlobalStore.getState(),
|
|
40
|
-
);
|
|
38
|
+
const config = settingsSelectors.providerConfig(provider)(useGlobalStore.getState());
|
|
41
39
|
|
|
42
40
|
return { apiKey: config?.apiKey, endpoint: config?.endpoint };
|
|
43
41
|
}
|
package/src/services/chat.ts
CHANGED
|
@@ -9,7 +9,6 @@ import { filesSelectors, useFileStore } from '@/store/file';
|
|
|
9
9
|
import { useGlobalStore } from '@/store/global';
|
|
10
10
|
import {
|
|
11
11
|
commonSelectors,
|
|
12
|
-
modelConfigSelectors,
|
|
13
12
|
modelProviderSelectors,
|
|
14
13
|
preferenceSelectors,
|
|
15
14
|
} from '@/store/global/selectors';
|
|
@@ -91,7 +90,7 @@ class ChatService {
|
|
|
91
90
|
const filterTools = toolSelectors.enabledSchema(enabledPlugins)(useToolStore.getState());
|
|
92
91
|
|
|
93
92
|
// check this model can use function call
|
|
94
|
-
const canUseFC = modelProviderSelectors.
|
|
93
|
+
const canUseFC = modelProviderSelectors.isModelEnabledFunctionCall(payload.model)(
|
|
95
94
|
useGlobalStore.getState(),
|
|
96
95
|
);
|
|
97
96
|
// the rule that model can use tools:
|
|
@@ -137,7 +136,7 @@ class ChatService {
|
|
|
137
136
|
|
|
138
137
|
// if the provider is Azure, get the deployment name as the request model
|
|
139
138
|
if (provider === ModelProvider.Azure) {
|
|
140
|
-
const chatModelCards =
|
|
139
|
+
const chatModelCards = modelProviderSelectors.getModelCardsById(provider)(
|
|
141
140
|
useGlobalStore.getState(),
|
|
142
141
|
);
|
|
143
142
|
|
|
@@ -257,7 +256,7 @@ class ChatService {
|
|
|
257
256
|
|
|
258
257
|
if (imageList.length === 0) return m.content;
|
|
259
258
|
|
|
260
|
-
const canUploadFile = modelProviderSelectors.
|
|
259
|
+
const canUploadFile = modelProviderSelectors.isModelEnabledUpload(model)(
|
|
261
260
|
useGlobalStore.getState(),
|
|
262
261
|
);
|
|
263
262
|
|
|
@@ -292,7 +291,7 @@ class ChatService {
|
|
|
292
291
|
|
|
293
292
|
return produce(postMessages, (draft) => {
|
|
294
293
|
if (!tools || tools.length === 0) return;
|
|
295
|
-
const hasFC = modelProviderSelectors.
|
|
294
|
+
const hasFC = modelProviderSelectors.isModelEnabledFunctionCall(model)(
|
|
296
295
|
useGlobalStore.getState(),
|
|
297
296
|
);
|
|
298
297
|
if (!hasFC) return;
|
|
@@ -3,7 +3,7 @@ import { describe, expect, it, vi } from 'vitest';
|
|
|
3
3
|
|
|
4
4
|
import { userService } from '@/services/user';
|
|
5
5
|
import { useGlobalStore } from '@/store/global';
|
|
6
|
-
import { modelConfigSelectors } from '@/store/global/slices/settings/selectors';
|
|
6
|
+
import { modelConfigSelectors, settingsSelectors } from '@/store/global/slices/settings/selectors';
|
|
7
7
|
import { GeneralModelProviderConfig } from '@/types/settings';
|
|
8
8
|
|
|
9
9
|
import { CustomModelCardDispatch, customModelCardsReducer } from '../reducers/customModelCard';
|
|
@@ -46,7 +46,7 @@ describe('LLMSettingsSliceAction', () => {
|
|
|
46
46
|
const payload: CustomModelCardDispatch = { type: 'add', modelCard: { id: 'test-id' } };
|
|
47
47
|
|
|
48
48
|
// Mock the selector to return undefined
|
|
49
|
-
vi.spyOn(
|
|
49
|
+
vi.spyOn(settingsSelectors, 'providerConfig').mockReturnValue(() => undefined);
|
|
50
50
|
vi.spyOn(result.current, 'setModelProviderConfig');
|
|
51
51
|
|
|
52
52
|
await act(async () => {
|
|
@@ -6,7 +6,7 @@ import { ChatModelCard } from '@/types/llm';
|
|
|
6
6
|
import { GlobalLLMConfig, GlobalLLMProviderKey } from '@/types/settings';
|
|
7
7
|
|
|
8
8
|
import { CustomModelCardDispatch, customModelCardsReducer } from '../reducers/customModelCard';
|
|
9
|
-
import {
|
|
9
|
+
import { settingsSelectors } from '../selectors/settings';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* 设置操作
|
|
@@ -37,7 +37,7 @@ export const llmSettingsSlice: StateCreator<
|
|
|
37
37
|
LLMSettingsAction
|
|
38
38
|
> = (set, get) => ({
|
|
39
39
|
dispatchCustomModelCards: async (provider, payload) => {
|
|
40
|
-
const prevState =
|
|
40
|
+
const prevState = settingsSelectors.providerConfig(provider)(get());
|
|
41
41
|
|
|
42
42
|
if (!prevState) return;
|
|
43
43
|
|
|
@@ -47,7 +47,7 @@ export const llmSettingsSlice: StateCreator<
|
|
|
47
47
|
},
|
|
48
48
|
|
|
49
49
|
removeEnabledModels: async (provider, model) => {
|
|
50
|
-
const config =
|
|
50
|
+
const config = settingsSelectors.providerConfig(provider)(get());
|
|
51
51
|
|
|
52
52
|
await get().setModelProviderConfig(provider, {
|
|
53
53
|
enabledModels: config?.enabledModels?.filter((s) => s !== model).filter(Boolean),
|
|
@@ -7,55 +7,6 @@ import { GlobalSettingsState, initialSettingsState } from '../initialState';
|
|
|
7
7
|
import { modelConfigSelectors } from './modelConfig';
|
|
8
8
|
|
|
9
9
|
describe('modelConfigSelectors', () => {
|
|
10
|
-
describe('providerListWithConfig', () => {
|
|
11
|
-
it('visible', () => {
|
|
12
|
-
const s = merge(initialSettingsState, {
|
|
13
|
-
settings: {
|
|
14
|
-
languageModel: {
|
|
15
|
-
ollama: {
|
|
16
|
-
enabledModels: ['llava'],
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
},
|
|
20
|
-
} as GlobalSettingsState) as unknown as GlobalStore;
|
|
21
|
-
|
|
22
|
-
const ollamaList = modelConfigSelectors
|
|
23
|
-
.providerListWithConfig(s)
|
|
24
|
-
.find((r) => r.id === 'ollama');
|
|
25
|
-
|
|
26
|
-
expect(ollamaList?.chatModels.find((c) => c.id === 'llava')).toEqual({
|
|
27
|
-
displayName: 'LLaVA 7B',
|
|
28
|
-
functionCall: false,
|
|
29
|
-
enabled: true,
|
|
30
|
-
id: 'llava',
|
|
31
|
-
tokens: 4000,
|
|
32
|
-
vision: true,
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
it('with user custom models', () => {
|
|
36
|
-
const s = merge(initialSettingsState, {
|
|
37
|
-
settings: {
|
|
38
|
-
languageModel: {
|
|
39
|
-
perplexity: {
|
|
40
|
-
customModelCards: [{ id: 'sonar-online', displayName: 'Sonar Online' }],
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
} as GlobalSettingsState) as unknown as GlobalStore;
|
|
45
|
-
|
|
46
|
-
const providerList = modelConfigSelectors
|
|
47
|
-
.providerListWithConfig(s)
|
|
48
|
-
.find((r) => r.id === 'perplexity');
|
|
49
|
-
|
|
50
|
-
expect(providerList?.chatModels.find((c) => c.id === 'sonar-online')).toEqual({
|
|
51
|
-
id: 'sonar-online',
|
|
52
|
-
displayName: 'Sonar Online',
|
|
53
|
-
enabled: false,
|
|
54
|
-
isCustom: true,
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
10
|
describe('isProviderEnabled', () => {
|
|
60
11
|
it('should return true if provider is enabled', () => {
|
|
61
12
|
const s = merge(initialSettingsState, {
|
|
@@ -82,45 +33,6 @@ describe('modelConfigSelectors', () => {
|
|
|
82
33
|
});
|
|
83
34
|
});
|
|
84
35
|
|
|
85
|
-
describe('getModelCardsByProviderId', () => {
|
|
86
|
-
it('should return model cards including custom model cards', () => {
|
|
87
|
-
const s = merge(initialSettingsState, {
|
|
88
|
-
settings: {
|
|
89
|
-
languageModel: {
|
|
90
|
-
perplexity: {
|
|
91
|
-
customModelCards: [{ id: 'custom-model', displayName: 'Custom Model' }],
|
|
92
|
-
},
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
} as GlobalSettingsState) as unknown as GlobalStore;
|
|
96
|
-
|
|
97
|
-
const modelCards = modelConfigSelectors.getModelCardsByProviderId('perplexity')(s);
|
|
98
|
-
|
|
99
|
-
expect(modelCards).toContainEqual({
|
|
100
|
-
id: 'custom-model',
|
|
101
|
-
displayName: 'Custom Model',
|
|
102
|
-
isCustom: true,
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
describe('providerListForModelSelect', () => {
|
|
108
|
-
it('should return only enabled providers', () => {
|
|
109
|
-
const s = merge(initialSettingsState, {
|
|
110
|
-
settings: {
|
|
111
|
-
languageModel: {
|
|
112
|
-
perplexity: { enabled: true },
|
|
113
|
-
azure: { enabled: false },
|
|
114
|
-
},
|
|
115
|
-
},
|
|
116
|
-
} as GlobalSettingsState) as unknown as GlobalStore;
|
|
117
|
-
|
|
118
|
-
const enabledProviders = modelConfigSelectors.providerListForModelSelect(s);
|
|
119
|
-
expect(enabledProviders).toHaveLength(2);
|
|
120
|
-
expect(enabledProviders[1].id).toBe('perplexity');
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
|
|
124
36
|
describe('getCustomModelCardById', () => {
|
|
125
37
|
it('should return the custom model card with the given id and provider', () => {
|
|
126
38
|
const s = merge(initialSettingsState, {
|
|
@@ -1,76 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { ChatModelCard, ModelProviderCard } from '@/types/llm';
|
|
4
|
-
import { GeneralModelProviderConfig, GlobalLLMProviderKey } from '@/types/settings';
|
|
1
|
+
import { GlobalLLMProviderKey } from '@/types/settings';
|
|
5
2
|
|
|
6
3
|
import { GlobalStore } from '../../../store';
|
|
7
|
-
import {
|
|
8
|
-
import { currentSettings } from './settings';
|
|
9
|
-
|
|
10
|
-
const getConfigByProviderId = (provider: string) => (s: GlobalStore) =>
|
|
11
|
-
currentSettings(s).languageModel[provider as GlobalLLMProviderKey] as
|
|
12
|
-
| GeneralModelProviderConfig
|
|
13
|
-
| undefined;
|
|
14
|
-
|
|
15
|
-
const getModeProviderById = (provider: string) => (s: GlobalStore) =>
|
|
16
|
-
modelProviderSelectors.providerModelList(s).find((s) => s.id === provider);
|
|
4
|
+
import { currentLLMSettings, getProviderConfigById } from './settings';
|
|
17
5
|
|
|
18
6
|
const isProviderEnabled = (provider: GlobalLLMProviderKey) => (s: GlobalStore) =>
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const getEnableModelsByProviderId = (provider: string) => (s: GlobalStore) => {
|
|
22
|
-
if (!getConfigByProviderId(provider)(s)?.enabledModels) return;
|
|
23
|
-
|
|
24
|
-
return getConfigByProviderId(provider)(s)?.enabledModels?.filter(Boolean);
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const getModelCardsByProviderId =
|
|
28
|
-
(provider: string) =>
|
|
29
|
-
(s: GlobalStore): ChatModelCard[] => {
|
|
30
|
-
const builtinCards = getModeProviderById(provider)(s)?.chatModels || [];
|
|
31
|
-
|
|
32
|
-
const userCards = (getConfigByProviderId(provider)(s)?.customModelCards || []).map((model) => ({
|
|
33
|
-
...model,
|
|
34
|
-
isCustom: true,
|
|
35
|
-
}));
|
|
36
|
-
|
|
37
|
-
return uniqBy([...userCards, ...builtinCards], 'id');
|
|
38
|
-
};
|
|
7
|
+
getProviderConfigById(provider)(s)?.enabled || false;
|
|
39
8
|
|
|
40
9
|
const getCustomModelCard =
|
|
41
10
|
({ id, provider }: { id?: string; provider?: string }) =>
|
|
42
11
|
(s: GlobalStore) => {
|
|
43
12
|
if (!provider) return;
|
|
44
13
|
|
|
45
|
-
const config =
|
|
14
|
+
const config = getProviderConfigById(provider)(s);
|
|
46
15
|
|
|
47
16
|
return config?.customModelCards?.find((m) => m.id === id);
|
|
48
17
|
};
|
|
49
18
|
|
|
50
|
-
const providerListWithConfig = (s: GlobalStore): ModelProviderCard[] =>
|
|
51
|
-
modelProviderSelectors.providerModelList(s).map((list) => ({
|
|
52
|
-
...list,
|
|
53
|
-
chatModels: getModelCardsByProviderId(list.id)(s)?.map((model) => {
|
|
54
|
-
const models = getEnableModelsByProviderId(list.id)(s);
|
|
55
|
-
|
|
56
|
-
if (!models) return model;
|
|
57
|
-
|
|
58
|
-
return {
|
|
59
|
-
...model,
|
|
60
|
-
enabled: models?.some((m) => m === model.id),
|
|
61
|
-
};
|
|
62
|
-
}),
|
|
63
|
-
enabled: isProviderEnabled(list.id as any)(s),
|
|
64
|
-
}));
|
|
65
|
-
|
|
66
|
-
const providerListForModelSelect = (s: GlobalStore): ModelProviderCard[] =>
|
|
67
|
-
providerListWithConfig(s)
|
|
68
|
-
.filter((s) => s.enabled)
|
|
69
|
-
.map((provider) => ({
|
|
70
|
-
...provider,
|
|
71
|
-
chatModels: provider.chatModels.filter((model) => model.enabled),
|
|
72
|
-
}));
|
|
73
|
-
|
|
74
19
|
const currentEditingCustomModelCard = (s: GlobalStore) => {
|
|
75
20
|
if (!s.editingCustomCardModel) return;
|
|
76
21
|
const { id, provider } = s.editingCustomCardModel;
|
|
@@ -81,36 +26,27 @@ const currentEditingCustomModelCard = (s: GlobalStore) => {
|
|
|
81
26
|
const isAutoFetchModelsEnabled =
|
|
82
27
|
(provider: GlobalLLMProviderKey) =>
|
|
83
28
|
(s: GlobalStore): boolean => {
|
|
84
|
-
return
|
|
29
|
+
return getProviderConfigById(provider)(s)?.autoFetchModelLists || false;
|
|
85
30
|
};
|
|
86
31
|
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
const ollamaConfig = (s: GlobalStore) => llmSettings(s).ollama;
|
|
92
|
-
const azureConfig = (s: GlobalStore) => llmSettings(s).azure;
|
|
32
|
+
const openAIConfig = (s: GlobalStore) => currentLLMSettings(s).openai;
|
|
33
|
+
const bedrockConfig = (s: GlobalStore) => currentLLMSettings(s).bedrock;
|
|
34
|
+
const ollamaConfig = (s: GlobalStore) => currentLLMSettings(s).ollama;
|
|
35
|
+
const azureConfig = (s: GlobalStore) => currentLLMSettings(s).azure;
|
|
93
36
|
|
|
94
|
-
const isAzureEnabled = (s: GlobalStore) =>
|
|
37
|
+
const isAzureEnabled = (s: GlobalStore) => currentLLMSettings(s).azure.enabled;
|
|
95
38
|
|
|
96
|
-
/* eslint-disable sort-keys-fix/sort-keys-fix, */
|
|
97
39
|
export const modelConfigSelectors = {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
currentEditingCustomModelCard,
|
|
40
|
+
azureConfig,
|
|
41
|
+
bedrockConfig,
|
|
101
42
|
|
|
102
|
-
|
|
103
|
-
getEnableModelsByProviderId,
|
|
104
|
-
getModelCardsByProviderId,
|
|
43
|
+
currentEditingCustomModelCard,
|
|
105
44
|
getCustomModelCard,
|
|
106
45
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
openAIConfig,
|
|
111
|
-
azureConfig,
|
|
112
|
-
bedrockConfig,
|
|
46
|
+
isAutoFetchModelsEnabled,
|
|
47
|
+
isAzureEnabled,
|
|
48
|
+
isProviderEnabled,
|
|
113
49
|
ollamaConfig,
|
|
114
50
|
|
|
115
|
-
|
|
51
|
+
openAIConfig,
|
|
116
52
|
};
|
|
@@ -1,54 +1,138 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
2
|
|
|
3
|
-
import { DEFAULT_SETTINGS } from '@/const/settings';
|
|
4
|
-
import { agentSelectors } from '@/store/session/slices/agent';
|
|
5
3
|
import { merge } from '@/utils/merge';
|
|
6
4
|
|
|
7
5
|
import { GlobalStore, useGlobalStore } from '../../../store';
|
|
8
|
-
import { initialSettingsState } from '../initialState';
|
|
9
|
-
import { modelProviderSelectors } from './modelProvider';
|
|
6
|
+
import { GlobalSettingsState, initialSettingsState } from '../initialState';
|
|
7
|
+
import { getDefaultModeProviderById, modelProviderSelectors } from './modelProvider';
|
|
10
8
|
|
|
11
9
|
describe('modelProviderSelectors', () => {
|
|
10
|
+
describe('providerListWithConfig', () => {
|
|
11
|
+
it('visible', () => {
|
|
12
|
+
const s = merge(initialSettingsState, {
|
|
13
|
+
settings: {
|
|
14
|
+
languageModel: {
|
|
15
|
+
ollama: {
|
|
16
|
+
enabledModels: ['llava'],
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
} as GlobalSettingsState) as unknown as GlobalStore;
|
|
21
|
+
|
|
22
|
+
const ollamaList = modelProviderSelectors.modelProviderList(s).find((r) => r.id === 'ollama');
|
|
23
|
+
|
|
24
|
+
expect(ollamaList?.chatModels.find((c) => c.id === 'llava')).toEqual({
|
|
25
|
+
displayName: 'LLaVA 7B',
|
|
26
|
+
functionCall: false,
|
|
27
|
+
enabled: true,
|
|
28
|
+
id: 'llava',
|
|
29
|
+
tokens: 4000,
|
|
30
|
+
vision: true,
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
it('with user custom models', () => {
|
|
34
|
+
const s = merge(initialSettingsState, {
|
|
35
|
+
settings: {
|
|
36
|
+
languageModel: {
|
|
37
|
+
perplexity: {
|
|
38
|
+
customModelCards: [{ id: 'sonar-online', displayName: 'Sonar Online' }],
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
} as GlobalSettingsState) as unknown as GlobalStore;
|
|
43
|
+
|
|
44
|
+
const providerList = modelProviderSelectors
|
|
45
|
+
.modelProviderList(s)
|
|
46
|
+
.find((r) => r.id === 'perplexity');
|
|
47
|
+
|
|
48
|
+
expect(providerList?.chatModels.find((c) => c.id === 'sonar-online')).toEqual({
|
|
49
|
+
id: 'sonar-online',
|
|
50
|
+
displayName: 'Sonar Online',
|
|
51
|
+
enabled: false,
|
|
52
|
+
isCustom: true,
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('providerListForModelSelect', () => {
|
|
58
|
+
it('should return only enabled providers', () => {
|
|
59
|
+
const s = merge(initialSettingsState, {
|
|
60
|
+
settings: {
|
|
61
|
+
languageModel: {
|
|
62
|
+
perplexity: { enabled: true },
|
|
63
|
+
azure: { enabled: false },
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
} as GlobalSettingsState) as unknown as GlobalStore;
|
|
67
|
+
|
|
68
|
+
const enabledProviders = modelProviderSelectors.modelProviderListForModelSelect(s);
|
|
69
|
+
expect(enabledProviders).toHaveLength(2);
|
|
70
|
+
expect(enabledProviders[1].id).toBe('perplexity');
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
12
74
|
describe('providerCard', () => {
|
|
13
75
|
it('should return the correct ModelProviderCard when provider ID matches', () => {
|
|
14
76
|
const s = merge(initialSettingsState, {}) as unknown as GlobalStore;
|
|
15
77
|
|
|
16
|
-
const result =
|
|
78
|
+
const result = getDefaultModeProviderById('openai')(s);
|
|
17
79
|
expect(result).not.toBeUndefined();
|
|
18
80
|
});
|
|
19
81
|
|
|
20
82
|
it('should return undefined when provider ID does not exist', () => {
|
|
21
83
|
const s = merge(initialSettingsState, {}) as unknown as GlobalStore;
|
|
22
|
-
const result =
|
|
84
|
+
const result = getDefaultModeProviderById('nonExistingProvider')(s);
|
|
23
85
|
expect(result).toBeUndefined();
|
|
24
86
|
});
|
|
25
87
|
});
|
|
26
88
|
|
|
89
|
+
describe('getModelCardsById', () => {
|
|
90
|
+
it('should return model cards including custom model cards', () => {
|
|
91
|
+
const s = merge(initialSettingsState, {
|
|
92
|
+
settings: {
|
|
93
|
+
languageModel: {
|
|
94
|
+
perplexity: {
|
|
95
|
+
customModelCards: [{ id: 'custom-model', displayName: 'Custom Model' }],
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
} as GlobalSettingsState) as unknown as GlobalStore;
|
|
100
|
+
|
|
101
|
+
const modelCards = modelProviderSelectors.getModelCardsById('perplexity')(s);
|
|
102
|
+
|
|
103
|
+
expect(modelCards).toContainEqual({
|
|
104
|
+
id: 'custom-model',
|
|
105
|
+
displayName: 'Custom Model',
|
|
106
|
+
isCustom: true,
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
27
111
|
describe('defaultEnabledProviderModels', () => {
|
|
28
112
|
it('should return enabled models for a given provider', () => {
|
|
29
113
|
const s = merge(initialSettingsState, {}) as unknown as GlobalStore;
|
|
30
114
|
|
|
31
|
-
const result = modelProviderSelectors.
|
|
115
|
+
const result = modelProviderSelectors.getDefaultEnabledModelsById('openai')(s);
|
|
32
116
|
expect(result).toEqual(['gpt-3.5-turbo', 'gpt-4-turbo']);
|
|
33
117
|
});
|
|
34
118
|
|
|
35
119
|
it('should return undefined for a non-existing provider', () => {
|
|
36
120
|
const s = merge(initialSettingsState, {}) as unknown as GlobalStore;
|
|
37
121
|
|
|
38
|
-
const result = modelProviderSelectors.
|
|
122
|
+
const result = modelProviderSelectors.getDefaultEnabledModelsById('nonExistingProvider')(s);
|
|
39
123
|
expect(result).toBeUndefined();
|
|
40
124
|
});
|
|
41
125
|
});
|
|
42
126
|
describe('modelEnabledVision', () => {
|
|
43
127
|
it('should return true if the model has vision ability', () => {
|
|
44
|
-
const hasAbility = modelProviderSelectors.
|
|
128
|
+
const hasAbility = modelProviderSelectors.isModelEnabledVision('gpt-4-vision-preview')(
|
|
45
129
|
useGlobalStore.getState(),
|
|
46
130
|
);
|
|
47
131
|
expect(hasAbility).toBeTruthy();
|
|
48
132
|
});
|
|
49
133
|
|
|
50
134
|
it('should return false if the model does not have vision ability', () => {
|
|
51
|
-
const hasAbility = modelProviderSelectors.
|
|
135
|
+
const hasAbility = modelProviderSelectors.isModelEnabledVision('some-other-model')(
|
|
52
136
|
useGlobalStore.getState(),
|
|
53
137
|
);
|
|
54
138
|
|
|
@@ -56,7 +140,7 @@ describe('modelProviderSelectors', () => {
|
|
|
56
140
|
});
|
|
57
141
|
|
|
58
142
|
it('should return false if the model include vision in id', () => {
|
|
59
|
-
const hasAbility = modelProviderSelectors.
|
|
143
|
+
const hasAbility = modelProviderSelectors.isModelEnabledVision('some-other-model-vision')(
|
|
60
144
|
useGlobalStore.getState(),
|
|
61
145
|
);
|
|
62
146
|
|
|
@@ -66,14 +150,14 @@ describe('modelProviderSelectors', () => {
|
|
|
66
150
|
|
|
67
151
|
describe('modelEnabledFiles', () => {
|
|
68
152
|
it('should return false if the model does not have file ability', () => {
|
|
69
|
-
const enabledFiles = modelProviderSelectors.
|
|
153
|
+
const enabledFiles = modelProviderSelectors.isModelEnabledFiles('gpt-4-vision-preview')(
|
|
70
154
|
useGlobalStore.getState(),
|
|
71
155
|
);
|
|
72
156
|
expect(enabledFiles).toBeFalsy();
|
|
73
157
|
});
|
|
74
158
|
|
|
75
159
|
it('should return true if the model has file ability', () => {
|
|
76
|
-
const enabledFiles = modelProviderSelectors.
|
|
160
|
+
const enabledFiles = modelProviderSelectors.isModelEnabledFiles('gpt-4-all')(
|
|
77
161
|
useGlobalStore.getState(),
|
|
78
162
|
);
|
|
79
163
|
expect(enabledFiles).toBeTruthy();
|
|
@@ -82,14 +166,14 @@ describe('modelProviderSelectors', () => {
|
|
|
82
166
|
|
|
83
167
|
describe('modelHasMaxToken', () => {
|
|
84
168
|
it('should return true if the model is in the list of models that show tokens', () => {
|
|
85
|
-
const show = modelProviderSelectors.
|
|
169
|
+
const show = modelProviderSelectors.isModelHasMaxToken('gpt-3.5-turbo')(
|
|
86
170
|
useGlobalStore.getState(),
|
|
87
171
|
);
|
|
88
172
|
expect(show).toBeTruthy();
|
|
89
173
|
});
|
|
90
174
|
|
|
91
175
|
it('should return false if the model is not in the list of models that show tokens', () => {
|
|
92
|
-
const show = modelProviderSelectors.
|
|
176
|
+
const show = modelProviderSelectors.isModelHasMaxToken('some-other-model')(
|
|
93
177
|
useGlobalStore.getState(),
|
|
94
178
|
);
|
|
95
179
|
expect(show).toBe(false);
|