Files
OmniRoute/src/lib/db/models.ts

240 lines
7.4 KiB
TypeScript

/**
* db/models.js — Model aliases, MITM aliases, and custom models.
*/
import { getDbInstance } from "./core";
import { backupDbFile } from "./backup";
type JsonRecord = Record<string, unknown>;
function asRecord(value: unknown): JsonRecord {
return value && typeof value === "object" && !Array.isArray(value) ? (value as JsonRecord) : {};
}
function getKeyValue(row: unknown): { key: string | null; value: string | null } {
const record = asRecord(row);
return {
key: typeof record.key === "string" ? record.key : null,
value: typeof record.value === "string" ? record.value : null,
};
}
// ──────────────── Model Aliases ────────────────
export async function getModelAliases() {
const db = getDbInstance();
const rows = db
.prepare("SELECT key, value FROM key_value WHERE namespace = 'modelAliases'")
.all();
const result: Record<string, unknown> = {};
for (const row of rows) {
const { key, value } = getKeyValue(row);
if (!key || value === null) continue;
result[key] = JSON.parse(value);
}
return result;
}
export async function setModelAlias(alias, model) {
const db = getDbInstance();
db.prepare(
"INSERT OR REPLACE INTO key_value (namespace, key, value) VALUES ('modelAliases', ?, ?)"
).run(alias, JSON.stringify(model));
backupDbFile("pre-write");
}
export async function deleteModelAlias(alias) {
const db = getDbInstance();
db.prepare("DELETE FROM key_value WHERE namespace = 'modelAliases' AND key = ?").run(alias);
backupDbFile("pre-write");
}
// ──────────────── MITM Alias ────────────────
export async function getMitmAlias(toolName) {
const db = getDbInstance();
if (toolName) {
const row = db
.prepare("SELECT value FROM key_value WHERE namespace = 'mitmAlias' AND key = ?")
.get(toolName);
const value = getKeyValue(row).value;
return value ? JSON.parse(value) : {};
}
const rows = db.prepare("SELECT key, value FROM key_value WHERE namespace = 'mitmAlias'").all();
const result: Record<string, unknown> = {};
for (const row of rows) {
const { key, value } = getKeyValue(row);
if (!key || value === null) continue;
result[key] = JSON.parse(value);
}
return result;
}
export async function setMitmAliasAll(toolName, mappings) {
const db = getDbInstance();
db.prepare(
"INSERT OR REPLACE INTO key_value (namespace, key, value) VALUES ('mitmAlias', ?, ?)"
).run(toolName, JSON.stringify(mappings || {}));
backupDbFile("pre-write");
}
// ──────────────── Custom Models ────────────────
export async function getCustomModels(providerId) {
const db = getDbInstance();
if (providerId) {
const row = db
.prepare("SELECT value FROM key_value WHERE namespace = 'customModels' AND key = ?")
.get(providerId);
const value = getKeyValue(row).value;
return value ? JSON.parse(value) : [];
}
const rows = db
.prepare("SELECT key, value FROM key_value WHERE namespace = 'customModels'")
.all();
const result: Record<string, unknown> = {};
for (const row of rows) {
const { key, value } = getKeyValue(row);
if (!key || value === null) continue;
result[key] = JSON.parse(value);
}
return result;
}
export async function getAllCustomModels() {
const db = getDbInstance();
const rows = db
.prepare("SELECT key, value FROM key_value WHERE namespace = 'customModels'")
.all();
const result: Record<string, unknown> = {};
for (const row of rows) {
const { key, value } = getKeyValue(row);
if (!key || value === null) continue;
result[key] = JSON.parse(value);
}
return result;
}
export async function addCustomModel(
providerId: string,
modelId: string,
modelName?: string,
source = "manual",
apiFormat: "chat-completions" | "responses" = "chat-completions",
supportedEndpoints: string[] = ["chat"]
) {
const db = getDbInstance();
const row = db
.prepare("SELECT value FROM key_value WHERE namespace = 'customModels' AND key = ?")
.get(providerId);
const value = getKeyValue(row).value;
const models = value ? JSON.parse(value) : [];
const exists = models.find((m) => m.id === modelId);
if (exists) return exists;
const model = {
id: modelId,
name: modelName || modelId,
source,
apiFormat,
supportedEndpoints,
};
models.push(model);
db.prepare(
"INSERT OR REPLACE INTO key_value (namespace, key, value) VALUES ('customModels', ?, ?)"
).run(providerId, JSON.stringify(models));
backupDbFile("pre-write");
return model;
}
export async function removeCustomModel(providerId, modelId) {
const db = getDbInstance();
const row = db
.prepare("SELECT value FROM key_value WHERE namespace = 'customModels' AND key = ?")
.get(providerId);
if (!row) return false;
const value = getKeyValue(row).value;
if (!value) return false;
const models = JSON.parse(value);
const before = models.length;
const filtered = models.filter((m) => m.id !== modelId);
if (filtered.length === before) return false;
if (filtered.length === 0) {
db.prepare("DELETE FROM key_value WHERE namespace = 'customModels' AND key = ?").run(
providerId
);
} else {
db.prepare("UPDATE key_value SET value = ? WHERE namespace = 'customModels' AND key = ?").run(
JSON.stringify(filtered),
providerId
);
}
backupDbFile("pre-write");
return true;
}
export async function updateCustomModel(providerId, modelId, updates = {}) {
const db = getDbInstance();
const row = db
.prepare("SELECT value FROM key_value WHERE namespace = 'customModels' AND key = ?")
.get(providerId);
if (!row) return null;
const value = getKeyValue(row).value;
if (!value) return null;
const models = JSON.parse(value);
const index = models.findIndex((m) => m.id === modelId);
if (index === -1) return null;
const current = models[index];
const next = {
...current,
...(updates.modelName !== undefined ? { name: updates.modelName || current.name } : {}),
...(updates.apiFormat !== undefined ? { apiFormat: updates.apiFormat } : {}),
...(updates.supportedEndpoints !== undefined
? { supportedEndpoints: updates.supportedEndpoints }
: {}),
...(updates.normalizeToolCallId !== undefined
? { normalizeToolCallId: Boolean(updates.normalizeToolCallId) }
: {}),
};
models[index] = next;
db.prepare("UPDATE key_value SET value = ? WHERE namespace = 'customModels' AND key = ?").run(
JSON.stringify(models),
providerId
);
backupDbFile("pre-write");
return next;
}
/**
* Whether the given provider/model has "normalize tool call id" (9-char Mistral-style) enabled.
* Only custom models can have this set; returns false for built-in models.
*/
export function getModelNormalizeToolCallId(providerId: string, modelId: string): boolean {
const db = getDbInstance();
const row = db
.prepare("SELECT value FROM key_value WHERE namespace = 'customModels' AND key = ?")
.get(providerId);
const value = getKeyValue(row).value;
if (!value) return false;
let models: { id: string; normalizeToolCallId?: boolean }[];
try {
models = JSON.parse(value);
} catch {
return false;
}
if (!Array.isArray(models)) return false;
const m = models.find((x: { id: string }) => x.id === modelId);
return Boolean(m?.normalizeToolCallId);
}