From 86b2efd67a95933eee4f7d2b307dc2849222a01b Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Thu, 26 Feb 2026 18:17:37 -0800 Subject: [PATCH 1/8] [Feature] Add /public/endpoints endpoint for provider endpoint support Add new /public/endpoints endpoint that returns which providers support each LiteLLM endpoint (e.g., chat_completions, embeddings). The endpoint reads from a local backup JSON file bundled with the package, caches the result in-process, and transforms the raw provider-centric data into an endpoint-centric response format. Changes: - Add litellm/provider_endpoints_support_backup.json (copy of root source file) - Add Pydantic response models (EndpointProvider, SupportedEndpoint, SupportedEndpointsResponse) - Add /public/endpoints route with transformation and caching logic - Add 16 comprehensive tests covering HTTP layer and transformation functions Co-Authored-By: Claude Haiku 4.5 --- .../provider_endpoints_support_backup.json | 2748 +++++++++++++++++ .../public_endpoints/public_endpoints.py | 116 +- .../public_endpoints/public_endpoints.py | 16 + .../public_endpoints/test_public_endpoints.py | 161 + 4 files changed, 3035 insertions(+), 6 deletions(-) create mode 100644 litellm/provider_endpoints_support_backup.json diff --git a/litellm/provider_endpoints_support_backup.json b/litellm/provider_endpoints_support_backup.json new file mode 100644 index 0000000000..8834d8b19c --- /dev/null +++ b/litellm/provider_endpoints_support_backup.json @@ -0,0 +1,2748 @@ +{ + "_comment": "This file defines which endpoints are supported by each LiteLLM provider", + "_schema": { + "provider_slug": { + "display_name": "Display name shown in README (e.g., 'OpenAI (`openai`)')", + "url": "Link to provider documentation", + "endpoints": { + "chat_completions": "Supports /chat/completions endpoint", + "messages": "Supports /messages endpoint (Anthropic format)", + "responses": "Supports /responses endpoint (OpenAI/Anthropic unified)", + "embeddings": "Supports /embeddings endpoint", + "image_generations": "Supports /image/generations endpoint", + "audio_transcriptions": "Supports /audio/transcriptions endpoint", + "audio_speech": "Supports /audio/speech endpoint", + "moderations": "Supports /moderations endpoint", + "batches": "Supports /batches endpoint", + "rerank": "Supports /rerank endpoint", + "ocr": "Supports /ocr endpoint", + "search": "Supports /search endpoint", + "skills": "Supports /skills endpoint", + "interactions": "Supports /interactions endpoint (Google AI Interactions API)", + "a2a_(Agent Gateway)": "Supports /a2a/{agent}/message/send endpoint (A2A Protocol)", + "container": "Supports OpenAI's /containers endpoint", + "container_file": "Supports OpenAI's /containers/{id}/files endpoint", + "compact": "Supports /responses/compact endpoint", + "files": "Supports /files endpoint for file operations", + "image_edits": "Supports /images/edits endpoint for image editing", + "vector_stores_create": "Supports creating a new vector store via /vector_stores endpoint", + "vector_stores_search": "Supports searching a vector store via /vector_stores/{id}/search endpoint", + "video_generations": "Supports /videos/generations endpoint for video generation" + } + } + }, + "providers": { + "a2a": { + "display_name": "A2A (Agent-to-Agent) (`a2a`)", + "url": "https://docs.litellm.ai/docs/providers/a2a", + "endpoints": { + "chat_completions": true, + "messages": false, + "responses": false, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": false + } + }, + "abliteration": { + "display_name": "Abliteration (`abliteration`)", + "url": "https://docs.litellm.ai/docs/providers/abliteration", + "endpoints": { + "chat_completions": true, + "messages": false, + "responses": false, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": false + } + }, + "aiml": { + "display_name": "AI/ML API (`aiml`)", + "url": "https://docs.litellm.ai/docs/providers/aiml", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": true, + "image_generations": true, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "ai21": { + "display_name": "AI21 (`ai21`)", + "url": "https://docs.litellm.ai/docs/providers/ai21", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "ai21_chat": { + "display_name": "AI21 Chat (`ai21_chat`)", + "url": "https://docs.litellm.ai/docs/providers/ai21", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "amazon_nova": { + "display_name": "Amazon Nova (`amazon_nova`)", + "url": "https://docs.litellm.ai/docs/providers/amazon_nova", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "anthropic": { + "display_name": "Anthropic (`anthropic`)", + "url": "https://docs.litellm.ai/docs/providers/anthropic", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": true, + "rerank": false, + "skills": true, + "a2a": true, + "interactions": true, + "count_tokens": true + } + }, + "anthropic_text": { + "display_name": "Anthropic Text (`anthropic_text`)", + "url": "https://docs.litellm.ai/docs/providers/anthropic", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": true, + "rerank": false, + "skills": true, + "a2a": true, + "interactions": true + } + }, + "apertis": { + "display_name": "Apertis (`apertis`)", + "endpoints": { + "chat_completions": true, + "messages": false, + "responses": false, + "embeddings": true, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": false + } + }, + "assemblyai": { + "display_name": "AssemblyAI (`assemblyai`)", + "url": "https://docs.litellm.ai/docs/pass_through/assembly_ai", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": true, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "auto_router": { + "display_name": "Auto Router (`auto_router`)", + "url": "https://docs.litellm.ai/docs/proxy/auto_routing", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "bedrock": { + "display_name": "AWS - Bedrock (`bedrock`)", + "url": "https://docs.litellm.ai/docs/providers/bedrock", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": true, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": true, + "a2a": true, + "interactions": true, + "bedrock_invoke": true, + "bedrock_converse": true, + "vector_stores_search": true, + "count_tokens": true, + "rag_ingest": true, + "rag_query": true + } + }, + "s3_vectors": { + "display_name": "AWS S3 Vectors (`s3_vectors`)", + "url": "https://docs.litellm.ai/docs/providers/s3_vectors", + "endpoints": { + "chat_completions": false, + "messages": false, + "responses": false, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": false, + "interactions": false, + "vector_stores_create": true, + "vector_stores_search": true + } + }, + "sagemaker": { + "display_name": "AWS - Sagemaker (`sagemaker`)", + "url": "https://docs.litellm.ai/docs/providers/aws_sagemaker", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": true, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "aws_polly": { + "display_name": "AWS - Polly (`aws_polly`)", + "url": "https://docs.litellm.ai/docs/providers/aws_polly", + "endpoints": { + "chat_completions": false, + "messages": false, + "responses": false, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": true, + "moderations": false, + "batches": false, + "rerank": false + } + }, + "azure": { + "display_name": "Azure (`azure`)", + "url": "https://docs.litellm.ai/docs/providers/azure", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": true, + "image_generations": true, + "audio_transcriptions": true, + "audio_speech": true, + "moderations": true, + "batches": true, + "rerank": false, + "a2a": true, + "interactions": true, + "vector_stores_search": true, + "assistants": true, + "fine_tuning": true, + "text_completion": true + } + }, + "azure_ai": { + "display_name": "Azure AI (`azure_ai`)", + "url": "https://docs.litellm.ai/docs/providers/azure_ai", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": true, + "image_generations": true, + "image_edits": true, + "audio_transcriptions": true, + "audio_speech": true, + "moderations": true, + "batches": true, + "rerank": false, + "ocr": true, + "a2a": true, + "interactions": true, + "vector_stores_create": true, + "vector_stores_search": true + } + }, + "azure_ai/doc-intelligence": { + "display_name": "Azure AI Document Intelligence (`azure_ai/doc-intelligence`)", + "url": "https://docs.litellm.ai/docs/providers/azure_document_intelligence", + "endpoints": { + "chat_completions": false, + "messages": false, + "responses": false, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "ocr": true + } + }, + "azure_ai/agents": { + "display_name": "Azure AI Foundry Agents (`azure_ai/agents`)", + "url": "https://docs.litellm.ai/docs/providers/azure_ai_agents", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "azure_text": { + "display_name": "Azure Text (`azure_text`)", + "url": "https://docs.litellm.ai/docs/providers/azure", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": true, + "audio_speech": true, + "moderations": true, + "batches": true, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "baseten": { + "display_name": "Baseten (`baseten`)", + "url": "https://docs.litellm.ai/docs/providers/baseten", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "bytez": { + "display_name": "Bytez (`bytez`)", + "url": "https://docs.litellm.ai/docs/providers/bytez", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "cerebras": { + "display_name": "Cerebras (`cerebras`)", + "url": "https://docs.litellm.ai/docs/providers/cerebras", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "chutes": { + "display_name": "Chutes (`chutes`)", + "endpoints": { + "chat_completions": true, + "messages": false, + "responses": false, + "embeddings": true, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": false + } + }, + "clarifai": { + "display_name": "Clarifai (`clarifai`)", + "url": "https://docs.litellm.ai/docs/providers/clarifai", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "cloudflare": { + "display_name": "Cloudflare AI Workers (`cloudflare`)", + "url": "https://docs.litellm.ai/docs/providers/cloudflare_workers", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "codestral": { + "display_name": "Codestral (`codestral`)", + "url": "https://docs.litellm.ai/docs/providers/codestral", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "cohere": { + "display_name": "Cohere (`cohere`)", + "url": "https://docs.litellm.ai/docs/providers/cohere", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": true, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": true, + "a2a": true, + "interactions": true + } + }, + "cohere_chat": { + "display_name": "Cohere Chat (`cohere_chat`)", + "url": "https://docs.litellm.ai/docs/providers/cohere", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "cometapi": { + "display_name": "CometAPI (`cometapi`)", + "url": "https://docs.litellm.ai/docs/providers/cometapi", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": true, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "compactifai": { + "display_name": "CompactifAI (`compactifai`)", + "url": "https://docs.litellm.ai/docs/providers/compactifai", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "custom": { + "display_name": "Custom (`custom`)", + "url": "https://docs.litellm.ai/docs/providers/custom_llm_server", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "custom_openai": { + "display_name": "Custom OpenAI (`custom_openai`)", + "url": "https://docs.litellm.ai/docs/providers/openai_compatible", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": true, + "audio_speech": true, + "moderations": true, + "batches": true, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "dashscope": { + "display_name": "Dashscope (`dashscope`)", + "url": "https://docs.litellm.ai/docs/providers/dashscope", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "databricks": { + "display_name": "Databricks (`databricks`)", + "url": "https://docs.litellm.ai/docs/providers/databricks", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "dataforseo": { + "display_name": "DataForSEO (`dataforseo`)", + "url": "https://docs.litellm.ai/docs/search/dataforseo", + "endpoints": { + "chat_completions": false, + "messages": false, + "responses": false, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "search": true + } + }, + "datarobot": { + "display_name": "DataRobot (`datarobot`)", + "url": "https://docs.litellm.ai/docs/providers/datarobot", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "deepgram": { + "display_name": "Deepgram (`deepgram`)", + "url": "https://docs.litellm.ai/docs/providers/deepgram", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": true, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "deepinfra": { + "display_name": "DeepInfra (`deepinfra`)", + "url": "https://docs.litellm.ai/docs/providers/deepinfra", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "deepseek": { + "display_name": "Deepseek (`deepseek`)", + "url": "https://docs.litellm.ai/docs/providers/deepseek", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "duckduckgo": { + "display_name": "DuckDuckGo (`duckduckgo`)", + "url": "https://docs.litellm.ai/docs/search/duckduckgo", + "endpoints": { + "chat_completions": false, + "messages": false, + "responses": false, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "search": true + } + }, + "elevenlabs": { + "display_name": "ElevenLabs (`elevenlabs`)", + "url": "https://docs.litellm.ai/docs/providers/elevenlabs", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": true, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "exa_ai": { + "display_name": "Exa AI (`exa_ai`)", + "url": "https://docs.litellm.ai/docs/search/exa_ai", + "endpoints": { + "chat_completions": false, + "messages": false, + "responses": false, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "search": true + } + }, + "brave": { + "display_name": "Brave Search (`brave`)", + "url": "https://docs.litellm.ai/docs/search/brave", + "endpoints": { + "chat_completions": false, + "messages": false, + "responses": false, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "search": true + } + }, + "empower": { + "display_name": "Empower (`empower`)", + "url": "https://docs.litellm.ai/docs/providers/empower", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "fal_ai": { + "display_name": "Fal AI (`fal_ai`)", + "url": "https://docs.litellm.ai/docs/providers/fal_ai", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": true, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "featherless_ai": { + "display_name": "Featherless AI (`featherless_ai`)", + "url": "https://docs.litellm.ai/docs/providers/featherless_ai", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "fireworks_ai": { + "display_name": "Fireworks AI (`fireworks_ai`)", + "url": "https://docs.litellm.ai/docs/providers/fireworks_ai", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": true, + "a2a": true, + "interactions": true + } + }, + "firecrawl": { + "display_name": "Firecrawl (`firecrawl`)", + "url": "https://docs.litellm.ai/docs/search/firecrawl", + "endpoints": { + "chat_completions": false, + "messages": false, + "responses": false, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "search": true + } + }, + "linkup": { + "display_name": "Linkup (`linkup`)", + "url": "https://docs.litellm.ai/docs/search/linkup", + "endpoints": { + "chat_completions": false, + "messages": false, + "responses": false, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "search": true + } + }, + "friendliai": { + "display_name": "FriendliAI (`friendliai`)", + "url": "https://docs.litellm.ai/docs/providers/friendliai", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "galadriel": { + "display_name": "Galadriel (`galadriel`)", + "url": "https://docs.litellm.ai/docs/providers/galadriel", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "github_copilot": { + "display_name": "GitHub Copilot (`github_copilot`)", + "url": "https://docs.litellm.ai/docs/providers/github_copilot", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "chatgpt": { + "display_name": "ChatGPT Subscription (`chatgpt`)", + "url": "https://docs.litellm.ai/docs/providers/chatgpt", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": false, + "interactions": false + } + }, + "github": { + "display_name": "GitHub Models (`github`)", + "url": "https://docs.litellm.ai/docs/providers/github", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "gmi": { + "display_name": "GMI Cloud (`gmi`)", + "url": "https://docs.litellm.ai/docs/providers/gmi_cloud", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "vertex_ai": { + "display_name": "Google - Vertex AI (`vertex_ai`)", + "url": "https://docs.litellm.ai/docs/providers/vertex", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": true, + "image_generations": true, + "audio_transcriptions": false, + "audio_speech": true, + "moderations": false, + "batches": false, + "rerank": false, + "ocr": true, + "a2a": true, + "interactions": true, + "vector_stores_search": true, + "count_tokens": true, + "fine_tuning": true, + "rag_ingest": true, + "rag_query": true, + "generateContent": true, + "realtime": true + } + }, + "gemini": { + "display_name": "Google AI Studio - Gemini (`gemini`)", + "url": "https://docs.litellm.ai/docs/providers/gemini", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "interactions": true, + "a2a": true, + "vector_stores_search": true, + "count_tokens": true, + "rag_ingest": true, + "realtime": true, + "generateContent": true + } + }, + "gradient_ai": { + "display_name": "GradientAI (`gradient_ai`)", + "url": "https://docs.litellm.ai/docs/providers/gradient_ai", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "groq": { + "display_name": "Groq AI (`groq`)", + "url": "https://docs.litellm.ai/docs/providers/groq", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "heroku": { + "display_name": "Heroku (`heroku`)", + "url": "https://docs.litellm.ai/docs/providers/heroku", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "hosted_vllm": { + "display_name": "Hosted VLLM (`hosted_vllm`)", + "url": "https://docs.litellm.ai/docs/providers/vllm", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": true, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": true, + "files": true, + "rerank": true, + "a2a": true, + "interactions": true + } + }, + "huggingface": { + "display_name": "Huggingface (`huggingface`)", + "url": "https://docs.litellm.ai/docs/providers/huggingface", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": true, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": true, + "a2a": true, + "interactions": true + } + }, + "hyperbolic": { + "display_name": "Hyperbolic (`hyperbolic`)", + "url": "https://docs.litellm.ai/docs/providers/hyperbolic", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "watsonx": { + "display_name": "IBM - Watsonx.ai (`watsonx`)", + "url": "https://docs.litellm.ai/docs/providers/watsonx", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": true, + "image_generations": false, + "audio_transcriptions": true, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "infinity": { + "display_name": "Infinity (`infinity`)", + "url": "https://docs.litellm.ai/docs/providers/infinity", + "endpoints": { + "chat_completions": false, + "messages": false, + "responses": false, + "embeddings": true, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false + } + }, + "jina_ai": { + "display_name": "Jina AI (`jina_ai`)", + "url": "https://docs.litellm.ai/docs/providers/jina_ai", + "endpoints": { + "chat_completions": false, + "messages": false, + "responses": false, + "embeddings": true, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false + } + }, + "lambda_ai": { + "display_name": "Lambda AI (`lambda_ai`)", + "url": "https://docs.litellm.ai/docs/providers/lambda_ai", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "lemonade": { + "display_name": "Lemonade (`lemonade`)", + "url": "https://docs.litellm.ai/docs/providers/lemonade", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "litellm_proxy": { + "display_name": "LiteLLM Proxy (`litellm_proxy`)", + "url": "https://docs.litellm.ai/docs/providers/litellm_proxy", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": true, + "image_generations": true, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "llamafile": { + "display_name": "Llamafile (`llamafile`)", + "url": "https://docs.litellm.ai/docs/providers/llamafile", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "lm_studio": { + "display_name": "LM Studio (`lm_studio`)", + "url": "https://docs.litellm.ai/docs/providers/lm_studio", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "maritalk": { + "display_name": "Maritalk (`maritalk`)", + "url": "https://docs.litellm.ai/docs/providers/maritalk", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "meta_llama": { + "display_name": "Meta - Llama API (`meta_llama`)", + "url": "https://docs.litellm.ai/docs/providers/meta_llama", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "mistral": { + "display_name": "Mistral AI API (`mistral`)", + "url": "https://docs.litellm.ai/docs/providers/mistral", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": true, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "ocr": true, + "a2a": true, + "interactions": true + } + }, + "moonshot": { + "display_name": "Moonshot (`moonshot`)", + "url": "https://docs.litellm.ai/docs/providers/moonshot", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "docker_model_runner": { + "display_name": "Docker Model Runner (`docker_model_runner`)", + "url": "https://docs.litellm.ai/docs/providers/docker_model_runner", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "morph": { + "display_name": "Morph (`morph`)", + "url": "https://docs.litellm.ai/docs/providers/morph", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "nanogpt": { + "display_name": "NanoGPT (`nanogpt`)", + "endpoints": { + "chat_completions": true, + "messages": false, + "responses": false, + "embeddings": true, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": false + } + }, + "nebius": { + "display_name": "Nebius AI Studio (`nebius`)", + "url": "https://docs.litellm.ai/docs/providers/nebius", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": true, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "nlp_cloud": { + "display_name": "NLP Cloud (`nlp_cloud`)", + "url": "https://docs.litellm.ai/docs/providers/nlp_cloud", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "novita": { + "display_name": "Novita AI (`novita`)", + "url": "https://novita.ai/models/llm?utm_source=github_litellm&utm_medium=github_readme&utm_campaign=github_link", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "nscale": { + "display_name": "Nscale (`nscale`)", + "url": "https://docs.litellm.ai/docs/providers/nscale", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "nvidia_nim": { + "display_name": "Nvidia NIM (`nvidia_nim`)", + "url": "https://docs.litellm.ai/docs/providers/nvidia_nim", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "oci": { + "display_name": "OCI (`oci`)", + "url": "https://docs.litellm.ai/docs/providers/oci", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "ollama": { + "display_name": "Ollama (`ollama`)", + "url": "https://docs.litellm.ai/docs/providers/ollama", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": true, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "ollama_chat": { + "display_name": "Ollama Chat (`ollama_chat`)", + "url": "https://docs.litellm.ai/docs/providers/ollama", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "oobabooga": { + "display_name": "Oobabooga (`oobabooga`)", + "url": "https://docs.litellm.ai/docs/providers/openai_compatible", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": true, + "audio_speech": true, + "moderations": true, + "batches": true, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "openai": { + "display_name": "OpenAI (`openai`)", + "url": "https://docs.litellm.ai/docs/providers/openai", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": true, + "image_generations": true, + "audio_transcriptions": true, + "audio_speech": true, + "moderations": true, + "batches": true, + "rerank": false, + "container": true, + "compact": true, + "a2a": true, + "interactions": true, + "vector_store_files": true, + "vector_stores_create": true, + "vector_stores_search": true, + "assistants": true, + "container_files": true, + "fine_tuning": true, + "image_variations": true, + "rag_ingest": true, + "rag_query": true, + "realtime": true, + "text_completion": true + } + }, + "openai_like": { + "display_name": "OpenAI-like (`openai_like`)", + "url": "https://docs.litellm.ai/docs/providers/openai_compatible", + "endpoints": { + "chat_completions": false, + "messages": false, + "responses": false, + "embeddings": true, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "assistants": true + } + }, + "openrouter": { + "display_name": "OpenRouter (`openrouter`)", + "url": "https://docs.litellm.ai/docs/providers/openrouter", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": true, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "ovhcloud": { + "display_name": "OVHCloud AI Endpoints (`ovhcloud`)", + "url": "https://docs.litellm.ai/docs/providers/ovhcloud", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": true, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "parallel_ai": { + "display_name": "Parallel AI (`parallel_ai`)", + "url": "https://docs.litellm.ai/docs/search/parallel_ai", + "endpoints": { + "chat_completions": false, + "messages": false, + "responses": false, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "search": true + } + }, + "perplexity": { + "display_name": "Perplexity AI (`perplexity`)", + "url": "https://docs.litellm.ai/docs/providers/perplexity", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "search": true, + "a2a": true, + "interactions": true + } + }, + "petals": { + "display_name": "Petals (`petals`)", + "url": "https://docs.litellm.ai/docs/providers/petals", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "poe": { + "display_name": "Poe (`poe`)", + "endpoints": { + "chat_completions": true, + "messages": false, + "responses": false, + "embeddings": true, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": false + } + }, + "publicai": { + "display_name": "PublicAI (`publicai`)", + "url": "https://docs.litellm.ai/docs/providers/publicai", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "predibase": { + "display_name": "Predibase (`predibase`)", + "url": "https://docs.litellm.ai/docs/providers/predibase", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "recraft": { + "display_name": "Recraft (`recraft`)", + "url": "https://docs.litellm.ai/docs/providers/recraft", + "endpoints": { + "chat_completions": false, + "messages": false, + "responses": false, + "embeddings": false, + "image_generations": true, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false + } + }, + "replicate": { + "display_name": "Replicate (`replicate`)", + "url": "https://docs.litellm.ai/docs/providers/replicate", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "runwayml": { + "display_name": "RunwayML (`runwayml`)", + "url": "https://docs.litellm.ai/docs/providers/runwayml/videos", + "endpoints": { + "chat_completions": false, + "messages": false, + "responses": false, + "embeddings": false, + "image_generations": true, + "audio_transcriptions": false, + "audio_speech": true, + "moderations": false, + "batches": false, + "rerank": false, + "video_generations": true + } + }, + "sagemaker_chat": { + "display_name": "Sagemaker Chat (`sagemaker_chat`)", + "url": "https://docs.litellm.ai/docs/providers/aws_sagemaker", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "searxng": { + "display_name": "SearXNG (`searxng`)", + "url": "https://docs.litellm.ai/docs/search/searxng", + "endpoints": { + "chat_completions": false, + "messages": false, + "responses": false, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "search": true + } + }, + "sambanova": { + "display_name": "Sambanova (`sambanova`)", + "url": "https://docs.litellm.ai/docs/providers/sambanova", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "sap": { + "display_name": "SAP Generative AI Hub (`sap`)", + "url": "https://docs.litellm.ai/docs/providers/sap", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "scaleway": { + "display_name": "Scaleway (`scaleway`)", + "url": "https://docs.litellm.ai/docs/providers/scaleway", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "snowflake": { + "display_name": "Snowflake (`snowflake`)", + "url": "https://docs.litellm.ai/docs/providers/snowflake", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "synthetic": { + "display_name": "Synthetic (`synthetic`)", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": true, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": false + } + }, + "text-completion-codestral": { + "display_name": "Text Completion Codestral (`text-completion-codestral`)", + "url": "https://docs.litellm.ai/docs/providers/codestral", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "text-completion-openai": { + "display_name": "Text Completion OpenAI (`text-completion-openai`)", + "url": "https://docs.litellm.ai/docs/providers/text_completion_openai", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": true, + "audio_speech": true, + "moderations": true, + "batches": true, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "together_ai": { + "display_name": "Together AI (`together_ai`)", + "url": "https://docs.litellm.ai/docs/providers/togetherai", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "topaz": { + "display_name": "Topaz (`topaz`)", + "url": "https://docs.litellm.ai/docs/providers/topaz", + "endpoints": { + "image_variations": true + } + }, + "tavily": { + "display_name": "Tavily (`tavily`)", + "url": "https://docs.litellm.ai/docs/search/tavily", + "endpoints": { + "search": true + } + }, + "triton": { + "display_name": "Triton (`triton`)", + "url": "https://docs.litellm.ai/docs/providers/triton-inference-server", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "v0": { + "display_name": "V0 (`v0`)", + "url": "https://docs.litellm.ai/docs/providers/v0", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "vercel_ai_gateway": { + "display_name": "Vercel AI Gateway (`vercel_ai_gateway`)", + "url": "https://docs.litellm.ai/docs/providers/vercel_ai_gateway", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "vllm": { + "display_name": "VLLM (`vllm`)", + "url": "https://docs.litellm.ai/docs/providers/vllm", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": true, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": true, + "files": true, + "rerank": true, + "a2a": true, + "interactions": true + } + }, + "volcengine": { + "display_name": "Volcengine (`volcengine`)", + "url": "https://docs.litellm.ai/docs/providers/volcano", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "voyage": { + "display_name": "Voyage AI (`voyage`)", + "url": "https://docs.litellm.ai/docs/providers/voyage", + "endpoints": { + "chat_completions": false, + "messages": false, + "responses": false, + "embeddings": true, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": true + } + }, + "wandb": { + "display_name": "WandB Inference (`wandb`)", + "url": "https://docs.litellm.ai/docs/providers/wandb_inference", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "watsonx_text": { + "display_name": "Watsonx Text (`watsonx_text`)", + "url": "https://docs.litellm.ai/docs/providers/watsonx", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "xai": { + "display_name": "xAI (`xai`)", + "url": "https://docs.litellm.ai/docs/providers/xai", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true, + "realtime": true + } + }, + "xinference": { + "display_name": "Xinference (`xinference`)", + "url": "https://docs.litellm.ai/docs/providers/xinference", + "endpoints": { + "chat_completions": false, + "messages": false, + "responses": false, + "embeddings": true, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false + } + }, + "zai": { + "display_name": "Z.AI (Zhipu AI) (`zai`)", + "url": "https://docs.litellm.ai/docs/providers/zai", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "ragflow": { + "display_name": "RAGFlow (`ragflow`)", + "url": "https://docs.litellm.ai/docs/providers/ragflow", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "vector_stores_create": true, + "a2a": true, + "interactions": true + } + }, + "cursor": { + "display_name": "Cursor BYOK (`cursor`)", + "url": "https://docs.litellm.ai/docs/providers/cursor", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "langgraph": { + "display_name": "LangGraph (`langgraph`)", + "url": "https://docs.litellm.ai/docs/providers/langgraph", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "vertex_ai/agent_engine": { + "display_name": "Vertex AI Agent Engine (`vertex_ai/agent_engine`)", + "url": "https://docs.litellm.ai/docs/providers/vertex_ai_agent_engine", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "pydantic_ai_agents": { + "display_name": "Pydantic AI Agents (`pydantic_ai_agents`)", + "url": "https://docs.litellm.ai/docs/providers/pydantic_ai_agent", + "endpoints": { + "chat_completions": false, + "messages": false, + "responses": false, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true + } + }, + "stability": { + "display_name": "Stability AI (`stability`)", + "url": "https://docs.litellm.ai/docs/providers/stability", + "endpoints": { + "chat_completions": false, + "messages": false, + "responses": false, + "embeddings": false, + "image_generations": true, + "image_edits": true, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false + } + }, + "venice": { + "display_name": "Venice.ai (`venice`)", + "url": "https://docs.litellm.ai/docs/providers/venice", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "gigachat": { + "display_name": "GigaChat (`gigachat`)", + "url": "https://docs.litellm.ai/docs/providers/gigachat", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": true + } + }, + "google_pse": { + "display_name": "Google PSE (`google_pse`)", + "url": "https://docs.litellm.ai/docs/search/google_pse", + "endpoints": { + "search": true + } + }, + "milvus": { + "display_name": "Milvus (`milvus`)", + "url": "https://docs.litellm.ai/docs/providers/milvus_vector_stores", + "endpoints": { + "vector_stores_search": true + } + }, + "minimax": { + "display_name": "Minimax (`minimax`)", + "url": "https://docs.litellm.ai/docs/providers/minimax", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true + } + }, + "pg_vector": { + "display_name": "PG Vector (`pg_vector`)", + "url": "https://docs.litellm.ai/docs/providers/pg_vector", + "endpoints": { + "vector_stores_search": true + } + }, + "helicone": { + "display_name": "Helicone (`helicone`)", + "url": "https://docs.litellm.ai/docs/providers/helicone", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true + } + }, + "llamagate": { + "display_name": "LlamaGate (`llamagate`)", + "url": "https://docs.litellm.ai/docs/providers/llamagate", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true + } + }, + "xiaomi_mimo": { + "display_name": "Xiaomi Mimo (`xiaomi_mimo`)", + "url": "https://docs.litellm.ai/docs/providers/xiaomi_mimo", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true + } + }, + "manus": { + "display_name": "Manus (`manus`)", + "url": "https://docs.litellm.ai/docs/providers/manus", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "files": true, + "rerank": false, + "a2a": true, + "interactions": true + } + }, + "sarvam": { + "display_name": "Sarvam (`sarvam`)", + "url": "https://docs.litellm.ai/docs/providers/sarvam", + "endpoints": { + "chat_completions": true, + "messages": true, + "responses": true + } + } + }, + "endpoints": { + "a2a": { + "docs_label": "a2a", + "display_name": "A2A (Agent-to-Agent) protocol for agent communication", + "leftnav_label": "/a2a", + "provider_json_field": "a2a", + "url": "https://docs.litellm.ai/docs/a2a", + "bridges_to_chat_completion": true + }, + "messages": { + "docs_label": "anthropic_unified", + "display_name": "Anthropic Messages API", + "leftnav_label": "/messages", + "provider_json_field": "messages", + "url": "https://docs.litellm.ai/docs/anthropic_unified", + "bridges_to_chat_completion": true + }, + "anthropic_count_tokens": { + "docs_label": "anthropic_count_tokens", + "display_name": "Anthropic Count Tokens API", + "leftnav_label": "/count_tokens", + "provider_json_field": "count_tokens", + "url": "https://docs.litellm.ai/docs/anthropic_count_tokens" + }, + "apply_guardrail": { + "docs_label": "apply_guardrail", + "display_name": "Unified Apply Guardrail API", + "leftnav_label": "/guardrails/apply_guardrail", + "provider_json_field": "apply_guardrail", + "url": "https://docs.litellm.ai/docs/apply_guardrail" + }, + "assistants": { + "docs_label": "assistants", + "display_name": "OpenAI Assistants API", + "leftnav_label": "/assistants", + "provider_json_field": "assistants", + "url": "https://docs.litellm.ai/docs/assistants" + }, + "audio_transcription": { + "docs_label": "audio_transcription", + "display_name": "OpenAI Audio Transcription API", + "leftnav_label": "/audio/transcriptions", + "provider_json_field": "audio_transcriptions", + "url": "https://docs.litellm.ai/docs/audio_transcription" + }, + "batches": { + "docs_label": "batches", + "display_name": "OpenAI Batches API", + "leftnav_label": "/batches", + "provider_json_field": "batches", + "url": "https://docs.litellm.ai/docs/batches" + }, + "bedrock_invoke": { + "docs_label": "bedrock_invoke", + "display_name": "Bedrock Invoke API", + "leftnav_label": "/invoke", + "provider_json_field": "bedrock_invoke", + "url": "https://docs.litellm.ai/docs/bedrock_invoke" + }, + "bedrock_converse": { + "docs_label": "bedrock_converse", + "display_name": "Bedrock Converse API", + "leftnav_label": "/converse", + "provider_json_field": "bedrock_converse", + "url": "https://docs.litellm.ai/docs/bedrock_converse" + }, + "chat_completions": { + "docs_label": "chat_completions", + "display_name": "OpenAI Chat Completions API", + "leftnav_label": "/chat/completions", + "provider_json_field": "chat_completions", + "url": "https://docs.litellm.ai/docs/chat_completions" + }, + "container_files": { + "docs_label": "container_files", + "display_name": "OpenAI Container Files API", + "leftnav_label": "/create/container/files", + "provider_json_field": "container_files", + "url": "https://docs.litellm.ai/docs/container_files" + }, + "container": { + "docs_label": "containers", + "display_name": "OpenAI Containers API", + "leftnav_label": "/container", + "provider_json_field": "container", + "url": "https://docs.litellm.ai/docs/containers" + }, + "embeddings": { + "docs_label": "embedding/supported_embedding", + "display_name": "OpenAI Embeddings API", + "leftnav_label": "/embeddings", + "provider_json_field": "embeddings", + "url": "https://docs.litellm.ai/docs/embedding/supported_embedding" + }, + "files": { + "docs_label": "files", + "display_name": "OpenAI Files API", + "leftnav_label": "/files", + "provider_json_field": "files", + "url": "https://docs.litellm.ai/docs/proxy/litellm_managed_files" + }, + "fine_tuning": { + "docs_label": "fine_tuning", + "display_name": "OpenAI Fine-Tuning API", + "leftnav_label": "/fine_tuning", + "provider_json_field": "fine_tuning", + "url": "https://docs.litellm.ai/docs/proxy/managed_finetuning" + }, + "generateContent": { + "docs_label": "generateContent", + "display_name": "Google GenerateContent API", + "leftnav_label": "/generateContent", + "provider_json_field": "generateContent", + "url": "https://docs.litellm.ai/docs/generateContent", + "bridges_to_chat_completion": true + }, + "image_edits": { + "docs_label": "image_edits", + "display_name": "OpenAI Images Edits API", + "leftnav_label": "/images/edits", + "provider_json_field": "image_edits", + "url": "https://docs.litellm.ai/docs/image_edits" + }, + "image_generations": { + "docs_label": "image_generation", + "display_name": "OpenAI Images Generations API", + "leftnav_label": "/images/generations", + "provider_json_field": "image_generations", + "url": "https://docs.litellm.ai/docs/image_generation" + }, + "image_variations": { + "docs_label": "image_variations", + "display_name": "OpenAI Images Variations API", + "leftnav_label": "/images/variations", + "provider_json_field": "image_variations", + "url": "https://docs.litellm.ai/docs/image_variations" + }, + "interactions": { + "docs_label": "interactions", + "display_name": "Google Interactions API", + "leftnav_label": "/interactions", + "provider_json_field": "interactions", + "url": "https://docs.litellm.ai/docs/interactions", + "bridges_to_chat_completion": true + }, + "mcp": { + "docs_label": "mcp", + "display_name": "Model Context Protocol (MCP)", + "leftnav_label": "/mcp", + "provider_json_field": "mcp", + "url": "https://docs.litellm.ai/docs/mcp" + }, + "moderation": { + "docs_label": "moderation", + "display_name": "OpenAI Moderations API", + "leftnav_label": "/moderations", + "provider_json_field": "moderations", + "url": "https://docs.litellm.ai/docs/moderation" + }, + "ocr": { + "docs_label": "ocr", + "display_name": "Mistral OCR API", + "leftnav_label": "/ocr", + "provider_json_field": "ocr", + "url": "https://docs.litellm.ai/docs/ocr" + }, + "rag_ingest": { + "docs_label": "rag_ingest", + "display_name": "RAG Ingest API", + "leftnav_label": "/rag/ingest", + "provider_json_field": "rag_ingest", + "url": "https://docs.litellm.ai/docs/rag_ingest" + }, + "rag_query": { + "docs_label": "rag_query", + "display_name": "RAG Query API", + "leftnav_label": "/rag/query", + "provider_json_field": "rag_query", + "url": "https://docs.litellm.ai/docs/rag_query" + }, + "realtime": { + "docs_label": "realtime", + "display_name": "OpenAI Realtime API", + "leftnav_label": "/realtime", + "provider_json_field": "realtime", + "url": "https://docs.litellm.ai/docs/realtime" + }, + "rerank": { + "docs_label": "rerank", + "display_name": "Cohere Rerank API", + "leftnav_label": "/rerank", + "provider_json_field": "rerank", + "url": "https://docs.litellm.ai/docs/rerank" + }, + "responses": { + "docs_label": "response_api", + "display_name": "OpenAI Responses API", + "leftnav_label": "/responses", + "provider_json_field": "responses", + "url": "https://docs.litellm.ai/docs/response_api", + "bridges_to_chat_completion": true + }, + "response_api_compact": { + "docs_label": "response_api_compact", + "display_name": "OpenAI Responses API", + "leftnav_label": "/responses", + "provider_json_field": "compact", + "url": "https://docs.litellm.ai/docs/response_api" + }, + "search": { + "docs_label": "search", + "display_name": "Search API", + "leftnav_label": "/search", + "provider_json_field": "search", + "url": "https://docs.litellm.ai/docs/search" + }, + "skills": { + "docs_label": "skills", + "display_name": "Anthropic Skills API", + "leftnav_label": "/skills", + "provider_json_field": "skills", + "url": "https://docs.litellm.ai/docs/skills" + }, + "text_completion": { + "docs_label": "text_completion", + "display_name": "OpenAI Completions API", + "leftnav_label": "/completions", + "provider_json_field": "text_completion", + "url": "https://docs.litellm.ai/docs/text_completion", + "bridges_to_chat_completion": true + }, + "text_to_speech": { + "docs_label": "text_to_speech", + "display_name": "OpenAI Text-to-Speech API", + "leftnav_label": "/audio/speech", + "provider_json_field": "audio_speech", + "url": "https://docs.litellm.ai/docs/text_to_speech" + }, + "vector_store_files": { + "docs_label": "vector_store_files", + "display_name": "OpenAI Vector Store Files API", + "leftnav_label": "/vector_stores/files", + "provider_json_field": "vector_store_files", + "url": "https://docs.litellm.ai/docs/vector_store_files" + }, + "vector_stores_create": { + "docs_label": "vector_stores_create", + "display_name": "OpenAI Vector Stores Create API", + "leftnav_label": "/vector_stores/create", + "provider_json_field": "vector_stores_create", + "url": "https://docs.litellm.ai/docs/vector_stores/create" + }, + "vector_stores_search": { + "docs_label": "vector_stores_search", + "display_name": "OpenAI Vector Stores Search API", + "leftnav_label": "/vector_stores/search", + "provider_json_field": "vector_stores_search", + "url": "https://docs.litellm.ai/docs/vector_stores/search" + }, + "videos": { + "docs_label": "videos", + "display_name": "OpenAI Videos API", + "leftnav_label": "/videos", + "provider_json_field": "video_generations", + "url": "https://docs.litellm.ai/docs/videos" + } + } +} diff --git a/litellm/proxy/public_endpoints/public_endpoints.py b/litellm/proxy/public_endpoints/public_endpoints.py index 29c9cb571c..ff90c778d8 100644 --- a/litellm/proxy/public_endpoints/public_endpoints.py +++ b/litellm/proxy/public_endpoints/public_endpoints.py @@ -1,6 +1,8 @@ import json import os -from typing import List +import re +from importlib.resources import files +from typing import Any, Dict, List, Optional import litellm from fastapi import APIRouter, Depends, HTTPException @@ -23,11 +25,95 @@ from litellm.types.proxy.public_endpoints.public_endpoints import ( AgentCreateInfo, ProviderCreateInfo, PublicModelHubInfo, + SupportedEndpointsResponse, ) from litellm.types.utils import LlmProviders router = APIRouter() +# --------------------------------------------------------------------------- +# /public/endpoints — helpers +# --------------------------------------------------------------------------- + +_ENDPOINT_METADATA: Dict[str, Dict[str, str]] = { + "chat_completions": {"label": "Chat Completions", "endpoint": "/chat/completions"}, + "messages": {"label": "Messages", "endpoint": "/messages"}, + "responses": {"label": "Responses", "endpoint": "/responses"}, + "embeddings": {"label": "Embeddings", "endpoint": "/embeddings"}, + "image_generations": {"label": "Image Generations", "endpoint": "/images/generations"}, + "audio_transcriptions": {"label": "Audio Transcriptions", "endpoint": "/audio/transcriptions"}, + "audio_speech": {"label": "Audio Speech", "endpoint": "/audio/speech"}, + "moderations": {"label": "Moderations", "endpoint": "/moderations"}, + "batches": {"label": "Batches", "endpoint": "/batches"}, + "rerank": {"label": "Rerank", "endpoint": "/rerank"}, + "ocr": {"label": "OCR", "endpoint": "/ocr"}, + "search": {"label": "Search", "endpoint": "/search"}, + "skills": {"label": "Skills", "endpoint": "/skills"}, + "interactions": {"label": "Interactions", "endpoint": "/interactions"}, + "a2a_(Agent Gateway)": {"label": "A2A (Agent Gateway)", "endpoint": "/a2a/{agent}/message/send"}, + "container": {"label": "Containers", "endpoint": "/containers"}, + "container_file": {"label": "Container Files", "endpoint": "/containers/{id}/files"}, + "compact": {"label": "Compact", "endpoint": "/responses/compact"}, + "files": {"label": "Files", "endpoint": "/files"}, + "image_edits": {"label": "Image Edits", "endpoint": "/images/edits"}, + "vector_stores_create": {"label": "Vector Stores (Create)", "endpoint": "/vector_stores"}, + "vector_stores_search": {"label": "Vector Stores (Search)", "endpoint": "/vector_stores/{id}/search"}, + "video_generations": {"label": "Video Generations", "endpoint": "/videos/generations"}, +} + +_SLUG_SUFFIX_RE = re.compile(r"\s*\(`[^`]+`\)\s*$") + +# Loaded once on first request; never invalidated (local file, no TTL needed). +_cached_endpoints: Optional[List[Dict[str, Any]]] = None + + +def _clean_display_name(raw: str) -> str: + return _SLUG_SUFFIX_RE.sub("", raw).strip() + + +def _build_endpoints(raw: Dict[str, Any]) -> List[Dict[str, Any]]: + """Transform raw provider_endpoints_support_backup.json into the response shape.""" + providers: Dict[str, Any] = raw.get("providers", {}) + + # Collect endpoint keys in insertion order (union across all providers). + seen: set = set() + all_keys: List[str] = [] + for provider_data in providers.values(): + for key in provider_data.get("endpoints", {}): + if key not in seen: + seen.add(key) + all_keys.append(key) + + result: List[Dict[str, Any]] = [] + for key in all_keys: + meta = _ENDPOINT_METADATA.get(key) + label = meta["label"] if meta else key.replace("_", " ").title() + path = meta["endpoint"] if meta else "/" + key.replace("_", "/") + + supporting: List[Dict[str, str]] = [ + { + "slug": slug, + "display_name": _clean_display_name(pd.get("display_name", slug)), + } + for slug, pd in providers.items() + if pd.get("endpoints", {}).get(key) + ] + result.append({"key": key, "label": label, "endpoint": path, "providers": supporting}) + + return result + + +def _load_endpoints() -> List[Dict[str, Any]]: + raw = json.loads( + files("litellm") + .joinpath("provider_endpoints_support_backup.json") + .read_text(encoding="utf-8") + ) + return _build_endpoints(raw) + + +# --------------------------------------------------------------------------- + @router.get( "/public/model_hub", @@ -225,6 +311,24 @@ async def get_litellm_blog_posts(): return BlogPostsResponse(posts=posts) +@router.get( + "/public/endpoints", + tags=["public"], + response_model=SupportedEndpointsResponse, +) +async def get_supported_endpoints() -> SupportedEndpointsResponse: + """ + Return the list of LiteLLM proxy endpoints and which providers support each one. + + Reads from the bundled local backup file. Result is cached in-process for + the lifetime of the server process. + """ + global _cached_endpoints + if _cached_endpoints is None: + _cached_endpoints = _load_endpoints() + return SupportedEndpointsResponse(endpoints=_cached_endpoints) + + @router.get( "/public/agents/fields", tags=["public", "[beta] Agents"], @@ -233,7 +337,7 @@ async def get_litellm_blog_posts(): async def get_agent_fields() -> List[AgentCreateInfo]: """ Return agent type metadata required by the dashboard create-agent flow. - + If an agent has `inherit_credentials_from_provider`, the provider's credential fields are automatically appended to the agent's credential_fields. """ @@ -242,19 +346,19 @@ async def get_agent_fields() -> List[AgentCreateInfo]: "proxy", "public_endpoints", ) - + agent_create_fields_path = os.path.join(base_path, "agent_create_fields.json") provider_create_fields_path = os.path.join(base_path, "provider_create_fields.json") with open(agent_create_fields_path, "r") as f: agent_create_fields = json.load(f) - + with open(provider_create_fields_path, "r") as f: provider_create_fields = json.load(f) - + # Build a lookup map for providers by name provider_map = {p["provider"]: p for p in provider_create_fields} - + # Merge inherited credential fields for agent in agent_create_fields: inherit_from = agent.get("inherit_credentials_from_provider") diff --git a/litellm/types/proxy/public_endpoints/public_endpoints.py b/litellm/types/proxy/public_endpoints/public_endpoints.py index 57d68771c7..caa9a97853 100644 --- a/litellm/types/proxy/public_endpoints/public_endpoints.py +++ b/litellm/types/proxy/public_endpoints/public_endpoints.py @@ -52,3 +52,19 @@ class AgentCreateInfo(BaseModel): credential_fields: List[AgentCredentialField] litellm_params_template: Optional[Dict[str, str]] = None model_template: Optional[str] = None + + +class EndpointProvider(BaseModel): + slug: str + display_name: str + + +class SupportedEndpoint(BaseModel): + key: str + label: str + endpoint: str + providers: List[EndpointProvider] + + +class SupportedEndpointsResponse(BaseModel): + endpoints: List[SupportedEndpoint] diff --git a/tests/test_litellm/proxy/public_endpoints/test_public_endpoints.py b/tests/test_litellm/proxy/public_endpoints/test_public_endpoints.py index 5f5e2cf1ff..53c98c8c40 100644 --- a/tests/test_litellm/proxy/public_endpoints/test_public_endpoints.py +++ b/tests/test_litellm/proxy/public_endpoints/test_public_endpoints.py @@ -357,3 +357,164 @@ def test_public_model_hub_mixed_health_statuses(): assert claude["health_checked_at"] is None app.dependency_overrides.clear() + +# --------------------------------------------------------------------------- +# /public/endpoints +# --------------------------------------------------------------------------- + +import litellm.proxy.public_endpoints.public_endpoints as _pe_module +from litellm.proxy.public_endpoints.public_endpoints import _build_endpoints, _clean_display_name + + +@pytest.fixture(autouse=False) +def reset_endpoints_cache(): + """Reset the module-level cache before and after each cache-related test.""" + original = _pe_module._cached_endpoints + _pe_module._cached_endpoints = None + yield + _pe_module._cached_endpoints = original + + +def _make_client(): + app = FastAPI() + app.include_router(router) + return TestClient(app) + + +def test_get_supported_endpoints_returns_200(reset_endpoints_cache): + response = _make_client().get("/public/endpoints") + assert response.status_code == 200 + + +def test_get_supported_endpoints_response_shape(reset_endpoints_cache): + data = _make_client().get("/public/endpoints").json() + assert "endpoints" in data + assert isinstance(data["endpoints"], list) + assert len(data["endpoints"]) > 0 + + +def test_get_supported_endpoints_item_fields(reset_endpoints_cache): + endpoints = _make_client().get("/public/endpoints").json()["endpoints"] + for item in endpoints: + assert "key" in item + assert "label" in item + assert "endpoint" in item + assert "providers" in item + assert isinstance(item["providers"], list) + + +def test_get_supported_endpoints_provider_fields(reset_endpoints_cache): + endpoints = _make_client().get("/public/endpoints").json()["endpoints"] + for item in endpoints: + for provider in item["providers"]: + assert "slug" in provider + assert "display_name" in provider + + +def test_get_supported_endpoints_paths_start_with_slash(reset_endpoints_cache): + endpoints = _make_client().get("/public/endpoints").json()["endpoints"] + for item in endpoints: + assert item["endpoint"].startswith("/"), f"Expected path starting with /, got: {item['endpoint']}" + + +def test_get_supported_endpoints_chat_completions_present(reset_endpoints_cache): + endpoints = _make_client().get("/public/endpoints").json()["endpoints"] + keys = [item["key"] for item in endpoints] + assert "chat_completions" in keys + + chat = next(item for item in endpoints if item["key"] == "chat_completions") + assert chat["endpoint"] == "/chat/completions" + assert chat["label"] == "Chat Completions" + assert len(chat["providers"]) > 0 + + +def test_get_supported_endpoints_display_names_have_no_slug_suffix(reset_endpoints_cache): + """Provider display_names must not contain the raw `` (`slug`) `` suffix.""" + import re + suffix_re = re.compile(r"\(`[^`]+`\)") + endpoints = _make_client().get("/public/endpoints").json()["endpoints"] + for item in endpoints: + for provider in item["providers"]: + assert not suffix_re.search(provider["display_name"]), ( + f"display_name still contains slug suffix: {provider['display_name']!r}" + ) + + +def test_get_supported_endpoints_is_cached(reset_endpoints_cache): + """`_load_endpoints` is called only once; subsequent requests use the cache.""" + client = _make_client() + with patch( + "litellm.proxy.public_endpoints.public_endpoints._load_endpoints", + wraps=_pe_module._load_endpoints, + ) as mock_load: + client.get("/public/endpoints") + client.get("/public/endpoints") + client.get("/public/endpoints") + + mock_load.assert_called_once() + + +# --------------------------------------------------------------------------- +# _build_endpoints unit tests (transformation logic) +# --------------------------------------------------------------------------- + +_MINIMAL_RAW = { + "providers": { + "openai": { + "display_name": "OpenAI (`openai`)", + "url": "https://example.com", + "endpoints": {"chat_completions": True, "embeddings": True, "images": False}, + }, + "anthropic": { + "display_name": "Anthropic (`anthropic`)", + "url": "https://example.com", + "endpoints": {"chat_completions": True, "embeddings": False, "images": False}, + }, + } +} + + +def test_build_endpoints_known_key_uses_metadata(): + result = _build_endpoints(_MINIMAL_RAW) + chat = next(e for e in result if e["key"] == "chat_completions") + assert chat["label"] == "Chat Completions" + assert chat["endpoint"] == "/chat/completions" + + +def test_build_endpoints_only_includes_supporting_providers(): + result = _build_endpoints(_MINIMAL_RAW) + embeddings = next(e for e in result if e["key"] == "embeddings") + slugs = [p["slug"] for p in embeddings["providers"]] + assert slugs == ["openai"] + + +def test_build_endpoints_unknown_key_derives_label_and_path(): + raw = { + "providers": { + "someprovider": { + "display_name": "Some Provider (`someprovider`)", + "endpoints": {"my_custom_endpoint": True}, + } + } + } + result = _build_endpoints(raw) + item = result[0] + assert item["key"] == "my_custom_endpoint" + assert item["label"] == "My Custom Endpoint" + assert item["endpoint"].startswith("/") + + +def test_build_endpoints_empty_providers_returns_empty(): + result = _build_endpoints({"providers": {}}) + assert result == [] + + +def test_clean_display_name_strips_suffix(): + assert _clean_display_name("OpenAI (`openai`)") == "OpenAI" + assert _clean_display_name("AI/ML API (`aiml`)") == "AI/ML API" + assert _clean_display_name("A2A (Agent-to-Agent) (`a2a`)") == "A2A (Agent-to-Agent)" + + +def test_clean_display_name_passthrough_when_no_suffix(): + assert _clean_display_name("OpenAI") == "OpenAI" + assert _clean_display_name("") == "" From ffc00c0c90fa21ff6456de972d40daab8fbe2d32 Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Thu, 26 Feb 2026 18:30:28 -0800 Subject: [PATCH 2/8] fix: correct _ENDPOINT_METADATA keys to match actual JSON data (a2a, container_files) --- litellm/proxy/public_endpoints/public_endpoints.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/public_endpoints/public_endpoints.py b/litellm/proxy/public_endpoints/public_endpoints.py index ff90c778d8..247d386a96 100644 --- a/litellm/proxy/public_endpoints/public_endpoints.py +++ b/litellm/proxy/public_endpoints/public_endpoints.py @@ -50,9 +50,9 @@ _ENDPOINT_METADATA: Dict[str, Dict[str, str]] = { "search": {"label": "Search", "endpoint": "/search"}, "skills": {"label": "Skills", "endpoint": "/skills"}, "interactions": {"label": "Interactions", "endpoint": "/interactions"}, - "a2a_(Agent Gateway)": {"label": "A2A (Agent Gateway)", "endpoint": "/a2a/{agent}/message/send"}, + "a2a": {"label": "A2A (Agent Gateway)", "endpoint": "/a2a/{agent}/message/send"}, "container": {"label": "Containers", "endpoint": "/containers"}, - "container_file": {"label": "Container Files", "endpoint": "/containers/{id}/files"}, + "container_files": {"label": "Container Files", "endpoint": "/containers/{id}/files"}, "compact": {"label": "Compact", "endpoint": "/responses/compact"}, "files": {"label": "Files", "endpoint": "/files"}, "image_edits": {"label": "Image Edits", "endpoint": "/images/edits"}, From fc69d6e8d1e9f4e9fe1754cd6f7a0daeba2afd23 Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Thu, 26 Feb 2026 19:18:17 -0800 Subject: [PATCH 3/8] fix: add 12 missing endpoint keys to _ENDPOINT_METADATA, fix stale _schema keys in backup JSON --- litellm/provider_endpoints_support_backup.json | 4 ++-- litellm/proxy/public_endpoints/public_endpoints.py | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/litellm/provider_endpoints_support_backup.json b/litellm/provider_endpoints_support_backup.json index 8834d8b19c..fc79ba5475 100644 --- a/litellm/provider_endpoints_support_backup.json +++ b/litellm/provider_endpoints_support_backup.json @@ -19,9 +19,9 @@ "search": "Supports /search endpoint", "skills": "Supports /skills endpoint", "interactions": "Supports /interactions endpoint (Google AI Interactions API)", - "a2a_(Agent Gateway)": "Supports /a2a/{agent}/message/send endpoint (A2A Protocol)", + "a2a": "Supports /a2a/{agent}/message/send endpoint (A2A Protocol)", "container": "Supports OpenAI's /containers endpoint", - "container_file": "Supports OpenAI's /containers/{id}/files endpoint", + "container_files": "Supports OpenAI's /containers/{id}/files endpoint", "compact": "Supports /responses/compact endpoint", "files": "Supports /files endpoint for file operations", "image_edits": "Supports /images/edits endpoint for image editing", diff --git a/litellm/proxy/public_endpoints/public_endpoints.py b/litellm/proxy/public_endpoints/public_endpoints.py index 247d386a96..d611a45401 100644 --- a/litellm/proxy/public_endpoints/public_endpoints.py +++ b/litellm/proxy/public_endpoints/public_endpoints.py @@ -58,7 +58,19 @@ _ENDPOINT_METADATA: Dict[str, Dict[str, str]] = { "image_edits": {"label": "Image Edits", "endpoint": "/images/edits"}, "vector_stores_create": {"label": "Vector Stores (Create)", "endpoint": "/vector_stores"}, "vector_stores_search": {"label": "Vector Stores (Search)", "endpoint": "/vector_stores/{id}/search"}, + "vector_store_files": {"label": "Vector Store Files", "endpoint": "/vector_stores/{id}/files"}, "video_generations": {"label": "Video Generations", "endpoint": "/videos/generations"}, + "assistants": {"label": "Assistants", "endpoint": "/assistants"}, + "fine_tuning": {"label": "Fine Tuning", "endpoint": "/fine_tuning/jobs"}, + "text_completion": {"label": "Text Completion", "endpoint": "/completions"}, + "realtime": {"label": "Realtime", "endpoint": "/realtime"}, + "count_tokens": {"label": "Count Tokens", "endpoint": "/utils/token_counter"}, + "image_variations": {"label": "Image Variations", "endpoint": "/images/variations"}, + "generateContent": {"label": "Generate Content", "endpoint": "/generateContent"}, + "bedrock_invoke": {"label": "Bedrock Invoke", "endpoint": "/bedrock/invoke"}, + "bedrock_converse": {"label": "Bedrock Converse", "endpoint": "/bedrock/converse"}, + "rag_ingest": {"label": "RAG Ingest", "endpoint": "/rag/ingest"}, + "rag_query": {"label": "RAG Query", "endpoint": "/rag/query"}, } _SLUG_SUFFIX_RE = re.compile(r"\s*\(`[^`]+`\)\s*$") From 369c0ec3924b6e7d9d1ca4071c570bb7cf706e6b Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Thu, 26 Feb 2026 20:03:25 -0800 Subject: [PATCH 4/8] [Infra] Add prisma_schema_sync CircleCI job before e2e UI tests Adds a new CircleCI job that runs the proxy with --use_prisma_db_push against the base Neon branch before the e2e UI tests create their branches from it, ensuring the schema is synced on the parent. Co-Authored-By: Claude Sonnet 4.6 --- .circleci/config.yml | 68 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index fbbb6deeba..8709f730c2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4100,6 +4100,63 @@ jobs: path: playwright-report destination: playwright-report + prisma_schema_sync: + machine: + image: ubuntu-2204:2023.10.1 + resource_class: xlarge + working_directory: ~/project + steps: + - checkout + - setup_google_dns + - attach_workspace: + at: ~/project + - run: + name: Load Docker Database Image + command: | + gunzip -c litellm-docker-database.tar.gz | docker load + docker images | grep litellm-docker-database + - run: + name: Install Neon CLI + command: | + npm i -g neonctl + - run: + name: Install curl and dockerize + command: | + sudo apt-get update + sudo apt-get install -y curl + sudo wget https://github.com/jwilder/dockerize/releases/download/v0.6.1/dockerize-linux-amd64-v0.6.1.tar.gz + sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.6.1.tar.gz + sudo rm dockerize-linux-amd64-v0.6.1.tar.gz + - run: + name: Sync schema on base e2e database + command: | + BASE_DATABASE_URL=$(neon connection-string \ + --project-id $NEON_PROJECT_ID \ + --api-key $NEON_API_KEY \ + --branch br-fancy-paper-ad1olsb3 \ + --database-name yuneng-trial-db \ + --role neondb_owner) + docker run -d \ + -p 4000:4000 \ + -e DATABASE_URL=$BASE_DATABASE_URL \ + -e LITELLM_MASTER_KEY="sk-1234" \ + --name schema-sync \ + -v $(pwd)/litellm/proxy/example_config_yaml/simple_config.yaml:/app/config.yaml \ + litellm-docker-database:ci \ + --config /app/config.yaml \ + --port 4000 \ + --use_prisma_db_push + - run: + name: Start outputting logs + command: docker logs -f schema-sync + background: true + - run: + name: Wait for proxy to be ready (schema sync complete) + command: dockerize -wait http://localhost:4000 -timeout 5m + - run: + name: Stop schema sync container + command: docker stop schema-sync + test_nonroot_image: machine: image: ubuntu-2204:2023.10.1 @@ -4298,6 +4355,15 @@ workflows: only: - main - /litellm_.*/ + - prisma_schema_sync: + context: e2e_ui_tests + requires: + - build_docker_database_image + filters: + branches: + only: + - main + - /litellm_.*/ - e2e_ui_testing: name: e2e_ui_testing_chromium browser: chromium @@ -4305,6 +4371,7 @@ workflows: requires: - ui_build - build_docker_database_image + - prisma_schema_sync filters: branches: only: @@ -4317,6 +4384,7 @@ workflows: requires: - ui_build - build_docker_database_image + - prisma_schema_sync filters: branches: only: From ee7b73764cd7fe023087fa2500584f966ff019b7 Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Thu, 26 Feb 2026 20:29:43 -0800 Subject: [PATCH 5/8] =?UTF-8?q?bump:=20version=200.4.48=20=E2=86=92=200.4.?= =?UTF-8?q?49?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- litellm-proxy-extras/litellm_proxy_extras/schema.prisma | 1 + litellm-proxy-extras/pyproject.toml | 4 ++-- litellm/proxy/schema.prisma | 1 + pyproject.toml | 2 +- requirements.txt | 2 +- schema.prisma | 1 + 6 files changed, 7 insertions(+), 4 deletions(-) diff --git a/litellm-proxy-extras/litellm_proxy_extras/schema.prisma b/litellm-proxy-extras/litellm_proxy_extras/schema.prisma index 440c9c1d82..34308b29eb 100644 --- a/litellm-proxy-extras/litellm_proxy_extras/schema.prisma +++ b/litellm-proxy-extras/litellm_proxy_extras/schema.prisma @@ -390,6 +390,7 @@ model LiteLLM_DeletedVerificationToken { config Json @default("{}") user_id String? team_id String? + agent_id String? project_id String? permissions Json @default("{}") max_parallel_requests Int? diff --git a/litellm-proxy-extras/pyproject.toml b/litellm-proxy-extras/pyproject.toml index bd57b248cd..968536712d 100644 --- a/litellm-proxy-extras/pyproject.toml +++ b/litellm-proxy-extras/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "litellm-proxy-extras" -version = "0.4.48" +version = "0.4.49" description = "Additional files for the LiteLLM Proxy. Reduces the size of the main litellm package." authors = ["BerriAI"] readme = "README.md" @@ -22,7 +22,7 @@ requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" [tool.commitizen] -version = "0.4.48" +version = "0.4.49" version_files = [ "pyproject.toml:version", "../requirements.txt:litellm-proxy-extras==", diff --git a/litellm/proxy/schema.prisma b/litellm/proxy/schema.prisma index 440c9c1d82..34308b29eb 100644 --- a/litellm/proxy/schema.prisma +++ b/litellm/proxy/schema.prisma @@ -390,6 +390,7 @@ model LiteLLM_DeletedVerificationToken { config Json @default("{}") user_id String? team_id String? + agent_id String? project_id String? permissions Json @default("{}") max_parallel_requests Int? diff --git a/pyproject.toml b/pyproject.toml index 8eb433fb06..07f3ea30fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,7 +61,7 @@ boto3 = { version = "1.40.76", optional = true } redisvl = {version = "^0.4.1", optional = true, markers = "python_version >= '3.9' and python_version < '3.14'"} mcp = {version = ">=1.25.0,<2.0.0", optional = true, python = ">=3.10"} a2a-sdk = {version = "^0.3.22", optional = true, python = ">=3.10"} -litellm-proxy-extras = {version = "0.4.48", optional = true} +litellm-proxy-extras = {version = "0.4.49", optional = true} rich = {version = "13.7.1", optional = true} litellm-enterprise = {version = "0.1.32", optional = true} diskcache = {version = "^5.6.1", optional = true} diff --git a/requirements.txt b/requirements.txt index 6cdb2f63a3..67f390cf27 100644 --- a/requirements.txt +++ b/requirements.txt @@ -57,7 +57,7 @@ grpcio>=1.75.0; python_version >= "3.14" sentry_sdk==2.21.0 # for sentry error handling detect-secrets==1.5.0 # Enterprise - secret detection / masking in LLM requests tzdata==2025.1 # IANA time zone database -litellm-proxy-extras==0.4.48 # for proxy extras - e.g. prisma migrations +litellm-proxy-extras==0.4.49 # for proxy extras - e.g. prisma migrations llm-sandbox==0.3.31 # for skill execution in sandbox ### LITELLM PACKAGE DEPENDENCIES python-dotenv==1.0.1 # for env diff --git a/schema.prisma b/schema.prisma index 440c9c1d82..34308b29eb 100644 --- a/schema.prisma +++ b/schema.prisma @@ -390,6 +390,7 @@ model LiteLLM_DeletedVerificationToken { config Json @default("{}") user_id String? team_id String? + agent_id String? project_id String? permissions Json @default("{}") max_parallel_requests Int? From 1e82ec644828e28df67576aea5368268b2eb9255 Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Thu, 26 Feb 2026 20:30:10 -0800 Subject: [PATCH 6/8] adding build --- ...litellm_proxy_extras-0.4.49-py3-none-any.whl | Bin 0 -> 65579 bytes .../dist/litellm_proxy_extras-0.4.49.tar.gz | Bin 0 -> 28710 bytes .../migration.sql | 3 +++ 3 files changed, 3 insertions(+) create mode 100644 litellm-proxy-extras/dist/litellm_proxy_extras-0.4.49-py3-none-any.whl create mode 100644 litellm-proxy-extras/dist/litellm_proxy_extras-0.4.49.tar.gz create mode 100644 litellm-proxy-extras/litellm_proxy_extras/migrations/20260226202727_add_agent_id_to_delete_keys/migration.sql diff --git a/litellm-proxy-extras/dist/litellm_proxy_extras-0.4.49-py3-none-any.whl b/litellm-proxy-extras/dist/litellm_proxy_extras-0.4.49-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..e44b58f8e63bac05327c43d004bfbf3c2eec533d GIT binary patch literal 65579 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�a7yJtx2MjC^ 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 Date: Thu, 26 Feb 2026 20:42:47 -0800 Subject: [PATCH 7/8] Update litellm/proxy/public_endpoints/public_endpoints.py Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- litellm/proxy/public_endpoints/public_endpoints.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/public_endpoints/public_endpoints.py b/litellm/proxy/public_endpoints/public_endpoints.py index d611a45401..ac5d912614 100644 --- a/litellm/proxy/public_endpoints/public_endpoints.py +++ b/litellm/proxy/public_endpoints/public_endpoints.py @@ -337,8 +337,8 @@ async def get_supported_endpoints() -> SupportedEndpointsResponse: """ global _cached_endpoints if _cached_endpoints is None: - _cached_endpoints = _load_endpoints() - return SupportedEndpointsResponse(endpoints=_cached_endpoints) + _cached_endpoints = SupportedEndpointsResponse(endpoints=_load_endpoints()) + return _cached_endpoints @router.get( From 8565c70e539dd307918a430926330c0d285cb19b Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Fri, 27 Feb 2026 12:45:08 +0530 Subject: [PATCH 8/8] Revert "Fix mapping of parallel_tool_calls for bedrock converse" --- .../bedrock/chat/converse_transformation.py | 19 ------ .../chat/test_converse_transformation.py | 66 +------------------ 2 files changed, 3 insertions(+), 82 deletions(-) diff --git a/litellm/llms/bedrock/chat/converse_transformation.py b/litellm/llms/bedrock/chat/converse_transformation.py index d4fd060630..6208111406 100644 --- a/litellm/llms/bedrock/chat/converse_transformation.py +++ b/litellm/llms/bedrock/chat/converse_transformation.py @@ -511,7 +511,6 @@ class AmazonConverseConfig(BaseConfig): "response_format", "requestMetadata", "service_tier", - "parallel_tool_calls", ] if ( @@ -914,13 +913,6 @@ class AmazonConverseConfig(BaseConfig): ) if _tool_choice_value is not None: optional_params["tool_choice"] = _tool_choice_value - if param == "parallel_tool_calls": - disable_parallel = not value - optional_params["_parallel_tool_use_config"] = { - "tool_choice": { - "disable_parallel_tool_use": disable_parallel - } - } if param == "thinking": optional_params["thinking"] = value elif param == "reasoning_effort" and isinstance(value, str): @@ -1215,17 +1207,6 @@ class AmazonConverseConfig(BaseConfig): k: v for k, v in inference_params.items() if k in total_supported_params } - # Handle parallel_tool_calls configuration - parallel_tool_use_config = additional_request_params.pop("_parallel_tool_use_config", None) - if parallel_tool_use_config is not None: - # Merge the tool_choice config from parallel_tool_calls into additional_request_params - for key, value in parallel_tool_use_config.items(): - if key in additional_request_params and isinstance(additional_request_params[key], dict) and isinstance(value, dict): - # Merge dictionaries - additional_request_params[key].update(value) - else: - additional_request_params[key] = value - # Only set the topK value in for models that support it additional_request_params.update( self._handle_top_k_value(model, inference_params) diff --git a/tests/test_litellm/llms/bedrock/chat/test_converse_transformation.py b/tests/test_litellm/llms/bedrock/chat/test_converse_transformation.py index 2639559716..c773db2107 100644 --- a/tests/test_litellm/llms/bedrock/chat/test_converse_transformation.py +++ b/tests/test_litellm/llms/bedrock/chat/test_converse_transformation.py @@ -2616,11 +2616,11 @@ def test_empty_assistant_message_handling(): empty or whitespace-only content with a placeholder to prevent AWS Bedrock Converse API 400 Bad Request errors. """ - # Import the litellm module that factory.py uses to ensure we patch the correct reference - import litellm.litellm_core_utils.prompt_templates.factory as factory_module from litellm.litellm_core_utils.prompt_templates.factory import ( _bedrock_converse_messages_pt, ) + # Import the litellm module that factory.py uses to ensure we patch the correct reference + import litellm.litellm_core_utils.prompt_templates.factory as factory_module # Test case 1: Empty string content - test with modify_params=True to prevent merging messages = [ @@ -3135,12 +3135,7 @@ def test_native_structured_output_no_fake_stream(): def test_transform_request_with_output_config(): """Test that outputConfig flows through _transform_request_helper into the final request.""" - from litellm.types.llms.bedrock import ( - JsonSchemaDefinition, - OutputConfigBlock, - OutputFormat, - OutputFormatStructure, - ) + from litellm.types.llms.bedrock import OutputConfigBlock, OutputFormat, OutputFormatStructure, JsonSchemaDefinition config = AmazonConverseConfig() @@ -3382,61 +3377,6 @@ def test_output_config_applies_additional_properties(): -def test_parallel_tool_calls_in_request_transformation(): - """Test that parallel_tool_calls is correctly placed in additionalModelRequestFields after full transformation""" - config = AmazonConverseConfig() - - messages = [ - {"role": "user", "content": "What's the weather in SF and NYC?"} - ] - - non_default_params = { - "parallel_tool_calls": False, - "tools": [ - { - "type": "function", - "function": { - "name": "get_weather", - "description": "Get the weather", - "parameters": { - "type": "object", - "properties": { - "location": { - "type": "string", - "description": "The location to get weather for" - } - }, - "required": ["location"] - } - } - } - ], - "max_tokens": 100, - } - - optional_params = config.map_openai_params( - non_default_params=non_default_params, - optional_params={}, - model="anthropic.claude-sonnet-4-5-v2:0", - drop_params=False, - ) - - # Transform the request - request_data = config.transform_request( - model="anthropic.claude-sonnet-4-5-v2:0", - messages=messages, - optional_params=optional_params, - litellm_params={}, - headers={}, - ) - - # Verify the structure - assert "additionalModelRequestFields" in request_data - assert "tool_choice" in request_data["additionalModelRequestFields"] - assert "disable_parallel_tool_use" in request_data["additionalModelRequestFields"]["tool_choice"] - assert request_data["additionalModelRequestFields"]["tool_choice"]["disable_parallel_tool_use"] is True - - class TestBedrockMinThinkingBudgetTokens: """Test that thinking.budget_tokens is clamped to the Bedrock minimum (1024)."""