mirror of
https://github.com/fosrl/pangolin.git
synced 2026-08-06 20:51:36 +02:00
allow no provider on a resource
This commit is contained in:
@@ -1739,7 +1739,6 @@
|
|||||||
"aiResourceProvidersHelp": "Models must be defined on each provider. Model names cannot overlap across selected providers.",
|
"aiResourceProvidersHelp": "Models must be defined on each provider. Model names cannot overlap across selected providers.",
|
||||||
"aiResourceProvidersSelect": "Select providers",
|
"aiResourceProvidersSelect": "Select providers",
|
||||||
"aiResourceProvidersEmpty": "No AI providers found",
|
"aiResourceProvidersEmpty": "No AI providers found",
|
||||||
"aiResourceProvidersRequired": "Select at least one AI provider",
|
|
||||||
"aiResourceProvidersUpdated": "Providers updated",
|
"aiResourceProvidersUpdated": "Providers updated",
|
||||||
"aiResourceProvidersErrorUpdate": "Failed to update providers",
|
"aiResourceProvidersErrorUpdate": "Failed to update providers",
|
||||||
"aiResourceAliasRequired": "Alias is required for inference resources",
|
"aiResourceAliasRequired": "Alias is required for inference resources",
|
||||||
|
|||||||
@@ -394,7 +394,7 @@ async function createHttpResource(
|
|||||||
const resolved = await resolveProviderAttachments({
|
const resolved = await resolveProviderAttachments({
|
||||||
orgId,
|
orgId,
|
||||||
attachments: aiProviderInputs ?? [],
|
attachments: aiProviderInputs ?? [],
|
||||||
requireAtLeastOne: true
|
requireAtLeastOne: false
|
||||||
});
|
});
|
||||||
if (isInferenceFieldsError(resolved)) {
|
if (isInferenceFieldsError(resolved)) {
|
||||||
return next(createHttpError(HttpCode.BAD_REQUEST, resolved.error));
|
return next(createHttpError(HttpCode.BAD_REQUEST, resolved.error));
|
||||||
|
|||||||
@@ -77,8 +77,9 @@ export async function removeAiProviderFromResource(
|
|||||||
|
|
||||||
const { providerId } = parsedBody.data;
|
const { providerId } = parsedBody.data;
|
||||||
|
|
||||||
const parsedParams =
|
const parsedParams = removeAiProviderFromResourceParamsSchema.safeParse(
|
||||||
removeAiProviderFromResourceParamsSchema.safeParse(req.params);
|
req.params
|
||||||
|
);
|
||||||
if (!parsedParams.success) {
|
if (!parsedParams.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
@@ -129,22 +130,15 @@ export async function removeAiProviderFromResource(
|
|||||||
modelAccessMode: a.modelAccessMode
|
modelAccessMode: a.modelAccessMode
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (remaining.length === 0) {
|
|
||||||
return next(
|
|
||||||
createHttpError(
|
|
||||||
HttpCode.BAD_REQUEST,
|
|
||||||
"At least one AI provider is required for inference-mode resources"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const attachments = await resolveProviderAttachments({
|
const attachments = await resolveProviderAttachments({
|
||||||
orgId: resource.orgId,
|
orgId: resource.orgId,
|
||||||
attachments: remaining,
|
attachments: remaining,
|
||||||
requireAtLeastOne: true
|
requireAtLeastOne: false
|
||||||
});
|
});
|
||||||
if (isInferenceFieldsError(attachments)) {
|
if (isInferenceFieldsError(attachments)) {
|
||||||
return next(createHttpError(HttpCode.BAD_REQUEST, attachments.error));
|
return next(
|
||||||
|
createHttpError(HttpCode.BAD_REQUEST, attachments.error)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await setPublicResourceAiProviders(resourceId, attachments);
|
await setPublicResourceAiProviders(resourceId, attachments);
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ registry.registerPath({
|
|||||||
method: "post",
|
method: "post",
|
||||||
path: "/resource/{resourceId}/ai-providers",
|
path: "/resource/{resourceId}/ai-providers",
|
||||||
description:
|
description:
|
||||||
"Replace the AI providers attached to an inference resource. At least one provider is required. Model keys must be unique across attached catalog providers.",
|
"Replace the AI providers attached to an inference resource. An empty list clears all providers. Model keys must be unique across attached catalog providers.",
|
||||||
tags: [OpenAPITags.PublicResource],
|
tags: [OpenAPITags.PublicResource],
|
||||||
request: {
|
request: {
|
||||||
params: setResourceAiProvidersParamsSchema,
|
params: setResourceAiProvidersParamsSchema,
|
||||||
@@ -113,7 +113,7 @@ export async function setResourceAiProviders(
|
|||||||
const attachments = await resolveProviderAttachments({
|
const attachments = await resolveProviderAttachments({
|
||||||
orgId: resource.orgId,
|
orgId: resource.orgId,
|
||||||
attachments: providers,
|
attachments: providers,
|
||||||
requireAtLeastOne: true
|
requireAtLeastOne: false
|
||||||
});
|
});
|
||||||
if (isInferenceFieldsError(attachments)) {
|
if (isInferenceFieldsError(attachments)) {
|
||||||
return next(
|
return next(
|
||||||
|
|||||||
@@ -353,7 +353,7 @@ export async function createSiteResource(
|
|||||||
const resolved = await resolveProviderAttachments({
|
const resolved = await resolveProviderAttachments({
|
||||||
orgId,
|
orgId,
|
||||||
attachments: aiProviderInputs ?? [],
|
attachments: aiProviderInputs ?? [],
|
||||||
requireAtLeastOne: true
|
requireAtLeastOne: false
|
||||||
});
|
});
|
||||||
if (isInferenceFieldsError(resolved)) {
|
if (isInferenceFieldsError(resolved)) {
|
||||||
return next(
|
return next(
|
||||||
|
|||||||
@@ -63,8 +63,9 @@ export async function removeAiProviderFromSiteResource(
|
|||||||
next: NextFunction
|
next: NextFunction
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
try {
|
try {
|
||||||
const parsedBody =
|
const parsedBody = removeAiProviderFromSiteResourceBodySchema.safeParse(
|
||||||
removeAiProviderFromSiteResourceBodySchema.safeParse(req.body);
|
req.body
|
||||||
|
);
|
||||||
if (!parsedBody.success) {
|
if (!parsedBody.success) {
|
||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
@@ -128,22 +129,15 @@ export async function removeAiProviderFromSiteResource(
|
|||||||
modelAccessMode: a.modelAccessMode
|
modelAccessMode: a.modelAccessMode
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (remaining.length === 0) {
|
|
||||||
return next(
|
|
||||||
createHttpError(
|
|
||||||
HttpCode.BAD_REQUEST,
|
|
||||||
"At least one AI provider is required for inference-mode resources"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const attachments = await resolveProviderAttachments({
|
const attachments = await resolveProviderAttachments({
|
||||||
orgId: siteResource.orgId,
|
orgId: siteResource.orgId,
|
||||||
attachments: remaining,
|
attachments: remaining,
|
||||||
requireAtLeastOne: true
|
requireAtLeastOne: false
|
||||||
});
|
});
|
||||||
if (isInferenceFieldsError(attachments)) {
|
if (isInferenceFieldsError(attachments)) {
|
||||||
return next(createHttpError(HttpCode.BAD_REQUEST, attachments.error));
|
return next(
|
||||||
|
createHttpError(HttpCode.BAD_REQUEST, attachments.error)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await setSiteResourceAiProviders(siteResourceId, attachments);
|
await setSiteResourceAiProviders(siteResourceId, attachments);
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ registry.registerPath({
|
|||||||
method: "post",
|
method: "post",
|
||||||
path: "/site-resource/{siteResourceId}/ai-providers",
|
path: "/site-resource/{siteResourceId}/ai-providers",
|
||||||
description:
|
description:
|
||||||
"Replace the AI providers attached to an inference site resource. At least one provider is required. Model keys must be unique across attached catalog providers.",
|
"Replace the AI providers attached to an inference site resource. An empty list clears all providers. Model keys must be unique across attached catalog providers.",
|
||||||
tags: [OpenAPITags.PrivateResource],
|
tags: [OpenAPITags.PrivateResource],
|
||||||
request: {
|
request: {
|
||||||
params: setSiteResourceAiProvidersParamsSchema,
|
params: setSiteResourceAiProvidersParamsSchema,
|
||||||
@@ -115,7 +115,7 @@ export async function setSiteResourceAiProviders(
|
|||||||
const attachments = await resolveProviderAttachments({
|
const attachments = await resolveProviderAttachments({
|
||||||
orgId: siteResource.orgId,
|
orgId: siteResource.orgId,
|
||||||
attachments: providers,
|
attachments: providers,
|
||||||
requireAtLeastOne: true
|
requireAtLeastOne: false
|
||||||
});
|
});
|
||||||
if (isInferenceFieldsError(attachments)) {
|
if (isInferenceFieldsError(attachments)) {
|
||||||
return next(
|
return next(
|
||||||
|
|||||||
@@ -58,11 +58,9 @@ export default function PrivateResourceProvidersPage() {
|
|||||||
const formSchema = useMemo(
|
const formSchema = useMemo(
|
||||||
() =>
|
() =>
|
||||||
z.object({
|
z.object({
|
||||||
providerIds: z
|
providerIds: z.array(z.number().int().positive())
|
||||||
.array(z.number().int().positive())
|
|
||||||
.min(1, t("aiResourceProvidersRequired"))
|
|
||||||
}),
|
}),
|
||||||
[t]
|
[]
|
||||||
);
|
);
|
||||||
type FormValues = z.infer<typeof formSchema>;
|
type FormValues = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
|
|||||||
@@ -58,11 +58,9 @@ export default function PublicResourceProvidersPage() {
|
|||||||
const formSchema = useMemo(
|
const formSchema = useMemo(
|
||||||
() =>
|
() =>
|
||||||
z.object({
|
z.object({
|
||||||
providerIds: z
|
providerIds: z.array(z.number().int().positive())
|
||||||
.array(z.number().int().positive())
|
|
||||||
.min(1, t("aiResourceProvidersRequired"))
|
|
||||||
}),
|
}),
|
||||||
[t]
|
[]
|
||||||
);
|
);
|
||||||
type FormValues = z.infer<typeof formSchema>;
|
type FormValues = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
|
|||||||
@@ -252,7 +252,6 @@ export default function Page() {
|
|||||||
const [selectedProviders, setSelectedProviders] = useState<
|
const [selectedProviders, setSelectedProviders] = useState<
|
||||||
SelectedAiProvider[]
|
SelectedAiProvider[]
|
||||||
>([]);
|
>([]);
|
||||||
const [showProvidersError, setShowProvidersError] = useState(false);
|
|
||||||
|
|
||||||
// SSH-specific state
|
// SSH-specific state
|
||||||
const [sshServerMode, setSshServerMode] = useState<"standard" | "native">(
|
const [sshServerMode, setSshServerMode] = useState<"standard" | "native">(
|
||||||
@@ -355,9 +354,6 @@ export default function Page() {
|
|||||||
if (!availableTypes.includes(resourceType)) {
|
if (!availableTypes.includes(resourceType)) {
|
||||||
setResourceType("http");
|
setResourceType("http");
|
||||||
}
|
}
|
||||||
if (resourceType !== "inference") {
|
|
||||||
setShowProvidersError(false);
|
|
||||||
}
|
|
||||||
}, [availableTypes, resourceType]);
|
}, [availableTypes, resourceType]);
|
||||||
|
|
||||||
const baseResourceFormSchema = useMemo(
|
const baseResourceFormSchema = useMemo(
|
||||||
@@ -1440,14 +1436,6 @@ export default function Page() {
|
|||||||
setSelectedProviders(
|
setSelectedProviders(
|
||||||
providers
|
providers
|
||||||
);
|
);
|
||||||
if (
|
|
||||||
providers.length >
|
|
||||||
0
|
|
||||||
) {
|
|
||||||
setShowProvidersError(
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
@@ -1455,13 +1443,6 @@ export default function Page() {
|
|||||||
"aiResourceProvidersHelp"
|
"aiResourceProvidersHelp"
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
{showProvidersError && (
|
|
||||||
<p className="text-sm text-destructive">
|
|
||||||
{t(
|
|
||||||
"aiResourceProvidersRequired"
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</SettingsFormCell>
|
</SettingsFormCell>
|
||||||
</SettingsFormGrid>
|
</SettingsFormGrid>
|
||||||
@@ -1493,16 +1474,6 @@ export default function Page() {
|
|||||||
const tcpValid = !isHttpResource
|
const tcpValid = !isHttpResource
|
||||||
? await tcpUdpForm.trigger()
|
? await tcpUdpForm.trigger()
|
||||||
: true;
|
: true;
|
||||||
const providersValid =
|
|
||||||
resourceType !== "inference" ||
|
|
||||||
selectedProviders.length > 0;
|
|
||||||
|
|
||||||
if (
|
|
||||||
resourceType === "inference" &&
|
|
||||||
!providersValid
|
|
||||||
) {
|
|
||||||
setShowProvidersError(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
resourceType === "ssh" &&
|
resourceType === "ssh" &&
|
||||||
@@ -1527,8 +1498,7 @@ export default function Page() {
|
|||||||
baseValid &&
|
baseValid &&
|
||||||
domainValid &&
|
domainValid &&
|
||||||
tcpValid &&
|
tcpValid &&
|
||||||
bgValid &&
|
bgValid
|
||||||
providersValid
|
|
||||||
) {
|
) {
|
||||||
onSubmit();
|
onSubmit();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -467,13 +467,6 @@ export function createCreateFormSchema(t: TranslateFn) {
|
|||||||
path: ["alias"]
|
path: ["alias"]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (!data.providerIds || data.providerIds.length < 1) {
|
|
||||||
ctx.addIssue({
|
|
||||||
code: z.ZodIssueCode.custom,
|
|
||||||
message: t("aiResourceProvidersRequired"),
|
|
||||||
path: ["providerIds"]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
data.mode !== "ssh" &&
|
data.mode !== "ssh" &&
|
||||||
@@ -639,13 +632,6 @@ export function createInferenceFormSchema(t: TranslateFn) {
|
|||||||
path: ["alias"]
|
path: ["alias"]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (!data.providerIds || data.providerIds.length < 1) {
|
|
||||||
ctx.addIssue({
|
|
||||||
code: z.ZodIssueCode.custom,
|
|
||||||
message: t("aiResourceProvidersRequired"),
|
|
||||||
path: ["providerIds"]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user