mirror of
https://github.com/EKKOLearnAI/hermes-web-ui.git
synced 2026-07-08 17:50:25 +00:00
4c38199171
* fix(models): add default provider and model actions * fix(models): keep default model action compact
176 lines
5.9 KiB
TypeScript
176 lines
5.9 KiB
TypeScript
// @vitest-environment jsdom
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import { createPinia, setActivePinia } from 'pinia'
|
|
|
|
const mockSystemApi = vi.hoisted(() => ({
|
|
fetchAvailableModels: vi.fn(),
|
|
fetchAvailableModelsForProfile: vi.fn(),
|
|
updateDefaultModel: vi.fn(),
|
|
addCustomProvider: vi.fn(),
|
|
removeCustomProvider: vi.fn(),
|
|
}))
|
|
|
|
vi.mock('@/api/hermes/system', () => mockSystemApi)
|
|
vi.mock('@/api/client', () => ({ hasApiKey: () => true }))
|
|
|
|
import { useAppStore } from '@/stores/hermes/app'
|
|
import { useModelsStore } from '@/stores/hermes/models'
|
|
|
|
describe('Models Store', () => {
|
|
beforeEach(() => {
|
|
setActivePinia(createPinia())
|
|
vi.clearAllMocks()
|
|
window.localStorage.clear()
|
|
})
|
|
|
|
it('keeps the sidebar model picker in sync after provider model visibility changes', async () => {
|
|
const visibleGroups = [
|
|
{
|
|
provider: 'deepseek',
|
|
label: 'DeepSeek',
|
|
base_url: 'https://api.deepseek.com/v1',
|
|
api_key: 'sk-test',
|
|
models: ['deepseek-v4-flash', 'deepseek-v4-pro'],
|
|
available_models: ['deepseek-v4-flash', 'deepseek-v4-pro'],
|
|
model_meta: {
|
|
'deepseek-v4-pro': { preview: true },
|
|
},
|
|
},
|
|
]
|
|
const availableModelsResponse = {
|
|
default: 'deepseek-v4-flash',
|
|
default_provider: 'deepseek',
|
|
groups: visibleGroups,
|
|
allProviders: visibleGroups,
|
|
model_visibility: {
|
|
deepseek: { mode: 'include', models: ['deepseek-v4-flash', 'deepseek-v4-pro'] },
|
|
},
|
|
profiles: [
|
|
{
|
|
profile: 'default',
|
|
default: 'deepseek-v4-flash',
|
|
default_provider: 'deepseek',
|
|
groups: visibleGroups,
|
|
},
|
|
],
|
|
}
|
|
mockSystemApi.fetchAvailableModelsForProfile.mockResolvedValue(availableModelsResponse)
|
|
mockSystemApi.fetchAvailableModels.mockResolvedValue(availableModelsResponse)
|
|
mockSystemApi.addCustomProvider.mockResolvedValue(undefined)
|
|
|
|
const appStore = useAppStore()
|
|
appStore.modelGroups = [
|
|
{
|
|
provider: 'deepseek',
|
|
label: 'DeepSeek',
|
|
base_url: 'https://api.deepseek.com/v1',
|
|
api_key: 'sk-test',
|
|
models: ['deepseek-v4-flash'],
|
|
available_models: ['deepseek-v4-flash', 'deepseek-v4-pro'],
|
|
},
|
|
]
|
|
|
|
const modelsStore = useModelsStore()
|
|
await modelsStore.addProvider({
|
|
name: 'deepseek',
|
|
base_url: 'https://api.deepseek.com/v1',
|
|
api_key: 'sk-test',
|
|
model: 'deepseek-v4-flash',
|
|
})
|
|
|
|
expect(mockSystemApi.fetchAvailableModelsForProfile).toHaveBeenCalledWith('default')
|
|
expect(mockSystemApi.fetchAvailableModels).toHaveBeenCalled()
|
|
expect(modelsStore.providers[0].models).toEqual(['deepseek-v4-flash', 'deepseek-v4-pro'])
|
|
expect(appStore.modelGroups[0].models).toEqual(['deepseek-v4-flash', 'deepseek-v4-pro'])
|
|
expect(appStore.modelGroups[0].available_models).toEqual(['deepseek-v4-flash', 'deepseek-v4-pro'])
|
|
expect(appStore.modelGroups[0].model_meta).toEqual({
|
|
'deepseek-v4-pro': { preview: true },
|
|
})
|
|
expect(appStore.modelVisibility).toEqual({
|
|
deepseek: { mode: 'include', models: ['deepseek-v4-flash', 'deepseek-v4-pro'] },
|
|
})
|
|
expect(appStore.selectedModel).toBe('deepseek-v4-flash')
|
|
expect(appStore.selectedProvider).toBe('deepseek')
|
|
})
|
|
|
|
it('sets the default provider to the first visible model when the current default is not available there', async () => {
|
|
const deepseekGroup = {
|
|
provider: 'deepseek',
|
|
label: 'DeepSeek',
|
|
base_url: 'https://api.deepseek.com/v1',
|
|
api_key: 'sk-test',
|
|
models: ['deepseek-chat'],
|
|
}
|
|
const openaiGroup = {
|
|
provider: 'openai',
|
|
label: 'OpenAI',
|
|
base_url: 'https://api.openai.com/v1',
|
|
api_key: 'sk-openai',
|
|
models: ['gpt-4.1', 'gpt-4.1-mini'],
|
|
}
|
|
const availableModelsResponse = {
|
|
default: 'gpt-4.1',
|
|
default_provider: 'openai',
|
|
groups: [deepseekGroup, openaiGroup],
|
|
allProviders: [deepseekGroup, openaiGroup],
|
|
}
|
|
|
|
mockSystemApi.fetchAvailableModels.mockResolvedValue(availableModelsResponse)
|
|
mockSystemApi.updateDefaultModel.mockResolvedValue(undefined)
|
|
|
|
const modelsStore = useModelsStore()
|
|
modelsStore.providers = [deepseekGroup, openaiGroup]
|
|
modelsStore.defaultModel = 'deepseek-chat'
|
|
modelsStore.defaultProvider = 'deepseek'
|
|
|
|
await modelsStore.setDefaultProvider('openai')
|
|
|
|
expect(mockSystemApi.updateDefaultModel).toHaveBeenCalledWith({
|
|
default: 'gpt-4.1',
|
|
provider: 'openai',
|
|
})
|
|
expect(modelsStore.defaultModel).toBe('gpt-4.1')
|
|
expect(modelsStore.defaultProvider).toBe('openai')
|
|
})
|
|
|
|
it('keeps the current default model when another provider exposes the same model id', async () => {
|
|
const providerA = {
|
|
provider: 'provider-a',
|
|
label: 'Provider A',
|
|
base_url: 'https://provider-a.example/v1',
|
|
api_key: 'sk-a',
|
|
models: ['shared-model'],
|
|
}
|
|
const providerB = {
|
|
provider: 'provider-b',
|
|
label: 'Provider B',
|
|
base_url: 'https://provider-b.example/v1',
|
|
api_key: 'sk-b',
|
|
models: ['shared-model', 'provider-b-only'],
|
|
}
|
|
const availableModelsResponse = {
|
|
default: 'shared-model',
|
|
default_provider: 'provider-b',
|
|
groups: [providerA, providerB],
|
|
allProviders: [providerA, providerB],
|
|
}
|
|
|
|
mockSystemApi.fetchAvailableModels.mockResolvedValue(availableModelsResponse)
|
|
mockSystemApi.updateDefaultModel.mockResolvedValue(undefined)
|
|
|
|
const modelsStore = useModelsStore()
|
|
modelsStore.providers = [providerA, providerB]
|
|
modelsStore.defaultModel = 'shared-model'
|
|
modelsStore.defaultProvider = 'provider-a'
|
|
|
|
await modelsStore.setDefaultProvider('provider-b')
|
|
|
|
expect(mockSystemApi.updateDefaultModel).toHaveBeenCalledWith({
|
|
default: 'shared-model',
|
|
provider: 'provider-b',
|
|
})
|
|
expect(modelsStore.defaultModel).toBe('shared-model')
|
|
expect(modelsStore.defaultProvider).toBe('provider-b')
|
|
})
|
|
})
|