Repository navigation
ollama ≥0.32: high reasoning efforts 500 in GGUF chat template — need a per-effort way to omit reasoning_effort on the wire #2356
Description
Area
Providers / reasoning-effort mapping (openai-chat adapter, ollama provider)
What are you trying to accomplish?
Route Codex (global model_reasoning_effort = "ultra", so requests carry effort max) to a local ollama model: hf.co/JonathanColetti/Qwen3.8-27B-Uncensored-GGUF:Q4_K_M (27B Q4_K_M GGUF with an embedded Jinja chat template). The template validates reasoning_effort against exactly xhigh/medium/low and — crucially — its top mode is only selected when the field is absent:
{%- set resolved_reasoning_effort = reasoning_effort|default('xhigh') %}
{%- if resolved_reasoning_effort not in ('xhigh', 'medium', 'low') %}
{{- raise_exception('Unexpected reasoning effort ' ~ reasoning_effort ~ ...) }}
{%- endif %}I want a working ladder: low/medium for fast turns, deep thinking (template xhigh) for high/xhigh/max/ultra.
What prevents this today?
1. Default behavior is a hard 500 retry loop. With the stock ollama provider config (adapter: openai-chat, baseUrl: http://localhost:11434/v1) the requested effort is forwarded as-is. ollama returns 500 and Codex retries indefinitely (usage.jsonl):
{"requestedModel":"ollama/qwen3.8-uncensored:27b-q4","requestedEffort":"max","effectiveEffort":"max",
"reasoningWireValue":"max","status":500,"errorCode":"upstream_server_error",
"upstreamError":"Provider error 500: ... Jinja Exception: Unexpected reasoning effort max. Supported types are xhigh (default), medium, and low."}2. ollama ≥0.32 normalizes effort values before template rendering, so even ladder-correct values fail. Empirically verified on ollama 0.32.15 (probing which template instruction the model actually received):
| wire value sent to ollama /v1/chat/completions | value the template sees | result |
|---|---|---|
| (field omitted) | default xhigh |
✅ deep mode |
minimal |
low |
✅ |
low |
low |
✅ |
medium |
medium |
✅ |
high |
high |
❌ 500 |
xhigh, ultra, max |
max (normalized by ollama) |
❌ 500 |
none |
thinking disabled | ✅ |
So the template's xhigh mode is unreachable through any wire value — only by omitting reasoning_effort entirely.
3. No existing config can express per-effort omission.
modelReasoningEffortMapcan only rewrite values; every reachable wire value ≥highis rejected upstream.noReasoningModelsomits the field for all efforts, losinglow/mediumcontrol.clampToSupportedCodexEffortcannot help either: the failure is on the wire, not in the Codex ladder.
What should OpenCodex do?
Support per-effort omission. Minimal change: treat a sentinel wire value (e.g. "__omit__") in reasoningEffortMap/modelReasoningEffortMap as "drop reasoning_effort from the body". Verified locally against 2.29.0 with this one-line patch in src/adapters/openai-chat.ts:
const rawReasoningEffort = mapReasoningEffort(provider, parsed.modelId, parsed.options.reasoning);
const reasoningEffort = rawReasoningEffort === "__omit__" ? undefined : rawReasoningEffort;with provider config:
"ollama": {
"adapter": "openai-chat",
"baseUrl": "http://localhost:11434/v1",
"modelReasoningEfforts": {
"qwen3.8-uncensored:27b-q4": ["low", "medium", "high", "xhigh", "max"]
},
"modelReasoningEffortMap": {
"qwen3.8-uncensored:27b-q4": {
"low": "low", "medium": "medium",
"high": "__omit__", "xhigh": "__omit__", "max": "__omit__"
}
}
}Verified end-to-end through the proxy: max → HTTP 200 with the model confirming the template's xhigh instruction; low → HTTP 200 with the low instruction.
Optionally, also document (or registry-default for the ollama provider) that xhigh is never a safe wire value under ollama ≥0.32, since ollama collapses xhigh/ultra/max to max before the template sees it.
Alternatives or workarounds
- Patching the installed source (what I run today) — upgrade-fragile, same concern raised in Feature: suppress synthetic max per model while retaining ultra #2279.
noReasoningModels— works but forces deep mode for every turn and removes effort control.- Editing the model's embedded GGUF template is not viable via Modelfile:
ollama createparses TEMPLATE as a Go template and rejects this Jinja (template error: function "content" not defined).
Environment
- opencodex 2.29.0 (
@bitkyc08/opencodexon npm), service mode on Windows 11 x64 - Codex desktop 26.818.41509 (model catalog client 0.146.0)
- ollama 0.32.15
Related: #1870, #2279 (catalog-layer synthetic rungs). This is the wire-layer counterpart: even with an exact catalog ladder, requests still need per-effort field omission for templates like this one.
Activity
lidge-jun commentedon Aug 22, 2026
리뷰 · 우선순위 58 / 80
설명: 이 이슈는 로컬 Ollama 모델에 Codex 추론 강도(high, xhigh, max, ultra)를 보낼 때 업스트림이 500을 내고 Codex가 끝없이 재시도하는 구멍이다. 지금 CURRENT
devHEAD는ced9a85c5이다. 이 시간에 착지한 것은 문서뿐이다:#2348WP4 감사(0 P0/P1, P2 두 개, 전체 스위트 14264 통과),#2349GO 판정(코드 마지막은 여전히#2346Opus Fast 카탈로그). 이 이슈의 openai-chat 추론 와이어와 그 머지들은 파일이 겹치지 않는다. 현재 HEAD에서 레지스트리src/providers/registry.ts의 ollama 줄은 adapteropenai-chat, baseUrlhttp://localhost:11434/v1이고 reasoningEffortMap 이 없다. 요청 라벨을 와이어 값으로 바꾸는 곳은src/reasoning-effort.tsmapReasoningEffort 한 함수다. ultra 는 먼저 max 로 바뀌고, modelReasoningEffortMap / reasoningEffortMap 에 키가 있으면 그 문자열을 그대로 돌려준다. 맵이 없으면 카탈로그 사다리로 클램프한다. 생략을 뜻하는 센티널은 없다. 어댑터src/adapters/openai-chat.ts는 mapReasoningEffort 결과가 undefined 일 때만 reasoning_effort 필드를 빼다. 값이 문자열이면 그대로 붙인다. 그래서 본문이 찍은 표가 코드와 맞다. ollama 0.32 는 와이어의 xhigh/ultra/max 를 템플릿에 주기 전에 max 로 접는다. 이 GGUF Jinja 는 xhigh/medium/low 만 받고, 맨 위 모드(xhigh)는 필드가 없을 때만 켜진다. high 를 보내면 템플릿이 high 를 보고 500. xhigh 를 보내면 ollama 가 max 로 바꿔서 또 500. 지금 설정으로 고칠 길이 없다. 맵은 값만 바꿀 수 있고, 바뀐 값도 결국 high 이상이거나 max 로 접힌다. noReasoningModels 는 모든 강도를 한꺼번에 빼서 low/medium 조절이 사라진다. clampToSupportedCodexEffort 는 Codex 사다리 안의 이야기라 이 500과 무관하다. 본문이 제안한__omit__센티널은 openai-chat 에서 undefined 로 바꾸면 필드가 빠지는 현재 분기와 맞다. 다만 센티널 검사는 mapReasoningEffort 안에 두는 편이 맞다. 같은 맵을 쓰는 다른 어댑터도 같이 빠지게 하기 위해서다. 카탈로그 쪽 짝은#2279(합성 max 숨기기)와 구현 PR#2280이다. 그건 픽커에 가짜 max 를 넣을지이고, 이 이슈는 이미 고른 강도를 와이어에서 뺄지다. 한 PR로 묶지 않는다. Cursor#2334CursorCredentialRouter 는 여전히src/providers/cursor-pool.ts모듈+테스트만 있고 어댑터에 연결되지 않았다.#2332H2 풀은 discovery 전용, 종료 훅은#2338. Run 은 자기 세션.#2320overflow 매핑은dev에 있고#2342size prior 가 provably-small 만 429 로 좁힌다. 카탈로그 팁은 Ox Alphax-preview-f-free+deepseek-v4-flash-vision-exp.#2346으로 Cursor Opus Fast 패밀리가 추가됐지만 이 이슈와 무관하다. package.json 은 아직 2.27.0, 태그는 v2.29.0. 프리뷰 배포는 메인테이너 일이지 이 이슈의 일이 아니다.#2188사이드카+routed vision 은 이미dev. types.ts/config.ts 스플릿과 이 이슈의 첫 PR이 맵 키를 같이 만지면 리베이스하지 말고 닫고 다시 연다. 로컬 Ollama 가 500 재시도 루프에 빠지는 운영 구멍이라 58.src/reasoning-effort.ts mapReasoningEffort - 맵에 키가 있으면 문자열을 그대로 반환한다. 필드를 빼라는 값이 없다
src/adapters/openai-chat.ts reasoningEffort === undefined 분기 - undefined 일 때만 와이어에서 뺀다. 맵이 max 나 high 를 주면 500이 난다
src/types/provider.ts reasoningEffortMap / modelReasoningEffortMap - 값 타입이 string 뿐이라 생략을 표현할 자리가 스키마에 없다
src/providers/registry.ts ollama 줄 - openai-chat 기본값만 있고 강도 맵이 없다. 레지스트리에 xhigh 생략을 기본으로 심지 말 것. 템플릿마다 다르다
issue #2279 OPEN / PR #2280 - 카탈로그에 합성 max 를 넣을지. 이 이슈는 와이어 생략. 한 장에 묶지 말 것
메인테이너의 판단이 필요한 지점
__omit__같은 문자열로 둘지, 빈 문자열/null 을 스키마에 허용할지너의 추천
이 이슈는 연다. 지금 Cursor round-3 / 릴리스 GO 문서 레인(
#2349GO,#2334미연결 라우터)을 막거나 묶지 않는다. 첫 PR은 mapReasoningEffort 가 생략 센티널을 undefined 로 바꾸게 한다. openai-chat 은 지금처럼 undefined 면 필드를 뺀다. 카탈로그 사다리와#2279/#2280은 손대지 않는다. 계산은 맵에 센티널이 있는 강도만 뺀다. noReasoningModels 로 우회하라고 닫지 말 것. ollama 레지스트리 기본값으로 모든 모델의 high 이상을 빼지 말 것. 문서에 0.32 가 xhigh/ultra/max 를 max 로 접는다는 점만 적는다. types.ts/config.ts 스플릿이 맵 필드를 이미 옮긴 뒤에야 충돌이 보이면 리베이스하지 말고 닫고 다시 연다. 지금은 그 정도 아님. 라벨은 그대로 둔다. 프리뷰 배포가 아니다.이 댓글은 grok-bot이 작성했습니다
test(reasoning): cover omission after fallback effort clamping (lidge…
fix(reasoning): support per-effort field omission sentinel (__omit__) (…
fix(reasoning): support per-effort field omission sentinel (__omit__) (…
lidge-jun commentedon Aug 22, 2026
Merge from upstream (#8)
fix(reasoning): support per-effort field omission sentinel (__omit__) (…
fix(reasoning): support per-effort field omission sentinel (__omit__) (…