diff --git a/litellm/llms/fireworks_ai/chat/transformation.py b/litellm/llms/fireworks_ai/chat/transformation.py index 08654520907..881ee27f07f 100644 --- a/litellm/llms/fireworks_ai/chat/transformation.py +++ b/litellm/llms/fireworks_ai/chat/transformation.py @@ -26,6 +26,7 @@ from litellm.types.utils import ( ProviderSpecificModelInfo, ) from litellm.utils import ( + get_model_cost_mutation_generation, supports_function_calling, supports_reasoning, supports_tool_choice, @@ -254,15 +255,16 @@ class FireworksAIConfig(OpenAIGPTConfig): # Cached index of fireworks_ai/* entries from litellm.model_cost. Building # this index requires a full scan of model_cost (tens of thousands of - # entries), so we memoize it and invalidate when the dict identity or size - # changes. The cached value is a list of (key_short, model_info) tuples - # restricted to fireworks_ai/* entries. + # entries), so we memoize it. The cache key is (id(model_cost), + # mutation_generation): the generation counter is bumped on every + # register_model / reload path, so add+remove or in-place value + # replacement (which can leave id and len unchanged) still invalidates. _fireworks_index_cache: Optional[Tuple[int, int, List[Tuple[str, dict]]]] = None @classmethod def _get_fireworks_index(cls) -> List[Tuple[str, dict]]: model_cost = litellm.model_cost - signature = (id(model_cost), len(model_cost)) + signature = (id(model_cost), get_model_cost_mutation_generation()) cached = cls._fireworks_index_cache if ( cached is not None diff --git a/litellm/utils.py b/litellm/utils.py index 63f0f280104..2487d39bd0d 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -5387,6 +5387,16 @@ def _strip_model_name(model: str, custom_llm_provider: Optional[str]) -> str: # Global case-insensitive lookup map for model_cost (built eagerly at module import) _model_cost_lowercase_map: Optional[Dict[str, str]] = None +# Monotonic counter bumped on every model_cost mutation. Consumers that +# memoize derived state (e.g. provider-specific indices) can include this +# value in their cache key so they invalidate even when key add+remove or +# in-place value replacement leaves len/id unchanged. +_model_cost_mutation_generation: int = 0 + + +def get_model_cost_mutation_generation() -> int: + return _model_cost_mutation_generation + def _invalidate_model_cost_lowercase_map() -> None: """Invalidate the case-insensitive lookup map for model_cost. @@ -5394,8 +5404,9 @@ def _invalidate_model_cost_lowercase_map() -> None: Call this whenever litellm.model_cost is modified to ensure the map is rebuilt. Also clears related LRU caches that depend on model_cost data. """ - global _model_cost_lowercase_map + global _model_cost_lowercase_map, _model_cost_mutation_generation _model_cost_lowercase_map = None + _model_cost_mutation_generation += 1 # Clear LRU caches that depend on model_cost data get_model_info.cache_clear()