diff --git a/messages/en-US.json b/messages/en-US.json
index 5b9eb0ce5..646a82664 100644
--- a/messages/en-US.json
+++ b/messages/en-US.json
@@ -1817,6 +1817,8 @@
"aiResourceAliasRequired": "Alias is required for inference resources",
"aiResourceDomainConfiguration": "Domain configuration",
"aiResourceDomainConfigurationDescription": "Choose the domain clients will use to reach this inference resource.",
+ "resourceBudgetSettings": "Budget",
+ "resourceBudgetSettingsDescription": "Configure how this inference resource restricts usage based on spending or token limits",
"sidebarApiKeys": "API Keys",
"sidebarProvisioning": "Provisioning",
"sidebarSettings": "Settings",
diff --git a/src/app/[orgId]/settings/resources/private/[niceId]/budget/page.tsx b/src/app/[orgId]/settings/resources/private/[niceId]/budget/page.tsx
new file mode 100644
index 000000000..9268d664e
--- /dev/null
+++ b/src/app/[orgId]/settings/resources/private/[niceId]/budget/page.tsx
@@ -0,0 +1,42 @@
+"use client";
+
+import { SettingsContainer } from "@app/components/Settings";
+import { BudgetsEditor } from "@app/components/BudgetsEditor";
+import { useSiteResourceContext } from "@app/hooks/useSiteResourceContext";
+import { useTranslations } from "next-intl";
+import { useRouter } from "next/navigation";
+import { useEffect } from "react";
+
+export default function PrivateResourceBudgetPage() {
+ const { siteResource } = useSiteResourceContext();
+ const router = useRouter();
+ const t = useTranslations();
+
+ useEffect(() => {
+ if (siteResource.mode !== "inference") {
+ router.replace(
+ `/${siteResource.orgId}/settings/resources/private/${siteResource.niceId}/general`
+ );
+ }
+ }, [
+ router,
+ siteResource.mode,
+ siteResource.niceId,
+ siteResource.orgId
+ ]);
+
+ if (siteResource.mode !== "inference") {
+ return null;
+ }
+
+ return (
+
+
+
+ );
+}
diff --git a/src/app/[orgId]/settings/resources/private/[niceId]/layout.tsx b/src/app/[orgId]/settings/resources/private/[niceId]/layout.tsx
index 294e8f81a..e4bb32ef7 100644
--- a/src/app/[orgId]/settings/resources/private/[niceId]/layout.tsx
+++ b/src/app/[orgId]/settings/resources/private/[niceId]/layout.tsx
@@ -70,6 +70,13 @@ export default async function PrivateResourceLayout(
}
];
+ if (siteResource.mode === "inference") {
+ navItems.push({
+ title: t("resourceBudgetSettings"),
+ href: `/{orgId}/settings/resources/private/{niceId}/budget`
+ });
+ }
+
return (
<>
{
+ if (resource.mode !== "inference") {
+ router.replace(
+ `/${resource.orgId}/settings/resources/public/${resource.niceId}/general`
+ );
+ }
+ }, [router, resource.mode, resource.niceId, resource.orgId]);
+
+ if (resource.mode !== "inference") {
+ return null;
+ }
+
+ return (
+
+
+
+ );
+}
diff --git a/src/app/[orgId]/settings/resources/public/[niceId]/layout.tsx b/src/app/[orgId]/settings/resources/public/[niceId]/layout.tsx
index 43a3a62c0..ed69a22b0 100644
--- a/src/app/[orgId]/settings/resources/public/[niceId]/layout.tsx
+++ b/src/app/[orgId]/settings/resources/public/[niceId]/layout.tsx
@@ -113,6 +113,13 @@ export default async function ResourceLayout(props: ResourceLayoutProps) {
}
}
+ if (resource.mode === "inference") {
+ navItems.push({
+ title: t("resourceBudgetSettings"),
+ href: `/{orgId}/settings/resources/public/{niceId}/budget`
+ });
+ }
+
return (
<>