diff --git a/agent/model_metadata.py b/agent/model_metadata.py index 14c26ecb6..0664a23fd 100644 --- a/agent/model_metadata.py +++ b/agent/model_metadata.py @@ -142,7 +142,17 @@ DEFAULT_CONTEXT_LENGTHS = { "gemma-4-31b": 256000, "gemma-3": 131072, "gemma": 8192, # fallback for older gemma models - # DeepSeek + # DeepSeek — V4 family ships with a 1M context window. The legacy + # aliases ``deepseek-chat`` / ``deepseek-reasoner`` are server-side + # mapped to the non-thinking / thinking modes of ``deepseek-v4-flash`` + # and inherit the same 1M window. The ``deepseek`` substring entry + # below remains as a 128K fallback for older / unknown DeepSeek model + # ids (e.g. via custom endpoints). + # https://api-docs.deepseek.com/zh-cn/quick_start/pricing + "deepseek-v4-pro": 1_000_000, + "deepseek-v4-flash": 1_000_000, + "deepseek-chat": 1_000_000, + "deepseek-reasoner": 1_000_000, "deepseek": 128000, # Meta "llama": 131072, diff --git a/tests/agent/test_model_metadata.py b/tests/agent/test_model_metadata.py index ee6019429..72981f391 100644 --- a/tests/agent/test_model_metadata.py +++ b/tests/agent/test_model_metadata.py @@ -192,6 +192,43 @@ class TestDefaultContextLengths: f"{model_id}: expected {expected_ctx}, got {actual}" ) + def test_deepseek_v4_models_1m_context(self): + from agent.model_metadata import get_model_context_length + from unittest.mock import patch as mock_patch + + expected_keys = { + "deepseek-v4-pro": 1_000_000, + "deepseek-v4-flash": 1_000_000, + "deepseek-chat": 1_000_000, + "deepseek-reasoner": 1_000_000, + } + for key, value in expected_keys.items(): + assert key in DEFAULT_CONTEXT_LENGTHS, f"{key} missing" + assert DEFAULT_CONTEXT_LENGTHS[key] == value, ( + f"{key} should be {value}, got {DEFAULT_CONTEXT_LENGTHS[key]}" + ) + + # Longest-first substring matching must resolve both the bare V4 + # ids (native DeepSeek) and the vendor-prefixed forms (OpenRouter + # / Nous Portal) to 1M without probing down to the legacy 128K + # ``deepseek`` substring fallback. + with mock_patch("agent.model_metadata.fetch_model_metadata", return_value={}), \ + mock_patch("agent.model_metadata.fetch_endpoint_model_metadata", return_value={}), \ + mock_patch("agent.model_metadata.get_cached_context_length", return_value=None): + cases = [ + ("deepseek-v4-pro", 1_000_000), + ("deepseek-v4-flash", 1_000_000), + ("deepseek/deepseek-v4-pro", 1_000_000), + ("deepseek/deepseek-v4-flash", 1_000_000), + ("deepseek-chat", 1_000_000), + ("deepseek-reasoner", 1_000_000), + ] + for model_id, expected_ctx in cases: + actual = get_model_context_length(model_id) + assert actual == expected_ctx, ( + f"{model_id}: expected {expected_ctx}, got {actual}" + ) + def test_all_values_positive(self): for key, value in DEFAULT_CONTEXT_LENGTHS.items(): assert value > 0, f"{key} has non-positive context length"