Skip to content
Closed
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
24 changes: 24 additions & 0 deletions litellm/model_prices_and_context_window_backup.json
Original file line number Diff line number Diff line change
Expand Up @@ -37762,6 +37762,30 @@
"mode": "embedding",
"output_cost_per_token": 0.0
},
"voyage/voyage-4": {
"input_cost_per_token": 6e-08,
"litellm_provider": "voyage",
"max_input_tokens": 32000,
"max_tokens": 32000,
"mode": "embedding",
"output_cost_per_token": 0.0
},
"voyage/voyage-4-large": {
"input_cost_per_token": 1.2e-07,
"litellm_provider": "voyage",
"max_input_tokens": 32000,
"max_tokens": 32000,
"mode": "embedding",
"output_cost_per_token": 0.0
},
"voyage/voyage-4-lite": {
"input_cost_per_token": 2e-08,
"litellm_provider": "voyage",
"max_input_tokens": 32000,
"max_tokens": 32000,
"mode": "embedding",
"output_cost_per_token": 0.0
},
"voyage/voyage-code-2": {
"input_cost_per_token": 1.2e-07,
"litellm_provider": "voyage",
Expand Down
24 changes: 24 additions & 0 deletions model_prices_and_context_window.json
Original file line number Diff line number Diff line change
Expand Up @@ -37964,6 +37964,30 @@
"mode": "embedding",
"output_cost_per_token": 0.0
},
"voyage/voyage-4": {
"input_cost_per_token": 6e-08,
"litellm_provider": "voyage",
"max_input_tokens": 32000,
"max_tokens": 32000,
"mode": "embedding",
"output_cost_per_token": 0.0
},
"voyage/voyage-4-large": {
"input_cost_per_token": 1.2e-07,
"litellm_provider": "voyage",
"max_input_tokens": 32000,
"max_tokens": 32000,
"mode": "embedding",
"output_cost_per_token": 0.0
},
"voyage/voyage-4-lite": {
"input_cost_per_token": 2e-08,
"litellm_provider": "voyage",
"max_input_tokens": 32000,
"max_tokens": 32000,
"mode": "embedding",
"output_cost_per_token": 0.0
},
"voyage/voyage-code-2": {
"input_cost_per_token": 1.2e-07,
"litellm_provider": "voyage",
Expand Down
53 changes: 53 additions & 0 deletions tests/test_litellm/test_voyage_4_model_metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import json
from pathlib import Path

import pytest

import litellm

VOYAGE_4_MODELS = {
"voyage/voyage-4-large": 1.2e-07,
"voyage/voyage-4": 6e-08,
"voyage/voyage-4-lite": 2e-08,
}


@pytest.mark.parametrize("model,input_cost", VOYAGE_4_MODELS.items())
def test_voyage_4_model_info(model, input_cost):
json_path = Path(__file__).parents[2] / "model_prices_and_context_window.json"
with open(json_path) as f:
model_cost = json.load(f)

info = model_cost.get(model)
assert info is not None, f"{model} not found in model_prices_and_context_window.json"

assert info["litellm_provider"] == "voyage"
assert info["mode"] == "embedding"
assert info["input_cost_per_token"] == input_cost
assert info["output_cost_per_token"] == 0.0
assert info["max_input_tokens"] == 32000
assert info["max_tokens"] == 32000


@pytest.mark.parametrize("model,input_cost", VOYAGE_4_MODELS.items())
def test_voyage_4_get_model_info_surfaces_mode(model, input_cost):
litellm.model_cost = litellm.get_model_cost_map(url="")
info = litellm.get_model_info(model=model)
assert info["mode"] == "embedding"
assert info["input_cost_per_token"] == input_cost
Comment on lines +34 to +37

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.

P1 Unguarded global state mutation

litellm.model_cost is a module-level singleton; assigning to it here and never restoring it means any test that runs after this one in the same pytest worker will see the modified value. If, for example, the module-level cost map was already populated from the remote URL before this test ran, subsequent tests that rely on the original value could produce wrong results or false-positive passes. Use a monkeypatch fixture (or @pytest.fixture(autouse=True)) to scope the override to this test only.

Comment on lines +32 to +37

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 Passing url="" to get_model_cost_map relies on an empty-string URL causing httpx to raise an exception, which then triggers the local-fallback path. This is an indirect side-effect of error handling rather than an intentional API contract, and it could silently break if httpx changes its URL-validation behaviour. Setting LITELLM_LOCAL_MODEL_COST_MAP=True via monkeypatch.setenv is the documented, supported way to force the local backup without any network-call attempt.

Suggested change
@pytest.mark.parametrize("model,input_cost", VOYAGE_4_MODELS.items())
def test_voyage_4_get_model_info_surfaces_mode(model, input_cost):
litellm.model_cost = litellm.get_model_cost_map(url="")
info = litellm.get_model_info(model=model)
assert info["mode"] == "embedding"
assert info["input_cost_per_token"] == input_cost
@pytest.mark.parametrize("model,input_cost", VOYAGE_4_MODELS.items())
def test_voyage_4_get_model_info_surfaces_mode(model, input_cost, monkeypatch):
monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True")
monkeypatch.setattr(litellm, "model_cost", litellm.get_model_cost_map(url=""))
info = litellm.get_model_info(model=model)
assert info["mode"] == "embedding"
assert info["input_cost_per_token"] == input_cost

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!



@pytest.mark.parametrize("model", VOYAGE_4_MODELS.keys())
def test_voyage_4_backup_matches_main(model):
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"

with open(main_path) as f:
main_cost = json.load(f)
with open(backup_path) as f:
backup_cost = json.load(f)

assert backup_cost.get(model) == main_cost.get(model), (
f"{model} differs between main and backup model cost maps"
)
Loading