evOo-local/packages/db/src/model-credentials.ts
cynarAI 6c7eed7580 Initial clean evOo-local from f00577b
Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline
(Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/*
packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
2026-09-20 17:15:16 +02:00

126 lines
3.4 KiB
TypeScript

import type { PrismaClient } from "./client.js";
export const newestCredentialOrder = [
{ updatedAt: "desc" as const },
{ createdAt: "desc" as const },
{ id: "desc" as const },
];
export const newestModelCredentialOrder = newestCredentialOrder;
type ModelCredentialScope = { userId: string; spaceId: string };
export async function selectSpaceModelPreference(
prisma: Pick<PrismaClient, "spaceModelPreference">,
scope: ModelCredentialScope,
credentialId: string,
modelId: string | null,
) {
await prisma.spaceModelPreference.updateMany({
where: {
spaceId: scope.spaceId,
userId: scope.userId,
isDefault: true,
credentialId: { not: credentialId },
},
data: { isDefault: false },
});
return prisma.spaceModelPreference.upsert({
where: {
spaceId_userId_credentialId: {
spaceId: scope.spaceId,
userId: scope.userId,
credentialId,
},
},
create: {
spaceId: scope.spaceId,
userId: scope.userId,
credentialId,
modelId,
isDefault: true,
},
update: { modelId, isDefault: true },
});
}
function withModelPreference<
T extends {
credential: {
id: string;
userId: string;
provider: string;
label: string;
secretId: string;
createdAt: Date;
updatedAt: Date;
};
isDefault: boolean;
modelId: string | null;
},
>(preference: T) {
return {
...preference.credential,
isDefault: preference.isDefault,
defaultModel: preference.modelId,
};
}
export async function findDefaultModelCredential(
prisma: PrismaClient,
scope: ModelCredentialScope,
) {
const preference = await prisma.spaceModelPreference.findFirst({
where: { spaceId: scope.spaceId, userId: scope.userId, isDefault: true },
include: { credential: true },
orderBy: [{ updatedAt: "desc" }, { id: "desc" }],
});
return preference ? withModelPreference(preference) : null;
}
export function findNewestUserModelCredential(
prisma: PrismaClient,
userId: string,
provider: string,
) {
return prisma.userModelCredential.findFirst({
where: { userId, provider },
orderBy: newestModelCredentialOrder,
});
}
export async function findModelCredential(
prisma: PrismaClient,
scope: ModelCredentialScope,
provider: string,
modelId?: string | null,
) {
// When a helper or bot selects a free-form model, prefer the preference that owns
// that modelId so runtime uses the same credential the picker advertised.
if (modelId) {
const matching = await prisma.spaceModelPreference.findFirst({
where: {
spaceId: scope.spaceId,
userId: scope.userId,
modelId,
credential: { provider },
},
include: { credential: true },
orderBy: [{ isDefault: "desc" }, { updatedAt: "desc" }, { id: "desc" }],
});
if (matching) return withModelPreference(matching);
}
const preference = await prisma.spaceModelPreference.findFirst({
where: {
spaceId: scope.spaceId,
userId: scope.userId,
credential: { provider },
},
include: { credential: true },
orderBy: [{ isDefault: "desc" }, { updatedAt: "desc" }, { id: "desc" }],
});
if (preference) return withModelPreference(preference);
const credential = await findNewestUserModelCredential(prisma, scope.userId, provider);
return credential ? { ...credential, isDefault: false, defaultModel: null } : null;
}