import { nanoid } from 'nanoid'; import * as schema from '~~/drizzle/schema'; export type Model = typeof schema.models.$inferSelect; export type Provider = typeof schema.providers.$inferSelect; export type ProviderWithModels = Provider & { models: Model[]; }; export type ModelWithProvider = Model & { provider: Provider; }; export const useModels = async () => { const providers = useState('models_state', () => []); const loaded = useState('models_loaded', () => false); const { refresh } = await useFetch('/api/providers', { key: 'models_request', immediate: !loaded.value, onRequest() { loaded.value = true; }, onResponse({ response }) { if (response.ok) { providers.value = response._data ?? []; } } }); const allModels = computed(() => { const result: ModelWithProvider[] = []; for (const provider of providers.value) { if (!provider.enabled) continue; for (const model of provider.models || []) { if (!model.enabled) continue; result.push({ ...model, provider }); } } return result; }); const getModel = (id: string): ModelWithProvider | undefined => { return allModels.value.find((model) => model.id === id); }; const getProvider = (id: string): ProviderWithModels | undefined => { return providers.value.find((provider) => provider.id === id); }; const getFirstAvailableModel = () => { return allModels.value[0] ?? null; }; const createModel = async (model: Model) => { model.id = nanoid(); await $fetch(`/api/model`, { method: 'POST', body: model, onRequest() { providers.value = providers.value.map(p => ({ ...p, models: p.id === model.providerId ? [...p.models, model] : p.models })); }, onResponseError() { providers.value = providers.value.map(p => ({ ...p, models: p.id === model.providerId ? p.models.filter(m => m.id !== model.id) : p.models })); }, }); } const updateModel = async (id: string, updates: Partial) => { const model = providers.value.find(p => p.models.find(m => m.id === id)); if (!model) return; const original = model; await $fetch(`/api/model/${id}`, { method: 'PATCH', body: updates, onRequest() { providers.value = providers.value.map(p => ({ ...p, models: p.models.map(m => m.id === id ? { ...m, ...updates } : m ) })); }, onRequestError() { providers.value = providers.value.map(p => ({ ...p, models: p.models.map(m => m.id === id ? original : m ) }) as ProviderWithModels); }, onResponseError() { providers.value = providers.value.map(p => ({ ...p, models: p.models.map(m => m.id === id ? original : m ) }) as ProviderWithModels); }, // async onResponse() { // await refresh(); // } }); }; const deleteModel = async (id: string) => { const model = providers.value.find(p => p.models.find(m => m.id === id)); if (!model) return; await $fetch(`/api/model/${id}`, { method: 'DELETE', onRequest() { providers.value = providers.value.map(p => ({ ...p, models: p.models.filter(m => m.id !== id) })); }, onResponseError() { providers.value.push(model); }, async onResponse() { await refresh(); } }); } const updateProvider = async (id: string, updates: Partial) => { const provider = providers.value.find(p => p.id === id); if (!provider) return; const original = provider; await $fetch(`/api/provider/${id}`, { method: 'PATCH', body: updates, onRequest() { providers.value = providers.value.map(p => p.id === id ? { ...p, ...updates } : p ); }, onRequestError() { providers.value = providers.value.map(p => p.id === id ? original : p ); }, onResponseError() { providers.value = providers.value.map(p => p.id === id ? original : p ); }, async onResponse() { await refresh(); } }); }; return { providers, allModels, getModel, getProvider, getFirstAvailableModel, createModel, updateModel, updateProvider, deleteModel }; }