Commit Graph
144 Commits
Author SHA1 Message Date
Tarjei Mandt 3d3c83bbbd Merge remote-tracking branch 'origin/main' into ds41-perf
# Conflicts:
#	ds4.c
#	ds4_engram.c
2026-09-21 08:48:08 +10:00
Salvatore Sanfilippo 5994f062de Validate image positions before token-prefix slot reuse
Keep fingerprint-only matching for authenticated visible histories that rebase spans, but require exact historical image positions for token and rewind reuse. Reproduce the old false hit with a synthetic checkpoint. Fix the live eviction oracle to include all competing slots. Four-slot Qwen and GLM vision/tool/eviction and concurrent-request tests pass. Follow-up to #765.
2026-09-18 23:27:51 +02:00
antirez 4067320f06 Merge remote-tracking branch 'pr-review/765' 2026-09-18 23:27:47 +02:00
Salvatore Sanfilippo 25334fef16 Test numeric reasoning strings at the API boundary
Cover 0 through 100, malformed values, overflow-sized input and unchanged named controls. Follow-up to #1074.
2026-09-18 23:27:31 +02:00
Alessandro Checco ecf7a93148 Enhance reasoning effort parsing logic
With this fix the server with respond to e.g.
"reasoning_effort": "50"

so it becomes compatible with pi.dev that doens't accept numerical value (and cannot send think_level by default)

(cherry picked from commit de418c0861)
2026-09-18 09:54:45 +02:00
Spencer Gilbert 63bbba4ba7 Advertise the loaded GLM model id in /v1/models
(cherry picked from commit 28c06c88e0)
2026-09-18 09:54:41 +02:00
Salvatore Sanfilippo 39bc628cc3 Cover split Qwen reasoning markers in both streaming APIs
Verify tool-enabled answers are emitted before generation finishes for OpenAI and Anthropic clients. Keep the existing DeepSeek repeated-reasoning regressions. Follow-up to #1077.
2026-09-18 09:52:20 +02:00
Jeff d31089d5ad Fix Qwen tool content streaming
(cherry picked from commit 77a39ebf0ea81ff2f7767af1bf9631890215482e)
2026-09-18 09:50:23 +02:00
ayuan1129 fff391e6c7 GLM: render the tool call block inline, as the chat template does
append_glm_tool_calls_text() wrapped the reconstructed block in a newline on
each side.  GLM's tokenizer.chat_template does not: it emits

  <|assistant|><think></think><tool_call>name<arg_key>k</arg_key>...</tool_call>

with no surrounding whitespace in any variant (empty content, non-empty
content, reasoning present, followed by an observation or by a user turn).
The block is only wrapped when it has to be rebuilt from parsed arguments;
when the raw sampled bytes are available they are emitted verbatim, so the two
paths disagreed.

That disagreement is invisible while a conversation stays in one process: the
live tool turn feeds the raw sampled bytes into the prompt, so the running KV
prefix still lines up.  It surfaces when a client replays the history, because
the replay goes through the reconstruction path and picks up the extra
newlines.  Any key or checkpoint derived from the visible transcript then fails
to match the replay: in the restart smoke test a conversation that should
re-prefill ~14 tokens re-prefilled 3800 of 11992, because the visible-key
checkpoint it wrote was three bytes off.

Cold prefill is affected too, and independently of any cache: a replayed GLM
tool conversation was being rendered in a shape the model never trained on.

Drop the two newlines.  test_glm_tool_calls_render_matches_template asserts the
template-true block and that the raw and reconstructed renders are byte
identical; test_render_glm_preserves_reasoning_with_tools had pinned the old
bytes and is corrected.

Reproduced with the restart-restore smoke test on GLM-5.3-Flash-Q2:
  before: FAIL, restored 8192 of 11992 tokens (3800 re-prefilled)
  after:  OK,   restored 11998 of 12012 tokens (14 re-prefilled)

(cherry picked from commit d186421d96)
2026-09-18 09:49:08 +02:00
Mikhail Korobov 0edbd9cd64 Merge upstream/main: vision live-state accounting in the reuse probe
Upstream 81a7658 ("Preserve live vision context across server tool calls")
rewrote the live-continuation helpers this branch replaced with
slot_probe_reuse(): image markers carry request-local nonces and are
normalized for visible replay, image tokens are spliced into continuations
instead of being tokenized as text, and image appends are validated by
fingerprint and row count rather than by exact span equality.  The merge
keeps the probe architecture and folds the new conditions into it, so slot
routing and the execution ladder still cannot disagree:

- The vision gate is fingerprint-prefix (new
  ds4_session_vision_fingerprint_prefix_matches): historical images must
  match by row count and fingerprint, positions may shift because
  build_live_prompt_suffix() rebases them, and appended images are allowed.
  An exact-state gate rejected valid appends, and a position-sensitive one
  rejected replays that omit hidden reasoning.
- Every live tier materializes through build_live_prompt_suffix(), so new
  images are spliced into the continuation and a failed rebase is a safe
  miss rather than a malformed prompt.
- responses-visible and thinking-visible compare in visible_prompt_key
  space, so a replay with fresh marker nonces still binds while a changed or
  shifted image does not.
- Tool-output-only continuations keep upstream's contract: if the selected
  live tier cannot be materialized, the request is answered with 409
  (live_continuation_unavailable) instead of being generated from a
  context-free prompt built from tool outputs alone.
- Single-session mode refreshes the slot's rendered-text cache like the
  batched worker does, so the memory-text tier is probe-authoritative on
  both worker paths; the redundant ladder fallback and the four superseded
  upstream helpers are removed.

Tests: test_slot_probe_vision_tiers covers image appends, pixel mismatches
and marker-nonce visible replay at the probe/routing level;
test_live_continuation_contract pins the 409 predicate;
tests/test_session_state.c gains fingerprint-prefix cases; and the new live
suite tests/test_server_vision_live_state.py checks slot routing, eviction
victims and the tool-result-only 409 against a real vision model.

Verified on DeepSeek V4 Flash Vision Experimental (M5 Max): ./ds4_test
--server, ds4_agent_test, test-session-state, live test_server_vision_cache.py
(1- and 4-session), test_server_story.py, test_server_batching.py and the new
vision live-state suite all pass.
2026-09-18 04:07:30 +05:00
Tarjei Mandt 7da9535f2d Sync idle server slots in the engine's prefill chunk 2026-09-17 12:13:32 +10:00
Salvatore Sanfilippo ab38bc6b7a Serve Qwen batched MTP with safe session state and output limits
Keep exact sampled requests on ordinary batches. Clear stale verification snapshots, honor logical context limits, and reset speculative timing estimates when batch width changes. Exercise forced rejection, mixed decode, snapshot restore, disk failures and arena replacement.
2026-09-16 15:17:25 +02:00
davidandClaude Fable 5.1 ba3b0c23e3 Batch Qwen3.8 Flash Next decode natively across sessions
Qwen3.8 sessions were excluded from native session batching: each decode
step ran the whole graph once per session, so sixteen sessions cost
sixteen times the weight traffic.  The batched encoder now runs the dense
work (hyper-connection mixers, projections, MoE, output head) once over
the stacked rows and keeps only the recurrent state, the caches and the
PLE history per session, with batched GDN scan and conv kernels reading
the pooled state through per-row slots.

Session transients (prefill scratch) move to one shared arena sized for
the largest session, so each further session costs its caches only
(memory per session divided by ~3.6 at ctx 4096).  The dense GEMM gains a
split-K path for the narrow-batch regime, and the dense/MoE/mixer GEMM
thresholds, tuned for T=1 or T>=8192, are moved to serve decode batches
of 4..64 rows.  Every step keeps the tokens of the sequential path;
float reduction order changes where the batched kernels do.

C=16 at ctx 4096 on an M5 Max goes from ~50 to ~143 tok/s aggregate.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SxdJvkAEqcDT2o7tqKMw6d
2026-09-16 14:06:23 +02:00
antirez 97ba327538 Read Qwen native BF16 n-grams directly from the GGUF
Keep the table outside model mappings and Metal residency. Use bounded explicit reads, reject failed lookups before GPU consumption, and remove the runtime sidecar option and paging controls. Preserve Qwen hash and speculative state handling; cover malformed files and exact recovery after prefill, decode, and MTP read failures.
2026-09-14 13:32:48 +02:00
Salvatore Sanfilippo 923b819abf Harden Qwen checkpoints, speculative decode and tool calls
Restore matching logits on verifier rewinds, replay stale recurrent state, discard old snapshots on restore, and invalidate partial payload reads. Keep lazy snapshots all-or-nothing and respect small prefill buffers and the unfused diagnostic path.

Preserve schema-declared string arguments. Repair all-row reference dumps and teacher-forced chunking. Add model-backed regressions and fix test-fixture leaks exposed by ASAN.
2026-09-14 11:06:16 +02:00
Salvatore Sanfilippo efc27d6d86 Keep literal reasoning and tool tags inside Qwen arguments
Use the structural reasoning delimiter on the server and wait for the parameter delimiter while streaming in ds4-agent. Add server and bytewise agent regressions; both fail before the fixes and pass afterward on M5 Max.
2026-09-14 10:34:10 +02:00
ccea768827 Add Qwen3.8 Flash Next model support
Implement qwen4exp loading, tokenizer and chat formatting, PLE sidecars,
Metal text and vision graphs, built-in MTP, and recurrent KV persistence.
Integrate the CLI, agent, server, download targets, and conversion tools.

Preserve the native Q4_1 encoder, Q4_K accumulation guard, and shared
quantizer build target. Keep exact-MTP rewind and vision-cache row-width
fixes. Persist valid logits and recurrent state at completed prefill chunks,
and reuse Qwen text-answer history when tools clients omit reasoning.
Retain exact reasoning replay and test continued checkpoints, cancellation,
cache eviction, and restart reuse with and without reasoning_content. Include
reproducible charts and measurements comparing throughput before and after
the checkpoint fixes on M3 Ultra.

Add Qwen trunk steering and prompt activation capture, including GDN layers.
Keep the MTP predictor outside the 48-row bank and preserve prompt dumps
through one-token prefill tails and multi-row speculative verification.
Recover Chrome target creation and retain its last page target. Preserve
full agent transcripts in stripped-session format when KV no longer fits,
while retaining the newer automatic compaction and continuation logic.
Add steering/MTP/capture, Chrome CDP recovery, and full-session save/reload
regressions. Adapt the intended changes from fork PRs #13 and #7.

Reconcile with antirez main bd66c40, preserving DeepSeek v4.1 model IDs,
tool syntax, session state, and Metal kernel dispatch. Exclude superseded
engine code and prototypes.

Co-authored-by: Andreas Parusel <aparuael@7170c824-6152-4cf1-985e-9503832ce673.fritz.box>
Co-authored-by: david <contact@davidberard.fr>
Co-authored-by: Michael Kuebbeler <cropduster@users.noreply.github.com>
Co-authored-by: Ogilthorp3 <bertrand@nepveu.name>
Co-authored-by: anemll <realanemll@gmail.com>
2026-09-13 14:57:29 +02:00
Salvatore Sanfilippo bd66c40207 DeepSeek v4.1 Flash support for Metal 2026-09-12 11:47:54 +02:00
Salvatore Sanfilippo 81a76585ff Preserve live vision context across server tool calls
Reuse bounded image embeddings, preserve exact image identities while appending new images, and splice image tokens into authenticated live continuations. Handle image tool results in Responses and Anthropic requests as well as Chat Completions. Normalize request-local image markers for visible replay without trusting text placeholders as image identities.

Add prefix and encoder-cache unit coverage plus real-model image replay and Pi coding-client regressions. Validated GLM 5.3 Flash and DeepSeek Vision-Exp on M5 Max, including 53K/56K prefixes, batched sessions, all three API formats, fresh logit replay, and frontend ASan/UBSan.
2026-09-08 13:57:09 +02:00
Salvatore Sanfilippo 6eac69e0aa Bound tool recovery by the request budget and explicit stops 2026-09-06 01:34:57 +02:00
Salvatore Sanfilippo 930ab73279 Keep valid opportunistic drafts across tool sampling boundaries 2026-09-06 01:27:29 +02:00
Salvatore Sanfilippo d108ae4e8d Preserve literal tool data across agent and server rendering
Do not HTML-unescape argument bodies. Share a narrow, reversible escape for each body closing delimiter, including its literal escaped spelling. Keep streaming fragments intact across those escapes. Preserve protocol-looking text inside GLM agent arguments until the actual argument terminator. Add round-trip, streaming and agent regressions.
2026-09-06 01:14:31 +02:00
Salvatore Sanfilippo fc6414cdfc Keep literal tool and thinking markers inside argument data
Use the incremental grammar state to detect completed calls, and skip argument/key bodies when searching final control markers. Validate complete calls before recovering unclosed reasoning. Cover DeepSeek and GLM arguments containing both tool endings and thinking tags without truncating or losing the call.
2026-09-06 01:07:50 +02:00
Salvatore Sanfilippo 759dd7cd69 Do not repair truncated tool actions or exceed output limits
Only repair an omitted DeepSeek wrapper around already closed invocations. Never supply a missing argument or invocation terminator. Preserve length finishes and do not retry beyond the client output budget. Log the actual stop cause, token ID and available budget when a tool call is incomplete.
2026-09-06 01:03:59 +02:00
Salvatore Sanfilippo 5b3cc8bfd1 Respect GLM tool syntax and speculative server boundaries
Track GLM function names and argument bodies with the existing incremental recognizer. Keep reasoning quotes out of syntax sampling. Discard unconsumed speculative tokens at tool and control stops, and resample after a structural/payload boundary with restored logits. Rebuild invalid recurrent prefixes with their image conditioning. Add GLM fragment and byte-boundary coverage.
2026-09-06 00:58:00 +02:00
Salvatore Sanfilippo d077fa67e5 Keep split tool delimiters in the server sampling tracker
Retain a lone opening angle bracket until the next token, without forcing code or HTML greedy. Previously the tracker consumed it and missed a parameter close split across tokens. Add byte-by-byte regressions for all three DeepSeek spellings and string/JSON bodies; all twelve failing checks now pass.
2026-09-06 00:50:14 +02:00
Mikhail Korobov 469ed2f4b5 Merge upstream/main: fold multimodal live-KV hardening into the reuse probe
Upstream 98b9135 ('server: reuse exact multimodal live KV safely') and
b0982a1 ('Harden multimodal cache fallback') restructured the execution
ladder this PR replaced with slot_probe_reuse().  Resolution keeps the
probe architecture and moves the new safety conditions into it, so
execution and routing still cannot disagree:

- The live_vision_match gate (image fingerprints are invisible to text/
  token prefixes) is now checked once at the top of
  slot_probe_reuse_locked(); job_slot_score inherits it, subsuming
  upstream's -1 vision guard.
- The GLM memory-rewind materialization re-validates the frontier after
  ds4_session_rewind() (common prefix lands on the rewind target and the
  vision state still matches) and falls back to disk cache / cold prefill
  when validation fails, as upstream's ladder did.
- Upstream's ladder reorder (memory-text before disk cache) is subsumed
  by the probe's tier order; the deleted ladder helpers stay deleted.
- Tests from both sides kept: the slot probe/routing tests and upstream's
  test_multimodal_prefill_resume_frontier.
2026-09-03 12:27:00 +05:00
antirez 110afdd888 Use random completion response IDs 2026-09-01 17:32:21 +02:00
antirez b0982a1b4e Harden multimodal cache fallback 2026-09-01 13:33:00 +02:00
Jordi Posthumus 98b91351ad server: reuse exact multimodal live KV safely 2026-09-01 07:46:20 -03:00
Salvatore Sanfilippo fc8bf3c39c Add DeepSeek V4 Flash vision support 2026-09-01 00:11:58 +02:00
antirez a547fdff4a Merge pull request #887 from kyuz0/perf/rocm-gfx1151-mmq-kernel-lab
# Conflicts:
#	Makefile
#	ds4.c
#	ds4_bench.c
#	ds4_help.c
#	tests/test_gpu_args_cli.sh
2026-08-31 11:48:21 +02:00
Donato Capitella e1147cdcbd server: support fixed-length greedy decode 2026-08-30 12:40:52 +01:00
Salvatore Sanfilippo 47713296d5 Add GLM 5.3 Flash vision support 2026-08-28 23:26:29 +02:00
antirez 147109a3cc Add GLM 5.3 Flash inference
Implement GLM 5.3 Q2 and Q4 inference across Metal and CUDA, including tensor parallelism, RDMA, batching, MTP, snapshots, SSD streaming, and GLM 5.2 shared-path improvements.

Add quantization and validation tools, official quality fixtures, model downloads, regression coverage, release QA, and user documentation.
2026-08-28 23:26:29 +02:00
antirez abd4ed3885 cli: expose exact MTP sampling mode 2026-08-23 15:05:13 +02:00
antirez 8d683d6616 dspark: add opportunistic temperature sampling 2026-08-23 15:05:13 +02:00
antirez 769a8ba42d dspark: support exact stochastic speculative decoding 2026-08-23 15:05:13 +02:00
Mikhail Korobov ada805ad59 server: staleness-aware eviction tiers in slot routing
The reuse-aware router sent a request that reuses nothing to the slot
with the shortest resident checkpoint (-live).  On a long-running
server this leaks: a finished long conversation is never the cheapest
victim, so such checkpoints pile up, the pool of usable slots shrinks,
and subagent/parallel requests end up evicting shorter *active*
conversations - the prefill thrashing this routing set out to fix
comes back.

Split the no-reuse victim selection into staleness tiers:

- empty slots still win (nothing to evict);
- a checkpoint idle past SLOT_STALE_AFTER_SEC (1h) is most likely a
  finished conversation: victims are picked most-idle-first,
  regardless of size, so forgotten sessions get reclaimed;
- fresher checkpoints belong to active conversations: keep the -live
  rule (evict the shortest, lose the least prefilled work).

Staleness only lowers a checkpoint's priority as a *victim*; it never
blocks reuse - the owner of a stale checkpoint who continues the
conversation still pins the slot via the reuse tier (which now scores
the actual reusable prefix instead of the full checkpoint length).

Also log evictions at dispatch time (slot, evicted tokens, idle time,
stale/protected), so routing decisions are visible in the server log
instead of being inferable only from the next request's cache miss.
2026-08-14 12:26:49 +05:00
Mikhail Korobov 4d7028e066 server: reuse-aware slot routing via shared live-state probe
With --batched-session N, job_slot_score routed requests to the slot
with the longest raw common token prefix, with no check that the slot's
checkpoint is actually reusable. Parallel subagents sharing a system
prompt were attracted to the slot holding the main agent's long context,
evicting it with zero reuse benefit and forcing a full re-prefill when
main resumed (masked only by --kv-disk-dir round-trips). Even an alien
request sharing no prefix at all evicted slot[0] on the tie-break.

Split every RAM reuse tier into a cheap probe predicate and its
materialization, and define tier order and conditions exactly once in
slot_probe_reuse_locked(): responses-visible, responses/anthropic
tool-output, GLM memory-rewind, memory-token, thinking-visible,
memory-text. The execution ladder (generate_job) probes once and
materializes only the selected tier; job_slot_score consumes the same
probe, so routing and execution cannot disagree. A slot scores +live
when the request would reuse its live state, -live otherwise: an empty
slot always beats an eviction, and the cheapest (shortest) unusable
checkpoint becomes the victim. Text-tier bindings are part of the probe,
so single-session chats whose resent history retokenizes differently or
omits hidden thinking keep pinning to their slot.

The memory-text probe needs rendered checkpoint text; cache it per slot
and refresh in the slot worker after each job, so probing stays a memcmp
and never detokenizes under tool_mu.

Verified: unit tests for every probe tier plus dispatch routing
(./ds4_test --server), full make test green, and live checks showing the
subagent deflected to the empty slot, main resuming via memory-token,
and thinking-visible / anthropic-tool-output / responses-tool-output
continuations unchanged.
2026-08-10 16:05:17 +05:00
Salvatore Sanfilippo 0ad494e514 metal: make exact fast paths automatic and trim experiments 2026-08-09 17:48:01 +02:00
antirez fd2d5e05a1 Merge Ivan Fioravanti's exact Metal MXFP4 speedups 2026-08-09 16:57:08 +02:00
ivanfioravanti 319614935f server: canonicalize OpenAI tool schema JSON spelling
Render tool schemas with stable Python-style separators and escapes so
semantically identical client JSON tokenizes the same way in the prompt.
2026-08-09 11:38:02 +02:00
ivanfioravanti 0ead8a87b0 server: recover truncated DSML tool calls 2026-08-09 11:38:02 +02:00
Salvatore Sanfilippo 8a703b6707 server: test real client disconnects 2026-08-07 10:40:02 +02:00
Matteo Collina e9ded97afe Cancel server work when clients disconnect 2026-08-05 20:45:38 +00:00
Salvatore Sanfilippo 355da75342 Add focused regressions for critical parser fixes 2026-08-04 13:47:47 +02:00
Salvatore Sanfilippo 51a1c14f35 Recover complete tool calls from unclosed reasoning
Stop relying on the model to restart a call after an injected reasoning close. Wait for a complete call, keep the preceding prose as reasoning, return the call structurally, and keep its protocol text out of streaming output.

Closes #675
2026-08-04 13:40:20 +02:00
Salvatore Sanfilippo fe2d3b0c12 Keep repeated reasoning out of streamed answers
Stop asking tool-enabled models to open a reasoning block that the prompt already opened. Hold the tentative answer until a tool call, response end, or second close marker proves whether it is answer text or another reasoning pass, for both OpenAI and Anthropic streams.

Closes #678
2026-08-04 13:35:02 +02:00
Salvatore Sanfilippo 7694112475 Reuse repeated GLM prompts by rewinding live state
When an incoming GLM prompt exactly matches the beginning of the live session, rewind one token before its boundary and reevaluate that final prompt token. This restores the correct logits without rebuilding a long prompt.

Closes #668
2026-08-04 13:32:49 +02:00