260 lines
7.5 KiB
JavaScript
260 lines
7.5 KiB
JavaScript
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<Array<{id: number, name: string, description: string, fallback_provider: string, fallback_model: string, cooldown_seconds: number, createdAt: Date, ruleCount: number, workspaceCount: number}>>}
|
|
*/
|
|
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 };
|