From 6fafb46731cd94881e715c685d983c7b817fdf61 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 25 Aug 2026 16:45:50 -0700 Subject: [PATCH 1/3] fix(cost): apply Together AI cache read pricing and per-model registry rates --- litellm/cost_calculator.py | 11 +++- litellm/llms/together_ai/cost_calculator.py | 6 ++ ...odel_prices_and_context_window_backup.json | 38 +++++++++++-- model_prices_and_context_window.json | 38 +++++++++++-- tests/test_litellm/test_cost_calculator.py | 57 +++++++++++++++++++ .../test_together_ai_model_metadata.py | 48 ++++++++++++++++ 6 files changed, 183 insertions(+), 15 deletions(-) diff --git a/litellm/cost_calculator.py b/litellm/cost_calculator.py index 6536941a094..6b1218415af 100644 --- a/litellm/cost_calculator.py +++ b/litellm/cost_calculator.py @@ -75,7 +75,10 @@ from litellm.llms.perplexity.cost_calculator import ( from litellm.llms.tencent.cost_calculator import ( cost_per_token as tencent_cost_per_token, ) -from litellm.llms.together_ai.cost_calculator import get_model_params_and_category +from litellm.llms.together_ai.cost_calculator import ( + get_model_params_and_category, + has_together_registry_entry, +) from litellm.llms.vertex_ai.cost_calculator import ( cost_per_character as google_cost_per_character, ) @@ -1551,8 +1554,10 @@ def completion_cost( return MCPCostCalculator.calculate_mcp_tool_call_cost(litellm_logging_obj=litellm_logging_obj) # Calculate cost based on prompt_tokens, completion_tokens - if "togethercomputer" in model or "together_ai" in model or custom_llm_provider == "together_ai": - # together ai prices based on size of llm + if ( + "togethercomputer" in model or "together_ai" in model or custom_llm_provider == "together_ai" + ) and not has_together_registry_entry(model, litellm.model_cost): + # together ai prices unmapped models based on size of llm # get_model_params_and_category takes a model name and returns the category of LLM size it is in model_prices_and_context_window.json model = get_model_params_and_category(model, call_type=CallTypes(call_type)) diff --git a/litellm/llms/together_ai/cost_calculator.py b/litellm/llms/together_ai/cost_calculator.py index 431e94f1442..2f492496e7b 100644 --- a/litellm/llms/together_ai/cost_calculator.py +++ b/litellm/llms/together_ai/cost_calculator.py @@ -3,6 +3,7 @@ Handles calculating cost for together ai models """ import re +from collections.abc import Mapping from typing import Final from litellm.constants import ( @@ -18,6 +19,11 @@ from litellm.constants import ( from litellm.types.utils import CallTypes +def has_together_registry_entry(model: str, cost_map: Mapping[str, object]) -> bool: + stripped: Final = model.removeprefix("together_ai/") + return f"together_ai/{stripped}" in cost_map + + # Extract the number of billion parameters from the model name # only used for together_computer LLMs def get_model_params_and_category(model_name, call_type: CallTypes) -> str: diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 9ca8d9e1bac..528f428c802 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -38309,6 +38309,7 @@ "supports_tool_choice": true }, "together_ai/MiniMaxAI/MiniMax-M3": { + "cache_read_input_token_cost": 6e-08, "input_cost_per_token": 3e-07, "litellm_provider": "together_ai", "max_input_tokens": 524288, @@ -38319,6 +38320,7 @@ "source": "https://docs.together.ai/docs/serverless-models", "supports_function_calling": true, "supports_parallel_function_calling": true, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true, @@ -38362,14 +38364,16 @@ "supports_reasoning": true }, "together_ai/Qwen/Qwen3.7-Max": { - "input_cost_per_token": 1.25e-06, + "cache_read_input_token_cost": 5e-07, + "input_cost_per_token": 2.5e-06, "litellm_provider": "together_ai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, "max_tokens": 1000000, "mode": "chat", - "output_cost_per_token": 3.75e-06, - "source": "https://docs.together.ai/docs/serverless-models" + "output_cost_per_token": 7.5e-06, + "source": "https://docs.together.ai/docs/serverless-models", + "supports_prompt_caching": true }, "together_ai/Qwen/Qwen3.7-Plus": { "input_cost_per_token": 3.2e-07, @@ -38382,6 +38386,7 @@ "source": "https://docs.together.ai/docs/serverless-models" }, "together_ai/Qwen/Qwen3.8-2.4T-A95B": { + "cache_read_input_token_cost": 5e-07, "input_cost_per_token": 2.5e-06, "litellm_provider": "together_ai", "max_input_tokens": 1010000, @@ -38389,7 +38394,8 @@ "max_tokens": 1010000, "mode": "chat", "output_cost_per_token": 6.25e-06, - "source": "https://docs.together.ai/docs/serverless-models" + "source": "https://docs.together.ai/docs/serverless-models", + "supports_prompt_caching": true }, "together_ai/arize-ai/qwen-2-1.5b-instruct": { "input_cost_per_token": 1e-07, @@ -38402,6 +38408,7 @@ "source": "https://docs.together.ai/docs/serverless-models" }, "together_ai/deepseek-ai/DeepSeek-V4-Flash-0731": { + "cache_read_input_token_cost": 3e-08, "input_cost_per_token": 1.4e-07, "litellm_provider": "together_ai", "max_input_tokens": 1048576, @@ -38412,10 +38419,12 @@ "source": "https://docs.together.ai/docs/serverless-models", "supports_function_calling": true, "supports_parallel_function_calling": true, + "supports_prompt_caching": true, "supports_response_schema": true, "supports_tool_choice": true }, "together_ai/deepseek-ai/DeepSeek-V4-Pro": { + "cache_read_input_token_cost": 2e-07, "input_cost_per_token": 1.74e-06, "litellm_provider": "together_ai", "max_input_tokens": 512000, @@ -38426,11 +38435,13 @@ "source": "https://docs.together.ai/docs/serverless-models", "supports_function_calling": true, "supports_parallel_function_calling": true, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true }, "together_ai/deepseek-ai/DeepSeek-V4-Pro-0813": { + "cache_read_input_token_cost": 1.3e-07, "input_cost_per_token": 1.32e-06, "litellm_provider": "together_ai", "max_input_tokens": 1048576, @@ -38441,6 +38452,7 @@ "source": "https://docs.together.ai/docs/serverless-models", "supports_function_calling": true, "supports_parallel_function_calling": true, + "supports_prompt_caching": true, "supports_response_schema": true, "supports_tool_choice": true }, @@ -38490,6 +38502,7 @@ "source": "https://docs.together.ai/docs/serverless-models" }, "together_ai/meta-models/Muse-Glimmer-30B": { + "cache_read_input_token_cost": 4e-08, "input_cost_per_token": 3.5e-07, "litellm_provider": "together_ai", "max_input_tokens": 131072, @@ -38497,9 +38510,11 @@ "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.5e-06, - "source": "https://docs.together.ai/docs/serverless-models" + "source": "https://docs.together.ai/docs/serverless-models", + "supports_prompt_caching": true }, "together_ai/moonshotai/Kimi-K2.7-Code": { + "cache_read_input_token_cost": 1.9e-07, "input_cost_per_token": 9.5e-07, "litellm_provider": "together_ai", "max_input_tokens": 262144, @@ -38510,11 +38525,13 @@ "source": "https://docs.together.ai/docs/serverless-models", "supports_function_calling": true, "supports_parallel_function_calling": true, + "supports_prompt_caching": true, "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true }, "together_ai/moonshotai/Kimi-K3": { + "cache_read_input_token_cost": 3e-07, "input_cost_per_token": 3e-06, "litellm_provider": "together_ai", "max_input_tokens": 1048576, @@ -38525,12 +38542,14 @@ "source": "https://docs.together.ai/docs/serverless-models", "supports_function_calling": true, "supports_parallel_function_calling": true, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true }, "together_ai/nvidia/nemotron-3-ultra-550b-a55b": { + "cache_read_input_token_cost": 2e-07, "input_cost_per_token": 6e-07, "litellm_provider": "together_ai", "max_input_tokens": 512288, @@ -38541,6 +38560,7 @@ "source": "https://docs.together.ai/docs/serverless-models", "supports_function_calling": true, "supports_parallel_function_calling": true, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true @@ -38556,6 +38576,7 @@ "source": "https://docs.together.ai/docs/serverless-models" }, "together_ai/thinkingmachines/Inkling": { + "cache_read_input_token_cost": 1.7e-07, "input_cost_per_token": 1e-06, "litellm_provider": "together_ai", "max_input_tokens": 524288, @@ -38566,10 +38587,12 @@ "source": "https://docs.together.ai/docs/serverless-models", "supports_function_calling": true, "supports_parallel_function_calling": true, + "supports_prompt_caching": true, "supports_response_schema": true, "supports_tool_choice": true }, "together_ai/thinkingmachines/Inkling-Small": { + "cache_read_input_token_cost": 1e-07, "input_cost_per_token": 5e-07, "litellm_provider": "together_ai", "max_input_tokens": 524288, @@ -38577,9 +38600,11 @@ "max_tokens": 524288, "mode": "chat", "output_cost_per_token": 1.2e-06, - "source": "https://docs.together.ai/docs/serverless-models" + "source": "https://docs.together.ai/docs/serverless-models", + "supports_prompt_caching": true }, "together_ai/zai-org/GLM-5.2": { + "cache_read_input_token_cost": 2.6e-07, "input_cost_per_token": 1.4e-06, "litellm_provider": "together_ai", "max_input_tokens": 1048575, @@ -38590,6 +38615,7 @@ "source": "https://docs.together.ai/docs/serverless-models", "supports_function_calling": true, "supports_parallel_function_calling": true, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 9ca8d9e1bac..528f428c802 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -38309,6 +38309,7 @@ "supports_tool_choice": true }, "together_ai/MiniMaxAI/MiniMax-M3": { + "cache_read_input_token_cost": 6e-08, "input_cost_per_token": 3e-07, "litellm_provider": "together_ai", "max_input_tokens": 524288, @@ -38319,6 +38320,7 @@ "source": "https://docs.together.ai/docs/serverless-models", "supports_function_calling": true, "supports_parallel_function_calling": true, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true, @@ -38362,14 +38364,16 @@ "supports_reasoning": true }, "together_ai/Qwen/Qwen3.7-Max": { - "input_cost_per_token": 1.25e-06, + "cache_read_input_token_cost": 5e-07, + "input_cost_per_token": 2.5e-06, "litellm_provider": "together_ai", "max_input_tokens": 1000000, "max_output_tokens": 1000000, "max_tokens": 1000000, "mode": "chat", - "output_cost_per_token": 3.75e-06, - "source": "https://docs.together.ai/docs/serverless-models" + "output_cost_per_token": 7.5e-06, + "source": "https://docs.together.ai/docs/serverless-models", + "supports_prompt_caching": true }, "together_ai/Qwen/Qwen3.7-Plus": { "input_cost_per_token": 3.2e-07, @@ -38382,6 +38386,7 @@ "source": "https://docs.together.ai/docs/serverless-models" }, "together_ai/Qwen/Qwen3.8-2.4T-A95B": { + "cache_read_input_token_cost": 5e-07, "input_cost_per_token": 2.5e-06, "litellm_provider": "together_ai", "max_input_tokens": 1010000, @@ -38389,7 +38394,8 @@ "max_tokens": 1010000, "mode": "chat", "output_cost_per_token": 6.25e-06, - "source": "https://docs.together.ai/docs/serverless-models" + "source": "https://docs.together.ai/docs/serverless-models", + "supports_prompt_caching": true }, "together_ai/arize-ai/qwen-2-1.5b-instruct": { "input_cost_per_token": 1e-07, @@ -38402,6 +38408,7 @@ "source": "https://docs.together.ai/docs/serverless-models" }, "together_ai/deepseek-ai/DeepSeek-V4-Flash-0731": { + "cache_read_input_token_cost": 3e-08, "input_cost_per_token": 1.4e-07, "litellm_provider": "together_ai", "max_input_tokens": 1048576, @@ -38412,10 +38419,12 @@ "source": "https://docs.together.ai/docs/serverless-models", "supports_function_calling": true, "supports_parallel_function_calling": true, + "supports_prompt_caching": true, "supports_response_schema": true, "supports_tool_choice": true }, "together_ai/deepseek-ai/DeepSeek-V4-Pro": { + "cache_read_input_token_cost": 2e-07, "input_cost_per_token": 1.74e-06, "litellm_provider": "together_ai", "max_input_tokens": 512000, @@ -38426,11 +38435,13 @@ "source": "https://docs.together.ai/docs/serverless-models", "supports_function_calling": true, "supports_parallel_function_calling": true, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true }, "together_ai/deepseek-ai/DeepSeek-V4-Pro-0813": { + "cache_read_input_token_cost": 1.3e-07, "input_cost_per_token": 1.32e-06, "litellm_provider": "together_ai", "max_input_tokens": 1048576, @@ -38441,6 +38452,7 @@ "source": "https://docs.together.ai/docs/serverless-models", "supports_function_calling": true, "supports_parallel_function_calling": true, + "supports_prompt_caching": true, "supports_response_schema": true, "supports_tool_choice": true }, @@ -38490,6 +38502,7 @@ "source": "https://docs.together.ai/docs/serverless-models" }, "together_ai/meta-models/Muse-Glimmer-30B": { + "cache_read_input_token_cost": 4e-08, "input_cost_per_token": 3.5e-07, "litellm_provider": "together_ai", "max_input_tokens": 131072, @@ -38497,9 +38510,11 @@ "max_tokens": 131072, "mode": "chat", "output_cost_per_token": 1.5e-06, - "source": "https://docs.together.ai/docs/serverless-models" + "source": "https://docs.together.ai/docs/serverless-models", + "supports_prompt_caching": true }, "together_ai/moonshotai/Kimi-K2.7-Code": { + "cache_read_input_token_cost": 1.9e-07, "input_cost_per_token": 9.5e-07, "litellm_provider": "together_ai", "max_input_tokens": 262144, @@ -38510,11 +38525,13 @@ "source": "https://docs.together.ai/docs/serverless-models", "supports_function_calling": true, "supports_parallel_function_calling": true, + "supports_prompt_caching": true, "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true }, "together_ai/moonshotai/Kimi-K3": { + "cache_read_input_token_cost": 3e-07, "input_cost_per_token": 3e-06, "litellm_provider": "together_ai", "max_input_tokens": 1048576, @@ -38525,12 +38542,14 @@ "source": "https://docs.together.ai/docs/serverless-models", "supports_function_calling": true, "supports_parallel_function_calling": true, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true, "supports_vision": true }, "together_ai/nvidia/nemotron-3-ultra-550b-a55b": { + "cache_read_input_token_cost": 2e-07, "input_cost_per_token": 6e-07, "litellm_provider": "together_ai", "max_input_tokens": 512288, @@ -38541,6 +38560,7 @@ "source": "https://docs.together.ai/docs/serverless-models", "supports_function_calling": true, "supports_parallel_function_calling": true, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true @@ -38556,6 +38576,7 @@ "source": "https://docs.together.ai/docs/serverless-models" }, "together_ai/thinkingmachines/Inkling": { + "cache_read_input_token_cost": 1.7e-07, "input_cost_per_token": 1e-06, "litellm_provider": "together_ai", "max_input_tokens": 524288, @@ -38566,10 +38587,12 @@ "source": "https://docs.together.ai/docs/serverless-models", "supports_function_calling": true, "supports_parallel_function_calling": true, + "supports_prompt_caching": true, "supports_response_schema": true, "supports_tool_choice": true }, "together_ai/thinkingmachines/Inkling-Small": { + "cache_read_input_token_cost": 1e-07, "input_cost_per_token": 5e-07, "litellm_provider": "together_ai", "max_input_tokens": 524288, @@ -38577,9 +38600,11 @@ "max_tokens": 524288, "mode": "chat", "output_cost_per_token": 1.2e-06, - "source": "https://docs.together.ai/docs/serverless-models" + "source": "https://docs.together.ai/docs/serverless-models", + "supports_prompt_caching": true }, "together_ai/zai-org/GLM-5.2": { + "cache_read_input_token_cost": 2.6e-07, "input_cost_per_token": 1.4e-06, "litellm_provider": "together_ai", "max_input_tokens": 1048575, @@ -38590,6 +38615,7 @@ "source": "https://docs.together.ai/docs/serverless-models", "supports_function_calling": true, "supports_parallel_function_calling": true, + "supports_prompt_caching": true, "supports_reasoning": true, "supports_response_schema": true, "supports_tool_choice": true diff --git a/tests/test_litellm/test_cost_calculator.py b/tests/test_litellm/test_cost_calculator.py index 1f1c9be973f..bf230bf794c 100644 --- a/tests/test_litellm/test_cost_calculator.py +++ b/tests/test_litellm/test_cost_calculator.py @@ -3781,3 +3781,60 @@ def test_completion_cost_prices_anthropic_shaped_cache_read_tokens(_local_model_ ) assert cost == pytest.approx(3 * 4e-6 + 4014 * 4e-7 + 5 * 2e-5, rel=1e-9) + + +def _together_chat_response(model: str, prompt_tokens: int, completion_tokens: int, cached_tokens: int) -> ModelResponse: + return ModelResponse( + id="chatcmpl-together-cache", + choices=[{"finish_reason": "stop", "index": 0, "message": {"content": "acknowledged", "role": "assistant"}}], + created=1756164000, + model=model, + object="chat.completion", + usage=Usage( + prompt_tokens=prompt_tokens, + completion_tokens=completion_tokens, + total_tokens=prompt_tokens + completion_tokens, + prompt_tokens_details=PromptTokensDetailsWrapper(cached_tokens=cached_tokens), + ), + ) + + +def test_completion_cost_prices_together_cached_tokens_at_cache_read_rate(_local_model_cost_map): + """Regression: Together reports prompt_tokens_details.cached_tokens but no together_ai + registry entry carried cache_read_input_token_cost, so cache-hit tokens were priced at + 0.0 and spend on cache-heavy workloads was understated.""" + + cost = completion_cost( + completion_response=_together_chat_response( + model="deepseek-ai/DeepSeek-V4-Flash-0731", prompt_tokens=7864, completion_tokens=16, cached_tokens=7863 + ), + custom_llm_provider="together_ai", + ) + + assert cost == pytest.approx(1 * 1.4e-07 + 7863 * 3e-08 + 16 * 2.8e-07, rel=1e-9) + + +def test_completion_cost_together_mapped_model_skips_size_bucket(_local_model_cost_map): + """Regression: any together model whose name matches (\\d+b) was rewritten to a + together-ai-* size bucket before the registry lookup, so mapped models like + Muse-Glimmer-30B never used their per-model rates, cache fields included.""" + + cost = completion_cost( + completion_response=_together_chat_response( + model="meta-models/Muse-Glimmer-30B", prompt_tokens=63, completion_tokens=16, cached_tokens=0 + ), + custom_llm_provider="together_ai", + ) + + assert cost == pytest.approx(63 * 3.5e-07 + 16 * 1.5e-06, rel=1e-9) + + +def test_completion_cost_together_unmapped_model_still_uses_size_bucket(_local_model_cost_map): + cost = completion_cost( + completion_response=_together_chat_response( + model="qwen/Qwen2-72B-Instruct", prompt_tokens=23, completion_tokens=15, cached_tokens=0 + ), + custom_llm_provider="together_ai", + ) + + assert cost == pytest.approx((23 + 15) * 9e-07, rel=1e-9) diff --git a/tests/test_litellm/test_together_ai_model_metadata.py b/tests/test_litellm/test_together_ai_model_metadata.py index 5a0aadf4737..e4531a1c283 100644 --- a/tests/test_litellm/test_together_ai_model_metadata.py +++ b/tests/test_litellm/test_together_ai_model_metadata.py @@ -159,3 +159,51 @@ def test_together_backup_cost_map_in_sync(cost_map: CostMap): together_main = {k: v for k, v in cost_map.items() if k.startswith("together_ai/")} together_backup = {k: v for k, v in backup.items() if k.startswith("together_ai/")} assert together_backup == together_main + + +CACHED_INPUT_MODELS: Final = ( + "together_ai/moonshotai/Kimi-K3", + "together_ai/zai-org/GLM-5.2", + "together_ai/meta-models/Muse-Glimmer-30B", + "together_ai/Qwen/Qwen3.8-2.4T-A95B", + "together_ai/deepseek-ai/DeepSeek-V4-Pro-0813", + "together_ai/deepseek-ai/DeepSeek-V4-Flash-0731", + "together_ai/thinkingmachines/Inkling", + "together_ai/MiniMaxAI/MiniMax-M3", + "together_ai/thinkingmachines/Inkling-Small", + "together_ai/moonshotai/Kimi-K2.7-Code", + "together_ai/deepseek-ai/DeepSeek-V4-Pro", + "together_ai/nvidia/nemotron-3-ultra-550b-a55b", + "together_ai/Qwen/Qwen3.7-Max", +) + + +@pytest.mark.parametrize("model", CACHED_INPUT_MODELS) +def test_together_cached_input_model_carries_cache_read_pricing(cost_map: CostMap, model: str): + info = cost_map.get(model) + assert info is not None, f"{model} missing from model_prices_and_context_window.json" + assert info.get("supports_prompt_caching") is True + cache_read = info.get("cache_read_input_token_cost") + assert isinstance(cache_read, float) + assert 0 < cache_read < info["input_cost_per_token"] + assert "cache_creation_input_token_cost" not in info + + +def test_together_prompt_caching_flag_implies_cache_read_rate(cost_map: CostMap): + for model, info in cost_map.items(): + if model.startswith("together_ai/") and info.get("supports_prompt_caching"): + assert "cache_read_input_token_cost" in info, f"{model} flags caching without a cache read rate" + + +def test_together_deepseek_v4_flash_cache_read_rate(cost_map: CostMap): + info = cost_map["together_ai/deepseek-ai/DeepSeek-V4-Flash-0731"] + assert info["input_cost_per_token"] == 1.4e-07 + assert info["cache_read_input_token_cost"] == 3e-08 + assert info["output_cost_per_token"] == 2.8e-07 + + +def test_together_qwen_37_max_repriced_to_current_together_rate(cost_map: CostMap): + info = cost_map["together_ai/Qwen/Qwen3.7-Max"] + assert info["input_cost_per_token"] == 2.5e-06 + assert info["output_cost_per_token"] == 7.5e-06 + assert info["cache_read_input_token_cost"] == 5e-07 From ac866e98c8d1d3a6622663f8625dd1cd6e05ea5a Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 25 Aug 2026 16:56:17 -0700 Subject: [PATCH 2/3] chore(cost): drop redundant together fallback comment --- litellm/cost_calculator.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/litellm/cost_calculator.py b/litellm/cost_calculator.py index 6b1218415af..78a555940c8 100644 --- a/litellm/cost_calculator.py +++ b/litellm/cost_calculator.py @@ -1557,9 +1557,6 @@ def completion_cost( if ( "togethercomputer" in model or "together_ai" in model or custom_llm_provider == "together_ai" ) and not has_together_registry_entry(model, litellm.model_cost): - # together ai prices unmapped models based on size of llm - # get_model_params_and_category takes a model name and returns the category of LLM size it is in model_prices_and_context_window.json - model = get_model_params_and_category(model, call_type=CallTypes(call_type)) # replicate llms are calculate based on time for request running From abe9af622bbb6c6e497022876b3d0bbb26de9fcb Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Tue, 25 Aug 2026 17:09:12 -0700 Subject: [PATCH 3/3] fix(cost): keep size buckets for Together registry rows without pricing --- litellm/cost_calculator.py | 4 ++-- litellm/llms/together_ai/cost_calculator.py | 5 +++-- tests/test_litellm/test_cost_calculator.py | 13 +++++++++++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/litellm/cost_calculator.py b/litellm/cost_calculator.py index 78a555940c8..153b8457e38 100644 --- a/litellm/cost_calculator.py +++ b/litellm/cost_calculator.py @@ -77,7 +77,7 @@ from litellm.llms.tencent.cost_calculator import ( ) from litellm.llms.together_ai.cost_calculator import ( get_model_params_and_category, - has_together_registry_entry, + has_together_registry_pricing, ) from litellm.llms.vertex_ai.cost_calculator import ( cost_per_character as google_cost_per_character, @@ -1556,7 +1556,7 @@ def completion_cost( # Calculate cost based on prompt_tokens, completion_tokens if ( "togethercomputer" in model or "together_ai" in model or custom_llm_provider == "together_ai" - ) and not has_together_registry_entry(model, litellm.model_cost): + ) and not has_together_registry_pricing(model, litellm.model_cost): model = get_model_params_and_category(model, call_type=CallTypes(call_type)) # replicate llms are calculate based on time for request running diff --git a/litellm/llms/together_ai/cost_calculator.py b/litellm/llms/together_ai/cost_calculator.py index 2f492496e7b..6fc2c949fa6 100644 --- a/litellm/llms/together_ai/cost_calculator.py +++ b/litellm/llms/together_ai/cost_calculator.py @@ -19,9 +19,10 @@ from litellm.constants import ( from litellm.types.utils import CallTypes -def has_together_registry_entry(model: str, cost_map: Mapping[str, object]) -> bool: +def has_together_registry_pricing(model: str, cost_map: Mapping[str, object]) -> bool: stripped: Final = model.removeprefix("together_ai/") - return f"together_ai/{stripped}" in cost_map + entry: Final = cost_map.get(f"together_ai/{stripped}") + return isinstance(entry, Mapping) and "input_cost_per_token" in entry # Extract the number of billion parameters from the model name diff --git a/tests/test_litellm/test_cost_calculator.py b/tests/test_litellm/test_cost_calculator.py index bf230bf794c..edc0635a741 100644 --- a/tests/test_litellm/test_cost_calculator.py +++ b/tests/test_litellm/test_cost_calculator.py @@ -3838,3 +3838,16 @@ def test_completion_cost_together_unmapped_model_still_uses_size_bucket(_local_m ) assert cost == pytest.approx((23 + 15) * 9e-07, rel=1e-9) + + +def test_completion_cost_together_metadata_only_model_still_uses_size_bucket(_local_model_cost_map): + assert "input_cost_per_token" not in litellm.model_cost["together_ai/togethercomputer/CodeLlama-34b-Instruct"] + + cost = completion_cost( + completion_response=_together_chat_response( + model="togethercomputer/CodeLlama-34b-Instruct", prompt_tokens=23, completion_tokens=15, cached_tokens=0 + ), + custom_llm_provider="together_ai", + ) + + assert cost == pytest.approx((23 + 15) * 8e-07, rel=1e-9)