refactor(ui): remove dead local variables and gate the rule in eslint

@typescript-eslint/no-unused-vars was disabled in the dashboard's eslint
config, so only unused imports were ever caught and dead locals accumulated
unchecked. CodeQL's js/unused-local-variable was the only thing seeing them,
where 168 findings buried the genuinely high-severity alerts in the nightly
scan.

Removes every unused local in ui/litellm-dashboard/src and turns the rule on
so the cleanup holds. Declarations whose initializer carries a side effect
(await, vi.spyOn, renderHook) keep the call and drop only the binding;
positional array slots are elided rather than shifted. The two
declaration-merged ColumnMeta interfaces keep their type parameters behind a
scoped suppression, since TS2428 requires them to match upstream exactly.
This commit is contained in:
Yuneng Jiang 2026-08-04 13:28:19 -07:00
parent 487074f602
commit f3ba562b20
No known key found for this signature in database
87 changed files with 101 additions and 751 deletions

View file

@ -42,9 +42,6 @@
},
"react-hooks/set-state-in-effect": {
"count": 2
},
"unused-imports/no-unused-imports": {
"count": 1
}
},
"src/app/(dashboard)/agents/_components/agent_card_discovery.tsx": {
@ -255,11 +252,6 @@
"count": 1
}
},
"src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_cost_results.test.tsx": {
"unused-imports/no-unused-imports": {
"count": 1
}
},
"src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_cost_results.tsx": {
"local/filename-pascal-case": {
"count": 1
@ -268,11 +260,6 @@
"count": 2
}
},
"src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_export_dropdown.test.tsx": {
"unused-imports/no-unused-imports": {
"count": 1
}
},
"src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_export_dropdown.tsx": {
"local/filename-pascal-case": {
"count": 1
@ -286,11 +273,6 @@
"count": 1
}
},
"src/app/(dashboard)/cost-tracking/_components/provider_discount_table.test.tsx": {
"unused-imports/no-unused-imports": {
"count": 1
}
},
"src/app/(dashboard)/cost-tracking/_components/provider_discount_table.tsx": {
"local/filename-pascal-case": {
"count": 1
@ -366,7 +348,7 @@
},
"src/app/(dashboard)/guardrails/_components/GuardrailTestResults.tsx": {
"no-restricted-imports": {
"count": 2
"count": 1
}
},
"src/app/(dashboard)/guardrails/_components/GuardrailsPanel.tsx": {
@ -813,11 +795,6 @@
"count": 1
}
},
"src/app/(dashboard)/mcp-servers/_components/MCPLogoSelector.test.tsx": {
"unused-imports/no-unused-imports": {
"count": 1
}
},
"src/app/(dashboard)/mcp-servers/_components/MCPNetworkSettings.tsx": {
"react-hooks/immutability": {
"count": 2
@ -1146,9 +1123,6 @@
},
"react-hooks/set-state-in-effect": {
"count": 4
},
"unused-imports/no-unused-imports": {
"count": 13
}
},
"src/app/(dashboard)/playground/components/chat_ui/CodeInterpreterOutput.tsx": {
@ -2179,11 +2153,6 @@
"count": 1
}
},
"src/components/HelpLink.test.tsx": {
"unused-imports/no-unused-imports": {
"count": 1
}
},
"src/components/LicenseExpiryBanner.tsx": {
"no-restricted-imports": {
"count": 1
@ -2448,11 +2417,6 @@
"count": 1
}
},
"src/components/ToolDetail.tsx": {
"unused-imports/no-unused-imports": {
"count": 1
}
},
"src/components/UIAccessControlForm.tsx": {
"no-restricted-imports": {
"count": 2
@ -2678,9 +2642,6 @@
"src/components/agent_management/AgentSelector.test.tsx": {
"react/display-name": {
"count": 1
},
"unused-imports/no-unused-imports": {
"count": 1
}
},
"src/components/agent_management/AgentSelector.tsx": {
@ -3272,7 +3233,7 @@
"count": 2
},
"prefer-const": {
"count": 5
"count": 4
},
"react-hooks/set-state-in-effect": {
"count": 1
@ -3307,9 +3268,6 @@
"src/components/navbar.test.tsx": {
"prefer-const": {
"count": 1
},
"unused-imports/no-unused-imports": {
"count": 1
}
},
"src/components/navbar.tsx": {
@ -3383,7 +3341,7 @@
"count": 2
},
"prefer-const": {
"count": 4
"count": 2
},
"react-hooks/set-state-in-effect": {
"count": 4
@ -3570,7 +3528,7 @@
"count": 3
},
"prefer-const": {
"count": 7
"count": 4
}
},
"src/components/shared/CreatedKeyDisplay.tsx": {
@ -3793,11 +3751,6 @@
"count": 2
}
},
"src/components/templates/key_info_view.test.tsx": {
"unused-imports/no-unused-imports": {
"count": 2
}
},
"src/components/templates/key_info_view.tsx": {
"local/filename-pascal-case": {
"count": 1
@ -4117,11 +4070,6 @@
"count": 1
}
},
"src/components/view_logs/LogDetailsDrawer/RealtimePrettyView.test.tsx": {
"unused-imports/no-unused-imports": {
"count": 2
}
},
"src/components/view_logs/LogDetailsDrawer/RealtimePrettyView.tsx": {
"no-restricted-imports": {
"count": 1

View file

@ -22,7 +22,10 @@ const eslintConfig = [
"local/no-complex-jsx-arrow": ["error", { maxStatements: 2 }],
"@typescript-eslint/no-explicit-any": "warn",
"no-console": ["warn", { allow: ["warn", "error"] }],
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{ args: "none", caughtErrors: "none", ignoreRestSiblings: true, varsIgnorePattern: "^_" },
],
"@typescript-eslint/no-unused-expressions": "off",
"@typescript-eslint/ban-ts-comment": "off",
"prefer-const": "error",

View file

@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react";
import { Modal, Form, Select, Input, Steps, Radio, Tag, Divider, Switch, InputNumber, Collapse } from "antd";
import { Modal, Form, Select, Input, Steps, Radio, Tag, Divider, Switch, InputNumber } from "antd";
import MessageManager from "@/components/molecules/message_manager";
import { Logo } from "@/components/molecules/logo/Logo";
import { Button } from "@tremor/react";
@ -47,7 +47,7 @@ const AddAgentForm: React.FC<AddAgentFormProps> = ({ visible, onClose, accessTok
const [isSubmitting, setIsSubmitting] = useState(false);
const [agentType, setAgentType] = useState<string>("a2a");
const [agentTypeMetadata, setAgentTypeMetadata] = useState<AgentCreateInfo[]>([]);
const [loadingMetadata, setLoadingMetadata] = useState(false);
const [, setLoadingMetadata] = useState(false);
// Step 3: key assignment state
const [keyAssignOption, setKeyAssignOption] = useState<"create_new" | "existing_key" | "skip">("create_new");

View file

@ -65,32 +65,7 @@ interface CachePageProps {
premiumUser: boolean;
}
interface CacheHealthResponse {
status?: string;
cache_type?: string;
ping_response?: boolean;
set_cache_response?: string;
litellm_cache_params?: string;
error?: {
message: string;
type: string;
param: string;
code: string;
};
}
// Helper function to deep-parse a JSON string if possible
const deepParse = (input: any) => {
let parsed = input;
if (typeof parsed === "string") {
try {
parsed = JSON.parse(parsed);
} catch {
return parsed;
}
}
return parsed;
};
const CacheDashboard: React.FC<CachePageProps> = ({ accessToken, token, userRole, userID, premiumUser }) => {
const [selectedApiKeys, setSelectedApiKeys] = useState<string[]>([]);

View file

@ -20,7 +20,7 @@ const deepParse = (input: any) => {
// TableClickableErrorField component with copy-to-clipboard functionality
const TableClickableErrorField: React.FC<{ label: string; value: string | null | undefined }> = ({ label, value }) => {
const [isExpanded, setIsExpanded] = React.useState(false);
const [copied, setCopied] = React.useState(false);
const [, setCopied] = React.useState(false);
const safeValue = value?.toString() || "N/A";
const truncated = safeValue.length > 50 ? safeValue.substring(0, 50) + "..." : safeValue;

View file

@ -1,6 +1,6 @@
import React from "react";
import { describe, it, expect, vi, beforeEach } from "vitest";
import { screen, within } from "@testing-library/react";
import { screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { renderWithProviders } from "../../../../../../tests/test-utils";
import MultiCostResults from "./multi_cost_results";

View file

@ -1,5 +1,5 @@
import React from "react";
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { describe, it, expect, vi, beforeEach } from "vitest";
import { screen, fireEvent } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { renderWithProviders } from "../../../../../../tests/test-utils";

View file

@ -1,6 +1,6 @@
import React from "react";
import { describe, it, expect, vi, beforeEach } from "vitest";
import { screen, within } from "@testing-library/react";
import { screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { renderWithProviders } from "../../../../../tests/test-utils";
import ProviderDiscountTable from "./provider_discount_table";

View file

@ -69,14 +69,6 @@ const ProviderMarginTable: React.FC<ProviderMarginTableProps> = ({
setEditFixedAmount("");
};
const handleKeyDown = (e: React.KeyboardEvent, provider: string) => {
if (e.key === "Enter") {
handleSaveEdit(provider);
} else if (e.key === "Escape") {
handleCancelEdit();
}
};
const formatMargin = (margin: number | { percentage?: number; fixed_amount?: number }): string => {
if (typeof margin === "number") {
return `${(margin * 100).toFixed(1)}%`;

View file

@ -25,7 +25,7 @@ const statusColors: Record<string, { bg: string; text: string; dot: string }> =
export function GuardrailDetail({ guardrailId, onBack, accessToken = null, startDate, endDate }: GuardrailDetailProps) {
const [activeTab, setActiveTab] = useState("overview");
const [evaluationModalOpen, setEvaluationModalOpen] = useState(false);
const [logsPage, setLogsPage] = useState(1);
const [logsPage] = useState(1);
const logsPageSize = 50;
const {

View file

@ -1,11 +1,8 @@
import React, { useState } from "react";
import { Button, Card } from "@tremor/react";
import { Typography } from "antd";
import { CopyOutlined, CheckCircleOutlined, ClockCircleOutlined, DownOutlined, RightOutlined } from "@ant-design/icons";
import NotificationsManager from "@/components/molecules/notifications_manager";
const { Text } = Typography;
interface TestResult {
guardrailName: string;
response_text: string;

View file

@ -1,4 +1,4 @@
import { Form, Input, Modal, Select, Tag, Typography, Button } from "antd";
import { Form, Input, Modal, Select, Tag, Button } from "antd";
import React, { useEffect, useMemo, useState } from "react";
import NotificationsManager from "@/components/molecules/notifications_manager";
import {
@ -30,7 +30,6 @@ import LLMJudgeFields from "./llm_judge/LLMJudgeFields";
import PiiConfiguration from "./pii_configuration";
import ToolPermissionRulesEditor, { ToolPermissionConfig } from "./tool_permission/ToolPermissionRulesEditor";
const { Title, Text, Link } = Typography;
const { Option } = Select;
// Define human-friendly descriptions for each mode
@ -164,9 +163,9 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
const [providerParams, setProviderParams] = useState<ProviderParamsResponse | null>(null);
// Azure Text Moderation state
const [selectedCategories, setSelectedCategories] = useState<string[]>([]);
const [globalSeverityThreshold, setGlobalSeverityThreshold] = useState<number>(2);
const [categorySpecificThresholds, setCategorySpecificThresholds] = useState<{ [key: string]: number }>({});
const [, setSelectedCategories] = useState<string[]>([]);
const [, setGlobalSeverityThreshold] = useState<number>(2);
const [, setCategorySpecificThresholds] = useState<{ [key: string]: number }>({});
// Content Filter state
const [selectedPatterns, setSelectedPatterns] = useState<ContentFilterPattern[]>([]);
@ -336,22 +335,6 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
};
// Azure Text Moderation handlers
const handleCategorySelect = (category: string) => {
setSelectedCategories((prev) =>
prev.includes(category) ? prev.filter((c) => c !== category) : [...prev, category],
);
};
const handleGlobalSeverityChange = (threshold: number) => {
setGlobalSeverityThreshold(threshold);
};
const handleCategorySeverityChange = (category: string, threshold: number) => {
setCategorySpecificThresholds((prev) => ({
...prev,
[category]: threshold,
}));
};
const nextStep = async () => {
try {
@ -388,45 +371,6 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
setCurrentStep(currentStep - 1);
};
const handleAddAndContinue = (competitorIntentOnly?: boolean) => {
// Competitor intent only: just advance to next step (no category to add)
if (competitorIntentOnly) {
setCurrentStep(currentStep + 1);
return;
}
if (!pendingCategorySelection || !guardrailSettings) return;
const contentFilterSettings = guardrailSettings.content_filter_settings;
if (!contentFilterSettings) return;
const category = contentFilterSettings.content_categories?.find((c) => c.name === pendingCategorySelection);
if (!category) return;
// Check if already added
if (selectedContentCategories.some((c) => c.category === pendingCategorySelection)) {
setPendingCategorySelection("");
setCurrentStep(currentStep + 1);
return;
}
// Add the category
setSelectedContentCategories([
...selectedContentCategories,
{
id: `category-${Date.now()}`,
category: category.name,
display_name: category.display_name,
action: category.default_action as "BLOCK" | "MASK",
severity_threshold: "medium",
},
]);
// Clear pending selection and advance to next step
setPendingCategorySelection("");
setCurrentStep(currentStep + 1);
};
const resetForm = () => {
form.resetFields();
setSelectedProvider(null);
@ -965,48 +909,6 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
}
};
const renderStepButtons = () => {
const totalSteps = shouldRenderContentFilterConfigSettings(selectedProvider) ? 5 : 2;
const isLastStep = currentStep === totalSteps - 1;
const isCategoriesStep = shouldRenderContentFilterConfigSettings(selectedProvider) && currentStep === 1;
const hasPendingCategory = pendingCategorySelection !== "";
const hasCompetitorIntentConfigured =
competitorIntentEnabled && (competitorIntentConfig?.brand_self?.length ?? 0) > 0;
const canContinueFromCategoriesStep = hasPendingCategory || hasCompetitorIntentConfigured;
return (
<div className="flex justify-end space-x-2 mt-4">
{currentStep > 0 && <Button onClick={prevStep}>Previous</Button>}
{isCategoriesStep ? (
<>
<Button onClick={nextStep}>Skip</Button>
<Button
type="primary"
onClick={() => handleAddAndContinue(hasCompetitorIntentConfigured)}
disabled={!canContinueFromCategoriesStep}
>
{hasPendingCategory ? "Add & Continue →" : "Continue →"}
</Button>
</>
) : (
<>
{!isLastStep && (
<Button type="primary" onClick={nextStep}>
Next
</Button>
)}
{isLastStep && (
<Button type="primary" onClick={handleSubmit} loading={loading}>
Create Guardrail
</Button>
)}
</>
)}
<Button onClick={handleClose}>Cancel</Button>
</div>
);
};
const renderEndpointSettings = () => {
return (
<div className="space-y-6">

View file

@ -1,8 +1,7 @@
import { DeleteOutlined } from "@ant-design/icons";
import { Button, Select, Table, Typography } from "antd";
import { Button, Select, Table } from "antd";
import React from "react";
const { Text } = Typography;
const { Option } = Select;
interface BlockedWord {

View file

@ -229,7 +229,7 @@ describe("Guardrail Info", () => {
vi.mocked(networking.getGuardrailProviderSpecificParams).mockResolvedValue({});
vi.mocked(networking.updateGuardrailCall).mockResolvedValue({ status: "success" });
const { getByText, getByRole, getAllByRole, getByLabelText } = render(
const { getByText, getByLabelText } = render(
<GuardrailInfoView guardrailId="123" onClose={() => {}} accessToken="123" isAdmin={true} />,
);

View file

@ -35,22 +35,6 @@ export interface GuardrailInfoProps {
isAdmin: boolean;
}
interface ProviderParam {
param: string;
description: string;
required: boolean;
default_value?: string;
options?: string[];
type?: string;
fields?: { [key: string]: ProviderParam };
dict_key_options?: string[];
dict_value_type?: string;
}
interface ProviderParamsResponse {
[provider: string]: { [key: string]: ProviderParam };
}
const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose, accessToken, isAdmin }) => {
const [guardrailData, setGuardrailData] = useState<any>(null);
const [guardrailProviderSpecificParams, setGuardrailProviderSpecificParams] = useState<any>(null);
@ -244,11 +228,6 @@ const GuardrailInfoView: React.FC<GuardrailInfoProps> = ({ guardrailId, onClose,
resetToolPermissionEditor();
}, [resetToolPermissionEditor]);
const handleToolPermissionConfigChange = (config: ToolPermissionConfig) => {
setToolPermissionConfig(config);
setToolPermissionDirty(true);
};
const handlePiiEntitySelect = (entity: string) => {
setSelectedPiiEntities((prev) => {
if (prev.includes(entity)) {

View file

@ -437,7 +437,7 @@ describe("useTeam", () => {
});
// Import useQueryClient to get access to query client
const { useQueryClient } = await import("@tanstack/react-query");
await import("@tanstack/react-query");
// Manually test the queryFn logic by calling it directly
// This simulates what would happen if enabled check was bypassed

View file

@ -265,7 +265,7 @@ describe("useAuthorized", () => {
const token = createJwt(decodedPayload);
document.cookie = `token=${token}; path=/;`;
const { result } = renderHook(() => useAuthorized(), { wrapper });
renderHook(() => useAuthorized(), { wrapper });
await waitFor(() => {
expect(clearTokenCookiesMock).toHaveBeenCalled();

View file

@ -333,7 +333,6 @@ const CreateMCPServer: React.FC<CreateMCPServerProps> = ({
if (!pendingRestoredValues) {
return;
}
const transportReady = transportType || pendingRestoredValues.transport || "";
if (pendingRestoredValues.transport && !transportType) {
// wait until transportType state catches up so the URL field is mounted
return;

View file

@ -1,5 +1,5 @@
import React from "react";
import { render, screen, fireEvent } from "@testing-library/react";
import { render, screen } from "@testing-library/react";
import { describe, it, expect, vi, beforeEach } from "vitest";
import userEvent from "@testing-library/user-event";
import MCPLogoSelector from "./MCPLogoSelector";

View file

@ -1,14 +1,13 @@
/* eslint-disable react/no-unescaped-entities */
import React, { useState } from "react";
import { Card, Typography, Space, Alert, Button, Switch, Form, Collapse } from "antd";
import { Card, Typography, Space, Alert, Button, Switch, Form } from "antd";
import { TabPanel, TabPanels, TabGroup, TabList, Tab, Title as TremorTitle, Text as TremorText } from "@tremor/react";
import { CopyIcon, Code, Terminal, Globe, CheckIcon, ExternalLinkIcon, KeyIcon, ServerIcon, Zap } from "lucide-react";
import { getProxyBaseUrl } from "@/components/networking";
import { copyToClipboard as utilCopyToClipboard } from "@/utils/dataUtils";
const { Title, Text } = Typography;
const { Panel } = Collapse;
interface CodeBlockProps {
code: string;
@ -117,12 +116,6 @@ interface MCPConnectProps {
const MCPConnect: React.FC<MCPConnectProps> = ({ currentServerAccessGroups = [] }) => {
const proxyBaseUrl = getProxyBaseUrl();
const [copiedStates, setCopiedStates] = useState<Record<string, boolean>>({});
const [serverHeaders, setServerHeaders] = useState<Record<string, string[]>>({
openai: [],
litellm: [],
cursor: [],
http: [],
});
const [currentServer] = useState("Zapier_MCP"); // This should match the current server being viewed
const copyToClipboard = async (text: string, key: string) => {
@ -135,22 +128,6 @@ const MCPConnect: React.FC<MCPConnectProps> = ({ currentServerAccessGroups = []
}
};
const getHeadersConfig = (type: string) => {
const headers: Record<string, any> = {
"x-litellm-api-key": "Bearer YOUR_LITELLM_API_KEY",
};
if (serverHeaders[type]?.length > 0) {
// Format server names (replace spaces with underscores)
const formattedServers = serverHeaders[type].map((s) => s.replace(/\s+/g, "_"));
// Use comma-separated string (can include both servers and access groups)
headers["x-mcp-servers"] = formattedServers.join(",");
}
return headers;
};
const CodeBlock: React.FC<{
code: string;
copyKey: string;

View file

@ -60,7 +60,7 @@ describe("ChatUI", () => {
});
it("should show the voice selector when the endpoint type is audio_speech", async () => {
const { getByText, container } = render(
const { getByText } = render(
<ChatUI
accessToken="1234567890"
token="1234567890"
@ -110,7 +110,7 @@ describe("ChatUI", () => {
});
it("should allow the user to select a model", async () => {
const { getByText, container } = render(
const { getByText } = render(
<ChatUI
accessToken="1234567890"
token="1234567890"
@ -148,7 +148,7 @@ describe("ChatUI", () => {
{ model_group: "ResponsesModel", mode: "responses" },
]);
const { getByText, baseElement } = render(
const { getByText } = render(
<ChatUI
accessToken="1234567890"
token="1234567890"

View file

@ -7,7 +7,6 @@ import {
CodeOutlined,
DatabaseOutlined,
DeleteOutlined,
FilePdfOutlined,
InfoCircleOutlined,
KeyOutlined,
LinkOutlined,
@ -19,12 +18,10 @@ import {
SoundOutlined,
TagsOutlined,
ToolOutlined,
UserOutlined,
} from "@ant-design/icons";
import { Card, Text, TextInput, Title, Button as TremorButton } from "@tremor/react";
import { Button, Input, Modal, Popover, Select, Spin, Tooltip, Upload } from "antd";
import React, { useEffect, useRef, useState } from "react";
import ReactMarkdown from "react-markdown";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { coy } from "react-syntax-highlighter/dist/esm/styles/prism";
import { v4 as uuidv4 } from "uuid";
@ -50,14 +47,10 @@ import { makeOpenAIImageEditsRequest } from "../../llm_calls/image_edits";
import { makeOpenAIImageGenerationRequest } from "../../llm_calls/image_generation";
import { makeOpenAIResponsesRequest } from "@/components/llm_calls/responses_api";
import { makeInteractionsRequest } from "../../llm_calls/interactions_api";
import A2AMetrics from "./A2AMetrics";
import AdditionalModelSettings from "./AdditionalModelSettings";
import AudioRenderer from "./AudioRenderer";
import { OPEN_AI_VOICE_SELECT_OPTIONS, OpenAIVoice } from "./chatConstants";
import ChatImageRenderer from "./ChatImageRenderer";
import ChatImageUpload from "./ChatImageUpload";
import { createChatDisplayMessage, createChatMultimodalMessage } from "./ChatImageUtils";
import CodeInterpreterOutput from "./CodeInterpreterOutput";
import CodeInterpreterTool from "./CodeInterpreterTool";
import { generateCodeSnippet } from "@/components/chat_ui/CodeSnippets";
import EndpointSelector from "./EndpointSelector";
@ -65,15 +58,11 @@ import FilePreviewCard from "./FilePreviewCard";
import ChatMessageBubble from "./ChatMessageBubble";
import MCPEventsDisplay from "@/components/chat_ui/MCPEventsDisplay";
import { EndpointType, getEndpointType } from "@/components/chat_ui/mode_endpoint_mapping";
import ReasoningContent from "@/components/chat_ui/ReasoningContent";
import ResponseMetrics, { TokenUsage } from "@/components/chat_ui/ResponseMetrics";
import ResponsesImageRenderer from "./ResponsesImageRenderer";
import ResponsesImageUpload from "./ResponsesImageUpload";
import { createDisplayMessage, createMultimodalMessage } from "./ResponsesImageUtils";
import { SearchResultsDisplay } from "./SearchResultsDisplay";
import SessionManagement from "./SessionManagement";
import RealtimePlayground from "./RealtimePlayground";
import { A2ATaskMetadata, MessageType } from "@/components/chat_ui/types";
import { MessageType } from "@/components/chat_ui/types";
import { useCodeInterpreter } from "../../hooks/useCodeInterpreter";
import { useChatHistory } from "../../hooks/useChatHistory";
import { getSecureItem, setSecureItem } from "@/utils/secureStorage";
@ -147,13 +136,10 @@ const ChatUI: React.FC<ChatUIProps> = ({
chatHistory,
setChatHistory,
mcpEvents,
setMCPEvents,
messageTraceId,
setMessageTraceId,
responsesSessionId,
setResponsesSessionId,
useApiSessionManagement,
setUseApiSessionManagement,
updateTextUI,
updateReasoningContent,
updateTimingData,
@ -604,7 +590,7 @@ const ChatUI: React.FC<ChatUIProps> = ({
return;
}
// Resolve the real server ID (toolsets use toolset: prefix)
const mcpServerId = rawSelected.startsWith("toolset:") ? rawSelected : rawSelected;
rawSelected.startsWith("toolset:") ? rawSelected : rawSelected;
if (!selectedMCPDirectTool) {
NotificationsManager.fromBackend("Please select an MCP tool to call");
return;

View file

@ -37,8 +37,6 @@ const RealtimePlayground: React.FC<RealtimePlaygroundProps> = ({
const audioContextRef = useRef<AudioContext | null>(null);
const mediaStreamRef = useRef<MediaStream | null>(null);
const processorRef = useRef<ScriptProcessorNode | null>(null);
const playbackQueueRef = useRef<ArrayBuffer[]>([]);
const isPlayingRef = useRef(false);
const messagesEndRef = useRef<HTMLDivElement>(null);
const nextPlayTimeRef = useRef(0);

View file

@ -8,7 +8,6 @@ import NotificationsManager from "@/components/molecules/notifications_manager";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
const { Text } = Typography;
const { Option } = Select;
interface AddPolicyFormProps {
visible: boolean;
@ -162,7 +161,7 @@ const AddPolicyForm: React.FC<AddPolicyFormProps> = ({
const [form] = Form.useForm();
const [isSubmitting, setIsSubmitting] = useState(false);
const [resolvedGuardrails, setResolvedGuardrails] = useState<string[]>([]);
const [isLoadingResolved, setIsLoadingResolved] = useState(false);
const [, setIsLoadingResolved] = useState(false);
const [modelConditionType, setModelConditionType] = useState<"model" | "regex">("model");
const [availableModels, setAvailableModels] = useState<string[]>([]);
const [step, setStep] = useState<"pick_mode" | "simple_form">("pick_mode");

View file

@ -76,7 +76,7 @@ const AiSuggestionModal: React.FC<AiSuggestionModalProps> = ({
const [testInputText, setTestInputText] = useState("");
const [isTestLoading, setIsTestLoading] = useState(false);
const [testResults, setTestResults] = useState<GuardrailTestResult[] | null>(null);
const [testOverallAction, setTestOverallAction] = useState<string | null>(null);
const [, setTestOverallAction] = useState<string | null>(null);
const [collapsedResults, setCollapsedResults] = useState<Set<string>>(new Set());
// Enrichment state for competitor templates
const [enrichedDefs, setEnrichedDefs] = useState<Record<string, any[]>>({});

View file

@ -53,7 +53,7 @@ const PolicyInfoView: React.FC<PolicyInfoViewProps> = ({
const [policy, setPolicy] = useState<Policy | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [resolvedGuardrails, setResolvedGuardrails] = useState<string[]>([]);
const [isLoadingResolved, setIsLoadingResolved] = useState(false);
const [, setIsLoadingResolved] = useState(false);
const fetchPolicy = useCallback(async () => {
if (!accessToken || !policyId) return;

View file

@ -15,12 +15,6 @@ interface AddPromptFormProps {
onSuccess: () => void;
}
interface PromptFormData {
prompt_id: string;
prompt_integration: string;
prompt_file?: File;
}
const AddPromptForm: React.FC<AddPromptFormProps> = ({ visible, onClose, accessToken, onSuccess }) => {
const [form] = Form.useForm();
const [loading, setLoading] = useState(false);

View file

@ -47,7 +47,7 @@ const PromptsPanel: React.FC<PromptsProps> = ({ accessToken, userRole }) => {
const [isDeleting, setIsDeleting] = useState(false);
const [promptToDelete, setPromptToDelete] = useState<{ id: string; name: string } | null>(null);
const isAdmin = userRole ? isAdminRole(userRole) : false;
userRole ? isAdminRole(userRole) : false;
// Admin Viewer follows the read-parity rule: see prompts, no writes.
const canModify = userRole ? isProxyAdminRole(userRole) : false;

View file

@ -182,7 +182,7 @@ describe("VersionHistorySidePanel", () => {
render(<VersionHistorySidePanel {...defaultProps} />);
await waitFor(() => {
const versionItems = screen.getAllByTestId("tag");
screen.getAllByTestId("tag");
// Should have Active tag for the selected version
expect(screen.getByText("Active")).toBeInTheDocument();
});
@ -464,7 +464,7 @@ describe("VersionHistorySidePanel", () => {
render(<VersionHistorySidePanel {...defaultProps} />);
await waitFor(() => {
const versionElements = screen.getAllByTestId("tag");
screen.getAllByTestId("tag");
// Verify versions are displayed as they come from the API
expect(screen.getByText("v2")).toBeInTheDocument();
});

View file

@ -44,7 +44,7 @@ const PromptEditorView: React.FC<PromptEditorViewProps> = ({ onClose, onSuccess,
};
const [prompt, setPrompt] = useState<PromptType>(getInitialPrompt());
const [editMode, setEditMode] = useState<boolean>(!!initialPromptData);
const [editMode] = useState<boolean>(!!initialPromptData);
const [showHistoryModal, setShowHistoryModal] = useState(false);
// Construct versioned ID from prompt_id and version field

View file

@ -11,12 +11,6 @@ interface TransformRequestPanelProps {
accessToken: string | null;
}
interface TransformResponse {
raw_request_api_base: string;
raw_request_body: Record<string, any>;
raw_request_headers: Record<string, string>;
}
const TransformRequestPanel: React.FC<TransformRequestPanelProps> = ({ accessToken }) => {
const [originalRequestJSON, setOriginalRequestJSON] = useState(`{
"model": "openai/gpt-4o",

View file

@ -15,7 +15,7 @@ interface UIThemeSettingsProps {
}
const UIThemeSettings: React.FC<UIThemeSettingsProps> = ({ userID, userRole, accessToken }) => {
const { logoUrl, setLogoUrl, faviconUrl, setFaviconUrl } = useTheme();
const { setLogoUrl, setFaviconUrl } = useTheme();
const [logoUrlInput, setLogoUrlInput] = useState<string>("");
const [faviconUrlInput, setFaviconUrlInput] = useState<string>("");
const [loading, setLoading] = useState(false);

View file

@ -985,40 +985,5 @@ const UsagePage: React.FC<UsagePageProps> = ({ teams, organizations }) => {
};
// Add this helper function to process model-specific activity data
const getModelActivityData = (userSpendData: { results: DailyData[]; metadata: any }) => {
const modelData: {
[key: string]: {
total_requests: number;
total_tokens: number;
daily_data: Array<{
date: string;
api_requests: number;
total_tokens: number;
}>;
};
} = {};
userSpendData.results.forEach((day: DailyData) => {
Object.entries(day.breakdown.models || {}).forEach(([model, metrics]) => {
if (!modelData[model]) {
modelData[model] = {
total_requests: 0,
total_tokens: 0,
daily_data: [],
};
}
modelData[model].total_requests += metrics.metrics.api_requests;
modelData[model].total_tokens += metrics.metrics.total_tokens;
modelData[model].daily_data.push({
date: day.date,
api_requests: metrics.metrics.api_requests,
total_tokens: metrics.metrics.total_tokens,
});
});
});
return modelData;
};
export default UsagePage;

View file

@ -8,10 +8,10 @@ vi.mock("antd", async () => {
function Select(props: any) {
const { value, onChange, options, optionRender, labelRender, ...rest } = props;
const selectedOption = options?.find((opt: any) => opt.value === value);
const renderedLabel = labelRender ? labelRender({ value, label: selectedOption?.label }) : selectedOption?.label;
labelRender ? labelRender({ value, label: selectedOption?.label }) : selectedOption?.label;
const optionElements = options?.map((opt: any) => {
const rendered = optionRender ? optionRender({ value: opt.value, label: opt.label }) : opt.label;
optionRender ? optionRender({ value: opt.value, label: opt.label }) : opt.label;
return React.createElement("option", { key: opt.value, value: opt.value }, opt.label);
});

View file

@ -1,4 +1,4 @@
import { useEffect, useState } from "react";
import { useEffect } from "react";
import { TextInput, SelectItem } from "@tremor/react";
import { Button as Button2, Modal, Form, Select as Select2, InputNumber } from "antd";
@ -15,7 +15,6 @@ interface EditUserModalProps {
}
const EditUserModal: React.FC<EditUserModalProps> = ({ visible, possibleUIRoles, onCancel, user, onSubmit }) => {
const [editedUser, setEditedUser] = useState(user);
const [form] = Form.useForm();
useEffect(() => {

View file

@ -36,7 +36,6 @@ const VectorStoreInfoView: React.FC<VectorStoreInfoViewProps> = ({
const [isEditing, setIsEditing] = useState<boolean>(editVectorStore);
const [metadataString, setMetadataString] = useState<string>("{}");
const [credentials, setCredentials] = useState<CredentialItem[]>([]);
const [activeTab, setActiveTab] = useState<string>(editVectorStore ? "details" : "details");
const fetchVectorStoreDetails = async () => {
if (!accessToken) return;

View file

@ -65,7 +65,6 @@ export default function ChatConversationPage() {
updateLastAssistantMessage,
truncateFromMessage,
} = useChatShell();
const hadActiveConversationOnMountRef = useRef(activeConversationId !== null);
const [selectedModel, setSelectedModel] = useState<string | null>(null);
const [models, setModels] = useState<string[]>([]);

View file

@ -23,7 +23,7 @@ const UsefulLinksManagement: React.FC<UsefulLinksManagementProps> = ({ accessTok
const [links, setLinks] = useState<Link[]>([]);
const [newLink, setNewLink] = useState({ url: "", displayName: "" });
const [editingLink, setEditingLink] = useState<Link | null>(null);
const [loading, setLoading] = useState(false);
const [, setLoading] = useState(false);
const [isExpanded, setIsExpanded] = useState(true);
const [isRearranging, setIsRearranging] = useState(false);
const [originalLinksOrder, setOriginalLinksOrder] = useState<Link[]>([]);

View file

@ -15,7 +15,7 @@ import {
Tooltip,
Typography,
} from "antd";
import React, { useEffect, useMemo, useState } from "react";
import React, { useEffect, useState } from "react";
import BulkCreateUsers from "./bulk_create_users_button";
import TeamDropdown from "./common_components/team_dropdown";
import { getModelDisplayName } from "./key_team_helpers/fetch_available_models_team_key";
@ -29,7 +29,7 @@ import {
} from "./networking";
import OnboardingModal, { InvitationLink } from "./onboarding_link";
const { Option } = Select;
const { Text, Link, Title } = Typography;
const { Text, Link } = Typography;
// Helper function to generate UUID compatible across all environments
const generateUUID = (): string => {
if (typeof crypto !== "undefined" && crypto.randomUUID) {
@ -80,11 +80,6 @@ export const CreateUserButton: React.FC<CreateuserProps> = ({
const { data: organizations = [] } = useOrganizations();
// Derive teams from the user's organizations, falling back to the teams prop
const availableTeams = useMemo(() => {
const orgTeams = organizations.flatMap((org) => org.teams || []);
if (orgTeams.length > 0) return orgTeams;
return teams || [];
}, [organizations, teams]);
useEffect(() => {
const fetchData = async () => {

View file

@ -1876,7 +1876,7 @@ describe("EntityUsageExport utils", () => {
it("should create CSV file and trigger download", () => {
const createObjectURLSpy = vi.spyOn(window.URL, "createObjectURL").mockReturnValue("blob:mock-url");
const revokeObjectURLSpy = vi.spyOn(window.URL, "revokeObjectURL");
vi.spyOn(window.URL, "revokeObjectURL");
const createElementSpy = vi.spyOn(document, "createElement");
const appendChildSpy = vi.spyOn(document.body, "appendChild");
const removeChildSpy = vi.spyOn(document.body, "removeChild");
@ -1892,7 +1892,7 @@ describe("EntityUsageExport utils", () => {
it("should generate correct filename", () => {
const anchorElement = document.createElement("a");
const createElementSpy = vi.spyOn(document, "createElement").mockReturnValue(anchorElement);
vi.spyOn(document, "createElement").mockReturnValue(anchorElement);
const today = new Date().toISOString().split("T")[0];
@ -1935,7 +1935,7 @@ describe("EntityUsageExport utils", () => {
it("should create JSON file and trigger download", () => {
const createObjectURLSpy = vi.spyOn(window.URL, "createObjectURL").mockReturnValue("blob:mock-url");
const revokeObjectURLSpy = vi.spyOn(window.URL, "revokeObjectURL");
vi.spyOn(window.URL, "revokeObjectURL");
const createElementSpy = vi.spyOn(document, "createElement");
const appendChildSpy = vi.spyOn(document.body, "appendChild");
const removeChildSpy = vi.spyOn(document.body, "removeChild");
@ -1955,7 +1955,7 @@ describe("EntityUsageExport utils", () => {
it("should generate correct filename", () => {
const anchorElement = document.createElement("a");
const createElementSpy = vi.spyOn(document, "createElement").mockReturnValue(anchorElement);
vi.spyOn(document, "createElement").mockReturnValue(anchorElement);
const today = new Date().toISOString().split("T")[0];
const mockDateRange: DateRangePickerValue = {

View file

@ -1,5 +1,5 @@
import React from "react";
import { describe, it, expect, vi } from "vitest";
import { describe, it, expect } from "vitest";
import { screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { renderWithProviders } from "../../tests/test-utils";

View file

@ -2,7 +2,7 @@ import { ProxyModel, useAllProxyModels } from "@/app/(dashboard)/hooks/models/us
import { useOrganization } from "@/app/(dashboard)/hooks/organizations/useOrganizations";
import { useTeam } from "@/app/(dashboard)/hooks/teams/useTeams";
import { useCurrentUser } from "@/app/(dashboard)/hooks/users/useCurrentUser";
import { Select, Skeleton, Tooltip, type SelectProps } from "antd";
import { Select, Skeleton, Tooltip } from "antd";
import { Organization, Team } from "../networking";
import { splitWildcardModels } from "./modelUtils";
@ -93,8 +93,7 @@ const filterModels = (
export const ModelSelect = (props: ModelSelectProps) => {
const { teamID, organizationID, options, context, dataTestId, value = [], onChange, style } = props;
const { includeUserModels, showAllTeamModelsOption, showAllProxyModelsOverride, includeSpecialOptions } =
options || {};
const { showAllProxyModelsOverride, includeSpecialOptions } = options || {};
const { data: allProxyModels, isLoading: isLoadingAllProxyModels } = useAllProxyModels();
const { data: team, isLoading: isLoadingTeam } = useTeam(teamID);
const { data: organization, isLoading: isLoadingOrganization } = useOrganization(organizationID);
@ -113,10 +112,6 @@ export const ModelSelect = (props: ModelSelectProps) => {
return <Skeleton.Input active block />;
}
const optionRender: NonNullable<SelectProps["optionRender"]> = (option) => {
return <span>{option.label}</span>;
};
const handleChange = (values: string[]) => {
const specialValues = values.filter(isSpecialOption);

View file

@ -320,7 +320,7 @@ describe("EditSSOSettingsModal", () => {
useEditSSOSettings: { mutateAsync: mockMutateAsync, isPending: false },
});
const { mockOnSuccess } = renderComponent();
renderComponent();
fireEvent.click(screen.getByTestId(TEST_IDS.TRIGGER_FORM_SUBMIT));

View file

@ -43,13 +43,6 @@ interface TeamProps {
premiumUser?: boolean;
}
interface EditTeamModalProps {
visible: boolean;
onCancel: () => void;
team: any; // Assuming TeamType is a type representing your team object
onSubmit: (data: FormData) => void; // Assuming FormData is the type of data to be submitted
}
import DeleteResourceModal from "./common_components/DeleteResourceModal";
import { teamCreateCall } from "./networking";
import { ModelSelect } from "./ModelSelect/ModelSelect";
@ -112,18 +105,6 @@ const getAdminOrganizations = (
return [];
};
const getOrganizationAlias = (
organizationId: string | null | undefined,
organizations: Organization[] | null | undefined,
): string => {
if (!organizationId || !organizations) {
return organizationId || "N/A";
}
const organization = organizations.find((org) => org.organization_id === organizationId);
return organization?.organization_alias || organizationId;
};
// @deprecated
const Teams: React.FC<TeamProps> = ({ accessToken, userID, userRole, premiumUser = false }) => {
const { data: organizationsData } = useOrganizations();
@ -135,27 +116,22 @@ const Teams: React.FC<TeamProps> = ({ accessToken, userID, userRole, premiumUser
const [currentOrgForCreateTeam, setCurrentOrgForCreateTeam] = useState<Organization | null>(null);
const [form] = Form.useForm();
const [memberForm] = Form.useForm();
const [value, setValue] = useState("");
const [editModalVisible, setEditModalVisible] = useState(false);
const [selectedTeam, setSelectedTeam] = useState<Team | null>(null);
const { teamId: selectedTeamId, openTeam, close: closeTeamDetail } = useTeamDetailRouting();
const [editTeam, setEditTeam] = useState<boolean>(false);
const [isTeamModalVisible, setIsTeamModalVisible] = useState(false);
const [isAddMemberModalVisible, setIsAddMemberModalVisible] = useState(false);
const [isEditMemberModalVisible, setIsEditMemberModalVisible] = useState(false);
const [userModels, setUserModels] = useState<string[]>([]);
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [teamToDelete, setTeamToDelete] = useState<Team | null>(null);
const [modelsToPick, setModelsToPick] = useState<string[]>([]);
const [, setModelsToPick] = useState<string[]>([]);
const [isTeamDeleting, setIsTeamDeleting] = useState(false);
// Add this state near the other useState declarations
const [guardrailsList, setGuardrailsList] = useState<string[]>([]);
const [policiesList, setPoliciesList] = useState<string[]>([]);
const [loggingSettings, setLoggingSettings] = useState<any[]>([]);
const [mcpAccessGroups, setMcpAccessGroups] = useState<string[]>([]);
const [, setMcpAccessGroups] = useState<string[]>([]);
const [mcpAccessGroupsLoaded, setMcpAccessGroupsLoaded] = useState(false);
const [modelAliases, setModelAliases] = useState<{ [key: string]: string }>({});
const [routerSettings, setRouterSettings] = useState<RouterSettingsAccordionValue | null>(null);
@ -245,12 +221,6 @@ const Teams: React.FC<TeamProps> = ({ accessToken, userID, userRole, premiumUser
setRouterSettingsKey((prev) => prev + 1);
};
const handleMemberOk = () => {
setIsAddMemberModalVisible(false);
setIsEditMemberModalVisible(false);
memberForm.resetFields();
};
const handleCancel = () => {
setIsTeamModalVisible(false);
form.resetFields();
@ -260,12 +230,6 @@ const Teams: React.FC<TeamProps> = ({ accessToken, userID, userRole, premiumUser
setRouterSettingsKey((prev) => prev + 1);
};
const handleMemberCancel = () => {
setIsAddMemberModalVisible(false);
setIsEditMemberModalVisible(false);
memberForm.resetFields();
};
const handleDelete = async (team: Team) => {
// Set the team to delete and open the confirmation modal
setTeamToDelete(team);

View file

@ -26,10 +26,8 @@ import {
keyListCall,
teamListCall,
updateToolPolicy,
type ToolPolicyOption,
type ToolPolicyOverrideRow,
} from "@/components/networking";
import type { Team } from "@/components/key_team_helpers/key_list";
interface ToolDetailProps {
toolName: string;
@ -87,7 +85,7 @@ export function ToolDetail({ toolName, onBack, accessToken }: ToolDetailProps) {
staleTime: 60_000,
});
const { data: teamsData } = useQuery({
useQuery({
queryKey: ["teams-list-tool-detail"],
queryFn: () => teamListCall(accessToken!, null, null),
enabled: !!accessToken,
@ -122,24 +120,6 @@ export function ToolDetail({ toolName, onBack, accessToken }: ToolDetailProps) {
}));
}, [logsData?.logs]);
const teams: Team[] = useMemo(() => {
const arr = Array.isArray(teamsData) ? teamsData : teamsData?.data ?? [];
return arr.map((t: { team_id?: string; id?: string; team_alias?: string }) => ({
team_id: t.team_id ?? t.id ?? "",
team_alias: t.team_alias ?? t.team_id ?? "",
models: [],
max_budget: null,
budget_duration: null,
tpm_limit: null,
rpm_limit: null,
organization_id: "",
created_at: "",
keys: [],
members_with_roles: [],
spend: 0,
}));
}, [teamsData]);
const keys: KeyOption[] = useMemo(() => {
const keysRes = keysData?.keys ?? keysData?.data ?? [];
return keysRes.map((k: { token?: string; api_key?: string; key_hash?: string; key_alias?: string }) => ({

View file

@ -20,7 +20,7 @@ interface TopKeyViewProps {
}
const TopKeyView: React.FC<TopKeyViewProps> = ({ topKeys, teams, showTags = false, topKeysLimit, setTopKeysLimit }) => {
const { accessToken, userRole, userId: userID, premiumUser } = useAuthorized();
const { accessToken } = useAuthorized();
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
const [selectedKey, setSelectedKey] = useState<string | null>(null);
const [keyData, setKeyData] = useState<any | undefined>(undefined);

View file

@ -66,7 +66,7 @@ const AddModelForm: React.FC<AddModelFormProps> = ({
} = useProviderFields();
const { data: guardrailsData } = useGuardrails();
const guardrailsList = guardrailsData?.guardrails.map((g) => g.guardrail_name);
const { data: tagsList, isLoading: isTagsLoading, error: tagsError } = useTags();
const { data: tagsList } = useTags();
const handleTestConnection = async () => {
setIsTestingConnection(true);

View file

@ -126,13 +126,6 @@ const RouterConfigBuilder: React.FC<RouterConfigBuilderProps> = ({ modelInfo, va
};
// Handle utterances change (convert textarea string to array)
const handleUtterancesChange = (routeId: string, utterancesText: string) => {
const utterancesArray = utterancesText
.split("\n")
.map((line) => line.trim()) // Only trims leading/trailing whitespace, preserves internal spaces
.filter((line) => line.length > 0);
updateRoute(routeId, "utterances", utterancesArray);
};
// Prepare model options for dropdowns
const modelOptions = modelInfo.map((model) => ({

View file

@ -38,8 +38,6 @@ interface AddAutoRouterTabProps {
createScope?: ModelWriteScope;
}
const { Title } = Typography;
const AddAutoRouterTab: React.FC<AddAutoRouterTabProps> = ({
handleOk,
accessToken,
@ -91,7 +89,7 @@ const AddAutoRouterTab: React.FC<AddAutoRouterTabProps> = ({
const isAdmin = all_admin_roles.includes(userRole);
const modelGroupOptions = Array.from(new Set(modelInfo.map((option) => option.model_group))).map((model_group) => ({
Array.from(new Set(modelInfo.map((option) => option.model_group))).map((model_group) => ({
value: model_group,
label: model_group,
}));

View file

@ -197,7 +197,7 @@ export const handleAddModelSubmit = async (values: any, accessToken: string, for
model_info: modelInfoObj,
};
const response: any = await modelCreateCall(accessToken, new_model);
await modelCreateCall(accessToken, new_model);
}
callback && callback();

View file

@ -24,7 +24,7 @@ const ModelConnectionTest: React.FC<ModelConnectionTestProps> = ({
onTestComplete,
}) => {
const [error, setError] = React.useState<Error | string | null>(null);
const [rawRequest, setRawRequest] = React.useState<any>(null);
const [, setRawRequest] = React.useState<any>(null);
const [rawResponse, setRawResponse] = React.useState<any>(null);
const [isLoading, setIsLoading] = React.useState<boolean>(true);
const [isSuccess, setIsSuccess] = React.useState<boolean>(false);
@ -51,7 +51,7 @@ const ModelConnectionTest: React.FC<ModelConnectionTestProps> = ({
return;
}
const { litellmParamsObj, modelInfoObj, modelName: returnedModelName } = result[0];
const { litellmParamsObj, modelInfoObj } = result[0];
const response = await testConnectionRequest(accessToken, litellmParamsObj, modelInfoObj, modelInfoObj?.mode);
if (response.status === "success") {

View file

@ -37,7 +37,6 @@ const AddPassThroughEndpoint: React.FC<AddFallbacksProps> = ({
const [form] = Form.useForm();
const [isModalVisible, setIsModalVisible] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [selectedModel, setSelectedModel] = useState("");
const [pathValue, setPathValue] = useState("");
const [targetValue, setTargetValue] = useState("");
const [includeSubpath, setIncludeSubpath] = useState(true);
@ -107,11 +106,6 @@ const AddPassThroughEndpoint: React.FC<AddFallbacksProps> = ({
}
};
const copyToClipboard = (text: string) => {
navigator.clipboard.writeText(text);
NotificationsManager.success("Copied to clipboard!");
};
return (
<div>
<Button className="mx-auto mb-4 mt-4" onClick={() => setIsModalVisible(true)}>

View file

@ -1,5 +1,4 @@
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, expect, it, vi, beforeEach } from "vitest";
// Mock networking module

View file

@ -80,24 +80,6 @@ const BulkCreateUsersButton: React.FC<BulkCreateUsersProps> = ({
setBaseUrl(base.toString());
}, [accessToken]);
const downloadTemplate = () => {
const template = [
["user_email", "user_role", "teams", "max_budget", "budget_duration", "models"],
["user@example.com", "internal_user", "team-id-1,team-id-2", "100", "30d", "gpt-3.5-turbo,gpt-4"],
];
const csv = Papa.unparse(template);
const blob = new Blob([csv], { type: "text/csv" });
const url = window.URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "bulk_users_template.csv";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
};
const handleFileUpload = (file: File) => {
// Reset all error states
setParseError(null);

View file

@ -43,9 +43,6 @@ export const generateCodeSnippet = (params: GenerateCodeParams): string => {
selectedVectorStores,
selectedGuardrails,
selectedPolicies,
selectedMCPServers,
mcpServers,
mcpServerToolRestrictions,
selectedVoice,
endpointType,
selectedModel,

View file

@ -1,8 +1,7 @@
import React from "react";
import { Typography, Collapse } from "antd";
import { Collapse } from "antd";
import type { MCPEvent } from "@/components/mcp_tools/types";
const { Text } = Typography;
const { Panel } = Collapse;
interface MCPEventsDisplayProps {

View file

@ -32,7 +32,7 @@ export default function DeleteResourceModal({
confirmLoading,
requiredConfirmation,
}: DeleteResourceModalProps) {
const { Title, Text } = Typography;
const { Text } = Typography;
const { token } = theme.useToken();
const [requiredConfirmationInput, setRequiredConfirmationInput] = useState("");

View file

@ -143,8 +143,8 @@ const EditAutoRouterModal: React.FC<EditAutoRouterModalProps> = ({
const [loading, setLoading] = useState(false);
const [modelAccessGroups, setModelAccessGroups] = useState<string[]>([]);
const [modelInfo, setModelInfo] = useState<ModelGroup[]>([]);
const [showCustomDefaultModel, setShowCustomDefaultModel] = useState<boolean>(false);
const [showCustomEmbeddingModel, setShowCustomEmbeddingModel] = useState<boolean>(false);
const [, setShowCustomDefaultModel] = useState<boolean>(false);
const [, setShowCustomEmbeddingModel] = useState<boolean>(false);
const [showValidationErrors, setShowValidationErrors] = useState<boolean>(false);
const [routerConfig, setRouterConfig] = useState<any>(null);
const [customTechnicalKeywords, setCustomTechnicalKeywords] = useState<string[]>([]);

View file

@ -73,10 +73,6 @@ export async function makeOpenAIChatCompletionRequest(
let firstTokenReceived = false;
let timeToFirstToken: number | undefined = undefined;
// For collecting complete response text
let fullResponseContent = "";
let fullReasoningContent = "";
// Track MCP metadata cumulatively across chunks
let mcpMetadata: {
mcp_list_tools?: any[];
@ -167,7 +163,6 @@ export async function makeOpenAIChatCompletionRequest(
if (chunk.choices[0]?.delta?.content) {
const content = chunk.choices[0].delta.content;
updateUI(content, chunk.model);
fullResponseContent += content;
}
// Process image generation if present
@ -181,7 +176,6 @@ export async function makeOpenAIChatCompletionRequest(
if (onReasoningContent) {
onReasoningContent(reasoningContent);
}
fullReasoningContent += reasoningContent;
}
// Check for search results in provider_specific_fields

View file

@ -2,7 +2,7 @@ import React from "react";
import { Form, Button, Tooltip, Typography, Modal } from "antd";
import { TextInput } from "@tremor/react";
import { CredentialItem } from "../networking";
const { Title, Link } = Typography;
const { Link } = Typography;
interface ReuseCredentialsModalProps {
isVisible: boolean;

View file

@ -121,13 +121,6 @@ const ModelFilters: React.FC<ModelFiltersProps> = ({
};
// Expose filter values and reset function
const filterValues = {
searchTerm,
selectedProvider,
selectedMode,
selectedFeature,
resetFilters,
};
const filtersContent = (
<div className="flex flex-wrap gap-4 items-center">

View file

@ -137,7 +137,7 @@ export default function ModelInfoView({
const [deleteLoading, setDeleteLoading] = useState(false);
const [isCredentialModalOpen, setIsCredentialModalOpen] = useState(false);
const [isUpdateCredentialsModalOpen, setIsUpdateCredentialsModalOpen] = useState(false);
const [isDirty, setIsDirty] = useState(false);
const [, setIsDirty] = useState(false);
const [isSaving, setIsSaving] = useState(false);
const [isEditing, setIsEditing] = useState(false);
const [existingCredential, setExistingCredential] = useState<CredentialItem | null>(null);
@ -302,7 +302,7 @@ export default function ModelInfoView({
},
};
NotificationsManager.info("Storing credential..");
let credentialResponse = await credentialCreateCall(accessToken, credentialItem);
await credentialCreateCall(accessToken, credentialItem);
NotificationsManager.success("Credential stored successfully");
};

View file

@ -1,5 +1,5 @@
import userEvent from "@testing-library/user-event";
import React, { useState } from "react";
import React from "react";
import { describe, expect, it, vi } from "vitest";
import { renderWithProviders, screen, waitFor } from "../../tests/test-utils";
import Navbar from "./navbar";

View file

@ -356,7 +356,6 @@ export const getAgentCreateMetadata = async (): Promise<AgentCreateInfo[]> => {
// Global variable for the header name
let globalLitellmHeaderName: string = "Authorization";
const MCP_AUTH_HEADER: string = "x-mcp-auth";
// Function to set the global header name
export function setGlobalLitellmHeaderName(headerName: string = "Authorization") {

View file

@ -58,7 +58,7 @@ export default function OnboardingModal({
invitationLinkData,
modalType = "invitation",
}: OnboardingProps) {
const { Title, Paragraph } = Typography;
const { Paragraph } = Typography;
const handleInvitationOk = () => {
setIsInvitationLinkModalVisible(false);
};

View file

@ -93,26 +93,6 @@ interface UserOption {
user: User;
}
const getPredefinedTags = (data: any[] | null) => {
let allTags = [];
if (data) {
for (let key of data) {
if (key["metadata"] && key["metadata"]["tags"]) {
allTags.push(...key["metadata"]["tags"]);
}
}
}
// Deduplicate using Set
const uniqueTags = Array.from(new Set(allTags)).map((tag) => ({
value: tag,
label: tag,
}));
return uniqueTags;
};
export const fetchTeamModels = async (
userID: string,
userRole: string,
@ -178,7 +158,7 @@ const CreateKey: React.FC<CreateKeyProps> = ({ team, teams, data, addKey, autoOp
const [form] = Form.useForm();
const [isModalVisible, setIsModalVisible] = useState(false);
const [apiKey, setApiKey] = useState(null);
const [softBudget, setSoftBudget] = useState(null);
const [, setSoftBudget] = useState(null);
const [userModels, setUserModels] = useState<string[]>([]);
const [modelsToPick, setModelsToPick] = useState<string[]>([]);
const [keyOwner, setKeyOwner] = useState("you");
@ -192,11 +172,10 @@ const CreateKey: React.FC<CreateKeyProps> = ({ team, teams, data, addKey, autoOp
const [selectedOrganizationId, setSelectedOrganizationId] = useState<string | null>(null);
const [selectedProjectId, setSelectedProjectId] = useState<string | null>(null);
const [isCreateUserModalVisible, setIsCreateUserModalVisible] = useState(false);
const [newlyCreatedUserId, setNewlyCreatedUserId] = useState<string | null>(null);
const [, setNewlyCreatedUserId] = useState<string | null>(null);
const [possibleUIRoles, setPossibleUIRoles] = useState<Record<string, Record<string, string>>>({});
const [userOptions, setUserOptions] = useState<UserOption[]>([]);
const [userSearchLoading, setUserSearchLoading] = useState<boolean>(false);
const [mcpAccessGroups, setMcpAccessGroups] = useState<string[]>([]);
const [disabledCallbacks, setDisabledCallbacks] = useState<string[]>([]);
const [keyType, setKeyType] = useState<string>("llm_api");
const [modelAliases, setModelAliases] = useState<{ [key: string]: string }>({});
@ -592,10 +571,6 @@ const CreateKey: React.FC<CreateKeyProps> = ({ team, teams, data, addKey, autoOp
}
};
const handleCopy = () => {
NotificationsManager.success("Virtual Key copied to clipboard");
};
// Fetch available models when team or auth changes.
// Note: Model prefill from URL params is handled by the useEffect below, which
// watches for pendingPrefillModels + modelsToPick to both be populated.

View file

@ -69,7 +69,7 @@ const OrganizationInfoView: React.FC<OrganizationInfoProps> = ({
user_id: values.user_id,
role: values.role,
};
const response = await organizationMemberAddCall(accessToken, organizationId, member);
await organizationMemberAddCall(accessToken, organizationId, member);
NotificationsManager.success("Organization member added successfully");
setIsAddMemberModalVisible(false);
@ -90,7 +90,7 @@ const OrganizationInfoView: React.FC<OrganizationInfoProps> = ({
role: values.role,
};
const response = await organizationMemberUpdateCall(accessToken, organizationId, member);
await organizationMemberUpdateCall(accessToken, organizationId, member);
NotificationsManager.success("Organization member updated successfully");
setIsEditMemberModalVisible(false);
queryClient.invalidateQueries({ queryKey: organizationKeys.all });

View file

@ -72,7 +72,7 @@ const PassThroughInfoView: React.FC<PassThroughInfoProps> = ({
onEndpointUpdated,
}) => {
const [endpointData, setEndpointData] = useState<PassThroughEndpoint | null>(initialEndpointData);
const [loading, setLoading] = useState(false);
const [loading] = useState(false);
const [isEditing, setIsEditing] = useState(false);
const [authEnabled, setAuthEnabled] = useState(initialEndpointData?.auth || false);
const [selectedMethods, setSelectedMethods] = useState<string[]>(initialEndpointData?.methods || []);

View file

@ -55,7 +55,7 @@ const PerUserUsage: React.FC<PerUserUsageProps> = ({ accessToken, selectedTags,
total_pages: 0,
});
const [loading, setLoading] = useState(false);
const [, setLoading] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const fetchPerUserData = async () => {

View file

@ -60,9 +60,9 @@ const PriceDataReload: React.FC<PriceDataReloadProps> = ({
const [showScheduleModal, setShowScheduleModal] = useState(false);
const [hours, setHours] = useState<number>(6);
const [reloadStatus, setReloadStatus] = useState<ReloadStatus | null>(null);
const [loadingStatus, setLoadingStatus] = useState(false);
const [, setLoadingStatus] = useState(false);
const [sourceInfo, setSourceInfo] = useState<CostMapSourceInfo | null>(null);
const [loadingSource, setLoadingSource] = useState(false);
const [, setLoadingSource] = useState(false);
// Fetch status on component mount and periodically
useEffect(() => {

View file

@ -55,7 +55,7 @@ const PublicModelHub: React.FC<PublicModelHubProps> = ({ accessToken, isEmbedded
const [modelHubData, setModelHubData] = useState<ModelGroupInfo[] | null>(null);
const [agentHubData, setAgentHubData] = useState<AgentCard[] | null>(null);
const [mcpHubData, setMcpHubData] = useState<MCPServerData[] | null>(null);
const [pageTitle, setPageTitle] = useState<string>("LiteLLM Gateway");
const [, setPageTitle] = useState<string>("LiteLLM Gateway");
const [customDocsDescription, setCustomDocsDescription] = useState<string | null>(null);
const [litellmVersion, setLitellmVersion] = useState<string>("");
const [usefulLinks, setUsefulLinks] = useState<Record<string, string | { url: string; index: number }>>({});

View file

@ -20,13 +20,11 @@ import {
} from "@tremor/react";
import React, { useEffect, useState } from "react";
import { Button as Button2, Form, Input, Modal, Select, Typography } from "antd";
import { Button as Button2, Form, Input, Modal, Select } from "antd";
import EmailSettings from "./email_settings";
import { Logo } from "@/components/molecules/logo/Logo";
import NotificationsManager from "./molecules/notifications_manager";
const { Title, Paragraph } = Typography;
import FormItem from "antd/es/form/FormItem";
import AlertingSettings from "./alerting/alerting_settings";
import CloudZeroCostTracking from "./CloudZeroCostTracking/CloudZeroCostTracking";
@ -48,12 +46,6 @@ interface SettingsPageProps {
premiumUser: boolean;
}
interface genericCallbackParams {
litellm_callback_name: string; // what to send in request
ui_callback_name: string; // what to show on UI
litellm_callback_params: string[] | null; // known required params for this callback
}
const assetsLogoFolder = "/ui/assets/logos/";
export const backendCallbackLogoSrc = (logo: string | null | undefined): string | undefined => {
@ -214,7 +206,6 @@ const Settings: React.FC<SettingsPageProps> = ({ accessToken, userRole, userID,
const [callbacks, setCallbacks] = useState<AlertingObject[]>([]);
const [isLoadingCallbacks, setIsLoadingCallbacks] = useState(true);
const [alerts, setAlerts] = useState<any[]>([]);
const [isModalVisible, setIsModalVisible] = useState(false);
const [addForm] = Form.useForm();
const [editForm] = Form.useForm();
const [selectedCallback, setSelectedCallback] = useState<string | null>(null);
@ -418,119 +409,6 @@ const Settings: React.FC<SettingsPageProps> = ({ accessToken, userRole, userID,
}
NotificationsManager.success("Alerts updated successfully");
};
const handleSaveChanges = (callback: any) => {
if (!accessToken) {
return;
}
const updatedVariables = Object.fromEntries(
Object.entries(callback.variables).map(([key, value]) => [
key,
(document.querySelector(`input[name="${key}"]`) as HTMLInputElement)?.value || value,
]),
);
const payload = {
environment_variables: updatedVariables,
litellm_settings: {
success_callback: [callback.name],
},
};
try {
setCallbacksCall(accessToken, payload);
} catch (error) {
NotificationsManager.fromBackend(error);
}
NotificationsManager.success("Callback updated successfully");
};
const handleOk = () => {
if (!accessToken) {
return;
}
// Handle form submission
addForm.validateFields().then((values) => {
// Call API to add the callback
let payload;
if (values.callback === "langfuse" || values.callback === "langfuse_otel") {
payload = {
environment_variables: {
LANGFUSE_PUBLIC_KEY: values.langfusePublicKey,
LANGFUSE_SECRET_KEY: values.langfusePrivateKey,
},
litellm_settings: {
success_callback: [values.callback],
},
};
setCallbacksCall(accessToken, payload);
let newCallback: AlertingObject = {
name: values.callback,
variables: {
SLACK_WEBHOOK_URL: null,
LANGFUSE_HOST: null,
LANGFUSE_PUBLIC_KEY: values.langfusePublicKey,
LANGFUSE_SECRET_KEY: values.langfusePrivateKey,
OPENMETER_API_KEY: null,
},
};
// add langfuse to callbacks
setCallbacks(callbacks ? [...callbacks, newCallback] : [newCallback]);
} else if (values.callback === "slack") {
payload = {
general_settings: {
alerting: ["slack"],
alerting_threshold: 300,
},
environment_variables: {
SLACK_WEBHOOK_URL: values.slackWebhookUrl,
},
};
setCallbacksCall(accessToken, payload);
let newCallback: AlertingObject = {
name: values.callback,
variables: {
SLACK_WEBHOOK_URL: values.slackWebhookUrl,
LANGFUSE_HOST: null,
LANGFUSE_PUBLIC_KEY: null,
LANGFUSE_SECRET_KEY: null,
OPENMETER_API_KEY: null,
},
};
setCallbacks(callbacks ? [...callbacks, newCallback] : [newCallback]);
} else if (values.callback == "openmeter") {
payload = {
environment_variables: {
OPENMETER_API_KEY: values.openMeterApiKey,
},
litellm_settings: {
success_callback: [values.callback],
},
};
setCallbacksCall(accessToken, payload);
let newCallback: AlertingObject = {
name: values.callback,
variables: {
SLACK_WEBHOOK_URL: null,
LANGFUSE_HOST: null,
LANGFUSE_PUBLIC_KEY: null,
LANGFUSE_SECRET_KEY: null,
OPENMETER_API_KEY: values.openMeterAPIKey,
},
};
// add langfuse to callbacks
setCallbacks(callbacks ? [...callbacks, newCallback] : [newCallback]);
} else {
payload = {
error: "Invalid callback value",
};
}
setIsModalVisible(false);
addForm.resetFields();
setSelectedCallback(null);
});
};
const handleDeleteCallback = (callback: any) => {
setCallbackToDelete(callback);

View file

@ -4,6 +4,7 @@ import type * as React from "react";
import type { ColumnPinnedSide, DataTableSkeletonShape } from "./types";
declare module "@tanstack/react-table" {
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- declaration merging requires the type parameters to match the upstream ColumnMeta signature exactly (TS2428)
interface ColumnMeta<TData extends RowData, TValue> {
numeric?: boolean;
className?: string;

View file

@ -10,7 +10,7 @@ describe("LoggingSettings", () => {
});
it("passes a number to updateCallbackVar when user inputs a number in NumericalInput", async () => {
const user = userEvent.setup();
userEvent.setup();
const mockOnChange = vi.fn();
// Create initial config with a callback that has number parameters (LangSmith has langsmith_sampling_rate)

View file

@ -52,7 +52,6 @@ import MCPServerSelector from "../mcp_server_management/MCPServerSelector";
import MCPToolPermissions from "../mcp_server_management/MCPToolPermissions";
import { ModelSelect } from "../ModelSelect/ModelSelect";
import NotificationsManager from "../molecules/notifications_manager";
import { fetchMCPAccessGroups } from "../networking";
import ObjectPermissionsView from "../object_permissions_view";
import NumericalInput from "../shared/numerical_input";
import VectorStoreSelector from "../vector_store_management/VectorStoreSelector";
@ -161,29 +160,6 @@ export interface TeamInfoProps {
premiumUser?: boolean;
}
const getOrganizationModels = (organization: Organization | null, userModels: string[]) => {
let tempModelsToPick = [];
if (organization) {
// Check if organization has "all-proxy-models" in its models array
if (organization.models.includes("all-proxy-models")) {
// Treat as all-proxy-models (use userModels)
tempModelsToPick = userModels;
} else if (organization.models.length > 0) {
// Organization has specific models
tempModelsToPick = organization.models;
} else {
// Empty array [] is treated as all-proxy-models
tempModelsToPick = userModels;
}
} else {
// No organization, show all available models
tempModelsToPick = userModels;
}
return unfurlWildcardModelsInList(tempModelsToPick, userModels);
};
const TeamInfoView: React.FC<TeamInfoProps> = ({
teamId,
onClose,
@ -203,8 +179,6 @@ const TeamInfoView: React.FC<TeamInfoProps> = ({
const [isEditMemberModalVisible, setIsEditMemberModalVisible] = useState(false);
const [selectedEditMember, setSelectedEditMember] = useState<Member | null>(null);
const [isEditing, setIsEditing] = useState(false);
const [mcpAccessGroups, setMcpAccessGroups] = useState<string[]>([]);
const [mcpAccessGroupsLoaded, setMcpAccessGroupsLoaded] = useState(false);
const [copiedStates, setCopiedStates] = useState<Record<string, boolean>>({});
const { data: guardrailsData, isLoading: isGuardrailsLoading } = useGuardrails();
const globalGuardrailNames = guardrailsData?.globalGuardrailNames ?? new Set<string>();
@ -294,21 +268,6 @@ const TeamInfoView: React.FC<TeamInfoProps> = ({
}, [accessToken, teamData?.team_info?.organization_id]);
// Compute modelsToPick based on organization and userModels
const modelsToPick = useMemo(() => {
return getOrganizationModels(organization, userModels);
}, [organization, userModels]);
const fetchMcpAccessGroups = async () => {
if (!accessToken) return;
if (mcpAccessGroupsLoaded) return;
try {
const groups = await fetchMCPAccessGroups(accessToken);
setMcpAccessGroups(groups);
setMcpAccessGroupsLoaded(true);
} catch (error) {
console.error("Failed to fetch MCP access groups:", error);
}
};
useEffect(() => {
const fetchPolicies = async () => {
@ -653,7 +612,7 @@ const TeamInfoView: React.FC<TeamInfoProps> = ({
}
}
const response = await teamUpdateCall(accessToken, updateData);
await teamUpdateCall(accessToken, updateData);
queryClient.invalidateQueries({ queryKey: organizationKeys.all });
NotificationsManager.success("Team settings updated successfully");

View file

@ -197,7 +197,7 @@ vi.mock("lucide-react", async () => {
// Heavy children -> async factories & local React
vi.mock("../organisms/RegenerateKeyModal", async () => {
const React = await import("react");
await import("react");
function RegenerateKeyModal() {
return null;
}
@ -205,7 +205,7 @@ vi.mock("../organisms/RegenerateKeyModal", async () => {
return { RegenerateKeyModal };
});
vi.mock("../object_permissions_view", async () => {
const React = await import("react");
await import("react");
function ObjectPermissionsView() {
return null;
}
@ -213,7 +213,7 @@ vi.mock("../object_permissions_view", async () => {
return { __esModule: true, default: ObjectPermissionsView };
});
vi.mock("../logging_settings_view", async () => {
const React = await import("react");
await import("react");
function LoggingSettingsView() {
return null;
}
@ -221,7 +221,7 @@ vi.mock("../logging_settings_view", async () => {
return { __esModule: true, default: LoggingSettingsView };
});
vi.mock("../common_components/AutoRotationView", async () => {
const React = await import("react");
await import("react");
function AutoRotationView() {
return null;
}

View file

@ -1007,7 +1007,7 @@ describe("KeyEditView", () => {
});
it("should disable the organization dropdown for non-admin users", async () => {
const { container } = renderWithProviders(
renderWithProviders(
<KeyEditView
keyData={MOCK_KEY_DATA}
onCancel={() => {}}
@ -1029,7 +1029,7 @@ describe("KeyEditView", () => {
});
it("should not disable the organization dropdown for admin users", async () => {
const { container } = renderWithProviders(
renderWithProviders(
<KeyEditView
keyData={MOCK_KEY_DATA}
onCancel={() => {}}

View file

@ -50,22 +50,6 @@ interface KeyEditViewProps {
}
// Add this helper function
const getAvailableModelsForKey = (keyData: KeyResponse, teams: any[] | null): string[] => {
// If no teams data is available, return empty array
if (!teams || !keyData.team_id) {
return [];
}
// Find the team that matches the key's team_id
const keyTeam = teams.find((team) => team.team_id === keyData.team_id);
// If team found and has models, return those models
if (keyTeam?.models) {
return keyTeam.models;
}
return [];
};
// Helper function to determine key_type display value from allowed_routes
const getKeyTypeFromRoutes = (allowedRoutes: string[] | null | undefined): string => {

View file

@ -1,11 +1,9 @@
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { useProjects } from "@/app/(dashboard)/hooks/projects/useProjects";
import useTeams from "@/app/(dashboard)/hooks/useTeams";
import { renderWithProviders } from "../../../tests/test-utils";
import { screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { useResetKeySpend } from "@/app/(dashboard)/hooks/keys/useResetKeySpend";
import { KeyResponse, Team } from "../key_team_helpers/key_list";
import { keyDeleteCall, keyUpdateCall } from "../networking";
import { QueryClient } from "@tanstack/react-query";

View file

@ -6,7 +6,7 @@ import { formatNumberWithCommas } from "@/utils/dataUtils";
import { mapEmptyStringToNull } from "@/utils/keyUpdateUtils";
import { ArrowLeftIcon } from "@heroicons/react/outline";
import { Badge, Button, Card, Grid, Tab, TabGroup, TabList, TabPanel, TabPanels, Text, Title } from "@tremor/react";
import { Form, Modal, Tag } from "antd";
import { Modal, Tag } from "antd";
import { KeyInfoHeader } from "./KeyInfoHeader";
import { useEffect, useState } from "react";
import { isProxyAdminRole, isUserTeamAdminForSingleTeam, rolesWithWriteAccess } from "../../utils/roles";
@ -74,10 +74,9 @@ export default function KeyInfoView({
const { data: uiSettingsData } = useUISettings();
const enableProjectsUI = Boolean(uiSettingsData?.values?.enable_projects_ui);
const [isEditing, setIsEditing] = useState(false);
const [form] = Form.useForm();
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [deleteLoading, setDeleteLoading] = useState(false);
const [deleteConfirmInput, setDeleteConfirmInput] = useState("");
const [, setDeleteConfirmInput] = useState("");
const [isRegenerateModalOpen, setIsRegenerateModalOpen] = useState(false);
const [isResetSpendModalOpen, setIsResetSpendModalOpen] = useState(false);
const [isBlockModalOpen, setIsBlockModalOpen] = useState(false);

View file

@ -38,10 +38,6 @@ interface DistinctTagResponse {
tag: string;
}
interface DistinctTagsResponse {
results: DistinctTagResponse[];
}
interface UserAgentActivityProps {
accessToken: string | null;
userRole: string | null;
@ -59,7 +55,7 @@ const UserAgentActivity: React.FC<UserAgentActivityProps> = ({ accessToken, user
const [mauData, setMauData] = useState<ActiveUsersAnalyticsResponse>({ results: [] });
const [summaryData, setSummaryData] = useState<TagSummaryResponse>({ results: [] });
const [userAgentFilter, setUserAgentFilter] = useState<string>("");
const [userAgentFilter] = useState<string>("");
// Tag filtering state
const [availableTags, setAvailableTags] = useState<string[]>([]);

View file

@ -49,12 +49,6 @@ interface UserDashboardProps {
prefillData?: CreateKeyPrefillData;
}
type TeamInterface = {
models: any[];
team_id: null;
team_alias: string;
};
const UserDashboard: React.FC<UserDashboardProps> = ({
userID,
userRole,
@ -72,15 +66,15 @@ const UserDashboard: React.FC<UserDashboardProps> = ({
prefillData,
}) => {
const [userSpendData, setUserSpendData] = useState<UserInfo | null>(null);
const [currentOrg, setCurrentOrg] = useState<Organization | null>(null);
const [currentOrg] = useState<Organization | null>(null);
const token = getCookie("token");
const [accessToken, setAccessToken] = useState<string | null>(null);
const [teamSpend, setTeamSpend] = useState<number | null>(null);
const [userModels, setUserModels] = useState<string[]>([]);
const [proxySettings, setProxySettings] = useState<ProxySettings | null>(null);
const [selectedTeam, setSelectedTeam] = useState<any | null>(null);
const [, setTeamSpend] = useState<number | null>(null);
const [, setUserModels] = useState<string[]>([]);
const [, setProxySettings] = useState<ProxySettings | null>(null);
const [selectedTeam] = useState<any | null>(null);
// Clear session storage on page unload so next load fetches fresh data.
// Note: MCP auth tokens are persistent and should not be cleared on page refresh
@ -186,7 +180,7 @@ const UserDashboard: React.FC<UserDashboardProps> = ({
if (accessToken) {
const fetchKeyInfo = async () => {
try {
const keyInfo = await keyInfoCall(accessToken, [accessToken]);
await keyInfoCall(accessToken, [accessToken]);
} catch (error: any) {
if (error.message.includes("Invalid proxy server token passed")) {
gotoLogin();

View file

@ -235,18 +235,6 @@ const DownloadIcon = () => (
</svg>
);
const ExternalLinkIcon = () => (
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" className="inline ml-1">
<path
d="M6 2H3a1 1 0 00-1 1v8a1 1 0 001 1h8a1 1 0 001-1V8M8 2h4m0 0v4m0-4L6.5 7.5"
stroke="currentColor"
strokeWidth="1.2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
// ── Sub-components ──────────────────────────────────────────────────────────
const MatchDetailsTable = ({ matchDetails }: { matchDetails: MatchDetail[] }) => {
@ -647,10 +635,6 @@ const GuardrailViewer = ({ data, accessToken, logEntry }: GuardrailViewerProps)
return Math.round(guardrailEntries.reduce((sum, e) => sum + (e.duration ?? 0), 0) * 1000);
}, [guardrailEntries]);
const policyTemplates = useMemo(() => {
return Array.from(new Set(guardrailEntries.map((e) => e.policy_template).filter(Boolean)));
}, [guardrailEntries]);
if (guardrailEntries.length === 0) {
return null;
}

View file

@ -1,5 +1,5 @@
import React from "react";
import { render, screen, fireEvent, act, waitFor } from "@testing-library/react";
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, it, expect, vi, beforeEach } from "vitest";
import { RealtimePrettyView, isRealtimeResponse } from "./RealtimePrettyView";

View file

@ -31,7 +31,7 @@ describe("SimpleToolCallBlock", () => {
});
it("should not render arguments section when arguments are empty", () => {
const { container } = render(<SimpleToolCallBlock tool={{ id: "1", name: "get_weather", arguments: {} }} />);
render(<SimpleToolCallBlock tool={{ id: "1", name: "get_weather", arguments: {} }} />);
// The tool name and "function" badge should be there, but no key: value pairs
expect(screen.getByText("get_weather")).toBeInTheDocument();
expect(screen.queryByText(/:$/)).not.toBeInTheDocument();

View file

@ -14,6 +14,7 @@ import {
import { Table, TableHeader, TableHead, TableBody, TableRow, TableCell } from "@/components/ui/table";
declare module "@tanstack/react-table" {
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- declaration merging requires the type parameters to match the upstream ColumnMeta signature exactly (TS2428)
interface ColumnMeta<TData extends RowData, TValue> {
numeric?: boolean;
}

View file

@ -5,11 +5,6 @@ import { formatNumberWithCommas } from "@/utils/dataUtils";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
// Define the props type
interface UserSpendData {
spend: number; // Adjust the type accordingly based on your data
max_budget?: number | null; // Optional property with a default of null
// Add other properties if needed
}
interface ViewUserSpendProps {
userSpend: number | null;
userMaxBudget: number | null;