Merge branch 'aig' of github.com:fosrl/pangolin into aig

This commit is contained in:
Owen
2026-08-14 14:21:04 -04:00
6 changed files with 81 additions and 2 deletions
+6 -1
View File
@@ -1678,6 +1678,9 @@
"commandVirtualApiKeys": "Virtual API Keys",
"virtualApiKeysTitle": "Manage Virtual API Keys",
"virtualApiKeysDescription": "Create and manage manual API keys for AI Gateway access to public AI gateways",
"virtualApiKeysBannerTitle": "Identity Keys for Every User",
"virtualApiKeysBannerDescription": "Every user already has an identity key available at {keysUrl}. You can also manually generate keys here that grant direct access to public AI gateways.",
"virtualApiKeysBannerButtonText": "View Identity Keys",
"virtualApiKeys": "Virtual API Keys",
"virtualApiKeysSearch": "Search keys...",
"virtualApiKeysCreate": "Create Virtual API Key",
@@ -1687,7 +1690,7 @@
"virtualApiKeysName": "Name",
"virtualApiKeysDescriptionOptional": "Description (optional)",
"virtualApiKeysAssociateUserOptional": "Associate User (optional)",
"virtualApiKeysAssociateUserDescription": "Associate this key with a user to track usage.",
"virtualApiKeysAssociateUserDescription": "Associate this key with a user to track usage. Once created, this key immediately grants access to the selected public AI gateways. You do not need to associate a user to manually to the resource. The key will also show up in the user's profile.",
"virtualApiKeysAllResources": "All public AI gateways",
"virtualApiKeysAllResourcesDescription": "Allow this key to access every public AI gateway in the organization",
"virtualApiKeysSelectResources": "Public AI Gateways",
@@ -1742,6 +1745,8 @@
"myVirtualApiKeysViewSecretDescription": "This secret authenticates you to AI Gateway resources",
"aiProvidersTitle": "AI Providers",
"aiProvidersDescription": "Connect model providers for AI workloads in this organization",
"aiProvidersBannerTitle": "Connect Model Providers",
"aiProvidersBannerDescription": "Providers are the model backends Pangolin uses for AI workloads. Connect OpenAI, Anthropic, and other providers here, then attach them to public AI gateways so users can call models with identity-aware access, budgets, and logging.",
"aiProvidersAdd": "Add Provider",
"aiProvidersSearch": "Search providers...",
"aiProvidersEmpty": "No AI providers yet",
@@ -1,3 +1,4 @@
import AiProvidersBanner from "@app/components/AiProvidersBanner";
import AiProvidersTable from "@app/components/AiProvidersTable";
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
import { internal } from "@app/lib/api";
@@ -49,6 +50,8 @@ export default async function AiProvidersPage({ params, searchParams }: Props) {
description={t("aiProvidersDescription")}
/>
<AiProvidersBanner />
<AiProvidersTable
orgId={orgId}
providers={providers.map((provider) => ({
@@ -6,6 +6,7 @@ import { redirect } from "next/navigation";
import { cache } from "react";
import { GetOrgResponse } from "@server/routers/org";
import OrgProvider from "@app/providers/OrgProvider";
import VirtualApiKeysBanner from "@app/components/VirtualApiKeysBanner";
import VirtualApiKeysTable, {
type VirtualApiKeyRow
} from "@app/components/VirtualApiKeysTable";
@@ -142,6 +143,8 @@ export default async function VirtualApiKeysPage(
description={t("virtualApiKeysDescription")}
/>
<VirtualApiKeysBanner orgId={params.orgId} />
<OrgProvider org={org}>
<VirtualApiKeysTable
virtualApiKeys={rows}
+3 -1
View File
@@ -20,6 +20,8 @@ import {
LayoutGrid,
Link as LinkIcon,
Logs,
MessageSquare,
MessagesSquare,
MonitorUp,
Plug,
ReceiptText,
@@ -233,7 +235,7 @@ export const orgNavSections = (
{
title: "sidebarLogsAi",
href: "/{orgId}/settings/logs/ai",
icon: <Bot className="size-4 flex-none" />
icon: <MessagesSquare className="size-4 flex-none" />
},
{
title: "sidebarLogsAiUsage",
+21
View File
@@ -0,0 +1,21 @@
"use client";
import { Sparkles } from "lucide-react";
import { useTranslations } from "next-intl";
import DismissableBanner from "./DismissableBanner";
export const AiProvidersBanner = () => {
const t = useTranslations();
return (
<DismissableBanner
storageKey="ai-providers-banner-dismissed"
version={1}
title={t("aiProvidersBannerTitle")}
titleIcon={<Sparkles className="w-5 h-5 text-primary" />}
description={t("aiProvidersBannerDescription")}
/>
);
};
export default AiProvidersBanner;
+45
View File
@@ -0,0 +1,45 @@
"use client";
import { Button } from "@app/components/ui/button";
import { useEnvContext } from "@app/hooks/useEnvContext";
import { ArrowRight, KeyRound } from "lucide-react";
import { useTranslations } from "next-intl";
import Link from "next/link";
import DismissableBanner from "./DismissableBanner";
type VirtualApiKeysBannerProps = {
orgId: string;
};
export const VirtualApiKeysBanner = ({ orgId }: VirtualApiKeysBannerProps) => {
const t = useTranslations();
const { env } = useEnvContext();
const dashboardUrl = env.app.dashboardUrl?.replace(/\/$/, "") ?? "";
const keysUrl = dashboardUrl
? `${dashboardUrl}/${orgId}/keys`
: `/${orgId}/keys`;
return (
<DismissableBanner
storageKey="virtual-api-keys-banner-dismissed"
version={1}
title={t("virtualApiKeysBannerTitle")}
titleIcon={<KeyRound className="w-5 h-5 text-primary" />}
description={t("virtualApiKeysBannerDescription", { keysUrl })}
>
<Link href={`/${orgId}/keys`}>
<Button
variant="outline"
size="sm"
className="gap-2 hover:bg-primary/10 hover:border-primary/50 transition-colors"
>
{t("virtualApiKeysBannerButtonText")}
<ArrowRight className="w-4 h-4" />
</Button>
</Link>
</DismissableBanner>
);
};
export default VirtualApiKeysBanner;