const { Prisma } = require("@prisma/client"); const prisma = require("../utils/prisma"); const { ModelRouterRule } = require("./modelRouterRule"); const ModelRouter = { validations: { name: (value) => { if (!value || typeof value !== "string") return null; return String(value).trim().slice(0, 255); }, description: (value) => { if (!value || typeof value !== "string") return null; return String(value).trim(); }, fallback_provider: (value) => { if (!value || typeof value !== "string") return null; return String(value); }, fallback_model: (value) => { if (!value || typeof value !== "string") return null; return String(value); }, cooldown_seconds: (value) => { const num = Number(value); if (isNaN(num) || num < 0 || num > 3600) return null; return Math.round(num); }, }, create: async function (data = {}, creatorId = null) { const name = this.validations.name(data.name); if (!name) return { router: null, error: "Name is required." }; const fallback_provider = this.validations.fallback_provider( data.fallback_provider ); const fallback_model = this.validations.fallback_model(data.fallback_model); if (!fallback_provider || !fallback_model) return { router: null, error: "Fallback provider and model are required.", }; const { ModelRouterService } = require("../utils/router"); const cooldown_seconds = data.cooldown_seconds != null ? this.validations.cooldown_seconds(data.cooldown_seconds) : ModelRouterService.DEFAULT_STICKY_MS; try { const router = await prisma.model_routers.create({ data: { name, description: this.validations.description(data.description), fallback_provider, fallback_model, cooldown_seconds: cooldown_seconds ?? 30, created_by: creatorId ? Number(creatorId) : null, }, }); return { router, error: null }; } catch (error) { console.error(error.message); // P2002 is the unique constraint violation error code if ( error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002" ) return { router: null, error: "A router with that name already exists.", }; return { router: null, error: error.message }; } }, get: async function (clause = {}) { try { const router = await prisma.model_routers.findFirst({ where: clause, }); return router || null; } catch (error) { console.error(error.message); return null; } }, getWithRules: async function (clause = {}) { try { const router = await prisma.model_routers.findFirst({ where: clause, include: { rules: { orderBy: { priority: "asc" }, }, }, }); if (!router) return null; return { ...router, rules: router.rules.map(ModelRouterRule._hydrate) }; } catch (error) { console.error(error.message); return null; } }, getWithRulesAndCount: async function (clause = {}) { try { const router = await prisma.model_routers.findFirst({ where: clause, include: { rules: { orderBy: { priority: "asc" }, }, }, }); if (!router) return null; const workspaceCount = await this.workspaceCount(router.id); return { ...router, rules: router.rules.map(ModelRouterRule._hydrate), workspaceCount, }; } catch (error) { console.error(error.message); return null; } }, where: async function (clause = {}, limit = null, orderBy = null) { try { const results = await prisma.model_routers.findMany({ where: clause, ...(limit !== null ? { take: limit } : {}), ...(orderBy !== null ? { orderBy } : { orderBy: { createdAt: "asc" } }), }); return results; } catch (error) { console.error(error.message); return []; } }, /** * Get all model routers with their rule and workspace counts. * @returns {Promise>} */ getAllWithCounts: async function () { try { const routers = await prisma.model_routers.findMany({ orderBy: { createdAt: "asc" }, include: { _count: { select: { rules: true, }, }, }, }); const results = await Promise.all( routers.map(async ({ _count, ...router }) => ({ ...router, ruleCount: _count.rules, workspaceCount: await this.workspaceCount(router.id), })) ); return results; } catch (error) { console.error(error.message); return []; } }, update: async function (id = null, data = {}) { if (!id) throw new Error("No router id provided for update"); const updates = {}; if (data.name !== undefined) { const name = this.validations.name(data.name); if (!name) return { router: null, error: "Name cannot be empty." }; updates.name = name; } if (data.description !== undefined) updates.description = this.validations.description(data.description); if (data.fallback_provider !== undefined) { const provider = this.validations.fallback_provider( data.fallback_provider ); if (!provider) return { router: null, error: "Fallback provider is required." }; updates.fallback_provider = provider; } if (data.fallback_model !== undefined) { const model = this.validations.fallback_model(data.fallback_model); if (!model) return { router: null, error: "Fallback model is required." }; updates.fallback_model = model; } if (data.cooldown_seconds !== undefined) { const cooldown = this.validations.cooldown_seconds(data.cooldown_seconds); if (cooldown === null) return { router: null, error: "Cooldown must be a number between 0 and 3600.", }; updates.cooldown_seconds = cooldown; } if (Object.keys(updates).length === 0) return { router: { id }, error: "No valid fields to update." }; try { const router = await prisma.model_routers.update({ where: { id: Number(id) }, data: updates, }); return { router, error: null }; } catch (error) { console.error(error.message); // P2002 is the unique constraint violation error code if ( error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002" ) return { router: null, error: "A router with that name already exists.", }; return { router: null, error: error.message }; } }, delete: async function (id = null) { if (!id) return false; try { await prisma.model_routers.delete({ where: { id: Number(id) }, }); return true; } catch (error) { console.error(error.message); return false; } }, workspaceCount: async function (routerId) { try { const count = await prisma.workspaces.count({ where: { router_id: Number(routerId) }, }); return count; } catch (error) { console.error(error.message); return 0; } }, }; module.exports = { ModelRouter };