From 4b215e2a60ff21079863e0a7151532a947a52a40 Mon Sep 17 00:00:00 2001 From: kerry Date: Fri, 18 Sep 2026 18:17:36 +0000 Subject: [PATCH] fix(schema): require a schedule and well-formed windows in off_peak_pricing Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- ci_cd/generate_model_prices_schema.py | 18 +++++++++++++----- tests/test_litellm/test_model_prices_schema.py | 7 ++++++- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/ci_cd/generate_model_prices_schema.py b/ci_cd/generate_model_prices_schema.py index bf669ccce29..4ba4368e33c 100644 --- a/ci_cd/generate_model_prices_schema.py +++ b/ci_cd/generate_model_prices_schema.py @@ -19,6 +19,10 @@ NONNEG_NUMBER: JsonSchema = {"type": "number", "minimum": 0} NONNEG_INTEGER: JsonSchema = {"type": "integer", "minimum": 0} BOOLEAN: JsonSchema = {"type": "boolean"} STRING: JsonSchema = {"type": "string"} +TIME_WINDOW: JsonSchema = {"type": "string", "pattern": r"^([01]\d|2[0-3]):[0-5]\d-([01]\d|2[0-3]):[0-5]\d$"} +WEEKDAY_PATTERN = ( + r"(?i)^(mon|monday|tue|tues|tuesday|wed|wednesday|thu|thur|thurs|thursday|fri|friday|sat|saturday|sun|sunday)$" +) EXTRA_BOOLEAN_KEYS = frozenset( { @@ -33,7 +37,7 @@ EXTRA_BOOLEAN_KEYS = frozenset( HOURS_UTC: JsonSchema = { "description": 'UTC "HH:MM-HH:MM" window, or a list of them; a window may wrap past midnight.', - "oneOf": [STRING, {"type": "array", "items": STRING, "minItems": 1}], + "oneOf": [TIME_WINDOW, {"type": "array", "items": TIME_WINDOW, "minItems": 1}], } OFF_PEAK_WINDOW: JsonSchema = { @@ -43,7 +47,12 @@ OFF_PEAK_WINDOW: JsonSchema = { "weekdays": { "type": "array", "description": "ISO-8601 weekday numbers (1 = Monday .. 7 = Sunday) or English day names the window applies on.", - "items": {"oneOf": [{"type": "integer", "minimum": 1, "maximum": 7}, STRING]}, + "items": { + "oneOf": [ + {"type": "integer", "minimum": 1, "maximum": 7}, + {"type": "string", "pattern": WEEKDAY_PATTERN}, + ] + }, "minItems": 1, }, }, @@ -68,6 +77,7 @@ OBJECT_KEYS: dict[str, JsonSchema] = { "cache_read_input_token_cost": NONNEG_NUMBER, "cache_creation_input_token_cost": NONNEG_NUMBER, }, + "anyOf": [{"required": ["hours_utc"]}, {"required": ["windows"]}], "additionalProperties": False, }, "search_context_cost_per_query": { @@ -365,9 +375,7 @@ def render(schema: JsonSchema) -> str: def validation_errors(prices: dict, schema: JsonSchema) -> tuple: - validator = jsonschema.Draft202012Validator( - schema, format_checker=jsonschema.Draft202012Validator.FORMAT_CHECKER - ) + validator = jsonschema.Draft202012Validator(schema, format_checker=jsonschema.Draft202012Validator.FORMAT_CHECKER) return tuple( f"{'.'.join(str(part) for part in error.absolute_path)}: {error.message}" for error in validator.iter_errors(prices) diff --git a/tests/test_litellm/test_model_prices_schema.py b/tests/test_litellm/test_model_prices_schema.py index 65aaa2870ef..a9015397b32 100644 --- a/tests/test_litellm/test_model_prices_schema.py +++ b/tests/test_litellm/test_model_prices_schema.py @@ -133,7 +133,7 @@ OFF_PEAK_ENTRY: Final = MappingProxyType( "output_cost_per_token": 8e-6, "off_peak_pricing": { "hours_utc": "16:30-00:30", - "windows": [{"hours_utc": ["00:30-02:00"], "weekdays": [6, "Sunday"]}], + "windows": [{"hours_utc": ["00:30-02:00"], "weekdays": [6, "Sunday", "mon", "THURS"]}], "weekday_timezone": "Asia/Shanghai", "input_cost_per_token": 1e-6, "output_cost_per_token": 4e-6, @@ -160,6 +160,11 @@ def test_generator_classifies_off_peak_pricing_as_a_windowed_rate_block(): {"windows": [{"weekdays": [6]}], "input_cost_per_token": 1e-6}, {"windows": [{"hours_utc": "00:30-02:00", "weekdays": [0]}], "input_cost_per_token": 1e-6}, {"windows": [], "input_cost_per_token": 1e-6}, + {"input_cost_per_token": 1e-6}, + {"hours_utc": "16:30", "input_cost_per_token": 1e-6}, + {"hours_utc": "25:00-01:00", "input_cost_per_token": 1e-6}, + {"hours_utc": ["16:30-00:30", "4pm-midnight"], "input_cost_per_token": 1e-6}, + {"windows": [{"hours_utc": "00:30-02:00", "weekdays": ["Funday"]}], "input_cost_per_token": 1e-6}, ], ) def test_generated_off_peak_schema_rejects_malformed_blocks(block: dict):