mirror of
https://github.com/moeru-ai/airi.git
synced 2026-10-30 19:34:10 +08:00
revert(provider-inference): remove API Route provider
This reverts commit c097190cb7.
This commit is contained in:
@@ -1,53 +0,0 @@
|
||||
import { createOpenAI } from '@xsai-ext/providers/create'
|
||||
import { z } from 'zod'
|
||||
|
||||
import { ProviderValidationCheck } from '../../../types'
|
||||
import { createOpenAICompatibleValidators } from '../../../validators'
|
||||
import { defineProvider } from '../../registry'
|
||||
|
||||
const apiRouteConfigSchema = z.object({
|
||||
apiKey: z
|
||||
.string('API Key'),
|
||||
baseUrl: z
|
||||
.string('Base URL')
|
||||
.optional()
|
||||
.default('https://global.api-route.com/v1'),
|
||||
})
|
||||
|
||||
type ApiRouteConfig = z.input<typeof apiRouteConfigSchema>
|
||||
|
||||
export const providerApiRoute = defineProvider<ApiRouteConfig, 'api-route'>({
|
||||
id: 'api-route',
|
||||
name: 'API Route',
|
||||
nameLocalize: () => 'API Route',
|
||||
description: 'api-route.com',
|
||||
descriptionLocalize: () => 'api-route.com',
|
||||
tasks: ['chat'],
|
||||
icon: 'i-lobe-icons:openai',
|
||||
|
||||
createProviderConfig: ({ t }) => apiRouteConfigSchema.extend({
|
||||
apiKey: apiRouteConfigSchema.shape.apiKey.meta({
|
||||
labelLocalized: t('settings.pages.providers.catalog.edit.config.common.fields.field.api-key.label'),
|
||||
descriptionLocalized: t('settings.pages.providers.catalog.edit.config.common.fields.field.api-key.description'),
|
||||
placeholderLocalized: t('settings.pages.providers.catalog.edit.config.common.fields.field.api-key.placeholder'),
|
||||
type: 'password',
|
||||
}),
|
||||
baseUrl: apiRouteConfigSchema.shape.baseUrl.meta({
|
||||
labelLocalized: t('settings.pages.providers.catalog.edit.config.common.fields.field.base-url.label'),
|
||||
descriptionLocalized: t('settings.pages.providers.catalog.edit.config.common.fields.field.base-url.description'),
|
||||
placeholderLocalized: t('settings.pages.providers.catalog.edit.config.common.fields.field.base-url.placeholder'),
|
||||
}),
|
||||
}),
|
||||
createProvider(config) {
|
||||
return createOpenAI(config.apiKey, config.baseUrl)
|
||||
},
|
||||
|
||||
validationRequiredWhen(config) {
|
||||
return !!config.apiKey?.trim()
|
||||
},
|
||||
validators: {
|
||||
...createOpenAICompatibleValidators({
|
||||
checks: [ProviderValidationCheck.Connectivity, ProviderValidationCheck.ModelList, ProviderValidationCheck.ChatCompletions],
|
||||
}),
|
||||
},
|
||||
})
|
||||
@@ -1,150 +1,148 @@
|
||||
import type { ProviderDefinition } from '../types'
|
||||
|
||||
import { provider302AI } from './cloud/302-ai'
|
||||
import type { ProviderDefinition } from '../types'
|
||||
|
||||
import { provider302AI } from './cloud/302-ai'
|
||||
import { providerAIHubMix } from './cloud/aihubmix'
|
||||
import { providerAmazonBedrock } from './cloud/amazon-bedrock'
|
||||
import { providerAnthropic } from './cloud/anthropic'
|
||||
import { providerApiRoute } from './cloud/api-route'
|
||||
import { providerAtlasCloud } from './cloud/atlascloud'
|
||||
import { providerAzureAIFoundry } from './cloud/azure-ai-foundry'
|
||||
import { providerAzureOpenAI } from './cloud/azure-openai'
|
||||
import { providerBytePlus } from './cloud/byteplus'
|
||||
import { providerBytePlusCodingPlan } from './cloud/byteplus-coding-plan'
|
||||
import { providerCerebrasAI } from './cloud/cerebras-ai'
|
||||
import { providerCloudflareWorkersAI } from './cloud/cloudflare-workers-ai'
|
||||
import { providerCometAPI, providerCometAPISpeech, providerCometAPITranscription } from './cloud/comet-api'
|
||||
import { providerDeepSeek } from './cloud/deepseek'
|
||||
import { providerElevenLabs } from './cloud/elevenlabs'
|
||||
import { providerFeatherlessAI } from './cloud/featherless-ai'
|
||||
import { providerFireworksAI } from './cloud/fireworks-ai'
|
||||
import { providerGoogleGeminiAudioSpeech } from './cloud/google-gemini-audio-speech'
|
||||
import { providerGoogleGenerativeAI } from './cloud/google-generative-ai'
|
||||
import { providerGroq } from './cloud/groq'
|
||||
import { providerMimo } from './cloud/mimo'
|
||||
import { providerMimoAudioSpeech, providerMimoAudioTranscription } from './cloud/mimo-audio'
|
||||
import { providerMinimax, providerMinimaxGlobal } from './cloud/minimax'
|
||||
import { providerMinimaxSpeech } from './cloud/minimax-speech'
|
||||
import { providerMistralAI } from './cloud/mistral-ai'
|
||||
import { providerModelScope } from './cloud/modelscope'
|
||||
import { providerMoonshotAI } from './cloud/moonshot-ai'
|
||||
import { providerN1N } from './cloud/n1n'
|
||||
import { providerNovitaAI } from './cloud/novita-ai'
|
||||
import { providerOpenAI } from './cloud/openai'
|
||||
import {
|
||||
providerOpenAIAudioSpeech,
|
||||
providerOpenAIAudioTranscription,
|
||||
providerOpenAICompatibleAudioSpeech,
|
||||
providerOpenAICompatibleAudioTranscription,
|
||||
} from './cloud/openai-audio'
|
||||
import { providerOpenAICompatible } from './cloud/openai-compatible'
|
||||
import { providerOpenPaths } from './cloud/openpaths'
|
||||
import { providerOpenRouterAI } from './cloud/openrouter-ai'
|
||||
import { providerOpenRouterAudioSpeech } from './cloud/openrouter-audio-speech'
|
||||
import { providerPerplexityAI } from './cloud/perplexity-ai'
|
||||
import { providerTogetherAI } from './cloud/together-ai'
|
||||
import {
|
||||
providerAlibabaCloudModelStudio,
|
||||
providerDeepgramTts,
|
||||
providerMicrosoftSpeech,
|
||||
providerVolcengineSpeech,
|
||||
} from './cloud/unspeech'
|
||||
import { providerVolcengineCodingPlan } from './cloud/volcengine-coding-plan'
|
||||
import { providerXAI } from './cloud/xai'
|
||||
import { providerZai } from './cloud/zai'
|
||||
import { providerBrowserWebSpeechApi } from './local/browser-web-speech-api'
|
||||
import { providerIndexTtsVllm } from './local/index-tts-vllm'
|
||||
import { providerLmStudio } from './local/lm-studio'
|
||||
import { providerOllama } from './local/ollama'
|
||||
import { providerPlayer2Speech } from './local/player2-speech'
|
||||
import { providerSpeechNoop } from './local/speech-noop'
|
||||
import { providerAivisSpeech, providerVoicevox } from './local/voicevox'
|
||||
|
||||
/**
|
||||
* Erases configuration types at the registry boundary.
|
||||
*
|
||||
* A registry selects definitions by a runtime id. It cannot know the selected
|
||||
* configuration type. Individual provider exports preserve their exact type.
|
||||
*/
|
||||
type ProviderDefinitionRegistration<TId extends string = string> = Pick<ProviderDefinition, 'name' | 'order'> & { id: TId }
|
||||
|
||||
type ErasedProviderDefinitions<TDefinitions extends readonly ProviderDefinitionRegistration[]> = {
|
||||
[K in keyof TDefinitions]: TDefinitions[K] extends ProviderDefinitionRegistration<infer TId>
|
||||
? ProviderDefinition<Record<string, unknown>, TId>
|
||||
: never
|
||||
}
|
||||
|
||||
function eraseProviderDefinitions<const TDefinitions extends readonly ProviderDefinitionRegistration[]>(
|
||||
...definitions: TDefinitions
|
||||
): ErasedProviderDefinitions<TDefinitions> {
|
||||
return definitions as unknown as ErasedProviderDefinitions<TDefinitions>
|
||||
}
|
||||
|
||||
/**
|
||||
* Definitions that can load without Vue, Pinia, persistence, or Electron.
|
||||
*
|
||||
* This list includes Browser-only definitions. Hosts must call
|
||||
* `isAvailableBy` before they offer one to a user in the current runtime.
|
||||
*/
|
||||
export const portableProviderDefinitions = eraseProviderDefinitions(
|
||||
import { providerAzureAIFoundry } from './cloud/azure-ai-foundry'
|
||||
import { providerAzureOpenAI } from './cloud/azure-openai'
|
||||
import { providerBytePlus } from './cloud/byteplus'
|
||||
import { providerBytePlusCodingPlan } from './cloud/byteplus-coding-plan'
|
||||
import { providerCerebrasAI } from './cloud/cerebras-ai'
|
||||
import { providerCloudflareWorkersAI } from './cloud/cloudflare-workers-ai'
|
||||
import { providerCometAPI, providerCometAPISpeech, providerCometAPITranscription } from './cloud/comet-api'
|
||||
import { providerDeepSeek } from './cloud/deepseek'
|
||||
import { providerElevenLabs } from './cloud/elevenlabs'
|
||||
import { providerFeatherlessAI } from './cloud/featherless-ai'
|
||||
import { providerFireworksAI } from './cloud/fireworks-ai'
|
||||
import { providerGoogleGeminiAudioSpeech } from './cloud/google-gemini-audio-speech'
|
||||
import { providerGoogleGenerativeAI } from './cloud/google-generative-ai'
|
||||
import { providerGroq } from './cloud/groq'
|
||||
import { providerMimo } from './cloud/mimo'
|
||||
import { providerMimoAudioSpeech, providerMimoAudioTranscription } from './cloud/mimo-audio'
|
||||
import { providerMinimax, providerMinimaxGlobal } from './cloud/minimax'
|
||||
import { providerMinimaxSpeech } from './cloud/minimax-speech'
|
||||
import { providerMistralAI } from './cloud/mistral-ai'
|
||||
import { providerModelScope } from './cloud/modelscope'
|
||||
import { providerMoonshotAI } from './cloud/moonshot-ai'
|
||||
import { providerN1N } from './cloud/n1n'
|
||||
import { providerNovitaAI } from './cloud/novita-ai'
|
||||
import { providerOpenAI } from './cloud/openai'
|
||||
import {
|
||||
providerOpenAIAudioSpeech,
|
||||
providerOpenAIAudioTranscription,
|
||||
providerOpenAICompatibleAudioSpeech,
|
||||
providerOpenAICompatibleAudioTranscription,
|
||||
} from './cloud/openai-audio'
|
||||
import { providerOpenAICompatible } from './cloud/openai-compatible'
|
||||
import { providerOpenPaths } from './cloud/openpaths'
|
||||
import { providerOpenRouterAI } from './cloud/openrouter-ai'
|
||||
import { providerOpenRouterAudioSpeech } from './cloud/openrouter-audio-speech'
|
||||
import { providerPerplexityAI } from './cloud/perplexity-ai'
|
||||
import { providerTogetherAI } from './cloud/together-ai'
|
||||
import {
|
||||
providerAlibabaCloudModelStudio,
|
||||
providerDeepgramTts,
|
||||
providerMicrosoftSpeech,
|
||||
providerVolcengineSpeech,
|
||||
} from './cloud/unspeech'
|
||||
import { providerVolcengineCodingPlan } from './cloud/volcengine-coding-plan'
|
||||
import { providerXAI } from './cloud/xai'
|
||||
import { providerZai } from './cloud/zai'
|
||||
import { providerBrowserWebSpeechApi } from './local/browser-web-speech-api'
|
||||
import { providerIndexTtsVllm } from './local/index-tts-vllm'
|
||||
import { providerLmStudio } from './local/lm-studio'
|
||||
import { providerOllama } from './local/ollama'
|
||||
import { providerPlayer2Speech } from './local/player2-speech'
|
||||
import { providerSpeechNoop } from './local/speech-noop'
|
||||
import { providerAivisSpeech, providerVoicevox } from './local/voicevox'
|
||||
|
||||
/**
|
||||
* Erases configuration types at the registry boundary.
|
||||
*
|
||||
* A registry selects definitions by a runtime id. It cannot know the selected
|
||||
* configuration type. Individual provider exports preserve their exact type.
|
||||
*/
|
||||
type ProviderDefinitionRegistration<TId extends string = string> = Pick<ProviderDefinition, 'name' | 'order'> & { id: TId }
|
||||
|
||||
type ErasedProviderDefinitions<TDefinitions extends readonly ProviderDefinitionRegistration[]> = {
|
||||
[K in keyof TDefinitions]: TDefinitions[K] extends ProviderDefinitionRegistration<infer TId>
|
||||
? ProviderDefinition<Record<string, unknown>, TId>
|
||||
: never
|
||||
}
|
||||
|
||||
function eraseProviderDefinitions<const TDefinitions extends readonly ProviderDefinitionRegistration[]>(
|
||||
...definitions: TDefinitions
|
||||
): ErasedProviderDefinitions<TDefinitions> {
|
||||
return definitions as unknown as ErasedProviderDefinitions<TDefinitions>
|
||||
}
|
||||
|
||||
/**
|
||||
* Definitions that can load without Vue, Pinia, persistence, or Electron.
|
||||
*
|
||||
* This list includes Browser-only definitions. Hosts must call
|
||||
* `isAvailableBy` before they offer one to a user in the current runtime.
|
||||
*/
|
||||
export const portableProviderDefinitions = eraseProviderDefinitions(
|
||||
provider302AI,
|
||||
providerAIHubMix,
|
||||
providerAmazonBedrock,
|
||||
providerAnthropic,
|
||||
providerApiRoute,
|
||||
providerAtlasCloud,
|
||||
providerAzureAIFoundry,
|
||||
providerAzureOpenAI,
|
||||
providerBytePlus,
|
||||
providerBytePlusCodingPlan,
|
||||
providerCerebrasAI,
|
||||
providerCloudflareWorkersAI,
|
||||
providerCometAPI,
|
||||
providerCometAPISpeech,
|
||||
providerCometAPITranscription,
|
||||
providerDeepSeek,
|
||||
providerElevenLabs,
|
||||
providerFeatherlessAI,
|
||||
providerFireworksAI,
|
||||
providerGoogleGeminiAudioSpeech,
|
||||
providerGoogleGenerativeAI,
|
||||
providerGroq,
|
||||
providerMinimax,
|
||||
providerMinimaxGlobal,
|
||||
providerMinimaxSpeech,
|
||||
providerMimo,
|
||||
providerMimoAudioSpeech,
|
||||
providerMimoAudioTranscription,
|
||||
providerMistralAI,
|
||||
providerModelScope,
|
||||
providerMoonshotAI,
|
||||
providerN1N,
|
||||
providerNovitaAI,
|
||||
providerOpenAI,
|
||||
providerOpenAIAudioSpeech,
|
||||
providerOpenAIAudioTranscription,
|
||||
providerOpenAICompatibleAudioSpeech,
|
||||
providerOpenAICompatibleAudioTranscription,
|
||||
providerOpenAICompatible,
|
||||
providerOpenPaths,
|
||||
providerOpenRouterAI,
|
||||
providerOpenRouterAudioSpeech,
|
||||
providerPerplexityAI,
|
||||
providerTogetherAI,
|
||||
providerAlibabaCloudModelStudio,
|
||||
providerDeepgramTts,
|
||||
providerMicrosoftSpeech,
|
||||
providerVolcengineSpeech,
|
||||
providerVolcengineCodingPlan,
|
||||
providerXAI,
|
||||
providerZai,
|
||||
providerBrowserWebSpeechApi,
|
||||
providerIndexTtsVllm,
|
||||
providerLmStudio,
|
||||
providerOllama,
|
||||
providerPlayer2Speech,
|
||||
providerSpeechNoop,
|
||||
providerAivisSpeech,
|
||||
providerVoicevox,
|
||||
)
|
||||
|
||||
export { createProviderRegistry, defineProvider } from './registry'
|
||||
providerAzureAIFoundry,
|
||||
providerAzureOpenAI,
|
||||
providerBytePlus,
|
||||
providerBytePlusCodingPlan,
|
||||
providerCerebrasAI,
|
||||
providerCloudflareWorkersAI,
|
||||
providerCometAPI,
|
||||
providerCometAPISpeech,
|
||||
providerCometAPITranscription,
|
||||
providerDeepSeek,
|
||||
providerElevenLabs,
|
||||
providerFeatherlessAI,
|
||||
providerFireworksAI,
|
||||
providerGoogleGeminiAudioSpeech,
|
||||
providerGoogleGenerativeAI,
|
||||
providerGroq,
|
||||
providerMinimax,
|
||||
providerMinimaxGlobal,
|
||||
providerMinimaxSpeech,
|
||||
providerMimo,
|
||||
providerMimoAudioSpeech,
|
||||
providerMimoAudioTranscription,
|
||||
providerMistralAI,
|
||||
providerModelScope,
|
||||
providerMoonshotAI,
|
||||
providerN1N,
|
||||
providerNovitaAI,
|
||||
providerOpenAI,
|
||||
providerOpenAIAudioSpeech,
|
||||
providerOpenAIAudioTranscription,
|
||||
providerOpenAICompatibleAudioSpeech,
|
||||
providerOpenAICompatibleAudioTranscription,
|
||||
providerOpenAICompatible,
|
||||
providerOpenPaths,
|
||||
providerOpenRouterAI,
|
||||
providerOpenRouterAudioSpeech,
|
||||
providerPerplexityAI,
|
||||
providerTogetherAI,
|
||||
providerAlibabaCloudModelStudio,
|
||||
providerDeepgramTts,
|
||||
providerMicrosoftSpeech,
|
||||
providerVolcengineSpeech,
|
||||
providerVolcengineCodingPlan,
|
||||
providerXAI,
|
||||
providerZai,
|
||||
providerBrowserWebSpeechApi,
|
||||
providerIndexTtsVllm,
|
||||
providerLmStudio,
|
||||
providerOllama,
|
||||
providerPlayer2Speech,
|
||||
providerSpeechNoop,
|
||||
providerAivisSpeech,
|
||||
providerVoicevox,
|
||||
)
|
||||
|
||||
export { createProviderRegistry, defineProvider } from './registry'
|
||||
|
||||
@@ -1,50 +1,48 @@
|
||||
import type { PortableProviderId } from '../index'
|
||||
|
||||
import { describe, expect, expectTypeOf, it } from 'vitest'
|
||||
|
||||
import { createProviderRegistry, getDefinedProvider, listProviders, portableProviderDefinitions } from '../index'
|
||||
|
||||
describe('portable provider registry', () => {
|
||||
import type { PortableProviderId } from '../index'
|
||||
|
||||
import { describe, expect, expectTypeOf, it } from 'vitest'
|
||||
|
||||
import { createProviderRegistry, getDefinedProvider, listProviders, portableProviderDefinitions } from '../index'
|
||||
|
||||
describe('portable provider registry', () => {
|
||||
it('exports the portable provider id union', () => {
|
||||
expectTypeOf<'api-route'>().toExtend<PortableProviderId>()
|
||||
expectTypeOf<'openai'>().toExtend<PortableProviderId>()
|
||||
expectTypeOf<string>().not.toExtend<PortableProviderId>()
|
||||
|
||||
expect(getDefinedProvider('api-route')).toBeDefined()
|
||||
expectTypeOf<string>().not.toExtend<PortableProviderId>()
|
||||
|
||||
expect(getDefinedProvider('openai')).toBeDefined()
|
||||
})
|
||||
|
||||
it('registers each portable definition by id', () => {
|
||||
for (const definition of portableProviderDefinitions)
|
||||
expect(getDefinedProvider(definition.id)).toBe(definition)
|
||||
})
|
||||
|
||||
it('lists definitions in deterministic display order', () => {
|
||||
expect(listProviders()).toEqual(listProviders())
|
||||
})
|
||||
|
||||
it('places definitions without an order after all ordered definitions', () => {
|
||||
const [template] = portableProviderDefinitions
|
||||
const registry = createProviderRegistry([
|
||||
{ ...template, id: 'unordered-z', name: 'Zeta', order: undefined },
|
||||
{ ...template, id: 'high-order', name: 'High order', order: 100_000 },
|
||||
{ ...template, id: 'unordered-a', name: 'Alpha', order: undefined },
|
||||
])
|
||||
|
||||
expect(registry.list().map(provider => provider.id)).toEqual([
|
||||
'high-order',
|
||||
'unordered-a',
|
||||
'unordered-z',
|
||||
])
|
||||
})
|
||||
|
||||
it('does not include providers that require application runtime adapters', () => {
|
||||
const providerIds = new Set(listProviders().map(provider => provider.id))
|
||||
|
||||
expect(providerIds).not.toContain('official-provider')
|
||||
expect(providerIds).not.toContain('apple-speech-transcription')
|
||||
expect(providerIds).not.toContain('kokoro-local')
|
||||
expect(providerIds).not.toContain('browser-local-audio-speech')
|
||||
expect(providerIds).not.toContain('aliyun-nls-transcription')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('registers each portable definition by id', () => {
|
||||
for (const definition of portableProviderDefinitions)
|
||||
expect(getDefinedProvider(definition.id)).toBe(definition)
|
||||
})
|
||||
|
||||
it('lists definitions in deterministic display order', () => {
|
||||
expect(listProviders()).toEqual(listProviders())
|
||||
})
|
||||
|
||||
it('places definitions without an order after all ordered definitions', () => {
|
||||
const [template] = portableProviderDefinitions
|
||||
const registry = createProviderRegistry([
|
||||
{ ...template, id: 'unordered-z', name: 'Zeta', order: undefined },
|
||||
{ ...template, id: 'high-order', name: 'High order', order: 100_000 },
|
||||
{ ...template, id: 'unordered-a', name: 'Alpha', order: undefined },
|
||||
])
|
||||
|
||||
expect(registry.list().map(provider => provider.id)).toEqual([
|
||||
'high-order',
|
||||
'unordered-a',
|
||||
'unordered-z',
|
||||
])
|
||||
})
|
||||
|
||||
it('does not include providers that require application runtime adapters', () => {
|
||||
const providerIds = new Set(listProviders().map(provider => provider.id))
|
||||
|
||||
expect(providerIds).not.toContain('official-provider')
|
||||
expect(providerIds).not.toContain('apple-speech-transcription')
|
||||
expect(providerIds).not.toContain('kokoro-local')
|
||||
expect(providerIds).not.toContain('browser-local-audio-speech')
|
||||
expect(providerIds).not.toContain('aliyun-nls-transcription')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -44,7 +44,6 @@ const providerAttributesById = {
|
||||
'aliyun-nls-transcription': paidCloud,
|
||||
'amazon-bedrock': paidCloud,
|
||||
'anthropic': paidCloud,
|
||||
'api-route': paidCloud,
|
||||
'app-local-audio-speech': freeLocal,
|
||||
'app-local-audio-transcription': freeLocal,
|
||||
'apple-vision': freeLocal,
|
||||
|
||||
@@ -72,15 +72,4 @@ describe('provider metadata selector', () => {
|
||||
expect(definition.id).toBe('test-provider')
|
||||
expect(definition.tasks).toEqual(['chat'])
|
||||
})
|
||||
|
||||
// https://github.com/moeru-ai/airi/pull/2526#discussion_r3994122656
|
||||
it('keeps API Route in the paid and cloud catalogue filters', async () => {
|
||||
const metadata = await selectProviderMetadata({ ...definition, id: 'api-route' }, t)
|
||||
|
||||
const paidProviders = [metadata].filter(provider => provider.pricing === 'paid')
|
||||
const cloudProviders = [metadata].filter(provider => provider.deployment === 'cloud')
|
||||
|
||||
expect(paidProviders).toEqual([metadata])
|
||||
expect(cloudProviders).toEqual([metadata])
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user