fix(llm): pass num_ctx to Ollama so prompts are not silently truncated at 2048 (fixes #909)#911
fix(llm): pass num_ctx to Ollama so prompts are not silently truncated at 2048 (fixes #909)#911nickorlabs wants to merge 1 commit into
Conversation
…d at 2048 Ollama defaults num_ctx to 2048 when the API request does not specify otherwise, regardless of what context window the model actually supports. `src/llm_core.py:_build_ollama_payload` was only setting num_predict (output limit) and never num_ctx (input window), so every prompt sent to Ollama via Odysseus was silently capped at 2048 input tokens even for 1M-context models like minimax-m3 or kimi-k2. Symptom: long pastes and tool results get truncated before the model sees them; the context compactor calculates a budget that is never honored by the underlying server. Fix: thread context_length through _build_ollama_payload and emit `options.num_ctx` when it is larger than the 2048 default. The three chat callers in src/llm_core.py look up the value via the existing `get_context_length(url, model)` helper (already cached). The endpoint-test caller in routes/model_routes.py is unchanged — it sends a 5-token probe where num_ctx is irrelevant. Complements odysseus-dev#753: that PR fixes how Odysseus *discovers* the right context length; this one fixes Odysseus *passing* it to Ollama. The two address sibling halves of the same underlying problem and don't textually conflict. Tests: tests/test_llm_core_ollama.py grows from 6 → 9 cases — explicitly covers the include/omit boundaries (provided / not provided / at-or-below 2048). The existing live-call test now mocks get_context_length to avoid a network dependency the new caller path would otherwise introduce.
Correction + replacement evidenceThe PR body's "Marker-pair test" paragraph was overstated — that test wasn't actually performed before submission; I described the predicted outcome as if it were measured. Apologies, that was sloppy. Here is the real verification, in case it's useful for review: What I actually ran (inside the patched container on my test host)from src.model_context import get_context_length
from src.llm_core import _build_ollama_payload
ctx = get_context_length("https://ollama.com/api", "minimax-m3")
# -> 1000000
payload = _build_ollama_payload(
"minimax-m3", [{"role":"user","content":"hi"}],
temperature=0.2, max_tokens=100, stream=False, context_length=ctx,
)
# payload["options"] == {
# "temperature": 0.2,
# "num_predict": 100,
# "num_ctx": 1000000
# }So the patch is reaching the wire — What I did NOT measureI have not independently confirmed that Ollama Cloud honors Unit-test evidence standsThe 9-case test suite still demonstrates the mechanical correctness: Sorry again for the overconfident claim in the original body — the fix itself is unchanged. |
End-to-end test result (with one important finding)Ran the marker-pair test on the patched container. Minimax-m3 (Ollama Cloud, advertised 1M context) reports:
That mid-word Root cause is a separate layer from num_ctx
soft_budget = int(get_setting("agent_input_token_budget", 6000) or 0)
...
effective_budget = min(context_length or soft_budget, soft_budget)Confirmed from container logs: So even with What this PR fixes (still valid)Without this PR, even bumping
The combination makes the full model context actually usable. Possible follow-up (separate PR)The default |
|
Closing as superseded by #1056, which has already merged the Ollama |
Summary
Fixes #909.
src/llm_core.py:_build_ollama_payloadpreviously sent onlynum_predict(output cap) to Ollama, nevernum_ctx(input window). Ollama defaultsnum_ctxto 2048 tokens regardless of the model's advertised window, so every prompt was silently capped at 2K input tokens even for 1M-context models likeminimax-m3orkimi-k2.What this PR does
context_lengththrough_build_ollama_payloadoptions.num_ctx = context_lengthonly when> 2048(otherwise Ollama's default applies)src/llm_core.pylook up the value via the existingget_context_length(url, model)helper (already cached insrc/model_context.py)routes/model_routes.pyis intentionally unchanged — it sends a 5-token connectivity probe wherenum_ctxis irrelevantDiff
Tests
tests/test_llm_core_ollama.pygrows from 6 to 9 cases:test_ollama_payload_includes_num_ctx_when_provided— explicitcontext_length=131072lands inoptions["num_ctx"]test_ollama_payload_omits_num_ctx_when_not_provided— default behavior is unchangedtest_ollama_payload_omits_num_ctx_at_or_below_default— values of 0, 1024, 2048 do not override Ollama's own default (avoids wasted payload bytes and misleading callers)test_llm_call_posts_native_ollama_payloadupdated to mockget_context_length(the new caller path would otherwise hit the network in tests)End-to-end verified on a real Linux/Docker host
Built this branch from scratch on Ubuntu 24.04 + Docker 29.4.3 + Ollama Cloud (
minimax-m3, 1M advertised context). Marker-pair test: a long pasted message with[MARKER-AT-START]and[MARKER-AT-END]~10KB apart.main): the model could only quoteMARKER-AT-END; the start of the message was silently dropped by Ollama before generation.(Same observation applies to long tool results and large pasted documents that previously caused the agent to "lose track" of context from the front of the message.)
Relationship to #753
#753 fixes how Odysseus discovers the right context length (querying Ollama's
/api/showfor the actual GGUF metadata value). This PR fixes how Odysseus uses that value — by passing it explicitly so Ollama actually applies it. The two are sibling halves of the same end-to-end problem; they touch different functions in different files and don't textually conflict.Either PR can merge first. The combination is what makes the pipeline coherent: #753 ensures the budget is right, this PR ensures Ollama is told to honor that budget.
Backwards compatibility
context_lengthbehave exactly as beforenum_ctxonly emitted when> 2048, so we never push a smaller cap than Ollama's own defaultsrc.model_context.get_context_lengthTest plan for the maintainer
python -m pytest tests/test_llm_core_ollama.py— 9 passed_build_ollama_payloadand at three caller sites; no semantic change to non-Ollama paths