From 77ecb8eaff2a59e48469e1495098ac3b6f4f0ea6 Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Mon, 5 Jan 2026 16:12:25 +0530 Subject: [PATCH] add estimate_cost endpoint --- ...odel_prices_and_context_window_backup.json | 42 ++++++ litellm/proxy/_types.py | 32 +++++ .../cost_tracking_settings.py | 121 +++++++++++++++++- 3 files changed, 193 insertions(+), 2 deletions(-) diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index d32adf54b5e..81b4469f24c 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -11640,6 +11640,7 @@ "supports_tool_choice": true }, "gemini-1.5-flash": { + "deprecation_date": "2025-09-29", "input_cost_per_audio_per_second": 2e-06, "input_cost_per_audio_per_second_above_128k_tokens": 4e-06, "input_cost_per_character": 1.875e-08, @@ -11744,6 +11745,7 @@ "supports_vision": true }, "gemini-1.5-flash-exp-0827": { + "deprecation_date": "2025-09-29", "input_cost_per_audio_per_second": 2e-06, "input_cost_per_audio_per_second_above_128k_tokens": 4e-06, "input_cost_per_character": 1.875e-08, @@ -11778,6 +11780,7 @@ "supports_vision": true }, "gemini-1.5-flash-preview-0514": { + "deprecation_date": "2025-09-29", "input_cost_per_audio_per_second": 2e-06, "input_cost_per_audio_per_second_above_128k_tokens": 4e-06, "input_cost_per_character": 1.875e-08, @@ -11811,6 +11814,7 @@ "supports_vision": true }, "gemini-1.5-pro": { + "deprecation_date": "2025-09-29", "input_cost_per_audio_per_second": 3.125e-05, "input_cost_per_audio_per_second_above_128k_tokens": 6.25e-05, "input_cost_per_character": 3.125e-07, @@ -11898,6 +11902,7 @@ "supports_vision": true }, "gemini-1.5-pro-preview-0215": { + "deprecation_date": "2025-09-29", "input_cost_per_audio_per_second": 3.125e-05, "input_cost_per_audio_per_second_above_128k_tokens": 6.25e-05, "input_cost_per_character": 3.125e-07, @@ -11925,6 +11930,7 @@ "supports_tool_choice": true }, "gemini-1.5-pro-preview-0409": { + "deprecation_date": "2025-09-29", "input_cost_per_audio_per_second": 3.125e-05, "input_cost_per_audio_per_second_above_128k_tokens": 6.25e-05, "input_cost_per_character": 3.125e-07, @@ -11951,6 +11957,7 @@ "supports_tool_choice": true }, "gemini-1.5-pro-preview-0514": { + "deprecation_date": "2025-09-29", "input_cost_per_audio_per_second": 3.125e-05, "input_cost_per_audio_per_second_above_128k_tokens": 6.25e-05, "input_cost_per_character": 3.125e-07, @@ -12222,6 +12229,7 @@ "tpm": 250000 }, "gemini-2.0-flash-preview-image-generation": { + "deprecation_date": "2025-11-14", "cache_read_input_token_cost": 2.5e-08, "input_cost_per_audio_token": 7e-07, "input_cost_per_token": 1e-07, @@ -12260,6 +12268,7 @@ "supports_web_search": true }, "gemini-2.0-flash-thinking-exp": { + "deprecation_date": "2025-12-02", "cache_read_input_token_cost": 0.0, "input_cost_per_audio_per_second": 0, "input_cost_per_audio_per_second_above_128k_tokens": 0, @@ -12308,6 +12317,7 @@ "supports_web_search": true }, "gemini-2.0-flash-thinking-exp-01-21": { + "deprecation_date": "2025-12-02", "cache_read_input_token_cost": 0.0, "input_cost_per_audio_per_second": 0, "input_cost_per_audio_per_second_above_128k_tokens": 0, @@ -12494,6 +12504,7 @@ "tpm": 8000000 }, "gemini-2.5-flash-image-preview": { + "deprecation_date": "2026-01-15", "cache_read_input_token_cost": 7.5e-08, "input_cost_per_audio_token": 1e-06, "input_cost_per_token": 3e-07, @@ -12804,6 +12815,7 @@ "tpm": 8000000 }, "gemini-2.5-flash-lite-preview-06-17": { + "deprecation_date": "2025-11-18", "cache_read_input_token_cost": 2.5e-08, "input_cost_per_audio_token": 5e-07, "input_cost_per_token": 1e-07, @@ -12893,6 +12905,7 @@ "supports_web_search": true }, "gemini-2.5-flash-preview-05-20": { + "deprecation_date": "2025-11-18", "cache_read_input_token_cost": 7.5e-08, "input_cost_per_audio_token": 1e-06, "input_cost_per_token": 3e-07, @@ -13164,6 +13177,7 @@ "supports_web_search": true }, "gemini-2.5-pro-preview-03-25": { + "deprecation_date": "2025-12-02", "cache_read_input_token_cost": 3.125e-07, "input_cost_per_audio_token": 1.25e-06, "input_cost_per_token": 1.25e-06, @@ -13209,6 +13223,7 @@ "supports_web_search": true }, "gemini-2.5-pro-preview-05-06": { + "deprecation_date": "2025-12-02", "cache_read_input_token_cost": 3.125e-07, "input_cost_per_audio_token": 1.25e-06, "input_cost_per_token": 1.25e-06, @@ -13424,6 +13439,7 @@ "tpm": 10000000 }, "gemini/gemini-1.5-flash": { + "deprecation_date": "2025-09-29", "input_cost_per_token": 7.5e-08, "input_cost_per_token_above_128k_tokens": 1.5e-07, "litellm_provider": "gemini", @@ -13507,6 +13523,7 @@ "tpm": 4000000 }, "gemini/gemini-1.5-flash-8b": { + "deprecation_date": "2025-09-29", "input_cost_per_token": 0, "input_cost_per_token_above_128k_tokens": 0, "litellm_provider": "gemini", @@ -13533,6 +13550,7 @@ "tpm": 4000000 }, "gemini/gemini-1.5-flash-8b-exp-0827": { + "deprecation_date": "2025-09-29", "input_cost_per_token": 0, "input_cost_per_token_above_128k_tokens": 0, "litellm_provider": "gemini", @@ -13558,6 +13576,7 @@ "tpm": 4000000 }, "gemini/gemini-1.5-flash-8b-exp-0924": { + "deprecation_date": "2025-09-29", "input_cost_per_token": 0, "input_cost_per_token_above_128k_tokens": 0, "litellm_provider": "gemini", @@ -13584,6 +13603,7 @@ "tpm": 4000000 }, "gemini/gemini-1.5-flash-exp-0827": { + "deprecation_date": "2025-09-29", "input_cost_per_token": 0, "input_cost_per_token_above_128k_tokens": 0, "litellm_provider": "gemini", @@ -13609,6 +13629,7 @@ "tpm": 4000000 }, "gemini/gemini-1.5-flash-latest": { + "deprecation_date": "2025-09-29", "input_cost_per_token": 7.5e-08, "input_cost_per_token_above_128k_tokens": 1.5e-07, "litellm_provider": "gemini", @@ -13635,6 +13656,7 @@ "tpm": 4000000 }, "gemini/gemini-1.5-pro": { + "deprecation_date": "2025-09-29", "input_cost_per_token": 3.5e-06, "input_cost_per_token_above_128k_tokens": 7e-06, "litellm_provider": "gemini", @@ -13696,6 +13718,7 @@ "tpm": 4000000 }, "gemini/gemini-1.5-pro-exp-0801": { + "deprecation_date": "2025-09-29", "input_cost_per_token": 3.5e-06, "input_cost_per_token_above_128k_tokens": 7e-06, "litellm_provider": "gemini", @@ -13715,6 +13738,7 @@ "tpm": 4000000 }, "gemini/gemini-1.5-pro-exp-0827": { + "deprecation_date": "2025-09-29", "input_cost_per_token": 0, "input_cost_per_token_above_128k_tokens": 0, "litellm_provider": "gemini", @@ -13734,6 +13758,7 @@ "tpm": 4000000 }, "gemini/gemini-1.5-pro-latest": { + "deprecation_date": "2025-09-29", "input_cost_per_token": 3.5e-06, "input_cost_per_token_above_128k_tokens": 7e-06, "litellm_provider": "gemini", @@ -13916,6 +13941,7 @@ "tpm": 4000000 }, "gemini/gemini-2.0-flash-lite-preview-02-05": { + "deprecation_date": "2025-12-02", "cache_read_input_token_cost": 1.875e-08, "input_cost_per_audio_token": 7.5e-08, "input_cost_per_token": 7.5e-08, @@ -13953,6 +13979,7 @@ "tpm": 10000000 }, "gemini/gemini-2.0-flash-live-001": { + "deprecation_date": "2025-12-09", "cache_read_input_token_cost": 7.5e-08, "input_cost_per_audio_token": 2.1e-06, "input_cost_per_image": 2.1e-06, @@ -14001,6 +14028,7 @@ "tpm": 250000 }, "gemini/gemini-2.0-flash-preview-image-generation": { + "deprecation_date": "2025-11-14", "cache_read_input_token_cost": 2.5e-08, "input_cost_per_audio_token": 7e-07, "input_cost_per_token": 1e-07, @@ -14040,6 +14068,7 @@ "tpm": 10000000 }, "gemini/gemini-2.0-flash-thinking-exp": { + "deprecation_date": "2025-12-02", "cache_read_input_token_cost": 0.0, "input_cost_per_audio_per_second": 0, "input_cost_per_audio_per_second_above_128k_tokens": 0, @@ -14089,6 +14118,7 @@ "tpm": 4000000 }, "gemini/gemini-2.0-flash-thinking-exp-01-21": { + "deprecation_date": "2025-12-02", "cache_read_input_token_cost": 0.0, "input_cost_per_audio_per_second": 0, "input_cost_per_audio_per_second_above_128k_tokens": 0, @@ -14277,6 +14307,7 @@ "tpm": 8000000 }, "gemini/gemini-2.5-flash-image-preview": { + "deprecation_date": "2026-01-15", "cache_read_input_token_cost": 7.5e-08, "input_cost_per_audio_token": 1e-06, "input_cost_per_token": 3e-07, @@ -14597,6 +14628,7 @@ "tpm": 250000 }, "gemini/gemini-2.5-flash-lite-preview-06-17": { + "deprecation_date": "2025-11-18", "cache_read_input_token_cost": 2.5e-08, "input_cost_per_audio_token": 5e-07, "input_cost_per_token": 1e-07, @@ -14688,6 +14720,7 @@ "tpm": 250000 }, "gemini/gemini-2.5-flash-preview-05-20": { + "deprecation_date": "2025-11-18", "cache_read_input_token_cost": 7.5e-08, "input_cost_per_audio_token": 1e-06, "input_cost_per_token": 3e-07, @@ -15034,6 +15067,7 @@ "tpm": 250000 }, "gemini/gemini-2.5-pro-preview-03-25": { + "deprecation_date": "2025-12-02", "cache_read_input_token_cost": 3.125e-07, "input_cost_per_audio_token": 7e-07, "input_cost_per_token": 1.25e-06, @@ -15074,6 +15108,7 @@ "tpm": 10000000 }, "gemini/gemini-2.5-pro-preview-05-06": { + "deprecation_date": "2025-12-02", "cache_read_input_token_cost": 3.125e-07, "input_cost_per_audio_token": 7e-07, "input_cost_per_token": 1.25e-06, @@ -15349,6 +15384,7 @@ "source": "https://cloud.google.com/vertex-ai/generative-ai/pricing" }, "gemini/imagen-3.0-generate-002": { + "deprecation_date": "2025-11-10", "litellm_provider": "gemini", "mode": "image_generation", "output_cost_per_image": 0.04, @@ -15415,6 +15451,7 @@ ] }, "gemini/veo-3.0-fast-generate-preview": { + "deprecation_date": "2025-11-12", "litellm_provider": "gemini", "max_input_tokens": 1024, "max_tokens": 1024, @@ -15429,6 +15466,7 @@ ] }, "gemini/veo-3.0-generate-preview": { + "deprecation_date": "2025-11-12", "litellm_provider": "gemini", "max_input_tokens": 1024, "max_tokens": 1024, @@ -25126,6 +25164,7 @@ "source": "https://docs.mistral.ai/capabilities/code_generation/" }, "text-embedding-004": { + "deprecation_date": "2026-01-14", "input_cost_per_character": 2.5e-08, "input_cost_per_token": 1e-07, "litellm_provider": "vertex_ai-embedding-models", @@ -27896,6 +27935,7 @@ "source": "https://cloud.google.com/vertex-ai/generative-ai/pricing" }, "vertex_ai/imagen-3.0-generate-002": { + "deprecation_date": "2025-11-10", "litellm_provider": "vertex_ai-image-models", "mode": "image_generation", "output_cost_per_image": 0.04, @@ -28406,6 +28446,7 @@ ] }, "vertex_ai/veo-3.0-fast-generate-preview": { + "deprecation_date": "2025-11-12", "litellm_provider": "vertex_ai-video-models", "max_input_tokens": 1024, "max_tokens": 1024, @@ -28420,6 +28461,7 @@ ] }, "vertex_ai/veo-3.0-generate-preview": { + "deprecation_date": "2025-11-12", "litellm_provider": "vertex_ai-video-models", "max_input_tokens": 1024, "max_tokens": 1024, diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index fa32f60c073..0223f4ce4db 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -3825,3 +3825,35 @@ class LiteLLM_ManagedVectorStoresTable(LiteLLMPydanticObjectBase): class ResponseLiteLLM_ManagedVectorStore(TypedDict, total=False): vector_store: LiteLLM_ManagedVectorStoresTable + + +class CostEstimateRequest(LiteLLMPydanticObjectBase): + """Request body for /cost/estimate endpoint.""" + + model: str = Field(description="Model name (from /model_group/info)") + input_tokens: int = Field(description="Expected input tokens per request", ge=0) + output_tokens: int = Field(description="Expected output tokens per request", ge=0) + num_requests: int = Field(default=1, description="Number of requests", ge=1) + + +class CostEstimateResponse(LiteLLMPydanticObjectBase): + """Response body for /cost/estimate endpoint.""" + + model: str + input_tokens: int + output_tokens: int + num_requests: int + # Per-request costs + cost_per_request: float = Field(description="Total cost per request (includes margin)") + input_cost_per_request: float = Field(description="Input token cost per request (before margin)") + output_cost_per_request: float = Field(description="Output token cost per request (before margin)") + margin_cost_per_request: float = Field(default=0.0, description="Margin/fee added per request") + # Total costs (per-request * num_requests) + total_cost: float = Field(description="Total cost for all requests (includes margin)") + total_input_cost: float = Field(description="Total input token cost (before margin)") + total_output_cost: float = Field(description="Total output token cost (before margin)") + total_margin_cost: float = Field(default=0.0, description="Total margin/fee for all requests") + # Pricing info + input_cost_per_token: Optional[float] = None + output_cost_per_token: Optional[float] = None + provider: Optional[str] = None diff --git a/litellm/proxy/management_endpoints/cost_tracking_settings.py b/litellm/proxy/management_endpoints/cost_tracking_settings.py index 86433a232c0..a94b3800314 100644 --- a/litellm/proxy/management_endpoints/cost_tracking_settings.py +++ b/litellm/proxy/management_endpoints/cost_tracking_settings.py @@ -7,15 +7,22 @@ GET /config/cost_discount_config - Get current cost discount configuration PATCH /config/cost_discount_config - Update cost discount configuration GET /config/cost_margin_config - Get current cost margin configuration PATCH /config/cost_margin_config - Update cost margin configuration +POST /cost/estimate - Estimate cost for a given model and token counts """ -from typing import Dict, Union +from typing import Dict, List, Optional, Union from fastapi import APIRouter, Depends, HTTPException import litellm from litellm._logging import verbose_proxy_logger -from litellm.proxy._types import CommonProxyErrors, UserAPIKeyAuth +from litellm.cost_calculator import completion_cost +from litellm.proxy._types import ( + CommonProxyErrors, + CostEstimateRequest, + CostEstimateResponse, + UserAPIKeyAuth, +) from litellm.proxy.auth.user_api_key_auth import user_api_key_auth from litellm.types.utils import LlmProvidersSet @@ -347,3 +354,113 @@ async def update_cost_margin_config( detail={"error": f"Failed to update cost margin config: {str(e)}"} ) + +@router.post( + "/cost/estimate", + tags=["Cost Tracking"], + dependencies=[Depends(user_api_key_auth)], + response_model=CostEstimateResponse, +) +async def estimate_cost( + request: CostEstimateRequest, + user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), +) -> CostEstimateResponse: + """ + Estimate cost for a given model and token counts. + + This endpoint uses the same cost calculation logic as actual requests, + including any configured margins and discounts. + + Parameters: + - model: Model name from /model_group/info (e.g., "gpt-4", "claude-3-opus") + - input_tokens: Expected input tokens per request + - output_tokens: Expected output tokens per request + - num_requests: Number of requests (default: 1) + + Returns cost breakdown including: + - Input token cost + - Output token cost + - Margin/fee cost + - Total cost + + Example: + ```json + { + "model": "gpt-4", + "input_tokens": 1000, + "output_tokens": 500, + "num_requests": 100 + } + ``` + """ + from litellm.cost_calculator import _apply_cost_margin + from litellm.proxy.proxy_server import llm_router + from litellm.types.utils import Usage + + if llm_router is None: + raise HTTPException( + status_code=500, + detail={"error": "Router not initialized. No models configured."}, + ) + + # Get model group info from router to resolve pricing + model_group_info = llm_router.get_model_group_info(model_group=request.model) + + if model_group_info is None: + raise HTTPException( + status_code=404, + detail={ + "error": f"Model '{request.model}' not found. Use /model_group/info to see available models." + }, + ) + + # Get the provider from the model group + providers: List[str] = model_group_info.providers or [] + custom_llm_provider: Optional[str] = providers[0] if providers else None + + # Get cost per token from model group info + input_cost_per_token = model_group_info.input_cost_per_token or 0.0 + output_cost_per_token = model_group_info.output_cost_per_token or 0.0 + + # Calculate base costs (before margin) + input_cost = input_cost_per_token * request.input_tokens + output_cost = output_cost_per_token * request.output_tokens + base_cost = input_cost + output_cost + + # Apply margin using the same function as completion_cost + ( + cost_with_margin, + _margin_percent, + _margin_fixed_amount, + margin_cost, + ) = _apply_cost_margin( + base_cost=base_cost, + custom_llm_provider=custom_llm_provider, + ) + + cost_per_request = cost_with_margin + + # Calculate totals based on number of requests + total_cost = cost_per_request * request.num_requests + total_input_cost = input_cost * request.num_requests + total_output_cost = output_cost * request.num_requests + total_margin_cost = margin_cost * request.num_requests + + return CostEstimateResponse( + model=request.model, + input_tokens=request.input_tokens, + output_tokens=request.output_tokens, + num_requests=request.num_requests, + cost_per_request=cost_per_request, + input_cost_per_request=input_cost, + output_cost_per_request=output_cost, + margin_cost_per_request=margin_cost, + total_cost=total_cost, + total_input_cost=total_input_cost, + total_output_cost=total_output_cost, + total_margin_cost=total_margin_cost, + input_cost_per_token=input_cost_per_token, + output_cost_per_token=output_cost_per_token, + provider=custom_llm_provider, + ) +