first commit

This commit is contained in:
Beyhan Oğur
2026-04-26 21:52:23 +03:00
commit 880f412e2c
2662 changed files with 866266 additions and 0 deletions

View File

@@ -0,0 +1,300 @@
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "@/components/ui/alertDialog";
import { Button } from "@/components/ui/button";
import { CardHeader, CardTitle } from "@/components/ui/card";
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdownMenu";
import { Switch } from "@/components/ui/switch";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { getErrorMessage } from "@/lib/store";
import { useDeleteProviderKeyMutation, useGetProviderKeysQuery, useUpdateProviderKeyMutation } from "@/lib/store/apis/providersApi";
import { ModelProvider } from "@/lib/types/config";
import { cn } from "@/lib/utils";
import { RbacOperation, RbacResource, useRbac } from "@enterprise/lib";
import { AlertCircle, CheckCircle2, EllipsisIcon, PencilIcon, PlusIcon, TrashIcon } from "lucide-react";
import { ReactNode, useState } from "react";
import { toast } from "sonner";
import AddNewKeySheet from "../dialogs/addNewKeySheet";
interface Props {
className?: string;
provider: ModelProvider;
headerActions?: ReactNode;
isKeyless?: boolean;
}
export default function ModelProviderKeysTableView({ provider, className, headerActions, isKeyless }: Props) {
const providerName = provider.name?.toLowerCase() ?? "";
const isVLLM = providerName === "vllm";
const isOllamaOrSGL = providerName === "ollama" || providerName === "sgl";
const entityLabel = isVLLM ? "model" : isOllamaOrSGL ? "server" : "key";
const entityLabelPlural = isVLLM ? "models" : isOllamaOrSGL ? "servers" : "keys";
const EntityLabel = entityLabel.charAt(0).toUpperCase() + entityLabel.slice(1);
const hasUpdateProviderAccess = useRbac(RbacResource.ModelProvider, RbacOperation.Update);
const hasDeleteProviderAccess = useRbac(RbacResource.ModelProvider, RbacOperation.Delete);
const [updateProviderKey, { isLoading: isUpdatingProviderKey }] = useUpdateProviderKeyMutation();
const [deleteProviderKey, { isLoading: isDeletingProviderKey }] = useDeleteProviderKeyMutation();
const { data: keys = [] } = useGetProviderKeysQuery(provider.name);
const isMutatingProviderKey = isUpdatingProviderKey || isDeletingProviderKey;
const [togglingKeyIds, setTogglingKeyIds] = useState<Set<string>>(new Set());
const [showAddNewKeyDialog, setShowAddNewKeyDialog] = useState<{ show: boolean; keyId: string | null } | undefined>(undefined);
const [showDeleteKeyDialog, setShowDeleteKeyDialog] = useState<{ show: boolean; keyId: string } | undefined>(undefined);
function handleAddKey() {
setShowAddNewKeyDialog({ show: true, keyId: null });
}
return (
<div className={cn("w-full", className)}>
{showDeleteKeyDialog && (
<AlertDialog open={showDeleteKeyDialog.show}>
<AlertDialogContent onClick={(e) => e.stopPropagation()}>
<AlertDialogHeader>
<AlertDialogTitle>Delete {EntityLabel}</AlertDialogTitle>
<AlertDialogDescription>
Are you sure you want to delete this {entityLabel}. This action cannot be undone.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter className="pt-4">
<AlertDialogCancel onClick={() => setShowDeleteKeyDialog(undefined)} disabled={isMutatingProviderKey}>
Cancel
</AlertDialogCancel>
<AlertDialogAction
disabled={isMutatingProviderKey || !hasDeleteProviderAccess}
onClick={() => {
deleteProviderKey({
provider: provider.name,
keyId: showDeleteKeyDialog.keyId,
})
.unwrap()
.then(() => {
toast.success(`${EntityLabel} deleted successfully`);
setShowDeleteKeyDialog(undefined);
})
.catch((err) => {
toast.error(`Failed to delete ${entityLabel}`, {
description: getErrorMessage(err),
});
});
}}
>
Delete
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)}
{showAddNewKeyDialog && (
<AddNewKeySheet
show={showAddNewKeyDialog.show}
onCancel={() => setShowAddNewKeyDialog(undefined)}
provider={provider}
keyId={showAddNewKeyDialog.keyId}
providerName={providerName}
/>
)}
<CardHeader className="mb-4 px-0">
<CardTitle className="flex items-center justify-between">
<div className="flex items-center gap-2">Configured {entityLabelPlural}</div>
<div className="flex items-center gap-2">
{headerActions}
{!isKeyless && (
<Button
disabled={!hasUpdateProviderAccess}
data-testid="add-key-btn"
onClick={() => {
handleAddKey();
}}
>
<PlusIcon className="h-4 w-4" />
Add new {entityLabel}
</Button>
)}
</div>
</CardTitle>
</CardHeader>
{isKeyless ? (
<div className="text-muted-foreground flex flex-col items-center justify-center gap-2 rounded-sm border py-10 text-center text-sm">
<p>This is a keyless provider - no API keys are required.</p>
<p>You can edit the provider configuration using the button above.</p>
</div>
) : (
<div className="flex w-full flex-col gap-2 rounded-sm border">
<Table className="w-full" data-testid="keys-table">
<TableHeader className="w-full">
<TableRow>
<TableHead>{isVLLM ? "Model" : isOllamaOrSGL ? "Server" : "API Key"}</TableHead>
<TableHead>Weight</TableHead>
<TableHead>Enabled</TableHead>
<TableHead className="text-right"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{keys.length === 0 && (
<TableRow data-testid="keys-table-empty-state">
<TableCell colSpan={4} className="py-6 text-center">
No {entityLabelPlural} found.
</TableCell>
</TableRow>
)}
{keys.map((key) => {
const isKeyEnabled = key.enabled ?? true;
return (
<TableRow
key={key.id}
data-testid={`key-row-${key.name}`}
className="text-sm transition-colors hover:bg-white"
onClick={() => {}}
>
<TableCell>
<div className="flex items-center space-x-2">
{key.status === "success" && (
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
aria-label="Key status: list models working"
data-testid={`key-status-success-${key.name}`}
className="inline-flex"
>
<CheckCircle2 aria-hidden className="h-4 w-4 flex-shrink-0 text-green-600" />
</button>
</TooltipTrigger>
<TooltipContent>List models working</TooltipContent>
</Tooltip>
)}
{key.status === "list_models_failed" &&
(() => {
// Check if the failure might be due to an env var that the server couldn't resolve
const hasEnvVarConfig =
key.azure_key_config?.endpoint?.from_env ||
key.vertex_key_config?.project_id?.from_env ||
key.vertex_key_config?.region?.from_env ||
key.bedrock_key_config?.region?.from_env ||
key.vllm_key_config?.url?.from_env ||
key.value?.from_env;
const isEnvResolutionError =
hasEnvVarConfig && key.description && /not set|empty|missing/i.test(key.description);
return isEnvResolutionError ? (
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
aria-label="Key status: env var may not be resolved"
data-testid={`key-status-warning-${key.name}`}
className="inline-flex"
>
<AlertCircle aria-hidden className="h-4 w-4 flex-shrink-0 text-orange-500" />
</button>
</TooltipTrigger>
<TooltipContent className="max-w-xs break-words">
{key.description} verify the environment variable is set on the server
</TooltipContent>
</Tooltip>
) : (
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
aria-label="Key status: list models failed"
data-testid={`key-status-error-${key.name}`}
className="inline-flex"
>
<AlertCircle aria-hidden className="text-destructive h-4 w-4 flex-shrink-0" />
</button>
</TooltipTrigger>
<TooltipContent className="max-w-xs break-words">
{key.description || "Model discovery failed for this key"}
</TooltipContent>
</Tooltip>
);
})()}
<span className="font-mono text-sm">{key.name}</span>
</div>
</TableCell>
<TableCell data-testid="key-weight-value">
<div className="flex items-center space-x-2">
<span className="font-mono text-sm">{key.weight}</span>
</div>
</TableCell>
<TableCell>
<Switch
data-testid="key-enabled-switch"
checked={isKeyEnabled}
size="md"
disabled={!hasUpdateProviderAccess || togglingKeyIds.has(key.id)}
onAsyncCheckedChange={async (checked) => {
setTogglingKeyIds((prev) => new Set(prev).add(key.id));
await updateProviderKey({
provider: provider.name,
keyId: key.id,
key: { ...key, enabled: checked },
})
.unwrap()
.then(() => {
toast.success(`${EntityLabel} ${checked ? "enabled" : "disabled"} successfully`);
})
.catch((err) => {
toast.error(`Failed to update ${entityLabel}`, { description: getErrorMessage(err) });
})
.finally(() => {
setTogglingKeyIds((prev) => {
const next = new Set(prev);
next.delete(key.id);
return next;
});
});
}}
/>
</TableCell>
<TableCell className="text-right">
<div className="flex items-center justify-end space-x-2">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button onClick={(e) => e.stopPropagation()} variant="ghost">
<EllipsisIcon className="h-5 w-5" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem
onClick={() => {
setShowAddNewKeyDialog({ show: true, keyId: key.id });
}}
disabled={!hasUpdateProviderAccess}
>
<PencilIcon className="mr-1 h-4 w-4" />
Edit
</DropdownMenuItem>
<DropdownMenuItem
variant="destructive"
onClick={() => {
setShowDeleteKeyDialog({ show: true, keyId: key.id });
}}
disabled={!hasDeleteProviderAccess}
>
<TrashIcon className="mr-1 h-4 w-4" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div>
)}
</div>
);
}