Skip to content

Sampling is deterministic (does not re-randomize per request) on fallback-loaded models #171

Description

@lmargit

Models loaded through OpenArc's fallback path for architectures the installed transformers/optimum-intel version doesn't fully recognize (logged as You are using a model of type to instantiate a model of type . This is not supported for all configurations of models and can yield errors.) show three related generation-quality problems that do not appear on a natively-supported architecture. Tested across three models on the same host/image/config to isolate the pattern.

Environment

  • Image: ghcr.io/searchsavior/openarc-battlemage:latest (digest sha256:3036d10c7bb27705eb5f801c3967bba2969a9c126e3b2620b499c58a679aa414, built 2026-09-05)
  • OpenARC Version: v2.0.5-dirty, Git ref fb39ea7942459580fa0af47cdaa624993e307bd4
  • openvino 2026.5.0.dev20260903, openvino-genai 2026.5.0.0.dev20260903, openvino-tokenizers 2026.5.0.0.dev20260903
  • optimum 2.3.0, optimum-intel 2.2.0.dev0+4632054
  • Hardware: Intel Arc Pro B70 (Battlemage), engine ovgenai, device GPU
  • Host: TrueNAS Scale, docker compose deployment

Models tested

Model | Provenance | Architecture warning at load? | Loaded as -- | -- | -- | -- gemma4-31b | Locally converted via optimum-cli export openvino --model google/gemma-4-31b-it --task image-text-to-text --weight-format int4 --group-size 128 --ratio 1.0 --trust-remote-code | Yes — type gemma4 | vlm qwen3.8-27b-int4 | Pre-converted, downloaded verbatim from OpenVINO/Qwen3.8-27B-int4-ov on HuggingFace (no local conversion) | Yes — type qwen3_5 | vlm qwen3-vl-8b-int4 | OpenVINO-provided, natively supported | No | vlm

Note the two affected models have different provenance — one a local optimum-cli export, the other an official pre-converted release downloaded as-is — yet both trigger the identical fallback warning and show the identical determinism bug. This makes a local-conversion mistake an unlikely explanation for at least the qwen3.8-27b-int4 case, pointing more toward a genuine OpenArc/optimum-intel architecture-support gap for both gemma4 and qwen3_5 types.


Bug 1 — Sampling is deterministic (does not re-randomize per request) on fallback-loaded models

Repro:

bash
for i in 1 2 3 4 5; do
  curl -s http://<host>:<port>/v1/chat/completions \
    -H "Content-Type: application/json" \
    -d '{"model":"<model>","messages":[{"role":"user","content":"Please, provide the history of the James Webb Telescope, the new functionalities and advances it represents versus the previous telescopes."}],"stream":false,"temperature":0.7}' \
    | jq -r '.choices[0].message.content' > /tmp/run_$i.txt
done
md5sum /tmp/run_*.txt

Expected: with do_sample: true, temperature: 0.7, top_p: 0.95 (from generation_config.json), and no seed supplied, five independent requests should produce five different completions.

Actual:

  • gemma4-31b: (natively supported), reproduced across a full model unload/reload cycle.
  • qwen3.8-27b-int4: 5/5 identical (same md5), independently confirmed.
  • qwen3-vl-8b-int4 (natively supported, no fallback warning): 5/5 different md5s — normal sampling variation, as expected.

Passing an explicit seed value restores variation on gemma4-31b, but the same seed value does not reproduce identical output across repeated calls either — i.e. seed is accepted but does not appear to be wired into deterministic reproduction. This suggests the RNG state used for sampling is fixed/not re-initialized per request specifically on the fallback-loaded code path, rather than a temperature/top_p handling bug.

Attempted native-only isolation: confirmed via direct requests to OpenArc's port (bypassing a custom reverse proxy sitting in front of it), ruling out anything proxy-side as the cause.


Bug 2 — Reasoning/content field separation breaks down on fallback-loaded reasoning model, even with a large token budget

Model: qwen3.8-27b-int4 (fallback-loaded, type qwen3_5)

Repro:

bash
curl -s http://<host>:<port>/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen3.8-27b-int4","messages":[{"role":"user","content":"Please, provide the history of the James Webb Telescope, the new functionalities and advances it represents versus the previous telescopes."}],"stream":false,"temperature":0.7,"max_tokens":4000}' \
  | jq '{reasoning: .choices[0].message.reasoning_content, content: .choices[0].message.content}'

Expected: internal reasoning/thinking content routed to reasoning_content; final answer routed to content (this works correctly for this same model on a trivial prompt, e.g. "Hello" — reasoning_content and content are populated separately and correctly).

Actual: for this longer/more complex prompt, reasoning_content is null and the entire internal reasoning/planning trace (visibly in a "we need to..." planning voice, second-guessing facts, drafting an outline) is dumped into content instead — and even at max_tokens: 4000, generation is still deep in unstructured planning with no final answer ever produced, i.e. it isn't merely a token-budget issue at moderate limits (already confirmed not to be one at 1200 tokens, then re-confirmed by more than tripling the budget).

Control: the natively-supported qwen3-vl-8b-int4 produces a clean, complete, well-terminated answer with reasoning_content: null and a fully-formed content on the identical prompt at the same max_tokens: 4000 — though this model may not implement a reasoning/thinking mode at all, so this is not a fully like-for-like comparison for this specific bug; noted for completeness rather than as definitive negative evidence.


Bug 3 — Intermittent stray token substitution in markdown table divider rows

Model: gemma4-31b (fallback-loaded, type gemma4)

Repro: ask for a response containing a 3-column markdown comparison table; inspect the header divider row.

Actual: across 3 independent generations (2 at temperature: 0.7 with real sampling variation, one from an unrelated frozen-RNG run), the third column's divider cell renders as own instead of --- in 2 of the 3:

| Feature | Hubble Space Telescope | James Webb Space Telescope |
| :--- | :--- | own |
| Feature | Hubble Space Telescope | James Webb Space Telescope |
| :--- | :--- | : own |
  • Verified own is an ordinary, unremarkable vocabulary token (id 931) via direct inspection of tokenizer.json — not a corrupted or control-token-adjacent entry, so this doesn't look like a tokenizer/vocab mismatch.
  • The model's chat_template.jinja (Gemma4's channel/turn/tool-call structured template) was inspected and appears well-formed; no evidence the leaked token is a mishandled control-token fragment.
  • Control: the natively-supported qwen3-vl-8b-int4, asked to produce comparable tables (once via explicit table request, once inside a long free-form answer), produced clean, correct --- divider rows both times, no anomaly.

Given Bug 1, we can't fully rule out that this is a downstream symptom of whatever mechanism causes the frozen sampling (e.g., a bad early logits pass being replayed) rather than an independent defect — flagging both possibilities for the maintainers to assess given visibility into the relevant code paths.


Suggested framing

All three symptoms appear only on models routed through the "unsupported model type" fallback path (gemma4, qwen3_5), and none appear on a natively-supported architecture (qwen3-vl) tested identically on the same host/build. This suggests the fallback instantiation path is missing some initialization step — plausibly around per-request generation/RNG state setup — that the natively-supported path performs correctly. Happy to provide full raw request/response logs, the exact docker-compose.yaml, or run further isolation tests on request.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions