mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-13 23:11:40 +00:00
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:
parent
487074f602
commit
f3ba562b20
87 changed files with 101 additions and 751 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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[]>([]);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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)}%`;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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} />,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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[]>>({});
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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(() => {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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[]>([]);
|
||||
|
|
|
|||
|
|
@ -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[]>([]);
|
||||
|
|
|
|||
|
|
@ -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 () => {
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -320,7 +320,7 @@ describe("EditSSOSettingsModal", () => {
|
|||
useEditSSOSettings: { mutateAsync: mockMutateAsync, isPending: false },
|
||||
});
|
||||
|
||||
const { mockOnSuccess } = renderComponent();
|
||||
renderComponent();
|
||||
|
||||
fireEvent.click(screen.getByTestId(TEST_IDS.TRIGGER_FORM_SUBMIT));
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 }) => ({
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) => ({
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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") {
|
||||
|
|
|
|||
|
|
@ -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)}>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -43,9 +43,6 @@ export const generateCodeSnippet = (params: GenerateCodeParams): string => {
|
|||
selectedVectorStores,
|
||||
selectedGuardrails,
|
||||
selectedPolicies,
|
||||
selectedMCPServers,
|
||||
mcpServers,
|
||||
mcpServerToolRestrictions,
|
||||
selectedVoice,
|
||||
endpointType,
|
||||
selectedModel,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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("");
|
||||
|
||||
|
|
|
|||
|
|
@ -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[]>([]);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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") {
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ export default function OnboardingModal({
|
|||
invitationLinkData,
|
||||
modalType = "invitation",
|
||||
}: OnboardingProps) {
|
||||
const { Title, Paragraph } = Typography;
|
||||
const { Paragraph } = Typography;
|
||||
const handleInvitationOk = () => {
|
||||
setIsInvitationLinkModalVisible(false);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
|
|
|
|||
|
|
@ -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 || []);
|
||||
|
|
|
|||
|
|
@ -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 () => {
|
||||
|
|
|
|||
|
|
@ -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(() => {
|
||||
|
|
|
|||
|
|
@ -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 }>>({});
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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={() => {}}
|
||||
|
|
|
|||
|
|
@ -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 => {
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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[]>([]);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue