mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-01 02:02:20 +00:00
fix(cost): require every override_dates entry to parse
A window rule's override_dates now applies only when the value is a non-empty sequence of strings that all parse as YYYY-MM-DD. One malformed entry disables the whole list, matching how the rule already disappears when override_dates itself is malformed. Also updates the hand-written schema in test_utils for the new windows key.
This commit is contained in:
parent
33374a9207
commit
156b1efc24
3 changed files with 32 additions and 2 deletions
|
|
@ -4,7 +4,7 @@
|
|||
import re
|
||||
from collections.abc import Mapping, Sequence
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timezone, tzinfo
|
||||
from datetime import date, datetime, timezone, tzinfo
|
||||
from types import MappingProxyType
|
||||
from typing import Final, Literal, TypedDict, cast
|
||||
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
||||
|
|
@ -492,10 +492,23 @@ def _as_window_strings(value: object) -> tuple[str, ...]:
|
|||
return ()
|
||||
|
||||
|
||||
def _is_iso_date(value: object) -> bool:
|
||||
if not isinstance(value, str) or len(value) != 10:
|
||||
return False
|
||||
try:
|
||||
date.fromisoformat(value)
|
||||
except ValueError:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _as_date_strings(value: object) -> tuple[str, ...]:
|
||||
if isinstance(value, str) or not isinstance(value, Sequence):
|
||||
return ()
|
||||
return tuple(entry for entry in value if isinstance(entry, str))
|
||||
entries: Final = tuple(value)
|
||||
if not entries or not all(_is_iso_date(entry) for entry in entries):
|
||||
return ()
|
||||
return entries
|
||||
|
||||
|
||||
def _is_off_peak(off_peak: Mapping[str, object], current_time: datetime | None = None) -> bool:
|
||||
|
|
|
|||
|
|
@ -724,6 +724,22 @@ def test_is_off_peak_ignores_malformed_override_dates():
|
|||
)
|
||||
|
||||
|
||||
def test_is_off_peak_disables_rules_with_partially_malformed_override_dates():
|
||||
"""One bad entry disables the whole override_dates list: a rule whose dates do not all
|
||||
parse never applies on any date, so the flat hours decide alone."""
|
||||
from datetime import datetime, timezone
|
||||
|
||||
peak_instant = datetime(2026, 3, 4, 12, 0, tzinfo=timezone.utc)
|
||||
for bad in (["2026-03-04", 20260304], ["2026-03-04", "2026-3-5"]):
|
||||
block = {
|
||||
"hours_utc": "00:30-01:00",
|
||||
"windows": [{"hours_utc": "00:00-00:00", "override_dates": bad}],
|
||||
}
|
||||
assert _is_off_peak(block, peak_instant) is False, (
|
||||
f"override_dates={bad!r} must disable its rule, leaving only the closed flat window"
|
||||
)
|
||||
|
||||
|
||||
def test_is_off_peak_without_override_dates_is_unchanged():
|
||||
"""The plain DeepSeek windows with no override_dates bill a holiday peak hour at
|
||||
standard rates."""
|
||||
|
|
|
|||
|
|
@ -1057,6 +1057,7 @@ def test_aaamodel_prices_and_context_window_json_is_valid():
|
|||
"type": "array",
|
||||
"items": {"oneOf": [{"type": "integer"}, {"type": "string"}]},
|
||||
},
|
||||
"override_dates": {"type": "array", "items": {"type": "string"}},
|
||||
},
|
||||
"required": ["hours_utc"],
|
||||
"additionalProperties": False,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue