Skip to content

feat: add Granite 4.2 model defaults - #1587

Open
planetf1 wants to merge 24 commits into
generative-computing:mainfrom
planetf1:feat/granite-4-2-defaults
Open

feat: add Granite 4.2 model defaults#1587
planetf1 wants to merge 24 commits into
generative-computing:mainfrom
planetf1:feat/granite-4-2-defaults

Conversation

@planetf1

@planetf1 planetf1 commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Pull Request

Issue

Fixes #1583

Description

Make Granite 4.2 3B the default local text model, and add the public 3B, 8B,
and 30B identifiers. The PR also aligns CI, the GPU runner, and existing
Ollama/vLLM tests with the published model tags so the same model is selected
locally and in automation.

What changed

  • Add Granite 4.2 model identifiers for Hugging Face and Ollama.
  • Change the default 3B local model used by start_session,
    OllamaModelBackend, and LiteLLMBackend.
  • Update Ollama, OpenAI-compatible, and vLLM test fixtures and runners to use
    the same Granite 4.2 text model.
  • Disable Granite 4.2's default thinking on the OpenAI-compatible paths:
    ModelOption.THINKING: False now sends reasoning_effort="none" on the
    OpenAI and LiteLLM backends, scoped to non-api.openai.com targets
    (alongside the existing chat_template_kwargs.enable_thinking for
    vLLM-served models) — real OpenAI reasoning models reject "none", so
    this only fires for Ollama and other self-hosted OpenAI-compatible
    servers. The /v1 live tests pass it. Root cause of the CI stalls — see
    below.
  • Add granite-4.2-{3b,8b,30b} entries to the intrinsics adapter
    canonical-name table so adapter-function path resolution stays
    consistent with the 4.0/4.1 siblings, and update the onboarding docs
    (quickstart, installation, troubleshooting, tutorials, examples index)
    that still told new users to pull the previous default model.
  • Keep adapter-function tests on Granite 4.1: the pinned public adapter
    catalogues do not provide Granite 4.2 weights.
  • Keep image tests on the Granite Vision 4.1 GGUF model. Granite 4.2 is a
    dense text-model update; this PR does not introduce a Granite 4.2 vision
    model.
  • Make formatter-only tool tests use the existing mocked Ollama fixture rather
    than require a running Ollama service.

CI stability fixes

Changing the default model means CI's Ollama runner now provisions and serves
Granite 4.2 instead of 4.1, which surfaced several latent bugs — all
test-only, no changes to the library under test. CI lanes running the 4.2
live tests wedged for 15–70 minutes, past the 900 s pytest watchdog, until
these were found and fixed (each now pinned by a regression test in
test/test_flaky_ollama_rerun.py):

  • 🔧 Production code. Granite 4.2 reasons by default on /v1: Ollama's
    OpenAI-compatible endpoint ignores the vLLM enable_thinking convention
    mellea was sending, so the /v1 live tests ran with reasoning always on —
    a 64-token reply became ~45 thinking tokens, occasionally pushing a
    request over the per-call time bound on a slow runner. This was the
    primary cause of the stalls. Fix: ModelOption.THINKING: False now
    also sends reasoning_effort="none" on OpenAI-compatible backends
    (mellea/backends/openai.py, litellm.py, model_options.py; Ollama's
    /v1 only accepts this from 0.33.1, so the CI pin was bumped), scoped to
    non-api.openai.com targets since real OpenAI reasoning models reject
    "none".
  • 🧪 Test-only. No time bounds on live requests: a stalled server could
    hang a live request, its stream, or the tail of stream consumption
    indefinitely. Fix: every live request/stream/value-consumption path in
    the test suite is now bounded to 300 s, and the test HTTP client uses
    max_retries=0, timeout=300.0 (the SDK's own retry+timeout defaults could
    otherwise outlast the pytest watchdog before a test-level retry ever saw
    the failure).
  • 🧪 Test-only. Two of our tests sent an empty prompt: they built context
    on SimpleContext, whose view_for_generation() always returns [], so
    the model received an empty conversation. Granite 4.2 rambles for 2000+
    tokens on empty input (4.1 stopped quickly, which hid this). Fix:
    switched those tests to ChatContext and capped output at 64 tokens.
  • 🧪 Test-only. Ollama 0.33.1 occasionally ignores num_predict after a
    context-size reload (e.g. 2048→8192), generating 1000+ tokens per request
    instead of the requested cap. Fix: CI now pins num_ctx=8192
    everywhere and warms up each model at the context its tests use, so the
    mismatched reload can't happen (upstream Ollama issue to follow).
  • 🧪 Test-only. A streaming metrics assertion needed ≥2 chunks:
    time_per_output_chunk only records inter-chunk intervals, and a short
    "say hello" reply could produce a single chunk, leaving the histogram
    empty. Fix: switched to a counting prompt that reliably spans multiple
    chunks.

Result: the quality matrix is green on
b6678123
(run 33207685575);
the full suite runs in ~18 min instead of wedging for up to ~2 h. Investigation
detail (refuted hypotheses, intermediate failing runs, instrumentation used)
is in the commit history and prior review discussion on this PR.

Verification

  • Ruff, mypy clean; uv run pytest test/ -m "not qualitative" (4,159
    passed locally) and the GitHub Actions quality matrix both green on
    b6678123.
  • Full CUDA GPU suite: run cleanly.
  • HF backend GPU smoke check, vision e2e, and tool-call behaviour
    verified on GPU hardware.
  • DCO check.

Known, non-blocking limitations:

  • HF span coverage deferred to enhancement: huggingface should handle ModelOption.THINKING #1212 (maps ModelOption.THINKING=False to
    the HF backend's chat-template variable).
  • The unquantised 30B model is catalogued but not exercised by local adapter
    tests.
  • Vision coverage stays on Granite Vision 4.1 GGUF — 4.2 is a text-only
    update.

Attribution

  • AI coding assistants used

Adding a new component, requirement, sampling strategy, or tool?

  • Component
  • Requirement
  • Sampling Strategy
  • Tool

@github-actions github-actions Bot added the enhancement New feature or request label Aug 26, 2026
@planetf1
planetf1 force-pushed the feat/granite-4-2-defaults branch from c798894 to 2eabb25 Compare August 26, 2026 12:00
planetf1 added a commit to planetf1/mellea that referenced this pull request Aug 26, 2026
Quality-matrix runs on PR generative-computing#1587 hit two Ollama stall failure shapes the
existing retry net could not handle:

- test_litellm_token_metrics_integration[non-streaming] died to the 900 s
  pytest-timeout watchdog: LiteLLMBackend passes no request deadline, so
  litellm falls back to a 600 s per-attempt timeout and the OpenAI SDK
  silently retries a stalled attempt (verified: 3 attempts on a stalled
  socket), exhausting the budget with no retryable error.
- The flaky marker from generative-computing#1369 matched only the native httpx.ReadTimeout
  shape; the litellm path raises litellm.exceptions.Timeout whose
  APITimeoutError message never matched, so its retry was dead code.

Changes:
- Bound the litellm live test to the same 300 s the native
  OllamaModelBackend uses, with num_retries: 0 so one pytest attempt can
  never exceed the watchdog budget (verified end-to-end through
  LiteLLMBackend against a stalled socket: 1 request, 4.1 s at timeout=4).
- Extend the conftest flaky marker to a shared
  OLLAMA_TIMEOUT_RERUN_PATTERNS constant covering both timeout shapes, and
  pin the match behaviour in test/test_flaky_ollama_rerun.py (including
  that the watchdog kill itself is NOT retried).
- Capture the Ollama server log in the quality workflow and dump it on
  failure: server-side stalls were previously undiagnosable from the job
  log (zero server lines across a 90-minute test window).

Local verification: new regression tests + litellm, native-Ollama metrics
and tracing live tests pass against Ollama 0.33.0 / granite4.2:3b;
ruff, mypy clean.

Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
@planetf1

Copy link
Copy Markdown
Contributor Author

Temporarily closing to re-trigger the quality matrix on c027873 (the synchronize event for that push was not dispatched during the GitHub Actions outage). Reopening now.

@planetf1 planetf1 closed this Aug 26, 2026
@planetf1 planetf1 reopened this Aug 26, 2026
@planetf1

Copy link
Copy Markdown
Contributor Author

Still persuing CI failures - above commits will be squashed, but wanted to push the changes to probe at and debug the CI failures which differ from local testing.

@planetf1

Copy link
Copy Markdown
Contributor Author

Status update: the root cause of the prolonged Ollama CI stalls is identified and fixed — the telemetry tests were inadvertently sending an empty conversation to the model, and granite4.2 generates runaway output on it. The instrumented re-run now completes the full quality matrix in ~18 min (previously ~2 h) with the previously-stalling /v1 tests passing; one remaining flaky streaming chunk-timing assertion is being chased down and will be re-run.

@planetf1

Copy link
Copy Markdown
Contributor Author

Update: the re-run is green — 33166578641 on f675e0b0 passes the full quality matrix (all three lanes, ~18 min test time). The remaining flake was the streaming chunk-timing assertion: that histogram only records inter-chunk intervals, and the "say hello" prompt can produce a single-chunk reply, so the test now uses a counting prompt that reliably spans many chunks.

planetf1 added a commit to planetf1/mellea that referenced this pull request Aug 28, 2026
Quality-matrix runs on PR generative-computing#1587 hit two Ollama stall failure shapes the
existing retry net could not handle:

- test_litellm_token_metrics_integration[non-streaming] died to the 900 s
  pytest-timeout watchdog: LiteLLMBackend passes no request deadline, so
  litellm falls back to a 600 s per-attempt timeout and the OpenAI SDK
  silently retries a stalled attempt (verified: 3 attempts on a stalled
  socket), exhausting the budget with no retryable error.
- The flaky marker from generative-computing#1369 matched only the native httpx.ReadTimeout
  shape; the litellm path raises litellm.exceptions.Timeout whose
  APITimeoutError message never matched, so its retry was dead code.

Changes:
- Bound the litellm live test to the same 300 s the native
  OllamaModelBackend uses, with num_retries: 0 so one pytest attempt can
  never exceed the watchdog budget (verified end-to-end through
  LiteLLMBackend against a stalled socket: 1 request, 4.1 s at timeout=4).
- Extend the conftest flaky marker to a shared
  OLLAMA_TIMEOUT_RERUN_PATTERNS constant covering both timeout shapes, and
  pin the match behaviour in test/test_flaky_ollama_rerun.py (including
  that the watchdog kill itself is NOT retried).
- Capture the Ollama server log in the quality workflow and dump it on
  failure: server-side stalls were previously undiagnosable from the job
  log (zero server lines across a 90-minute test window).

Local verification: new regression tests + litellm, native-Ollama metrics
and tracing live tests pass against Ollama 0.33.0 / granite4.2:3b;
ruff, mypy clean.

Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
@planetf1
planetf1 force-pushed the feat/granite-4-2-defaults branch from f675e0b to 1eebfb4 Compare August 28, 2026 20:07
…4.2-*

Add GRANITE42_MODEL-driven size selection for the HF and Ollama live
tests (defaults to 3B; set it to test the 8B/30B sizes on LSF or
locally), align every fixture and test-runner script to Granite 4.2,
and bound the CI e2e context window.

Assisted-by: Codex
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
Assisted-by: Codex
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
Assisted-by: Codex
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
Assisted-by: Codex
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
Assisted-by: Codex
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
Assisted-by: Codex
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
Assisted-by: Codex
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
Quality-matrix runs on PR generative-computing#1587 hit two Ollama stall failure shapes the
existing retry net could not handle:

- test_litellm_token_metrics_integration[non-streaming] died to the 900 s
  pytest-timeout watchdog: LiteLLMBackend passes no request deadline, so
  litellm falls back to a 600 s per-attempt timeout and the OpenAI SDK
  silently retries a stalled attempt (verified: 3 attempts on a stalled
  socket), exhausting the budget with no retryable error.
- The flaky marker from generative-computing#1369 matched only the native httpx.ReadTimeout
  shape; the litellm path raises litellm.exceptions.Timeout whose
  APITimeoutError message never matched, so its retry was dead code.

Changes:
- Bound the litellm live test to the same 300 s the native
  OllamaModelBackend uses, with num_retries: 0 so one pytest attempt can
  never exceed the watchdog budget (verified end-to-end through
  LiteLLMBackend against a stalled socket: 1 request, 4.1 s at timeout=4).
- Extend the conftest flaky marker to a shared
  OLLAMA_TIMEOUT_RERUN_PATTERNS constant covering both timeout shapes, and
  pin the match behaviour in test/test_flaky_ollama_rerun.py (including
  that the watchdog kill itself is NOT retried).
- Capture the Ollama server log in the quality workflow and dump it on
  failure: server-side stalls were previously undiagnosable from the job
  log (zero server lines across a 90-minute test window).

Local verification: new regression tests + litellm, native-Ollama metrics
and tracing live tests pass against Ollama 0.33.0 / granite4.2:3b;
ruff, mypy clean.

Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
The first use of the failure-time log dump (run 32991484037) showed that a
plain tail is useless for diagnosis: Ollama logs slot print_timing lines
every ~25 tokens, so the last 100 lines covered ~30 s of a 1 h 43 m run and
missed the 31-minute stall window entirely. Dump the request-level story
instead (one GIN line per HTTP request with its duration, plus
error/warning and model load/unload lines) capped at 500 lines, and keep a
short raw tail for the final moment.

Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
…l shape

Run 33006850028 (3.13 lane) failed on test_openai_token_metrics_integration
[streaming] via the 900 s watchdog, and its server log (now captured
per-request) shows why: the runner's Ollama degraded mid-run (one request
took 15 m 0 s) and the 'Say hello' prompt is not reliably obeyed by the
model -- generations of 1865-2295 tokens were observed, which at ~9 t/s
CPU decode is a 3-15 minute single request. The streaming tests have no
output bound and no total-time bound, so one such generation can consume
the entire pytest-timeout budget.

Changes:
- Cap the five live 'say hello' telemetry tests at 64 output tokens via
  ModelOption.MAX_NEW_TOKENS (natively mapped to num_predict /
  max_tokens / max_completion_tokens by the Ollama, OpenAI and LiteLLM
  backends). 64 tokens is far more than a 'hello' answer needs and caps
  the worst case at seconds even on a degraded runner.
- Extend OLLAMA_TIMEOUT_RERUN_PATTERNS with 'TimeoutError': when a stalled
  stream goes quiet for the 120 s chunk guard, send_to_queue aborts it and
  the builtin TimeoutError is raised verbatim at the consumer. That is the
  streaming path's bounded form of the same stall; without it the test
  rides the per-chunk guard to the 900 s watchdog. The watchdog kill
  string ('Failed: Timeout (>900.0s) ...') does not match any pattern --
  pinned by the regression tests, which now cover four shapes.

Local verification: 14 passed (all live ollama/openai/litellm telemetry
tests, streaming and non-streaming, plus the four regression tests)
against Ollama 0.33.0 / granite4.2:3b; ruff + mypy clean.

Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
Run 33015176815 (3.13 lane) isolated the last unbounded client path:
test_openai_token_metrics_integration[streaming] ran for the full 900 s
pytest watchdog even though the 64-token output cap landed. The server
log shows why: a /v1/chat/completions stream that stayed open for
exactly 15m0s (the request completed at the same second the watchdog
fired). The 120 s per-chunk stream guard only bounds inter-chunk gaps;
a stalled or queued stream that keeps the connection alive keeps the
guard re-armed and has no total-time backstop, so it rides to the
watchdog and kills the job. The non-streaming paths already have the
300 s httpx bound -- streaming did not.

Changes:
- Wrap the mot.astream() consumption in the four live streaming telemetry
  tests with asyncio.wait_for(..., timeout=300.0), mirroring the
  non-streaming bound. A genuine stall now surfaces as a bounded builtin
  TimeoutError (empty message), which the OLLAMA_TIMEOUT_RERUN_PATTERNS
  net already retries; a healthy stream finishes in seconds so the
  wrapper is a no-op on a good runner.
- Pin the wait_for error shape ('TimeoutError: ', empty message, verified
  empirically) in the flaky-rerun regression tests.

Local verification: 17 passed (all live ollama/openai/litellm/HF
streaming tests with the wrapper plus the five regression tests) against
Ollama 0.33.0 / granite4.2:3b; ruff + mypy clean.

Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
Run 33039206380 (rerun, 3.11 lane) exposed the last unbounded path:
test_openai_token_metrics_integration[non-streaming] was watchdog-killed
at 900 s. The GIN timeline shows why: the request stalled, the OpenAI
SDK's 600 s read timeout fired at 10m0s (server logged the client
disconnect as a 500), the SDK then auto-retried the 5xx with its
default max_retries=2, and the second attempt was still in flight when
the 900 s pytest watchdog killed the test. The SDK's retry chain
multiplies the per-attempt bound past the per-attempt budget.

Set max_retries=0 and timeout=300.0 on the test's OpenAIBackend client
(passthrough to openai.OpenAI kwargs, verified on the live client),
mirroring the litellm test's timeout=300.0 + num_retries=0 and the 300 s
bound the other live paths use. A stalled server now yields one bounded
APITimeoutError/500 per attempt that the flaky marker retries.

The other two OpenAIBackend constructions in this file are error-path
tests (nonexistent model -> fast 4xx, which the SDK does not retry) and
are unchanged.

Local verification: 15 passed (all live ollama/openai/litellm telemetry
tests plus the five regression tests) against Ollama 0.33.0 /
granite4.2:3b; ruff + mypy clean.

Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
E.12 lane of run 33048969379 root-caused a fifth failure shape on
test_litellm_token_metrics_integration[streaming] (900-s watchdog).
EWO-fold root cause, both pinned empirically:

1. Cap: this PR's 64-token bound used ModelOption.MAX_NEW_TOKENS, which
   mellea maps to max_completion_tokens on the OpenAI/LiteLLM paths. The
   CI-pinned Ollama 0.32.2 /v1 handler (openai/openai.go at v0.32.2,
   verified in source) only maps max_tokens -> num_predict and silently
   ignores max_completion_tokens, so the say-hello request ran uncapped
   on CI for 15 m (GIN: 200 | 15m0s; the raw tail shows the server's
   decode running to a 2549-token runaway at ~9 t/s). Local Ollama 0.33.0
   accepts both fields, which masked the problem locally. Both /v1 live
   tests now send the raw "max_tokens": 64 key (it passes through
   ModelOption.replace_keys and the SDK's create() filter unchanged).
   The native-ollama test keeps the sentinel, which maps to num_predict
   on the native API.

2. Consumption: wait_for(300) wrapped only the first astream() call, but
   astream() returns as soon as its queue drains, so a long stream is
   finished by avalue() — unbounded — and rode to the 900-s pytest
   watchdog. All four live tests now wrap avalue() too (worst case
   300 s + 300 s < 900 s).

Two regression source-pins in test_flaky_ollama_rerun.py guard both
bounds so they cannot be silently 'tidied' away.

Local verification against Ollama 0.33.0 / granite4.2:3b: 17 passed in
3m32s (previously 6m47s — the cap now bites locally too); ruff format +
ruff check + mypy clean.

Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
The docstring quality gate (test_validate.py::
test_repository_docstrings_use_markdown_backticks[test]) rejects RST
double-backtick inline code; run 33063265303 (3.13 lane) caught the two
occurrences added in 83774ae. Switch to single backticks per the
project docstring convention.

Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
The published granite4.2:3b tag ships num_ctx 131072 in its Modelfile
(granite4.1:3b carries no num_ctx, so CI ran the 4096 default). On the
16 GB CPU runner the 131K context allocates a ~6 GB KV cache at load
time (8.35 GB total vs 2.75 GB at 8192, measured locally); the memory
pressure wedges inference for 15-30 minute windows that outlast the
3x300 s retry budget. Cross-PR data: 268 pull_request runs on other
branches since 2026-08-14 have zero Ollama-timeout reds, while every
red lane of this PR ran granite4.2:3b.

Re-point the local tag at an 8192-context build of the same weights
after the pull (tests request the same tag name, so no test changes),
and pin the override in test_flaky_ollama_rerun.py.

Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
The granite4.2:3b live-test stalls persist with the 8192-context
constraint in place (run 33093698028: ~47-minute wedge on the 3.13
lane, requests hanging at the 300 s cap on both the native and /v1
paths), which refutes the KV-allocation hypothesis. The native tests
already send thinking:false with a 2048 per-request context, so
thinking mode is not a sufficient explanation either.

The remaining differentiator versus main's green runs is the Ollama
version itself (0.32.2, a week old) exercising the 4.2 model on slow
CPU runners. 0.33.1 (2026-08-26) is the current stable and still
carries the GCC-13 AMX fix the pin exists for (generative-computing#1388). Bumping is the
minimal controlled experiment; if the wedge persists the issue goes
upstream to Ollama/Granite.

Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
granite4.2 thinks by default (model-intrinsic; the Ollama-served build
uses a raw prompt template, so the HF chat template's
enable_thinking variable is not in play). Ollama's /v1 endpoint
ignores chat_template_kwargs — the vLLM convention mellea sent for
ModelOption.THINKING: False — so the openai/litellm live tests ran 4.2
with thinking permanently on: a 64-token generation that is ~45
thinking tokens, i.e. minutes on a 4-vCPU CI runner, blowing the 300 s
request cap whenever the runner was 2-3x slower than nominal. That is
the CI wedge (runs 33093698028, 33104419835); native-path tests already
sent think=false and were unaffected.

Ollama >= 0.33.1 (the CI pin) maps the OpenAI reasoning_effort param
on /v1 to its think control: "none" -> think=false (verified locally:
completion_tokens 47 -> 2). Send it for THINKING: False on both
OpenAI-compatible backends, alongside the existing
chat_template_kwargs.enable_thinking so vLLM-served models keep their
mechanism (mirrors the THINKING: True case, which already sends both).

Tests: openai live /v1 test now uses ModelOption.THINKING: False
instead of the ignored manual extra_body; unit pins updated from
"reasoning_effort absent for False" to "reasoning_effort == 'none'";
new OpenAI-backend mapping tests for True/False.

Assisted-by: opencode
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
Run 33122730668 (c9172e0) shows the thinking-off fix working (early
/v1 granite4.2 requests complete in 4-46 s, openai /v1 test passes)
but a second, engine-level stall remains: from ~mid-run, every 4.2
request on both /v1 and native /api/chat degrades to exactly 300 s ->
500 for ~1.6 h on the 4-vCPU/16 GB runner (5 test failures on 3.11),
while the same tests pass locally and on 4.1. The degradation-over-time
signature points at accumulation (memory pressure / swapping with
several models resident, or engine state), but the serve log at default
verbosity records nothing around the hangs.

Three additions, all feeding the existing failure-gated dump step so
green runs are unaffected:
- OLLAMA_DEBUG=1: slog debug level (envconfig/config.go in v0.33.1) for
  engine-level detail during the hang.
- OLLAMA_DEBUG_LOG_REQUESTS=1: per-request bodies + replay curl commands
  (server/inference_request_log.go) to confirm what the server actually
  receives and processes.
- 30 s resource sampler (vmstat, with a free/loadavg fallback) from
  server start through the tests: memory, swap in/out, CPU wait.
Dump step gains the sampler log and the newest request-debug files.

Assisted-by: opencode
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
The remaining quality-matrix failures had two distinct causes, both
proven by the instrumented runs 33122730668 / 33152524235:

1. The telemetry tests built their prompt on SimpleContext, a stateless
   context whose view_for_generation() always returns [] - so the user
   message never reached Ollama and the model received an empty
   conversation. granite4.2 rambles for 2000+ tokens on empty input
   (4.1 stopped quickly, which is why this never surfaced), so uncapped
   requests ran into the client's 300 s cap. Those tests now use
   ChatContext and cap output at 64 tokens.

2. A context-size mismatch forces Ollama to reload the per-model runner,
   and Ollama 0.33.1 on x86 has a bug where such a reloaded runner
   ignores num_predict (fresh runners enforce it). All CI 4.2 requests
   now align on num_ctx=8192 (the Modelfile-constrained default) and the
   group warmup pins each model at the context its tests use, so the
   mismatched reload can no longer happen.

Assisted-by: opencode
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
test_ollama_token_metrics_integration[streaming] asserts that
gen_ai.client.operation.time_per_output_chunk is recorded, but that
histogram only records inter-chunk intervals - it needs >=2 streamed
chunks. The 'say hello' prompt can produce a single-chunk reply (the
model emits one token, e.g. 'hello'), which leaves the histogram empty
and fails the assertion (run 33163851256). Use a counting prompt that
reliably spans many tokens/chunks.

Assisted-by: opencode
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
…ndings

reasoning_effort="none" was being sent unconditionally to every
OpenAI-compatible server, including real api.openai.com, where it is
rejected by most current reasoning models. Gate it behind
self._server_type != _ServerType.OPENAI on the OpenAI backend and the
existing Ollama-provider-prefix check on the LiteLLM backend, matching
the mechanism each backend already uses for equivalent divergence.

Also, from the same review pass:
- Add granite-4.2 entries to BASE_MODEL_TO_CANONICAL_NAME so intrinsic
  adapter-path resolution stays consistent with the 4.0/4.1 siblings.
- Update onboarding docs (quickstart, installation, troubleshooting,
  tutorials, examples index) that still told new users to pull
  granite4.1:3b, now stale since start_session()'s default moved to
  granite4.2:3b.
- Fix the CI context-window guard to check the exact num_ctx value
  instead of just line presence, so a silently-failed re-point to 8192
  is actually caught.
- Replace the test asserting the raw "max_tokens" key reaches the wire
  unmapped (it doesn't — both backends remap it to
  ModelOption.MAX_NEW_TOKENS and back out to max_completion_tokens
  regardless) with one that reflects actual behaviour.
- Stop test_reasoning_effort_conditional_passing from being confounded
  by the shared fixture's default_extra_body, which forced
  enable_thinking=False regardless of what the mapping code did.
- Fix test_thinking_false_sets_reasoning_effort_none_and_disable in
  test_litellm_thinking.py, which had been flipped to assert the
  regression itself against a vLLM target; split it into a non-Ollama
  case (reasoning_effort absent) and a new Ollama-routed case (sent).

Assisted-by: Claude Code
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
@planetf1
planetf1 force-pushed the feat/granite-4-2-defaults branch from 1eebfb4 to b667812 Compare August 28, 2026 20:18
Cuts the incident-report-style comments in quality.yml (run IDs,
refuted hypotheses, PR cross-refs) and the duplicated reasoning_effort
comment in openai.py down to what/why, no history.

Assisted-by: Claude Code
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
@planetf1
planetf1 marked this pull request as ready for review August 29, 2026 08:11
@planetf1
planetf1 requested a review from a team as a code owner August 29, 2026 08:11
Same pass as 0f7a350: cuts run-ID/incident-report detail from the
rerun-pattern and warmup-model comments down to what/why.

Assisted-by: Claude Code
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
The chat_template_kwargs/reasoning_effort mapping was duplicated
verbatim across _generate_from_intrinsic and
_generate_from_chat_context_standard. Extract _map_thinking_option()
as the single implementation both call sites use.

Assisted-by: Claude Code
Signed-off-by: Nigel Jones <jonesn@uk.ibm.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Granite 4.2 models

1 participant