Skip to content

5080deploy: merge upstream #559, #531, #269, #468, #461, #484, #464, #266, #572 - #76

Open
gdevenyi wants to merge 25 commits into
5080/33-rebuild-d3512b4from
5080/34-upstream-prs
Open

gdevenyi wants to merge 25 commits into
5080/33-rebuild-d3512b4from
5080/34-upstream-prs

Conversation

@gdevenyi

@gdevenyi gdevenyi commented Oct 2, 2026 •

Copy link
Copy Markdown
Owner

Stacked on #75 (the rebuild). This merges 9 open upstream PRs that are generic or matter for this deployment, one merge each, at their current heads:

PR What Why here
FlashML-org#559 clone inserted radix keys hybrid_radix node keys were views that pinned each request's whole id buffer (up to 262K tokens) in host RAM
FlashML-org#531 clamp /v1/models context_length to the KV pool correct metadata for agent compaction (262144 here, unchanged)
FlashML-org#269 report why a tokenizer worker died startup diagnostics
FlashML-org#468 reject fa below sm_90 at config time sm_120 guard; auto still picks qsa_sparse
FlashML-org#461 + 1080deploy b54cced loopback-bind the TP rendezvous store, --dist-port production listened on *:1920 (all interfaces), now on 127.0.0.1:1920; b54cced hands the fd to TCPStore
FlashML-org#484 --decode-interleave-every off by default, unchanged behaviour; an A/B for long cold prefills comes later
FlashML-org#464 + 1080deploy 1814a3a, 577427a, fc2392b stop strings with incremental decoding multibyte stop strings; same-uid detokenize guard (fork #69); the follow-ups match FlashML-org#546's delivered-limit semantics and skip_special_tokens
FlashML-org#266 hide Qwen transport markers a leaked <|im_start|> is fed back to the agent as a control token
FlashML-org#572 keep a silent stream alive, notice a client that left Node's fetch drops a body after 300 s of silence, and a 255K cold prefill takes ~187 s

Conflict resolutions:

Live checks on the new build:

  • max_tokens 2/8/64 return exactly 2/8/64 tokens.
  • Ports: 127.0.0.1:1919 and 127.0.0.1:1920.
  • /v1/models context_length 262144; /v1/models/ → 200.
  • A 120K uncached prompt got 5 keep-alive comments over 80 s.
  • A client dropped mid-prefill after 20 s gets Aborting request in the log.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Bu6LgoxLR4wETqb7RPR2vt

HaileyStorm and others added 25 commits August 28, 2026 19:53
_run_scheduler wraps startup and pushes an ("error", reason) ack before dying,
so the supervisor can report the real cause. _run_tokenize_worker did not, so
every tokenizer-side startup failure reached the parent only as a dead process
and surfaced as the generic "backend worker freetoken-detokenizer-0 exited
during load" — the actionable reason (unreadable tokenizer.json, missing
checkpoint file, ZMQ bind clash) was lost.

Wrap tokenize_worker in the same try/report/re-raise the scheduler already uses.
The traceback still prints and the process still exits non-zero; only the ack is
added. No ack_queue (direct call) stays a plain propagate.

Adds tests/server/test_launch_worker_errors.py, which fails before this change.
_schedule_next_batch gives prefill unconditional priority (or-short-circuit), so a
long prompt prefilled in chunks occupies that many consecutive scheduler steps
during which in-flight decodes are never scheduled. Measured on the live service:
four cold prefills of 680/797/506/250 chunks over 267/317/190/93 s, a
prefill-burst share of 70% in a 15-minute window, at 0.388 s per 384-token chunk.
An already-decoding request therefore waited up to ~5 minutes for its next token.

Reversing the order outright would starve prefill, and a request that never
prefills never starts. The scheduler already carried the marker for this: a TODO
reading "support other policies: e.g. DECODE first".

Add --decode-interleave-every N: prefill keeps priority, but after N consecutive
prefill steps one decode step is taken if a decode is runnable. At N=8 that bounds
the stall to ~3 s instead of ~300 s, for about 1% of prefill wall time. Unset
keeps the historical order bit-for-bit, and a Scheduler constructed without the
policy (as the accounting tests build it) still works.

Assisted-by: Claude Code
…of it

Review of the previous commit returned "merge with 2 test nits" and no
correctness defect, and named the gap precisely: several tests drove _decide, a
pure re-implementation of the decision written inside the test file. That is a
model of the code, not the code, so it can drift and the tests keep passing while
the real _schedule_next_batch diverges.

Adds coverage against the real method via Scheduler.__new__ with stub managers: a
truth table over policy=None/1/3/8 asserting the exact slot sequence, strict
alternation at every=1, the latch case (decode due but unavailable, prefills
continue, then decode fires exactly once), the pure-decode and no-work paths, a
policy that exists but is disabled, decode-manager call counts, and the
config -> Scheduler.__init__ -> CLI wiring.

Two expectations written from reasoning were wrong and the real scheduler
corrected them (every=1 starts with prefill, because the streak begins at 0),
which is the value this coverage adds.

Assisted-by: Claude Code
/v1/models published `config.max_seq_len` -- the model's positional ceiling -- while the
engine clamps its own `max_seq_len` to `min(config.max_seq_len, num_pages * page_size)` at
startup and again in `_refresh_seq_state` after a rebuild, and the scheduler admits against
that clamped value. The frontend process holds an unclamped ServerArgs copy, so the two
disagreed about one quantity: on a 12 GiB card, 262144 advertised against a 178176-token pool.

`ft launch` reads this number to size each agent's context window (opencode `limit.context`,
codex `context_window`/`max_context_window`, CLAUDE_CODE_MAX_CONTEXT_TOKENS), so agents set
their compaction threshold past what the server can hold and hit `context_length_exceeded`
instead of compacting in time.

Split the pool resolution out of `cache_geometry` into `kv_pool_geometry` so /v1/models and
/v1/cache/status cannot drift apart, and clamp the advertised value with it. Falls back to the
model ceiling while `num_pages` is still 0 (loading, or an engine build that sends no meta ack).

The existing route test passed either way -- FakeState carries no pool geometry, so it fell
through to the ceiling. It now asserts that fallback explicitly, alongside new cases for the
clamp, a pool larger than the ceiling, and a rebuild superseding the load-time allocation.

Fixes FlashML-org#448

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Declare requires_sm90 on the fa BackendInfo entry so an explicit
--attention-backend fa fails during config validation on Ada and
consumer Blackwell instead of after model load, during CUDA-graph
capture.

Fixes FlashML-org#442
STREAM_KEEPALIVE_SECONDS exists, so the tests set it without raising=False: a
renamed constant now fails them instead of leaving them on the 15 s default. The
manager helper drops the abort list no test read.
…tore, add --dist-port

# Conflicts:
#	python/freetoken/engine/config.py
…ping its socket

TCPStore owns master_listen_fd and closes it with the store; FlashML-org#461 also kept the Python
socket on the engine, whose finalizer then closed whatever file reused that fd number
after destroy_process_group at shutdown.

Assisted-by: Claude Opus 5.5 (1M context)
(cherry picked from commit b54cced)
…ental decoding

# Conflicts:
#	python/freetoken/tokenizer/detokenize.py
…limit

FlashML-org#464 used `not req.can_decode`, but since FlashML-org#546 overlap runs device_len a step ahead of
the host, so a non-terminal EOS was dropped from the matched text one step early.

Assisted-by: Claude Opus 5.5 (1M context)
(cherry picked from commit 1814a3a)
…han once

Assisted-by: Claude Opus 5.5 (1M context)
(cherry picked from commit 577427a)
…cial_tokens decodes

FlashML-org#464's scheduler-side DecodeStatus decoded with special tokens while FlashML-org#393 lets a request
skip them on the frontend, so a stop spanning a special token matched different text.

Assisted-by: Claude Opus 5.5 (1M context)
(cherry picked from commit fc2392b)
…semantic responses

# Conflicts:
#	python/freetoken/server/generation.py
…tice a client that left

# Conflicts:
#	python/freetoken/server/api_server.py

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

TCPStore compatibility, port validation, and streaming event-order defects remain unresolved.

Review effort: Balanced
Findings: 1 High severity · 3 Medium severity · 2 Low severity

Open (6)
What changed in this PR

This deployment merge combines upstream reliability, scheduling, cache, streaming, and API metadata improvements.

Changes:

  • Adds stream keep-alives, Qwen marker filtering, incremental stop matching, and startup diagnostics.
  • Adds decode interleaving, KV-aware context reporting, and compact radix-key storage.
  • Secures/configures TP rendezvous and validates attention backend architecture support.
File Description
tests/​tokenizer/​test_detokenize_extras.py Tests ordered same-UID decoding.
tests/​server/​test_stream_keepalive.py Tests SSE keep-alives and disconnects.
tests/​server/​test_qwen_semantic_special_tokens.py Tests Qwen marker filtering.
tests/​server/​test_openai_api.py Tests KV-clamped model metadata.
tests/​server/​test_launch_worker_errors.py Tests tokenizer startup diagnostics.
tests/​server/​test_dist_port_arg.py Tests distributed-port parsing.
tests/​scheduler/​test_match_stop_str.py Tests incremental stop matching.
tests/​scheduler/​test_interleave.py Tests decode interleaving policy.
tests/​engine/​test_distributed_store.py Tests loopback rendezvous storage.
tests/​engine/​test_attention_backend_arch.py Tests FA architecture guards.
python/​freetoken/​tokenizer/​detokenize.py Refactors incremental decoding state.
python/​freetoken/​server/​openai_api.py Clamps advertised context capacity.
python/​freetoken/​server/​launch.py Reports tokenizer worker failures.
python/​freetoken/​server/​generation.py Filters Qwen transport markers.
python/​freetoken/​server/​args.py Adds distributed-port and interleave options.
python/​freetoken/​server/​api_server.py Adds keep-alives and KV geometry.
python/​freetoken/​scheduler/​scheduler.py Integrates stop decoding and interleaving.
python/​freetoken/​scheduler/​interleave.py Defines interleaving policy.
python/​freetoken/​kvcache/​swa_radix_cache.py Clones stored SWA radix keys.
python/​freetoken/​kvcache/​radix_cache.py Clones stored radix keys.
python/​freetoken/​kvcache/​hybrid_radix_cache.py Clones stored hybrid radix keys.
python/​freetoken/​engine/​engine.py Adds architecture checks and loopback TCPStore.
python/​freetoken/​engine/​config.py Adds interleave and rendezvous configuration.
python/​freetoken/​core.py Stores per-request stop decode state.
python/​freetoken/​attention/​__init__.py Declares FA architecture requirements.
docs/​cli.md Documents --dist-port.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +643 to +646
return torch.distributed.TCPStore(
"127.0.0.1", config.distributed_port, config.tp_info.size,
is_master=True, timeout=timeout, multi_tenant=True, master_listen_fd=sock.detach(),
)
Comment on lines +170 to +173
# A view would keep the request's full output-budget token buffer alive.
new_node.set_key_value(
input_ids[prefix_len:].clone(), indices[prefix_len:].clone()
)
Comment on lines +173 to +174
if not 0 <= n <= 65535:
raise argparse.ArgumentTypeError("must be between 0 and 65535")
reasoning_delta, content_delta = reasoning_parser.parse_stream_chunk(content_delta)
if reasoning_delta:
stripped_reasoning = strip_special_tokens(reasoning_delta, specials)
stripped_reasoning = _filter_reasoning(reasoning_delta)
Comment on lines +15 to +16
four long cold prefills: 680/797/506/250 chunks, 267/317/190/93 s
prefill-burst share of a 15-minute window: 70%
Comment on lines +448 to +451
parser.add_argument(
"--decode-interleave-every",
type=int,
default=ServerArgs.decode_interleave_every,
@gdevenyi

gdevenyi commented Oct 7, 2026

Copy link
Copy Markdown
Owner Author

Superseded in part: upstream merged FlashML-org#559 as 555efd8 (same patch-id as the PR head merged here). The rebuild on upstream 377a3bc (#81) drops that merge; the rest of this PR's merges are carried forward unchanged.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Bu6LgoxLR4wETqb7RPR2vt

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants