DEV Community

Cover image for One max-tokens Flag, Two Very Different Models: My Head-to-Head Run of Qwen 0902 and DeepSeek V4.1-Flash
张洲诚(Zack.ZHANG)
张洲诚(Zack.ZHANG)

Posted on

One max-tokens Flag, Two Very Different Models: My Head-to-Head Run of Qwen 0902 and DeepSeek V4.1-Flash

On September 2, Alibaba put the 0902 snapshot of qwen3.8-max on Bailian, and it took the top spot on Arena.ai's Code Arena WebDev leaderboard on launch day, ahead of Claude Opus 5 and Kimi K3. Eight days later DeepSeek shipped V4.1-Flash: KV cache 437x smaller than the first generation, open weights on HuggingFace, and an input price around a fifth of the previous flagship Pro's.

I read the coverage like everyone else, and I suspect most of you came out the other side holding the same question: what do these two feel like on my actual work? So I ran them head-to-head. Same prompt, same parameters, receipts kept. And somewhere in the middle of it I hit something I've never seen before: the same max-tokens flag means different things on the two models.

Two flagships, one command

The setup

I installed Bailian's agent skill into my coding agent (one command install, grab a free API key), so switching models is one sentence in a conversation I'm having anyway. Terminal people can npm-install the CLI directly; the commands are identical.

A sanity check first. One mechanism worth knowing: the agent doesn't reach for bl on an ordinary question — the skill's routing rules say name Bailian or a specific model, and it hands the job to the CLI; anything else it answers itself. So I told my agent:

Answer with Bailian's default model: explain idempotency in one sentence, with an example from e-commerce order flows.

bl text chat --message "Explain idempotency in one sentence, with an example from e-commerce order flows."
Enter fullscreen mode Exit fullscreen mode

No --model, so it hit the default, qwen3.8-max. To pick a different model, pass the name via --model — the two this post races were called exactly so. One detail easy to miss: the model field came back as the alias, not the snapshot name. The official mechanism rolls the alias to the newest snapshot automatically, so the default already switched to 0902, and you can't tell from the response. Where to get the full name: the "Models" page in the Bailian console lists a "latest version" on every model card — for qwen3.8-max that's qwen3.8-max-0902 right now. Pin it with --model qwen3.8-max-0902 if you care.

Both entries counted my prompt as exactly 76 tokens. Both responses carried reasoning tokens (27 and 33), billed as output. qwen3.8-max thinks by default, and the thinking is billed.

The head-to-head, and the flag that split in two

The task: a single-file dark tech-style landing page, requirements pinned (nav, typewriter hero, three cards with hover glow, footer). Why a frontend task: the leaderboard 0902 topped is a frontend-programming board, the requirements pin down hard enough that both models got a word-for-word identical brief, and a complete page runs to several hundred lines — big enough that model behavior, billing, and long-job pits have nowhere to hide.

0902 first, with an 8,192 budget. The bare command died in about a minute with Request timed out, so long jobs need --timeout 600:

bl text chat --model qwen3.8-max-0902 --max-tokens 8192 --timeout 600 \
  --message "<the brief>" --output json
Enter fullscreen mode Exit fullscreen mode

That run finished with finish_reason: length, code amputated mid-file, no </html>. The receipt held a surprise: total completion was 11,160 tokens against my 8,192 cap. Split it open: 2,966 reasoning, 8,194 text. On 0902, max-tokens caps the prose only. The thinking bills on top, outside the budget.

deepseek-v4.1-flash, same prompt, same 8,192: completion came back as 8,192 reasoning and zero prose. The model wrote itself 20,000 characters of deliberation, exhausted the budget, and terminated still inside its own head. On V4.1-Flash, max-tokens is the combined budget for thinking plus text.

One flag, two meanings

One flag, two semantics. Carry one mental model into both and you get hit one way or the other.

Doubled the budget to 16,384. V4.1-Flash delivered 495 complete lines. 0902 hit a different wall first: a Headers Timeout partway through, which turned out to be the Node HTTP layer's response-header timer (roughly 300 seconds) firing before the CLI's own --timeout 600. --stream walks around it, headers come back immediately:

bl text chat --model qwen3.8-max-0902 --max-tokens 16384 --timeout 600 --stream \
  --message "<same brief>"
Enter fullscreen mode Exit fullscreen mode

Streamed to completion: 673 lines. Both pages run in a browser, and the temperaments couldn't differ more. 0902 invented a brand for itself, typed the name out in the hero, then volunteered things I never asked for: a stats strip, a BOOT OK terminal flourish, scroll-aware nav highlighting. V4.1-Flash held the spec line for line, nothing extra, and handled prefers-reduced-motion for users who opt out of animation. Client-facing page, I take 0902's version and cut. Quick proof of idea, Flash's version is the easier life.

Same brief, two landing pages

Three pits on long jobs

Attribution first: none of these are about installing the Bailian CLI — that's one command and went smoothly. The pits live in the invocation stage, the kind that only shows up when you ask a model to write several hundred lines of code in one shot. And they escalate: the first gets your wait long enough, the second discovers long enough still breaks, the third fixes the break and loses the bill.

  1. The default timeout. The first submission quit after about a minute with a single line on screen: Request timed out. The CLI's default wait is sized for chat; long jobs need --timeout 600. The flag exists in --help, it's just not prominent.
  2. The headers timeout. Even with the CLI timeout raised, non-streaming long jobs still break with UND_ERR_HEADERS_TIMEOUT — the Node HTTP layer underneath has its own ~300-second response-header timer, waiting for the server's first byte, and --timeout can't reach it. --stream walks around it: the server speaks immediately, so the wait-for-first-word never happens.
  3. Streaming carries no receipt. --stream output is plain text chunks with no totals at the end — the usage data lives only in the full response's JSON. Need the numbers, run non-streaming with --output json.

Three pits on long jobs

One rule, then: short jobs don't care; long jobs take --timeout 600 and --stream together; run one extra non-streaming pass when you need the accounting.

The receipts

I had the agent check the books (bl usage free). Headline: paid amount, zero. Both models carry a 1-million-token free quota each, 0902's to December 1 and V4.1-Flash's to December 13, and everything rode inside them.

At list rates: 0902 did 608 input and roughly 26,000 output tokens, about 0.94 CNY (13 US cents). V4.1-Flash did 393 and 17,914, about 0.14 CNY at peak (two cents). Same order of work. V4.1-Flash also has peak/off-peak pricing: input 2/1 and output 8/4 CNY per million, so off-peak is exactly half. Batch jobs moved into the off-peak window cost half with zero code changes.

My routing after this run: bulk generation, drafts, and daily Q&A on V4.1-Flash; deliverables on 0902. At peak, Flash's output price is 22% of 0902's. This matches what Reddit vibe-coding budget threads keep converging on: cheap model for 80% of the work, expensive model at the moments that matter.

The actual bill

Two more findings worth keeping. The tokenizers differ: the identical Chinese prompt came back as 136 input tokens on 0902 and 99 on V4.1-Flash, so cross-model price math in tokens is fiction, do it in currency. And the 3x output ceiling gap (128K vs 384K) is real at the API layer: --max-tokens 200000 gets rejected by 0902 with a 400 spelling out [1, 131072], while V4.1-Flash serves it normally.

One caution before you budget around anything you read: a widely-shared write-up claimed 0902 prices at 2 CNY in and 6 out per million. The catalog (bl model list --model qwen3.8-max) says 12 and 36, six times higher. Check numbers yourself before they go into a cost model.

Leaderboards are fine for direction. Your own workload, run head-to-head, takes half an hour and beats ten more reviews. Install here: one command install, free API key. Drop a task in the comments and I'll run a few head-to-head and post the receipts.

Top comments (0)