(withdrawn — opened against wrong repo) - #650
Closed
tylerwagler wants to merge 420 commits into
Closed
Conversation
a569b5e froze the MoE tuning matrix to the release configuration; this propagates those constants through the dispatch. write_gate_up==0 drops the gate/up aux stores (and their kernel params/output pointers); expert_tile_m==8, gate_row_span==1024 and down_row_span==2048 collapse the tile/rowspan ladders; use_gate_row2048/use_atomic_down/ use_down_tile16/use_down_row2048 were all the same predicate and fold into one use_big_batch. Nine unreachable kernels deleted (sorted/p2 gate+down, tile4 gate+down, gate row2048, down tile16 row32, down rowspan template) plus the two orphaned block4 dot helpers; -970 lines. Validated: sm_120f build clean, full model test exit 0 (all gates OK), bench unchanged (prefill 449.6/419.0/413.4/408.2, decode within noise). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
… hot paths Kills (semantic variants per AGENTS.md one-release-path rule): - DS4_MTP_CONTINUOUS: opt-in non-bit-exact continuous depth-1 speculation (~105 lines), plus cont_anchor_row/valid session state and the cont-argmax-gap test that existed only to validate it. - DS4_MTP_MIN_MARGIN: env override of the configured e->mtp_margin. - DS4_MTP_BATCH_VERIFY: forced the near-greedy batch verifier under --quality/strict, violating the strictness contract. - DS4_MTP_CAPTURE_PREFIX1: enabled the older no-replay partial path under strict; capture_prefix1 is now simply draft_n==2 && !strict. - DS4_ROUTED_TOKEN_PARALLEL: force-on half of a dual-polarity pair; DS4_NO_ROUTED_TOKEN_PARALLEL stays as the single escape. Hot paths no longer call getenv (surviving diagnostics read once): - session.c: MTP_SPEC_LOG/PROBE/FULL_LOGITS/STRICT/TIMING/CONF_LOG/ EXACT_REPLAY/FORCE_SNAPSHOT, DSPARK_STATS/DTREE_STATS/MIN_CONF -> gpu_graph_env_flag cached statics (PROBE/FULL_LOGITS were read every token, SPEC_LOG at six sites per spec cycle). - moe.c: DECODE/PREFILL_PROFILE_DETAIL (per layer) cached. - gpu_prefill.c/gpu_decode.c: LAYER/DECODE/INDEXER/Q stage profile gates cached; GPU_BATCH_EMBED_MIN parsed once. - ds4_cli.c: DS4_MTP_SPEC_DISABLE read once per generation, not per token. Kept as-is: load-time knobs (DIRECT_MODEL, PRELOAD_SPAN_MB, PREFILL_CHUNK, PREFILL_SLICE already cached), per-request server reads (generate.c), operational escapes (DSPARK_LEGACY_LOOP, MTP_SPEC_DISABLE, DSPARK_DISABLE) and dump/replay tooling. Validated: full model test exit 0 (all gates OK), bench unchanged (449.6/419.0/413.4/408.2 prefill; decode within noise, two runs). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…_MAX test plumbing Both surfaced by post-commit review: the capture-prefix1 comment still documented the deleted env escape, and test_local_golden_vectors still saved/unset DS4_CUDA_MOE_TILE_MAX, gone since a569b5e. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
User decision 2026-07-12: the DSpark drafter is the production spec-decode
path and MTP will never be used; remove the whole subsystem (-1,744 lines).
Gone: --mtp/--mtp-draft/--mtp-margin across cli/agent/server/eval + help,
the mtp download_model.sh target, ds4_engine_has_mtp/_mtp_draft_tokens and
ds4_session_eval_speculative_argmax from the public API, the MTP engine
state (support-model load/map/preload/close, ds4_mtp_weights binder), the
draft functions (gpu_graph_eval_mtp_draft{,_from_hc},
gpu_graph_encode_output_head_mtp), the strict decode2 verifier
(gpu_graph_verify_decode2_exact), the MTP graph tensors + private raw
cache + mtp_n_raw, the prefix1 partial-accept capture machinery
(MTP-only: both DSpark verify calls pass capture=false), the per-session
mtp_logits/draft state, all DS4_MTP_* env gates, the mtp-verify-depth
test + helpers + DS4_TEST_MTP, and two orphaned leaves the compiler pass
surfaced (logits_top2, ds4_gpu_repeat_hc_tensor + kernel).
Kept (shared spec-decode infrastructure, used by DSpark):
spec_frontier_{snapshot,restore,free}, gpu_graph_verify_suffix_tops
(capture_prefix1 param dropped; timing gate renamed
DS4_SPEC_VERIFY_TIMING), gpu_graph_read_spec_logits_row, spec_logits,
spec attn/index frontier scratch (now gated by enable_spec ==
dspark_ready), gpu_graph_encode_output_head_batch, argmax kernel.
Validated: sm_120f build clean (no unused-function leaves), full model
test exit 0 all gates OK, bench identical to pre-strip
(prefill 449.0/418.1/411.9/407.2, decode 15.62/15.52/15.48/15.33),
grep residue zero.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…ownloader Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
… detect gguf-tools/merge_dspark_gguf.py splices the drafter's dspark.* tensors + the deepseek_v4_dspark.embedding_length / dspark.target_layer_ids.* kv into the main model (main blob byte-identical, drafter tensors appended 32-byte aligned). Engine auto-enables DSpark when dspark.main_proj.weight is present: dspark_model aliases the main model by value, teardown and the external map/cache plumbing are gated on a new dspark_external flag. --dspark PATH still loads a split file; new --no-dspark opts out and the tensor-span cache now skips a disabled drafter's dspark.* spans (skip_prefix param on accelerator_cache_model_tensors). The CLI greedy path now consumes ds4_session_eval_speculative_block (same toks[] chunk plumbing the server uses), so ds4 gets the DSpark speedup at temperature 0. ds4-bench (plain-decode baseline by design), ds4-eval (probe headroom) and ds4-agent (per-token DSML/forcing loop cannot consume speculative chunks yet) set dspark_disable. Artifact: gguf/ds4flash-reap25compact-mxfp8head-dspark-v1.gguf (86.7 GB, 1328+78 tensors, 67+4 kv). Validated: full model test exit 0 on the merged file; server A/B merged-auto vs split --dspark byte-identical output at equal t/s (20.6 warm, essay prompt); CLI greedy smoke 31.7 t/s; plain bench baseline-identical with 70.45 GiB cached (drafter spans skipped). model.gguf symlink now points at the merged artifact. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
A stale deepseek4-quantize binary (built 07-02, before the 4fbeb3e MXFP4 sign fix) kept producing sign-stripped donors while make test rebuilt and passed test_quants against the fixed sources. Tie the binary into the test target so a green test run implies a current binary. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…allocator New all-Q2_K expert combo for the mid quant rung (between IQ2_XXS/Q2_K cheap and lossless MXFP4 rich): binder accepts gate/up/down all-q2_k, and routed_moe_launch dispatches three Q2_K gate/up kernel clones (plain qwarp32 for decode, tile8_row32 and rowspan<1024> for sorted prefill tiles) built on the same production-validated dev_dot_q2_K_q8_K_block/ block8 helpers every down kernel uses. The IQ2 decode-LUT kernel stays IQ2-only. Validated: temp/q2kgate_test.cu oracle (synthetic experts, independent f64 host reference) passes all three dispatch paths at n_tokens=1/32/160 with max |diff| <= 2.6e-5; full model test on production exit 0 (no regression — production has no q2k-gate layers). prisma_alloc.py: adds the mid preset and generalizes the exact 0/1 DP to a grouped per-layer choice (cheap|mid|rich) fed by a second --kl-mid measurement file; mid benefit is capped at the rich benefit (subset improvement). Binary solves reproduce exactly when --kl-mid is absent. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…ntizer The deepseek4-quantize --output flag already re-encodes output.weight; document it in the pipeline recipe so the temp/splice_mxfp8_head.py post-step is no longer needed for new builds. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…A header quality-score still linked the pre-restructure ds4.o/ds4_cuda.o monolith objects; point it at the split engine/cuda TUs (built via ds4-bench) and add -lcublasLt. collect_official.py sends a User-Agent so Cloudflare- fronted OpenAI-compatible gateways (error 1010) accept it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
Three nondeterminism sources removed so speculative verify and plain decode see stable numerics: - indexer compaction: the comp_rows atomicAdd slot race made the float accumulation order of indexed attention depend on warp scheduling. Replaced with an order-preserving Hillis-Steele scan in smem; costs nothing vs the racy version (decode 15.66/15.30/15.29/15.15 t/s at 2k..8k, within noise of the pre-change baseline on the same box). - MoE big-batch down: per-token atomicAdd accumulation replaced with per-pair stores + the fixed-order moe_sum pass (zero_kernel retired). - cuBLASLt fp8 GEMMs: split-K reduction schemes masked off in both cached-handle sites; NONE-scheme algos accumulate in a fixed order. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
The drafter previously disabled itself whenever temperature > 0, so the sampling recipes everyone actually uses got zero speedup. Rebuilt the speculative path around exact sampled acceptance: - accept draft token d with probability p_filtered(d); on rejection, sample from the residual distribution (p excluding d, renormalized). Greedy is the point-mass special case of the same walk. - new ds4_sample_dist API in the tokenizer mirrors the plain sampler's filtering exactly (temp scale, top-k, min-p floor, top-p nucleus incl. the crossing candidate), so spec and plain draw from identical distributions. - carry-token architecture: sampling is owned inside the speculative block; the bonus token from a fully-accepted draft is carried into the next call and dropped if sampling params change mid-stream. - snapshot-restore hygiene: load_payload now resets the spec carry, the pending drafts, and the drafter context ring -- stale draft state across restores was shaping batch sizes and leaking rounding differences into the output (the run-to-run flakes). - server + cli generation loops rewired onto the new ds4_session_generate_speculative at every temperature. Validated by a statistical oracle (2500 trajectories x 2 paths, chi2 clean at p~.001 per position), 24/24 greedy prefix agreement with plain decode, and a byte-stable double run of the full test suite. Sampled spec decode at temp 0.95/top-p 0.38: 16.34 t/s vs 14.75 plain (+11%). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
Code review of 27376c8 found the carry surviving session-state advances it wasn't conditioned on, letting a token drawn from a PREVIOUS request's distribution be committed unverified as the first token of the next one: - cross-request: a generation truncated by max_tokens or a stop string leaves the carry armed; the next request hits the prefix cache, sync extends the checkpoint, and the stale carry is emitted. - mid-decode: the think-close recovery injects a token via plain eval; the pre-injection carry then fires right after the forced token. Fixes, layered: the carry is stamped with the checkpoint length it was drawn at and refused if the position moved; sync drops it uncondition- ally (a same-length full rebuild defeats the stamp); plain eval drops it; load_payload drops carry + pendings at entry so a mid-restore failure can't leave them armed. Regression test temp/carry_stale_test.c reproduces both scenarios at greedy (PASS with fix); full test suite + spec sampling oracle re-pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
A generation that hits the token cap (or a stop) inside its think block never emits </think>. The non-stream parse path returned the entire raw text -- reasoning included -- as visible content in that case, handing raw chain-of-thought to any client that displays or scores the answer channel. The streaming path already promised the opposite. This was found the expensive way: it silently gutted the tool-eval-bench accuracy legs (41/300 MMLU answers unextractable, IFEval strict rate ~10% vs a class norm of ~80) because long-thinking responses truncated mid-think and were scored on their reasoning dump. Both parse sites are fixed -- the thinking-mode branch (which also covers the prompt-pre- opened <think> form) and the generic splitter -- and the regression test pins content=="" + full text on reasoning_content for both forms. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
The v5mx prisma allocation promotes gate/up and down independently per
layer, so the artifact mixes iq2_xxs and mxfp4 across roles (mxfp4 gate/up
with iq2_xxs down and vice versa) alongside the existing uniform combos.
Loading it required two things:
- weights.c combo validation goes per-role: gate/up must be a matching
pair (the fused gate+up kernels assume one format) in
{iq2_xxs, q2_k, mxfp4, cutlass_mxfp4}; down independently in
{iq2_xxs, q2_k, mxfp4}; combos may differ per layer. CUTLASS stays
all-three (the grouped GEMM runs the whole expert FFN in one dispatch).
- ds4_cuda_moe.cu grows the missing IQ2_XXS down family, cloned from the
Q2_K down kernels on the production-validated iq2 dot helpers:
qwarp32 (generic), sum6 decode (smem-LUT dot, direct final-row store),
tile8_row32, and tile16_row2048 big-batch with per-pair stores + the
fixed-order moe_sum pass (no atomicAdd -- numerics stay run-to-run
deterministic). routed_moe_launch now selects gate/up and down kernels
independently from the per-layer tensor types passed in by the engine
(no new hot-path env/flag branching); sorted expert tiles are built
whenever at least one stage consumes them, and mxfp4 stages keep their
per-pair qwarp32 kernels.
Validated: temp/iq2down_test.cu oracle on REAL iq2_xxs down experts
extracted from the v5mx artifact (synthetic q2_k gate/up => also covers
mixed-combo dispatch), independent f64 host reference with a bit-exact
host replica of the GPU q8_K mid requant; all four down dispatch paths
(n_tokens=1/e6, 1/e4, 32, 160) pass with max |out diff| <= 1.4e-5
(bound 2e-3). ds4_test --server passes; the v5mx artifact (previously
rejected at load) now loads and generates coherently at 17.8 t/s decode
with the DSpark drafter.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
- Model Weights: per-layer, per-role expert combos (gate/up matching pair and down independently in IQ2_XXS/Q2_K/MXFP4; CUTLASS layout all-or-nothing), prisma measured-allocation RC + all-Q2_K fallback, byte-lossless MXFP4 re-encode, REAP expert pruning, merged DSpark drafter, deepseek4-quantize --format-map pointer. - New "Speculative decoding (DSpark)" section: MTP removed, merged-GGUF auto-enable / --no-dspark / --dspark, exact sampled acceptance at all temperatures (output distribution identical to plain sampling), and run-to-run deterministic decode/prefill numerics. - Model residency: current ~87 GB REAP-pruned build figures; note the default DS4_ATTN_PACK packed comp cache and DS4_IDX_FP4 MXFP4 indexer cache. - Server: release scope (one live session, contexts up to 1M tokens), 64-connection cap + 30 s request-read deadline, unterminated <think> always returned as reasoning, never visible content. - More Documentation: link gguf-tools/prisma/README.md and docs/MODEL_CARD.md; drop the stale 26GB/22GB 1M-context KV figures that predate the packed caches. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
Near-term (v1.x): temperature-matched p/q draft sampling with fused GPU verify, fused K-row accept kernel, online drafter confidence calibration, agent-loop speculative decoding, and the final measured-allocation release artifact plus scoreboard. Longer-term: multi-session serving, MXFP6 tensor-core paths, MoE verify-microbatch routing audit, CPU-path retirement, and upstreaming prisma pipeline fixes to PrismaQuant. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…rter/ Absorb the last converter/ capability into deepseek4-quantize: routed-expert tensors can now target cutlass_mxfp4 (GGUF type 40, the tensor-core CUTLASS B layout) via --experts/--routed-w*/--format-map, packed in one pass straight from the QAT E2M1+E8M0 safetensors source — byte-lossless (data verbatim, E8M0 scales placed by the Blackwell 128x4 SF tile swizzle, same formula as the engine's mx_sfoff and CUTLASS Sm1xxBlkScaledConfig SFB). - quants.h/quants_common.c: repurpose the unused NVFP4=40 slot as DS4Q_TYPE_CUTLASS_MXFP4 (matches the engine's DS4_TENSOR_CUTLASS_MXFP4); not a fixed-stride row format, sized via ds4q_cutlass_mxfp4_bytes() - quants_fp.c: ds4q_pack_cutlass_mxfp4 + SF sizing helpers - dsq_generate.c: expert-path lossless pack branch, per-expert/tensor sizing, regular-tensor rejection; dsq_names.c: allow type 40 as a planned target (also lets a type-40 template like gguf/dspark.gguf load for --compare) - test_quants.c: sizing ground truth (real dspark expert blob), data-verbatim and SF-swizzle injectivity/round-trip invariants - Makefile test-cutlass-mxfp4: byte-compares quantizer output against the packer-produced gguf/dspark.gguf on gate+down (both N/K orientations); verified byte_compare: OK (identical fnv1a64) for both Retire converter/ (all three superseded): - splice_cutlass_mxfp4.py: replaced by the native type-40 path above; prisma_alloc.py rich preset now emits CUTLASS_MXFP4 manifests directly (same 4.25 bpw, DP budget math unchanged), no post-step splice - dspark_convert.py + p0conv.py: drafter pipeline is now build_dspark_template.py -> deepseek4-quantize --experts cutlass_mxfp4 -> merge_dspark_gguf.py (type-40 experts byte-identical; FP8/F32 tensors value-lossless per test_quants idempotency, same precedent as the MXFP8 head absorption in b2bf7ec) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
Replace the upstream antirez/deepseek-v4-gguf targets (old imatrix filenames, PRO variants we do not ship) with the ds4 release repo and the measured-allocation v5mx build. Single self-contained artifact (merged drafter), Xet-aware hf download with curl fallback, token handling unchanged. Repo is private pre-release; downloads need a token until public. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
Single ptr-array grouped launch to replace the per-expert CUTLASS dispatch loop + blocking offset readback in prefill; available in the SM120 blockscaled builder at the current CUTLASS pin. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
The measured-allocation build won the quality bake-off (92/100 vs 84/100 all-Q2_K) and shipped as ds4flash-v5mx-reap25-mxfp8head-dspark-v1.gguf (91 GB). Update the README to match: - Model Weights: state the measured build as the shipped release, not a candidate under evaluation; drop the all-Q2_K fallback framing; add the recommended sampling (temp 0.95 / top-p 0.38). - download_model.sh description: rewrite for the single `v5mx` target and our now-public Elytron Defense HF repo (filename + size + sha256); drop the stale upstream q2/q4/pro-imatrix commands and antirez source. - Speed: replace the TODO placeholder with the measured GB10 table (prefill ~420→390 t/s, decode 16.5 t/s flat, DSpark accept α=77.2%). - Correct the ~87 GB residency figures to ~91 GB. - Soften the intro PRO claim to an opportunistic target; mark the measured-allocation release roadmap item as shipped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
Attribution rides on repo ownership + links, not free-text org claims. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
Drop the upstream multi-size-class / PRO-on-big-machines opportunism (512 GB, other size classes) that doesn't match this fork's deliberate one-model, one-box scope. Remaining PRO references are factual (model family, API aliases, tool-format spec location), not scope claims. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
The --power throttle will be stripped from the binaries post-release; drop its documentation now so the README doesn't advertise a going-away knob. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
The native agent is out of scope for this release and untested after the CUDA-only cleanup edits. Remove it from the default build targets and the test suite, and strip its README section + status note. Source and the standalone `make ds4-agent` rule are kept; it stays a longer-term roadmap item, just not a v1 deliverable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
- Single build target (cuda-spark / sm_120f); drop the generic-CUDA and explicit-arch targets and their docs. This is a GB10-only fork. - Drop ds4-eval from the default build, test suite, and docs (out of scope for this release, like ds4-agent). Both keep their source and a standalone `make <name>` rule; neither is a v1 deliverable. - Remove the Motivations section and the intro's 'integrated coding agent' framing — the native agent is deferred, so the README no longer advertises it as a shipped goal. Shipped binaries: ds4, ds4-server, ds4-bench. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
The HTTP API server is the product. The ds4 CLI joins ds4-bench, ds4-eval, and ds4-agent as a development tool: source kept, buildable by name, not in the default build. `make cuda-spark` now builds just ds4-server. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…ver) The release surface is now just ds4-server (the OpenAI/Anthropic-compatible HTTP API). ds4, ds4-bench, ds4-eval, and ds4-agent are development tools: source kept, buildable by name, not shipped and not built by `make cuda-spark`. README: - Intro reframed around ds4-server as the product; offline/dev tools noted. - Build/run examples center on `./ds4-server -m ds4flash.gguf --ctx ...`; "default model path used by both binaries" -> ds4-server; dropped `./ds4 --help`. - Removed the standalone CLI section (`./ds4 -p`, interactive `ds4>`). - Removed the Benchmarking (`./ds4-bench`) section; kept the prefill-chunk guidance as its own section pointing at the server `--prefill-chunk` flag. Speed numbers untouched. - Added a "Development tools" subsection under Building. - Steering: added the ds4-server `--dir-steering-*` server form. - Recommended-sampling note and spec-decode text de-CLI'd (API temperature/ top_p, temperature 0). - Test Vectors / Debugging Notes: kept `--dump-*` probes but labelled them the ds4 dev CLI; led Debugging with `ds4-server --trace`. download_model.sh: - Post-download help now points at `./ds4-server -m ds4flash.gguf --ctx 100000` instead of `./ds4 -p "Hello"`. v5mx target + ds4flash.gguf symlink unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
… (transliteration; numerics verbatim) — engine host phase fully C++ Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
…x gguf_types array designators The harness #includes every server TU, so the server converts atomically: renames, explicit casts at void* boundaries, four goto-crossing declarations split into decl+assign (semantics identical — the variable is simply uninitialized at the label, as in C), remaining 'namespace' locals/params renamed tool_namespace, two test designated-initializer order fixes. Also: gguf_types converts from C array designators (not valid C++) to positional rows with a static_assert pinning the id mapping — the designator form had never actually compiled as C++; a stale C-era object was masking it (caught by a forced clean host rebuild, which is now the standard check). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
…ent TUs, ds4_help, ds4_agent_test harness -> C++
Vendored src/vendor/*.c (linenoise, rax) intentionally stay C. Mechanical
fixes: explicit casts at void* boundaries, {0} -> {} where the first member
is an enum, hardmode-eval .choice[i] designators -> positional lists,
ds4_bench engine-options designators reordered to declaration order,
agent-internal header gains extern-C guards.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
…l tree one language) Keyword-safe pass: 31 identifier uses of 'new' renamed to 'newn' (comments/ strings untouched), the rax*rax type/variable shadow goes through 'struct rax', one goto-crossed decl split, casts at rax_malloc/realloc boundaries, one string-literal char* constified. linenoiseEditInsert — deliberately absent from linenoise.h — gets explicit extern "C" so the agent editor keeps linking. Vendor Makefile rule moves to CXX. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
…C++ linkage
Removes the extern "C" blocks from all nine headers (ds4.h, ds4_gpu.h,
engine/server/agent internals, help, kvstore, rax, linenoise) and strips the
explicit extern "C" from all ~150 .cu API definitions (nvcc and g++ share
the Itanium ABI, so host<->device TU linkage needs no C bridge). The twelve
model-gate harnesses under tests/ move to C++ with the same mechanical fixes
(casts, two goto splits, bank_fork's &(uint64_t){7} compound literals become
one shared greedy seed — greedy never consumes RNG state). The
prefill-gate baseline flow ships the .cpp gate source into its baseline
worktree. linenoiseEditInsert loses its C-linkage pin.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
…es (Dot8 traits) The remaining family variants (iq2 sum6/tile8/tile16, gate_up_mid iq2 tiles) are NOT clones — they deliberately use the smem-LUT decoder and, in tile16, a restructured midq staging order — so they stay separate kernels. This completes the collapsible set flagged in the kernel-phase review (~180 lines of hand-cloned duplication removed). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
…riptor slots The slot/session/bank overload is gone: struct server owns THE session (classic mode == 1 bank-less slot over it, pool mode == pool_banks banks); session_slot loses its sess pointer (a bool provisioned carries the provisioned/evicted idiom) and the classic-mode multi-independent-session provisioning + its eviction/free paths are deleted (~250 net lines). A classic-mode request needing a second slot now queues for slot 0 — the pre-pool single-session behavior. Pool-mode bank switching, eviction, spill/restore and warm-fork routing are unchanged. Refactor executed by a subagent to spec; reviewed. Approved by Tyler 2026-07-23 as the port-window structural cleanup (plan-70 adjacent). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
…inaries, strings, docs
7,167 ds4_->pulsar_ + 3,761 DS4_->PULSAR_ identifier renames (string-literal-
aware lexer; comments included), 25 file renames, binaries pulsar{,-server,
-bench,-eval,-agent,_test,_agent_test} with transitional make aliases +
symlinks. Brand strings (log prefixes, banners, usage text, dump-JSON
source, /version engine) rebranded; docs rebranded.
Compatibility surfaces deliberately unchanged: all 117 getenv("DS4_*")
names, /tmp/ds4.lock (old+new binaries must contend on ONE lock or the GB10
OOMs), ds4flash.gguf refs, dspark.* GGUF keys, KVC/DS4KLRF1/DS4PFXG1 disk
magics, kv-cache directory paths, +DWARFSTAR_* protocol markers, and
comments where DS4 names the DeepSeek-V4 architecture.
Rename executed by a subagent to spec; reviewed. Note: cuda-prefill-gate-
baseline can no longer build against pre-rename refs (harness includes
pulsar.h) — rebaseline on next use.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
… (23 methods) The struct+verb families become real classes in the engine-internal header (static factories create/open, destroy() for the teardown verbs, const where the free decls were const). Method bodies are the original bodies behind an 'auto *s = this;' alias — zero transcription of numerics code. The public boundary (pulsar.h) deliberately keeps the opaque-handle free-function API: 76 one-line facades in engine_api.cpp, each preserving the original null-object path OUTSIDE the member call (a member call through NULL is UB that -O3 null-check elision would exploit). 28 engine-internal call sites converted to method calls. Three verbs stay free functions where a method would collide with a same-named public field (noted in the header). Refactor executed by a subagent to spec; reviewed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
… + make seam-check pulsar_gpu.h now states the seam contract (engine talks only pulsar_gpu_*; legacy DS4_CUDA_* env NAMES grandfathered as ops compat; backend-branded logs come only from backend TUs). tools/seam_check.py enforces it: strips comments/strings, then flags any CUDA include/API identifier/launch syntax outside src/cuda — wired into 'make test'. 73 host files verified clean, so Metal/ROCm can return as src/<backend>/ implementing this header with zero engine changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
…multiseq TUs session.cpp 4722 -> 1510 lines; session_spec.cpp (1536: speculative loop + yield-quench family), session_payload.cpp (969: payload/snapshot codec), session_banks.cpp (557: 18 bank_* methods + carries), session_multiseq.cpp (162). Move-only (bodies verbatim); the only two cross-TU statics (payload_set_err, spec_quench_reset) are promoted to the internal header. No file-scope data statics moved, so initialization order cannot matter. Refactor executed by a subagent to spec; reviewed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
generate.cpp 4026 -> 202 (keeps the cross-TU request-text helpers); server_sched.cpp (1682: worker/quantum lanes, provisioning, eviction, spill guard, bank switching), server_jobs.cpp (2055: gen_* state machine, prefill progress, tool-checkpoint canonicalization, DSML recovery); cli_main.cpp 6261 -> 1302 with the ~130 embedded unit tests extracted to server_tests.cpp (PULSAR_SERVER_TEST-guarded; empty object in the server link, verified 0 symbols). Move-only, proven by sorted-line diff; struct gen_state promoted to the internal header (the scheduler lanes drive job phases directly — field-level encapsulation would be a rewrite, not a move). Refactor executed by a subagent to spec; reviewed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
…n, gate fidelity tests on pruned models test_thinking_checkpoint_remember_gate still asserted the pre-openwebui-fix rule (has_tools disqualifies); the implementation deliberately made prompt_preserves_reasoning the sole gate — the test now pins the current contract. The official-logprob-vector suite and think-tool-recovery's content assertions are recorded against the FULL reference model; the production REAP25 model legitimately diverges, so a new pulsar_engine_is_pruned() accessor gates them: vectors skip loudly, the recovery test keeps its machinery assertions (decode/end-marker/parse) and skips only the exact-tool-choice/phrasing checks. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
… guarded default-ON Implements the condition the opt-in revert set for re-flipping: fusing is refused while >=2 active decoders' summed committed depth exceeds DS4_MIXED_DEEP_GUARD_ROWS (default 16384 — the measured-neutral boundary; 4x8192=32768 was the -29% pp / -48% tg regime). Regimes (a) shallow-neutral and (b) one-big-prefill-over-shallow-decoders keep the fused win; regime (c) now takes the classic path automatically with an edge-logged pause notice. Default flips to ON in pool mode (DS4_MIXED_BATCH=0 opts out); pending teb --perf-only A/B verification before this lands on pulsar. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G8LCjbJmFeq7LTUzQikQcT
…embers The server_*/worker_* free-function family (103 fns: 95 mutating + 5 const readers + the 3 wrapped-signature trace_* helpers) becomes struct server member methods, mirroring the pulsar_session/pulsar_engine class-ify (2ba96c3). Bodies are byte-verbatim behind an 'auto *s = this;' alias; the type-name prefix is dropped from method names (server_bank_switch -> s->bank_switch), worker_* names kept. Const readers become const methods. served_model_id / served_model_name stay FREE functions (each collides with a same-named struct server data field, the documented precedent exception); worker_main stays free (pthread void* entry). Header: 103 member decls added; family forward-decls removed; three types referenced by pointer-params but defined after struct server are forward- declared before it (struct job / server_prefill_progress / thinking_state / trace_cache_diag, the last three tagged on their anonymous typedefs), and the provision_refusal enum is relocated ahead of the struct. Byte-preserving; no numerics or behavior change. Gate: make pulsar-server (clean compile+link, zero warnings) + make seam-check (80 host files CUDA-clean). GPU numerics gates N/A (no numerics touched). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
The agent_bash_* verb family (bash_jobs.cpp: 8 static helpers + the public agent_bash_observation) becomes struct agent_bash_job member methods, same pattern as struct server / pulsar_session. Bodies byte-verbatim behind an 'auto *job = this;' alias; agent_bash_ prefix dropped from method names (agent_bash_poll -> job->poll()); the three const readers (display_lines, read_head, read_tail_lines) become const methods. No field-name collisions. Header: 9 member decls added to struct agent_bash_job; the lone free decl (agent_bash_observation) removed. No forward-decl needed (agent_worker is defined earlier). Only external call site (tool_dispatch.cpp) converted. Gate: make pulsar-agent + pulsar_agent_test (clean compile+link, zero warnings) + make seam-check. No numerics/behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…-> members The pulsar_vocab verb family in tokenizer.cpp (8 static bpe_/tokenize_/ special_/lookup helpers + vocab_load/vocab_free/tokenize_rendered_chat_vocab/ dump_tokens) becomes struct pulsar_vocab member methods, same pattern as the prior class-ify commits. Bodies byte-verbatim behind an 'auto *vocab = this;' alias; the eight read-only helpers become const methods. Names kept verbatim — none carry the pulsar_vocab type-name prefix, and a bare 'free' would shadow libc free() used inside the bodies. No field-name collisions. dump_tokens_fp stays a free function (takes vocab as its SECOND arg, not a natural receiver). pulsar_engine::dump_tokens now calls e->vocab.dump_tokens() directly instead of the ::-qualified global (stale comment updated). All internal cross-calls and the session.cpp external sites (&vocab / &e->vocab) converted. Header: 11 member decls added to struct pulsar_vocab; four free decls removed (dump_tokens_fp kept); owned_str (bpe_rank param, defined after the struct) is tagged on its anonymous typedef and forward-declared before the struct. Pure host tokenization — NO kernels/numerics touched; token IDs unchanged (bodies verbatim). Gate: make pulsar-server + pulsar + pulsar-agent (clean compile+link across the engine, zero warnings) + make seam-check. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
Adds pulsar::FileHandle (src/lib/pulsar_file.hpp), a move-only RAII owner for a C FILE* with an explicit close() that returns the fclose status and disarms the destructor. The destructor is a leak safety-net for early-return paths. Applies it to write_f32_binary_file / read_f32_binary_file (util.cpp), which both had a real leak: `ok = n_io == n && fclose(fp) == 0` short-circuits past fclose on a short read/write, leaking fp. Now fp.close() is short-circuited the same way but the FileHandle destructor closes it — no leak. The success-path fclose-error check is preserved exactly (close() returns the status). First RAII increment. The higher-value / higher-risk RAII (GPU-tensor teardown, mutex guards) changes resource LIFETIMES and needs the GPU runtime/teardown gates to verify — deferred to a server-down window. Gate: make pulsar-server + pulsar + pulsar-agent (clean, zero warnings) + make seam-check (81 host files CUDA-clean). Behaviour-preserving except the leak fix. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
… class-ify The struct-server class-ify (cf207fb) mis-converted the value-fixture call sites in server_tests.cpp — `method(&s, args)` became `&s->method(args)` (address of a call rvalue, `->` on a value) instead of `s.method(args)` — and left the free-function call in pulsar_test.cpp (chat_think_tool_recovery(&srv, ...)) unconverted. These slipped the original gate because server_tests.cpp is wrapped in #ifdef PULSAR_SERVER_TEST, which is defined ONLY by tests/pulsar_test.cpp; the pulsar-server build compiles that TU empty, so `make pulsar-server` never saw the errors. Building pulsar_test (the server-test runner) surfaced them. Fix: `&VAR->method(` -> `VAR.method(` across server_tests.cpp value fixtures (s/src/dst/srv), and srv.chat_think_tool_recovery(...) in pulsar_test.cpp. Test-only files; no shipped-code change. Gate LESSON: `make pulsar_test` (with PULSAR_SERVER_TEST) must be part of the class-ify gate, not just `make pulsar-server`. Compile-clean now; runtime run pending an sm_120f build (plain make targets sm_75, engine won't open on GB10). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…uard elision) The class-ify (cf207fb) turned several NULL-tolerant free functions into server:: methods. Those functions had an internal `if (!s) return` guard because callers legitimately pass a null server (parse-without-server and the streaming unit tests, which pass s=NULL). As members, `s == this`, and at -O3 the compiler assumes `this` is non-null and ELIDES the `!s` guard entirely — so `s->method()` on a null server dereferences null and SIGSEGVs. Caught by running pulsar_test on the GB10 (sm_120f); invisible to compile+seam and to the pulsar-server build (production always passes a real server, so the shipped binary never hit it — this was test-reachable UB). Two correct fixes, depending on where the guard can live: - CALL-SITE guard when the receiver is a normal pointer (not `this`): parse_chat/anthropic/responses_request wrap the tool-memory restore/attach pair in `if (s) { ... }` and gate the validate_* calls with `s &&`. A guard on a normal param is NOT elided. - REVERT TO FREE FUNCTION when the method itself is invoked with null `this`: anthropic_tool_stream_id / openai_tool_stream_id are called with s=NULL from the stream path, so an internal guard would always be dead. Restored to `static ... (server *s, ...)` (their original form) where `if (!s)` holds; removed their method decls from struct server. RULE: a NULL-tolerant function must not be a non-static member — the `!s` guard is unreachable under -O3. Keep it free, or guard outside the call. Gate: full pulsar_test suite GREEN at runtime on GB10 (sm_120f) — 15/15 including server/tool-call-quality/api-sampling; pulsar-server clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…y, tail_capture, syntax Completes the method-ification: the remaining small struct+verb families become member methods (same pattern as the big three). - thinking_state (server): tail_ends_with() const + feed() - pulsar_bank_carry (engine): copy() + free_one() - agent_tail_capture (agent): append() + take() - agent_syntax (agent): line_comment() const + match_keyword() const Each has a NULL-tolerant verb (a bare `if (!obj)` guard) — the exact -O3 elision hazard from 260ffbc. VERIFIED SAFE before converting: every receiver is provably non-null at every call site (agent_syntax_for_lang always returns &agent_syntaxes[0]; chat_think_tool_recovery derefs thinking->inside with no null check; bank carry is &s->bank_carry[i]; append is called under `if (r->capture)`). The dead `!obj` guards are kept verbatim in the bodies but never fire. Test-file call sites (server_tests.cpp, pulsar_test.cpp thinking_*) converted too (the 08885a1 lesson). Gate: all 5 binaries + seam-check clean; FULL RUNTIME suites GREEN on GB10 sm_120f — pulsar_test 15/15 (server: OK) AND pulsar_agent_test ok. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…fe single-bracket sites Adds pulsar::ScopedLock (src/lib/pulsar_lock.hpp): locks a pthread_mutex_t on construction, unlocks on scope exit, so an early return cannot leak the lock (missed-unlock deadlock). Replaces the hand-paired lock/unlock idiom, which is correct today but one forgotten unlock away from a hang. Adopted ONLY where a lock brackets a clean lexical scope — verified per function: - kv_cache.cpp kv_tool_map_write: the most fragile site (one lock, THREE unlock paths incl. two early returns) -> one guard, unlocks vanish. - tool_memory.cpp: 9 single-lock live-tool-state / has-id / snapshot functions. Deliberately NOT converted (a scope-end guard would be wrong): functions that unlock EARLY to do expensive work outside the lock (e.g. kv_cache.cpp:588's xstrdup-then-process), multi-bracket unlock/relock functions, and the cond_wait worker/drain loops (http_server, cli_main, server_sched) — those need their own careful pass. Behaviour-preserving: same mutex, same acquire point, release at the same scope end the manual unlock already sat at. Gate: pulsar-server + pulsar_test compile clean, seam-check (82 files). Runtime verification next. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…r + drain) Extends ScopedLock (aee3cf7) to the two cond_wait sites that are clean single brackets: the worker_main wait-for-work loop (server_sched.cpp) and the shutdown client-drain (cli_main.cpp). Each is `lock; while(!cond) cond_wait; [read state]; unlock` with NO early return between — cond_wait is RAII-safe (it atomically releases+reacquires the held mutex), so a nested-scope guard is behaviour-identical. The worker case hoists `quit` out so it's read under the lock then acted on after the guard closes. These had no missed-unlock leak to fix (already clean) — this is consistency, not a bugfix. NOT converted: http_server.cpp:513 — it has the leak-prone early-exit pattern BUT destroys j.mu right after unlock, so a scope-end guard would destroy a still-held mutex; it needs a restructure, left as-is. Gate: compile clean + seam-check, and LIVE server verification — a real /v1/chat/completions request drove the worker cond_wait wakeup (32 tok @ 20 t/s) and clean SIGTERM shutdown drained with no deadlock (exit in 2s). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…gle teardown
client_main held j.mu across enqueue + the wait for the worker's completion,
then unlocked and pthread_mutex_destroy'd it — with the teardown (destroy cv,
destroy mu, request_free) DUPLICATED on the enqueue-fail and enqueue-ok paths,
and a goto out of the locked section. A plain ScopedLock could not help because
the destroy must follow the unlock; and the duplicated cleanup is exactly the
kind of thing that rots when one copy is edited.
Restructure: hold j.mu in an inner { ScopedLock } scope spanning enqueue + the
cond_wait loop; the guard releases at scope close, THEN the single shared
teardown runs once. `enqueued` carries the branch out so the 503 error is
emitted after unlock. Behaviour-identical on both paths; the ~12 early goto-done
exits (before the mutex is even init'd) are untouched. Removes the duplicate
teardown and the lock leak-surface.
Gate: compile clean + seam-check; LIVE server battery on the new binary —
/health + /v1/models (early-goto paths) 200, a real completion drove the
restructured enqueue/wait/cleanup path (finish=length), a 2nd request 200, and
clean SIGTERM shutdown drained with no deadlock.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…ar-* Completes the DwarfStar/ds4 -> Pulsar rename. Removes the Makefile transitional alias targets (ds4/ds4-server/ds4-bench/ds4-eval/ds4-agent/ds4_test/ ds4_agent_test, each of which symlinked to its pulsar-* target) and the ds4 names from `clean`, and updates the ops/bench/profiling scripts to invoke ./pulsar-server and ./pulsar-bench (including the pgrep "already running" guards). The DS4_* runtime env vars are a separate, larger surface and are left untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
Completes the DwarfStar/ds4 -> Pulsar rename by renaming every DS4_* env var to PULSAR_* across the engine/server/cli/agent code (getenv reads + the diagnostic log/error strings that name them), the Makefile gate targets, tests, ops/bench/ profiling scripts, and docs. 169 getenv reads, ~120 distinct knobs. NO backward- compat shim: the old DS4_* names are gone; set PULSAR_* instead. Excluded (deliberately, not env vars): - DS4_IMATRIX_PROMPT — a data-format marker in the imatrix dataset .txt files; renaming it would break existing datasets. - gguf-tools/ DS4_* — C macros (gguf metadata keys, tensor-type names), not env vars. - upstream-ds4/ — vendored upstream submodule. Also refreshed the pulsar_gpu.h backend-seam note (the env names are no longer "legacy/grandfathered" — PULSAR_CUDA_* is just the current convention). Gate: clean full rebuild (all 7 binaries) + seam-check; PULSAR_TEST_MODEL read proven end-to-end (bogus path honored); full pulsar_test suite GREEN on GB10 sm_120f (15/15 incl. tensor-equivalence bit-exact + server). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QXh7ricvRaq4pMg4RKPDf5
…lsar Claude Code sends all traffic (main + haiku-class utility calls) to one ANTHROPIC_BASE_URL; per-model endpoints were declined upstream (#25146, #24160). pulsar-model-router.py splits by the body's model field — haiku-pattern to the small/fast box, everything else to pulsar-server — as a raw byte relay so replay byte-stability (prefix cache) is preserved. claude-pulsar launcher requests sonnet[1m] so Claude Code budgets a 1M context window; pulsar ignores the client model string and serves the loaded model. Keeps throwaway small-model calls from churning pulsar's session bank pool (LRU eviction of the big cached session = full re-prefill). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VE5ruaUJTbG7rnX26vWjL3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Withdrawn — opened against the wrong repository by mistake. Apologies for the noise.