diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index ab034d9f51b..6d87e0b5997 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -16094,6 +16094,181 @@ "output_cost_per_token": 0.0, "output_vector_size": 2560 }, + "gmi/anthropic/claude-opus-4.5": { + "input_cost_per_token": 5e-06, + "litellm_provider": "gmi", + "max_input_tokens": 409600, + "max_output_tokens": 32000, + "max_tokens": 32000, + "mode": "chat", + "output_cost_per_token": 2.5e-05, + "supports_function_calling": true, + "supports_vision": true + }, + "gmi/anthropic/claude-sonnet-4.5": { + "input_cost_per_token": 3e-06, + "litellm_provider": "gmi", + "max_input_tokens": 409600, + "max_output_tokens": 32000, + "max_tokens": 32000, + "mode": "chat", + "output_cost_per_token": 1.5e-05, + "supports_function_calling": true, + "supports_vision": true + }, + "gmi/anthropic/claude-sonnet-4": { + "input_cost_per_token": 3e-06, + "litellm_provider": "gmi", + "max_input_tokens": 409600, + "max_output_tokens": 32000, + "max_tokens": 32000, + "mode": "chat", + "output_cost_per_token": 1.5e-05, + "supports_function_calling": true, + "supports_vision": true + }, + "gmi/anthropic/claude-opus-4": { + "input_cost_per_token": 1.5e-05, + "litellm_provider": "gmi", + "max_input_tokens": 409600, + "max_output_tokens": 32000, + "max_tokens": 32000, + "mode": "chat", + "output_cost_per_token": 7.5e-05, + "supports_function_calling": true, + "supports_vision": true + }, + "gmi/openai/gpt-5.2": { + "input_cost_per_token": 1.75e-06, + "litellm_provider": "gmi", + "max_input_tokens": 409600, + "max_output_tokens": 32000, + "max_tokens": 32000, + "mode": "chat", + "output_cost_per_token": 1.4e-05, + "supports_function_calling": true + }, + "gmi/openai/gpt-5.1": { + "input_cost_per_token": 1.25e-06, + "litellm_provider": "gmi", + "max_input_tokens": 409600, + "max_output_tokens": 32000, + "max_tokens": 32000, + "mode": "chat", + "output_cost_per_token": 1e-05, + "supports_function_calling": true + }, + "gmi/openai/gpt-5": { + "input_cost_per_token": 1.25e-06, + "litellm_provider": "gmi", + "max_input_tokens": 409600, + "max_output_tokens": 32000, + "max_tokens": 32000, + "mode": "chat", + "output_cost_per_token": 1e-05, + "supports_function_calling": true + }, + "gmi/openai/gpt-4o": { + "input_cost_per_token": 2.5e-06, + "litellm_provider": "gmi", + "max_input_tokens": 131072, + "max_output_tokens": 16384, + "max_tokens": 16384, + "mode": "chat", + "output_cost_per_token": 1e-05, + "supports_function_calling": true, + "supports_vision": true + }, + "gmi/openai/gpt-4o-mini": { + "input_cost_per_token": 1.5e-07, + "litellm_provider": "gmi", + "max_input_tokens": 131072, + "max_output_tokens": 16384, + "max_tokens": 16384, + "mode": "chat", + "output_cost_per_token": 6e-07, + "supports_function_calling": true, + "supports_vision": true + }, + "gmi/deepseek-ai/DeepSeek-V3.2": { + "input_cost_per_token": 2.8e-07, + "litellm_provider": "gmi", + "max_input_tokens": 163840, + "max_output_tokens": 16384, + "max_tokens": 16384, + "mode": "chat", + "output_cost_per_token": 4e-07, + "supports_function_calling": true + }, + "gmi/deepseek-ai/DeepSeek-V3-0324": { + "input_cost_per_token": 2.8e-07, + "litellm_provider": "gmi", + "max_input_tokens": 163840, + "max_output_tokens": 16384, + "max_tokens": 16384, + "mode": "chat", + "output_cost_per_token": 8.8e-07, + "supports_function_calling": true + }, + "gmi/google/gemini-3-pro-preview": { + "input_cost_per_token": 2e-06, + "litellm_provider": "gmi", + "max_input_tokens": 1048576, + "max_output_tokens": 65536, + "max_tokens": 65536, + "mode": "chat", + "output_cost_per_token": 1.2e-05, + "supports_function_calling": true, + "supports_vision": true + }, + "gmi/google/gemini-3-flash-preview": { + "input_cost_per_token": 5e-07, + "litellm_provider": "gmi", + "max_input_tokens": 1048576, + "max_output_tokens": 65536, + "max_tokens": 65536, + "mode": "chat", + "output_cost_per_token": 3e-06, + "supports_function_calling": true, + "supports_vision": true + }, + "gmi/moonshotai/Kimi-K2-Thinking": { + "input_cost_per_token": 8e-07, + "litellm_provider": "gmi", + "max_input_tokens": 262144, + "max_output_tokens": 16384, + "max_tokens": 16384, + "mode": "chat", + "output_cost_per_token": 1.2e-06 + }, + "gmi/MiniMaxAI/MiniMax-M2.1": { + "input_cost_per_token": 3e-07, + "litellm_provider": "gmi", + "max_input_tokens": 196608, + "max_output_tokens": 16384, + "max_tokens": 16384, + "mode": "chat", + "output_cost_per_token": 1.2e-06 + }, + "gmi/Qwen/Qwen3-VL-235B-A22B-Instruct-FP8": { + "input_cost_per_token": 3e-07, + "litellm_provider": "gmi", + "max_input_tokens": 262144, + "max_output_tokens": 16384, + "max_tokens": 16384, + "mode": "chat", + "output_cost_per_token": 1.4e-06, + "supports_vision": true + }, + "gmi/zai-org/GLM-4.7-FP8": { + "input_cost_per_token": 4e-07, + "litellm_provider": "gmi", + "max_input_tokens": 202752, + "max_output_tokens": 16384, + "max_tokens": 16384, + "mode": "chat", + "output_cost_per_token": 2e-06 + }, "google.gemma-3-12b-it": { "input_cost_per_token": 9e-08, "litellm_provider": "bedrock_converse", diff --git a/litellm/proxy/policy_engine/policy_matcher.py b/litellm/proxy/policy_engine/policy_matcher.py new file mode 100644 index 00000000000..96b79f17168 --- /dev/null +++ b/litellm/proxy/policy_engine/policy_matcher.py @@ -0,0 +1,133 @@ +""" +Policy Matcher - Matches requests against policy scopes. + +Uses existing wildcard pattern matching helpers to determine which policies +apply to a given request based on team alias, key alias, and model. +""" + +from typing import Dict, List, Optional + +from litellm._logging import verbose_proxy_logger +from litellm.proxy.auth.route_checks import RouteChecks +from litellm.types.proxy.policy_engine import Policy, PolicyMatchContext, PolicyScope + + +class PolicyMatcher: + """ + Matches incoming requests against policy scopes. + + Supports wildcard patterns: + - "*" matches everything + - "prefix-*" matches anything starting with "prefix-" + """ + + @staticmethod + def matches_pattern(value: Optional[str], patterns: List[str]) -> bool: + """ + Check if a value matches any of the given patterns. + + Uses the existing RouteChecks._route_matches_wildcard_pattern helper. + + Args: + value: The value to check (e.g., team alias, key alias, model) + patterns: List of patterns to match against + + Returns: + True if value matches any pattern, False otherwise + """ + # If no value provided, only match if patterns include "*" + if value is None: + return "*" in patterns + + for pattern in patterns: + # Use existing wildcard pattern matching helper + if RouteChecks._route_matches_wildcard_pattern( + route=value, pattern=pattern + ): + return True + + return False + + @staticmethod + def scope_matches(scope: PolicyScope, context: PolicyMatchContext) -> bool: + """ + Check if a policy scope matches the given context. + + A scope matches if ALL of its fields match: + - teams matches context.team_alias + - keys matches context.key_alias + - models matches context.model + + Args: + scope: The policy scope to check + context: The request context + + Returns: + True if scope matches context, False otherwise + """ + # Check teams + if not PolicyMatcher.matches_pattern(context.team_alias, scope.get_teams()): + return False + + # Check keys + if not PolicyMatcher.matches_pattern(context.key_alias, scope.get_keys()): + return False + + # Check models + if not PolicyMatcher.matches_pattern(context.model, scope.get_models()): + return False + + return True + + @staticmethod + def get_matching_policies( + policies: Dict[str, Policy], + context: PolicyMatchContext, + ) -> List[str]: + """ + Get list of policy names that match the given context. + + Args: + policies: Dictionary of all policies + context: The request context to match against + + Returns: + List of policy names that match the context + """ + matching: List[str] = [] + + for policy_name, policy in policies.items(): + if PolicyMatcher.scope_matches(scope=policy.scope, context=context): + matching.append(policy_name) + verbose_proxy_logger.debug( + f"Policy '{policy_name}' matches context: " + f"team_alias={context.team_alias}, " + f"key_alias={context.key_alias}, " + f"model={context.model}" + ) + + return matching + + @staticmethod + def get_matching_policies_from_registry( + context: PolicyMatchContext, + ) -> List[str]: + """ + Get list of policy names that match the given context from the global registry. + + Args: + context: The request context to match against + + Returns: + List of policy names that match the context + """ + from litellm.proxy.policy_engine.policy_registry import get_policy_registry + + registry = get_policy_registry() + if not registry.is_initialized(): + return [] + + return PolicyMatcher.get_matching_policies( + policies=registry.get_all_policies(), + context=context, + )