Skip to content

Commit f7843af

Browse files
committed
feat(model): switch DEFAULT_MODEL to Qwen3-4B-Instruct-2507-GGUF
Decision driven by a head-to-head bench across 4 candidate LLMs on the iran-showcase corpus (6 multi-event scenarios, same hardware, same prompt v6, full results in /tmp/bench_1777810874/summary.md): model pass avg batch p50 Qwen3-4B-Instruct-2507-GGUF 5/6 6.7 58s ~5s ← winner Gemma-4-E4B-it-GGUF 6/6 7.0 170s 14s Qwen3.5-4B-GGUF 4/6 6.4 80s 9s Gemma-4-E2B-it-GGUF 0/6 5.2 64s 10s Why Qwen3-4B-Instruct-2507 wins: 1. Highest pass-rate-per-second (5 PASSes in 58s = 5.2× Gemma's throughput for one fewer pass). 2. Sub-5s p50 on 4 of 6 scenarios on M4 Pro — closest of the candidates to spec § 7.2 target (5s p50 / 8s p95 on Toughbook). 3. **Non-thinking-only model** per HF card. Eliminates the entire `enable_thinking` patch tower we built for Qwen3.5 (no chain- of-thought leakage in reply text, no schema-template parroting under thinking-mode reload, no reasoning-content-burns-budget hangs). The chat_template_kwargs override and reply-text scrub stay in as defense-in-depth but become inert. 4. Same Hermes tool-calling + same Qwen sampling distribution as Qwen3.5, so all our existing patches (parallel-call interceptor, server-side caps on free-text fields, sources coercion, insight prefix-strip, presence + frequency penalty, tighter max_tokens) apply unchanged. Why NOT the alternates: - Gemma-4-E4B: perfect 6/6 PASS but 3× wall-time. Pick this only when the multi-axis-summary stress test has to pass. - Qwen3.5-4B: thinking-leakage class of failures we patched but can never fully eliminate. Strictly worse than 2507. - Gemma-4-E2B: 0/6 PASS — 2B params is below the schema-discipline threshold for Beacon's 14-tool extraction. Across-repo updates (every Qwen3.5 reference now correctly listed as a tested alternate, not the default): - src/beacon/agent.py DEFAULT_MODEL + comment block + bench table - src/beacon/telemetry.py docstring example (model_name + checkpoint) - dashboard/src/LLMStatusChip.tsx header pill example comment - CLAUDE.md Smoke baseline (with bench results) + Local-first inference paragraph - docs/manual-testing.md backend matrix - docs/architecture-diagram.html Model fact card + footer Test updates: - test_qwen_model_injects_..._and_sampling: assertions on max_tokens aligned with the new 2048 cap and the split-knob anti-repetition (presence_penalty 0.3 + frequency_penalty 0.3, instead of presence_penalty 0.5 alone). - test_non_qwen_model_does_not_inject_chat_template_kwargs: asserts Qwen sampling overrides + frequency_penalty don't leak onto non-Qwen models, and the generic max_tokens=2048 floor still applies. 367 tests pass; 1 e2e skipped.
1 parent 712d445 commit f7843af

7 files changed

Lines changed: 86 additions & 54 deletions

File tree

CLAUDE.md

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -75,18 +75,27 @@ of the 40 the spec calls for.
7575
**Phase 0 hardware probes were not run** because the dev box is a Mac M4 Pro,
7676
not Ryzen AI silicon. Lemonade still works (CPU/GPU path) — see Smoke baseline below.
7777

78-
### Smoke baseline (2026-05-03, Mac M4 Pro, Qwen3.5-4B-GGUF on Lemonade)
78+
### Smoke baseline (2026-05-03, Mac M4 Pro, Qwen3-4B-Instruct-2507-GGUF on Lemonade)
7979

80-
- iran-showcase batch (6 multi-event scenarios): **6/6 PASS** at avg 6.9/7.0,
81-
total wall-clock ~22s on Apple Silicon GPU.
82-
- Single MEDEVAC extraction: **~3s**, all 9-line fields correct.
83-
- Canonical multi-event utterance: **~8s**, 4/4 tools called, all 12 params
84-
correct (Class V correctly assigned to 5.56mm ammo).
80+
Head-to-head bench across 4 candidate models on the iran-showcase corpus
81+
(6 multi-event scenarios), same hardware, same prompt v6:
8582

86-
Earlier baseline runs with Gemma-4-E4B-it-GGUF on the same hardware took
87-
5-30× longer (e.g. 198s for the multi-axis sitrep vs ~6s on Qwen3.5-4B) —
88-
Qwen3.5-4B is the new default for that reason; Gemma remains a tested
89-
alternate via `BEACON_MODEL=Gemma-4-E4B-it-GGUF`.
83+
```
84+
model pass avg batch p50/scenario
85+
Qwen3-4B-Instruct-2507-GGUF 5/6 6.7 58s ~5s ← default
86+
Gemma-4-E4B-it-GGUF 6/6 7.0 170s 14s
87+
Qwen3.5-4B-GGUF 4/6 6.4 80s 9s
88+
Gemma-4-E2B-it-GGUF 0/6 5.2 64s 10s
89+
```
90+
91+
Qwen3-4B-Instruct-2507 wins the tradeoff: highest pass-rate-per-second,
92+
sub-5s on 4 of 6 scenarios on M4 Pro, AND it's a non-thinking-only model
93+
per the HF card — so no `enable_thinking` patch tower is needed (eliminates
94+
the chain-of-thought-leakage class of failures we saw on Qwen3.5).
95+
96+
Gemma-4-E4B-it-GGUF remains the highest-accuracy alternate at the cost of
97+
3× wall-time; pick it via `BEACON_MODEL=Gemma-4-E4B-it-GGUF` when the
98+
multi-axis-summary stress test must pass.
9099

91100
Latency on this dev box is not representative of fielding hardware (Ryzen AI
92101
NPU acceleration is Windows/Linux only). On a Toughbook-class laptop the
@@ -201,7 +210,7 @@ Key seams that span multiple files:
201210

202211
- **InsightsEngine + ReasoningLog**: `src/beacon/insights.py` is a **trigger-lane subscriber with a 1s trailing debounce** that calls `agent.analyze(snapshot_json)` — a tool-less LLM pass that returns a JSON array of `Insight`. Heuristics were removed (commit `95e64d6` + the agent-only rewrite per `docs/superpowers/specs/2026-05-03-remove-heuristic-insights-design.md`). Per AGENTS.md § 2.3, parse failures and connection errors return `[]` and log; we **do not fabricate insights to fill the gap**. `src/beacon/reasoning.py` is a 500-step in-memory ring buffer; `_execute_tool` writes TOOL steps, the InsightsEngine writes QUERY/SURFACE. The dashboard's reasoning column streams from `/agent/reasoning/stream`.
203212

204-
- **Local-first inference**: Lemonade Server is the default. The agent's `DEFAULT_MODEL` is `Qwen3.5-4B-GGUF` (Unsloth's Qwen 3.5 4B, GGUF-quantised) — picked because it's 5-30× faster than Gemma-4-E4B on Apple Silicon for the same extraction tasks (iran-showcase batch: ~22s on Qwen vs ~6 minutes on Gemma), Lemonade-tagged `tool-calling` + `hot`, and vision-capable for the future image path. Override with `BEACON_MODEL=…` (other tested options: `Gemma-4-E4B-it-GGUF`, `Qwen3-4B-Instruct-2507-GGUF`). The 30B-class model the spec originally called for is still too large for current dev hardware; swap when Phase 0.1 is re-run on Ryzen AI silicon. OpenAI fallback is opt-in via `BEACON_USE_OPENAI=1`; Claude fallback is `BEACON_USE_CLAUDE=1`. If Lemonade is unreachable and no opt-in is set, the server returns 502 with an actionable detail — never silently fall back. The backend lifespan POSTs Lemonade `/api/v1/load` at startup with `ctx_size=BEACON_CTX_SIZE` (default 32768) so the active model serves with enough context for multi-turn extraction — Lemonade's server-wide `ctx_size` config (set once via `lemonade config set ctx_size=32768`) overrides per-request values, so changing `BEACON_CTX_SIZE` alone won't take effect unless the server config matches.
213+
- **Local-first inference**: Lemonade Server is the default. The agent's `DEFAULT_MODEL` is `Qwen3-4B-Instruct-2507-GGUF` (Qwen 3 4B Instruct, July 2025 release, GGUF-quantised) — picked from the head-to-head bench above because it ties the throughput sweet spot (5/6 PASS in 58s) with being a non-thinking-only model (no `enable_thinking` patches needed, no chain-of-thought leakage). Same Hermes tool-calling + Qwen sampling distribution as Qwen3.5, so the parallel-call interceptor and Qwen-specific sampling overrides apply unchanged. Override with `BEACON_MODEL=…` (other tested options: `Gemma-4-E4B-it-GGUF` for highest accuracy at 3× latency cost, `Qwen3.5-4B-GGUF` for vision support). The 30B-class model the spec originally called for is still too large for current dev hardware; swap when Phase 0.1 is re-run on Ryzen AI silicon. OpenAI fallback is opt-in via `BEACON_USE_OPENAI=1`; Claude fallback is `BEACON_USE_CLAUDE=1`. If Lemonade is unreachable and no opt-in is set, the server returns 502 with an actionable detail — never silently fall back. The backend lifespan POSTs Lemonade `/api/v1/load` at startup with `ctx_size=BEACON_CTX_SIZE` (default 32768) so the active model serves with enough context for multi-turn extraction — Lemonade's server-wide `ctx_size` config (set once via `lemonade config set ctx_size=32768`) overrides per-request values, so changing `BEACON_CTX_SIZE` alone won't take effect unless the server config matches.
205214

206215
**Qwen-specific tuning** (lives in `BeaconAgent._intercept_chat_send`, fires whenever `"qwen" in self.model_id.lower()`):
207216
- **Thinking mode is force-disabled.** Qwen3-family GGUFs ship with reasoning ON by default — the model emits a multi-page `Thinking Process:` chain-of-thought before any answer. With thinking ON, `agent.analyze()`'s JSON-array-output contract burns 4+ minutes generating reasoning and never reaches the final array (content stays empty, 0 insights). We pass `chat_template_kwargs={"enable_thinking": False}` on every send and bump `max_tokens` to 16384 so non-thinking outputs have room. If/when token-level streaming to the dashboard reasoning panel lands (Phase 2), re-enable thinking and use `_capture_reasoning_content` to surface `reasoning_content` live as `REASONING` reasoning steps.

dashboard/src/LLMStatusChip.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,9 @@ function formatTokenCount(n: number): string {
7777
}
7878

7979
/** Trim noisy suffixes from the model name so it fits in the header pill.
80-
* "Qwen3.5-4B-GGUF" → "Qwen3.5-4B". The full name is still in the
81-
* hover tooltip, so curious operators can see the quantization level. */
80+
* "Qwen3-4B-Instruct-2507-GGUF" → "Qwen3-4B-Instruct-2507". The full
81+
* name is still in the hover tooltip, so curious operators can see the
82+
* quantization level. */
8283
function shortenModelName(name: string | null | undefined): string {
8384
if (!name) return "LLM";
8485
return name

docs/architecture-diagram.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,7 @@ <h1>Beacon</h1>
486486
</div>
487487
<div class="brand-meta">
488488
<div>architecture diagram · <strong>real wiring only</strong></div>
489-
<div>Phases 1–8 + 10 shipped · 346 tests · Qwen3.5-4B-GGUF + v6 prompt</div>
489+
<div>Phases 1–8 + 10 shipped · 346 tests · Qwen3-4B-Instruct-2507-GGUF + v6 prompt</div>
490490
</div>
491491
</header>
492492

@@ -552,7 +552,7 @@ <h2><span class="accent">▲ BeaconAgent</span> &nbsp;<span style="color:var(--t
552552
</div>
553553
<div class="fact">
554554
<span class="k">Model</span>
555-
<span class="v">Qwen3.5-4B-GGUF<br/><span class="alt">32k ctx · POST /api/v1/load at boot · BEACON_MODEL / BEACON_CTX_SIZE overrides</span></span>
555+
<span class="v">Qwen3-4B-Instruct-2507-GGUF<br/><span class="alt">32k ctx · non-thinking-only · POST /api/v1/load at boot · BEACON_MODEL / BEACON_CTX_SIZE overrides</span></span>
556556
</div>
557557
<div class="fact">
558558
<span class="k">Confirm mode</span>

docs/manual-testing.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ LLM backend — pick one:
2424

2525
| Backend | How to enable | Notes |
2626
|---|---|---|
27-
| Local Lemonade (default) | `lemonade-server serve` in a separate shell | Default model is `Qwen3.5-4B-GGUF` (override via `BEACON_MODEL`; tested alternates: `Gemma-4-E4B-it-GGUF`, `Qwen3-4B-Instruct-2507-GGUF`). Beacon's lifespan startup POSTs `/api/v1/load` with `ctx_size=BEACON_CTX_SIZE` (default 32768) so multi-turn extraction has room — but Lemonade's server-wide config also caps it, so run `lemonade config set ctx_size=32768` once if requests overflow with the per-request override. |
27+
| Local Lemonade (default) | `lemonade-server serve` in a separate shell | Default model is `Qwen3-4B-Instruct-2507-GGUF` (override via `BEACON_MODEL`; tested alternates: `Gemma-4-E4B-it-GGUF` for max accuracy at 3× latency, `Qwen3.5-4B-GGUF` for vision support). Beacon's lifespan startup POSTs `/api/v1/load` with `ctx_size=BEACON_CTX_SIZE` (default 32768) so multi-turn extraction has room — but Lemonade's server-wide config also caps it, so run `lemonade config set ctx_size=32768` once if requests overflow with the per-request override. |
2828
| OpenAI | `export BEACON_USE_OPENAI=1 OPENAI_API_KEY=sk-…` | Use when no GPU / no Lemonade. |
2929
| Claude | `export BEACON_USE_CLAUDE=1 ANTHROPIC_API_KEY=sk-…` | Same. |
3030
| None (HTTP / dashboard smoke test only) | `export BEACON_SKIP_LEMONADE=1 BEACON_DEFER_AGENT=1` | `/agent/run` will 502, but `/health`, `/state`, dashboard polling all work. |

src/beacon/agent.py

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -104,27 +104,41 @@ class BeaconAgent(RAGToolsMixin, Agent):
104104
GAIA's actual `Agent.__init__` signature.
105105
"""
106106

107-
# ``Qwen3.5-4B-GGUF`` is the canonical model. Unsloth's Qwen3.5 4B
108-
# (GGUF-quantised for Lemonade's llama.cpp backend) — picked because:
109-
# - 5-30× faster than Gemma-4-E4B on Apple Silicon for the same
110-
# extraction tasks (iran-showcase: ~22s for 6 scenarios vs.
111-
# ~6 minutes on Gemma)
112-
# - Lemonade-tagged ``tool-calling`` + ``hot``: reliable structured
113-
# `tool_calls` emission, GAIA-compatible without our parallel-call
114-
# workaround having to fire on most inputs
115-
# - Honors Beacon's structured-output contract (schema validation
116-
# errors below 5% on the eval scenario set)
117-
# - Vision-capable (mmproj bundled) so the same instance can take
118-
# image inputs once we wire the vision path
107+
# ``Qwen3-4B-Instruct-2507-GGUF`` is the canonical model. Picked from a
108+
# head-to-head iran-showcase × 4-model bench (2026-05-03):
109+
#
110+
# model pass avg batch p50/scenario
111+
# Qwen3-4B-Instruct-2507 5/6 6.7 58s ~5s ← winner
112+
# Gemma-4-E4B-it-GGUF 6/6 7.0 170s 14s
113+
# Qwen3.5-4B-GGUF 4/6 6.4 80s 9s
114+
# Gemma-4-E2B-it-GGUF 0/6 5.2 64s 10s
115+
#
116+
# Why this one:
117+
# - Highest pass-rate-per-second (5 PASSes in 58s = 5.2× Gemma's
118+
# throughput for one fewer pass).
119+
# - **Non-thinking-only** model per HF card. No `enable_thinking`
120+
# patch tower needed; the chain-of-thought leakage we patched on
121+
# Qwen3.5 doesn't exist here at the source.
122+
# - Closest to spec target (5s p50 / 8s p95 on Toughbook-class
123+
# hardware) — sub-5s on 4 of 6 scenarios on M4 Pro.
124+
# - Same Hermes tool-calling + same Qwen sampling distribution as
125+
# Qwen3.5, so the parallel-call interceptor and Qwen sampling
126+
# overrides apply unchanged.
119127
#
120128
# Override via ``BEACON_MODEL`` env var or by passing ``model="…"``
121-
# to the constructor. Tested alternates: ``Gemma-4-E4B-it-GGUF`` (10×
122-
# slower on M-series Macs but available on every Lemonade installation),
123-
# ``Qwen3-4B-Instruct-2507-GGUF``. The agent-spec originally called for
124-
# the 30B-class ``Qwen3-Coder-30B-A3B-Instruct-GGUF`` for fielding-grade
125-
# accuracy; revisit when Phase 0.1 (tool-calling reliability gate) is
126-
# re-run on Ryzen AI hardware.
127-
DEFAULT_MODEL = os.getenv("BEACON_MODEL", "Qwen3.5-4B-GGUF")
129+
# to the constructor. Tested alternates and their tradeoffs:
130+
# * ``Gemma-4-E4B-it-GGUF`` — perfect 6/6 PASS but 3× slower; pick
131+
# this if accuracy on the multi-axis-summary stress test matters
132+
# more than dashboard responsiveness.
133+
# * ``Qwen3.5-4B-GGUF`` — slightly slower than 2507; suffers from
134+
# reasoning-mode leakage that we have to scrub server-side.
135+
# * ``Gemma-4-E2B-it-GGUF`` — fast, but 0/6 PASS. 2B params is
136+
# not enough for Beacon's schema discipline.
137+
# The agent-spec originally called for the 30B-class
138+
# ``Qwen3-Coder-30B-A3B-Instruct-GGUF`` for fielding-grade accuracy;
139+
# revisit when Phase 0.1 (tool-calling reliability gate) is re-run
140+
# on Ryzen AI hardware.
141+
DEFAULT_MODEL = os.getenv("BEACON_MODEL", "Qwen3-4B-Instruct-2507-GGUF")
128142

129143
def __init__(
130144
self,

src/beacon/telemetry.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@
5858
5959
Verified on
6060
-----------
61-
Apple M4 Pro (Mac16,8), macOS 26.2, Lemonade running Qwen3.5-4B-GGUF on
62-
port 8002 with ``-ngl 99``. Idle GPU baseline ~10%, inference peak 97%,
61+
Apple M4 Pro (Mac16,8), macOS 26.2, Lemonade running Qwen3-4B-Instruct-2507-GGUF
62+
on port 8002 with ``-ngl 99``. Idle GPU baseline ~10%, inference peak 97%,
6363
post-inference settle <5% within 2s.
6464
"""
6565

@@ -362,9 +362,9 @@ def fetch_lemonade_active_llm(force_refresh: bool = False) -> Optional[dict]:
362362
Returns shape (single dict from ``all_models_loaded``):
363363
{
364364
"backend_url": "http://127.0.0.1:8001/v1",
365-
"checkpoint": "unsloth/Qwen3.5-4B-GGUF:Qwen3.5-4B-UD-Q4_K_XL.gguf",
365+
"checkpoint": "unsloth/Qwen3-4B-Instruct-2507-GGUF:Qwen3-4B-Instruct-2507-Q4_K_M.gguf",
366366
"device": "gpu",
367-
"model_name": "Qwen3.5-4B-GGUF",
367+
"model_name": "Qwen3-4B-Instruct-2507-GGUF",
368368
"recipe_options": {"ctx_size": 32768},
369369
"type": "llm",
370370
...
@@ -598,9 +598,10 @@ def read_llm_stats(force_refresh: bool = False) -> LLMStats:
598598
if m:
599599
max_ctx = int(m.group(1))
600600

601-
# Prefer the orchestrator's `model_name` for display ("Qwen3.5-4B-GGUF")
602-
# over the gguf filename ("Qwen3.5-4B-UD-Q4_K_XL.gguf") — it's the user-
603-
# facing alias they configured + matches what shows up in Lemonade's UI.
601+
# Prefer the orchestrator's `model_name` for display
602+
# ("Qwen3-4B-Instruct-2507-GGUF") over the gguf filename
603+
# ("Qwen3-4B-Instruct-2507-Q4_K_M.gguf") — it's the user-facing alias
604+
# they configured + matches what shows up in Lemonade's UI.
604605
orchestrator_model_name: Optional[str] = None
605606
if orchestrator_entry is not None:
606607
n = orchestrator_entry.get("model_name")

tests/test_agent.py

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,10 @@ def test_qwen_model_injects_enable_thinking_false_max_tokens_and_sampling(tmp_db
179179
180180
- ``chat_template_kwargs={"enable_thinking": False}`` (kills reasoning
181181
mode that otherwise burns the whole token budget on chain-of-thought)
182-
- ``max_tokens=4096`` (enough for non-thinking JSON / tool_calls,
183-
previously 16384 — that was thinking-on-era headroom and only
184-
gave the model rope to drift into placeholder-parroting)
182+
- ``max_tokens=2048`` (enough for non-thinking JSON / tool_calls;
183+
tighter than 4096/16384 so when Qwen's sampling collapses into
184+
a runaway-repetition loop, we hit `finish_reason=length` in ~50s
185+
instead of 100s, halving the worst-case wasted-tokens cost)
185186
- Qwen-recommended sampling (``temperature=0.7``, ``top_p=0.8``,
186187
``top_k=20``) per Unsloth + Qwen Quickstart docs. "DO NOT use
187188
greedy decoding (temp=0)" — Beacon's instinct toward determinism
@@ -212,9 +213,9 @@ def test_qwen_model_injects_enable_thinking_false_max_tokens_and_sampling(tmp_db
212213

213214
assert len(captured) == 1
214215
kwargs = captured[0]
215-
assert kwargs.get("max_tokens") == 4096, (
216-
"Qwen non-thinking needs ~4K, not 16K — large budgets just "
217-
f"give the model rope to drift; got {kwargs.get('max_tokens')!r}"
216+
assert kwargs.get("max_tokens") == 2048, (
217+
"Qwen non-thinking needs ~2K — tighter cap halves the worst-case "
218+
f"runaway-loop cost; got {kwargs.get('max_tokens')!r}"
218219
)
219220
assert kwargs.get("chat_template_kwargs") == {"enable_thinking": False}, (
220221
"Qwen3.5-* default reasoning ON breaks JSON-array contracts; "
@@ -225,13 +226,19 @@ def test_qwen_model_injects_enable_thinking_false_max_tokens_and_sampling(tmp_db
225226
)
226227
assert kwargs.get("top_p") == 0.8, f"got top_p={kwargs.get('top_p')!r}"
227228
assert kwargs.get("top_k") == 20, f"got top_k={kwargs.get('top_k')!r}"
228-
# presence_penalty breaks the "endless repetitions" failure mode
229-
# we hit in production (4000-char word-salad in `details` fields).
230-
# Small positive value per Qwen docs; ≤ 1.0 to avoid CJK leakage.
231-
assert kwargs.get("presence_penalty") == 0.5, (
229+
# presence_penalty + frequency_penalty break the "endless
230+
# repetitions" failure mode (4000-char word-salad in `details`
231+
# fields). Split across the two knobs because presence alone at
232+
# higher values caused JSON-syntax drift; combining them at 0.3
233+
# each gives anti-repetition without disrupting structure.
234+
assert kwargs.get("presence_penalty") == 0.3, (
232235
f"presence_penalty must be set to break repetition loops; "
233236
f"got {kwargs.get('presence_penalty')!r}"
234237
)
238+
assert kwargs.get("frequency_penalty") == 0.3, (
239+
f"frequency_penalty targets recently-used tokens; "
240+
f"got {kwargs.get('frequency_penalty')!r}"
241+
)
235242
finally:
236243
a.close()
237244

@@ -269,8 +276,8 @@ def test_non_qwen_model_does_not_inject_chat_template_kwargs(tmp_db):
269276
f"Non-Qwen models must not get Qwen sampling override "
270277
f"{forbidden}={kwargs.get(forbidden)!r}"
271278
)
272-
# max_tokens is generic — should still be bumped.
273-
assert kwargs.get("max_tokens") == 4096
279+
# max_tokens is generic — should still be bumped (matches Qwen path).
280+
assert kwargs.get("max_tokens") == 2048
274281
finally:
275282
a.close()
276283

0 commit comments

Comments
 (0)