add basic ui for private inference resource

This commit is contained in:
miloschwartz
2026-08-04 16:54:23 -04:00
parent c085de1e9e
commit 7759d87835
19 changed files with 828 additions and 208 deletions
+60 -19
View File
@@ -13,11 +13,7 @@ import { z } from "zod";
type DbOrTrx = Transaction | typeof db;
export const modelAccessModeSchema = z.enum([
"passthrough",
"catalog",
"allowlist"
]);
export const modelAccessModeSchema = z.enum(["catalog", "allowlist"]);
export type ModelAccessMode = z.infer<typeof modelAccessModeSchema>;
@@ -50,10 +46,7 @@ function normalizeAttachments(
): ResourceAiProviderAttachment[] {
const byProvider = new Map<number, ModelAccessMode>();
for (const input of inputs) {
byProvider.set(
input.providerId,
input.modelAccessMode ?? "passthrough"
);
byProvider.set(input.providerId, input.modelAccessMode ?? "catalog");
}
return [...byProvider.entries()].map(([providerId, modelAccessMode]) => ({
providerId,
@@ -61,9 +54,61 @@ function normalizeAttachments(
}));
}
/**
* Ensure enabled catalog modelKeys are unique across attached providers.
* Catalog attachments contribute all enabled models on the provider.
* Allowlist attachments contribute nothing until models are allowlisted
* (those are checked when the allowlist is set).
*/
export async function assertNoOverlappingModelKeys(
attachments: ResourceAiProviderAttachment[],
trx: DbOrTrx = db
): Promise<InferenceFieldsError | null> {
const catalogProviderIds = attachments
.filter((a) => a.modelAccessMode === "catalog")
.map((a) => a.providerId);
if (catalogProviderIds.length < 2) {
return null;
}
const models = await trx
.select({
providerId: aiModels.providerId,
modelKey: aiModels.modelKey
})
.from(aiModels)
.where(
and(
inArray(aiModels.providerId, catalogProviderIds),
eq(aiModels.enabled, true)
)
);
const keyToProviders = new Map<string, number[]>();
for (const model of models) {
const existing = keyToProviders.get(model.modelKey) ?? [];
if (!existing.includes(model.providerId)) {
existing.push(model.providerId);
}
keyToProviders.set(model.modelKey, existing);
}
const overlaps = [...keyToProviders.entries()].filter(
([, providerIds]) => providerIds.length > 1
);
if (overlaps.length === 0) {
return null;
}
const keys = overlaps.map(([key]) => key).sort();
return {
error: `Model keys must be unique across providers on a resource. Overlapping keys: ${keys.join(", ")}`
};
}
/**
* Validate provider attachments for an org.
* At most one passthrough provider is allowed per resource.
*/
export async function resolveProviderAttachments(input: {
orgId: string;
@@ -78,15 +123,6 @@ export async function resolveProviderAttachments(input: {
};
}
const passthroughCount = attachments.filter(
(a) => a.modelAccessMode === "passthrough"
).length;
if (passthroughCount > 1) {
return {
error: "A resource may have at most one AI provider in passthrough mode"
};
}
if (attachments.length === 0) {
return [];
}
@@ -119,6 +155,11 @@ export async function resolveProviderAttachments(input: {
};
}
const overlapError = await assertNoOverlappingModelKeys(attachments);
if (overlapError) {
return overlapError;
}
return attachments;
}