= ({ accessToken, userID, userRole, premiumUser
/>
-
-
+
+
Additional Settings
-
-
+
+
+
- {
e.target.value = e.target.value.trim();
}}
@@ -713,7 +715,7 @@ const Teams: React.FC = ({ accessToken, userID, userRole, premiumUser
name="team_member_key_duration"
tooltip="Set a limit to the duration of a team member's key. Format: 30s (seconds), 30m (minutes), 30h (hours), 30d (days), 1mo (month)"
>
-
+
= ({ accessToken, userID, userRole, premiumUser
disabled={!premiumUser || !isProxyAdminRole(userRole || "")}
/>
-
-
+
+
-
-
+
+
MCP Settings
-
-
+
+
+
@@ -951,14 +954,15 @@ const Teams: React.FC = ({ accessToken, userID, userRole, premiumUser
)}
-
-
+
+
-
-
+
+
Agent Settings
-
-
+
+
+
@@ -979,14 +983,15 @@ const Teams: React.FC = ({ accessToken, userID, userRole, premiumUser
placeholder="Select agents or access groups (optional)"
/>
-
-
+
+
-
-
+
+
Search Tool Settings
-
-
+
+
+
@@ -1007,14 +1012,15 @@ const Teams: React.FC = ({ accessToken, userID, userRole, premiumUser
placeholder="Select search tools (optional, empty = all allowed)"
/>
-
-
+
+
-
-
+
+
Logging Settings
-
-
+
+
+
= ({ accessToken, userID, userRole, premiumUser
premiumUser={premiumUser}
/>
-
-
+
+
-
-
+
+
Router Settings
-
-
+
+
+
= ({ accessToken, userID, userRole, premiumUser
}
/>
-
-
+
+
-
-
+
+
Model Aliases
-
-
+
+
+
Create custom aliases for models that can be used by team members in API calls. This allows you to
@@ -1061,8 +1072,8 @@ const Teams: React.FC = ({ accessToken, userID, userRole, premiumUser
showExampleConfig={false}
/>
-
-
+
+
>
diff --git a/ui/litellm-dashboard/src/components/team/LoggingSettings.test.tsx b/ui/litellm-dashboard/src/components/team/LoggingSettings.test.tsx
index 83c541afd15..17f20ad0dcc 100644
--- a/ui/litellm-dashboard/src/components/team/LoggingSettings.test.tsx
+++ b/ui/litellm-dashboard/src/components/team/LoggingSettings.test.tsx
@@ -1,5 +1,6 @@
import React from "react";
import { describe, it, expect, vi, beforeEach } from "vitest";
+import userEvent from "@testing-library/user-event";
import { renderWithProviders, screen, fireEvent } from "../../../tests/test-utils";
import LoggingSettings from "./LoggingSettings";
@@ -109,6 +110,29 @@ describe("LoggingSettings", () => {
expect(updatedConfig[0].callback_vars.langsmith_sampling_rate).toBe("0.3"); // Preserves initial value
});
+ it("masks a sensitive parameter until the reveal toggle is used", async () => {
+ const user = userEvent.setup({ delay: null });
+ const initialValue = [
+ {
+ callback_name: "langsmith",
+ callback_type: "success",
+ callback_vars: { langsmith_api_key: "sk-secret-value" },
+ },
+ ];
+
+ renderWithProviders( );
+
+ const apiKeyInput = screen.getByPlaceholderText("os.environ/LANGSMITH_API_KEY");
+ expect(apiKeyInput).toHaveAttribute("type", "password");
+
+ await user.click(screen.getByRole("button", { name: "Show password" }));
+ expect(apiKeyInput).toHaveAttribute("type", "text");
+ expect(apiKeyInput).toHaveValue("sk-secret-value");
+
+ await user.click(screen.getByRole("button", { name: "Hide password" }));
+ expect(apiKeyInput).toHaveAttribute("type", "password");
+ });
+
it("shows the bundled logo in the integration card header", () => {
const initialValue = [
{
diff --git a/ui/litellm-dashboard/src/components/team/LoggingSettings.tsx b/ui/litellm-dashboard/src/components/team/LoggingSettings.tsx
index 80bea3ef2d2..eee6e76569d 100644
--- a/ui/litellm-dashboard/src/components/team/LoggingSettings.tsx
+++ b/ui/litellm-dashboard/src/components/team/LoggingSettings.tsx
@@ -2,14 +2,51 @@
import React from "react";
import { Select, Tooltip, Divider } from "antd";
import { InfoCircleOutlined } from "@ant-design/icons";
-import { Button, Card, TextInput } from "@tremor/react";
-import { PlusIcon, TrashIcon, CogIcon, BanIcon } from "@heroicons/react/outline";
+import { Button } from "@/components/ui/button";
+import { Card } from "@/components/ui/card";
+import { Input } from "@/components/ui/input";
+import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from "@/components/ui/input-group";
+import { CogIcon, BanIcon } from "@heroicons/react/outline";
+import { Eye, EyeOff, Plus, Trash2 } from "lucide-react";
import { callbackInfo, callback_map, mapDisplayToInternalNames } from "../callback_info_helpers";
import { Logo } from "@/components/molecules/logo/Logo";
import NumericalInput from "../shared/numerical_input";
const { Option } = Select;
+const CallbackVarInput: React.FC<{
+ sensitive: boolean;
+ placeholder: string;
+ value: string;
+ onValueChange: (value: string) => void;
+}> = ({ sensitive, placeholder, value, onValueChange }) => {
+ const [revealed, setRevealed] = React.useState(false);
+
+ if (!sensitive) {
+ return onValueChange(e.target.value)} />;
+ }
+
+ return (
+
+ onValueChange(e.target.value)}
+ />
+
+ setRevealed(!revealed)}
+ aria-label={revealed ? "Hide password" : "Show password"}
+ >
+ {revealed ? : }
+
+
+
+ );
+};
+
interface LoggingConfig {
callback_name: string;
callback_type: string;
@@ -138,11 +175,11 @@ const LoggingSettings: React.FC = ({
onChange={(e: any) => updateCallbackVar(configIndex, paramName, e.target.value)}
/>
) : (
- updateCallbackVar(configIndex, paramName, e.target.value)}
+ onValueChange={(newValue) => updateCallbackVar(configIndex, paramName, newValue)}
/>
)}
@@ -212,11 +249,11 @@ const LoggingSettings: React.FC = ({
+
Add Integration
@@ -230,9 +267,7 @@ const LoggingSettings: React.FC = ({
return (
@@ -246,14 +281,13 @@ const LoggingSettings: React.FC = ({
{callbackDisplayName || "New Integration"} Configuration
removeLoggingConfig(index)}
- icon={TrashIcon}
- size="xs"
- color="red"
- className="hover:bg-red-50"
+ size="sm"
+ className="text-red-500 hover:bg-red-50 hover:text-red-500"
type="button"
>
+
Remove
diff --git a/ui/litellm-dashboard/src/components/team/TeamInfo.tsx b/ui/litellm-dashboard/src/components/team/TeamInfo.tsx
index 9e30104a4f1..0f1d22e5b9a 100644
--- a/ui/litellm-dashboard/src/components/team/TeamInfo.tsx
+++ b/ui/litellm-dashboard/src/components/team/TeamInfo.tsx
@@ -29,10 +29,14 @@ import {
SaveOutlined,
} from "@ant-design/icons";
import { ArrowLeftIcon } from "@heroicons/react/outline";
-import { Accordion, AccordionBody, AccordionHeader, Badge, Card, Grid, Text, TextInput, Title } from "@tremor/react";
+import { StatusBadge, type StatusTone } from "@/components/shared/table_cells/status_badge";
+import { Badge } from "@/components/ui/badge";
+import { Card } from "@/components/ui/card";
+import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
+import { Input as UIInput } from "@/components/ui/input";
import { Button, Form, Input, InputNumber, Select, Space, Switch, Tabs, Tag, Tooltip } from "antd";
import { toast } from "@/lib/toast";
-import { CheckIcon, CopyIcon } from "lucide-react";
+import { CheckIcon, ChevronDown, CopyIcon } from "lucide-react";
import React, { useEffect, useMemo, useState } from "react";
import { copyToClipboard as utilCopyToClipboard } from "../../utils/dataUtils";
import AccessGroupSelector from "../common_components/AccessGroupSelector";
@@ -92,11 +96,11 @@ const UI_MANAGED_METADATA_KEYS: ReadonlySet = new Set([
"disable_global_guardrails",
]);
-const TEAM_MODEL_BADGE_COLORS: Record = {
- "all-proxy": "red",
- "no-default": "gray",
- direct: "blue",
- "access-group": "green",
+const TEAM_MODEL_BADGE_TONES: Record = {
+ "all-proxy": "error",
+ "no-default": "neutral",
+ direct: "info",
+ "access-group": "success",
};
export interface TeamMembership {
@@ -732,9 +736,9 @@ const TeamInfoView: React.FC = ({
} onClick={onClose} className="mb-4">
Back to Teams
- {info.team_alias}
+ {info.team_alias}
-
{info.team_id}
+
{info.team_id}
= ({
key: TEAM_INFO_TAB_KEYS.OVERVIEW,
label: TEAM_INFO_TAB_LABELS[TEAM_INFO_TAB_KEYS.OVERVIEW],
children: (
-
-
- Budget Status
+
+
+ Budget Status
-
${formatNumberWithCommas(info.spend, 2)}
-
+ ${formatNumberWithCommas(info.spend, 2)}
+
of {info.max_budget === null ? "Unlimited" : `$${formatNumberWithCommas(info.max_budget, 2)}`}
-
- {info.budget_duration &&
Reset: {info.budget_duration} }
+
+ {info.budget_duration &&
Reset: {info.budget_duration}
}
{info.team_member_budget_table && (
-
+
Team Member Budget: ${formatNumberWithCommas(info.team_member_budget_table.max_budget, 2)}
-
+
)}
-
- Rate Limits
+
+ Rate Limits
-
TPM: {info.tpm_limit || "Unlimited"}
-
RPM: {info.rpm_limit || "Unlimited"}
- {info.max_parallel_requests &&
Max Parallel Requests: {info.max_parallel_requests} }
+
TPM: {info.tpm_limit || "Unlimited"}
+
RPM: {info.rpm_limit || "Unlimited"}
+ {info.max_parallel_requests &&
Max Parallel Requests: {info.max_parallel_requests}
}
{(() => {
const modelTpm = (info.metadata?.model_tpm_limit ?? {}) as Record
;
const modelRpm = (info.metadata?.model_rpm_limit ?? {}) as Record;
@@ -789,33 +793,33 @@ const TeamInfoView: React.FC = ({
if (models.length === 0) return null;
return (
-
Per-model limits:
+
Per-model limits:
{models.map((m) => (
-
+
{m}: TPM {modelTpm[m] ?? "—"}, RPM {modelRpm[m] ?? "—"}
-
+
))}
);
})()}
- Estimated Output Tokens: {info.metadata?.default_estimated_output_tokens ?? "Default"}
-
+ Estimated Output Tokens: {info.metadata?.default_estimated_output_tokens ?? "Default"}
+
Estimated Output Tokens Per Model:{" "}
{info.metadata?.default_estimated_output_tokens_per_model
? JSON.stringify(info.metadata.default_estimated_output_tokens_per_model)
: "Default"}
-
+
-
- Models
+
+ Models
{computeTeamModelBadges(info.models, info.access_group_models || [], info.access_group_details).map(
(badge, index) => (
- {badge.label}
+
),
@@ -823,12 +827,12 @@ const TeamInfoView: React.FC = ({
-
- Virtual Keys
+
+ Virtual Keys
-
User Keys: {teamData.keys.filter((key) => key.user_id).length}
-
Service Account Keys: {teamData.keys.filter((key) => !key.user_id).length}
-
Total: {teamData.keys.length}
+
User Keys: {teamData.keys.filter((key) => key.user_id).length}
+
Service Account Keys: {teamData.keys.filter((key) => !key.user_id).length}
+
Total: {teamData.keys.length}
@@ -838,7 +842,7 @@ const TeamInfoView: React.FC = ({
accessToken={accessToken}
/>
-
+
= ({
/>
-
- Policies
+
+ Policies
{info.policies && info.policies.length > 0 ? (
{info.policies.map((policy: string, index: number) => (
-
{policy}
- {loadingPolicies &&
Loading guardrails... }
+
{policy}
+ {loadingPolicies &&
Loading guardrails...
}
{!loadingPolicies && policyGuardrails[policy] && policyGuardrails[policy].length > 0 && (
-
Resolved Guardrails:
+
Resolved Guardrails:
{policyGuardrails[policy].map((guardrail: string, gIndex: number) => (
-
+
{guardrail}
))}
@@ -878,7 +882,7 @@ const TeamInfoView: React.FC = ({
))}
) : (
-
No policies configured
+
No policies configured
)}
@@ -887,7 +891,7 @@ const TeamInfoView: React.FC
= ({
disabledCallbacks={[]}
variant="card"
/>
-
+
),
},
{
@@ -923,9 +927,9 @@ const TeamInfoView: React.FC
= ({
key: TEAM_INFO_TAB_KEYS.SETTINGS,
label: TEAM_INFO_TAB_LABELS[TEAM_INFO_TAB_KEYS.SETTINGS],
children: (
-
+
-
Team Settings
+
Team Settings
{canEditTeam && !isEditing && (
}
@@ -1079,15 +1083,16 @@ const TeamInfoView: React.FC
= ({
-
-
+
+
Team Member Settings
-
-
-
+
+
+
+
Optional defaults applied when members join this team. All fields can be overridden per
member.
-
+
@@ -1132,7 +1137,7 @@ const TeamInfoView: React.FC = ({
name="team_member_key_duration"
tooltip="Set a limit to the duration of a team member's key. Format: 30s (seconds), 30m (minutes), 30h (hours), 30d (days), 1mo (month)"
>
-
+
= ({
>
-
-
+
+
@@ -1447,11 +1452,12 @@ const TeamInfoView: React.FC = ({
/>
-
-
+
+
Search Tool Settings
-
-
+
+
+
= ({
placeholder="Select search tools (optional, empty = all allowed)"
/>
-
-
+
+
= ({
) : (
-
Team Name
+
Team Name
{info.team_alias}
-
Team ID
+
Team ID
{info.team_id}
-
Created At
+
Created At
{new Date(info.created_at).toLocaleString()}
-
Models
+
Models
{info.models.map((model, index) => (
-
+
{model}
))}
@@ -1560,10 +1566,10 @@ const TeamInfoView: React.FC = ({
{info.default_team_member_models && info.default_team_member_models.length > 0 && (
-
Default Member Models
+
Default Member Models
{info.default_team_member_models.map((model, index) => (
-
+
{model}
))}
@@ -1571,7 +1577,7 @@ const TeamInfoView: React.FC = ({
)}
-
Model Aliases
+
Model Aliases
{(() => {
const aliasEntries = Object.entries(info.litellm_model_table?.model_aliases ?? {});
if (aliasEntries.length === 0) {
@@ -1591,7 +1597,7 @@ const TeamInfoView: React.FC
= ({
})()}
-
Rate Limits
+
Rate Limits
TPM: {info.tpm_limit || "Unlimited"}
RPM: {info.rpm_limit || "Unlimited"}
{(() => {
@@ -1601,7 +1607,7 @@ const TeamInfoView: React.FC
= ({
if (models.length === 0) return null;
return (
-
Per-model limits:
+
Per-model limits:
{models.map((m) => (
{m}: TPM {modelTpm[m] ?? "—"}, RPM {modelRpm[m] ?? "—"}
@@ -1619,7 +1625,7 @@ const TeamInfoView: React.FC = ({
-
Team Budget
+
Team Budget
Max Budget:{" "}
{info.max_budget !== null ? `$${formatNumberWithCommas(info.max_budget, 4)}` : "No Limit"}
@@ -1638,12 +1644,12 @@ const TeamInfoView: React.FC = ({
)}
-
+
Team Member Settings{" "}
-
+
Max Budget: {info.team_member_budget_table?.max_budget || "No Limit"}
Budget Duration: {info.team_member_budget_table?.budget_duration || "No Limit"}
Key Duration: {info.metadata?.team_member_key_duration || "No Limit"}
@@ -1651,7 +1657,7 @@ const TeamInfoView: React.FC
= ({
RPM Limit: {info.team_member_budget_table?.rpm_limit || "No Limit"}
-
Router Settings
+
Router Settings
{info.router_settings &&
Object.values(info.router_settings).some(
(v) => v !== null && v !== undefined && v !== "" && !(Array.isArray(v) && v.length === 0),
@@ -1659,7 +1665,8 @@ const TeamInfoView: React.FC
= ({
{info.router_settings.routing_strategy && (
- Routing Strategy: {info.router_settings.routing_strategy}
+ Routing Strategy:{" "}
+ {info.router_settings.routing_strategy}
)}
{info.router_settings.num_retries != null && (
@@ -1687,12 +1694,14 @@ const TeamInfoView: React.FC
= ({
)}
-
Organization ID
+
Organization ID
{info.organization_id}
-
Status
-
{info.blocked ? "Blocked" : "Active"}
+
Status
+
+ {info.blocked ? "Blocked" : "Active"}
+
= ({
{info.metadata?.secret_manager_settings && (
-
Secret Manager Settings
+
Secret Manager Settings
{JSON.stringify(info.metadata.secret_manager_settings, null, 2)}
diff --git a/ui/litellm-dashboard/src/lib/http/schema.d.ts b/ui/litellm-dashboard/src/lib/http/schema.d.ts
index 6a9b7e89305..3a0ed88ce17 100644
--- a/ui/litellm-dashboard/src/lib/http/schema.d.ts
+++ b/ui/litellm-dashboard/src/lib/http/schema.d.ts
@@ -27356,6 +27356,8 @@ export interface components {
cache_creation_input_token_cost_flex?: number | null;
/** Cache Creation Input Token Cost Priority */
cache_creation_input_token_cost_priority?: number | null;
+ /** Cache Creation Input Token Cost Ultrafast */
+ cache_creation_input_token_cost_ultrafast?: number | null;
/** Cache Read Input Audio Token Cost */
cache_read_input_audio_token_cost?: number | null;
/** Cache Read Input Token Cost */
@@ -27376,6 +27378,8 @@ export interface components {
cache_read_input_token_cost_flex?: number | null;
/** Cache Read Input Token Cost Priority */
cache_read_input_token_cost_priority?: number | null;
+ /** Cache Read Input Token Cost Ultrafast */
+ cache_read_input_token_cost_ultrafast?: number | null;
/** Citation Cost Per Token */
citation_cost_per_token?: number | null;
/** Complexity Router Config */
@@ -27440,6 +27444,8 @@ export interface components {
input_cost_per_token_flex?: number | null;
/** Input Cost Per Token Priority */
input_cost_per_token_priority?: number | null;
+ /** Input Cost Per Token Ultrafast */
+ input_cost_per_token_ultrafast?: number | null;
/** Input Cost Per Video Per Second */
input_cost_per_video_per_second?: number | null;
/** Input Cost Per Video Per Second Above 128K Tokens */
@@ -27537,6 +27543,8 @@ export interface components {
output_cost_per_token_flex?: number | null;
/** Output Cost Per Token Priority */
output_cost_per_token_priority?: number | null;
+ /** Output Cost Per Token Ultrafast */
+ output_cost_per_token_ultrafast?: number | null;
/** Output Cost Per Video Per Second */
output_cost_per_video_per_second?: number | null;
/** Output Cost Per Video Token */
@@ -36458,6 +36466,8 @@ export interface components {
cache_creation_input_token_cost_flex?: number | null;
/** Cache Creation Input Token Cost Priority */
cache_creation_input_token_cost_priority?: number | null;
+ /** Cache Creation Input Token Cost Ultrafast */
+ cache_creation_input_token_cost_ultrafast?: number | null;
/** Cache Read Input Audio Token Cost */
cache_read_input_audio_token_cost?: number | null;
/** Cache Read Input Token Cost */
@@ -36478,6 +36488,8 @@ export interface components {
cache_read_input_token_cost_flex?: number | null;
/** Cache Read Input Token Cost Priority */
cache_read_input_token_cost_priority?: number | null;
+ /** Cache Read Input Token Cost Ultrafast */
+ cache_read_input_token_cost_ultrafast?: number | null;
/** Citation Cost Per Token */
citation_cost_per_token?: number | null;
/** Complexity Router Config */
@@ -36542,6 +36554,8 @@ export interface components {
input_cost_per_token_flex?: number | null;
/** Input Cost Per Token Priority */
input_cost_per_token_priority?: number | null;
+ /** Input Cost Per Token Ultrafast */
+ input_cost_per_token_ultrafast?: number | null;
/** Input Cost Per Video Per Second */
input_cost_per_video_per_second?: number | null;
/** Input Cost Per Video Per Second Above 128K Tokens */
@@ -36639,6 +36653,8 @@ export interface components {
output_cost_per_token_flex?: number | null;
/** Output Cost Per Token Priority */
output_cost_per_token_priority?: number | null;
+ /** Output Cost Per Token Ultrafast */
+ output_cost_per_token_ultrafast?: number | null;
/** Output Cost Per Video Per Second */
output_cost_per_video_per_second?: number | null;
/** Output Cost Per Video Token */