From 9e4132fd3f3e2e4a5dc92b4ff3657c664a88c39e Mon Sep 17 00:00:00 2001 From: Chris Staley Date: Thu, 2 Apr 2026 11:37:23 -0600 Subject: [PATCH] feat(db): add syncedAvailableModels namespace and CRUD functions --- src/lib/db/models.ts | 68 ++++++++++++++++++++++++++++++++++++++++++++ src/lib/localDb.ts | 7 ++++- 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/src/lib/db/models.ts b/src/lib/db/models.ts index 9e2bc118b9..5d295d6148 100644 --- a/src/lib/db/models.ts +++ b/src/lib/db/models.ts @@ -506,6 +506,74 @@ export async function removeCustomModel(providerId: string, modelId: string) { return true; } +// ──────────────── Synced Available Models ──────────────── + +export interface SyncedAvailableModel { + id: string; + name: string; + source: "api-sync"; + supportedEndpoints?: string[]; + inputTokenLimit?: number; + outputTokenLimit?: number; + description?: string; + supportsThinking?: boolean; +} + +/** + * Get synced available models for a provider. + */ +export async function getSyncedAvailableModels(providerId: string): Promise { + const db = getDbInstance(); + const row = db + .prepare("SELECT value FROM key_value WHERE namespace = 'syncedAvailableModels' AND key = ?") + .get(providerId); + const value = getKeyValue(row).value; + return value ? JSON.parse(value) : []; +} + +/** + * Get all synced available models across all providers. + */ +export async function getAllSyncedAvailableModels(): Promise> { + const db = getDbInstance(); + const rows = db + .prepare("SELECT key, value FROM key_value WHERE namespace = 'syncedAvailableModels'") + .all(); + const result: Record = {}; + for (const row of rows) { + const { key, value } = getKeyValue(row); + if (!key || value === null) continue; + result[key] = JSON.parse(value); + } + return result; +} + +/** + * Union new models into the existing synced available models for a provider. + * Models are matched by ID. New models are added; existing models get their + * metadata updated from the new data. + */ +export async function unionSyncedAvailableModels( + providerId: string, + newModels: SyncedAvailableModel[] +): Promise { + const existing = await getSyncedAvailableModels(providerId); + const map = new Map(); + for (const m of existing) { + if (m.id) map.set(m.id, m); + } + for (const m of newModels) { + if (m.id) map.set(m.id, m); + } + const merged = Array.from(map.values()); + const db = getDbInstance(); + db.prepare( + "INSERT OR REPLACE INTO key_value (namespace, key, value) VALUES ('syncedAvailableModels', ?, ?)" + ).run(providerId, JSON.stringify(merged)); + backupDbFile("pre-write"); + return merged; +} + export async function updateCustomModel( providerId: string, modelId: string, diff --git a/src/lib/localDb.ts b/src/lib/localDb.ts index 0294b0620c..33e5d26067 100644 --- a/src/lib/localDb.ts +++ b/src/lib/localDb.ts @@ -58,9 +58,14 @@ export { getModelPreserveOpenAIDeveloperRole, getModelUpstreamExtraHeaders, getModelIsHidden, + + // Synced Available Models + getSyncedAvailableModels, + getAllSyncedAvailableModels, + unionSyncedAvailableModels, } from "./db/models"; -export type { ModelCompatPerProtocol, ModelCompatPatch } from "./db/models"; +export type { ModelCompatPerProtocol, ModelCompatPatch, SyncedAvailableModel } from "./db/models"; export { // Combos