import type { AgentRunRequest } from "@rakazo/adapter-kit"; import type { Actor } from "@rakazo/contracts"; import { type findDefaultModelCredential, findModelCredential, type PrismaClient, } from "@rakazo/db"; import { listPiCatalog, scriptedCatalogEntry } from "./pi-models.js"; import { OPENAI_COMPATIBLE_PROVIDER_ID } from "./pi-openai-compatible-provider.js"; type ModelCredential = Awaited>; export function isCatalogModelChoice(provider: string, modelId: string) { return [...listPiCatalog(), scriptedCatalogEntry].some( (item) => item.provider === provider && item.id === modelId, ); } export async function validateConnectedModelChoice( prisma: PrismaClient, actor: Pick, provider: string, modelId: string, ) { const credential = await findModelCredential(prisma, actor, provider); if (!credential) return "Connect that model provider first"; if (isCatalogModelChoice(provider, modelId)) return undefined; // Free-form saved IDs only resolve at runtime for openai-compatible connections. if (provider !== OPENAI_COMPATIBLE_PROVIDER_ID) { return "Unknown model for that provider"; } const savedChoice = await prisma.spaceModelPreference.findFirst({ where: { spaceId: actor.spaceId, userId: actor.userId, modelId, credential: { userId: actor.userId, provider }, }, select: { id: true }, }); return savedChoice ? undefined : "Unknown model for that provider"; } /** Select configuration without loading secrets or applying a runtime-specific fallback. */ export function selectConfiguredModel(input: { bot: { modelProvider: string | null; modelId: string | null; thinkingLevel: string | null; } | null; /** The space's model for the bot's tier (findSpaceModelTier); null when the tier is unmapped. */ tierModel?: { modelId: string; credential: NonNullable } | null; overrideCredential: ModelCredential; defaultCredential: ModelCredential; settings: { defaultModelProvider: string | null; defaultModelId: string | null } | null; deployment: { provider: string; model: string } | null; }) { const { bot, tierModel, overrideCredential, defaultCredential, settings, deployment } = input; // A mapped tier wins; an unmapped one leaves the usual choice below untouched. if (tierModel) { return { provider: tierModel.credential.provider, id: tierModel.modelId, credential: tierModel.credential, thinkingLevel: (bot?.thinkingLevel as AgentRunRequest["model"]["thinkingLevel"]) ?? null, }; } const hasOverride = Boolean(bot?.modelProvider && bot.modelId); // The override provider, model and credential must win together. const useOverride = Boolean(hasOverride && overrideCredential); const credential = useOverride ? overrideCredential : defaultCredential; return { provider: (useOverride ? bot!.modelProvider : null) ?? credential?.provider ?? settings?.defaultModelProvider ?? deployment?.provider, id: (useOverride ? bot!.modelId : null) ?? credential?.defaultModel ?? settings?.defaultModelId ?? deployment?.model, credential, // Preserve bot thinking for the Space default; drop it for an unavailable override. thinkingLevel: hasOverride && !useOverride ? null : ((bot?.thinkingLevel as AgentRunRequest["model"]["thinkingLevel"]) ?? null), }; }