diff --git a/frontend/src/views/admin/ModelManagement.vue b/frontend/src/views/admin/ModelManagement.vue index e8b09d9cc..3273612df 100644 --- a/frontend/src/views/admin/ModelManagement.vue +++ b/frontend/src/views/admin/ModelManagement.vue @@ -88,9 +88,6 @@ 价格 ($/M) - - {{ t('models.pricingSource.label') }} - 提供商 @@ -108,7 +105,7 @@ @@ -116,7 +113,7 @@ 没有找到匹配的模型 @@ -196,24 +193,6 @@ - - - {{ model.active_provider_count || 0 }}/{{ model.provider_count || 0 }} @@ -358,26 +337,6 @@ ${{ getFirstTierPrice(model, 'input')?.toFixed(2) || '-' }}/${{ getFirstTierPrice(model, 'output')?.toFixed(2) || '-' }} - -
- {{ t('models.pricingSource.label') }} - -
@@ -782,7 +741,6 @@ import { } from 'lucide-vue-next' import ModelDetailDrawer from '@/features/models/components/ModelDetailDrawer.vue' import GlobalModelFormDialog from '@/features/models/components/GlobalModelFormDialog.vue' -import GlobalModelPricingSourceSelect from '@/features/models/components/GlobalModelPricingSourceSelect.vue' import ExternalModelsAccessControl from '@/features/models/components/ExternalModelsAccessControl.vue' import ProviderModelFormDialog from '@/features/providers/components/ProviderModelFormDialog.vue' import type { Model } from '@/api/endpoints' @@ -889,7 +847,6 @@ const batchManageModels = ref([]) const batchManageLoading = ref(false) const batchManageOnlineModels = ref([]) const batchManageOnlineLoading = ref(false) -const modelPricingSourceSyncingIds = ref>(new Set()) const GLOBAL_MODELS_BATCH_FETCH_PAGE_SIZE = 1000 let globalModelsRequestId = 0 let modelSelectionRequestId = 0 @@ -1508,53 +1465,6 @@ const filteredBatchManageModels = computed(() => { }) }) -const onlinePricingCandidatesByModelName = computed(() => { - const candidatesByName = new Map() - for (const onlineModel of batchManageOnlineModels.value) { - const normalizedName = onlineModel.modelId.trim().toLowerCase() - const existing = candidatesByName.get(normalizedName) ?? [] - const normalizedProviderId = onlineModel.providerId.trim().toLowerCase() - if (!existing.some(candidate => ( - candidate.providerId.trim().toLowerCase() === normalizedProviderId - ))) { - existing.push(onlineModel) - candidatesByName.set(normalizedName, existing) - } - } - for (const candidates of candidatesByName.values()) { - candidates.sort((left, right) => ( - Number(right.official === true) - Number(left.official === true) - || left.providerName.localeCompare(right.providerName) - || left.providerId.localeCompare(right.providerId) - )) - } - return candidatesByName -}) - -function getModelPricingCandidates(model: GlobalModelResponse): ModelsDevModelItem[] { - return onlinePricingCandidatesByModelName.value.get(model.name.trim().toLowerCase()) ?? [] -} - -function getModelPricingSource(model: GlobalModelResponse) { - return getModelsDevPricingSource(model.id, model.config) -} - -function isModelPricingSourceLocalOnly(model: GlobalModelResponse): boolean { - return !getModelsDevPricingSourceFromConfig(model.config) - && !!getLocalModelsDevPricingSource(model.id) -} - -function isModelPricingSourceSyncing(modelId: string): boolean { - return modelPricingSourceSyncingIds.value.has(modelId) -} - -function setModelPricingSourceSyncing(modelId: string, syncing: boolean) { - const nextIds = new Set(modelPricingSourceSyncingIds.value) - if (syncing) nextIds.add(modelId) - else nextIds.delete(modelId) - modelPricingSourceSyncingIds.value = nextIds -} - function applyGlobalModelUpdate(updatedModel: GlobalModelResponse) { for (const models of [globalModels.value, batchManageModels.value]) { const current = models.find(model => model.id === updatedModel.id) @@ -1580,7 +1490,6 @@ function migrateLegacyModelsDevPricingSources(models: GlobalModelResponse[]) { } pricingSourceMigrationAttemptedIds.add(model.id) return [async () => { - setModelPricingSourceSyncing(model.id, true) const nextConfig = withModelsDevPricingSource(model.config, localSource) try { const updatedModel = await updateGlobalModel(model.id, { config: nextConfig }) @@ -1588,84 +1497,12 @@ function migrateLegacyModelsDevPricingSources(models: GlobalModelResponse[]) { } catch (err: unknown) { pricingSourceMigrationAttemptedIds.delete(model.id) log.warn('迁移本地模型价格来源失败:', err) - } finally { - setModelPricingSourceSyncing(model.id, false) } }] }) if (tasks.length > 0) void runBatchTasksWithConcurrency(tasks, 4) } -async function handleModelPricingSourceOpen(_model: GlobalModelResponse, open: boolean) { - if (open) await loadBatchManageOnlineModels() -} - -async function syncModelPricingSource(model: GlobalModelResponse, providerId: string) { - if (!providerId || providerId.startsWith('__') || isModelPricingSourceSyncing(model.id)) return - if (batchManageOnlineModels.value.length === 0) await loadBatchManageOnlineModels() - const normalizedProviderId = providerId.trim().toLowerCase() - const candidate = getModelPricingCandidates(model).find(item => ( - item.providerId.trim().toLowerCase() === normalizedProviderId - )) - if (!candidate) { - showError( - t('models.pricingSource.sourceGone'), - t('models.pricingSource.cannotSync'), - ) - return - } - if (candidate.pricingUnsupportedFields?.length) { - showError( - t('models.pricingSource.unsupported', { fields: formatBatchUnsupportedPricingFields(candidate) }), - t('models.pricingSource.incompatible'), - ) - return - } - if (!candidate.tieredPricing?.tiers?.length) { - showError( - t('models.pricingSource.noUsablePrice'), - t('models.pricingSource.cannotSync'), - ) - return - } - - const source = { - provider_id: candidate.providerId, - provider_name: candidate.providerName, - } - const pricing = cloneTieredPricingConfig(candidate.tieredPricing) - const nextConfig = withModelsDevPricingSource(model.config, source) - setModelPricingSourceSyncing(model.id, true) - try { - const updatedModel = await updateGlobalModel(model.id, { - default_tiered_pricing: pricing, - config: nextConfig, - }) - setModelsDevPricingSource(model.id, source) - applyGlobalModelUpdate({ - ...updatedModel, - default_tiered_pricing: pricing, - config: nextConfig, - }) - success(t('models.pricingSource.selectedAndSynced', { provider: candidate.providerName })) - } catch (err: unknown) { - log.error('更新模型价格来源失败:', err) - showError( - parseApiError(err, t('models.pricingSource.updateFailed')), - t('models.pricingSource.syncFailed'), - ) - } finally { - setModelPricingSourceSyncing(model.id, false) - } -} - -async function resyncModelPricingSource(model: GlobalModelResponse) { - const source = getModelPricingSource(model) - if (!source) return - await loadBatchManageOnlineModels() - await syncModelPricingSource(model, source.provider_id) -} - const batchPricingProviderOptions = computed(() => { const existingModelNames = new Set(batchManageModels.value.map(model => model.name.trim().toLowerCase())) const providers = new Map { }) }) - it('renders the new source and selection controls in English', async () => { + it('does not render the pricing source in the model list', async () => { mountView() await settle() setI18nLocale('en-US') await settle() - expect(document.body.textContent).toContain('Online pricing source') + expect(document.body.textContent).not.toContain('Online pricing source') expect(document.body.textContent).toContain('Batch manage') expect(document.body.querySelector( '[aria-label="Select model Test Model"]', )).not.toBeNull() expect(document.body.querySelector( '[data-testid="model-pricing-source-model-1"]', - )?.getAttribute('aria-label')).toContain('Current source: Old OpenAI label') + )).toBeNull() }) it('migrates a legacy browser-only source into the model database config', async () => {