refactor: extract provider logic into modular registry

Each AI provider now has its own module implementing a ProviderModule
interface with createGateway, getAuthHeaders, and fetchModels methods.
This replaces the monolithic switch statements in ai-provider.ts and
models.post.ts with a clean registry pattern.
This commit is contained in:
Zoe
2026-04-27 12:04:49 -05:00
parent 6ca4de4cab
commit 1d40812f2a
21 changed files with 1221 additions and 798 deletions
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { sortByReleaseDate } from '~/utils/sort';
import { encryptData, decrypt, uint8ArrayToBase64, base64ToUint8Array } from '~/utils/crypto';
import { providerBaseUrls, Providers, type Model } from '~/types/model';
import { type Model } from '~/types/model';
import ModelItem from './ModelItem.vue';
import RowVirtualizerDynamic from '../RowVirtualizerDynamic.vue';
@@ -31,10 +31,10 @@ const modelSearch = ref('');
watch(() => props.params, () => {
if (props.params === undefined) return;
console.log(scrollContainerRef.value);
apiKey.value = provider.value?.config.apiKey ?? '';
apiProxyUrl.value = provider.value?.config.apiProxyUrl ?? '';
modelSearch.value = '';
apiKeyVisible.value = false;
nextTick(() => {
if (scrollContainerRef.value) {
@@ -339,7 +339,7 @@ const saveCustomModel = async () => {
if (editingModel.value) {
await updateModel(editingModel.value!.id, { ...previewModelWithoutId });
} else {
await createModel(previewModelWithoutId);
await createModel(previewModelWithoutId as Model);
}
showAddModelPanel.value = false;
@@ -403,7 +403,7 @@ defineEmits(['navigate']);
<div class="flex flex-row justify-between gap-16">
<label class="whitespace-nowrap" for="provider-api-key">API Proxy URL</label>
<div class="text-sm font-mono flex flex-row rounded-md bg-[var(--bg-container)] items-center gap-1 w-7/10">
<input :placeholder="provider.type ? providerBaseUrls[provider.type as typeof Providers[number]] : ''"
<input :placeholder="provider.defaultBaseUrl || ''"
class="placeholder:text-[var(--text-tertiary)] w-full px-2 py-1 bg-transparent" type="text"
id="provider-proxy-url" :value="apiProxyUrl"
@input="updateProxyUrl(($event.target! as HTMLInputElement).value)" />
-25
View File
@@ -1,30 +1,5 @@
import * as schema from '~~/drizzle/schema';
export const Providers = [
'openrouter',
'ollama',
'vllm',
'cerebras',
'google',
'longcat',
'cohere',
'inception',
'mistral',
'closedrouter',
] as const;
export const SupportedModalities = ['text', 'image', 'audio', 'video', 'pdf'] as const;
export const providerBaseUrls = {
openrouter: 'https://openrouter.ai/api/v1',
vllm: '',
ollama: '',
cerebras: 'https://api.cerebras.ai/v1',
google: 'https://generativelanguage.googleapis.com/v1beta',
longcat: 'https://api.longcat.chat/openai/v1',
cohere: 'https://api.cohere.ai/v2',
inception: 'https://api.inceptionlabs.ai/v1',
mistral: 'https://api.mistral.ai/v1',
closedrouter: 'https://router.queef.in/v1',
};
export type Model = typeof schema.models.$inferSelect;