feat(models): 增加模型显示名重命名 (#614)
* feat(models): add WUI model display aliases Persist display-only model aliases in Web UI app config, surface them in the model selector/search, and keep canonical model IDs for Hermes calls. * fix(models): improve WUI model alias editing * fix(models): clarify unlisted model picker * fix(models): scope aliases to providers
This commit is contained in:
@@ -1,6 +1,17 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
import { checkHealth, fetchAvailableModels, updateDefaultModel, updateModelVisibility, triggerUpdate, type AvailableModelGroup, type AvailableModelsResponse, type ModelVisibility, type ModelVisibilityRule } from '@/api/hermes/system'
|
||||
import {
|
||||
checkHealth,
|
||||
fetchAvailableModels,
|
||||
updateDefaultModel,
|
||||
updateModelVisibility,
|
||||
triggerUpdate,
|
||||
updateModelAlias,
|
||||
type AvailableModelGroup,
|
||||
type AvailableModelsResponse,
|
||||
type ModelVisibility,
|
||||
type ModelVisibilityRule,
|
||||
} from '@/api/hermes/system'
|
||||
|
||||
const WEB_UI_VERSION = __APP_VERSION__
|
||||
|
||||
@@ -20,6 +31,7 @@ export const useAppStore = defineStore('app', () => {
|
||||
const selectedModel = ref('')
|
||||
const selectedProvider = ref('')
|
||||
const customModels = ref<Record<string, string[]>>({})
|
||||
const modelAliases = ref<Record<string, Record<string, string>>>({})
|
||||
const modelVisibility = ref<ModelVisibility>({})
|
||||
const healthPollTimer = ref<ReturnType<typeof setInterval>>()
|
||||
const nodeVersion = ref('')
|
||||
@@ -61,13 +73,55 @@ export const useAppStore = defineStore('app', () => {
|
||||
|
||||
function applyAvailableModelsResponse(res: AvailableModelsResponse) {
|
||||
modelGroups.value = res.groups
|
||||
modelAliases.value = res.model_aliases || {}
|
||||
modelVisibility.value = res.model_visibility || {}
|
||||
const defaultGroup = res.groups.find(g => g.provider === (res.default_provider || '') && g.models.includes(res.default))
|
||||
const inferredGroup = res.groups.find(g => g.models.includes(res.default))
|
||||
|
||||
const defaultModel = res.default || ''
|
||||
const defaultProvider = res.default_provider || ''
|
||||
const explicitGroup = res.groups.find(g => g.provider === defaultProvider && g.models.includes(defaultModel))
|
||||
const inferredGroup = res.groups.find(g => g.models.includes(defaultModel))
|
||||
const fallbackGroup = res.groups.find(g => g.models.length > 0)
|
||||
const selectedGroup = defaultGroup || inferredGroup || fallbackGroup
|
||||
selectedModel.value = selectedGroup ? (defaultGroup || inferredGroup ? res.default : selectedGroup.models[0]) : ''
|
||||
selectedProvider.value = selectedGroup?.provider || ''
|
||||
|
||||
const providerGroup = defaultProvider ? res.groups.find(g => g.provider === defaultProvider) : undefined
|
||||
const allProvider = defaultProvider ? res.allProviders.find(g => g.provider === defaultProvider) : undefined
|
||||
const providerCatalog = providerGroup?.available_models?.length
|
||||
? providerGroup.available_models
|
||||
: allProvider?.available_models?.length
|
||||
? allProvider.available_models
|
||||
: allProvider?.models || []
|
||||
const visibilityRule = defaultProvider ? modelVisibility.value[defaultProvider] : undefined
|
||||
const hiddenByVisibility = !!(
|
||||
defaultModel &&
|
||||
visibilityRule?.mode === 'include' &&
|
||||
!visibilityRule.models.includes(defaultModel) &&
|
||||
(providerCatalog.length === 0 || providerCatalog.includes(defaultModel))
|
||||
)
|
||||
const unlistedDefault = !!(
|
||||
defaultModel &&
|
||||
defaultProvider &&
|
||||
providerGroup &&
|
||||
!providerGroup.models.includes(defaultModel) &&
|
||||
!hiddenByVisibility
|
||||
)
|
||||
|
||||
if (explicitGroup || inferredGroup) {
|
||||
const selectedGroup = explicitGroup || inferredGroup!
|
||||
selectedModel.value = defaultModel
|
||||
selectedProvider.value = selectedGroup.provider
|
||||
customModels.value = {}
|
||||
} else if (unlistedDefault) {
|
||||
selectedModel.value = defaultModel
|
||||
selectedProvider.value = defaultProvider
|
||||
customModels.value = { [defaultProvider]: [defaultModel] }
|
||||
} else if (fallbackGroup) {
|
||||
selectedModel.value = fallbackGroup.models[0]
|
||||
selectedProvider.value = fallbackGroup.provider
|
||||
customModels.value = {}
|
||||
} else {
|
||||
selectedModel.value = ''
|
||||
selectedProvider.value = ''
|
||||
customModels.value = {}
|
||||
}
|
||||
}
|
||||
|
||||
async function loadModels() {
|
||||
@@ -79,6 +133,34 @@ export const useAppStore = defineStore('app', () => {
|
||||
}
|
||||
}
|
||||
|
||||
function getModelAlias(modelId: string, provider?: string): string {
|
||||
if (provider) return modelAliases.value[provider]?.[modelId] || ''
|
||||
for (const aliases of Object.values(modelAliases.value)) {
|
||||
if (aliases[modelId]) return aliases[modelId]
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
function displayModelName(modelId: string, provider?: string): string {
|
||||
return getModelAlias(modelId, provider) || modelId
|
||||
}
|
||||
|
||||
async function setModelAlias(modelId: string, provider: string, alias: string) {
|
||||
const cleanAlias = alias.trim()
|
||||
await updateModelAlias({ provider, model: modelId, alias: cleanAlias })
|
||||
const next = { ...modelAliases.value }
|
||||
const providerAliases = { ...(next[provider] || {}) }
|
||||
if (cleanAlias) {
|
||||
providerAliases[modelId] = cleanAlias
|
||||
next[provider] = providerAliases
|
||||
} else {
|
||||
delete providerAliases[modelId]
|
||||
if (Object.keys(providerAliases).length > 0) next[provider] = providerAliases
|
||||
else delete next[provider]
|
||||
}
|
||||
modelAliases.value = next
|
||||
}
|
||||
|
||||
async function switchModel(modelId: string, providerOverride?: string) {
|
||||
try {
|
||||
// Find the group containing this model to get provider info
|
||||
@@ -99,6 +181,27 @@ export const useAppStore = defineStore('app', () => {
|
||||
}
|
||||
}
|
||||
|
||||
async function removeCustomModel(modelId: string, provider: string) {
|
||||
const providerModels = customModels.value[provider] || []
|
||||
if (!providerModels.includes(modelId)) return
|
||||
|
||||
const nextCustomModels = { ...customModels.value }
|
||||
const remaining = providerModels.filter(m => m !== modelId)
|
||||
if (remaining.length > 0) nextCustomModels[provider] = remaining
|
||||
else delete nextCustomModels[provider]
|
||||
customModels.value = nextCustomModels
|
||||
|
||||
if (selectedModel.value === modelId && selectedProvider.value === provider) {
|
||||
const providerGroup = modelGroups.value.find(g => g.provider === provider && g.models.length > 0)
|
||||
const fallbackGroup = providerGroup || modelGroups.value.find(g => g.models.length > 0)
|
||||
if (fallbackGroup) {
|
||||
await switchModel(fallbackGroup.models[0], fallbackGroup.provider)
|
||||
} else {
|
||||
selectedModel.value = ''
|
||||
selectedProvider.value = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getProviderVisibility(provider: string): ModelVisibilityRule {
|
||||
return modelVisibility.value[provider] || { mode: 'all', models: [] }
|
||||
@@ -160,6 +263,7 @@ export const useAppStore = defineStore('app', () => {
|
||||
doUpdate,
|
||||
modelGroups,
|
||||
customModels,
|
||||
modelAliases,
|
||||
modelVisibility,
|
||||
selectedModel,
|
||||
selectedProvider,
|
||||
@@ -170,6 +274,10 @@ export const useAppStore = defineStore('app', () => {
|
||||
loadModels,
|
||||
applyAvailableModelsResponse,
|
||||
switchModel,
|
||||
removeCustomModel,
|
||||
getModelAlias,
|
||||
displayModelName,
|
||||
setModelAlias,
|
||||
getProviderVisibility,
|
||||
isModelVisible,
|
||||
setModelVisibility,
|
||||
|
||||
Reference in New Issue
Block a user