From 5a7f45cace63ef22430ee78c6d0f0525451965ef Mon Sep 17 00:00:00 2001 From: ItzCrazyKns <95534749+ItzCrazyKns@users.noreply.github.com> Date: Sat, 18 Oct 2025 15:06:00 +0530 Subject: [PATCH] feat(app): add provider methods --- src/app/api/providers/[id]/models/route.ts | 94 +++++++++++++ src/app/api/providers/[id]/route.ts | 89 ++++++++++++ src/app/api/providers/route.ts | 48 ++++++- src/lib/config/index.ts | 69 ++++++++++ src/lib/models/registry.ts | 150 ++++++++++++++++++++- 5 files changed, 447 insertions(+), 3 deletions(-) create mode 100644 src/app/api/providers/[id]/models/route.ts create mode 100644 src/app/api/providers/[id]/route.ts diff --git a/src/app/api/providers/[id]/models/route.ts b/src/app/api/providers/[id]/models/route.ts new file mode 100644 index 0000000..5b4acc3 --- /dev/null +++ b/src/app/api/providers/[id]/models/route.ts @@ -0,0 +1,94 @@ +import ModelRegistry from '@/lib/models/registry'; +import { Model } from '@/lib/models/types'; +import { NextRequest } from 'next/server'; + +export const POST = async ( + req: NextRequest, + { params }: { params: Promise<{ id: string }> }, +) => { + try { + const { id } = await params; + + const body: Partial & { type: 'embedding' | 'chat' } = + await req.json(); + + if (!body.key || !body.name) { + return Response.json( + { + message: 'Key and name must be provided', + }, + { + status: 400, + }, + ); + } + + const registry = new ModelRegistry(); + + await registry.addProviderModel(id, body.type, body); + + return Response.json( + { + message: 'Model added successfully', + }, + { + status: 200, + }, + ); + } catch (err) { + console.error('An error occurred while adding provider model', err); + return Response.json( + { + message: 'An error has occurred.', + }, + { + status: 500, + }, + ); + } +}; + +export const DELETE = async ( + req: NextRequest, + { params }: { params: Promise<{ id: string }> }, +) => { + try { + const { id } = await params; + + const body: { key: string; type: 'embedding' | 'chat' } = await req.json(); + + if (!body.key) { + return Response.json( + { + message: 'Key and name must be provided', + }, + { + status: 400, + }, + ); + } + + const registry = new ModelRegistry(); + + await registry.removeProviderModel(id, body.type, body.key); + + return Response.json( + { + message: 'Model added successfully', + }, + { + status: 200, + }, + ); + } catch (err) { + console.error('An error occurred while deleting provider model', err); + return Response.json( + { + message: 'An error has occurred.', + }, + { + status: 500, + }, + ); + } +}; diff --git a/src/app/api/providers/[id]/route.ts b/src/app/api/providers/[id]/route.ts new file mode 100644 index 0000000..489d73a --- /dev/null +++ b/src/app/api/providers/[id]/route.ts @@ -0,0 +1,89 @@ +import ModelRegistry from '@/lib/models/registry'; +import { NextRequest } from 'next/server'; + +export const DELETE = async ( + req: NextRequest, + { params }: { params: Promise<{ id: string }> }, +) => { + try { + const { id } = await params; + + if (!id) { + return Response.json( + { + message: 'Provider ID is required.', + }, + { + status: 400, + }, + ); + } + + const registry = new ModelRegistry(); + await registry.removeProvider(id); + + return Response.json( + { + message: 'Provider deleted successfully.', + }, + { + status: 200, + }, + ); + } catch (err: any) { + console.error('An error occurred while deleting provider', err.message); + return Response.json( + { + message: 'An error has occurred.', + }, + { + status: 500, + }, + ); + } +}; + +export const PATCH = async ( + req: NextRequest, + { params }: { params: Promise<{ id: string }> }, +) => { + try { + const body = await req.json(); + const { name, config } = body; + const { id } = await params; + + if (!id || !name || !config) { + return Response.json( + { + message: 'Missing required fields.', + }, + { + status: 400, + }, + ); + } + + const registry = new ModelRegistry(); + + const updatedProvider = await registry.updateProvider(id, name, config); + + return Response.json( + { + provider: updatedProvider, + }, + { + status: 200, + }, + ); + } catch (err: any) { + console.error('An error occurred while updating provider', err.message); + return Response.json( + { + message: 'An error has occurred.', + }, + { + status: 500, + }, + ); + } +}; diff --git a/src/app/api/providers/route.ts b/src/app/api/providers/route.ts index 1a80957..53d6e60 100644 --- a/src/app/api/providers/route.ts +++ b/src/app/api/providers/route.ts @@ -1,4 +1,5 @@ import ModelRegistry from '@/lib/models/registry'; +import { NextRequest } from 'next/server'; export const GET = async (req: Request) => { try { @@ -6,9 +7,13 @@ export const GET = async (req: Request) => { const activeProviders = await registry.getActiveProviders(); + const filteredProviders = activeProviders.filter((p) => { + return !p.chatModels.some((m) => m.key === 'error'); + }); + return Response.json( { - providers: activeProviders, + providers: filteredProviders, }, { status: 200, @@ -26,3 +31,44 @@ export const GET = async (req: Request) => { ); } }; + +export const POST = async (req: NextRequest) => { + try { + const body = await req.json(); + const { type, name, config } = body; + + if (!type || !name || !config) { + return Response.json( + { + message: 'Missing required fields.', + }, + { + status: 400, + }, + ); + } + + const registry = new ModelRegistry(); + + const newProvider = await registry.addProvider(type, name, config); + + return Response.json( + { + provider: newProvider, + }, + { + status: 200, + }, + ); + } catch (err) { + console.error('An error occurred while creating provider', err); + return Response.json( + { + message: 'An error has occurred.', + }, + { + status: 500, + }, + ); + } +}; diff --git a/src/lib/config/index.ts b/src/lib/config/index.ts index 159ee56..7291860 100644 --- a/src/lib/config/index.ts +++ b/src/lib/config/index.ts @@ -225,6 +225,8 @@ class ConfigManager { this.currentConfig.modelProviders.push(newModelProvider); this.saveConfig(); + + return newModelProvider; } public removeModelProvider(id: string) { @@ -240,6 +242,69 @@ class ConfigManager { this.saveConfig(); } + public async updateModelProvider(id: string, name: string, config: any) { + const provider = this.currentConfig.modelProviders.find((p) => { + return p.id === id; + }); + + if (!provider) throw new Error('Provider not found'); + + provider.name = name; + provider.config = config; + + this.saveConfig(); + + return provider; + } + + public addProviderModel( + providerId: string, + type: 'embedding' | 'chat', + model: any, + ) { + const provider = this.currentConfig.modelProviders.find( + (p) => p.id === providerId, + ); + + if (!provider) throw new Error('Invalid provider id'); + + delete model.type; + + if (type === 'chat') { + provider.chatModels.push(model); + } else { + provider.embeddingModels.push(model); + } + + this.saveConfig(); + + return model; + } + + public removeProviderModel( + providerId: string, + type: 'embedding' | 'chat', + modelKey: string, + ) { + const provider = this.currentConfig.modelProviders.find( + (p) => p.id === providerId, + ); + + if (!provider) throw new Error('Invalid provider id'); + + if (type === 'chat') { + provider.chatModels = provider.chatModels.filter( + (m) => m.key !== modelKey, + ); + } else { + provider.embeddingModels = provider.embeddingModels.filter( + (m) => m.key != modelKey, + ); + } + + this.saveConfig(); + } + public isSetupComplete() { return this.currentConfig.setupComplete; } @@ -255,6 +320,10 @@ class ConfigManager { public getUIConfigSections(): UIConfigSections { return this.uiConfigSections; } + + public getCurrentConfig(): Config { + return JSON.parse(JSON.stringify(this.currentConfig)); + } } const configManager = new ConfigManager(); diff --git a/src/lib/models/registry.ts b/src/lib/models/registry.ts index a2a7aa8..b44781a 100644 --- a/src/lib/models/registry.ts +++ b/src/lib/models/registry.ts @@ -4,7 +4,8 @@ import BaseModelProvider, { } from './providers/baseProvider'; import { getConfiguredModelProviders } from '../config/serverRegistry'; import { providers } from './providers'; -import { MinimalProvider, Model } from './types'; +import { MinimalProvider, ModelList } from './types'; +import configManager from '../config'; class ModelRegistry { activeProviders: (ConfigModelProvider & { @@ -40,7 +41,25 @@ class ModelRegistry { await Promise.all( this.activeProviders.map(async (p) => { - const m = await p.provider.getModelList(); + let m: ModelList = { chat: [], embedding: [] }; + + try { + m = await p.provider.getModelList(); + } catch (err: any) { + console.error( + `Failed to get model list. Type: ${p.type}, ID: ${p.id}, Error: ${err.message}`, + ); + + m = { + chat: [ + { + key: 'error', + name: err.message, + }, + ], + embedding: [], + }; + } providers.push({ id: p.id, @@ -48,6 +67,7 @@ class ModelRegistry { chatModels: m.chat, embeddingModels: m.embedding, }); + }), ); @@ -73,6 +93,132 @@ class ModelRegistry { return model; } + + async addProvider( + type: string, + name: string, + config: Record, + ): Promise { + const provider = providers[type]; + if (!provider) throw new Error('Invalid provider type'); + + const newProvider = configManager.addModelProvider(type, name, config); + + const instance = createProviderInstance( + provider, + newProvider.id, + newProvider.name, + newProvider.config, + ); + + let m: ModelList = { chat: [], embedding: [] }; + + try { + m = await instance.getModelList(); + } catch (err: any) { + console.error( + `Failed to get model list for newly added provider. Type: ${type}, ID: ${newProvider.id}, Error: ${err.message}`, + ); + + m = { + chat: [ + { + key: 'error', + name: err.message, + }, + ], + embedding: [], + }; + } + + this.activeProviders.push({ + ...newProvider, + provider: instance, + }); + + return { + ...newProvider, + chatModels: m.chat || [], + embeddingModels: m.embedding || [], + }; + } + + async removeProvider(providerId: string): Promise { + configManager.removeModelProvider(providerId); + this.activeProviders = this.activeProviders.filter( + (p) => p.id !== providerId, + ); + + return; + } + + async updateProvider( + providerId: string, + name: string, + config: any, + ): Promise { + const updated = await configManager.updateModelProvider( + providerId, + name, + config, + ); + const instance = createProviderInstance( + providers[updated.type], + providerId, + name, + config, + ); + + let m: ModelList = { chat: [], embedding: [] }; + + try { + m = await instance.getModelList(); + } catch (err: any) { + console.error( + `Failed to get model list for updated provider. Type: ${updated.type}, ID: ${updated.id}, Error: ${err.message}`, + ); + + m = { + chat: [ + { + key: 'error', + name: err.message, + }, + ], + embedding: [], + }; + } + + this.activeProviders.push({ + ...updated, + provider: instance, + }); + + return { + ...updated, + chatModels: m.chat || [], + embeddingModels: m.embedding || [], + }; + } + + /* Using async here because maybe in the future we might want to add some validation?? */ + async addProviderModel( + providerId: string, + type: 'embedding' | 'chat', + model: any, + ): Promise { + const addedModel = configManager.addProviderModel(providerId, type, model); + return addedModel; + } + + async removeProviderModel( + providerId: string, + type: 'embedding' | 'chat', + modelKey: string, + ): Promise { + configManager.removeProviderModel(providerId, type, modelKey); + return; + } } export default ModelRegistry;