fix(context): persist NVIDIA DeepSeek endpoint limit
This commit is contained in:
parent
2f2d90344c
commit
29eac371d1
|
|
@ -4746,6 +4746,13 @@ def run_conversation(
|
|||
provider=agent.provider,
|
||||
api_mode=agent.api_mode,
|
||||
)
|
||||
# Persist an explicit provider-reported limit before
|
||||
# compression/retry. The next request can be rate
|
||||
# limited, omit usage, or the process can restart; none
|
||||
# of those should discard metadata the provider already
|
||||
# confirmed. Keep the probe flags as a best-effort
|
||||
# post-success retry if this write cannot complete.
|
||||
save_context_length(agent.model, agent.base_url, new_ctx)
|
||||
# Context probing flags — only set on built-in
|
||||
# compressor (plugin engines manage their own). This
|
||||
# value came from the provider, so it is safe to cache.
|
||||
|
|
|
|||
|
|
@ -634,12 +634,17 @@ def _is_known_provider_base_url(base_url: str) -> bool:
|
|||
|
||||
|
||||
def _endpoint_scoped_context_length(model: str, base_url: str) -> Optional[int]:
|
||||
"""Return metadata confirmed only for the Kimi Coding endpoint.
|
||||
"""Return context metadata confirmed for one provider endpoint.
|
||||
|
||||
Kimi Coding serves K3 under the bare slug ``k3``, but users may also
|
||||
configure or select the public-facing aliases ``kimi-k3`` and
|
||||
``kimi-k3-cot``. Only canonical ``https://api.kimi.com/coding`` endpoints
|
||||
(legacy Moonshot keys do not serve K3) get the 1 Mi context window.
|
||||
|
||||
NVIDIA NIM serves ``deepseek-ai/deepseek-v4-pro`` with a 262,144-token
|
||||
window even though DeepSeek's native endpoint serves the V4 family with a
|
||||
1M window. Keep the lower limit scoped to NVIDIA instead of weakening the
|
||||
global model-family metadata.
|
||||
"""
|
||||
normalized = _normalize_base_url(base_url)
|
||||
try:
|
||||
|
|
@ -659,6 +664,18 @@ def _endpoint_scoped_context_length(model: str, base_url: str) -> Optional[int]:
|
|||
and model.strip().lower() in {"k3", "kimi-k3", "kimi-k3-cot"}
|
||||
):
|
||||
return 1_048_576
|
||||
if (
|
||||
parsed.scheme.lower() == "https"
|
||||
and (parsed.hostname or "").lower() == "integrate.api.nvidia.com"
|
||||
and port in (None, 443)
|
||||
and parsed.username is None
|
||||
and parsed.password is None
|
||||
and parsed.path.rstrip("/") == "/v1"
|
||||
and not parsed.query
|
||||
and not parsed.fragment
|
||||
and model.strip().lower() == "deepseek-ai/deepseek-v4-pro"
|
||||
):
|
||||
return 262_144
|
||||
return None
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -134,6 +134,42 @@ class TestEstimateRequestTokensRough:
|
|||
# =========================================================================
|
||||
|
||||
class TestDefaultContextLengths:
|
||||
def test_nvidia_deepseek_v4_pro_context_is_endpoint_scoped(self):
|
||||
"""NVIDIA's 262K NIM window must not lower DeepSeek V4 globally."""
|
||||
with patch("agent.model_metadata.get_cached_context_length", return_value=None), \
|
||||
patch("agent.model_metadata.fetch_model_metadata", return_value={}), \
|
||||
patch("agent.model_metadata.fetch_endpoint_model_metadata", return_value={}), \
|
||||
patch("agent.model_metadata._query_ollama_api_show", return_value=None), \
|
||||
patch("agent.models_dev.lookup_models_dev_context", return_value=None):
|
||||
accepted_urls = (
|
||||
"https://integrate.api.nvidia.com/v1",
|
||||
"https://INTEGRATE.API.NVIDIA.COM/v1/",
|
||||
"https://integrate.api.nvidia.com:443/v1",
|
||||
)
|
||||
rejected_urls = (
|
||||
"http://integrate.api.nvidia.com/v1",
|
||||
"https://integrate.api.nvidia.com:8443/v1",
|
||||
"https://integrate.api.nvidia.com/v1/other",
|
||||
"https://integrate.api.nvidia.com/v1?route=other",
|
||||
"https://example.invalid/v1",
|
||||
"https://api.deepseek.com/v1",
|
||||
"https://openrouter.ai/api/v1",
|
||||
)
|
||||
|
||||
for base_url in accepted_urls:
|
||||
assert get_model_context_length(
|
||||
"deepseek-ai/deepseek-v4-pro",
|
||||
provider="nvidia",
|
||||
base_url=base_url,
|
||||
) == 262_144
|
||||
|
||||
for base_url in rejected_urls:
|
||||
assert get_model_context_length(
|
||||
"deepseek-ai/deepseek-v4-pro",
|
||||
provider="nvidia",
|
||||
base_url=base_url,
|
||||
) == 1_000_000
|
||||
|
||||
def test_k3_context_is_scoped_to_confirmed_coding_endpoint(self):
|
||||
"""The bare ``k3`` slug's 1 Mi context must not leak to unverified endpoints.
|
||||
|
||||
|
|
@ -1076,4 +1112,3 @@ class TestMoAContextLength:
|
|||
assert compressor.context_length == configured_context
|
||||
assert compressor.threshold_tokens == configured_context // 2
|
||||
endpoint_probe.assert_not_called()
|
||||
|
||||
|
|
|
|||
|
|
@ -327,6 +327,62 @@ class TestHTTP413Compression:
|
|||
assert result["final_response"] == "Recovered after compression"
|
||||
|
||||
|
||||
def test_provider_context_limit_is_cached_before_retry_succeeds(self, agent):
|
||||
"""A confirmed limit survives when the recovery response omits usage."""
|
||||
err_400 = Exception(
|
||||
"Error code: 400 - {'error': {'message': "
|
||||
"\"This model's maximum context length is 262144 tokens. "
|
||||
"However, your messages resulted in 271877 tokens.\", 'code': 400}}"
|
||||
)
|
||||
err_400.status_code = 400
|
||||
# NVIDIA-compatible endpoints can omit usage. Before the fix, caching
|
||||
# happened only in the successful-response usage block, so this lost
|
||||
# the provider-confirmed limit across a restart.
|
||||
ok_resp = _mock_response(
|
||||
content="Recovered without usage metadata",
|
||||
finish_reason="stop",
|
||||
usage=None,
|
||||
)
|
||||
agent.model = "deepseek-ai/deepseek-v4-pro"
|
||||
agent.provider = "nvidia"
|
||||
agent.base_url = "https://integrate.api.nvidia.com/v1"
|
||||
agent.context_compressor.update_model(
|
||||
model=agent.model,
|
||||
context_length=1_000_000,
|
||||
base_url=agent.base_url,
|
||||
api_key=agent.api_key,
|
||||
provider=agent.provider,
|
||||
api_mode=agent.api_mode,
|
||||
)
|
||||
agent.client.chat.completions.create.side_effect = [err_400, ok_resp]
|
||||
|
||||
with (
|
||||
patch.object(agent, "_compress_context") as mock_compress,
|
||||
patch.object(agent, "_persist_session"),
|
||||
patch.object(agent, "_save_trajectory"),
|
||||
patch.object(agent, "_cleanup_task_resources"),
|
||||
patch("agent.conversation_loop.save_context_length") as mock_save,
|
||||
):
|
||||
mock_compress.return_value = (
|
||||
[{"role": "user", "content": "compressed summary"}],
|
||||
"compressed prompt",
|
||||
)
|
||||
result = agent.run_conversation(
|
||||
"continue",
|
||||
conversation_history=[
|
||||
{"role": "user", "content": "previous question"},
|
||||
{"role": "assistant", "content": "previous answer"},
|
||||
],
|
||||
)
|
||||
|
||||
assert result["completed"] is True
|
||||
mock_save.assert_called_once_with(
|
||||
"deepseek-ai/deepseek-v4-pro",
|
||||
"https://integrate.api.nvidia.com/v1",
|
||||
262_144,
|
||||
)
|
||||
|
||||
|
||||
def test_context_length_retry_rebuilds_request_after_compression(self, agent):
|
||||
"""Retry must send the compressed transcript, not the stale oversized payload."""
|
||||
err_400 = Exception(
|
||||
|
|
@ -1132,5 +1188,3 @@ class TestOverflowWithCompactionDisabled:
|
|||
assert result.get("failed") is True
|
||||
assert result.get("compaction_disabled") is True
|
||||
assert "auto-compaction is disabled" in result["error"]
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue