From 5e1a5a8949a31bc3b39f4d689c76e9a423a80740 Mon Sep 17 00:00:00 2001 From: stiyyagura0901 Date: Sat, 17 Jan 2026 10:15:28 -0500 Subject: [PATCH 01/16] fix: UI dashboard respects custom authentication header override Fix UI dashboard to use custom authentication header name configured in general settings instead of hardcoded 'Authorization' header. - Replace hardcoded Authorization headers with getGlobalLitellmHeaderName() in all hook files - Replace hardcoded Authorization headers with globalLitellmHeaderName in networking.tsx - Update test files to properly mock getGlobalLitellmHeaderName - Fixes issue where UI becomes unusable when custom auth header is configured --- .../hooks/cloudzero/useCloudZeroCreate.ts | 4 +-- .../hooks/cloudzero/useCloudZeroDryRun.ts | 4 +-- .../hooks/cloudzero/useCloudZeroExport.ts | 4 +-- .../hooks/cloudzero/useCloudZeroSettings.ts | 8 +++--- .../hooks/router/useRouterFields.test.ts | 1 + .../hooks/router/useRouterFields.ts | 4 +-- .../pricing_calculator/use_cost_estimate.ts | 4 +-- .../use_multi_cost_estimate.ts | 4 +-- .../use_discount_config.ts | 6 ++-- .../CostTrackingSettings/use_margin_config.ts | 6 ++-- .../src/components/cloudzero_export_modal.tsx | 7 +++-- .../src/components/cost_tracking_settings.tsx | 6 ++-- .../guardrails/edit_guardrail_form.tsx | 4 +-- .../src/components/networking.tsx | 28 +++++++++---------- .../chat_ui/CodeInterpreterOutput.test.tsx | 1 + .../chat_ui/CodeInterpreterOutput.tsx | 6 ++-- .../playground/llm_calls/a2a_send_message.tsx | 6 ++-- .../llm_calls/embeddings_api.test.tsx | 1 + .../playground/llm_calls/embeddings_api.tsx | 4 +-- .../playground/llm_calls/fetch_agents.tsx | 4 +-- .../conversation_panel/useConversation.ts | 4 +-- .../src/components/ui_theme_settings.tsx | 8 +++--- 22 files changed, 64 insertions(+), 60 deletions(-) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/cloudzero/useCloudZeroCreate.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/cloudzero/useCloudZeroCreate.ts index e1263903622..29c5ae3a0f0 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/hooks/cloudzero/useCloudZeroCreate.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/cloudzero/useCloudZeroCreate.ts @@ -1,4 +1,4 @@ -import { getProxyBaseUrl } from "@/components/networking"; +import { getProxyBaseUrl, getGlobalLitellmHeaderName } from "@/components/networking"; import { useMutation } from "@tanstack/react-query"; interface CreateParams { @@ -18,7 +18,7 @@ const performCloudZeroCreate = async (accessToken: string, params: CreateParams) const response = await fetch(url, { method: "POST", headers: { - Authorization: `Bearer ${accessToken}`, + [getGlobalLitellmHeaderName()]: `Bearer ${accessToken}`, "Content-Type": "application/json", }, body: JSON.stringify({ diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/cloudzero/useCloudZeroDryRun.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/cloudzero/useCloudZeroDryRun.ts index 1ed8a141603..e00fd2a6375 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/hooks/cloudzero/useCloudZeroDryRun.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/cloudzero/useCloudZeroDryRun.ts @@ -1,4 +1,4 @@ -import { getProxyBaseUrl } from "@/components/networking"; +import { getProxyBaseUrl, getGlobalLitellmHeaderName } from "@/components/networking"; import { useMutation } from "@tanstack/react-query"; interface DryRunParams { @@ -16,7 +16,7 @@ const performCloudZeroDryRun = async (accessToken: string, params: DryRunParams const response = await fetch(url, { method: "POST", headers: { - Authorization: `Bearer ${accessToken}`, + [getGlobalLitellmHeaderName()]: `Bearer ${accessToken}`, "Content-Type": "application/json", }, body: JSON.stringify({ diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/cloudzero/useCloudZeroExport.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/cloudzero/useCloudZeroExport.ts index 47d559b20d2..ba9a013ecc6 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/hooks/cloudzero/useCloudZeroExport.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/cloudzero/useCloudZeroExport.ts @@ -1,4 +1,4 @@ -import { getProxyBaseUrl } from "@/components/networking"; +import { getProxyBaseUrl, getGlobalLitellmHeaderName } from "@/components/networking"; import { useMutation } from "@tanstack/react-query"; interface ExportParams { @@ -16,7 +16,7 @@ const performCloudZeroExport = async (accessToken: string, params: ExportParams const response = await fetch(url, { method: "POST", headers: { - Authorization: `Bearer ${accessToken}`, + [getGlobalLitellmHeaderName()]: `Bearer ${accessToken}`, "Content-Type": "application/json", }, body: JSON.stringify({ diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/cloudzero/useCloudZeroSettings.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/cloudzero/useCloudZeroSettings.ts index 96f5ab2f944..7fd61077385 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/hooks/cloudzero/useCloudZeroSettings.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/cloudzero/useCloudZeroSettings.ts @@ -1,5 +1,5 @@ import { CloudZeroSettings } from "@/components/CloudZeroCostTracking/types"; -import { getProxyBaseUrl } from "@/components/networking"; +import { getProxyBaseUrl, getGlobalLitellmHeaderName } from "@/components/networking"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { createQueryKeys } from "../common/queryKeysFactory"; @@ -12,7 +12,7 @@ const getCloudZeroSettings = async (accessToken: string): Promise ({ proxyBaseUrl: null, + getGlobalLitellmHeaderName: vi.fn(() => "Authorization"), })); // Mock useAuthorized hook diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/router/useRouterFields.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/router/useRouterFields.ts index 589508c5ddb..5fa22b41096 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/hooks/router/useRouterFields.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/router/useRouterFields.ts @@ -1,7 +1,7 @@ import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; import { useQuery, UseQueryResult } from "@tanstack/react-query"; import { createQueryKeys } from "../common/queryKeysFactory"; -import { proxyBaseUrl } from "@/components/networking"; +import { proxyBaseUrl, getGlobalLitellmHeaderName } from "@/components/networking"; export interface RouterSettingsField { field_name: string; @@ -39,7 +39,7 @@ const getRouterFields = async (accessToken: string): Promise = ({ isOpen, onC const response = await fetch("/cloudzero/settings", { method: "GET", headers: { - Authorization: `Bearer ${accessToken}`, + [getGlobalLitellmHeaderName()]: `Bearer ${accessToken}`, "Content-Type": "application/json", }, }); @@ -88,7 +89,7 @@ const CloudZeroExportModal: React.FC = ({ isOpen, onC const response = await fetch(endpoint, { method, headers: { - Authorization: `Bearer ${accessToken}`, + [getGlobalLitellmHeaderName()]: `Bearer ${accessToken}`, "Content-Type": "application/json", }, body: JSON.stringify(payload), @@ -128,7 +129,7 @@ const CloudZeroExportModal: React.FC = ({ isOpen, onC const response = await fetch("/cloudzero/export", { method: "POST", headers: { - Authorization: `Bearer ${accessToken}`, + [getGlobalLitellmHeaderName()]: `Bearer ${accessToken}`, "Content-Type": "application/json", }, body: JSON.stringify({ diff --git a/ui/litellm-dashboard/src/components/cost_tracking_settings.tsx b/ui/litellm-dashboard/src/components/cost_tracking_settings.tsx index bab61da6775..75d650306ee 100644 --- a/ui/litellm-dashboard/src/components/cost_tracking_settings.tsx +++ b/ui/litellm-dashboard/src/components/cost_tracking_settings.tsx @@ -15,7 +15,7 @@ import { Col, Subtitle, } from "@tremor/react"; -import { getProxyBaseUrl } from "@/components/networking"; +import { getProxyBaseUrl, getGlobalLitellmHeaderName } from "@/components/networking"; import NotificationsManager from "./molecules/notifications_manager"; interface CostTrackingSettingsProps { @@ -56,7 +56,7 @@ const CostTrackingSettings: React.FC = ({ const response = await fetch(url, { method: "GET", headers: { - Authorization: `Bearer ${accessToken}`, + [getGlobalLitellmHeaderName()]: `Bearer ${accessToken}`, "Content-Type": "application/json", }, }); @@ -86,7 +86,7 @@ const CostTrackingSettings: React.FC = ({ const response = await fetch(url, { method: "PATCH", headers: { - Authorization: `Bearer ${accessToken}`, + [getGlobalLitellmHeaderName()]: `Bearer ${accessToken}`, "Content-Type": "application/json", }, body: JSON.stringify(discountConfig), diff --git a/ui/litellm-dashboard/src/components/guardrails/edit_guardrail_form.tsx b/ui/litellm-dashboard/src/components/guardrails/edit_guardrail_form.tsx index caa197e0cd5..a2cc3ad41dd 100644 --- a/ui/litellm-dashboard/src/components/guardrails/edit_guardrail_form.tsx +++ b/ui/litellm-dashboard/src/components/guardrails/edit_guardrail_form.tsx @@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react"; import { Form, Typography, Select, Input, Switch, Modal } from "antd"; import { Button, TextInput } from "@tremor/react"; import { guardrail_provider_map, guardrailLogoMap, getGuardrailProviders } from "./guardrail_info_helpers"; -import { getGuardrailUISettings } from "../networking"; +import { getGuardrailUISettings, getGlobalLitellmHeaderName } from "../networking"; import PiiConfiguration from "./pii_configuration"; import NotificationsManager from "../molecules/notifications_manager"; @@ -183,7 +183,7 @@ const EditGuardrailForm: React.FC = ({ const response = await fetch(url, { method: "PUT", headers: { - Authorization: `Bearer ${accessToken}`, + [getGlobalLitellmHeaderName()]: `Bearer ${accessToken}`, "Content-Type": "application/json", }, body: JSON.stringify(guardrailData), diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index 82894dfb0e2..cc434adee1a 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -6226,7 +6226,7 @@ export const tagCreateCall = async (accessToken: string, formValues: TagNewReque method: "POST", headers: { "Content-Type": "application/json", - Authorization: `Bearer ${accessToken}`, + [globalLitellmHeaderName]: `Bearer ${accessToken}`, }, body: JSON.stringify(formValues), }); @@ -6252,7 +6252,7 @@ export const tagUpdateCall = async (accessToken: string, formValues: TagUpdateRe method: "POST", headers: { "Content-Type": "application/json", - Authorization: `Bearer ${accessToken}`, + [globalLitellmHeaderName]: `Bearer ${accessToken}`, }, body: JSON.stringify(formValues), }); @@ -6278,7 +6278,7 @@ export const tagInfoCall = async (accessToken: string, tagNames: string[]): Prom method: "POST", headers: { "Content-Type": "application/json", - Authorization: `Bearer ${accessToken}`, + [globalLitellmHeaderName]: `Bearer ${accessToken}`, }, body: JSON.stringify({ names: tagNames }), }); @@ -6304,7 +6304,7 @@ export const tagListCall = async (accessToken: string): Promise const response = await fetch(url, { method: "GET", headers: { - Authorization: `Bearer ${accessToken}`, + [globalLitellmHeaderName]: `Bearer ${accessToken}`, }, }); @@ -6330,7 +6330,7 @@ export const tagDeleteCall = async (accessToken: string, tagName: string): Promi method: "POST", headers: { "Content-Type": "application/json", - Authorization: `Bearer ${accessToken}`, + [globalLitellmHeaderName]: `Bearer ${accessToken}`, }, body: JSON.stringify({ name: tagName }), }); @@ -6422,7 +6422,7 @@ export const getTeamPermissionsCall = async (accessToken: string, teamId: string method: "GET", headers: { "Content-Type": "application/json", - Authorization: `Bearer ${accessToken}`, + [globalLitellmHeaderName]: `Bearer ${accessToken}`, }, }); @@ -6450,7 +6450,7 @@ export const teamPermissionsUpdateCall = async (accessToken: string, teamId: str method: "POST", headers: { "Content-Type": "application/json", - Authorization: `Bearer ${accessToken}`, + [globalLitellmHeaderName]: `Bearer ${accessToken}`, }, body: JSON.stringify({ team_id: teamId, @@ -6514,7 +6514,7 @@ export const vectorStoreCreateCall = async (accessToken: string, formValues: Rec method: "POST", headers: { "Content-Type": "application/json", - Authorization: `Bearer ${accessToken}`, + [globalLitellmHeaderName]: `Bearer ${accessToken}`, }, body: JSON.stringify(formValues), }); @@ -6543,7 +6543,7 @@ export const vectorStoreListCall = async ( method: "GET", headers: { "Content-Type": "application/json", - Authorization: `Bearer ${accessToken}`, + [globalLitellmHeaderName]: `Bearer ${accessToken}`, }, }); @@ -6567,7 +6567,7 @@ export const vectorStoreDeleteCall = async (accessToken: string, vectorStoreId: method: "POST", headers: { "Content-Type": "application/json", - Authorization: `Bearer ${accessToken}`, + [globalLitellmHeaderName]: `Bearer ${accessToken}`, }, body: JSON.stringify({ vector_store_id: vectorStoreId }), }); @@ -6592,7 +6592,7 @@ export const vectorStoreInfoCall = async (accessToken: string, vectorStoreId: st method: "POST", headers: { "Content-Type": "application/json", - Authorization: `Bearer ${accessToken}`, + [globalLitellmHeaderName]: `Bearer ${accessToken}`, }, body: JSON.stringify({ vector_store_id: vectorStoreId }), }); @@ -6617,7 +6617,7 @@ export const vectorStoreUpdateCall = async (accessToken: string, formValues: Rec method: "POST", headers: { "Content-Type": "application/json", - Authorization: `Bearer ${accessToken}`, + [globalLitellmHeaderName]: `Bearer ${accessToken}`, }, body: JSON.stringify(formValues), }); @@ -7738,7 +7738,7 @@ export const vectorStoreSearchCall = async ( const response = await fetch(url, { method: "POST", headers: { - Authorization: `Bearer ${accessToken}`, + [globalLitellmHeaderName]: `Bearer ${accessToken}`, "Content-Type": "application/json", }, body: JSON.stringify({ @@ -7771,7 +7771,7 @@ export const searchToolQueryCall = async ( const response = await fetch(url, { method: "POST", headers: { - Authorization: `Bearer ${accessToken}`, + [globalLitellmHeaderName]: `Bearer ${accessToken}`, "Content-Type": "application/json", }, body: JSON.stringify({ diff --git a/ui/litellm-dashboard/src/components/playground/chat_ui/CodeInterpreterOutput.test.tsx b/ui/litellm-dashboard/src/components/playground/chat_ui/CodeInterpreterOutput.test.tsx index d87a74f4641..a42bd42ed81 100644 --- a/ui/litellm-dashboard/src/components/playground/chat_ui/CodeInterpreterOutput.test.tsx +++ b/ui/litellm-dashboard/src/components/playground/chat_ui/CodeInterpreterOutput.test.tsx @@ -5,6 +5,7 @@ import CodeInterpreterOutput from "./CodeInterpreterOutput"; vi.mock("@/components/networking", () => ({ getProxyBaseUrl: vi.fn(() => "https://example.com"), + getGlobalLitellmHeaderName: vi.fn(() => "Authorization"), })); global.fetch = vi.fn(); diff --git a/ui/litellm-dashboard/src/components/playground/chat_ui/CodeInterpreterOutput.tsx b/ui/litellm-dashboard/src/components/playground/chat_ui/CodeInterpreterOutput.tsx index 5625d0cbf4b..b72e55feaea 100644 --- a/ui/litellm-dashboard/src/components/playground/chat_ui/CodeInterpreterOutput.tsx +++ b/ui/litellm-dashboard/src/components/playground/chat_ui/CodeInterpreterOutput.tsx @@ -9,7 +9,7 @@ import { } from "@ant-design/icons"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { coy } from "react-syntax-highlighter/dist/esm/styles/prism"; -import { getProxyBaseUrl } from "@/components/networking"; +import { getProxyBaseUrl, getGlobalLitellmHeaderName } from "@/components/networking"; interface ContainerFileCitation { type: "container_file_citation"; @@ -55,7 +55,7 @@ const CodeInterpreterOutput: React.FC = ({ `${proxyBaseUrl}/v1/containers/${annotation.container_id}/files/${annotation.file_id}/content`, { headers: { - Authorization: `Bearer ${accessToken}`, + [getGlobalLitellmHeaderName()]: `Bearer ${accessToken}`, }, } ); @@ -90,7 +90,7 @@ const CodeInterpreterOutput: React.FC = ({ `${proxyBaseUrl}/v1/containers/${annotation.container_id}/files/${annotation.file_id}/content`, { headers: { - Authorization: `Bearer ${accessToken}`, + [getGlobalLitellmHeaderName()]: `Bearer ${accessToken}`, }, } ); diff --git a/ui/litellm-dashboard/src/components/playground/llm_calls/a2a_send_message.tsx b/ui/litellm-dashboard/src/components/playground/llm_calls/a2a_send_message.tsx index 0654db32920..b5cfc820f36 100644 --- a/ui/litellm-dashboard/src/components/playground/llm_calls/a2a_send_message.tsx +++ b/ui/litellm-dashboard/src/components/playground/llm_calls/a2a_send_message.tsx @@ -2,7 +2,7 @@ // A2A Protocol (JSON-RPC 2.0) implementation for sending messages to agents import { v4 as uuidv4 } from "uuid"; -import { getProxyBaseUrl } from "../../networking"; +import { getProxyBaseUrl, getGlobalLitellmHeaderName } from "../../networking"; import { A2ATaskMetadata } from "../chat_ui/types"; interface A2AMessagePart { @@ -143,7 +143,7 @@ export const makeA2ASendMessageRequest = async ( const response = await fetch(url, { method: "POST", headers: { - Authorization: `Bearer ${accessToken}`, + [getGlobalLitellmHeaderName()]: `Bearer ${accessToken}`, "Content-Type": "application/json", }, body: JSON.stringify(jsonRpcRequest), @@ -276,7 +276,7 @@ export const makeA2AStreamMessageRequest = async ( const response = await fetch(url, { method: "POST", headers: { - Authorization: `Bearer ${accessToken}`, + [getGlobalLitellmHeaderName()]: `Bearer ${accessToken}`, "Content-Type": "application/json", }, body: JSON.stringify(jsonRpcRequest), diff --git a/ui/litellm-dashboard/src/components/playground/llm_calls/embeddings_api.test.tsx b/ui/litellm-dashboard/src/components/playground/llm_calls/embeddings_api.test.tsx index f24a50074c6..19a79f3f9be 100644 --- a/ui/litellm-dashboard/src/components/playground/llm_calls/embeddings_api.test.tsx +++ b/ui/litellm-dashboard/src/components/playground/llm_calls/embeddings_api.test.tsx @@ -3,6 +3,7 @@ import { makeOpenAIEmbeddingsRequest } from "./embeddings_api"; vi.mock("@/components/networking", () => ({ getProxyBaseUrl: vi.fn(() => "https://example.com"), + getGlobalLitellmHeaderName: vi.fn(() => "Authorization"), })); describe("embeddings_api", () => { diff --git a/ui/litellm-dashboard/src/components/playground/llm_calls/embeddings_api.tsx b/ui/litellm-dashboard/src/components/playground/llm_calls/embeddings_api.tsx index 84192a1d866..cc3c0f81dc5 100644 --- a/ui/litellm-dashboard/src/components/playground/llm_calls/embeddings_api.tsx +++ b/ui/litellm-dashboard/src/components/playground/llm_calls/embeddings_api.tsx @@ -1,5 +1,5 @@ import NotificationManager from "@/components/molecules/notifications_manager"; -import { getProxyBaseUrl } from "@/components/networking"; +import { getProxyBaseUrl, getGlobalLitellmHeaderName } from "@/components/networking"; export async function makeOpenAIEmbeddingsRequest( input: string, @@ -34,7 +34,7 @@ export async function makeOpenAIEmbeddingsRequest( method: "POST", headers: { "Content-Type": "application/json", - Authorization: `Bearer ${accessToken}`, + [getGlobalLitellmHeaderName()]: `Bearer ${accessToken}`, ...headers, }, body: JSON.stringify({ diff --git a/ui/litellm-dashboard/src/components/playground/llm_calls/fetch_agents.tsx b/ui/litellm-dashboard/src/components/playground/llm_calls/fetch_agents.tsx index 889b012c5f7..f0f283c5252 100644 --- a/ui/litellm-dashboard/src/components/playground/llm_calls/fetch_agents.tsx +++ b/ui/litellm-dashboard/src/components/playground/llm_calls/fetch_agents.tsx @@ -1,6 +1,6 @@ // fetch_agents.tsx -import { getProxyBaseUrl } from "../../networking"; +import { getProxyBaseUrl, getGlobalLitellmHeaderName } from "../../networking"; export interface Agent { agent_id: string; @@ -27,7 +27,7 @@ export const fetchAvailableAgents = async ( const response = await fetch(url, { method: "GET", headers: { - Authorization: `Bearer ${accessToken}`, + [getGlobalLitellmHeaderName()]: `Bearer ${accessToken}`, "Content-Type": "application/json", }, }); diff --git a/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/conversation_panel/useConversation.ts b/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/conversation_panel/useConversation.ts index 4372622232e..7b595c777c1 100644 --- a/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/conversation_panel/useConversation.ts +++ b/ui/litellm-dashboard/src/components/prompts/prompt_editor_view/conversation_panel/useConversation.ts @@ -3,7 +3,7 @@ import NotificationsManager from "../../../molecules/notifications_manager"; import { TokenUsage } from "../../../playground/chat_ui/ResponseMetrics"; import { Message } from "./types"; import { convertToDotPrompt, extractVariables } from "../utils"; -import { getProxyBaseUrl } from "../../../networking"; +import { getProxyBaseUrl, getGlobalLitellmHeaderName } from "../../../networking"; export const useConversation = (prompt: any, accessToken: string | null) => { const [isLoading, setIsLoading] = useState(false); @@ -91,7 +91,7 @@ export const useConversation = (prompt: any, accessToken: string | null) => { const response = await fetch(`${proxyBaseUrl}/prompts/test`, { method: "POST", headers: { - Authorization: `Bearer ${accessToken}`, + [getGlobalLitellmHeaderName()]: `Bearer ${accessToken}`, "Content-Type": "application/json", }, body: JSON.stringify(requestBody), diff --git a/ui/litellm-dashboard/src/components/ui_theme_settings.tsx b/ui/litellm-dashboard/src/components/ui_theme_settings.tsx index 83c5cbb5c15..636163d1d70 100644 --- a/ui/litellm-dashboard/src/components/ui_theme_settings.tsx +++ b/ui/litellm-dashboard/src/components/ui_theme_settings.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from "react"; import { Card, Title, Text, TextInput, Button } from "@tremor/react"; import { useTheme } from "@/contexts/ThemeContext"; -import { getProxyBaseUrl } from "@/components/networking"; +import { getProxyBaseUrl, getGlobalLitellmHeaderName } from "@/components/networking"; import NotificationsManager from "./molecules/notifications_manager"; interface UIThemeSettingsProps { @@ -29,7 +29,7 @@ const UIThemeSettings: React.FC = ({ userID, userRole, acc const response = await fetch(url, { method: "GET", headers: { - Authorization: `Bearer ${accessToken}`, + [getGlobalLitellmHeaderName()]: `Bearer ${accessToken}`, "Content-Type": "application/json", }, }); @@ -53,7 +53,7 @@ const UIThemeSettings: React.FC = ({ userID, userRole, acc const response = await fetch(url, { method: "PATCH", headers: { - Authorization: `Bearer ${accessToken}`, + [getGlobalLitellmHeaderName()]: `Bearer ${accessToken}`, "Content-Type": "application/json", }, body: JSON.stringify({ @@ -87,7 +87,7 @@ const UIThemeSettings: React.FC = ({ userID, userRole, acc const response = await fetch(url, { method: "PATCH", headers: { - Authorization: `Bearer ${accessToken}`, + [getGlobalLitellmHeaderName()]: `Bearer ${accessToken}`, "Content-Type": "application/json", }, body: JSON.stringify({ From 5a068686524295f2171e3f79abda157b9e02095a Mon Sep 17 00:00:00 2001 From: Alexsander Hamir Date: Tue, 20 Jan 2026 12:17:06 -0800 Subject: [PATCH 02/16] Fix in-flight request termination on SIGTERM when health-check runs in a separate process (#19427) --- docker/prod_entrypoint.sh | 1 + docker/supervisord.conf | 2 ++ docs/my-website/docs/proxy/config_settings.md | 1 + docs/my-website/docs/proxy/prod.md | 5 +++++ 4 files changed, 9 insertions(+) diff --git a/docker/prod_entrypoint.sh b/docker/prod_entrypoint.sh index 1fc09d2c864..28d1bdcc294 100644 --- a/docker/prod_entrypoint.sh +++ b/docker/prod_entrypoint.sh @@ -2,6 +2,7 @@ if [ "$SEPARATE_HEALTH_APP" = "1" ]; then export LITELLM_ARGS="$@" + export SUPERVISORD_STOPWAITSECS="${SUPERVISORD_STOPWAITSECS:-3600}" exec supervisord -c /etc/supervisord.conf fi diff --git a/docker/supervisord.conf b/docker/supervisord.conf index 877335804fe..ba9d99d18a5 100644 --- a/docker/supervisord.conf +++ b/docker/supervisord.conf @@ -16,6 +16,7 @@ priority=1 exitcodes=0 stopasgroup=true killasgroup=true +stopwaitsecs=%(ENV_SUPERVISORD_STOPWAITSECS)s stdout_logfile=/dev/stdout stderr_logfile=/dev/stderr stdout_logfile_maxbytes = 0 @@ -31,6 +32,7 @@ priority=2 exitcodes=0 stopasgroup=true killasgroup=true +stopwaitsecs=%(ENV_SUPERVISORD_STOPWAITSECS)s stdout_logfile=/dev/stdout stderr_logfile=/dev/stderr stdout_logfile_maxbytes = 0 diff --git a/docs/my-website/docs/proxy/config_settings.md b/docs/my-website/docs/proxy/config_settings.md index 53b0f3eea71..f76fd214682 100644 --- a/docs/my-website/docs/proxy/config_settings.md +++ b/docs/my-website/docs/proxy/config_settings.md @@ -875,6 +875,7 @@ router_settings: | SECRET_MANAGER_REFRESH_INTERVAL | Refresh interval in seconds for secret manager. Default is 86400 (24 hours) | SEPARATE_HEALTH_APP | If set to '1', runs health endpoints on a separate ASGI app and port. Default: '0'. | SEPARATE_HEALTH_PORT | Port for the separate health endpoints app. Only used if SEPARATE_HEALTH_APP=1. Default: 4001. +| SUPERVISORD_STOPWAITSECS | Upper bound timeout in seconds for graceful shutdown when SEPARATE_HEALTH_APP=1. Default: 3600 (1 hour). | SERVER_ROOT_PATH | Root path for the server application | SEND_USER_API_KEY_ALIAS | Flag to send user API key alias to Zscaler AI Guard. Default is False | SEND_USER_API_KEY_TEAM_ID | Flag to send user API key team ID to Zscaler AI Guard. Default is False diff --git a/docs/my-website/docs/proxy/prod.md b/docs/my-website/docs/proxy/prod.md index 9216b0fbf30..a42d91a7d5f 100644 --- a/docs/my-website/docs/proxy/prod.md +++ b/docs/my-website/docs/proxy/prod.md @@ -277,8 +277,13 @@ Set the following environment variable(s): ```bash SEPARATE_HEALTH_APP="1" # Default "0" SEPARATE_HEALTH_PORT="8001" # Default "4001", Works only if `SEPARATE_HEALTH_APP` is "1" +SUPERVISORD_STOPWAITSECS="3600" # Optional: Upper bound timeout in seconds for graceful shutdown. Default: 3600 (1 hour). Only used when SEPARATE_HEALTH_APP=1. ``` +**Graceful Shutdown:** + +Previously, `stopwaitsecs` was not set, defaulting to 10 seconds and causing in-flight requests to fail. `SUPERVISORD_STOPWAITSECS` (default: 3600) provides an upper bound for graceful shutdown, allowing uvicorn to wait for all in-flight requests to complete. +