Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,14 @@
"""

import re
import sys
from collections.abc import Mapping, Sequence
from typing import TYPE_CHECKING, Any, Final, Literal, NotRequired, Optional, TypedDict, Union, cast
from typing import TYPE_CHECKING, Any, Final, Literal, Optional, TypedDict, Union, cast

if sys.version_info >= (3, 11):
from typing import NotRequired
else:
from typing_extensions import NotRequired

from typing_extensions import ReadOnly

Expand Down
4 changes: 2 additions & 2 deletions litellm/model_prices_and_context_window_backup.json
Original file line number Diff line number Diff line change
Expand Up @@ -12269,7 +12269,7 @@
},
"claude-3-haiku-20240307": {
"cache_creation_input_token_cost": 3e-07,
"cache_creation_input_token_cost_above_1hr": 6e-06,
"cache_creation_input_token_cost_above_1hr": 5e-07,
"cache_read_input_token_cost": 3e-08,
"deprecation_date": "2026-04-20",
"input_cost_per_token": 2.5e-07,
Expand All @@ -12288,7 +12288,7 @@
},
"claude-3-opus-20240229": {
"cache_creation_input_token_cost": 1.875e-05,
"cache_creation_input_token_cost_above_1hr": 6e-06,
"cache_creation_input_token_cost_above_1hr": 3e-05,
"cache_read_input_token_cost": 1.5e-06,
"deprecation_date": "2026-01-05",
"input_cost_per_token": 1.5e-05,
Expand Down
4 changes: 2 additions & 2 deletions model_prices_and_context_window.json
Original file line number Diff line number Diff line change
Expand Up @@ -12269,7 +12269,7 @@
},
"claude-3-haiku-20240307": {
"cache_creation_input_token_cost": 3e-07,
"cache_creation_input_token_cost_above_1hr": 6e-06,
"cache_creation_input_token_cost_above_1hr": 5e-07,
"cache_read_input_token_cost": 3e-08,
"deprecation_date": "2026-04-20",
"input_cost_per_token": 2.5e-07,
Expand All @@ -12288,7 +12288,7 @@
},
"claude-3-opus-20240229": {
"cache_creation_input_token_cost": 1.875e-05,
"cache_creation_input_token_cost_above_1hr": 6e-06,
"cache_creation_input_token_cost_above_1hr": 3e-05,
"cache_read_input_token_cost": 1.5e-06,
"deprecation_date": "2026-01-05",
"input_cost_per_token": 1.5e-05,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""
Unit tests for Python 3.10+ typing compatibility in context management compact editor.

Fixes Issue #38076:
- Verifies that NotRequired is correctly imported either from typing (>= 3.11) or typing_extensions (< 3.11).
- Verifies that _SummaryCallKwargs TypedDict definition is valid across Python versions.
"""

import importlib
import sys


def test_compact_editor_not_required_import():
"""Verify that compact.py imports NotRequired correctly based on sys.version_info."""
import litellm.llms.anthropic.experimental_pass_through.context_management.editors.compact as compact_module

assert hasattr(compact_module, "NotRequired")
if sys.version_info >= (3, 11):
import typing

assert compact_module.NotRequired is typing.NotRequired
else:
import typing_extensions

assert compact_module.NotRequired is typing_extensions.NotRequired
Comment on lines +18 to +25

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Python 3.10 Branch Untested

The test selects its assertion from the interpreter running pytest, while the standard unit workflow uses Python 3.12. As a result, the newly added typing_extensions.NotRequired fallback is not exercised there, so a Python 3.10 compatibility regression would not be caught by this test.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!



def test_compact_editor_summary_call_kwargs_definition():
"""Verify that _SummaryCallKwargs TypedDict is properly constructed."""
from litellm.llms.anthropic.experimental_pass_through.context_management.editors.compact import (
_SummaryCallKwargs,
)

annotations = _SummaryCallKwargs.__annotations__
assert "model" in annotations
assert "messages" in annotations
assert "max_tokens" in annotations
assert "timeout" in annotations
assert "litellm_metadata" in annotations
assert "user" in annotations
assert "allowed_model_region" in annotations


def test_compact_module_can_be_imported():
"""Ensure litellm compact module imports without error on the current runtime."""
compact_module = importlib.import_module(
"litellm.llms.anthropic.experimental_pass_through.context_management.editors.compact"
)
assert compact_module is not None
53 changes: 53 additions & 0 deletions tests/test_litellm/test_claude_3_cache_pricing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"""
Unit tests for Claude 3 cache creation pricing above 1 hour in model prices map.

Fixes Issue #38056:
- claude-3-haiku-20240307 cache_creation_input_token_cost_above_1hr should be 5e-07 (2x input cost of 2.5e-07)
- claude-3-opus-20240229 cache_creation_input_token_cost_above_1hr should be 3e-05 (2x input cost of 1.5e-05)
"""

import json
from pathlib import Path

import pytest

REPO_ROOT = Path(__file__).parents[2]
MAIN_PATH = REPO_ROOT / "model_prices_and_context_window.json"
BACKUP_PATH = REPO_ROOT / "litellm" / "model_prices_and_context_window_backup.json"


def _load_prices(path: Path) -> dict:
with open(path) as f:
return json.load(f)


@pytest.mark.parametrize("path", [MAIN_PATH, BACKUP_PATH], ids=["main", "backup"])
def test_claude_3_haiku_cache_creation_cost_above_1hr(path: Path):
prices = _load_prices(path)
model = "claude-3-haiku-20240307"
assert model in prices, f"{model} not found in {path}"
model_info = prices[model]

input_cost = model_info["input_cost_per_token"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Redundant Pricing Comments

This comment and the equivalent Opus comment merely restate the immediately following input_cost * 2 calculations. Removing both avoids duplicated prose that can become stale and follows the repository convention of reserving source comments for complex logic, tool directives, or TODO/FIXME items.

Context Used: CLAUDE.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

assert input_cost == 2.5e-07

# Cache creation cost above 1hr is 2x standard input cost (5e-07)
expected_cache_creation_above_1hr = input_cost * 2
assert model_info["cache_creation_input_token_cost_above_1hr"] == expected_cache_creation_above_1hr
assert model_info["cache_creation_input_token_cost_above_1hr"] == 5e-07


@pytest.mark.parametrize("path", [MAIN_PATH, BACKUP_PATH], ids=["main", "backup"])
def test_claude_3_opus_cache_creation_cost_above_1hr(path: Path):
prices = _load_prices(path)
model = "claude-3-opus-20240229"
assert model in prices, f"{model} not found in {path}"
model_info = prices[model]

input_cost = model_info["input_cost_per_token"]
assert input_cost == 1.5e-05

# Cache creation cost above 1hr is 2x standard input cost (3e-05)
expected_cache_creation_above_1hr = input_cost * 2
assert model_info["cache_creation_input_token_cost_above_1hr"] == expected_cache_creation_above_1hr
assert model_info["cache_creation_input_token_cost_above_1hr"] == 3e-05
Loading