fix(stage-ui): correct provider source filter categories (#1929)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: LemonNeko <self@lemonneko.moe>
This commit is contained in:
co-authored by
autofix-ci[bot]
LemonNeko
parent
4d496b18d7
commit
88fac74702
@@ -1,4 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { ProviderSourceDeployment, ProviderSourcePricing } from '@proj-airi/stage-ui/libs/providers/source-metadata'
|
||||
import type { Ref } from 'vue'
|
||||
|
||||
import { IconStatusItem, RippleGrid } from '@proj-airi/stage-ui/components'
|
||||
import { useAnalytics } from '@proj-airi/stage-ui/composables'
|
||||
import { useRippleGridState } from '@proj-airi/stage-ui/composables/use-ripple-grid-state'
|
||||
@@ -9,6 +12,31 @@ import { computed, onMounted, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
interface ProviderSourceCard {
|
||||
id: string
|
||||
category: string
|
||||
to?: string
|
||||
icon?: string
|
||||
iconColor?: string
|
||||
iconImage?: string
|
||||
name?: string
|
||||
description?: string
|
||||
localizedName?: string
|
||||
localizedDescription?: string
|
||||
configured?: boolean
|
||||
pricing?: ProviderSourcePricing
|
||||
deployment?: ProviderSourceDeployment
|
||||
beginnerRecommended?: boolean
|
||||
}
|
||||
|
||||
interface ProviderBlockConfig {
|
||||
id: string
|
||||
icon: string
|
||||
title: string
|
||||
description: string
|
||||
providersRef: Readonly<Ref<ProviderSourceCard[]>>
|
||||
}
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { t } = useI18n()
|
||||
@@ -23,7 +51,7 @@ const {
|
||||
allAudioTranscriptionProvidersMetadata,
|
||||
} = storeToRefs(providersStore)
|
||||
|
||||
const allArtistryProvidersMetadata = computed(() => {
|
||||
const allArtistryProvidersMetadata = computed<ProviderSourceCard[]>(() => {
|
||||
return [
|
||||
{
|
||||
id: 'comfyui',
|
||||
@@ -54,7 +82,6 @@ const allArtistryProvidersMetadata = computed(() => {
|
||||
to: '/settings/providers/artistry/replicate',
|
||||
pricing: 'paid',
|
||||
deployment: 'cloud',
|
||||
beginnerRecommended: false,
|
||||
iconImage: undefined,
|
||||
},
|
||||
{
|
||||
@@ -70,13 +97,12 @@ const allArtistryProvidersMetadata = computed(() => {
|
||||
to: '/settings/providers/artistry/nanobanana',
|
||||
pricing: 'free',
|
||||
deployment: 'cloud',
|
||||
beginnerRecommended: false,
|
||||
iconImage: undefined,
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
const providerBlocksConfig = [
|
||||
const providerBlocksConfig: ProviderBlockConfig[] = [
|
||||
{
|
||||
id: 'chat',
|
||||
icon: 'i-solar:chat-square-like-bold-duotone',
|
||||
@@ -133,7 +159,7 @@ const providerBlocks = computed(() => {
|
||||
.filter(block => block.id === activeTabId.value)
|
||||
.map((block) => {
|
||||
const filteredProviders = block.providersRef.value
|
||||
.filter((p: any) => {
|
||||
.filter((p) => {
|
||||
if (filterPricing.value !== 'all' && p.pricing !== filterPricing.value)
|
||||
return false
|
||||
if (filterDeployment.value !== 'all' && p.deployment !== filterDeployment.value)
|
||||
@@ -251,10 +277,10 @@ const providerBlocks = computed(() => {
|
||||
:icon="provider.icon"
|
||||
:icon-color="provider.iconColor"
|
||||
:icon-image="provider.iconImage"
|
||||
:to="`/settings/providers/${provider.category}/${provider.id}`"
|
||||
:to="provider.to ?? `/settings/providers/${provider.category}/${provider.id}`"
|
||||
:configured="provider.configured"
|
||||
:pricing="provider.pricing as any"
|
||||
:deployment="provider.deployment as any"
|
||||
:pricing="provider.pricing"
|
||||
:deployment="provider.deployment"
|
||||
:beginner-recommended="provider.beginnerRecommended"
|
||||
@click="trackProviderClick(provider.id, provider.category)"
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { ProviderSourceDeployment, ProviderSourcePricing } from '../../libs/providers/source-metadata'
|
||||
|
||||
const props = defineProps<{
|
||||
title: string
|
||||
description?: string
|
||||
@@ -7,8 +9,8 @@ const props = defineProps<{
|
||||
iconImage?: string
|
||||
to: string
|
||||
configured?: boolean
|
||||
pricing?: 'free' | 'paid' | 'internal'
|
||||
deployment?: 'local' | 'cloud'
|
||||
pricing?: ProviderSourcePricing
|
||||
deployment?: ProviderSourceDeployment
|
||||
beginnerRecommended?: boolean
|
||||
}>()
|
||||
</script>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './providers'
|
||||
export * from './source-metadata'
|
||||
export * from './types'
|
||||
export * from './validators'
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
export type ProviderSourcePricing = 'free' | 'paid'
|
||||
export type ProviderSourceDeployment = 'local' | 'cloud'
|
||||
|
||||
/**
|
||||
* Represents source catalogue tags used by provider filtering UI.
|
||||
*/
|
||||
export interface ProviderSourceMetadata {
|
||||
/** Price bucket shown by the provider source filter. */
|
||||
pricing?: ProviderSourcePricing
|
||||
/** Runtime/deployment bucket shown by the provider source filter. */
|
||||
deployment?: ProviderSourceDeployment
|
||||
/** Whether the provider should receive the existing recommended tag. */
|
||||
beginnerRecommended?: boolean
|
||||
}
|
||||
|
||||
export interface ProviderSourceMetadataInput {
|
||||
id?: string
|
||||
}
|
||||
|
||||
const paidCloud = {
|
||||
pricing: 'paid',
|
||||
deployment: 'cloud',
|
||||
} satisfies ProviderSourceMetadata
|
||||
|
||||
const freeLocal = {
|
||||
pricing: 'free',
|
||||
deployment: 'local',
|
||||
} satisfies ProviderSourceMetadata
|
||||
|
||||
const recommendedPaidCloud = {
|
||||
...paidCloud,
|
||||
beginnerRecommended: true,
|
||||
} satisfies ProviderSourceMetadata
|
||||
|
||||
const providerSourceMetadataById = {
|
||||
'302-ai': paidCloud,
|
||||
'aihubmix': paidCloud,
|
||||
'alibaba-cloud-model-studio': paidCloud,
|
||||
'aliyun-nls-transcription': paidCloud,
|
||||
'amazon-bedrock': paidCloud,
|
||||
'anthropic': paidCloud,
|
||||
'app-local-audio-speech': freeLocal,
|
||||
'app-local-audio-transcription': freeLocal,
|
||||
'azure-ai-foundry': paidCloud,
|
||||
'azure-openai': paidCloud,
|
||||
'browser-local-audio-speech': freeLocal,
|
||||
'browser-local-audio-transcription': freeLocal,
|
||||
'browser-web-speech-api': freeLocal,
|
||||
'byteplus': paidCloud,
|
||||
'byteplus-coding-plan': paidCloud,
|
||||
'cerebras-ai': paidCloud,
|
||||
'cloudflare-workers-ai': paidCloud,
|
||||
'comet-api': paidCloud,
|
||||
'comet-api-speech': paidCloud,
|
||||
'comet-api-transcription': paidCloud,
|
||||
'deepgram-tts': paidCloud,
|
||||
'deepseek': paidCloud,
|
||||
'elevenlabs': paidCloud,
|
||||
'featherless-ai': paidCloud,
|
||||
'fireworks-ai': paidCloud,
|
||||
'google-generative-ai': paidCloud,
|
||||
'groq': paidCloud,
|
||||
'index-tts-vllm': freeLocal,
|
||||
'kokoro-local': freeLocal,
|
||||
'lm-studio': freeLocal,
|
||||
'microsoft-speech': paidCloud,
|
||||
'mimo': paidCloud,
|
||||
'mimo-audio-speech': paidCloud,
|
||||
'mimo-audio-transcription': paidCloud,
|
||||
'minimax': paidCloud,
|
||||
'minimax-global': paidCloud,
|
||||
'minimax-speech': paidCloud,
|
||||
'mistral-ai': paidCloud,
|
||||
'modelscope': paidCloud,
|
||||
'moonshot-ai': paidCloud,
|
||||
'n1n': paidCloud,
|
||||
'novita-ai': paidCloud,
|
||||
'nvidia': paidCloud,
|
||||
'official-provider': recommendedPaidCloud,
|
||||
'official-provider-speech': recommendedPaidCloud,
|
||||
'official-provider-speech-streaming': recommendedPaidCloud,
|
||||
'ollama': freeLocal,
|
||||
'openai': paidCloud,
|
||||
'openai-audio-speech': paidCloud,
|
||||
'openai-audio-transcription': paidCloud,
|
||||
'openai-compatible': false,
|
||||
'openai-compatible-audio-speech': false,
|
||||
'openai-compatible-audio-transcription': false,
|
||||
'openrouter-ai': paidCloud,
|
||||
'openrouter-audio-speech': paidCloud,
|
||||
'perplexity-ai': paidCloud,
|
||||
'player2-speech': freeLocal,
|
||||
'speech-noop': false,
|
||||
'together-ai': paidCloud,
|
||||
'volcengine': paidCloud,
|
||||
'volcengine-coding-plan': paidCloud,
|
||||
'xai': paidCloud,
|
||||
'zai': paidCloud,
|
||||
} satisfies Record<string, ProviderSourceMetadata | false>
|
||||
|
||||
/**
|
||||
* Normalizes provider source metadata by dropping undefined fields.
|
||||
*
|
||||
* Before:
|
||||
* - `{ pricing: "paid", deployment: undefined }`
|
||||
*
|
||||
* After:
|
||||
* - `{ pricing: "paid" }`
|
||||
*/
|
||||
function compactProviderSourceMetadata(metadata: ProviderSourceMetadata): ProviderSourceMetadata {
|
||||
return {
|
||||
...(metadata.pricing ? { pricing: metadata.pricing } : {}),
|
||||
...(metadata.deployment ? { deployment: metadata.deployment } : {}),
|
||||
...(metadata.beginnerRecommended !== undefined ? { beginnerRecommended: metadata.beginnerRecommended } : {}),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the provider source tags used by settings/provider filtering.
|
||||
*
|
||||
* Use when:
|
||||
* - Rendering provider source cards.
|
||||
* - Converting defineProvider() catalogue entries to legacy ProviderMetadata.
|
||||
*
|
||||
* Expects:
|
||||
* - `metadata.id` may identify a provider with catalogue metadata.
|
||||
*
|
||||
* Returns:
|
||||
* - Compact metadata with only meaningful tag fields.
|
||||
*/
|
||||
export function resolveProviderSourceMetadata(
|
||||
metadata: ProviderSourceMetadataInput = {},
|
||||
): ProviderSourceMetadata {
|
||||
if (!metadata.id)
|
||||
return {}
|
||||
|
||||
const sourceMetadata = providerSourceMetadataById[metadata.id as keyof typeof providerSourceMetadataById]
|
||||
if (sourceMetadata === false)
|
||||
return {}
|
||||
if (sourceMetadata)
|
||||
return compactProviderSourceMetadata(sourceMetadata)
|
||||
|
||||
return {}
|
||||
}
|
||||
@@ -19,9 +19,11 @@ import type {
|
||||
VoiceProviderWithExtraOptions,
|
||||
} from 'unspeech'
|
||||
|
||||
import type { ProviderSourceDeployment, ProviderSourcePricing } from '../libs/providers/source-metadata'
|
||||
import type { ProviderOnboardingField } from '../libs/providers/types'
|
||||
import type { AliyunRealtimeSpeechExtraOptions } from './providers/aliyun/stream-transcription'
|
||||
|
||||
import { errorMessageFrom } from '@moeru/std'
|
||||
import { isStageTamagotchi, isUrl } from '@proj-airi/stage-shared'
|
||||
import { getCachedWebGPUCapabilities, isWebGPUSupported } from '@proj-airi/stage-shared/webgpu'
|
||||
import { computedAsync, useIntervalFn, useLocalStorage } from '@vueuse/core'
|
||||
@@ -51,6 +53,7 @@ import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { getKokoroAdapter } from '../libs/inference/adapters/kokoro'
|
||||
import { getProviderValidationIntervalMs, listProviders as listDefinedProviders, ProviderValidationCheck } from '../libs/providers'
|
||||
import { resolveProviderSourceMetadata } from '../libs/providers/source-metadata'
|
||||
import { getDefaultKokoroModel, KOKORO_MODELS, kokoroModelsToModelInfo } from '../workers/kokoro/constants'
|
||||
import { useAuthStore } from './auth'
|
||||
import { createAliyunNLSProvider as createAliyunNlsStreamProvider } from './providers/aliyun/stream-transcription'
|
||||
@@ -181,8 +184,8 @@ export interface ProviderMetadata {
|
||||
supportsStreamOutput: boolean
|
||||
supportsStreamInput: boolean
|
||||
}
|
||||
pricing?: 'free' | 'paid' | 'internal'
|
||||
deployment?: 'local' | 'cloud'
|
||||
pricing?: ProviderSourcePricing
|
||||
deployment?: ProviderSourceDeployment
|
||||
beginnerRecommended?: boolean
|
||||
}
|
||||
|
||||
@@ -2238,6 +2241,7 @@ export const useProvidersStore = defineStore('providers', () => {
|
||||
// translate unified provider definitions from libs/providers to legacy store metadata.
|
||||
// Existing metadata remains as fallback for providers not yet migrated.
|
||||
const definedProviders = listDefinedProviders()
|
||||
const definedProviderIds = new Set(definedProviders.map(d => d.id))
|
||||
|
||||
const translatedProviderMetadata = convertProviderDefinitionsToMetadata(
|
||||
definedProviders,
|
||||
@@ -2266,6 +2270,12 @@ export const useProvidersStore = defineStore('providers', () => {
|
||||
providerMetadata[providerId] = translated
|
||||
}
|
||||
|
||||
for (const metadata of Object.values(providerMetadata)) {
|
||||
if (definedProviderIds.has(metadata.id))
|
||||
continue
|
||||
Object.assign(metadata, resolveProviderSourceMetadata(metadata))
|
||||
}
|
||||
|
||||
// const validatedCredentials = ref<Record<string, string>>({})
|
||||
const providerRuntimeState = ref<Record<string, ProviderRuntimeState>>({})
|
||||
const providerValidationInFlight = new Map<string, Promise<boolean>>()
|
||||
@@ -2536,7 +2546,7 @@ export const useProvidersStore = defineStore('providers', () => {
|
||||
catch (error) {
|
||||
console.error(`Error fetching models for ${providerId}:`, error)
|
||||
if (runtimeState) {
|
||||
runtimeState.modelLoadError = error instanceof Error ? error.message : 'Unknown error'
|
||||
runtimeState.modelLoadError = errorMessageFrom(error) ?? 'Unknown error'
|
||||
}
|
||||
return []
|
||||
}
|
||||
@@ -2613,8 +2623,6 @@ export const useProvidersStore = defineStore('providers', () => {
|
||||
|
||||
// Get all providers metadata (for settings page).
|
||||
// Order: defined providers first (already sorted by order in registry), then legacy-only providers.
|
||||
const definedProviderIds = new Set(definedProviders.map(d => d.id))
|
||||
|
||||
const allProvidersMetadata = computed(() => {
|
||||
const localize = (metadata: ProviderMetadata) => ({
|
||||
...metadata,
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { ProviderMetadata } from '../providers'
|
||||
|
||||
import { listModels } from '@xsai/model'
|
||||
|
||||
import { resolveProviderSourceMetadata } from '../../libs/providers/source-metadata'
|
||||
import { CHAT_COMPLETIONS_VALIDATOR_ID, isModelProvider } from '../../libs/providers/types'
|
||||
import { getValidatorsOfProvider, validateProvider } from '../../libs/providers/validators/run'
|
||||
|
||||
@@ -99,6 +100,7 @@ export function convertProviderDefinitionToMetadata(
|
||||
const keyExtractor = (input: string): string => input
|
||||
const category = getCategoryFromTasks(definition.tasks)
|
||||
const schemaDefaults = extractSchemaDefaults(definition, t)
|
||||
const providerSourceMetadata = resolveProviderSourceMetadata(definition)
|
||||
return {
|
||||
id: definition.id,
|
||||
order: definition.order,
|
||||
@@ -111,6 +113,7 @@ export function convertProviderDefinitionToMetadata(
|
||||
icon: definition.icon,
|
||||
iconColor: definition.iconColor,
|
||||
iconImage: definition.iconImage,
|
||||
...providerSourceMetadata,
|
||||
isAvailableBy: definition.isAvailableBy,
|
||||
requiresCredentials: definition.requiresCredentials,
|
||||
onboardingFields: definition.onboardingFields?.({ t }),
|
||||
|
||||
Reference in New Issue
Block a user