Support DeepSeek V4 Flash Vision Experimental
This commit is contained in:
1 parent
7c18e738cd
commit
9bbe12e6b3
52 files changed
+4955
-67
No files matched your search
@@ -40,6 +40,10 @@ the Metal kernels and small native integration layers are adapted from
|
|||||||
sampling, cancellation, idle unload, and a bounded resident session pool.
|
sampling, cancellation, idle unload, and a bounded resident session pool.
|
||||||
Supported acceleration includes SSD expert streaming, DeepSeek legacy MTP,
|
Supported acceleration includes SSD expert streaming, DeepSeek legacy MTP,
|
||||||
DSpark, GLM MTP, directional steering, and model-specific memory admission.
|
DSpark, GLM MTP, directional steering, and model-specific memory admission.
|
||||||
|
- **DeepSeek V4 Flash Vision Experimental.** Model Manager offers the
|
||||||
|
Vision Experimental main model, required image encoder, and matching DSpark
|
||||||
|
drafter. Image input works in chat, tool results, and the HTTP APIs, with
|
||||||
|
image-aware KV reuse. The 0731 drafter is not interchangeable with this model.
|
||||||
- **Local HTTP APIs.** A configurable listener on `127.0.0.1:4000` exposes
|
- **Local HTTP APIs.** A configurable listener on `127.0.0.1:4000` exposes
|
||||||
Models, Chat Completions, Completions, Anthropic Messages, and Responses APIs
|
Models, Chat Completions, Completions, Anthropic Messages, and Responses APIs
|
||||||
with streaming, tool calls, usage accounting, cancellation, and prefix-aware
|
with streaming, tool calls, usage accounting, cancellation, and prefix-aware
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
# Issue #100: DeepSeek V4 Flash Vision Experimental
|
||||||
|
|
||||||
|
The catalog ID is `deepseek-v4-flash-vision-exp`. The main model and image
|
||||||
|
encoder are required together, including for text-only use; DSpark uses the
|
||||||
|
separate Vision-Exp support file. Model Manager verifies both hashes and GGUF
|
||||||
|
metadata before making the model available. Existing 0731 files remain separate.
|
||||||
|
The session database migration accepts the new model without changing existing
|
||||||
|
session IDs or content.
|
||||||
|
|
||||||
|
## Reference and architecture
|
||||||
|
|
||||||
|
The behavioral oracle is DS4 commit
|
||||||
|
`0aaea5a238fb41a35106a551e73c8409dfb751ac`. This is existing DS4 functionality;
|
||||||
|
MTPLX is the Qwen oracle and has no corresponding DeepSeek implementation.
|
||||||
|
The three GGUF artifacts are pinned by size and SHA256 in `src/model.rs`, from
|
||||||
|
`antirez/deepseek-v4-gguf` revision
|
||||||
|
`f71f23d552d664e523b422157b2befbf74040380`. Their required source checkpoint is
|
||||||
|
`deepseek-ai/DeepSeek-V4-Flash-Vision-Exp` revision
|
||||||
|
`e46e16bf6035c6f317eb2ac7458eb0362926d402`.
|
||||||
|
|
||||||
|
All image decoding, resizing, patch construction, encoder graph execution,
|
||||||
|
prompt layout, attention-mask construction, model loading, and cache ownership
|
||||||
|
are Rust. Objective-C only binds Metal buffers and dispatches GPU primitives.
|
||||||
|
No DS4 C application or Python model implementation is linked, compiled into,
|
||||||
|
or invoked by the product. The independent Python comparison in
|
||||||
|
`tools/issue100-vision-reference.py` is test-only.
|
||||||
|
|
||||||
|
| Requirement / DS4 behavior | Implementation |
|
||||||
|
| --- | --- |
|
||||||
|
| Distinct checkpoint and drafter | Three catalog artifacts; strict revision, variant, tensor inventory, and RMS epsilon checks; wrong drafter/encoder rejection |
|
||||||
|
| Image preprocessing | EXIF orientation; decoded RGB identity; DS4 bicubic resize, centered padding, 14-pixel channel-major patches, minimum pixels and 381-token pre-alignment budget |
|
||||||
|
| Vision tower | 32 Rust-dispatched ViT blocks, 2-D RoPE, DS4 BF16 rounding points, 3x3 aligner reorder, GELU projection to 4096 dimensions |
|
||||||
|
| Image prompt | Position-dependent leading pads, paired-row permutation, newline/pad/end sentinel vectors, synthetic token kinds, whole-image prefill chunks |
|
||||||
|
| Visual language-model execution | Image embeddings broadcast to all hyperconnection streams, bidirectional image-window attention, causal compressed keys with indexer masks, visual MoE routing biases and ordinary text hash routing |
|
||||||
|
| Continued conversations | Historical image identity and positions retained with the exact generated token frontier; new images laid out at their actual appended positions |
|
||||||
|
| Cache safety | Decoded-pixel fingerprints, position/count checks, changed/reordered-image invalidation, resident-session ownership, checkpoint serialization and validation |
|
||||||
|
| App / API use | Shared model selector, model manager, image/tool-result input, API model ID, reasoning modes, statistics, configuration, and evaluator support |
|
||||||
|
|
||||||
|
DeepSeek image identity in DS4Server's disk checkpoints is a DS4Server storage
|
||||||
|
extension. It validates every serialized image span against the synthetic token
|
||||||
|
stream before installing the restored state. Other model checkpoint formats
|
||||||
|
and payloads are unchanged.
|
||||||
|
|
||||||
|
## Numerical and regression evidence
|
||||||
|
|
||||||
|
The Rust encoder was reviewed operation by operation against
|
||||||
|
`ds4_gpu_deepseek4_vision_encode`. The reused BF16 matrix bridge is byte-identical
|
||||||
|
at the function level (SHA256
|
||||||
|
`621818731c2d43989a8fd08f186d1054ecdb49a91cb1453591f07bc2613d3fef`).
|
||||||
|
`metal/glm53_bf16.metal` is byte-identical to that DS4 revision (SHA256
|
||||||
|
`27d93dcfe81b09f93ed65cd986aaa0b9c1ca1df7fc43e58c15da533284d50db5`).
|
||||||
|
The shared vision kernels used here also match; the reference's unrelated GLM
|
||||||
|
merger-layernorm barrier change is outside the DeepSeek graph.
|
||||||
|
|
||||||
|
The independent published DeepSeek PyTorch encoder comparison on the fixed red
|
||||||
|
square produces 100x4096 aligned values, cosine similarity 0.99500793, RMSE
|
||||||
|
0.00568503, and maximum absolute difference 0.10229492. This is not a bitwise
|
||||||
|
DS4 oracle comparison: the published Python graph uses BF16 module operations,
|
||||||
|
while DS4 accepts FP32 patches and specifies its own BF16 rounding boundaries.
|
||||||
|
DS4's operation order and rounding are retained in the Rust graph.
|
||||||
|
|
||||||
|
Focused checks include DS4's published image-layout and attention-bound vectors,
|
||||||
|
image-size limits, real sidecar tensor validation, wrong-checkpoint rejection,
|
||||||
|
all DeepSeek reasoning modes in image continuations, mixed text/image routing
|
||||||
|
with exact selected experts and analytical normalized weights, and raw/mixed
|
||||||
|
attention over a wrapped KV ring with independently computed visible-key means.
|
||||||
|
The existing real-model 0731 resident-session and GLM 5.3 verifier tests pass.
|
||||||
|
|
||||||
|
## Reproducing the checks
|
||||||
|
|
||||||
|
Download the three catalog files at the artifact revision above into
|
||||||
|
`local-eval-results/issue100-artifacts/`. The ignored `issue100_managed_intake`
|
||||||
|
test hard-links these into `local-eval-results/issue100-managed/`, runs the real
|
||||||
|
SHA256/intake pipeline, and writes normal verification markers only after success.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo test --all-features issue100
|
||||||
|
cargo test --all-features issue100_checkpoint_metadata -- --ignored --nocapture
|
||||||
|
cargo test --all-features issue100_visual_text -- --ignored --nocapture
|
||||||
|
cargo test --all-features issue100_managed_intake -- --ignored --nocapture
|
||||||
|
```
|
||||||
|
|
||||||
|
Run Metal tests serially in separate processes under `test-supervisor`, with the
|
||||||
|
child output redirected to a separate log. Copy the compiled test binary outside
|
||||||
|
`target/` before starting a longer test. The encoder accepts
|
||||||
|
`DS4_ISSUE100_IMAGE` and `DS4_ISSUE100_ENCODER_OUTPUT` (dump prefix); its filter is
|
||||||
|
`issue100_encoder_real_weights`. The small primitive filter is
|
||||||
|
`issue100_visual_routing_and_attention`. The production generation/cache test
|
||||||
|
filter is `issue100_full_model_images_cache_and_dspark` and uses the committed
|
||||||
|
PNG fixtures here. `DS4_ISSUE100_KEEP_VISION=1` selects persistent encoder
|
||||||
|
residency; `DS4_ISSUE100_SSD=1` selects a 4096-expert SSD cache.
|
||||||
|
|
||||||
|
The full-model test requires the intake test first and checks real image answers,
|
||||||
|
target-only/DSpark agreement with actual drafting, warm/disk continuation,
|
||||||
|
same-token-layout image replacement, multiple images, tool images, and text-only
|
||||||
|
use after image requests. All three isolated runs passed on the M5 Max, using
|
||||||
|
the real managed artifacts after successful SHA256 and metadata intake.
|
||||||
|
|
||||||
|
|
||||||
|
## Final isolated validation
|
||||||
|
|
||||||
|
| Runtime mode | Result | Duration | DSpark drafted / accepted |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Resident, transient encoder | Pass | 15.10 s | 15 / 10 |
|
||||||
|
| Resident, persistent encoder | Pass | 15.94 s | 15 / 10 |
|
||||||
|
| SSD, 4096-expert cache and preload | Pass | 31.46 s | 30 / 0 |
|
||||||
|
|
||||||
|
Each fresh process checked red-square and blue-circle recognition, image order,
|
||||||
|
new user and tool images, changed images with identical synthetic prompt tokens,
|
||||||
|
warm and disk-restored continuation, exact checkpoint logits, and text-only use
|
||||||
|
after images. DSpark output matched target-only output within each runtime mode;
|
||||||
|
the resident and SSD answers use different wording, so these receipts do not
|
||||||
|
claim token equivalence between storage modes. Zero accepted drafts in the SSD
|
||||||
|
fixture is a measured result, not a disabled drafter.
|
||||||
|
|
||||||
|
The SSD log retains 129 existing mapped-range diagnostics (three per layer) from
|
||||||
|
the GPU-copy hotlist preload attempt. The existing Rust loader then successfully
|
||||||
|
seeds through the host-loading primitive after finishing GPU commands. Both
|
||||||
|
preload primitives exist in the DS4 reference; this change retains the existing
|
||||||
|
DS4Server fallback. These are not image-encoder failures. Resource logs are
|
||||||
|
process accounting and do not measure total file-backed GPU residency.
|
||||||
|
|
||||||
|
All commit gates passed: rustfmt, Clippy with warnings denied, signed app bundle,
|
||||||
|
and all-feature tests (323 application tests plus eight tool/supervisor tests;
|
||||||
|
247 hardware/artifact-dependent tests are ignored by that generic suite).
|
||||||
|
The relevant ignored tests were run explicitly as documented above. The bundle
|
||||||
|
contains the new Metal kernel and its DS4 license. Logs alongside this document
|
||||||
|
record intake, encoder/primitive checks, family regression controls, the three
|
||||||
|
full-model runs, and the commit gates.
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
cargo build --release --all-targets --all-features
|
||||||
|
Compiling ds4-server v0.1.0 (/Users/gb/Projects/DS4Server)
|
||||||
|
Finished `release` profile [optimized] target(s) in 43.29s
|
||||||
|
warning: the following packages contain code that will be rejected by a future version of Rust: block v0.1.6
|
||||||
|
note: to see what the problems were, use the option `--future-incompat-report`, or run `cargo report future-incompatibilities --id 1`
|
||||||
|
cargo packager --release --formats app
|
||||||
|
codesign --force --deep --sign - --timestamp=none target/release/DS4Server.app
|
||||||
|
target/release/DS4Server.app: replacing existing signature
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
Checking ds4-server v0.1.0 (/Users/gb/Projects/DS4Server)
|
||||||
|
Finished `dev` profile [unoptimized + debuginfo] target(s) in 4.60s
|
||||||
|
warning: the following packages contain code that will be rejected by a future version of Rust: block v0.1.6
|
||||||
|
note: to see what the problems were, use the option `--future-incompat-report`, or run `cargo report future-incompatibilities --id 1`
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
running 1 test
|
||||||
|
test engine::metal::tests::resident_multi_session_switching_preserves_each_kv_frontier ... ds4: Metal device Apple M5 Max, 128.00 GiB RAM
|
||||||
|
ds4: Metal 4 tensor API enabled for Tensor kernels
|
||||||
|
ds4: drift-patch flags hc_stable=on norm_unify=on kv_raw_f32=off rope_exp2_log2=off math_safe=off tensor_matmul=on
|
||||||
|
ds4: Metal model views created in 1.615 ms, residency requested in 9081.153 ms, warmup 3.442 ms (mapped 82697.67 MiB from offset 5.09 MiB)
|
||||||
|
ds4: Metal mapped mmaped model as 2 overlapping shared buffers
|
||||||
|
ok
|
||||||
|
|
||||||
|
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 568 filtered out; finished in 11.19s
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
running 1 test
|
||||||
|
test engine::tokenizer::tests::ds4_chat_matches_original_session_tokens ... ok
|
||||||
|
|
||||||
|
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 569 filtered out; finished in 0.14s
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
{"event":"test_supervisor_started","pid":13017,"limit_bytes":17179869184,"start_seconds":60.0,"idle_seconds":120.0,"sample_ms":100}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":0,"usage":{"cpu_time_ns":163666,"resident_bytes":688128,"physical_bytes":213064,"peak_physical_bytes":213064,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":101,"usage":{"cpu_time_ns":2659875,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1593344,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":205,"usage":{"cpu_time_ns":2659875,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1593344,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":306,"usage":{"cpu_time_ns":2659875,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1593344,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":407,"usage":{"cpu_time_ns":2659875,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1593344,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":518,"usage":{"cpu_time_ns":2659875,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1593344,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":626,"usage":{"cpu_time_ns":2659875,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1593344,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":733,"usage":{"cpu_time_ns":2659875,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1593344,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":835,"usage":{"cpu_time_ns":2659875,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1593344,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":943,"usage":{"cpu_time_ns":2659875,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1593344,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1052,"usage":{"cpu_time_ns":2659875,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1593344,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1163,"usage":{"cpu_time_ns":2659875,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1593344,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1273,"usage":{"cpu_time_ns":2659875,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1593344,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1374,"usage":{"cpu_time_ns":2659875,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1593344,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1482,"usage":{"cpu_time_ns":2659875,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1593344,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1592,"usage":{"cpu_time_ns":2659875,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1593344,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1697,"usage":{"cpu_time_ns":2659875,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1593344,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1802,"usage":{"cpu_time_ns":2659875,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1593344,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1908,"usage":{"cpu_time_ns":2659875,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1593344,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2018,"usage":{"cpu_time_ns":2659875,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1593344,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2119,"usage":{"cpu_time_ns":2659875,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1593344,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2224,"usage":{"cpu_time_ns":2659875,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1593344,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2334,"usage":{"cpu_time_ns":2659875,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1593344,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2441,"usage":{"cpu_time_ns":2659875,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1593344,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2542,"usage":{"cpu_time_ns":31532500,"resident_bytes":30605312,"physical_bytes":11207424,"peak_physical_bytes":11207424,"disk_read_bytes":2285568,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2648,"usage":{"cpu_time_ns":31631291,"resident_bytes":30621696,"physical_bytes":11223808,"peak_physical_bytes":12288768,"disk_read_bytes":2285568,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2751,"usage":{"cpu_time_ns":31631291,"resident_bytes":30621696,"physical_bytes":11223808,"peak_physical_bytes":12288768,"disk_read_bytes":2285568,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2855,"usage":{"cpu_time_ns":31631291,"resident_bytes":30621696,"physical_bytes":11223808,"peak_physical_bytes":12288768,"disk_read_bytes":2285568,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2958,"usage":{"cpu_time_ns":31631291,"resident_bytes":30621696,"physical_bytes":11223808,"peak_physical_bytes":12288768,"disk_read_bytes":2285568,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3068,"usage":{"cpu_time_ns":31631291,"resident_bytes":30621696,"physical_bytes":11223808,"peak_physical_bytes":12288768,"disk_read_bytes":2285568,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3178,"usage":{"cpu_time_ns":31631291,"resident_bytes":30621696,"physical_bytes":11223808,"peak_physical_bytes":12288768,"disk_read_bytes":2285568,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3288,"usage":{"cpu_time_ns":31631291,"resident_bytes":30621696,"physical_bytes":11223808,"peak_physical_bytes":12288768,"disk_read_bytes":2285568,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3393,"usage":{"cpu_time_ns":31631291,"resident_bytes":30621696,"physical_bytes":11223808,"peak_physical_bytes":12288768,"disk_read_bytes":2285568,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3500,"usage":{"cpu_time_ns":31631291,"resident_bytes":30621696,"physical_bytes":11223808,"peak_physical_bytes":12288768,"disk_read_bytes":2285568,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3607,"usage":{"cpu_time_ns":31631291,"resident_bytes":30621696,"physical_bytes":11223808,"peak_physical_bytes":12288768,"disk_read_bytes":2285568,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3717,"usage":{"cpu_time_ns":31631291,"resident_bytes":30621696,"physical_bytes":11223808,"peak_physical_bytes":12288768,"disk_read_bytes":2285568,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3818,"usage":{"cpu_time_ns":31631291,"resident_bytes":30621696,"physical_bytes":11223808,"peak_physical_bytes":12288768,"disk_read_bytes":2285568,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3924,"usage":{"cpu_time_ns":31631291,"resident_bytes":30621696,"physical_bytes":11223808,"peak_physical_bytes":12288768,"disk_read_bytes":2285568,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4029,"usage":{"cpu_time_ns":31631291,"resident_bytes":30621696,"physical_bytes":11223808,"peak_physical_bytes":12288768,"disk_read_bytes":2285568,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4134,"usage":{"cpu_time_ns":79446208,"resident_bytes":61718528,"physical_bytes":145966496,"peak_physical_bytes":145966496,"disk_read_bytes":2285568,"disk_write_bytes":4620288}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4244,"usage":{"cpu_time_ns":189471500,"resident_bytes":62750720,"physical_bytes":146769312,"peak_physical_bytes":146769312,"disk_read_bytes":2285568,"disk_write_bytes":4620288}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4349,"usage":{"cpu_time_ns":265315666,"resident_bytes":67895296,"physical_bytes":213583264,"peak_physical_bytes":213583264,"disk_read_bytes":2613248,"disk_write_bytes":4833280}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4451,"usage":{"cpu_time_ns":265315666,"resident_bytes":67895296,"physical_bytes":213583264,"peak_physical_bytes":213583264,"disk_read_bytes":2613248,"disk_write_bytes":4833280}}
|
||||||
|
{"event":"test_resource_summary","elapsed_ms":4560,"peak":{"cpu_time_ns":265315666,"resident_bytes":67895296,"physical_bytes":213583264,"peak_physical_bytes":213583264,"disk_read_bytes":2613248,"disk_write_bytes":4833280},"error":null}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
running 1 test
|
||||||
|
test engine::metal::deepseek_vision::tests::issue100_encoder_real_weights ... ds4: Metal device Apple M5 Max, 128.00 GiB RAM
|
||||||
|
ds4: Metal 4 tensor API enabled for Tensor kernels
|
||||||
|
ds4: drift-patch flags hc_stable=on norm_unify=on kv_raw_f32=off rope_exp2_log2=off math_safe=off tensor_matmul=on
|
||||||
|
ds4: Metal model views created in 0.039 ms, residency requested in 6.538 ms, warmup 3.368 ms (mapped 889.64 MiB from offset 0.02 MiB)
|
||||||
|
ds4: Metal mapped mmaped model as 1 overlapping shared buffers
|
||||||
|
{"grid":[28,28],"aligned_grid":[10,10],"tokens":100,"width":224,"height":224,"values":409600}
|
||||||
|
ok
|
||||||
|
|
||||||
|
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 563 filtered out; finished in 2.01s
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
{"event":"test_supervisor_started","pid":37642,"limit_bytes":120259084288,"start_seconds":60.0,"idle_seconds":120.0,"sample_ms":100}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":0,"usage":{"cpu_time_ns":158791,"resident_bytes":655360,"physical_bytes":180296,"peak_physical_bytes":213064,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":110,"usage":{"cpu_time_ns":86772708,"resident_bytes":61030400,"physical_bytes":39846512,"peak_physical_bytes":39846512,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":220,"usage":{"cpu_time_ns":174636750,"resident_bytes":108462080,"physical_bytes":68764608,"peak_physical_bytes":68764608,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":325,"usage":{"cpu_time_ns":281313375,"resident_bytes":108462080,"physical_bytes":68764608,"peak_physical_bytes":68764608,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":428,"usage":{"cpu_time_ns":383873000,"resident_bytes":108462080,"physical_bytes":68764608,"peak_physical_bytes":68764608,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":537,"usage":{"cpu_time_ns":492457791,"resident_bytes":108462080,"physical_bytes":68764608,"peak_physical_bytes":68764608,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":647,"usage":{"cpu_time_ns":602852916,"resident_bytes":109133824,"physical_bytes":187646912,"peak_physical_bytes":187646912,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":757,"usage":{"cpu_time_ns":712857208,"resident_bytes":109133824,"physical_bytes":187646912,"peak_physical_bytes":187646912,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":859,"usage":{"cpu_time_ns":812860541,"resident_bytes":109133824,"physical_bytes":187548608,"peak_physical_bytes":187646912,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":969,"usage":{"cpu_time_ns":922864833,"resident_bytes":109133824,"physical_bytes":187548608,"peak_physical_bytes":187646912,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1075,"usage":{"cpu_time_ns":1022868333,"resident_bytes":109133824,"physical_bytes":187548608,"peak_physical_bytes":187646912,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1185,"usage":{"cpu_time_ns":1059079041,"resident_bytes":109166592,"physical_bytes":187564992,"peak_physical_bytes":187646912,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1292,"usage":{"cpu_time_ns":1059079041,"resident_bytes":109166592,"physical_bytes":187564992,"peak_physical_bytes":187646912,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1402,"usage":{"cpu_time_ns":1068676208,"resident_bytes":109166592,"physical_bytes":187564992,"peak_physical_bytes":187646912,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1511,"usage":{"cpu_time_ns":1138948708,"resident_bytes":109166592,"physical_bytes":187564992,"peak_physical_bytes":187646912,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1621,"usage":{"cpu_time_ns":1248951791,"resident_bytes":109166592,"physical_bytes":187564992,"peak_physical_bytes":187646912,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1724,"usage":{"cpu_time_ns":1348956625,"resident_bytes":109166592,"physical_bytes":187564992,"peak_physical_bytes":187646912,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1834,"usage":{"cpu_time_ns":1458987666,"resident_bytes":109166592,"physical_bytes":187564992,"peak_physical_bytes":187646912,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1944,"usage":{"cpu_time_ns":1576030458,"resident_bytes":128204800,"physical_bytes":202311240,"peak_physical_bytes":202311240,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2054,"usage":{"cpu_time_ns":1678444500,"resident_bytes":132841472,"physical_bytes":268912248,"peak_physical_bytes":268912248,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2162,"usage":{"cpu_time_ns":1683235708,"resident_bytes":135757824,"physical_bytes":268977784,"peak_physical_bytes":268977784,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2270,"usage":{"cpu_time_ns":1787228333,"resident_bytes":136052736,"physical_bytes":270878328,"peak_physical_bytes":270878328,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2380,"usage":{"cpu_time_ns":1792551041,"resident_bytes":137576448,"physical_bytes":351028904,"peak_physical_bytes":351028904,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2490,"usage":{"cpu_time_ns":1798559166,"resident_bytes":138182656,"physical_bytes":371295912,"peak_physical_bytes":371295912,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2601,"usage":{"cpu_time_ns":1804472791,"resident_bytes":138674176,"physical_bytes":384419496,"peak_physical_bytes":384419496,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2711,"usage":{"cpu_time_ns":1809852125,"resident_bytes":139116544,"physical_bytes":396428968,"peak_physical_bytes":396428968,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2821,"usage":{"cpu_time_ns":1816155541,"resident_bytes":139624448,"physical_bytes":409552552,"peak_physical_bytes":409552552,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2931,"usage":{"cpu_time_ns":1873384375,"resident_bytes":140836864,"physical_bytes":434636456,"peak_physical_bytes":434636456,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3041,"usage":{"cpu_time_ns":1981844875,"resident_bytes":154206208,"physical_bytes":434914984,"peak_physical_bytes":434914984,"disk_read_bytes":16384,"disk_write_bytes":25169920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3152,"usage":{"cpu_time_ns":2037590041,"resident_bytes":160645120,"physical_bytes":306103976,"peak_physical_bytes":447579864,"disk_read_bytes":16384,"disk_write_bytes":25317376}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3259,"usage":{"cpu_time_ns":2126845083,"resident_bytes":161169408,"physical_bytes":318293672,"peak_physical_bytes":447579864,"disk_read_bytes":16384,"disk_write_bytes":25317376}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3369,"usage":{"cpu_time_ns":2231828375,"resident_bytes":147636224,"physical_bytes":260523712,"peak_physical_bytes":447579864,"disk_read_bytes":24576,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3475,"usage":{"cpu_time_ns":2338136333,"resident_bytes":162742272,"physical_bytes":255903400,"peak_physical_bytes":447579864,"disk_read_bytes":24576,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3585,"usage":{"cpu_time_ns":2409057041,"resident_bytes":164610048,"physical_bytes":320128680,"peak_physical_bytes":447579864,"disk_read_bytes":24576,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3696,"usage":{"cpu_time_ns":2417197916,"resident_bytes":150568960,"physical_bytes":375146200,"peak_physical_bytes":447579864,"disk_read_bytes":24576,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3806,"usage":{"cpu_time_ns":2422607208,"resident_bytes":151011328,"physical_bytes":387172056,"peak_physical_bytes":447579864,"disk_read_bytes":24576,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3916,"usage":{"cpu_time_ns":2428629583,"resident_bytes":151502848,"physical_bytes":400279256,"peak_physical_bytes":447579864,"disk_read_bytes":24576,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4022,"usage":{"cpu_time_ns":2433951458,"resident_bytes":151945216,"physical_bytes":411862744,"peak_physical_bytes":447579864,"disk_read_bytes":24576,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4125,"usage":{"cpu_time_ns":2439699666,"resident_bytes":152387584,"physical_bytes":442828504,"peak_physical_bytes":447579864,"disk_read_bytes":24576,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4235,"usage":{"cpu_time_ns":2876701875,"resident_bytes":195395584,"physical_bytes":360859448,"peak_physical_bytes":484361992,"disk_read_bytes":24576,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4342,"usage":{"cpu_time_ns":3007532875,"resident_bytes":205291520,"physical_bytes":482150200,"peak_physical_bytes":484361992,"disk_read_bytes":24576,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4449,"usage":{"cpu_time_ns":3332647875,"resident_bytes":208683008,"physical_bytes":484591416,"peak_physical_bytes":484591416,"disk_read_bytes":24576,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4559,"usage":{"cpu_time_ns":3765325791,"resident_bytes":208961536,"physical_bytes":484869944,"peak_physical_bytes":484869944,"disk_read_bytes":24576,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4669,"usage":{"cpu_time_ns":3864230375,"resident_bytes":209551360,"physical_bytes":484738872,"peak_physical_bytes":484886328,"disk_read_bytes":24576,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4779,"usage":{"cpu_time_ns":3967922875,"resident_bytes":205324288,"physical_bytes":375228192,"peak_physical_bytes":497403752,"disk_read_bytes":32768,"disk_write_bytes":77606912}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4881,"usage":{"cpu_time_ns":4068514458,"resident_bytes":221200384,"physical_bytes":391120696,"peak_physical_bytes":497403752,"disk_read_bytes":32768,"disk_write_bytes":77606912}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4991,"usage":{"cpu_time_ns":4091918750,"resident_bytes":221921280,"physical_bytes":447317816,"peak_physical_bytes":497403752,"disk_read_bytes":32768,"disk_write_bytes":77606912}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5099,"usage":{"cpu_time_ns":4102419666,"resident_bytes":206536704,"physical_bytes":506021688,"peak_physical_bytes":506021688,"disk_read_bytes":32768,"disk_write_bytes":77606912}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5209,"usage":{"cpu_time_ns":4107575708,"resident_bytes":206979072,"physical_bytes":518031160,"peak_physical_bytes":518031160,"disk_read_bytes":32768,"disk_write_bytes":77606912}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5319,"usage":{"cpu_time_ns":4113264541,"resident_bytes":207470592,"physical_bytes":531171128,"peak_physical_bytes":531171128,"disk_read_bytes":32768,"disk_write_bytes":77606912}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5429,"usage":{"cpu_time_ns":4118395625,"resident_bytes":207912960,"physical_bytes":542754616,"peak_physical_bytes":542754616,"disk_read_bytes":32768,"disk_write_bytes":77606912}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5539,"usage":{"cpu_time_ns":4127693000,"resident_bytes":208846848,"physical_bytes":572409656,"peak_physical_bytes":572409656,"disk_read_bytes":32768,"disk_write_bytes":77606912}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5649,"usage":{"cpu_time_ns":4559910291,"resident_bytes":211697664,"physical_bytes":488245096,"peak_physical_bytes":599164728,"disk_read_bytes":32768,"disk_write_bytes":77606912}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5759,"usage":{"cpu_time_ns":4996436541,"resident_bytes":212369408,"physical_bytes":488359808,"peak_physical_bytes":599164728,"disk_read_bytes":32768,"disk_write_bytes":77606912}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5870,"usage":{"cpu_time_ns":5435416416,"resident_bytes":212992000,"physical_bytes":488458112,"peak_physical_bytes":599164728,"disk_read_bytes":32768,"disk_write_bytes":77606912}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5980,"usage":{"cpu_time_ns":5499016208,"resident_bytes":213499904,"physical_bytes":488458112,"peak_physical_bytes":599164728,"disk_read_bytes":32768,"disk_write_bytes":77606912}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6090,"usage":{"cpu_time_ns":5606456291,"resident_bytes":227901440,"physical_bytes":488458112,"peak_physical_bytes":599164728,"disk_read_bytes":40960,"disk_write_bytes":104198144}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6202,"usage":{"cpu_time_ns":5715925250,"resident_bytes":227901440,"physical_bytes":488458112,"peak_physical_bytes":599164728,"disk_read_bytes":45056,"disk_write_bytes":130789376}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6309,"usage":{"cpu_time_ns":5720009125,"resident_bytes":228179968,"physical_bytes":502368128,"peak_physical_bytes":599164728,"disk_read_bytes":45056,"disk_write_bytes":130789376}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6419,"usage":{"cpu_time_ns":5724536500,"resident_bytes":228179968,"physical_bytes":503039872,"peak_physical_bytes":599164728,"disk_read_bytes":45056,"disk_write_bytes":130789376}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6529,"usage":{"cpu_time_ns":5732569833,"resident_bytes":228196352,"physical_bytes":503039872,"peak_physical_bytes":599164728,"disk_read_bytes":45056,"disk_write_bytes":130789376}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6639,"usage":{"cpu_time_ns":6201525250,"resident_bytes":228835328,"physical_bytes":491308928,"peak_physical_bytes":599164728,"disk_read_bytes":45056,"disk_write_bytes":130789376}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6749,"usage":{"cpu_time_ns":6311372583,"resident_bytes":228835328,"physical_bytes":491308928,"peak_physical_bytes":599164728,"disk_read_bytes":45056,"disk_write_bytes":130789376}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6859,"usage":{"cpu_time_ns":6415947833,"resident_bytes":222134272,"physical_bytes":385697592,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":158916608}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6961,"usage":{"cpu_time_ns":6494033333,"resident_bytes":223510528,"physical_bytes":448546592,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":158916608}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7063,"usage":{"cpu_time_ns":6496621833,"resident_bytes":223526912,"physical_bytes":405948216,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":158916608}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7173,"usage":{"cpu_time_ns":6501995166,"resident_bytes":223526912,"physical_bytes":429262648,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":158916608}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7275,"usage":{"cpu_time_ns":6593164875,"resident_bytes":224968704,"physical_bytes":476743504,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":158916608}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7381,"usage":{"cpu_time_ns":6986154666,"resident_bytes":227557376,"physical_bytes":479479632,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":158916608}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7491,"usage":{"cpu_time_ns":7096023166,"resident_bytes":227688448,"physical_bytes":479479632,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":158916608}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7601,"usage":{"cpu_time_ns":7201462833,"resident_bytes":228966400,"physical_bytes":479479632,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":187043840}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7711,"usage":{"cpu_time_ns":7219993875,"resident_bytes":230834176,"physical_bytes":543704912,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":187043840}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7821,"usage":{"cpu_time_ns":7321288833,"resident_bytes":228982784,"physical_bytes":450332496,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":187043840}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7931,"usage":{"cpu_time_ns":7330549833,"resident_bytes":236273664,"physical_bytes":518342480,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":187043840}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8042,"usage":{"cpu_time_ns":7342040750,"resident_bytes":233537536,"physical_bytes":577849168,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":187043840}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8152,"usage":{"cpu_time_ns":7350480041,"resident_bytes":234192896,"physical_bytes":578504528,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":187043840}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8262,"usage":{"cpu_time_ns":7360057041,"resident_bytes":234913792,"physical_bytes":579225424,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":187043840}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8372,"usage":{"cpu_time_ns":7369511708,"resident_bytes":235667456,"physical_bytes":579979088,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":187043840}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8482,"usage":{"cpu_time_ns":7377769000,"resident_bytes":236339200,"physical_bytes":580634448,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":187043840}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8592,"usage":{"cpu_time_ns":7819415708,"resident_bytes":236404736,"physical_bytes":457819984,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":187043840}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8702,"usage":{"cpu_time_ns":7929061291,"resident_bytes":236421120,"physical_bytes":457836368,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":187043840}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8812,"usage":{"cpu_time_ns":8034578750,"resident_bytes":248725504,"physical_bytes":457836368,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":227495936}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8922,"usage":{"cpu_time_ns":8101839000,"resident_bytes":250855424,"physical_bytes":522291024,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":227495936}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9033,"usage":{"cpu_time_ns":8153384833,"resident_bytes":249004032,"physical_bytes":458082128,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":227495936}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9143,"usage":{"cpu_time_ns":8210025625,"resident_bytes":250888192,"physical_bytes":522323792,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":227495936}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9253,"usage":{"cpu_time_ns":8270070583,"resident_bytes":249036800,"physical_bytes":458114896,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":227495936}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9359,"usage":{"cpu_time_ns":8317796000,"resident_bytes":250920960,"physical_bytes":522356560,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":227495936}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9469,"usage":{"cpu_time_ns":8322483791,"resident_bytes":254771200,"physical_bytes":480429880,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":227495936}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9579,"usage":{"cpu_time_ns":8326288208,"resident_bytes":254771200,"physical_bytes":480429880,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":227495936}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9689,"usage":{"cpu_time_ns":8336750625,"resident_bytes":255180800,"physical_bytes":582321976,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":227495936}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9799,"usage":{"cpu_time_ns":8346214125,"resident_bytes":255901696,"physical_bytes":571262800,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":227495936}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9909,"usage":{"cpu_time_ns":8355466000,"resident_bytes":256655360,"physical_bytes":572016464,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":227495936}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10019,"usage":{"cpu_time_ns":8364417875,"resident_bytes":257376256,"physical_bytes":572737360,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":227495936}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10125,"usage":{"cpu_time_ns":8373669666,"resident_bytes":258129920,"physical_bytes":573572944,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":227495936}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10235,"usage":{"cpu_time_ns":8823157958,"resident_bytes":258326528,"physical_bytes":460605264,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":227495936}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10342,"usage":{"cpu_time_ns":8929590083,"resident_bytes":258326528,"physical_bytes":460605264,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":227495936}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10452,"usage":{"cpu_time_ns":9035189583,"resident_bytes":223461376,"physical_bytes":358303568,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":281427968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10559,"usage":{"cpu_time_ns":9140813375,"resident_bytes":235307008,"physical_bytes":361875280,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":281427968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10660,"usage":{"cpu_time_ns":9189294625,"resident_bytes":237404160,"physical_bytes":426297168,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":281427968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10770,"usage":{"cpu_time_ns":9198647541,"resident_bytes":223264768,"physical_bytes":482707280,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":281427968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10880,"usage":{"cpu_time_ns":9203889041,"resident_bytes":223707136,"physical_bytes":494290768,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":281427968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10990,"usage":{"cpu_time_ns":9209785166,"resident_bytes":224215040,"physical_bytes":507414352,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":281427968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11101,"usage":{"cpu_time_ns":9215004875,"resident_bytes":224657408,"physical_bytes":519423824,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":281427968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11211,"usage":{"cpu_time_ns":9220738000,"resident_bytes":225099776,"physical_bytes":549930832,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":281427968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11321,"usage":{"cpu_time_ns":9674345333,"resident_bytes":228573184,"physical_bytes":500369280,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":281427968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11425,"usage":{"cpu_time_ns":9943252875,"resident_bytes":229130240,"physical_bytes":504547200,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":281427968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11532,"usage":{"cpu_time_ns":10150432208,"resident_bytes":229703680,"physical_bytes":504596352,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":281427968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11642,"usage":{"cpu_time_ns":10260039750,"resident_bytes":229703680,"physical_bytes":504596352,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":281427968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11753,"usage":{"cpu_time_ns":10365728416,"resident_bytes":250511360,"physical_bytes":421431120,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":307720192}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11863,"usage":{"cpu_time_ns":10468843916,"resident_bytes":251428864,"physical_bytes":476284752,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":307720192}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11973,"usage":{"cpu_time_ns":10485306541,"resident_bytes":253067264,"physical_bytes":476284752,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":307720192}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12083,"usage":{"cpu_time_ns":10580108791,"resident_bytes":251445248,"physical_bytes":476301136,"peak_physical_bytes":599164728,"disk_read_bytes":49152,"disk_write_bytes":307720192}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12193,"usage":{"cpu_time_ns":10590163125,"resident_bytes":227557376,"physical_bytes":625657704,"peak_physical_bytes":625657704,"disk_read_bytes":49152,"disk_write_bytes":307720192}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12303,"usage":{"cpu_time_ns":10600883083,"resident_bytes":228573184,"physical_bytes":647989096,"peak_physical_bytes":647989096,"disk_read_bytes":49152,"disk_write_bytes":307720192}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12413,"usage":{"cpu_time_ns":10611199541,"resident_bytes":229621760,"physical_bytes":658081640,"peak_physical_bytes":658081640,"disk_read_bytes":49152,"disk_write_bytes":307720192}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12524,"usage":{"cpu_time_ns":10621232500,"resident_bytes":230637568,"physical_bytes":667715432,"peak_physical_bytes":667715432,"disk_read_bytes":49152,"disk_write_bytes":307720192}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12634,"usage":{"cpu_time_ns":10631547416,"resident_bytes":231686144,"physical_bytes":677807976,"peak_physical_bytes":677807976,"disk_read_bytes":49152,"disk_write_bytes":307720192}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12744,"usage":{"cpu_time_ns":10643259916,"resident_bytes":232718336,"physical_bytes":649594728,"peak_physical_bytes":686278504,"disk_read_bytes":49152,"disk_write_bytes":307720192}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12854,"usage":{"cpu_time_ns":10652608916,"resident_bytes":233603072,"physical_bytes":673482600,"peak_physical_bytes":686278504,"disk_read_bytes":49152,"disk_write_bytes":307720192}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12963,"usage":{"cpu_time_ns":11113440875,"resident_bytes":237142016,"physical_bytes":508725096,"peak_physical_bytes":709068648,"disk_read_bytes":49152,"disk_write_bytes":307720192}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13073,"usage":{"cpu_time_ns":11310966750,"resident_bytes":237928448,"physical_bytes":517588840,"peak_physical_bytes":709068648,"disk_read_bytes":49152,"disk_write_bytes":307720192}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13182,"usage":{"cpu_time_ns":11583143708,"resident_bytes":238125056,"physical_bytes":517785448,"peak_physical_bytes":709068648,"disk_read_bytes":49152,"disk_write_bytes":307720192}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13292,"usage":{"cpu_time_ns":12074052916,"resident_bytes":238698496,"physical_bytes":520570728,"peak_physical_bytes":709068648,"disk_read_bytes":49152,"disk_write_bytes":307720192}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13402,"usage":{"cpu_time_ns":12183887500,"resident_bytes":238698496,"physical_bytes":520570728,"peak_physical_bytes":709068648,"disk_read_bytes":49152,"disk_write_bytes":307720192}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13512,"usage":{"cpu_time_ns":12287995458,"resident_bytes":264699904,"physical_bytes":520570728,"peak_physical_bytes":709068648,"disk_read_bytes":53248,"disk_write_bytes":345866240}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13623,"usage":{"cpu_time_ns":12365533041,"resident_bytes":266616832,"physical_bytes":584812392,"peak_physical_bytes":709068648,"disk_read_bytes":53248,"disk_write_bytes":345866240}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13730,"usage":{"cpu_time_ns":12406870416,"resident_bytes":268255232,"physical_bytes":584812392,"peak_physical_bytes":709068648,"disk_read_bytes":53248,"disk_write_bytes":345866240}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13841,"usage":{"cpu_time_ns":12476579583,"resident_bytes":266633216,"physical_bytes":584828776,"peak_physical_bytes":709068648,"disk_read_bytes":53248,"disk_write_bytes":345866240}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13951,"usage":{"cpu_time_ns":12525649333,"resident_bytes":231768064,"physical_bytes":404277072,"peak_physical_bytes":709068648,"disk_read_bytes":53248,"disk_write_bytes":345866240}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14061,"usage":{"cpu_time_ns":12634644083,"resident_bytes":230735872,"physical_bytes":397395792,"peak_physical_bytes":709068648,"disk_read_bytes":53248,"disk_write_bytes":345866240}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14171,"usage":{"cpu_time_ns":12645684541,"resident_bytes":232620032,"physical_bytes":461637456,"peak_physical_bytes":709068648,"disk_read_bytes":53248,"disk_write_bytes":345866240}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14281,"usage":{"cpu_time_ns":12654951833,"resident_bytes":233095168,"physical_bytes":545392464,"peak_physical_bytes":709068648,"disk_read_bytes":53248,"disk_write_bytes":345866240}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14391,"usage":{"cpu_time_ns":12660581375,"resident_bytes":233537536,"physical_bytes":556975952,"peak_physical_bytes":709068648,"disk_read_bytes":53248,"disk_write_bytes":345866240}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14501,"usage":{"cpu_time_ns":12666264500,"resident_bytes":233979904,"physical_bytes":568985424,"peak_physical_bytes":709068648,"disk_read_bytes":53248,"disk_write_bytes":345866240}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14609,"usage":{"cpu_time_ns":12671240625,"resident_bytes":234373120,"physical_bytes":579471184,"peak_physical_bytes":709068648,"disk_read_bytes":53248,"disk_write_bytes":345866240}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14719,"usage":{"cpu_time_ns":12676731250,"resident_bytes":234766336,"physical_bytes":608880464,"peak_physical_bytes":709068648,"disk_read_bytes":53248,"disk_write_bytes":345866240}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14829,"usage":{"cpu_time_ns":13145764291,"resident_bytes":238206976,"physical_bytes":514164584,"peak_physical_bytes":709068648,"disk_read_bytes":53248,"disk_write_bytes":345866240}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14939,"usage":{"cpu_time_ns":13600598416,"resident_bytes":238780416,"physical_bytes":518326120,"peak_physical_bytes":709068648,"disk_read_bytes":53248,"disk_write_bytes":345866240}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":15049,"usage":{"cpu_time_ns":13675123958,"resident_bytes":239763456,"physical_bytes":518358888,"peak_physical_bytes":709068648,"disk_read_bytes":53248,"disk_write_bytes":345866240}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":15159,"usage":{"cpu_time_ns":13781108666,"resident_bytes":262504448,"physical_bytes":523552616,"peak_physical_bytes":709068648,"disk_read_bytes":53248,"disk_write_bytes":372752384}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":15270,"usage":{"cpu_time_ns":13834173625,"resident_bytes":233472000,"physical_bytes":502433616,"peak_physical_bytes":709068648,"disk_read_bytes":53248,"disk_write_bytes":372752384}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":15375,"usage":{"cpu_time_ns":13837911416,"resident_bytes":233472000,"physical_bytes":526190416,"peak_physical_bytes":709068648,"disk_read_bytes":53248,"disk_write_bytes":372752384}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":15485,"usage":{"cpu_time_ns":13847026875,"resident_bytes":234209280,"physical_bytes":576014160,"peak_physical_bytes":709068648,"disk_read_bytes":53248,"disk_write_bytes":372752384}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":15596,"usage":{"cpu_time_ns":14333755000,"resident_bytes":237600768,"physical_bytes":578946896,"peak_physical_bytes":709068648,"disk_read_bytes":53248,"disk_write_bytes":372752384}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":15706,"usage":{"cpu_time_ns":14443734750,"resident_bytes":237600768,"physical_bytes":578946896,"peak_physical_bytes":709068648,"disk_read_bytes":53248,"disk_write_bytes":372752384}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":15816,"usage":{"cpu_time_ns":14541420333,"resident_bytes":219529216,"physical_bytes":299026184,"peak_physical_bytes":709068648,"disk_read_bytes":61440,"disk_write_bytes":387313664}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":15926,"usage":{"cpu_time_ns":14651424000,"resident_bytes":219529216,"physical_bytes":299026184,"peak_physical_bytes":709068648,"disk_read_bytes":61440,"disk_write_bytes":387313664}}
|
||||||
|
{"event":"test_resource_summary","elapsed_ms":16036,"peak":{"cpu_time_ns":14651424000,"resident_bytes":268255232,"physical_bytes":677807976,"peak_physical_bytes":709068648,"disk_read_bytes":61440,"disk_write_bytes":387313664},"error":null}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
running 1 test
|
||||||
|
test engine::metal::deepseek_vision::tests::issue100_full_model_images_cache_and_dspark ... ds4: Metal device Apple M5 Max, 128.00 GiB RAM
|
||||||
|
ds4: Metal 4 tensor API enabled for Tensor kernels
|
||||||
|
ds4: drift-patch flags hc_stable=on norm_unify=on kv_raw_f32=off rope_exp2_log2=off math_safe=off tensor_matmul=on
|
||||||
|
ds4: Metal model views created in 1.695 ms, residency requested in 461.052 ms, warmup 3.497 ms (mapped 82697.67 MiB from offset 5.09 MiB)
|
||||||
|
ds4: Metal mapped mmaped model as 2 overlapping shared buffers
|
||||||
|
ds4: Metal model views created in 0.143 ms, residency requested in 461.793 ms, warmup 0.639 ms (mapped 5711.67 MiB from offset 0.01 MiB)
|
||||||
|
ds4: Metal mapped mmaped model as 3 overlapping shared buffers
|
||||||
|
ds4: Metal model views created in 0.047 ms, residency requested in 833.434 ms, warmup 0.581 ms (mapped 889.64 MiB from offset 0.02 MiB)
|
||||||
|
ds4: Metal mapped mmaped model as 4 overlapping shared buffers
|
||||||
|
red: The image features a solid red square centered on a white background.
|
||||||
|
DSpark drafted=15 accepted=10
|
||||||
|
blue: A solid blue circle centered on a white background.
|
||||||
|
pair: The first shape is red, and the second shape is blue.
|
||||||
|
tool image: The color in the image is blue.
|
||||||
|
issue100: production rendering, image recognition, multi-image/tool input, same-layout image invalidation, disk/warm KV and DSpark target equivalence passed
|
||||||
|
ok
|
||||||
|
|
||||||
|
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 569 filtered out; finished in 15.94s
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
{"event":"test_supervisor_started","pid":37604,"limit_bytes":120259084288,"start_seconds":60.0,"idle_seconds":120.0,"sample_ms":100}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":0,"usage":{"cpu_time_ns":415125,"resident_bytes":180224,"physical_bytes":245952,"peak_physical_bytes":295032,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":101,"usage":{"cpu_time_ns":97345208,"resident_bytes":63045632,"physical_bytes":41222744,"peak_physical_bytes":41222744,"disk_read_bytes":126976,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":212,"usage":{"cpu_time_ns":161737875,"resident_bytes":108445696,"physical_bytes":68830144,"peak_physical_bytes":68830144,"disk_read_bytes":9883648,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":322,"usage":{"cpu_time_ns":271741791,"resident_bytes":108445696,"physical_bytes":68830144,"peak_physical_bytes":68830144,"disk_read_bytes":9883648,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":432,"usage":{"cpu_time_ns":391744500,"resident_bytes":108445696,"physical_bytes":68830144,"peak_physical_bytes":68830144,"disk_read_bytes":9883648,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":542,"usage":{"cpu_time_ns":501772208,"resident_bytes":108445696,"physical_bytes":68830144,"peak_physical_bytes":68830144,"disk_read_bytes":9883648,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":648,"usage":{"cpu_time_ns":599084208,"resident_bytes":109133824,"physical_bytes":187728832,"peak_physical_bytes":187728832,"disk_read_bytes":10391552,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":758,"usage":{"cpu_time_ns":709088500,"resident_bytes":109133824,"physical_bytes":187728832,"peak_physical_bytes":187728832,"disk_read_bytes":10391552,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":868,"usage":{"cpu_time_ns":793179208,"resident_bytes":109133824,"physical_bytes":187728832,"peak_physical_bytes":187728832,"disk_read_bytes":10391552,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":978,"usage":{"cpu_time_ns":803179541,"resident_bytes":109133824,"physical_bytes":187630528,"peak_physical_bytes":187728832,"disk_read_bytes":10391552,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1081,"usage":{"cpu_time_ns":913183000,"resident_bytes":109133824,"physical_bytes":187630528,"peak_physical_bytes":187728832,"disk_read_bytes":10391552,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1191,"usage":{"cpu_time_ns":1024193041,"resident_bytes":109133824,"physical_bytes":187630528,"peak_physical_bytes":187728832,"disk_read_bytes":10391552,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1298,"usage":{"cpu_time_ns":1124198625,"resident_bytes":109133824,"physical_bytes":187630528,"peak_physical_bytes":187728832,"disk_read_bytes":10391552,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1408,"usage":{"cpu_time_ns":1240872791,"resident_bytes":126746624,"physical_bytes":201606752,"peak_physical_bytes":201606752,"disk_read_bytes":10391552,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1518,"usage":{"cpu_time_ns":1347208333,"resident_bytes":132022272,"physical_bytes":206161528,"peak_physical_bytes":206161528,"disk_read_bytes":10461184,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1628,"usage":{"cpu_time_ns":1364817916,"resident_bytes":132825088,"physical_bytes":268961424,"peak_physical_bytes":268961424,"disk_read_bytes":11153408,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1738,"usage":{"cpu_time_ns":1455558833,"resident_bytes":135774208,"physical_bytes":269043344,"peak_physical_bytes":269043344,"disk_read_bytes":11153408,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1848,"usage":{"cpu_time_ns":1480383583,"resident_bytes":136052736,"physical_bytes":270927504,"peak_physical_bytes":270927504,"disk_read_bytes":11153408,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1959,"usage":{"cpu_time_ns":1493076208,"resident_bytes":138084352,"physical_bytes":367691480,"peak_physical_bytes":367691480,"disk_read_bytes":11755520,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2069,"usage":{"cpu_time_ns":1499107416,"resident_bytes":138592256,"physical_bytes":380831448,"peak_physical_bytes":380831448,"disk_read_bytes":11755520,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2179,"usage":{"cpu_time_ns":1504363291,"resident_bytes":139034624,"physical_bytes":392414936,"peak_physical_bytes":392414936,"disk_read_bytes":11755520,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2289,"usage":{"cpu_time_ns":1510003500,"resident_bytes":139526144,"physical_bytes":405522136,"peak_physical_bytes":405522136,"disk_read_bytes":11755520,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2399,"usage":{"cpu_time_ns":1532340166,"resident_bytes":140886016,"physical_bytes":434751192,"peak_physical_bytes":434751192,"disk_read_bytes":11821056,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2510,"usage":{"cpu_time_ns":1641900958,"resident_bytes":140886016,"physical_bytes":434751192,"peak_physical_bytes":434751192,"disk_read_bytes":11821056,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2620,"usage":{"cpu_time_ns":1727359458,"resident_bytes":160661504,"physical_bytes":285099736,"peak_physical_bytes":447645424,"disk_read_bytes":12017664,"disk_write_bytes":25317376}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2730,"usage":{"cpu_time_ns":1785970916,"resident_bytes":161185792,"physical_bytes":318375640,"peak_physical_bytes":447645424,"disk_read_bytes":12017664,"disk_write_bytes":25317376}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2831,"usage":{"cpu_time_ns":1886208291,"resident_bytes":161382400,"physical_bytes":318375640,"peak_physical_bytes":447645424,"disk_read_bytes":12021760,"disk_write_bytes":33705984}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2941,"usage":{"cpu_time_ns":1991190375,"resident_bytes":148144128,"physical_bytes":260867848,"peak_physical_bytes":447645424,"disk_read_bytes":12034048,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3051,"usage":{"cpu_time_ns":2066984541,"resident_bytes":150044672,"physical_bytes":305776392,"peak_physical_bytes":447645424,"disk_read_bytes":12034048,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3161,"usage":{"cpu_time_ns":2074594333,"resident_bytes":150274048,"physical_bytes":373950216,"peak_physical_bytes":447645424,"disk_read_bytes":12034048,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3272,"usage":{"cpu_time_ns":2080296208,"resident_bytes":150765568,"physical_bytes":387057416,"peak_physical_bytes":447645424,"disk_read_bytes":12034048,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3382,"usage":{"cpu_time_ns":2085985041,"resident_bytes":151257088,"physical_bytes":400164616,"peak_physical_bytes":447645424,"disk_read_bytes":12034048,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3492,"usage":{"cpu_time_ns":2091268916,"resident_bytes":151699456,"physical_bytes":411748104,"peak_physical_bytes":447645424,"disk_read_bytes":12034048,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3602,"usage":{"cpu_time_ns":2097277666,"resident_bytes":152190976,"physical_bytes":443942664,"peak_physical_bytes":447645424,"disk_read_bytes":12034048,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3708,"usage":{"cpu_time_ns":2535618166,"resident_bytes":195903488,"physical_bytes":388417360,"peak_physical_bytes":484345632,"disk_read_bytes":12247040,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3814,"usage":{"cpu_time_ns":2681934000,"resident_bytes":205062144,"physical_bytes":482248552,"peak_physical_bytes":484345632,"disk_read_bytes":12247040,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3925,"usage":{"cpu_time_ns":2984151416,"resident_bytes":208437248,"physical_bytes":484689768,"peak_physical_bytes":484689768,"disk_read_bytes":12247040,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4035,"usage":{"cpu_time_ns":3411319916,"resident_bytes":209207296,"physical_bytes":484788072,"peak_physical_bytes":484951912,"disk_read_bytes":12247040,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4145,"usage":{"cpu_time_ns":3521395875,"resident_bytes":209272832,"physical_bytes":484804456,"peak_physical_bytes":484951912,"disk_read_bytes":12247040,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4248,"usage":{"cpu_time_ns":3615762833,"resident_bytes":203587584,"physical_bytes":366987112,"peak_physical_bytes":497485696,"disk_read_bytes":12259328,"disk_write_bytes":77606912}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4358,"usage":{"cpu_time_ns":3701139166,"resident_bytes":205537280,"physical_bytes":431228776,"peak_physical_bytes":497485696,"disk_read_bytes":12259328,"disk_write_bytes":77606912}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4464,"usage":{"cpu_time_ns":3708145125,"resident_bytes":207290368,"physical_bytes":493815656,"peak_physical_bytes":497485696,"disk_read_bytes":12259328,"disk_write_bytes":77606912}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4574,"usage":{"cpu_time_ns":3714040458,"resident_bytes":207781888,"physical_bytes":512624488,"peak_physical_bytes":512624488,"disk_read_bytes":12259328,"disk_write_bytes":77606912}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4685,"usage":{"cpu_time_ns":3719232750,"resident_bytes":208224256,"physical_bytes":524683112,"peak_physical_bytes":524683112,"disk_read_bytes":12259328,"disk_write_bytes":77606912}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4787,"usage":{"cpu_time_ns":3724906291,"resident_bytes":208666624,"physical_bytes":536266600,"peak_physical_bytes":536266600,"disk_read_bytes":12259328,"disk_write_bytes":77606912}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4896,"usage":{"cpu_time_ns":3731142000,"resident_bytes":209158144,"physical_bytes":549373800,"peak_physical_bytes":549373800,"disk_read_bytes":12259328,"disk_write_bytes":77606912}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4999,"usage":{"cpu_time_ns":3942764875,"resident_bytes":211648512,"physical_bytes":477726616,"peak_physical_bytes":601360232,"disk_read_bytes":12259328,"disk_write_bytes":77606912}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5110,"usage":{"cpu_time_ns":4171788250,"resident_bytes":213417984,"physical_bytes":489736088,"peak_physical_bytes":601360232,"disk_read_bytes":12259328,"disk_write_bytes":77606912}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5221,"usage":{"cpu_time_ns":4603727416,"resident_bytes":214007808,"physical_bytes":489785240,"peak_physical_bytes":601360232,"disk_read_bytes":12259328,"disk_write_bytes":77606912}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5331,"usage":{"cpu_time_ns":5030277958,"resident_bytes":214073344,"physical_bytes":489834392,"peak_physical_bytes":601360232,"disk_read_bytes":12259328,"disk_write_bytes":77606912}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5441,"usage":{"cpu_time_ns":5125664208,"resident_bytes":214597632,"physical_bytes":489850776,"peak_physical_bytes":601360232,"disk_read_bytes":12259328,"disk_write_bytes":77606912}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5551,"usage":{"cpu_time_ns":5227421291,"resident_bytes":229031936,"physical_bytes":489850776,"peak_physical_bytes":601360232,"disk_read_bytes":12279808,"disk_write_bytes":104198144}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5661,"usage":{"cpu_time_ns":5311782708,"resident_bytes":230948864,"physical_bytes":554092440,"peak_physical_bytes":601360232,"disk_read_bytes":12279808,"disk_write_bytes":104198144}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5764,"usage":{"cpu_time_ns":5317321833,"resident_bytes":229294080,"physical_bytes":504416152,"peak_physical_bytes":601360232,"disk_read_bytes":12279808,"disk_write_bytes":104198144}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5874,"usage":{"cpu_time_ns":5322005333,"resident_bytes":229294080,"physical_bytes":504416152,"peak_physical_bytes":601360232,"disk_read_bytes":12279808,"disk_write_bytes":104198144}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5975,"usage":{"cpu_time_ns":5410758875,"resident_bytes":229343232,"physical_bytes":504448920,"peak_physical_bytes":601360232,"disk_read_bytes":12279808,"disk_write_bytes":104198144}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6085,"usage":{"cpu_time_ns":5802163208,"resident_bytes":229965824,"physical_bytes":492701592,"peak_physical_bytes":601360232,"disk_read_bytes":12279808,"disk_write_bytes":104198144}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6195,"usage":{"cpu_time_ns":5911762250,"resident_bytes":230850560,"physical_bytes":492701592,"peak_physical_bytes":601360232,"disk_read_bytes":12288000,"disk_write_bytes":120975360}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6305,"usage":{"cpu_time_ns":6013668791,"resident_bytes":222904320,"physical_bytes":385861480,"peak_physical_bytes":601360232,"disk_read_bytes":38891520,"disk_write_bytes":132325376}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6415,"usage":{"cpu_time_ns":6090444875,"resident_bytes":224690176,"physical_bytes":449972072,"peak_physical_bytes":601360232,"disk_read_bytes":38891520,"disk_write_bytes":132325376}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6525,"usage":{"cpu_time_ns":6096262625,"resident_bytes":224706560,"physical_bytes":410306408,"peak_physical_bytes":601360232,"disk_read_bytes":38891520,"disk_write_bytes":132325376}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6636,"usage":{"cpu_time_ns":6101809250,"resident_bytes":224706560,"physical_bytes":433276776,"peak_physical_bytes":601360232,"disk_read_bytes":38891520,"disk_write_bytes":132325376}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6746,"usage":{"cpu_time_ns":6446828208,"resident_bytes":228212736,"physical_bytes":448989056,"peak_physical_bytes":601360232,"disk_read_bytes":38891520,"disk_write_bytes":132325376}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6856,"usage":{"cpu_time_ns":6607495666,"resident_bytes":228737024,"physical_bytes":451725184,"peak_physical_bytes":601360232,"disk_read_bytes":38891520,"disk_write_bytes":132325376}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6966,"usage":{"cpu_time_ns":6707505833,"resident_bytes":230162432,"physical_bytes":451741568,"peak_physical_bytes":601360232,"disk_read_bytes":38912000,"disk_write_bytes":160452608}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7077,"usage":{"cpu_time_ns":6812796416,"resident_bytes":232030208,"physical_bytes":515966848,"peak_physical_bytes":601360232,"disk_read_bytes":38912000,"disk_write_bytes":160452608}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7187,"usage":{"cpu_time_ns":6828540666,"resident_bytes":233668608,"physical_bytes":515966848,"peak_physical_bytes":601360232,"disk_read_bytes":38912000,"disk_write_bytes":160452608}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7297,"usage":{"cpu_time_ns":6920792875,"resident_bytes":232062976,"physical_bytes":515999616,"peak_physical_bytes":601360232,"disk_read_bytes":38912000,"disk_write_bytes":160452608}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7400,"usage":{"cpu_time_ns":6927874916,"resident_bytes":236322816,"physical_bytes":575309696,"peak_physical_bytes":601360232,"disk_read_bytes":38912000,"disk_write_bytes":160452608}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7510,"usage":{"cpu_time_ns":6936864125,"resident_bytes":237092864,"physical_bytes":581633920,"peak_physical_bytes":601360232,"disk_read_bytes":38912000,"disk_write_bytes":160452608}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7620,"usage":{"cpu_time_ns":6946076750,"resident_bytes":237846528,"physical_bytes":582387584,"peak_physical_bytes":601360232,"disk_read_bytes":38912000,"disk_write_bytes":160452608}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7729,"usage":{"cpu_time_ns":6954095666,"resident_bytes":238501888,"physical_bytes":583042944,"peak_physical_bytes":601360232,"disk_read_bytes":38912000,"disk_write_bytes":160452608}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7839,"usage":{"cpu_time_ns":6962969291,"resident_bytes":239222784,"physical_bytes":583370624,"peak_physical_bytes":601360232,"disk_read_bytes":38912000,"disk_write_bytes":160452608}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7942,"usage":{"cpu_time_ns":6974284583,"resident_bytes":239845376,"physical_bytes":584304512,"peak_physical_bytes":601360232,"disk_read_bytes":38912000,"disk_write_bytes":160452608}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8052,"usage":{"cpu_time_ns":7409587958,"resident_bytes":239894528,"physical_bytes":461539200,"peak_physical_bytes":601360232,"disk_read_bytes":38912000,"disk_write_bytes":160452608}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8162,"usage":{"cpu_time_ns":7519416625,"resident_bytes":239894528,"physical_bytes":461539200,"peak_physical_bytes":601360232,"disk_read_bytes":38912000,"disk_write_bytes":160452608}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8264,"usage":{"cpu_time_ns":7616060750,"resident_bytes":252198912,"physical_bytes":461539200,"peak_physical_bytes":601360232,"disk_read_bytes":38928384,"disk_write_bytes":200904704}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8374,"usage":{"cpu_time_ns":7691425791,"resident_bytes":254099456,"physical_bytes":525764480,"peak_physical_bytes":601360232,"disk_read_bytes":38928384,"disk_write_bytes":200904704}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8481,"usage":{"cpu_time_ns":7731651416,"resident_bytes":255737856,"physical_bytes":525764480,"peak_physical_bytes":601360232,"disk_read_bytes":38928384,"disk_write_bytes":200904704}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8591,"usage":{"cpu_time_ns":7799902208,"resident_bytes":254115840,"physical_bytes":525780864,"peak_physical_bytes":601360232,"disk_read_bytes":38928384,"disk_write_bytes":200904704}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8699,"usage":{"cpu_time_ns":7848593166,"resident_bytes":255754240,"physical_bytes":525780864,"peak_physical_bytes":601360232,"disk_read_bytes":38928384,"disk_write_bytes":200904704}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8809,"usage":{"cpu_time_ns":7908017625,"resident_bytes":254099456,"physical_bytes":525764480,"peak_physical_bytes":601360232,"disk_read_bytes":38928384,"disk_write_bytes":200904704}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8919,"usage":{"cpu_time_ns":7914622000,"resident_bytes":254410752,"physical_bytes":480331648,"peak_physical_bytes":601360232,"disk_read_bytes":38928384,"disk_write_bytes":200904704}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9024,"usage":{"cpu_time_ns":7918761250,"resident_bytes":254410752,"physical_bytes":480331648,"peak_physical_bytes":601360232,"disk_read_bytes":38928384,"disk_write_bytes":200904704}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9131,"usage":{"cpu_time_ns":7928645208,"resident_bytes":254722048,"physical_bytes":582125440,"peak_physical_bytes":601360232,"disk_read_bytes":38928384,"disk_write_bytes":200904704}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9241,"usage":{"cpu_time_ns":7937778500,"resident_bytes":255492096,"physical_bytes":582879104,"peak_physical_bytes":601360232,"disk_read_bytes":38928384,"disk_write_bytes":200904704}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9351,"usage":{"cpu_time_ns":7945633041,"resident_bytes":256147456,"physical_bytes":583534464,"peak_physical_bytes":601360232,"disk_read_bytes":38928384,"disk_write_bytes":200904704}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9461,"usage":{"cpu_time_ns":7955466125,"resident_bytes":256966656,"physical_bytes":584353664,"peak_physical_bytes":601360232,"disk_read_bytes":38928384,"disk_write_bytes":200904704}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9564,"usage":{"cpu_time_ns":7963225708,"resident_bytes":257622016,"physical_bytes":585009024,"peak_physical_bytes":601360232,"disk_read_bytes":38928384,"disk_write_bytes":200904704}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9665,"usage":{"cpu_time_ns":8066724208,"resident_bytes":257966080,"physical_bytes":467289984,"peak_physical_bytes":601360232,"disk_read_bytes":38928384,"disk_write_bytes":200904704}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9775,"usage":{"cpu_time_ns":8474248208,"resident_bytes":257966080,"physical_bytes":460474240,"peak_physical_bytes":601360232,"disk_read_bytes":38928384,"disk_write_bytes":200904704}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9885,"usage":{"cpu_time_ns":8583266833,"resident_bytes":270729216,"physical_bytes":460474240,"peak_physical_bytes":601360232,"disk_read_bytes":38948864,"disk_write_bytes":251236352}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9995,"usage":{"cpu_time_ns":8688466750,"resident_bytes":223100928,"physical_bytes":358156136,"peak_physical_bytes":601360232,"disk_read_bytes":38948864,"disk_write_bytes":254836736}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10098,"usage":{"cpu_time_ns":8711523041,"resident_bytes":222429184,"physical_bytes":411535208,"peak_physical_bytes":601360232,"disk_read_bytes":38948864,"disk_write_bytes":254836736}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10201,"usage":{"cpu_time_ns":8721726416,"resident_bytes":222806016,"physical_bytes":485017448,"peak_physical_bytes":601360232,"disk_read_bytes":38948864,"disk_write_bytes":254836736}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10311,"usage":{"cpu_time_ns":8727381958,"resident_bytes":223248384,"physical_bytes":496600936,"peak_physical_bytes":601360232,"disk_read_bytes":38948864,"disk_write_bytes":254836736}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10414,"usage":{"cpu_time_ns":8732902750,"resident_bytes":223690752,"physical_bytes":508610408,"peak_physical_bytes":601360232,"disk_read_bytes":38948864,"disk_write_bytes":254836736}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10515,"usage":{"cpu_time_ns":8737633791,"resident_bytes":224083968,"physical_bytes":519096168,"peak_physical_bytes":601360232,"disk_read_bytes":38948864,"disk_write_bytes":254836736}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10626,"usage":{"cpu_time_ns":8743981500,"resident_bytes":224575488,"physical_bytes":551126888,"peak_physical_bytes":601360232,"disk_read_bytes":38948864,"disk_write_bytes":254836736}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10736,"usage":{"cpu_time_ns":9179050708,"resident_bytes":228032512,"physical_bytes":500074392,"peak_physical_bytes":601360232,"disk_read_bytes":38948864,"disk_write_bytes":254836736}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10846,"usage":{"cpu_time_ns":9607171375,"resident_bytes":228589568,"physical_bytes":504252312,"peak_physical_bytes":601360232,"disk_read_bytes":38948864,"disk_write_bytes":254836736}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10948,"usage":{"cpu_time_ns":9654308083,"resident_bytes":229146624,"physical_bytes":504285080,"peak_physical_bytes":601360232,"disk_read_bytes":38948864,"disk_write_bytes":254836736}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11058,"usage":{"cpu_time_ns":9764272291,"resident_bytes":232669184,"physical_bytes":504285080,"peak_physical_bytes":601360232,"disk_read_bytes":38948864,"disk_write_bytes":254836736}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11164,"usage":{"cpu_time_ns":9865513916,"resident_bytes":223969280,"physical_bytes":395118440,"peak_physical_bytes":601360232,"disk_read_bytes":38948864,"disk_write_bytes":281128960}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11274,"usage":{"cpu_time_ns":9900431958,"resident_bytes":224722944,"physical_bytes":449759080,"peak_physical_bytes":601360232,"disk_read_bytes":38948864,"disk_write_bytes":281128960}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11381,"usage":{"cpu_time_ns":9980970333,"resident_bytes":226361344,"physical_bytes":449759080,"peak_physical_bytes":601360232,"disk_read_bytes":38948864,"disk_write_bytes":281128960}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11491,"usage":{"cpu_time_ns":10008942166,"resident_bytes":224755712,"physical_bytes":449791848,"peak_physical_bytes":601360232,"disk_read_bytes":38948864,"disk_write_bytes":281128960}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11598,"usage":{"cpu_time_ns":10025156833,"resident_bytes":227459072,"physical_bytes":643467112,"peak_physical_bytes":643467112,"disk_read_bytes":39014400,"disk_write_bytes":281128960}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11708,"usage":{"cpu_time_ns":10035450875,"resident_bytes":228474880,"physical_bytes":653100904,"peak_physical_bytes":653100904,"disk_read_bytes":39014400,"disk_write_bytes":281128960}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11818,"usage":{"cpu_time_ns":10046117208,"resident_bytes":229523456,"physical_bytes":663193448,"peak_physical_bytes":663193448,"disk_read_bytes":39014400,"disk_write_bytes":281128960}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11928,"usage":{"cpu_time_ns":10056543416,"resident_bytes":230539264,"physical_bytes":672827240,"peak_physical_bytes":672827240,"disk_read_bytes":39014400,"disk_write_bytes":281128960}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12034,"usage":{"cpu_time_ns":10067115291,"resident_bytes":231587840,"physical_bytes":682919784,"peak_physical_bytes":682919784,"disk_read_bytes":39014400,"disk_write_bytes":281128960}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12144,"usage":{"cpu_time_ns":10077606875,"resident_bytes":232603648,"physical_bytes":692553576,"peak_physical_bytes":692553576,"disk_read_bytes":39014400,"disk_write_bytes":281128960}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12254,"usage":{"cpu_time_ns":10201624083,"resident_bytes":234848256,"physical_bytes":505087896,"peak_physical_bytes":747063144,"disk_read_bytes":39014400,"disk_write_bytes":281128960}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12364,"usage":{"cpu_time_ns":10523251625,"resident_bytes":237207552,"physical_bytes":517080984,"peak_physical_bytes":747063144,"disk_read_bytes":39047168,"disk_write_bytes":281128960}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12475,"usage":{"cpu_time_ns":10950040875,"resident_bytes":237273088,"physical_bytes":517130136,"peak_physical_bytes":747063144,"disk_read_bytes":39047168,"disk_write_bytes":281128960}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12581,"usage":{"cpu_time_ns":11380337916,"resident_bytes":237813760,"physical_bytes":519882648,"peak_physical_bytes":747063144,"disk_read_bytes":39047168,"disk_write_bytes":281128960}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12691,"usage":{"cpu_time_ns":11490253666,"resident_bytes":237813760,"physical_bytes":519882648,"peak_physical_bytes":747063144,"disk_read_bytes":39047168,"disk_write_bytes":281128960}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12798,"usage":{"cpu_time_ns":11591107666,"resident_bytes":263815168,"physical_bytes":519882648,"peak_physical_bytes":747063144,"disk_read_bytes":39051264,"disk_write_bytes":319275008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12908,"usage":{"cpu_time_ns":11666141750,"resident_bytes":265715712,"physical_bytes":584107928,"peak_physical_bytes":747063144,"disk_read_bytes":39051264,"disk_write_bytes":319275008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13018,"usage":{"cpu_time_ns":11710186333,"resident_bytes":267354112,"physical_bytes":584107928,"peak_physical_bytes":747063144,"disk_read_bytes":39051264,"disk_write_bytes":319275008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13120,"usage":{"cpu_time_ns":11774231000,"resident_bytes":265748480,"physical_bytes":584140696,"peak_physical_bytes":747063144,"disk_read_bytes":39051264,"disk_write_bytes":319275008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13230,"usage":{"cpu_time_ns":11819233625,"resident_bytes":230899712,"physical_bytes":403802032,"peak_physical_bytes":747063144,"disk_read_bytes":39051264,"disk_write_bytes":319275008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13340,"usage":{"cpu_time_ns":11902769000,"resident_bytes":231768064,"physical_bytes":461064112,"peak_physical_bytes":747063144,"disk_read_bytes":39051264,"disk_write_bytes":319275008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13450,"usage":{"cpu_time_ns":11911332291,"resident_bytes":231948288,"physical_bytes":537020336,"peak_physical_bytes":747063144,"disk_read_bytes":39051264,"disk_write_bytes":319275008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13560,"usage":{"cpu_time_ns":11917327625,"resident_bytes":232390656,"physical_bytes":548603824,"peak_physical_bytes":747063144,"disk_read_bytes":39051264,"disk_write_bytes":319275008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13671,"usage":{"cpu_time_ns":11923259083,"resident_bytes":232833024,"physical_bytes":560613296,"peak_physical_bytes":747063144,"disk_read_bytes":39051264,"disk_write_bytes":319275008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13781,"usage":{"cpu_time_ns":11929796458,"resident_bytes":233226240,"physical_bytes":541575088,"peak_physical_bytes":747063144,"disk_read_bytes":39051264,"disk_write_bytes":319275008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13891,"usage":{"cpu_time_ns":11935700958,"resident_bytes":233668608,"physical_bytes":541149104,"peak_physical_bytes":747063144,"disk_read_bytes":39051264,"disk_write_bytes":319275008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14001,"usage":{"cpu_time_ns":11945452541,"resident_bytes":234438656,"physical_bytes":595691440,"peak_physical_bytes":747063144,"disk_read_bytes":39051264,"disk_write_bytes":319275008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14111,"usage":{"cpu_time_ns":12379474208,"resident_bytes":237355008,"physical_bytes":517818336,"peak_physical_bytes":747063144,"disk_read_bytes":39051264,"disk_write_bytes":319275008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14221,"usage":{"cpu_time_ns":12807909208,"resident_bytes":238354432,"physical_bytes":517834720,"peak_physical_bytes":747063144,"disk_read_bytes":39051264,"disk_write_bytes":319275008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14331,"usage":{"cpu_time_ns":12912712875,"resident_bytes":238895104,"physical_bytes":517834720,"peak_physical_bytes":747063144,"disk_read_bytes":39051264,"disk_write_bytes":319275008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14441,"usage":{"cpu_time_ns":12979513958,"resident_bytes":232620032,"physical_bytes":496813952,"peak_physical_bytes":747063144,"disk_read_bytes":39075840,"disk_write_bytes":346161152}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14548,"usage":{"cpu_time_ns":12983783458,"resident_bytes":232620032,"physical_bytes":520570752,"peak_physical_bytes":747063144,"disk_read_bytes":39075840,"disk_write_bytes":346161152}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14658,"usage":{"cpu_time_ns":12992564791,"resident_bytes":233357312,"physical_bytes":562431872,"peak_physical_bytes":747063144,"disk_read_bytes":39075840,"disk_write_bytes":346161152}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14768,"usage":{"cpu_time_ns":13433878916,"resident_bytes":236748800,"physical_bytes":578373528,"peak_physical_bytes":747063144,"disk_read_bytes":39075840,"disk_write_bytes":346161152}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14878,"usage":{"cpu_time_ns":13543829166,"resident_bytes":236748800,"physical_bytes":578373528,"peak_physical_bytes":747063144,"disk_read_bytes":39075840,"disk_write_bytes":346161152}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14981,"usage":{"cpu_time_ns":13631951291,"resident_bytes":218923008,"physical_bytes":298518352,"peak_physical_bytes":747063144,"disk_read_bytes":39084032,"disk_write_bytes":360722432}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":15091,"usage":{"cpu_time_ns":13750884625,"resident_bytes":217546752,"physical_bytes":295946064,"peak_physical_bytes":747063144,"disk_read_bytes":39084032,"disk_write_bytes":360722432}}
|
||||||
|
{"event":"test_resource_summary","elapsed_ms":15199,"peak":{"cpu_time_ns":13750884625,"resident_bytes":270729216,"physical_bytes":692553576,"peak_physical_bytes":747063144,"disk_read_bytes":39084032,"disk_write_bytes":360722432},"error":null}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
running 1 test
|
||||||
|
test engine::metal::deepseek_vision::tests::issue100_full_model_images_cache_and_dspark ... ds4: Metal device Apple M5 Max, 128.00 GiB RAM
|
||||||
|
ds4: Metal 4 tensor API enabled for Tensor kernels
|
||||||
|
ds4: drift-patch flags hc_stable=on norm_unify=on kv_raw_f32=off rope_exp2_log2=off math_safe=off tensor_matmul=on
|
||||||
|
ds4: Metal model views created in 1.707 ms, residency requested in 460.920 ms, warmup 3.718 ms (mapped 82697.67 MiB from offset 5.09 MiB)
|
||||||
|
ds4: Metal mapped mmaped model as 2 overlapping shared buffers
|
||||||
|
ds4: Metal model views created in 0.118 ms, residency requested in 771.569 ms, warmup 0.756 ms (mapped 5711.67 MiB from offset 0.01 MiB)
|
||||||
|
ds4: Metal mapped mmaped model as 3 overlapping shared buffers
|
||||||
|
red: The image features a solid red square centered on a white background.
|
||||||
|
DSpark drafted=15 accepted=10
|
||||||
|
blue: A solid blue circle centered on a white background.
|
||||||
|
pair: The first shape is red, and the second shape is blue.
|
||||||
|
tool image: The color in the image is blue.
|
||||||
|
issue100: production rendering, image recognition, multi-image/tool input, same-layout image invalidation, disk/warm KV and DSpark target equivalence passed
|
||||||
|
ok
|
||||||
|
|
||||||
|
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 569 filtered out; finished in 15.10s
|
||||||
@@ -0,0 +1,293 @@
|
|||||||
|
{"event":"test_supervisor_started","pid":37684,"limit_bytes":120259084288,"start_seconds":60.0,"idle_seconds":120.0,"sample_ms":100}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":0,"usage":{"cpu_time_ns":172458,"resident_bytes":671744,"physical_bytes":196680,"peak_physical_bytes":213064,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":110,"usage":{"cpu_time_ns":79014291,"resident_bytes":60178432,"physical_bytes":38978136,"peak_physical_bytes":38978136,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":215,"usage":{"cpu_time_ns":165433500,"resident_bytes":129089536,"physical_bytes":83756592,"peak_physical_bytes":83756592,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":325,"usage":{"cpu_time_ns":261924375,"resident_bytes":134348800,"physical_bytes":269092472,"peak_physical_bytes":269092472,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":436,"usage":{"cpu_time_ns":275562625,"resident_bytes":137035776,"physical_bytes":269158008,"peak_physical_bytes":269158008,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":546,"usage":{"cpu_time_ns":381450875,"resident_bytes":137330688,"physical_bytes":271042144,"peak_physical_bytes":335267448,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":656,"usage":{"cpu_time_ns":392603041,"resident_bytes":146014208,"physical_bytes":286328488,"peak_physical_bytes":335267448,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":756,"usage":{"cpu_time_ns":626728583,"resident_bytes":1249771520,"physical_bytes":3413428696,"peak_physical_bytes":3413281240,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":865,"usage":{"cpu_time_ns":847348666,"resident_bytes":2936602624,"physical_bytes":4638740080,"peak_physical_bytes":4638740080,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":976,"usage":{"cpu_time_ns":1178879833,"resident_bytes":4585914368,"physical_bytes":6545969976,"peak_physical_bytes":6545658680,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1083,"usage":{"cpu_time_ns":1349362625,"resident_bytes":5337268224,"physical_bytes":8941491528,"peak_physical_bytes":8941491528,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1193,"usage":{"cpu_time_ns":1641163083,"resident_bytes":7489110016,"physical_bytes":8944147272,"peak_physical_bytes":8944147272,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1304,"usage":{"cpu_time_ns":1884554166,"resident_bytes":9855336448,"physical_bytes":10076299784,"peak_physical_bytes":10076021256,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1414,"usage":{"cpu_time_ns":2087453458,"resident_bytes":10206986240,"physical_bytes":13239427856,"peak_physical_bytes":13239427856,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1524,"usage":{"cpu_time_ns":2372938375,"resident_bytes":12270059520,"physical_bytes":13244098760,"peak_physical_bytes":13244098760,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1632,"usage":{"cpu_time_ns":2593712208,"resident_bytes":13402570752,"physical_bytes":17385925648,"peak_physical_bytes":17385581584,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1742,"usage":{"cpu_time_ns":2827094500,"resident_bytes":15023489024,"physical_bytes":17537298576,"peak_physical_bytes":17537298576,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1849,"usage":{"cpu_time_ns":3100383125,"resident_bytes":17033723904,"physical_bytes":17541035568,"peak_physical_bytes":17541035568,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1959,"usage":{"cpu_time_ns":3267652333,"resident_bytes":17762811904,"physical_bytes":21469231192,"peak_physical_bytes":21469231192,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2066,"usage":{"cpu_time_ns":3300018583,"resident_bytes":17762811904,"physical_bytes":21831710808,"peak_physical_bytes":21831710808,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2166,"usage":{"cpu_time_ns":3379156041,"resident_bytes":18569756672,"physical_bytes":21833661080,"peak_physical_bytes":21833661080,"disk_read_bytes":151552,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2272,"usage":{"cpu_time_ns":3662131166,"resident_bytes":20141162496,"physical_bytes":21837053696,"peak_physical_bytes":21837086464,"disk_read_bytes":495616,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2382,"usage":{"cpu_time_ns":3852920083,"resident_bytes":21920727040,"physical_bytes":22236775488,"peak_physical_bytes":22236775488,"disk_read_bytes":675840,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2484,"usage":{"cpu_time_ns":3967280583,"resident_bytes":22012739584,"physical_bytes":23021487216,"peak_physical_bytes":23021323376,"disk_read_bytes":675840,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2584,"usage":{"cpu_time_ns":3983733041,"resident_bytes":22012788736,"physical_bytes":23523312752,"peak_physical_bytes":23523312752,"disk_read_bytes":675840,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2694,"usage":{"cpu_time_ns":4016687291,"resident_bytes":22011412480,"physical_bytes":24661623920,"peak_physical_bytes":24661443696,"disk_read_bytes":675840,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2800,"usage":{"cpu_time_ns":4041146625,"resident_bytes":22011412480,"physical_bytes":25399985264,"peak_physical_bytes":25399985264,"disk_read_bytes":675840,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2901,"usage":{"cpu_time_ns":4055634333,"resident_bytes":22010118144,"physical_bytes":25707185264,"peak_physical_bytes":25707185264,"disk_read_bytes":675840,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3011,"usage":{"cpu_time_ns":4107557750,"resident_bytes":22010118144,"physical_bytes":26129106032,"peak_physical_bytes":26129106032,"disk_read_bytes":675840,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3115,"usage":{"cpu_time_ns":4365355458,"resident_bytes":24474632192,"physical_bytes":26132974432,"peak_physical_bytes":26132974432,"disk_read_bytes":1003520,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3225,"usage":{"cpu_time_ns":4619785166,"resident_bytes":25976422400,"physical_bytes":26304810952,"peak_physical_bytes":26304810952,"disk_read_bytes":1167360,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3331,"usage":{"cpu_time_ns":4690986541,"resident_bytes":26662043648,"physical_bytes":26988106176,"peak_physical_bytes":26988106176,"disk_read_bytes":1531904,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3432,"usage":{"cpu_time_ns":4811608291,"resident_bytes":26904952832,"physical_bytes":27541475944,"peak_physical_bytes":27541475944,"disk_read_bytes":1613824,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3533,"usage":{"cpu_time_ns":4841416375,"resident_bytes":26904035328,"physical_bytes":28270252648,"peak_physical_bytes":28270252648,"disk_read_bytes":1613824,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3633,"usage":{"cpu_time_ns":4863742666,"resident_bytes":26901102592,"physical_bytes":29019198056,"peak_physical_bytes":29019198056,"disk_read_bytes":1613824,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3736,"usage":{"cpu_time_ns":4903229916,"resident_bytes":26901037056,"physical_bytes":29393785448,"peak_physical_bytes":29393785448,"disk_read_bytes":1613824,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3841,"usage":{"cpu_time_ns":5122084916,"resident_bytes":29138714624,"physical_bytes":29483505864,"peak_physical_bytes":29483194568,"disk_read_bytes":2662400,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3951,"usage":{"cpu_time_ns":5374999500,"resident_bytes":29573578752,"physical_bytes":29910047256,"peak_physical_bytes":29910047256,"disk_read_bytes":3551232,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4061,"usage":{"cpu_time_ns":5577649916,"resident_bytes":29601906688,"physical_bytes":29939472944,"peak_physical_bytes":29939505712,"disk_read_bytes":4763648,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4166,"usage":{"cpu_time_ns":5777629208,"resident_bytes":29649616896,"physical_bytes":29991836256,"peak_physical_bytes":29991869024,"disk_read_bytes":26390528,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4277,"usage":{"cpu_time_ns":6085482000,"resident_bytes":29727326208,"physical_bytes":30072363664,"peak_physical_bytes":30072363664,"disk_read_bytes":65073152,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4382,"usage":{"cpu_time_ns":6389288875,"resident_bytes":29802315776,"physical_bytes":30147336896,"peak_physical_bytes":30147353280,"disk_read_bytes":154578944,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4492,"usage":{"cpu_time_ns":6722753375,"resident_bytes":29852008448,"physical_bytes":30199536344,"peak_physical_bytes":30199536344,"disk_read_bytes":184774656,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4599,"usage":{"cpu_time_ns":7061638375,"resident_bytes":29880516608,"physical_bytes":30231599856,"peak_physical_bytes":30231616240,"disk_read_bytes":230371328,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4702,"usage":{"cpu_time_ns":7353204083,"resident_bytes":29944348672,"physical_bytes":30305032992,"peak_physical_bytes":30305032992,"disk_read_bytes":279506944,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4807,"usage":{"cpu_time_ns":7656767625,"resident_bytes":30010556416,"physical_bytes":30384970576,"peak_physical_bytes":30384970576,"disk_read_bytes":332034048,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":4918,"usage":{"cpu_time_ns":7841943791,"resident_bytes":30041243648,"physical_bytes":30414019432,"peak_physical_bytes":30414019432,"disk_read_bytes":333590528,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5028,"usage":{"cpu_time_ns":7945233875,"resident_bytes":30048305152,"physical_bytes":30414297960,"peak_physical_bytes":30414297960,"disk_read_bytes":333590528,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5138,"usage":{"cpu_time_ns":8037639833,"resident_bytes":30076436480,"physical_bytes":30277819264,"peak_physical_bytes":30426897280,"disk_read_bytes":345223168,"disk_write_bytes":25317376}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5248,"usage":{"cpu_time_ns":8166172708,"resident_bytes":30076485632,"physical_bytes":30311603072,"peak_physical_bytes":30426897280,"disk_read_bytes":362643456,"disk_write_bytes":25317376}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5358,"usage":{"cpu_time_ns":8326059375,"resident_bytes":30077042688,"physical_bytes":30312242048,"peak_physical_bytes":30426897280,"disk_read_bytes":365887488,"disk_write_bytes":25317376}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5465,"usage":{"cpu_time_ns":8452181750,"resident_bytes":30077091840,"physical_bytes":30312258432,"peak_physical_bytes":30426897280,"disk_read_bytes":366034944,"disk_write_bytes":25317376}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5575,"usage":{"cpu_time_ns":8561194500,"resident_bytes":30077419520,"physical_bytes":30312258432,"peak_physical_bytes":30426897280,"disk_read_bytes":366034944,"disk_write_bytes":33705984}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5686,"usage":{"cpu_time_ns":8666788041,"resident_bytes":30070013952,"physical_bytes":30267005848,"peak_physical_bytes":30426897280,"disk_read_bytes":367099904,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5796,"usage":{"cpu_time_ns":8775923125,"resident_bytes":30085627904,"physical_bytes":30281505688,"peak_physical_bytes":30426897280,"disk_read_bytes":392626176,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":5906,"usage":{"cpu_time_ns":8814061958,"resident_bytes":30083891200,"physical_bytes":30317370288,"peak_physical_bytes":30426897280,"disk_read_bytes":1296498688,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6016,"usage":{"cpu_time_ns":8819137750,"resident_bytes":30083891200,"physical_bytes":30317370288,"peak_physical_bytes":30426897280,"disk_read_bytes":1325469696,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6126,"usage":{"cpu_time_ns":8991600500,"resident_bytes":30074470400,"physical_bytes":30360017840,"peak_physical_bytes":30426897280,"disk_read_bytes":1337217024,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6237,"usage":{"cpu_time_ns":9259636125,"resident_bytes":30074667008,"physical_bytes":30365260720,"peak_physical_bytes":30426897280,"disk_read_bytes":1339346944,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6347,"usage":{"cpu_time_ns":9522133791,"resident_bytes":30074863616,"physical_bytes":30370503600,"peak_physical_bytes":30426897280,"disk_read_bytes":1393807360,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6455,"usage":{"cpu_time_ns":9758080750,"resident_bytes":30075076608,"physical_bytes":30375746480,"peak_physical_bytes":30426897280,"disk_read_bytes":1394642944,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6565,"usage":{"cpu_time_ns":10011188666,"resident_bytes":30075273216,"physical_bytes":30380858288,"peak_physical_bytes":30426897280,"disk_read_bytes":1394642944,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6675,"usage":{"cpu_time_ns":10307665458,"resident_bytes":30075420672,"physical_bytes":30384954288,"peak_physical_bytes":30426897280,"disk_read_bytes":1394642944,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6782,"usage":{"cpu_time_ns":10580451791,"resident_bytes":30075568128,"physical_bytes":30388673456,"peak_physical_bytes":30426897280,"disk_read_bytes":1394823168,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":6892,"usage":{"cpu_time_ns":10896423833,"resident_bytes":30075715584,"physical_bytes":30392818608,"peak_physical_bytes":30426897280,"disk_read_bytes":1395314688,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7001,"usage":{"cpu_time_ns":11164930083,"resident_bytes":30075912192,"physical_bytes":30398061488,"peak_physical_bytes":30426897280,"disk_read_bytes":1395970048,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7111,"usage":{"cpu_time_ns":11499992666,"resident_bytes":30076059648,"physical_bytes":30401797040,"peak_physical_bytes":30426897280,"disk_read_bytes":1396494336,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7221,"usage":{"cpu_time_ns":11790385083,"resident_bytes":30076207104,"physical_bytes":30405942192,"peak_physical_bytes":30426897280,"disk_read_bytes":1396969472,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7331,"usage":{"cpu_time_ns":12101886958,"resident_bytes":30076354560,"physical_bytes":30428781488,"peak_physical_bytes":30428814256,"disk_read_bytes":1397493760,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7442,"usage":{"cpu_time_ns":12238553375,"resident_bytes":30076911616,"physical_bytes":30433549208,"peak_physical_bytes":30433549208,"disk_read_bytes":1405079552,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7543,"usage":{"cpu_time_ns":12275020583,"resident_bytes":30076878848,"physical_bytes":30434401176,"peak_physical_bytes":30434597784,"disk_read_bytes":2168623104,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7649,"usage":{"cpu_time_ns":12303557666,"resident_bytes":30070734848,"physical_bytes":30434401176,"peak_physical_bytes":30434597784,"disk_read_bytes":2840956928,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7759,"usage":{"cpu_time_ns":12377144416,"resident_bytes":30063755264,"physical_bytes":30344436632,"peak_physical_bytes":30456912792,"disk_read_bytes":3501359104,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7869,"usage":{"cpu_time_ns":12895256958,"resident_bytes":30102257664,"physical_bytes":30347598792,"peak_physical_bytes":30456912792,"disk_read_bytes":3513008128,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":7979,"usage":{"cpu_time_ns":13404548375,"resident_bytes":30109450240,"physical_bytes":30354725832,"peak_physical_bytes":30456912792,"disk_read_bytes":3524509696,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8089,"usage":{"cpu_time_ns":13917976666,"resident_bytes":30109499392,"physical_bytes":30354742216,"peak_physical_bytes":30456912792,"disk_read_bytes":3531341824,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8200,"usage":{"cpu_time_ns":14435387125,"resident_bytes":30109532160,"physical_bytes":30354742216,"peak_physical_bytes":30456912792,"disk_read_bytes":3542417408,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8310,"usage":{"cpu_time_ns":14681285291,"resident_bytes":30109548544,"physical_bytes":30354742216,"peak_physical_bytes":30456912792,"disk_read_bytes":3549560832,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8420,"usage":{"cpu_time_ns":15303234375,"resident_bytes":30109564928,"physical_bytes":30354742216,"peak_physical_bytes":30456912792,"disk_read_bytes":3557015552,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8531,"usage":{"cpu_time_ns":15556167666,"resident_bytes":30109614080,"physical_bytes":30354709448,"peak_physical_bytes":30456912792,"disk_read_bytes":3557015552,"disk_write_bytes":51015680}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8634,"usage":{"cpu_time_ns":15651460416,"resident_bytes":30107254784,"physical_bytes":30257798112,"peak_physical_bytes":30456912792,"disk_read_bytes":3557072896,"disk_write_bytes":77099008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8744,"usage":{"cpu_time_ns":15760740875,"resident_bytes":30123098112,"physical_bytes":30273625032,"peak_physical_bytes":30456912792,"disk_read_bytes":3585167360,"disk_write_bytes":77099008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8849,"usage":{"cpu_time_ns":15826246500,"resident_bytes":30122262528,"physical_bytes":30328757216,"peak_physical_bytes":30456912792,"disk_read_bytes":3585167360,"disk_write_bytes":77099008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":8959,"usage":{"cpu_time_ns":15924891833,"resident_bytes":30106697728,"physical_bytes":30346124280,"peak_physical_bytes":30456912792,"disk_read_bytes":3587559424,"disk_write_bytes":77099008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9061,"usage":{"cpu_time_ns":16231248291,"resident_bytes":30106845184,"physical_bytes":30369520632,"peak_physical_bytes":30456912792,"disk_read_bytes":3642888192,"disk_write_bytes":77099008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9163,"usage":{"cpu_time_ns":16508232875,"resident_bytes":30107090944,"physical_bytes":30374812664,"peak_physical_bytes":30456912792,"disk_read_bytes":3643904000,"disk_write_bytes":77099008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9274,"usage":{"cpu_time_ns":16835094875,"resident_bytes":30107238400,"physical_bytes":30379006968,"peak_physical_bytes":30456912792,"disk_read_bytes":3644755968,"disk_write_bytes":77099008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9384,"usage":{"cpu_time_ns":17064060750,"resident_bytes":30107435008,"physical_bytes":30384249848,"peak_physical_bytes":30456912792,"disk_read_bytes":3672969216,"disk_write_bytes":77099008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9494,"usage":{"cpu_time_ns":17367804333,"resident_bytes":30107582464,"physical_bytes":30387969016,"peak_physical_bytes":30456912792,"disk_read_bytes":3673378816,"disk_write_bytes":77099008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9599,"usage":{"cpu_time_ns":17635300833,"resident_bytes":30107779072,"physical_bytes":30393211896,"peak_physical_bytes":30456912792,"disk_read_bytes":3673493504,"disk_write_bytes":77099008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9701,"usage":{"cpu_time_ns":17876276583,"resident_bytes":30107877376,"physical_bytes":30395603960,"peak_physical_bytes":30456912792,"disk_read_bytes":3673493504,"disk_write_bytes":77099008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9804,"usage":{"cpu_time_ns":18099795833,"resident_bytes":30108073984,"physical_bytes":30400846840,"peak_physical_bytes":30456912792,"disk_read_bytes":3673493504,"disk_write_bytes":77099008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":9915,"usage":{"cpu_time_ns":18370225458,"resident_bytes":30108221440,"physical_bytes":30404991992,"peak_physical_bytes":30456912792,"disk_read_bytes":3673493504,"disk_write_bytes":77099008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10025,"usage":{"cpu_time_ns":18649135000,"resident_bytes":30108418048,"physical_bytes":30410234872,"peak_physical_bytes":30456912792,"disk_read_bytes":3673493504,"disk_write_bytes":77099008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10135,"usage":{"cpu_time_ns":18950571583,"resident_bytes":30108565504,"physical_bytes":30413954040,"peak_physical_bytes":30456912792,"disk_read_bytes":3673493504,"disk_write_bytes":77099008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10238,"usage":{"cpu_time_ns":19243127416,"resident_bytes":30108712960,"physical_bytes":30418099192,"peak_physical_bytes":30456912792,"disk_read_bytes":3673493504,"disk_write_bytes":77099008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10348,"usage":{"cpu_time_ns":19468346291,"resident_bytes":30109204480,"physical_bytes":30440954848,"peak_physical_bytes":30456912792,"disk_read_bytes":3674755072,"disk_write_bytes":77099008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10458,"usage":{"cpu_time_ns":19655778208,"resident_bytes":30110433280,"physical_bytes":30357183456,"peak_physical_bytes":30465694688,"disk_read_bytes":3674755072,"disk_write_bytes":77099008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10568,"usage":{"cpu_time_ns":20202366125,"resident_bytes":30111973376,"physical_bytes":30356986848,"peak_physical_bytes":30465694688,"disk_read_bytes":3674755072,"disk_write_bytes":77099008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10673,"usage":{"cpu_time_ns":20799819541,"resident_bytes":30111973376,"physical_bytes":30356986848,"peak_physical_bytes":30465694688,"disk_read_bytes":3674755072,"disk_write_bytes":77099008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10783,"usage":{"cpu_time_ns":21514310958,"resident_bytes":30111973376,"physical_bytes":30356986848,"peak_physical_bytes":30465694688,"disk_read_bytes":3674755072,"disk_write_bytes":77099008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10884,"usage":{"cpu_time_ns":21680742833,"resident_bytes":30111989760,"physical_bytes":30357150688,"peak_physical_bytes":30465694688,"disk_read_bytes":3674755072,"disk_write_bytes":77099008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":10994,"usage":{"cpu_time_ns":22207464041,"resident_bytes":30112006144,"physical_bytes":30357167072,"peak_physical_bytes":30465694688,"disk_read_bytes":3674755072,"disk_write_bytes":77099008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11104,"usage":{"cpu_time_ns":22741278583,"resident_bytes":30112006144,"physical_bytes":30357167072,"peak_physical_bytes":30465694688,"disk_read_bytes":3674755072,"disk_write_bytes":77099008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11215,"usage":{"cpu_time_ns":22851533541,"resident_bytes":30112006144,"physical_bytes":30357167072,"peak_physical_bytes":30465694688,"disk_read_bytes":3674755072,"disk_write_bytes":77099008}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11325,"usage":{"cpu_time_ns":22954984250,"resident_bytes":30126407680,"physical_bytes":30357167072,"peak_physical_bytes":30465694688,"disk_read_bytes":3674759168,"disk_write_bytes":129265664}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11432,"usage":{"cpu_time_ns":23052234291,"resident_bytes":30128291840,"physical_bytes":30421556192,"peak_physical_bytes":30465694688,"disk_read_bytes":3674759168,"disk_write_bytes":129265664}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11542,"usage":{"cpu_time_ns":23071331375,"resident_bytes":30130733056,"physical_bytes":30421556192,"peak_physical_bytes":30465694688,"disk_read_bytes":3693780992,"disk_write_bytes":129265664}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11646,"usage":{"cpu_time_ns":23209985166,"resident_bytes":30127259648,"physical_bytes":30357363680,"peak_physical_bytes":30465694688,"disk_read_bytes":3727990784,"disk_write_bytes":129265664}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11748,"usage":{"cpu_time_ns":23297664208,"resident_bytes":30127259648,"physical_bytes":30357363680,"peak_physical_bytes":30465694688,"disk_read_bytes":3753467904,"disk_write_bytes":129265664}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11859,"usage":{"cpu_time_ns":23372176750,"resident_bytes":30134337536,"physical_bytes":30364441568,"peak_physical_bytes":30465694688,"disk_read_bytes":3755483136,"disk_write_bytes":129265664}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":11965,"usage":{"cpu_time_ns":23459469083,"resident_bytes":30134337536,"physical_bytes":30364441568,"peak_physical_bytes":30465694688,"disk_read_bytes":3766525952,"disk_write_bytes":129265664}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12075,"usage":{"cpu_time_ns":23546605333,"resident_bytes":30134337536,"physical_bytes":30364441568,"peak_physical_bytes":30465694688,"disk_read_bytes":3793051648,"disk_write_bytes":129265664}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12182,"usage":{"cpu_time_ns":23583082666,"resident_bytes":30141415424,"physical_bytes":30371519456,"peak_physical_bytes":30465694688,"disk_read_bytes":3798605824,"disk_write_bytes":129265664}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12289,"usage":{"cpu_time_ns":23633072125,"resident_bytes":30141415424,"physical_bytes":30371519456,"peak_physical_bytes":30465694688,"disk_read_bytes":3836813312,"disk_write_bytes":129265664}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12396,"usage":{"cpu_time_ns":24173243166,"resident_bytes":30141480960,"physical_bytes":30371519456,"peak_physical_bytes":30465694688,"disk_read_bytes":3836862464,"disk_write_bytes":129265664}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12507,"usage":{"cpu_time_ns":24283430333,"resident_bytes":30141480960,"physical_bytes":30371519456,"peak_physical_bytes":30465694688,"disk_read_bytes":3836862464,"disk_write_bytes":129265664}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12615,"usage":{"cpu_time_ns":24386579583,"resident_bytes":30124589056,"physical_bytes":30272494560,"peak_physical_bytes":30465694688,"disk_read_bytes":3836866560,"disk_write_bytes":156889088}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12716,"usage":{"cpu_time_ns":24487581875,"resident_bytes":30136434688,"physical_bytes":30279801824,"peak_physical_bytes":30465694688,"disk_read_bytes":3836866560,"disk_write_bytes":156889088}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12827,"usage":{"cpu_time_ns":24542420333,"resident_bytes":30138351616,"physical_bytes":30343994336,"peak_physical_bytes":30465694688,"disk_read_bytes":3836866560,"disk_write_bytes":156889088}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":12937,"usage":{"cpu_time_ns":24609636250,"resident_bytes":30138368000,"physical_bytes":30316485600,"peak_physical_bytes":30465694688,"disk_read_bytes":3836866560,"disk_write_bytes":156889088}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13047,"usage":{"cpu_time_ns":24660025500,"resident_bytes":30138892288,"physical_bytes":30322711520,"peak_physical_bytes":30465694688,"disk_read_bytes":3836882944,"disk_write_bytes":156889088}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13157,"usage":{"cpu_time_ns":24704671666,"resident_bytes":30138892288,"physical_bytes":30322711520,"peak_physical_bytes":30465694688,"disk_read_bytes":3836882944,"disk_write_bytes":156889088}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13267,"usage":{"cpu_time_ns":24729231875,"resident_bytes":30138892288,"physical_bytes":30322711520,"peak_physical_bytes":30465694688,"disk_read_bytes":3836882944,"disk_write_bytes":156889088}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13377,"usage":{"cpu_time_ns":24752070791,"resident_bytes":30138892288,"physical_bytes":30322711520,"peak_physical_bytes":30465694688,"disk_read_bytes":3836882944,"disk_write_bytes":156889088}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13488,"usage":{"cpu_time_ns":24765555666,"resident_bytes":30138892288,"physical_bytes":30322711520,"peak_physical_bytes":30465694688,"disk_read_bytes":3836882944,"disk_write_bytes":156889088}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13598,"usage":{"cpu_time_ns":25018497416,"resident_bytes":30140628992,"physical_bytes":30333918176,"peak_physical_bytes":30465694688,"disk_read_bytes":3836882944,"disk_write_bytes":156889088}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13699,"usage":{"cpu_time_ns":25368037541,"resident_bytes":30141661184,"physical_bytes":30333918176,"peak_physical_bytes":30465694688,"disk_read_bytes":3836882944,"disk_write_bytes":156889088}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13809,"usage":{"cpu_time_ns":25477831208,"resident_bytes":30142873600,"physical_bytes":30333918176,"peak_physical_bytes":30465694688,"disk_read_bytes":3836882944,"disk_write_bytes":173666304}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":13920,"usage":{"cpu_time_ns":25581216666,"resident_bytes":30143119360,"physical_bytes":30333918176,"peak_physical_bytes":30465694688,"disk_read_bytes":3836956672,"disk_write_bytes":184512512}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14030,"usage":{"cpu_time_ns":25614751625,"resident_bytes":30145019904,"physical_bytes":30398143456,"peak_physical_bytes":30465694688,"disk_read_bytes":3836956672,"disk_write_bytes":184512512}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14140,"usage":{"cpu_time_ns":25695362083,"resident_bytes":30146658304,"physical_bytes":30398143456,"peak_physical_bytes":30465694688,"disk_read_bytes":3836956672,"disk_write_bytes":184512512}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14249,"usage":{"cpu_time_ns":25732065166,"resident_bytes":30145069056,"physical_bytes":30398192608,"peak_physical_bytes":30465694688,"disk_read_bytes":3836956672,"disk_write_bytes":184512512}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14359,"usage":{"cpu_time_ns":25959792416,"resident_bytes":30146412544,"physical_bytes":30448032736,"peak_physical_bytes":30465694688,"disk_read_bytes":3913797632,"disk_write_bytes":184512512}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14465,"usage":{"cpu_time_ns":26150650916,"resident_bytes":30146838528,"physical_bytes":30448409568,"peak_physical_bytes":30465694688,"disk_read_bytes":4004794368,"disk_write_bytes":184512512}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14572,"usage":{"cpu_time_ns":26389519916,"resident_bytes":30147198976,"physical_bytes":30448770016,"peak_physical_bytes":30465694688,"disk_read_bytes":4051668992,"disk_write_bytes":184512512}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14682,"usage":{"cpu_time_ns":26595790083,"resident_bytes":30148558848,"physical_bytes":30450113504,"peak_physical_bytes":30465694688,"disk_read_bytes":4068954112,"disk_write_bytes":184512512}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14792,"usage":{"cpu_time_ns":26764758875,"resident_bytes":30148935680,"physical_bytes":30450392032,"peak_physical_bytes":30465694688,"disk_read_bytes":4070150144,"disk_write_bytes":184512512}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":14902,"usage":{"cpu_time_ns":27008912125,"resident_bytes":30149296128,"physical_bytes":30450752480,"peak_physical_bytes":30465694688,"disk_read_bytes":4071575552,"disk_write_bytes":184512512}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":15013,"usage":{"cpu_time_ns":27269163333,"resident_bytes":30149591040,"physical_bytes":30451047392,"peak_physical_bytes":30465694688,"disk_read_bytes":4072296448,"disk_write_bytes":184512512}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":15123,"usage":{"cpu_time_ns":27524712833,"resident_bytes":30149951488,"physical_bytes":30451407840,"peak_physical_bytes":30465694688,"disk_read_bytes":4083585024,"disk_write_bytes":184512512}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":15233,"usage":{"cpu_time_ns":27783522833,"resident_bytes":30150311936,"physical_bytes":30451768288,"peak_physical_bytes":30465694688,"disk_read_bytes":4092989440,"disk_write_bytes":184512512}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":15343,"usage":{"cpu_time_ns":28006672083,"resident_bytes":30150672384,"physical_bytes":30452128736,"peak_physical_bytes":30465694688,"disk_read_bytes":4104966144,"disk_write_bytes":184512512}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":15453,"usage":{"cpu_time_ns":28262583291,"resident_bytes":30150934528,"physical_bytes":30452390880,"peak_physical_bytes":30465694688,"disk_read_bytes":4110848000,"disk_write_bytes":184512512}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":15563,"usage":{"cpu_time_ns":28436411791,"resident_bytes":30151081984,"physical_bytes":30471363552,"peak_physical_bytes":30471478240,"disk_read_bytes":4230991872,"disk_write_bytes":184512512}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":15673,"usage":{"cpu_time_ns":28522152083,"resident_bytes":30151081984,"physical_bytes":30490237920,"peak_physical_bytes":30490237920,"disk_read_bytes":4239347712,"disk_write_bytes":184512512}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":15782,"usage":{"cpu_time_ns":29074400041,"resident_bytes":30151131136,"physical_bytes":30379105248,"peak_physical_bytes":30490237920,"disk_read_bytes":4239364096,"disk_write_bytes":184512512}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":15885,"usage":{"cpu_time_ns":29177243833,"resident_bytes":30151131136,"physical_bytes":30379105248,"peak_physical_bytes":30490237920,"disk_read_bytes":4239364096,"disk_write_bytes":184512512}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":15996,"usage":{"cpu_time_ns":29282412833,"resident_bytes":30163435520,"physical_bytes":30379105248,"peak_physical_bytes":30490237920,"disk_read_bytes":4239372288,"disk_write_bytes":224579584}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":16106,"usage":{"cpu_time_ns":29379956166,"resident_bytes":30165303296,"physical_bytes":30443330528,"peak_physical_bytes":30490237920,"disk_read_bytes":4239372288,"disk_write_bytes":224579584}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":16216,"usage":{"cpu_time_ns":29401162041,"resident_bytes":30166941696,"physical_bytes":30443330528,"peak_physical_bytes":30490237920,"disk_read_bytes":4239372288,"disk_write_bytes":224579584}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":16326,"usage":{"cpu_time_ns":29502622625,"resident_bytes":30165336064,"physical_bytes":30443363296,"peak_physical_bytes":30490237920,"disk_read_bytes":4239372288,"disk_write_bytes":224579584}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":16436,"usage":{"cpu_time_ns":29519998500,"resident_bytes":30163484672,"physical_bytes":30379154400,"peak_physical_bytes":30490237920,"disk_read_bytes":4239372288,"disk_write_bytes":224579584}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":16547,"usage":{"cpu_time_ns":29620050500,"resident_bytes":30165368832,"physical_bytes":30443396064,"peak_physical_bytes":30490237920,"disk_read_bytes":4239372288,"disk_write_bytes":224579584}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":16657,"usage":{"cpu_time_ns":29683406750,"resident_bytes":30166482944,"physical_bytes":30395390944,"peak_physical_bytes":30490237920,"disk_read_bytes":4250497024,"disk_write_bytes":224579584}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":16765,"usage":{"cpu_time_ns":29947350000,"resident_bytes":30166548480,"physical_bytes":30396210144,"peak_physical_bytes":30490237920,"disk_read_bytes":4309020672,"disk_write_bytes":224579584}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":16875,"usage":{"cpu_time_ns":30196229333,"resident_bytes":30166581248,"physical_bytes":30396210144,"peak_physical_bytes":30490237920,"disk_read_bytes":4312068096,"disk_write_bytes":224579584}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":16982,"usage":{"cpu_time_ns":30371828625,"resident_bytes":30166581248,"physical_bytes":30396210144,"peak_physical_bytes":30490237920,"disk_read_bytes":4314918912,"disk_write_bytes":224579584}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":17092,"usage":{"cpu_time_ns":30628974000,"resident_bytes":30166482944,"physical_bytes":30396095456,"peak_physical_bytes":30490237920,"disk_read_bytes":4372295680,"disk_write_bytes":224579584}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":17197,"usage":{"cpu_time_ns":30840286958,"resident_bytes":30166908928,"physical_bytes":30487714808,"peak_physical_bytes":30490237920,"disk_read_bytes":4445351936,"disk_write_bytes":224579584}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":17307,"usage":{"cpu_time_ns":31024411875,"resident_bytes":30167236608,"physical_bytes":30488042488,"peak_physical_bytes":30490237920,"disk_read_bytes":4465127424,"disk_write_bytes":224579584}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":17418,"usage":{"cpu_time_ns":31204110208,"resident_bytes":30167662592,"physical_bytes":30488468472,"peak_physical_bytes":30490237920,"disk_read_bytes":4465307648,"disk_write_bytes":224579584}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":17528,"usage":{"cpu_time_ns":31387529458,"resident_bytes":30167990272,"physical_bytes":30488714232,"peak_physical_bytes":30490237920,"disk_read_bytes":4471746560,"disk_write_bytes":224579584}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":17638,"usage":{"cpu_time_ns":31573751916,"resident_bytes":30168317952,"physical_bytes":30489041912,"peak_physical_bytes":30490237920,"disk_read_bytes":4472057856,"disk_write_bytes":224579584}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":17748,"usage":{"cpu_time_ns":31762003708,"resident_bytes":30168727552,"physical_bytes":30489451512,"peak_physical_bytes":30490237920,"disk_read_bytes":4472123392,"disk_write_bytes":224579584}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":17858,"usage":{"cpu_time_ns":31929757500,"resident_bytes":30169055232,"physical_bytes":30489779192,"peak_physical_bytes":30490237920,"disk_read_bytes":4475121664,"disk_write_bytes":224579584}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":17969,"usage":{"cpu_time_ns":32113021125,"resident_bytes":30169481216,"physical_bytes":30490205176,"peak_physical_bytes":30490237920,"disk_read_bytes":4480774144,"disk_write_bytes":224579584}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":18079,"usage":{"cpu_time_ns":32287936958,"resident_bytes":30169808896,"physical_bytes":30490532856,"peak_physical_bytes":30490532856,"disk_read_bytes":4483657728,"disk_write_bytes":224579584}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":18189,"usage":{"cpu_time_ns":32439398458,"resident_bytes":30170005504,"physical_bytes":30490713056,"peak_physical_bytes":30490827768,"disk_read_bytes":4530761728,"disk_write_bytes":224579584}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":18299,"usage":{"cpu_time_ns":32522143625,"resident_bytes":30170005504,"physical_bytes":30490860512,"peak_physical_bytes":30490860512,"disk_read_bytes":4533006336,"disk_write_bytes":224579584}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":18409,"usage":{"cpu_time_ns":33100925041,"resident_bytes":30170103808,"physical_bytes":30384823264,"peak_physical_bytes":30490860512,"disk_read_bytes":4533596160,"disk_write_bytes":224579584}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":18519,"usage":{"cpu_time_ns":33210887083,"resident_bytes":30170103808,"physical_bytes":30384823264,"peak_physical_bytes":30490860512,"disk_read_bytes":4533596160,"disk_write_bytes":224579584}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":18620,"usage":{"cpu_time_ns":33306021083,"resident_bytes":30135828480,"physical_bytes":30284192736,"peak_physical_bytes":30490860512,"disk_read_bytes":4533669888,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":18730,"usage":{"cpu_time_ns":33414152833,"resident_bytes":30147657728,"physical_bytes":30289370080,"peak_physical_bytes":30490860512,"disk_read_bytes":4559912960,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":18832,"usage":{"cpu_time_ns":33488920500,"resident_bytes":30149541888,"physical_bytes":30353611744,"peak_physical_bytes":30490860512,"disk_read_bytes":4559912960,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":18942,"usage":{"cpu_time_ns":33569982125,"resident_bytes":30135369728,"physical_bytes":30372338680,"peak_physical_bytes":30490860512,"disk_read_bytes":4570775552,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":19049,"usage":{"cpu_time_ns":33810365125,"resident_bytes":30135566336,"physical_bytes":30397242336,"peak_physical_bytes":30490860512,"disk_read_bytes":4573577216,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":19159,"usage":{"cpu_time_ns":33994178166,"resident_bytes":30135762944,"physical_bytes":30402485216,"peak_physical_bytes":30490860512,"disk_read_bytes":4578951168,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":19263,"usage":{"cpu_time_ns":34182716625,"resident_bytes":30135959552,"physical_bytes":30407728096,"peak_physical_bytes":30490860512,"disk_read_bytes":4579131392,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":19365,"usage":{"cpu_time_ns":34420288500,"resident_bytes":30138220544,"physical_bytes":30415035360,"peak_physical_bytes":30490860512,"disk_read_bytes":4582146048,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":19467,"usage":{"cpu_time_ns":34705557958,"resident_bytes":30138368000,"physical_bytes":30417705952,"peak_physical_bytes":30490860512,"disk_read_bytes":4582342656,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":19577,"usage":{"cpu_time_ns":34939255625,"resident_bytes":30138466304,"physical_bytes":30420720608,"peak_physical_bytes":30490860512,"disk_read_bytes":4582375424,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":19688,"usage":{"cpu_time_ns":35300182250,"resident_bytes":30138613760,"physical_bytes":30424865760,"peak_physical_bytes":30490860512,"disk_read_bytes":4582424576,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":19798,"usage":{"cpu_time_ns":35573245375,"resident_bytes":30138761216,"physical_bytes":30428584928,"peak_physical_bytes":30490860512,"disk_read_bytes":4582490112,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":19908,"usage":{"cpu_time_ns":35878756583,"resident_bytes":30138957824,"physical_bytes":30433827808,"peak_physical_bytes":30490860512,"disk_read_bytes":4585406464,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":20018,"usage":{"cpu_time_ns":36185725041,"resident_bytes":30138646528,"physical_bytes":30437972960,"peak_physical_bytes":30490860512,"disk_read_bytes":4585996288,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":20128,"usage":{"cpu_time_ns":36479011166,"resident_bytes":30138793984,"physical_bytes":30441692128,"peak_physical_bytes":30490860512,"disk_read_bytes":4586143744,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":20237,"usage":{"cpu_time_ns":36787288625,"resident_bytes":30138957824,"physical_bytes":30464941024,"peak_physical_bytes":30490860512,"disk_read_bytes":4588519424,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":20347,"usage":{"cpu_time_ns":37007103291,"resident_bytes":30139449344,"physical_bytes":30468709344,"peak_physical_bytes":30490860512,"disk_read_bytes":4621811712,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":20457,"usage":{"cpu_time_ns":37104526250,"resident_bytes":30139465728,"physical_bytes":30381841376,"peak_physical_bytes":30492826592,"disk_read_bytes":4622778368,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":20567,"usage":{"cpu_time_ns":37688377958,"resident_bytes":30149312512,"physical_bytes":30391819232,"peak_physical_bytes":30492826592,"disk_read_bytes":4622778368,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":20677,"usage":{"cpu_time_ns":38275103833,"resident_bytes":30149312512,"physical_bytes":30391819232,"peak_physical_bytes":30492826592,"disk_read_bytes":4629872640,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":20787,"usage":{"cpu_time_ns":38856834416,"resident_bytes":30156406784,"physical_bytes":30399060960,"peak_physical_bytes":30492826592,"disk_read_bytes":4634345472,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":20898,"usage":{"cpu_time_ns":39451263125,"resident_bytes":30156406784,"physical_bytes":30399060960,"peak_physical_bytes":30492826592,"disk_read_bytes":4634345472,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":21003,"usage":{"cpu_time_ns":39596631333,"resident_bytes":30156455936,"physical_bytes":30399060960,"peak_physical_bytes":30492826592,"disk_read_bytes":4641406976,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":21113,"usage":{"cpu_time_ns":40182793708,"resident_bytes":30156488704,"physical_bytes":30399060960,"peak_physical_bytes":30492826592,"disk_read_bytes":4641456128,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":21223,"usage":{"cpu_time_ns":40695415708,"resident_bytes":30156505088,"physical_bytes":30399077344,"peak_physical_bytes":30492826592,"disk_read_bytes":4641456128,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":21332,"usage":{"cpu_time_ns":40803346916,"resident_bytes":30156505088,"physical_bytes":30399077344,"peak_physical_bytes":30492826592,"disk_read_bytes":4641456128,"disk_write_bytes":278126592}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":21442,"usage":{"cpu_time_ns":40907531041,"resident_bytes":30179966976,"physical_bytes":30328151008,"peak_physical_bytes":30492826592,"disk_read_bytes":4679618560,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":21548,"usage":{"cpu_time_ns":41013934541,"resident_bytes":30177378304,"physical_bytes":30318926816,"peak_physical_bytes":30492826592,"disk_read_bytes":4679618560,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":21659,"usage":{"cpu_time_ns":41027781583,"resident_bytes":30179311616,"physical_bytes":30383332320,"peak_physical_bytes":30492826592,"disk_read_bytes":4679618560,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":21765,"usage":{"cpu_time_ns":41119768458,"resident_bytes":30177460224,"physical_bytes":30319123424,"peak_physical_bytes":30492826592,"disk_read_bytes":4679618560,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":21875,"usage":{"cpu_time_ns":41156868000,"resident_bytes":30179328000,"physical_bytes":30383348704,"peak_physical_bytes":30492826592,"disk_read_bytes":4679618560,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":21985,"usage":{"cpu_time_ns":41342061583,"resident_bytes":30156095488,"physical_bytes":30509161464,"peak_physical_bytes":30509161464,"disk_read_bytes":4723347456,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":22096,"usage":{"cpu_time_ns":41616151666,"resident_bytes":30156390400,"physical_bytes":30524824568,"peak_physical_bytes":30524824568,"disk_read_bytes":4769927168,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":22206,"usage":{"cpu_time_ns":41852867791,"resident_bytes":30156685312,"physical_bytes":30527642616,"peak_physical_bytes":30527642616,"disk_read_bytes":4770009088,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":22316,"usage":{"cpu_time_ns":42065418000,"resident_bytes":30156980224,"physical_bytes":30530460664,"peak_physical_bytes":30530460664,"disk_read_bytes":4777152512,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":22416,"usage":{"cpu_time_ns":42257686500,"resident_bytes":30157275136,"physical_bytes":30533278712,"peak_physical_bytes":30533278712,"disk_read_bytes":4777234432,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":22527,"usage":{"cpu_time_ns":42481148333,"resident_bytes":30157570048,"physical_bytes":30535736312,"peak_physical_bytes":30536113144,"disk_read_bytes":4777742336,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":22632,"usage":{"cpu_time_ns":42659996041,"resident_bytes":30157864960,"physical_bytes":30538554360,"peak_physical_bytes":30538570744,"disk_read_bytes":4783755264,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":22739,"usage":{"cpu_time_ns":42862177166,"resident_bytes":30158159872,"physical_bytes":30541372408,"peak_physical_bytes":30541372408,"disk_read_bytes":4784214016,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":22849,"usage":{"cpu_time_ns":43088445208,"resident_bytes":30158618624,"physical_bytes":30545828856,"peak_physical_bytes":30545828856,"disk_read_bytes":4784836608,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":22959,"usage":{"cpu_time_ns":43349610208,"resident_bytes":30158913536,"physical_bytes":30548646904,"peak_physical_bytes":30548646904,"disk_read_bytes":4785442816,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":23067,"usage":{"cpu_time_ns":43608854250,"resident_bytes":30159208448,"physical_bytes":30551464952,"peak_physical_bytes":30551464952,"disk_read_bytes":4785786880,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":23177,"usage":{"cpu_time_ns":43805280375,"resident_bytes":30159503360,"physical_bytes":30554283000,"peak_physical_bytes":30554283000,"disk_read_bytes":4786147328,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":23287,"usage":{"cpu_time_ns":44005966125,"resident_bytes":30159929344,"physical_bytes":30558280696,"peak_physical_bytes":30558280696,"disk_read_bytes":4786786304,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":23397,"usage":{"cpu_time_ns":44262162083,"resident_bytes":30160224256,"physical_bytes":30561098744,"peak_physical_bytes":30561098744,"disk_read_bytes":4787277824,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":23508,"usage":{"cpu_time_ns":44530889250,"resident_bytes":30160519168,"physical_bytes":30563916792,"peak_physical_bytes":30563933176,"disk_read_bytes":4787834880,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":23618,"usage":{"cpu_time_ns":44753365875,"resident_bytes":30160814080,"physical_bytes":30566571000,"peak_physical_bytes":30566587384,"disk_read_bytes":4788342784,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":23728,"usage":{"cpu_time_ns":44987289625,"resident_bytes":30161108992,"physical_bytes":30569389048,"peak_physical_bytes":30569405432,"disk_read_bytes":4788736000,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":23838,"usage":{"cpu_time_ns":45216566166,"resident_bytes":30161403904,"physical_bytes":30572207096,"peak_physical_bytes":30572223480,"disk_read_bytes":4789194752,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":23948,"usage":{"cpu_time_ns":45458169458,"resident_bytes":30161862656,"physical_bytes":30576663544,"peak_physical_bytes":30576663544,"disk_read_bytes":4795437056,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":24059,"usage":{"cpu_time_ns":45684483458,"resident_bytes":30162010112,"physical_bytes":30596963320,"peak_physical_bytes":30596979704,"disk_read_bytes":4887220224,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":24169,"usage":{"cpu_time_ns":45826783208,"resident_bytes":30162567168,"physical_bytes":30599748600,"peak_physical_bytes":30599748600,"disk_read_bytes":4964274176,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":24279,"usage":{"cpu_time_ns":45925905458,"resident_bytes":30162583552,"physical_bytes":30408629240,"peak_physical_bytes":30624177144,"disk_read_bytes":4965617664,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":24382,"usage":{"cpu_time_ns":46507525833,"resident_bytes":30165368832,"physical_bytes":30411758584,"peak_physical_bytes":30624177144,"disk_read_bytes":4965617664,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":24488,"usage":{"cpu_time_ns":47114579208,"resident_bytes":30165417984,"physical_bytes":30411889656,"peak_physical_bytes":30624177144,"disk_read_bytes":4972318720,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":24599,"usage":{"cpu_time_ns":47722864833,"resident_bytes":30165434368,"physical_bytes":30411889656,"peak_physical_bytes":30624177144,"disk_read_bytes":4975120384,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":24709,"usage":{"cpu_time_ns":48327934916,"resident_bytes":30165549056,"physical_bytes":30411922424,"peak_physical_bytes":30624177144,"disk_read_bytes":4977561600,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":24815,"usage":{"cpu_time_ns":48436081625,"resident_bytes":30165549056,"physical_bytes":30411922424,"peak_physical_bytes":30624177144,"disk_read_bytes":4986753024,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":24925,"usage":{"cpu_time_ns":49015098916,"resident_bytes":30165565440,"physical_bytes":30411922424,"peak_physical_bytes":30624177144,"disk_read_bytes":4993798144,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":25036,"usage":{"cpu_time_ns":49613245416,"resident_bytes":30165565440,"physical_bytes":30411922424,"peak_physical_bytes":30624177144,"disk_read_bytes":4993814528,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":25146,"usage":{"cpu_time_ns":50201444791,"resident_bytes":30165614592,"physical_bytes":30411922424,"peak_physical_bytes":30624177144,"disk_read_bytes":4993814528,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":25248,"usage":{"cpu_time_ns":50782855791,"resident_bytes":30165614592,"physical_bytes":30411922424,"peak_physical_bytes":30624177144,"disk_read_bytes":4994600960,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":25359,"usage":{"cpu_time_ns":50855208875,"resident_bytes":30165614592,"physical_bytes":30411922424,"peak_physical_bytes":30624177144,"disk_read_bytes":4996796416,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":25465,"usage":{"cpu_time_ns":51429409333,"resident_bytes":30165630976,"physical_bytes":30411938808,"peak_physical_bytes":30624177144,"disk_read_bytes":4996812800,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":25575,"usage":{"cpu_time_ns":51539702083,"resident_bytes":30165630976,"physical_bytes":30411709432,"peak_physical_bytes":30624177144,"disk_read_bytes":4996812800,"disk_write_bytes":304209920}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":25686,"usage":{"cpu_time_ns":51647167208,"resident_bytes":30191632384,"physical_bytes":30411709432,"peak_physical_bytes":30624177144,"disk_read_bytes":4996812800,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":25800,"usage":{"cpu_time_ns":51757197416,"resident_bytes":30191632384,"physical_bytes":30411709432,"peak_physical_bytes":30624177144,"disk_read_bytes":4996812800,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":25910,"usage":{"cpu_time_ns":51790196791,"resident_bytes":30193500160,"physical_bytes":30476164088,"peak_physical_bytes":30624177144,"disk_read_bytes":4996812800,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":26015,"usage":{"cpu_time_ns":51860793958,"resident_bytes":30191648768,"physical_bytes":30411955192,"peak_physical_bytes":30624177144,"disk_read_bytes":4996812800,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":26125,"usage":{"cpu_time_ns":51925178791,"resident_bytes":30193532928,"physical_bytes":30476196856,"peak_physical_bytes":30624177144,"disk_read_bytes":4996812800,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":26235,"usage":{"cpu_time_ns":51976582666,"resident_bytes":30161207296,"physical_bytes":30306589688,"peak_physical_bytes":30624177144,"disk_read_bytes":4997353472,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":26346,"usage":{"cpu_time_ns":52086761666,"resident_bytes":30158635008,"physical_bytes":30303902712,"peak_physical_bytes":30624177144,"disk_read_bytes":4997877760,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":26456,"usage":{"cpu_time_ns":52140797166,"resident_bytes":30160535552,"physical_bytes":30368177168,"peak_physical_bytes":30624177144,"disk_read_bytes":4997877760,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":26566,"usage":{"cpu_time_ns":52166837416,"resident_bytes":30161747968,"physical_bytes":30325447696,"peak_physical_bytes":30624177144,"disk_read_bytes":4997877760,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":26676,"usage":{"cpu_time_ns":52270796375,"resident_bytes":30161747968,"physical_bytes":30384987152,"peak_physical_bytes":30624177144,"disk_read_bytes":5015490560,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":26786,"usage":{"cpu_time_ns":52371656958,"resident_bytes":30161747968,"physical_bytes":30384987152,"peak_physical_bytes":30624177144,"disk_read_bytes":5015490560,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":26897,"usage":{"cpu_time_ns":52488050708,"resident_bytes":30161747968,"physical_bytes":30384987152,"peak_physical_bytes":30624177144,"disk_read_bytes":5015490560,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":27007,"usage":{"cpu_time_ns":52742979291,"resident_bytes":30161846272,"physical_bytes":30431255568,"peak_physical_bytes":30624177144,"disk_read_bytes":5033021440,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":27117,"usage":{"cpu_time_ns":53054210000,"resident_bytes":30161993728,"physical_bytes":30434974736,"peak_physical_bytes":30624177144,"disk_read_bytes":5038051328,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":27227,"usage":{"cpu_time_ns":53321723708,"resident_bytes":30162141184,"physical_bytes":30439119888,"peak_physical_bytes":30624177144,"disk_read_bytes":5040885760,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":27335,"usage":{"cpu_time_ns":53600033875,"resident_bytes":30162337792,"physical_bytes":30444362768,"peak_physical_bytes":30624177144,"disk_read_bytes":5040984064,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":27445,"usage":{"cpu_time_ns":53854126750,"resident_bytes":30162485248,"physical_bytes":30448081936,"peak_physical_bytes":30624177144,"disk_read_bytes":5041082368,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":27556,"usage":{"cpu_time_ns":54171421458,"resident_bytes":30162632704,"physical_bytes":30452030480,"peak_physical_bytes":30624177144,"disk_read_bytes":5041213440,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":27666,"usage":{"cpu_time_ns":54459903458,"resident_bytes":30162780160,"physical_bytes":30455749648,"peak_physical_bytes":30624177144,"disk_read_bytes":5041311744,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":27776,"usage":{"cpu_time_ns":54741755500,"resident_bytes":30162976768,"physical_bytes":30460992528,"peak_physical_bytes":30624177144,"disk_read_bytes":5041328128,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":27886,"usage":{"cpu_time_ns":55008021375,"resident_bytes":30163124224,"physical_bytes":30465137680,"peak_physical_bytes":30624177144,"disk_read_bytes":5041737728,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":27996,"usage":{"cpu_time_ns":55327138000,"resident_bytes":30163288064,"physical_bytes":30468873232,"peak_physical_bytes":30624177144,"disk_read_bytes":5045161984,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":28107,"usage":{"cpu_time_ns":55571821666,"resident_bytes":30163435520,"physical_bytes":30473018384,"peak_physical_bytes":30624177144,"disk_read_bytes":5048455168,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":28217,"usage":{"cpu_time_ns":55881069708,"resident_bytes":30163582976,"physical_bytes":30476737552,"peak_physical_bytes":30624177144,"disk_read_bytes":5049126912,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":28327,"usage":{"cpu_time_ns":56205873666,"resident_bytes":30163681280,"physical_bytes":30479358992,"peak_physical_bytes":30624177144,"disk_read_bytes":5078896640,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":28437,"usage":{"cpu_time_ns":56506929625,"resident_bytes":30163697664,"physical_bytes":30502476816,"peak_physical_bytes":30624177144,"disk_read_bytes":5156802560,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":28547,"usage":{"cpu_time_ns":56577683625,"resident_bytes":30164221952,"physical_bytes":30506900496,"peak_physical_bytes":30624177144,"disk_read_bytes":5159899136,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":28657,"usage":{"cpu_time_ns":57208882291,"resident_bytes":30166990848,"physical_bytes":30413052944,"peak_physical_bytes":30624177144,"disk_read_bytes":5168140288,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":28768,"usage":{"cpu_time_ns":57817709875,"resident_bytes":30167040000,"physical_bytes":30413052944,"peak_physical_bytes":30624177144,"disk_read_bytes":5169434624,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":28877,"usage":{"cpu_time_ns":58415020625,"resident_bytes":30167040000,"physical_bytes":30413052944,"peak_physical_bytes":30624177144,"disk_read_bytes":5172547584,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":28988,"usage":{"cpu_time_ns":59005264291,"resident_bytes":30167040000,"physical_bytes":30413052944,"peak_physical_bytes":30624177144,"disk_read_bytes":5172547584,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":29098,"usage":{"cpu_time_ns":59093337875,"resident_bytes":30167040000,"physical_bytes":30413216784,"peak_physical_bytes":30624177144,"disk_read_bytes":5175087104,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":29198,"usage":{"cpu_time_ns":59665016416,"resident_bytes":30167056384,"physical_bytes":30413216784,"peak_physical_bytes":30624177144,"disk_read_bytes":5179658240,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":29309,"usage":{"cpu_time_ns":60259292458,"resident_bytes":30167072768,"physical_bytes":30413233168,"peak_physical_bytes":30624177144,"disk_read_bytes":5179707392,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":29416,"usage":{"cpu_time_ns":60366687875,"resident_bytes":30167072768,"physical_bytes":30413233168,"peak_physical_bytes":30624177144,"disk_read_bytes":5179707392,"disk_write_bytes":342355968}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":29526,"usage":{"cpu_time_ns":60476678000,"resident_bytes":30176428032,"physical_bytes":30413233168,"peak_physical_bytes":30624177144,"disk_read_bytes":5179707392,"disk_write_bytes":350744576}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":29630,"usage":{"cpu_time_ns":60584143166,"resident_bytes":30162436096,"physical_bytes":30322416656,"peak_physical_bytes":30624177144,"disk_read_bytes":5180850176,"disk_write_bytes":369209344}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":29740,"usage":{"cpu_time_ns":60708120333,"resident_bytes":30162960384,"physical_bytes":30353497104,"peak_physical_bytes":30624177144,"disk_read_bytes":5181718528,"disk_write_bytes":369209344}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":29848,"usage":{"cpu_time_ns":60799686958,"resident_bytes":30162976768,"physical_bytes":30362442768,"peak_physical_bytes":30624177144,"disk_read_bytes":5190975488,"disk_write_bytes":369209344}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":29959,"usage":{"cpu_time_ns":60934210333,"resident_bytes":30162976768,"physical_bytes":30362442768,"peak_physical_bytes":30624177144,"disk_read_bytes":5220745216,"disk_write_bytes":369209344}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":30069,"usage":{"cpu_time_ns":61006942583,"resident_bytes":30170054656,"physical_bytes":30369520656,"peak_physical_bytes":30624177144,"disk_read_bytes":5227823104,"disk_write_bytes":369209344}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":30179,"usage":{"cpu_time_ns":61092643750,"resident_bytes":30170071040,"physical_bytes":30369537040,"peak_physical_bytes":30624177144,"disk_read_bytes":5251203072,"disk_write_bytes":369209344}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":30289,"usage":{"cpu_time_ns":61174474250,"resident_bytes":30170071040,"physical_bytes":30369537040,"peak_physical_bytes":30624177144,"disk_read_bytes":5265276928,"disk_write_bytes":369209344}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":30399,"usage":{"cpu_time_ns":61227649875,"resident_bytes":30170071040,"physical_bytes":30369537040,"peak_physical_bytes":30624177144,"disk_read_bytes":5286395904,"disk_write_bytes":369209344}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":30510,"usage":{"cpu_time_ns":61296737041,"resident_bytes":30170071040,"physical_bytes":30369537040,"peak_physical_bytes":30624177144,"disk_read_bytes":5296275456,"disk_write_bytes":369209344}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":30615,"usage":{"cpu_time_ns":61672150000,"resident_bytes":30172348416,"physical_bytes":30380760080,"peak_physical_bytes":30624177144,"disk_read_bytes":5296275456,"disk_write_bytes":369209344}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":30722,"usage":{"cpu_time_ns":61967489916,"resident_bytes":30172856320,"physical_bytes":30380760080,"peak_physical_bytes":30624177144,"disk_read_bytes":5296275456,"disk_write_bytes":369209344}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":30833,"usage":{"cpu_time_ns":62077499208,"resident_bytes":30174543872,"physical_bytes":30380760080,"peak_physical_bytes":30624177144,"disk_read_bytes":5296275456,"disk_write_bytes":377597952}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":30943,"usage":{"cpu_time_ns":62166982708,"resident_bytes":29337665536,"physical_bytes":29333772152,"peak_physical_bytes":30624177144,"disk_read_bytes":5296357376,"disk_write_bytes":383737856}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":31048,"usage":{"cpu_time_ns":62264277083,"resident_bytes":26074611712,"physical_bytes":29324938824,"peak_physical_bytes":30624177144,"disk_read_bytes":5296357376,"disk_write_bytes":383737856}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":31159,"usage":{"cpu_time_ns":62378628833,"resident_bytes":17460772864,"physical_bytes":29284873680,"peak_physical_bytes":30624177144,"disk_read_bytes":5296357376,"disk_write_bytes":383737856}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":31265,"usage":{"cpu_time_ns":62482826916,"resident_bytes":10172383232,"physical_bytes":29252755760,"peak_physical_bytes":30624177144,"disk_read_bytes":5296357376,"disk_write_bytes":383737856}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":31371,"usage":{"cpu_time_ns":62534506250,"resident_bytes":8705425408,"physical_bytes":25881910376,"peak_physical_bytes":30624177144,"disk_read_bytes":5296357376,"disk_write_bytes":383737856}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":31482,"usage":{"cpu_time_ns":62631362208,"resident_bytes":127090688,"physical_bytes":0,"peak_physical_bytes":30624177144,"disk_read_bytes":5296357376,"disk_write_bytes":383737856}}
|
||||||
|
{"event":"test_resource_summary","elapsed_ms":31586,"peak":{"cpu_time_ns":62631362208,"resident_bytes":30193532928,"physical_bytes":30599748600,"peak_physical_bytes":30624177144,"disk_read_bytes":5296357376,"disk_write_bytes":383737856},"error":null}
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
|
||||||
|
running 1 test
|
||||||
|
test engine::metal::deepseek_vision::tests::issue100_full_model_images_cache_and_dspark ... ds4: Metal device Apple M5 Max, 128.00 GiB RAM
|
||||||
|
ds4: Metal 4 tensor API enabled for Tensor kernels
|
||||||
|
ds4: drift-patch flags hc_stable=on norm_unify=on kv_raw_f32=off rope_exp2_log2=off math_safe=off tensor_matmul=on
|
||||||
|
ds4: Metal SSD streaming mode enabled; full model residency and warmup are skipped
|
||||||
|
ds4: Metal model range 0.47..0.47 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 1.64..1.64 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 1.11..1.11 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 2.19..2.19 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 3.36..3.36 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 2.83..2.84 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 3.85..3.85 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 5.02..5.02 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 4.49..4.49 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 5.44..5.44 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 6.61..6.61 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 6.05..6.06 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 7.13..7.14 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 8.31..8.31 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 7.75..7.75 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 8.81..8.81 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 9.98..9.99 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 9.43..9.43 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 10.42..10.43 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 11.60..11.60 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 11.02..11.02 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 12.18..12.18 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 13.35..13.35 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 12.79..12.79 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 13.85..13.85 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 15.02..15.02 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 14.45..14.46 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 15.58..15.58 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 16.75..16.75 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 16.20..16.20 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 17.23..17.23 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 18.40..18.40 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 17.84..17.84 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 18.68..18.68 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 19.85..19.85 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 19.23..19.23 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 20.54..20.54 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 21.71..21.72 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 21.13..21.14 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 22.19..22.19 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 23.36..23.36 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 22.77..22.77 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 23.95..23.95 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 25.12..25.13 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 24.55..24.56 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 25.53..25.54 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 26.71..26.71 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 26.11..26.11 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 27.39..27.39 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 28.56..28.56 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 28.00..28.01 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 29.06..29.06 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 30.23..30.23 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 29.67..29.68 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 30.43..30.43 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 31.60..31.60 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 30.95..30.96 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 32.47..32.47 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 33.64..33.64 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 33.09..33.10 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 34.15..34.16 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 35.33..35.33 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 34.78..34.78 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 35.77..35.77 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 36.94..36.94 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 36.37..36.37 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 37.39..37.39 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 38.56..38.56 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 37.97..37.98 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 39.26..39.26 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 40.43..40.43 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 39.89..39.90 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 40.70..40.70 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 41.87..41.87 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 41.26..41.27 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 42.53..42.53 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 43.70..43.70 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 43.13..43.13 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 44.05..44.05 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 45.22..45.22 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 44.61..44.61 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 46.04..46.04 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 47.21..47.21 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 46.68..46.68 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 47.54..47.55 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 48.72..48.72 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 48.14..48.14 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 49.01..49.01 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 50.18..50.19 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 49.54..49.55 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 50.94..50.94 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 52.11..52.12 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 51.54..51.54 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 52.39..52.39 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 53.56..53.56 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 52.92..52.92 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 54.16..54.16 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 55.33..55.34 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 54.72..54.72 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 56.08..56.08 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 57.25..57.26 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 56.70..56.70 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 57.64..57.64 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 58.81..58.81 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 58.22..58.22 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 59.45..59.45 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 60.62..60.63 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 60.07..60.07 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 61.01..61.01 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 62.18..62.19 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 61.59..61.60 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 62.77..62.77 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 63.94..63.94 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 63.37..63.37 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 64.15..64.16 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 65.33..65.33 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 64.67..64.68 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 66.07..66.07 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 67.24..67.24 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 66.65..66.65 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 67.62..67.62 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 68.79..68.79 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 68.16..68.17 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 69.51..69.52 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 70.69..70.69 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 70.11..70.12 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 71.09..71.09 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 72.26..72.26 GiB is not covered by mapped model views
|
||||||
|
ds4: Metal model range 71.66..71.66 GiB is not covered by mapped model views
|
||||||
|
red: The image shows a solid red square.
|
||||||
|
DSpark drafted=30 accepted=0
|
||||||
|
blue: The image shows a solid blue circle.
|
||||||
|
pair: The first shape is red, and the second shape is blue.
|
||||||
|
tool image: The color in the image is blue.
|
||||||
|
issue100: production rendering, image recognition, multi-image/tool input, same-layout image invalidation, disk/warm KV and DSpark target equivalence passed
|
||||||
|
ok
|
||||||
|
|
||||||
|
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 569 filtered out; finished in 31.46s
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
|
||||||
|
running 1 test
|
||||||
|
test engine::metal::glm::tests::glm53_two_row_verifier_restores_recurrent_state ... ds4: Metal device Apple M5 Max, 128.00 GiB RAM
|
||||||
|
ds4: Metal 4 tensor API enabled for Tensor kernels
|
||||||
|
ds4: drift-patch flags hc_stable=on norm_unify=on kv_raw_f32=off rope_exp2_log2=off math_safe=off tensor_matmul=on
|
||||||
|
ds4: Metal model views created in 2.025 ms, residency requested in 9751.845 ms, warmup 4.026 ms (mapped 92026.06 MiB from offset 9.07 MiB)
|
||||||
|
ds4: Metal mapped mmaped model as 2 overlapping shared buffers
|
||||||
|
verify2 prefill 0
|
||||||
|
verify2 prefill 4
|
||||||
|
verify2 prefill 8
|
||||||
|
verify2 prefill 12
|
||||||
|
partial prefill HC workspace guards passed
|
||||||
|
partial prefill HC workspace guards passed
|
||||||
|
partial prefill HC workspace guards passed
|
||||||
|
partial prefill HC workspace guards passed
|
||||||
|
partial prefill HC workspace guards passed
|
||||||
|
verify2 prefill 0
|
||||||
|
verify2 prefill 4
|
||||||
|
verify2 prefill 8
|
||||||
|
verify2 prefill 12
|
||||||
|
ds4: GLM MTP verify2 at 12: 44.202 ms, draft 729, target 729
|
||||||
|
ds4: GLM MTP draft 387 at position 13 in 3.1 ms
|
||||||
|
verify2 accepted and rewind keep=0 passed
|
||||||
|
verify2 prefill 0
|
||||||
|
verify2 prefill 4
|
||||||
|
verify2 prefill 8
|
||||||
|
verify2 prefill 12
|
||||||
|
ds4: GLM MTP verify2 at 12: 36.927 ms, draft 729, target 729
|
||||||
|
ds4: GLM MTP draft 387 at position 13 in 2.8 ms
|
||||||
|
verify2 accepted and rewind keep=1 passed
|
||||||
|
verify2 prefill 0
|
||||||
|
verify2 prefill 4
|
||||||
|
verify2 prefill 8
|
||||||
|
verify2 prefill 12
|
||||||
|
ds4: GLM MTP verify2 at 12: 36.681 ms, draft 730, target 729
|
||||||
|
ds4: GLM MTP draft 5733 at position 12 in 2.1 ms
|
||||||
|
verify2 rejected and scalar replay state passed
|
||||||
|
verify2 prefill 0
|
||||||
|
verify2 prefill 4
|
||||||
|
verify2 prefill 8
|
||||||
|
verify2 prefill 12
|
||||||
|
ds4: GLM MTP draft 5733 at position 12 in 3.1 ms
|
||||||
|
verify2 scalar boundary cap=1 cancelled=false passed
|
||||||
|
verify2 prefill 0
|
||||||
|
verify2 prefill 4
|
||||||
|
verify2 prefill 8
|
||||||
|
verify2 prefill 12
|
||||||
|
ds4: GLM MTP draft 5733 at position 12 in 3.0 ms
|
||||||
|
verify2 scalar boundary cap=2 cancelled=true passed
|
||||||
|
verify2 prefill 0
|
||||||
|
verify2 prefill 4
|
||||||
|
verify2 prefill 8
|
||||||
|
verify2 prefill 12
|
||||||
|
ds4: GLM MTP draft 5733 at position 12 in 3.0 ms
|
||||||
|
engine-lifetime counters across new and restored chats passed
|
||||||
|
ok
|
||||||
|
|
||||||
|
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 568 filtered out; finished in 14.15s
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
running 1 test
|
||||||
|
test engine::tokenizer::tests::ds4_chat_matches_original_session_tokens ... ok
|
||||||
|
|
||||||
|
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 569 filtered out; finished in 0.22s
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 697 B |
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
running 1 test
|
||||||
|
test model::transfer::tests::issue100_managed_intake ... verified DeepSeek-V4-Flash-Vision-Encoder.gguf 932857760 bytes
|
||||||
|
verified DeepSeek-V4-Flash-Vision-Exp-DSpark-support.gguf 5989114528 bytes
|
||||||
|
verified DeepSeek-V4-Flash-Vision-Exp-IQ2XXS-w2Q2K-AProjQ8-SExpQ8-OutQ8.gguf 86720111776 bytes
|
||||||
|
ok
|
||||||
|
|
||||||
|
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 569 filtered out; finished in 622.11s
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
Compiling ds4-server v0.1.0 (/Users/gb/Projects/DS4Server)
|
||||||
|
Finished `test` profile [unoptimized + debuginfo] target(s) in 4.00s
|
||||||
|
warning: the following packages contain code that will be rejected by a future version of Rust: block v0.1.6
|
||||||
|
note: to see what the problems were, use the option `--future-incompat-report`, or run `cargo report future-incompatibilities --id 1`
|
||||||
|
Running unittests src/main.rs (target/debug/deps/ds4_server-d15236c42a8179f4)
|
||||||
|
|
||||||
|
running 1 test
|
||||||
|
test engine::validation::tests::issue100_checkpoint_metadata_is_strict ... ok
|
||||||
|
|
||||||
|
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 568 filtered out; finished in 0.01s
|
||||||
|
|
||||||
|
Running unittests tools/qwen38-artifacts.rs (target/debug/deps/qwen38_artifacts-3a34dfcd9c4f3de8)
|
||||||
|
|
||||||
|
running 0 tests
|
||||||
|
|
||||||
|
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 4 filtered out; finished in 0.00s
|
||||||
|
|
||||||
|
Running unittests tools/test-supervisor.rs (target/debug/deps/test_supervisor-978addc9e5e8dbda)
|
||||||
|
|
||||||
|
running 0 tests
|
||||||
|
|
||||||
|
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 4 filtered out; finished in 0.00s
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
{"event":"test_supervisor_started","pid":19964,"limit_bytes":4294967296,"start_seconds":60.0,"idle_seconds":120.0,"sample_ms":100}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":0,"usage":{"cpu_time_ns":155791,"resident_bytes":671744,"physical_bytes":196680,"peak_physical_bytes":213064,"disk_read_bytes":0,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":107,"usage":{"cpu_time_ns":2620666,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1597440,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":217,"usage":{"cpu_time_ns":2620666,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1597440,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":319,"usage":{"cpu_time_ns":2620666,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1597440,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":427,"usage":{"cpu_time_ns":2620666,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1597440,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":537,"usage":{"cpu_time_ns":2620666,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1597440,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":647,"usage":{"cpu_time_ns":2620666,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1597440,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":757,"usage":{"cpu_time_ns":2620666,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1597440,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":866,"usage":{"cpu_time_ns":2620666,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1597440,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":976,"usage":{"cpu_time_ns":2620666,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1597440,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1083,"usage":{"cpu_time_ns":2620666,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1597440,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1187,"usage":{"cpu_time_ns":2620666,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1597440,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1298,"usage":{"cpu_time_ns":2620666,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1597440,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1400,"usage":{"cpu_time_ns":2620666,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1597440,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1501,"usage":{"cpu_time_ns":2620666,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1597440,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1610,"usage":{"cpu_time_ns":2620666,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1597440,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1712,"usage":{"cpu_time_ns":2620666,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1597440,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1816,"usage":{"cpu_time_ns":2620666,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1597440,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":1918,"usage":{"cpu_time_ns":2620666,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1597440,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2025,"usage":{"cpu_time_ns":2620666,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1597440,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2132,"usage":{"cpu_time_ns":2620666,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1597440,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2243,"usage":{"cpu_time_ns":2620666,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1597440,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2344,"usage":{"cpu_time_ns":2620666,"resident_bytes":32768,"physical_bytes":81992,"peak_physical_bytes":98376,"disk_read_bytes":1597440,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2447,"usage":{"cpu_time_ns":2858791,"resident_bytes":376832,"physical_bytes":163912,"peak_physical_bytes":163912,"disk_read_bytes":1597440,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2558,"usage":{"cpu_time_ns":32403000,"resident_bytes":27983872,"physical_bytes":11141864,"peak_physical_bytes":12206824,"disk_read_bytes":3067904,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2666,"usage":{"cpu_time_ns":32403000,"resident_bytes":27983872,"physical_bytes":11141864,"peak_physical_bytes":12206824,"disk_read_bytes":3067904,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2770,"usage":{"cpu_time_ns":32403000,"resident_bytes":27983872,"physical_bytes":11141864,"peak_physical_bytes":12206824,"disk_read_bytes":3067904,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2877,"usage":{"cpu_time_ns":32403000,"resident_bytes":27983872,"physical_bytes":11141864,"peak_physical_bytes":12206824,"disk_read_bytes":3067904,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":2987,"usage":{"cpu_time_ns":32403000,"resident_bytes":27983872,"physical_bytes":11141864,"peak_physical_bytes":12206824,"disk_read_bytes":3067904,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3097,"usage":{"cpu_time_ns":32403000,"resident_bytes":27983872,"physical_bytes":11141864,"peak_physical_bytes":12206824,"disk_read_bytes":3067904,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3207,"usage":{"cpu_time_ns":32403000,"resident_bytes":27983872,"physical_bytes":11141864,"peak_physical_bytes":12206824,"disk_read_bytes":3067904,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3316,"usage":{"cpu_time_ns":32403000,"resident_bytes":27983872,"physical_bytes":11141864,"peak_physical_bytes":12206824,"disk_read_bytes":3067904,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3426,"usage":{"cpu_time_ns":32403000,"resident_bytes":27983872,"physical_bytes":11141864,"peak_physical_bytes":12206824,"disk_read_bytes":3067904,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3536,"usage":{"cpu_time_ns":32403000,"resident_bytes":27983872,"physical_bytes":11141864,"peak_physical_bytes":12206824,"disk_read_bytes":3067904,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3644,"usage":{"cpu_time_ns":32403000,"resident_bytes":27983872,"physical_bytes":11141864,"peak_physical_bytes":12206824,"disk_read_bytes":3067904,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3746,"usage":{"cpu_time_ns":32403000,"resident_bytes":27983872,"physical_bytes":11141864,"peak_physical_bytes":12206824,"disk_read_bytes":3067904,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3856,"usage":{"cpu_time_ns":32403000,"resident_bytes":27983872,"physical_bytes":11141864,"peak_physical_bytes":12206824,"disk_read_bytes":3067904,"disk_write_bytes":0}}
|
||||||
|
{"event":"test_resource_sample","elapsed_ms":3960,"usage":{"cpu_time_ns":38735291,"resident_bytes":46923776,"physical_bytes":21824280,"peak_physical_bytes":24675072,"disk_read_bytes":3256320,"disk_write_bytes":4456448}}
|
||||||
|
{"event":"test_resource_summary","elapsed_ms":4067,"peak":{"cpu_time_ns":38735291,"resident_bytes":46923776,"physical_bytes":21824280,"peak_physical_bytes":24675072,"disk_read_bytes":3256320,"disk_write_bytes":4456448},"error":null}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
running 1 test
|
||||||
|
test engine::metal::deepseek_vision::tests::issue100_visual_routing_and_attention ... ds4: Metal device Apple M5 Max, 128.00 GiB RAM
|
||||||
|
ds4: Metal 4 tensor API enabled for Tensor kernels
|
||||||
|
ds4: drift-patch flags hc_stable=on norm_unify=on kv_raw_f32=off rope_exp2_log2=off math_safe=off tensor_matmul=on
|
||||||
|
ds4: Metal model views created in 0.022 ms, residency requested in 0.060 ms, warmup 4.036 ms (mapped 0.02 MiB from offset 0.00 MiB)
|
||||||
|
ds4: Metal mapped mmaped model as 1 overlapping shared buffers
|
||||||
|
issue100 visual routing: hash/text/image rows and weights pass; raw/mixed/index-masked visual attention with wrapped KV passes
|
||||||
|
ok
|
||||||
|
|
||||||
|
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 569 filtered out; finished in 1.52s
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"source_revision": "e46e16bf6035c6f317eb2ac7458eb0362926d402", "ds4_revision": "0aaea5a238fb41a35106a551e73c8409dfb751ac", "torch": "2.14.1", "device": "mps", "shape": [100, 4096], "max_abs": 0.102294921875, "rmse": 0.005685029085725546, "reference_rms": 0.05688675865530968, "cosine": 0.9950079321861267}
|
||||||
@@ -0,0 +1,613 @@
|
|||||||
|
Compiling ds4-server v0.1.0 (/Users/gb/Projects/DS4Server)
|
||||||
|
Finished `test` profile [unoptimized + debuginfo] target(s) in 5.00s
|
||||||
|
warning: the following packages contain code that will be rejected by a future version of Rust: block v0.1.6
|
||||||
|
note: to see what the problems were, use the option `--future-incompat-report`, or run `cargo report future-incompatibilities --id 1`
|
||||||
|
Running unittests src/main.rs (target/debug/deps/ds4_server-d15236c42a8179f4)
|
||||||
|
|
||||||
|
running 570 tests
|
||||||
|
test agent::tests::ai_risk_response_requires_structured_risk_and_reason ... ok
|
||||||
|
test agent::tests::anchored_edits_require_unique_head_and_tail ... ok
|
||||||
|
test a2ui::tests::streaming_parser_only_accepts_complete_a2ui_jsonl_lines ... ok
|
||||||
|
test agent::tests::ai_shell_risk_assessment_receives_the_dev_brain_root ... ok
|
||||||
|
test a2ui::tests::embedded_catalog_and_validator_cover_the_same_components ... ok
|
||||||
|
test agent::tests::dev_brain_paths_are_trusted_by_shell_risk_assessment ... ok
|
||||||
|
test a2ui::tests::basic_formatters_follow_catalog_examples ... ok
|
||||||
|
test a2ui::tests::catalog_validation_rejects_invalid_variants_and_function_arguments ... ok
|
||||||
|
test a2ui::tests::catalog_accepts_pie_donut_and_heatmap_charts ... ok
|
||||||
|
test a2ui::tests::dismissal_boundaries_replay_history_and_a_fresh_active_epoch ... ok
|
||||||
|
test a2ui::tests::lifecycle_updates_incrementally_and_json_pointer_upserts ... ok
|
||||||
|
test a2ui::tests::latest_created_surface_is_the_active_surface ... ok
|
||||||
|
test agent::tests::present_svg_parsers_recover_exact_source_for_both_model_transports ... ok
|
||||||
|
test agent::tests::ralph_loop_stops_on_cap_blocker_failure_and_cancellation ... ok
|
||||||
|
test a2ui::tests::catalog_validation_and_actions_use_current_local_data ... ok
|
||||||
|
test a2ui::tests::all_basics_compose_into_one_surface ... ok
|
||||||
|
test agent::tests::model_adapters_preserve_primitives_and_report_recoverable_syntax_errors ... ok
|
||||||
|
test agent::tests::ralph_report_semantics_and_handoff_bound_are_enforced ... ok
|
||||||
|
test agent::tests::risky_shell_commands_require_one_time_approval ... ok
|
||||||
|
test a2ui::tests::v1_action_and_function_round_trips_follow_current_schema ... ok
|
||||||
|
test agent::tests::scripted_ralph_backend_gets_fresh_prompts_and_only_validated_handoffs ... ok
|
||||||
|
test agent::tests::project_boundary_rejects_parent_paths ... ok
|
||||||
|
test agent::tests::generated_schemas_match_the_executable_tool_contracts ... ok
|
||||||
|
test agent::tests::ralph_report_schemas_are_child_only_and_parse_for_both_models ... ok
|
||||||
|
test agent::tests::repeated_call_advisories_use_canonical_arguments_and_reset_per_user_turn ... ok
|
||||||
|
test a2ui::export::tests::static_svg_renderer_covers_the_complete_catalog ... ok
|
||||||
|
test agent::tests::stored_tool_cards_restore_ai_approval_reasons_by_call ... ok
|
||||||
|
test agent::tests::tool_output_keeps_terminal_lines_but_parameter_summaries_do_not ... ok
|
||||||
|
test agent::tests::present_svg_executes_without_side_effects_and_maps_only_completed_cards ... ok
|
||||||
|
test agent::web::tests::browser_helpers_preserve_ds4_wire_values ... ok
|
||||||
|
test agent::web::tests::headless_browser_extracts_a_local_page ... ignored, requires an installed Chrome browser
|
||||||
|
test agent::tests::prompts_and_parsers_expose_the_reference_tool_set ... ok
|
||||||
|
test agent::tests::validation_is_precise_and_prevents_side_effects_until_corrected ... ok
|
||||||
|
test app::generation::tests::attached_image_marker_is_model_only ... ok
|
||||||
|
test agent::tests::glob_confinement_skips_symlinks_and_honors_cancellation ... ok
|
||||||
|
test agent::tests::present_svg_validation_enforces_the_static_vector_boundary ... ok
|
||||||
|
test agent::tests::successful_structured_file_tools_report_project_paths ... ok
|
||||||
|
test agent::tests::glob_finds_all_regular_file_names_deterministically_without_reading_them ... ok
|
||||||
|
test agent::tests::standard_agent_skills_are_discovered_prompted_and_read_only ... ok
|
||||||
|
test agent::tests::shell_commands_skip_user_startup_files ... ok
|
||||||
|
test app::generation::tests::plain_chat_uses_the_same_unmodified_prompt_in_ui_and_headless ... ok
|
||||||
|
test app::generation::tests::queued_guidance_is_one_reference_style_user_turn ... ok
|
||||||
|
test app::generation::tests::title_context_starts_at_the_first_user_and_ignores_system_rows ... ok
|
||||||
|
test app::generation::tests::turn_summary_accumulates_model_continuations ... ok
|
||||||
|
test agent::tests::standard_file_tools_can_maintain_only_managed_dev_brain_pages ... ok
|
||||||
|
test agent::tests::shell_environment_is_loaded_once_from_login_and_interactive_startup_files ... ok
|
||||||
|
test app::git::tests::configured_git_diff_options_change_the_rendered_patch ... ok
|
||||||
|
test app::git::tests::unified_diff_parser_aligns_replacements_for_split_view ... ok
|
||||||
|
test agent::tests::ralph_round_contexts_are_unique_and_child_jobs_are_cleaned_up ... ok
|
||||||
|
test app::git::tests::unstage_works_before_the_first_commit ... ok
|
||||||
|
test app::preferences::tests::runtime_config_updates_after_lock_poisoning ... ok
|
||||||
|
test app::git::tests::libgit2_status_merges_staged_and_worktree_changes ... ok
|
||||||
|
test app::projects::tests::discarding_a_session_removes_every_checkpoint_stage ... ok
|
||||||
|
test app::projects::tests::draft_titles_follow_the_stored_session_count ... ok
|
||||||
|
test agent::tests::denial_and_stop_cancel_commands_awaiting_approval ... ok
|
||||||
|
test app::projects::tests::session_states_are_exclusive_and_ordered ... ok
|
||||||
|
test app::projects::tests::sweeping_removes_checkpoints_of_sessions_that_no_longer_exist ... ok
|
||||||
|
test app::projects::tests::git_state_lists_and_switches_local_branches ... ok
|
||||||
|
test app::tests::background_activity_drives_polling_indicators_and_quit_confirmation ... ok
|
||||||
|
test agent::tests::shell_environment_probe_stops_blocked_startup_files ... ok
|
||||||
|
test app::git::tests::origin_fetch_pull_and_push_use_the_current_branch ... ok
|
||||||
|
test app::tests::chat_html_export_embeds_loaded_a2ui_images_as_data_urls ... ok
|
||||||
|
test agent::tests::oversized_shell_output_is_bounded_and_retained_until_cleanup ... ok
|
||||||
|
test app::tests::chat_only_follows_updates_while_near_the_tail ... ok
|
||||||
|
test app::tests::ds4_gib_and_streaming_cache_inputs_are_typed ... ok
|
||||||
|
test app::tests::one_shot_titles_are_reduced_to_a_single_sidebar_line ... ok
|
||||||
|
test app::tests::owned_runtime_paths_stay_in_application_support ... ok
|
||||||
|
test app::tests::preferences_shortcut_and_dspark_support_are_explicit ... ok
|
||||||
|
test app::tests::source_does_not_depend_on_a_sibling_ds4_checkout ... ok
|
||||||
|
test app::tests::stored_session_always_selects_its_model ... ok
|
||||||
|
test app::tests::tab_scrolls_a_field_back_into_the_preferences_viewport ... ok
|
||||||
|
test app::view::a2ui::tests::renderer_input_helpers_cover_dates_numbers_choices_and_weights ... ok
|
||||||
|
test app::view::chat::tests::chat_divider_groups_token_counts_by_thousands ... ok
|
||||||
|
test app::view::chat::tests::composer_uses_shift_enter_for_line_breaks ... ok
|
||||||
|
test app::view::git::tests::split_diff_width_covers_the_longest_side_and_tabs ... ok
|
||||||
|
test app::preferences::tests::multiline_system_prompt_round_trips_through_the_editor ... ok
|
||||||
|
test app::view::tests::action_buttons_share_geometry ... ok
|
||||||
|
test app::view::tests::context_pie_wedge_follows_the_fill_grade ... ok
|
||||||
|
test app::view::tests::download_measurements_are_readable ... ok
|
||||||
|
test app::view::tests::raised_surfaces_stay_dark ... ok
|
||||||
|
test compaction::tests::a_new_summary_replaces_the_previous_rebuild_summary ... ok
|
||||||
|
test compaction::tests::hard_trigger_and_retry_reserve_match_the_reference ... ok
|
||||||
|
test compaction::tests::soft_trigger_matches_reference_thresholds ... ok
|
||||||
|
test compaction::tests::summary_budget_keeps_answer_room_and_rejects_exhausted_contexts ... ok
|
||||||
|
test compaction::tests::summary_control_markup_is_private ... ok
|
||||||
|
test compaction::tests::tail_prefers_the_first_user_boundary_after_target ... ok
|
||||||
|
test config::tests::defaults_write_an_empty_file_and_reload_unchanged ... ok
|
||||||
|
test app::generation::tests::legacy_generation_rows_collapse_into_their_user_turn ... ok
|
||||||
|
test app::preferences::tests::selectors_rehydrate_their_generation_and_model_profiles ... ok
|
||||||
|
test app::tests::chat_export_preserves_completed_svg_and_keeps_failures_generic ... ok
|
||||||
|
test app::generation::tests::forced_compaction_requires_new_visible_chat ... ok
|
||||||
|
test config::tests::deprecated_flash_config_migrates_and_persists_only_0731 ... ok
|
||||||
|
test app::generation::tests::model_history_uses_persisted_content_without_changing_the_transcript ... ok
|
||||||
|
test config::tests::model_profiles_expose_only_their_upstream_thinking_modes ... ok
|
||||||
|
test app::tests::chat_export_formats_visible_turns_reasoning_and_bash_tools ... ok
|
||||||
|
test app::tests::assistant_stream_keeps_reasoning_separate_from_the_answer ... ok
|
||||||
|
test config::tests::legacy_flat_model_settings_are_dropped_but_the_prompt_is_kept ... ok
|
||||||
|
test config::tests::invalid_files_are_reported_instead_of_reset ... ok
|
||||||
|
test app::tests::chat_html_export_is_safe_standalone_and_renders_embedded_surfaces ... ok
|
||||||
|
test config::tests::explicit_0731_dspark_opt_out_survives_the_default ... ok
|
||||||
|
test config::tests::only_changed_values_are_written_and_read_back ... ok
|
||||||
|
test app::view::stats::tests::current_session_summary_uses_persisted_chat_facts ... ok
|
||||||
|
test app::generation::tests::assistant_markdown_keeps_llm_tables ... ok
|
||||||
|
test app::generation::tests::last_compaction_selects_its_tail_without_hiding_history ... ok
|
||||||
|
test app::generation::tests::complete_tool_call_in_reasoning_gets_one_correction ... ok
|
||||||
|
test app::generation::tests::extension_context_deduplicates_session_start_and_rearms_after_compaction ... ok
|
||||||
|
test config::tests::qwen_selection_and_native_defaults_survive_restart ... ok
|
||||||
|
test app::generation::tests::a2ui_auto_switch_waits_for_a_renderable_surface ... ok
|
||||||
|
test dev_brain::tests::revision_fixture_preserves_numeric_looking_strings ... ok
|
||||||
|
test dev_brain::tests::path_confinement_and_unrelated_notes_are_preserved ... ok
|
||||||
|
test dev_brain::tests::skill_index_migration_does_not_overwrite_an_unrelated_root_note ... ok
|
||||||
|
test dsml::tests::parses_reference_and_legacy_dsml_syntaxes ... ok
|
||||||
|
test dsml::tests::recovers_complete_invokes_before_an_incomplete_one ... ok
|
||||||
|
test dsml::tests::recovers_complete_invokes_without_the_outer_closing_tag ... ok
|
||||||
|
test engine::deepseek_vision::tests::issue100_ds4_image_layout_and_attention_vectors ... ok
|
||||||
|
test database::tests::context_work_reactivates_an_archived_session ... ok
|
||||||
|
test database::tests::sessions_are_ordered_by_recent_use_within_sidebar_groups ... ok
|
||||||
|
test engine::ds4_sampling::tests::heap_fallback_preserves_rng_and_signed_zero_ties ... ok
|
||||||
|
test engine::gguf::tests::accepts_exact_mxfp4_block_layout ... ok
|
||||||
|
test engine::gguf::tests::keeps_unknown_tensor_kinds_rejected ... ok
|
||||||
|
test engine::gguf::tests::mxfp4_scalar_dot_matches_independent_values ... ok
|
||||||
|
test engine::gguf::tests::maps_metadata_and_tensor_payload_without_copying_weights ... ok
|
||||||
|
test engine::gguf::tests::rejects_mxfp4_dimension_overflow ... ok
|
||||||
|
test engine::gguf::tests::rejects_mxfp4_rows_that_are_not_block_aligned ... ok
|
||||||
|
test database::tests::inline_svg_tool_call_survives_session_switch_compaction_and_reopen ... ok
|
||||||
|
test engine::gguf::tests::rejects_truncated_mxfp4_payload ... ok
|
||||||
|
test database::tests::a2ui_dismissal_persists_a_fresh_surface_boundary ... ok
|
||||||
|
test engine::metal::deepseek_vision::tests::issue100_encoder_real_weights ... ignored, requires the pinned Vision-Exp sidecar and Apple Metal
|
||||||
|
test engine::metal::deepseek_vision::tests::issue100_full_model_images_cache_and_dspark ... ignored, requires the verified Vision-Exp managed artifacts and 110 GiB of Apple unified memory
|
||||||
|
test engine::metal::deepseek_vision::tests::issue100_visual_routing_and_attention ... ignored, requires Apple Metal; isolated visual routing and attention primitives
|
||||||
|
test engine::metal::glm::tests::dsa_indexer_schedule_matches_the_reference ... ok
|
||||||
|
test engine::metal::glm::tests::glm53_m5_text_performance_gate ... ignored, requires the installed GLM 5.3 Flash Q2 checkpoint and Apple Metal
|
||||||
|
test engine::metal::glm::tests::glm53_m5_vision_performance_and_oracle_gate ... ignored, requires GLM 5.3 Q2, its vision sidecar, a fixed image, and Apple Metal
|
||||||
|
test engine::metal::glm::tests::glm53_reference_logits_replay_separates_sampling_from_execution ... ignored, requires the installed GLM 5.3 Flash Q2 checkpoint and Apple Metal
|
||||||
|
test engine::metal::glm::tests::glm53_reference_prompt_tokens_match_shared_runtime ... ignored, installed GLM and DS4SERVER_GLM_REFERENCE_LOG from the standalone reference
|
||||||
|
test engine::metal::glm::tests::glm53_two_row_verifier_restores_recurrent_state ... ignored, installed GLM 5.3 and Metal; run under test-supervisor
|
||||||
|
test engine::metal::glm::tests::glm_byte_budget_reserves_prefill_before_dynamic_experts ... ok
|
||||||
|
test engine::metal::glm::tests::glm_mtp_preserves_target_tokens_and_drafts ... ignored, requires the 197 GiB GLM 5.2 checkpoint and Apple Metal
|
||||||
|
test engine::metal::glm::tests::glm_prefill_schedule_matches_ds4_boundaries ... ok
|
||||||
|
test engine::metal::glm::tests::glm_resident_decode_flushes_periodically_without_a_final_or_ssd_flush ... ok
|
||||||
|
test engine::metal::glm::tests::repeated_glm_prompt_rewinds_one_token_for_logits ... ok
|
||||||
|
test engine::metal::glm::tests::resident_and_streamed_glm_match_ds4_decode_oracles ... ignored, requires the 197 GiB GLM 5.2 checkpoint and Apple Metal
|
||||||
|
test engine::metal::glm::tests::streamed_glm_uses_ds4_indexed_prefill_for_long_prompts ... ignored, requires the 197 GiB GLM 5.2 checkpoint and Apple Metal
|
||||||
|
test engine::metal::gpu::qwen_mtplx_allocation_tests::backend_allocation_policy_is_explicit_inside_and_outside_qwen_scope ... ignored, requires Apple Metal; allocation only, no GPU dispatch or model
|
||||||
|
test engine::metal::gpu::qwen_mtplx_allocation_tests::dispatch_retains_allocation_without_changing_data_donation ... ignored, requires Apple Metal, no model weights
|
||||||
|
test engine::metal::gpu::qwen_mtplx_allocation_tests::empty_storage_preserves_data_identity_views_and_zero_allocator_usage ... ignored, requires Apple Metal context; empty descriptors, no GPU allocation
|
||||||
|
test engine::metal::gpu::qwen_mtplx_allocation_tests::mtplx_tensor_cpu_access_releases_native_storage ... ignored, requires Apple Metal; CPU buffer access only, no GPU dispatch or model
|
||||||
|
test engine::metal::markov::tests::installed_markov_worker_dispatch ... ignored, requires installed 0731 DSpark support; CPU dispatch diagnostic, not parity acceptance
|
||||||
|
test database::tests::migrations_and_crud_work ... ok
|
||||||
|
test engine::kvstore::tests::opening_reclaims_checkpoints_the_index_cannot_reach ... ok
|
||||||
|
test engine::metal::markov::tests::markov_workers_reuse_threads_and_buffers_with_ordered_ties ... ok
|
||||||
|
test engine::metal::power_throttle_keeps_ds4_ewma_and_full_power_bypass ... ok
|
||||||
|
test engine::metal::qwen::tests::affine_split_k_matches_reference_shapes_and_scratch_bound ... ok
|
||||||
|
test engine::metal::qwen::tests::dense_attention_crossover_is_phase_specific ... ok
|
||||||
|
test engine::metal::qwen::tests::qwen_affine_qmv_and_qmm_match_mlx_reference ... ignored, requires the pinned Qwen artifact set and Apple Metal
|
||||||
|
test engine::metal::qwen::tests::qwen_affine_qmv_profile ... ignored, requires the pinned 105 GB Qwen artifact set and Apple Metal
|
||||||
|
test engine::metal::qwen::tests::qwen_core_boundary_and_checkpoint_are_stable ... ignored, requires the pinned 105 GB Qwen artifact set and Apple Metal
|
||||||
|
test engine::metal::qwen::tests::qwen_fused_gdn_input_rows_match_separate_projections ... ignored, requires the pinned Qwen artifact set and Apple Metal
|
||||||
|
test engine::metal::qwen::tests::qwen_gdn_conv_norm_rows_matches_mtplx ... ignored, requires Apple Metal
|
||||||
|
test engine::metal::qwen::tests::qwen_gdn_delta_rows_matches_mtplx ... ignored, requires Apple Metal
|
||||||
|
test engine::metal::qwen::tests::qwen_layer_profile ... ignored, requires the pinned 105 GB Qwen artifact set and Apple Metal
|
||||||
|
test engine::metal::qwen::tests::qsa_selection_and_capacity_cover_the_full_native_window ... ok
|
||||||
|
test engine::metal::qwen::tests::qwen_lazy_target_probabilities_match_batched_sampling ... ignored, requires the pinned Qwen metadata and Apple Metal; no model inference
|
||||||
|
test engine::metal::qwen::tests::qwen_m5_performance_gate ... ignored, requires the pinned 105 GB Qwen artifact set and Apple Metal
|
||||||
|
test engine::metal::qwen::tests::qwen_metal_primitives_match_reference_vectors ... ignored, requires Apple Metal
|
||||||
|
test engine::metal::qwen::tests::qwen_mtp_matches_target_and_restores_its_cache ... ignored, requires the pinned 105 GB Qwen artifact set and Apple Metal
|
||||||
|
test engine::metal::qwen::tests::qwen_ple_gather_copies_only_the_requested_row ... ok
|
||||||
|
test engine::metal::qwen::tests::qwen_qsa_dense_mask_accepts_unsorted_blocks ... ignored, requires Apple Metal
|
||||||
|
test engine::metal::qwen::tests::qwen_qsa_full_context_preallocation_succeeds ... ignored, requires the pinned 105 GB Qwen artifact set and Apple Metal
|
||||||
|
test engine::metal::qwen::tests::qwen_qsa_logical_depth_oracles_cover_prefill_decode_and_state ... ignored, requires the pinned 105 GB Qwen artifact set and Apple Metal
|
||||||
|
test engine::metal::qwen::tests::qwen_qsa_real_layer_executes_the_first_sparse_token ... ignored, requires the pinned 105 GB Qwen artifact set and Apple Metal
|
||||||
|
test engine::metal::qwen::tests::qwen_qsa_selection_and_attention_remain_bounded_at_native_depths ... ignored, requires Apple Metal and allocates the full QSA score window
|
||||||
|
test engine::metal::qwen::tests::qwen_reference_layer_trace ... ignored, diagnostic trace against the pinned MLX-LM reference
|
||||||
|
test engine::metal::qwen::tests::qwen_reference_sdpa_trace ... ignored, diagnostic comparison with captured MTPLX SDPA inputs
|
||||||
|
test engine::metal::qwen::tests::qwen_reference_stateful_trace ... ignored, diagnostic trace against the pinned MLX-LM reference
|
||||||
|
test engine::metal::qwen::tests::qwen_reference_verify_trace ... ignored, diagnostic trace against the pinned MTPLX verifier
|
||||||
|
test engine::metal::qwen::tests::qwen_sorted_routed_prefill_matches_grouped_output ... ignored, requires the pinned 105 GB Qwen artifact set and Apple Metal
|
||||||
|
test engine::metal::qwen::tests::qwen_stage_counters_preserve_async_batches ... ignored, requires pinned Qwen embeddings and Apple Metal; stage profiler opt-in
|
||||||
|
test engine::metal::qwen::tests::qwen_target_submission_profile ... ignored, requires the pinned 105 GB Qwen artifact set and Apple Metal
|
||||||
|
test engine::metal::qwen::tests::qwen_verify_layer_profile ... ignored, requires the pinned 105 GB Qwen artifact set and Apple Metal
|
||||||
|
test engine::metal::qwen_mtplx::allocator::mtplx_allocations_preserve_logical_bounds_with_reference_rounding ... ignored, requires Apple Metal, no model weights
|
||||||
|
test engine::kvstore::tests::budget_evicts_the_least_valuable_unprotected_entry ... ok
|
||||||
|
test engine::metal::qwen::tests::qwen_ple_hash_contract_matches_golden_boundaries ... ok
|
||||||
|
test engine::metal::qwen_mtplx::allocator::mtplx_native_allocator_heap_limits_and_gpu_lifetime ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx::allocator::mtplx_native_allocator_replays_reference_allocations ... ignored, requires Apple Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx::ar::mtplx_ar_driver_matches_reference ... ignored, requires Apple Metal; unchanged MTPLX generate_ar orchestration oracle
|
||||||
|
test engine::metal::qwen_mtplx::array::mtplx_array_detach_clears_every_siblings_inputs_and_primitive ... ok
|
||||||
|
test engine::metal::qwen_mtplx::array::mtplx_array_materialization_keeps_geometry_and_rejects_short_storage ... ignored, requires Apple Metal; typed materialized descriptors without model weights
|
||||||
|
test engine::metal::qwen_mtplx::array::mtplx_array_layout_preserves_dtype_shape_stride_and_materialization_flags ... ok
|
||||||
|
test engine::metal::qwen_mtplx::array::mtplx_dtype_promotion_matches_all_reference_pairs ... ok
|
||||||
|
test engine::metal::qwen_mtplx::attention::mtplx_qsa_dense_mask_graph_matches_reference ... ignored, requires Apple Metal; original MTPLX block mask including sentinel and tails
|
||||||
|
test engine::metal::qwen_mtplx::attention::mtplx_qsa_eager_array_graph_matches_reference ... ignored, requires Apple Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx::attention::mtplx_qsa_prefill_gather_graph_matches_reference ... ignored, requires Apple Metal; actual MTPLX tiled prefill gather
|
||||||
|
test engine::metal::qwen_mtplx::attention::mtplx_qsa_rows_gather_graph_matches_reference ... ignored, requires Apple Metal; actual MTPLX QSA rows-gather attention
|
||||||
|
test engine::metal::qwen_mtplx::attention::mtplx_sdpa_fallback_graph_matches_reference ... ignored, requires Apple M5 Max; actual Qwen SDPA fallback reference
|
||||||
|
test engine::metal::qwen_mtplx::binary::mtplx_graph_binary_preserves_reference_branches ... ignored, requires Apple Metal; binary graph broadcast/layout and donation branches
|
||||||
|
test engine::metal::qwen_mtplx::binding::mtplx_installed_module_binding_matches_reference ... ignored, requires installed DS4_QWEN38_ARTIFACTS and SSD policy; only small norms and four table rows evaluated
|
||||||
|
test engine::metal::qwen_mtplx::cache::mtplx_kv_cache_graph_matches_reference ... ignored, requires Apple Metal; actual MTPLX KVCache growth, trim and state receipts
|
||||||
|
test database::tests::session_model_migration_preserves_data_and_accepts_catalog ... ok
|
||||||
|
test engine::metal::qwen_mtplx::allocator::mtplx_allocator_cache_matches_live_reference_trace ... ok
|
||||||
|
test engine::metal::qwen_mtplx::cache::mtplx_qsa_cache_graph_matches_reference ... ignored, requires Apple Metal; actual MTPLX positional cache, mirror and held state aliases
|
||||||
|
test engine::metal::qwen_mtplx::compile_graph::mtplx_compile_scalar_bits_and_custom_kernel_identity ... ignored, requires Metal buffers and external supervisor; scalar ownership only, no GPU dispatch
|
||||||
|
test engine::kvstore::tests::longest_compatible_prefix_wins_and_hits_survive_reopen ... ok
|
||||||
|
test engine::metal::qwen_mtplx::decoder::mtplx_decoder_connected_attention_and_moe_advance_caches ... ignored, requires Apple Metal and external test-supervisor; complete decoder/cache smoke
|
||||||
|
test engine::metal::qwen_mtplx::context_copy::prompt_copy_match_and_backoff_preserve_reference_policy ... ok
|
||||||
|
test engine::metal::qwen_mtplx::dense::mtplx_dense_attention_batches_match_reference ... ignored, requires Apple M5 Max; actual BF16 batched matmul reference
|
||||||
|
test engine::metal::qwen_mtplx::dense::mtplx_dense_float_graph_matches_reference ... ignored, requires Apple M5 Max; floating Matmul factory and original dispatch
|
||||||
|
test engine::metal::qwen_mtplx::dense::mtplx_qsa_score_array_graph_matches_reference ... ignored, requires Apple M5 Max; canonical QSA F32 graph with the original TF32 setting
|
||||||
|
test engine::metal::qwen_mtplx::encoder::mtplx_empty_bindings_preserve_nil_dependencies_without_dummy_storage ... ignored, requires Apple Metal; empty original-kernel grids and nil bindings
|
||||||
|
test engine::metal::qwen_mtplx::decoder::mtplx_decoder_residual_preserves_graph_order ... ok
|
||||||
|
test engine::metal::qwen_mtplx::encoder::mtplx_encoder_commits_after_primitives_and_balances_completion ... ignored, requires Apple Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx::compiled::mtplx_replay_replaces_descriptors_without_rebuilding_primitives ... ok
|
||||||
|
test engine::metal::qwen_mtplx::encoder::mtplx_encoder_barrier_epochs_and_commit_thresholds ... ok
|
||||||
|
test engine::metal::qwen_mtplx::encoder::mtplx_encoder_scope_routes_operators_and_allocator ... ignored, requires Apple Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx::encoder::mtplx_encoder_shared_events_and_error_completion_order ... ignored, requires Apple Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx::encoder::mtplx_encoder_untracked_dependencies_across_encoders_and_commands ... ignored, requires Apple Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx::eval::execution_tests::mtplx_eval_drains_failure_preserves_cause_and_retries_only_failed_nodes ... ignored, requires Apple Metal; supervised evaluator failure and retry
|
||||||
|
test engine::metal::qwen_mtplx::eval::execution_tests::mtplx_eval_releases_intermediates_and_preserves_descriptor_donation ... ignored, requires Apple Metal; graph ownership and last-consumer release
|
||||||
|
test engine::metal::qwen_mtplx::eval::execution_tests::mtplx_eval_retains_tracer_topology_without_input_donation ... ignored, requires Apple Metal; evaluator tracer donation and retained topology
|
||||||
|
test engine::metal::qwen_mtplx::eval::execution_tests::mtplx_eval_runs_cpu_gpu_siblings_and_async_continuations ... ignored, requires Apple Metal; tiny supervised CPU/GPU evaluator graph
|
||||||
|
test engine::metal::qwen_mtplx::eval::mtplx_eval_tape_tracks_stream_fences_per_leaf_and_cross_device_union ... ok
|
||||||
|
test engine::metal::qwen_mtplx::execution::mtplx_full_installed_ar_matches_reference ... ignored, full installed model; external guard and DS4_QWEN38_AR_REFERENCE required
|
||||||
|
test engine::metal::qwen_mtplx::eval::mtplx_eval_tape_preserves_bfs_width_and_sibling_choice ... ok
|
||||||
|
test engine::metal::qwen_mtplx::execution::mtplx_full_installed_mtp_prefill_matches_reference ... ignored, full installed MTP model; external guard and DS4_QWEN38_MTP_PREFILL_REFERENCE required
|
||||||
|
test engine::metal::qwen_mtplx::eval::mtplx_eval_tape_preserves_scheduled_detach_and_tracer_errors ... ok
|
||||||
|
test engine::metal::qwen_mtplx::execution::mtplx_full_installed_ping_forward_matches_reference ... ignored, full installed model; requires external memory guard and DS4_QWEN38_FORWARD_REFERENCE
|
||||||
|
test engine::metal::qwen_mtplx::execution::qwen_power_throttle_drains_before_sleep_and_bypasses_at_full_power ... ignored, requires Apple Metal; run under test-supervisor
|
||||||
|
test engine::metal::qwen_mtplx::fused::mtplx_compiled_constant_spelling ... ok
|
||||||
|
test engine::metal::qwen_mtplx::fused::mtplx_compiled_fusion_gpu_matches_reference ... ignored, requires Metal and external supervisor; generated fusion vs pinned SiLU plus multi-output/scalar dispatch
|
||||||
|
test engine::metal::qwen_mtplx::gdn::mtplx_graph_gdn_forward_matches_model_reference ... ignored, requires Apple Metal; actual MTPLX GDN forward and cache receipts
|
||||||
|
test engine::metal::qwen_mtplx::gdn::mtplx_graph_gdn_prefill_and_verify_match_model_reference ... ignored, requires Apple Metal; complete GDN Conv1d prefill and fused verify graphs
|
||||||
|
test engine::metal::qwen_mtplx::gdn::mtplx_graph_gdn_staged_primitives_match_reference ... ignored, requires Apple Metal; 80 actual delta-update receipts and six conv-normalization kernels
|
||||||
|
test engine::metal::qwen_mtplx::gdn::mtplx_graph_gdn_step_preserves_views_and_siblings ... ignored, requires Apple Metal; GDN custom primitive strided copies and sibling roots
|
||||||
|
test engine::metal::qwen_mtplx::graph_sync::mtplx_graph_event_snapshots_preserve_values_streams_and_shared_errors ... ignored, requires Apple Metal events, no model weights
|
||||||
|
test engine::metal::qwen_mtplx::graph_sync::mtplx_graph_fence_coherence_preserves_empty_and_partial_word_spans ... ignored, requires Apple Metal3, model-free empty coherent dispatch
|
||||||
|
test engine::metal::qwen_mtplx::graph_sync::mtplx_graph_fences_transfer_cpu_gpu_gpu_cpu_in_both_modes ... ignored, requires Apple Metal3, tiny model-free coherent fence kernels
|
||||||
|
test engine::metal::qwen_mtplx::hyper::mtplx_graph_hyper_forward_matches_reference ... ignored, requires Apple M5 Max; actual MTPLX GatedResidual reference receipts
|
||||||
|
test engine::metal::qwen_mtplx::hyper::mtplx_graph_hyper_pack_and_reads_match_reference ... ignored, requires Apple Metal; original hyper primitives and runtime pack
|
||||||
|
test engine::metal::qwen_mtplx::indexing::mtplx_graph_indexing_preserves_layouts_and_offsets ... ignored, requires Apple Metal; Select layout branches, GatherAxis views and empty arguments
|
||||||
|
test engine::metal::qwen_mtplx::indexing::mtplx_scatter_axis_graph_matches_reference ... ignored, requires Apple Metal; pinned Bool ScatterAxis layout and broadcast paths
|
||||||
|
test engine::metal::qwen_mtplx::issue103_analysis::issue103_numeric_receipts ... ignored, #103 diagnostic; Metal required, eager versus compiled numeric operands
|
||||||
|
test engine::metal::qwen_mtplx::issue103_analysis::issue103_sampling_receipts ... ignored, #103 diagnostic; Metal required, pins currently broken sampling law and invalid rows
|
||||||
|
test engine::metal::qwen_mtplx::issue103_analysis::issue103_sorted_gather_receipts ... ignored, #103 diagnostic; M5 and pinned runtime shader required; initialized sentinels
|
||||||
|
test engine::metal::qwen_mtplx::issue103_analysis::issue103_tokenizer_receipts ... ignored, #103 diagnostic; installed tokenizer and latest oracle JSONL; no GPU
|
||||||
|
test engine::metal::qwen_mtplx::issue103_analysis::issue103_vision_position_receipts ... ignored, #103 diagnostic; small active attention module on Metal; mixed position boundary
|
||||||
|
test engine::metal::qwen_mtplx::issue104::issue104_installed_ar_stream_and_state ... ignored, full pinned Bare-Speed model; external memory guard required
|
||||||
|
test engine::metal::qwen_mtplx::issue105::issue105_batched_greedy_rejects_each_invalid_row ... ignored, Metal required; run serially under test-supervisor
|
||||||
|
test engine::metal::qwen_mtplx::issue106::issue106_complete_state_and_checkpoint_recovery ... ignored, installed model and actual pre-fix checkpoint; external memory guard required
|
||||||
|
test engine::metal::qwen_mtplx::issue106::issue106_upstream_exit_matrix ... ignored, installed model, scripted logits on real caches; external memory guard required
|
||||||
|
test engine::metal::qwen_mtplx::issue108::issue108_attention_chunk_positions ... ignored, Metal and pinned MTPLX chunk fixtures; external supervisor required
|
||||||
|
test engine::metal::qwen_mtplx::issue109::issue109_gate_phase_and_dtype_laws ... ignored, Metal and exhaustive pinned gate fixtures; external supervisor required
|
||||||
|
test engine::metal::qwen_mtplx::issue110::issue110_actual_projection_rows_match_oracle ... ignored, installed layer weights and pinned oracle receipts; external memory supervisor required
|
||||||
|
test engine::metal::qwen_mtplx::issue110::issue110_guard_allocation_refusal_releases_graph_and_retries ... ignored, Metal allocation refusal and retry; external supervisor required
|
||||||
|
test engine::metal::qwen_mtplx::issue110::issue110_large_cold_and_warm_prefill ... ignored, full installed model and large partial chunks; serial memory supervisor required
|
||||||
|
test engine::metal::qwen_mtplx::issue112_bench::issue112_hidden_prefill ... ignored, full installed model hidden-state prefill; serial supervisor required
|
||||||
|
test engine::metal::qwen_mtplx::issue112_bench::issue112_paired_prefill ... ignored, full installed model; serial memory supervisor and explicit output directory
|
||||||
|
test engine::metal::qwen_mtplx::linear::mtplx_graph_affine_dequantize_matches_reference ... ignored, requires Apple Metal; original table dequantize dtype/group/bit/layout receipts
|
||||||
|
test engine::metal::qwen_mtplx::linear::mtplx_graph_affine_quantize_matches_reference ... ignored, requires Apple Metal; original affine quantize dtype/group/bit/layout receipts
|
||||||
|
test engine::metal::qwen_mtplx::linear::mtplx_graph_gather_qmm_matches_reference ... ignored, requires Apple Metal; original GatherQMM array layouts and expert routing
|
||||||
|
test engine::metal::qwen_mtplx::linear::mtplx_graph_quantized_linear_batched_identity ... ignored, requires Apple Metal and external supervisor; strided/broadcast batched affine dispatch
|
||||||
|
test engine::metal::qwen_mtplx::linear::mtplx_graph_quantized_linear_matches_reference ... ignored, requires Apple M5 Max Metal; 78 pinned affine fixtures through lazy graph
|
||||||
|
test engine::metal::qwen_mtplx::linear::sorted_gather_guard_matches_pinned_call_predicates ... ok
|
||||||
|
test engine::metal::qwen_mtplx::load::mtplx_safetensors_load_laziness_boundaries_and_lifetime ... ignored, requires Apple Metal; bounded CPU Load and dependent GPU graph
|
||||||
|
test engine::metal::qwen_mtplx::mlp::mtplx_graph_shared_mlp_matches_reference ... ignored, requires Apple M5 Max; actual MTPLX shared MLP reference receipts
|
||||||
|
test engine::metal::qwen_mtplx::mlp::mtplx_graph_split_and_compiled_silu_layouts ... ignored, requires Apple Metal; Split sibling ownership and compiled SiLU layouts
|
||||||
|
test engine::metal::qwen_mtplx::model::mtplx_model_compile_policy_and_run_boundaries ... ok
|
||||||
|
test engine::metal::qwen_mtplx::moe::mtplx_graph_sparse_moe_matches_reference ... ignored, requires Apple M5 Max; full MoE Array graph, no model weights; use test-supervisor
|
||||||
|
test engine::metal::qwen_mtplx::moe_sorted::issue112_all_kernel_variants ... ignored, Metal synthetic all supported row-map variants; external supervisor
|
||||||
|
test engine::metal::qwen_mtplx::moe_sorted::issue112_canary_failures ... ignored, Metal canary rejection and sticky fallback; external supervisor
|
||||||
|
test engine::metal::qwen_mtplx::moe_sorted::issue112_full_model_state ... ignored, full installed model; validates all cold/warm state receipts against the pre-port oracle
|
||||||
|
test engine::metal::qwen_mtplx::moe_sorted::issue112_installed_gate_up ... ignored, installed projection weights; external supervisor required
|
||||||
|
test engine::metal::qwen_mtplx::moe_sorted::row_map_applicability ... ok
|
||||||
|
test engine::metal::qwen_mtplx::fused::mtplx_compile_fusion_preserves_parent_and_output_boundaries ... ok
|
||||||
|
test engine::metal::qwen_mtplx::ngram::mtplx_ngram_embedding_matches_reference ... ignored, requires Apple Metal; full resident NGram graph and lazy decode history
|
||||||
|
test engine::metal::qwen_mtplx::ngram::mtplx_ngram_gpu_rows_and_history_match_reference ... ignored, requires Apple Metal; original lazy NGram GPU row graph, no table
|
||||||
|
test engine::metal::qwen_mtplx::ngram::mtplx_ngram_resident_matches_reference ... ignored, requires Apple Metal; original NGramTable resident gather/dequantize receipts
|
||||||
|
test engine::metal::qwen_mtplx::ngram::mtplx_ngram_table_priority ... ignored, requires Apple Metal; NGram table routing and missing-sidecar behavior
|
||||||
|
test engine::metal::qwen_mtplx::ngram_sidecar::mtplx_ngram_sidecar_matches_reference ... ignored, requires Apple Metal; actual file-backed MTPLX sidecar/LRU/dequantization receipts
|
||||||
|
test engine::metal::qwen_mtplx::ngram_stage::mtplx_ngram_stage_allclose_branches ... ignored, requires Apple Metal; stage verification special values and original reduction branches
|
||||||
|
test engine::metal::qwen_mtplx::ngram_stage::mtplx_ngram_stage_matches_reference ... ignored, requires Apple Metal; original complete stage lifecycle and file-backed tables
|
||||||
|
test engine::metal::qwen_mtplx::normalization::mtplx_graph_norm_modules_match_reference ... ignored, requires Apple Metal; connected Qwen normalization graph golden fixtures
|
||||||
|
test engine::metal::qwen_mtplx::normalization::mtplx_graph_rms_norm_preserves_reference_layout_and_values ... ignored, requires Apple Metal; graph RMSNorm donation, copy, broadcast and looped kernels
|
||||||
|
test engine::metal::qwen_mtplx::normalization::mtplx_logsumexp_graph_matches_reference ... ignored, requires Apple Metal; original sampler LogSumExp branches
|
||||||
|
test engine::metal::qwen_mtplx::normalization::mtplx_softmax_graph_storage_matches_reference ... ignored, requires Apple Metal; original Softmax type and storage branches
|
||||||
|
test engine::metal::qwen_mtplx::ops::mtplx_concatenate_preserves_general_general_copy_semantics ... ignored, requires Apple Metal; Concatenate promotion, offsets, strided writers and validation
|
||||||
|
test engine::metal::qwen_mtplx::ops::mtplx_owned_operations_build_and_execute_qsa_layout_graph ... ignored, requires Apple Metal; self-contained lazy QSA-layout recipe, no callback map
|
||||||
|
test engine::metal::qwen_mtplx::ops::mtplx_slice_and_contiguous_preserve_cache_window_semantics ... ignored, requires Apple Metal; normalized Slice and Contiguous cache-window graph
|
||||||
|
test engine::metal::qwen_mtplx::owner::issue105_product::issue105_product_ar_cleanup_and_http_retry ... ignored, installed Qwen model; serial test-supervisor with memory guard required
|
||||||
|
test engine::metal::qwen_mtplx::owner::issue105_product::issue105_product_mtp_cleanup_and_http_retry ... ignored, installed Qwen MTP model; serial test-supervisor with memory guard required
|
||||||
|
test engine::metal::qwen_mtplx::owner::issue106_product::issue106_product_discards_old_checkpoint_and_recovers_cold ... ignored, installed model and actual pre-fix checkpoint; external memory guard required
|
||||||
|
test engine::metal::qwen_mtplx::fused::mtplx_compiled_emitter_matches_all_pinned_silu_bodies ... ok
|
||||||
|
test engine::metal::qwen_mtplx::owner::issue107_product::issue107_vision_product ... ignored, installed model plus vision tower; serial external memory supervisor required
|
||||||
|
test engine::metal::qwen_mtplx::owner::issue109_product::issue109_verify_rows_and_warm_checkpoint ... ignored, installed model; serial external memory supervisor required
|
||||||
|
test engine::metal::qwen_mtplx::owner::issue109_product::issue113_default_verify_and_checkpoint ... ignored, installed model and actual pre-head-chunk checkpoint; serial external memory supervisor
|
||||||
|
test engine::metal::qwen_mtplx::owner::issue111_product::issue111_token_prefix_cache ... ignored, installed model; serial external memory supervisor required
|
||||||
|
test engine::metal::qwen_mtplx::owner::issue116_product::issue116_normalized_token_cache ... ignored, installed model; serial external memory supervisor required
|
||||||
|
test engine::metal::qwen_mtplx::owner::issue117_product::issue117_reasoning_history_cache ... ignored, installed model; serial external memory supervisor required
|
||||||
|
test engine::metal::qwen_mtplx::owner::qwen_product_generator_chat_matches_reference ... ignored, full installed model through product Generator; external supervisor and chat reference required
|
||||||
|
test engine::metal::qwen_mtplx::owner::qwen_vision_chat_checkpoint_preserves_image_identity ... ignored, requires installed Qwen, Metal, and DS4_QWEN_VISION_IMAGE; run under test-supervisor
|
||||||
|
test engine::metal::qwen_mtplx::owner::qwen_mtp_progress_publishes_without_turn_completion_or_double_counting ... ok
|
||||||
|
test engine::metal::qwen_mtplx::ple::mtplx_graph_ple_connected_matches_reference ... ignored, requires Apple M5 Max; complete original PLELayer with real lookup
|
||||||
|
test engine::metal::qwen_mtplx::ple::mtplx_graph_ple_projection_and_cache_match_reference ... ignored, requires Apple M5 Max; actual MTPLX PLELayer after embedding lookup
|
||||||
|
test engine::metal::qwen_mtplx::policy::qwen_server_policy_matches_original_functions ... ignored, requires DS4_QWEN38_POLICY_REFERENCE from pinned MTPLX; CPU-only
|
||||||
|
test engine::metal::qwen_mtplx::policy::sorted_moe_switch_and_family_rehearsal ... ok
|
||||||
|
test engine::metal::qwen_mtplx::prefill::mtplx_mtp_prefill_driver_matches_reference ... ignored, requires Apple Metal; original streaming MTP history prefill
|
||||||
|
test engine::metal::qwen_mtplx::prefill::mtplx_prefill_driver_matches_reference ... ignored, requires Apple Metal; original _prefill control flow and raw cache roots
|
||||||
|
test engine::metal::qwen_mtplx::qsa_attention::mtplx_qsa_attention_module_matches_reference ... ignored, requires Metal and unchanged whole-Attention receipts; no model download
|
||||||
|
test engine::metal::qwen_mtplx::qsa_compile::mtplx_compiled_indexer_array_replay_matches_reference ... ignored, requires Metal and original compiled indexer receipts; no model weights
|
||||||
|
test engine::metal::qwen_mtplx::qsa_core::mtplx_qsa_core_host_matches_reference ... ignored, requires Metal and original host-core receipts; no model weights
|
||||||
|
test engine::metal::qwen_mtplx::qsa_graph::qsa_cache_donation_requires_descriptor_data_and_allocation_contract ... ignored, requires Apple Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx::qsa_graph::qsa_completion_owns_buffers_until_gpu_finishes ... ignored, requires Apple Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx::qsa_graph::qsa_evaluator_retains_inputs_and_siblings_not_primary_data ... ignored, requires Apple Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx::policy::retired_ar_pipeline_flag_cannot_change_server_or_turn_policy ... ok
|
||||||
|
test engine::metal::qwen_mtplx::qsa_indexer::issue107::issue107_real_geometry_query_and_block_start_positions ... ignored, Metal and independently verified pinned indexer fixtures; external supervisor required
|
||||||
|
test engine::metal::qwen_mtplx::qsa_indexer::mtplx_qsa_canonical_indexer_routes_match_reference ... ignored, requires Metal and original ongoing indexer routing receipts; no weights
|
||||||
|
test engine::metal::qwen_mtplx::qsa_graph::qsa_schedule_preserves_the_selected_sibling ... ok
|
||||||
|
test engine::metal::qwen_mtplx::qsa_indexer::mtplx_qsa_indexer_compiled_commit_matches_reference ... ignored, requires Metal and original indexer lifecycle receipts; no model weights
|
||||||
|
test engine::metal::qwen_mtplx::qsa_indexer::mtplx_qsa_indexer_prefix_matches_reference ... ignored, requires Apple Metal, original stateful QSAIndexer receipts, no model weights
|
||||||
|
test engine::metal::qwen_mtplx::qsa_prefill::mtplx_qsa_prefill_graph_matches_reference ... ignored, requires Metal and original two-stage prefill receipts, no weights
|
||||||
|
test engine::metal::qwen_mtplx::qsa_prepare::mtplx_qsa_prepare_graph_matches_reference ... ignored, requires Apple Metal; parameterized original QSA preparation and traced positions
|
||||||
|
test engine::metal::qwen_mtplx::qsa_select::mtplx_qsa_fused_graph_matches_reference ... ignored, requires Apple Metal and original QSA selector receipts; no model weights
|
||||||
|
test engine::metal::qwen_mtplx::random::mtplx_sampler_prefix_matches_reference ... ignored, requires Apple Metal; original target-prefix sampler and default RNG
|
||||||
|
test engine::metal::qwen_mtplx::random::mtplx_sampler_resident_matches_reference ... ignored, requires Apple Metal; original MTPLX resident sampling and keyed RNG
|
||||||
|
test engine::metal::qwen_mtplx::reduce::mtplx_sampler_argmax_matches_reference ... ignored, requires Apple Metal; original MTPLX greedy ArgReduce and storage
|
||||||
|
test engine::metal::qwen_mtplx::reduce::mtplx_sum_graph_matches_reference ... ignored, requires Apple Metal; original shared sum dispatch and QSA mean
|
||||||
|
test engine::metal::qwen_mtplx::request::qwen_request_settings_override_backend_policy ... ok
|
||||||
|
test engine::metal::qwen_mtplx::request::qwen_request_progress_uses_ui_frontiers_and_decode_timer ... ok
|
||||||
|
test engine::metal::qwen_mtplx::residency::mtplx_residency_matches_reference_lifecycle_cases ... ignored, requires Apple Metal 3 / macOS 15, no model weights
|
||||||
|
test engine::metal::qwen_mtplx::rope::mtplx_rope_graph_matches_reference ... ignored, requires Apple Metal; actual MTPLX RoPE, static YaRN and multi-axis receipts
|
||||||
|
test engine::metal::qwen_mtplx::sampling::mtplx_sampler_batched_matches_reference ... ignored, requires Apple Metal; original MTPLX bound and serial batch distributions
|
||||||
|
test engine::metal::qwen_mtplx::sampling::mtplx_sampler_entry_matches_reference ... ignored, requires Apple Metal; actual MTPLX generator sampling entry
|
||||||
|
test engine::metal::qwen_mtplx::sampling::mtplx_sampler_fallback_matches_reference ... ignored, requires Apple Metal; original sparse fallback and host dense sampler
|
||||||
|
test database::tests::chat_and_compaction_history_survive_reopen_and_session_deletion ... ok
|
||||||
|
test engine::metal::qwen_mtplx::sampling::mtplx_sampler_penalties_match_reference ... ignored, requires Apple Metal; original GPU and CPU raw-logit penalties
|
||||||
|
test engine::metal::qwen_mtplx::sampling::mtplx_sampler_selection_matches_reference ... ignored, requires Apple Metal; original MTPLX sampler selection, no model weights
|
||||||
|
test engine::metal::qwen_mtplx::sampling::mtplx_sampler_serial_support_matches_reference ... ignored, requires Apple Metal; actual MTPLX serial candidate route, no weights
|
||||||
|
test engine::metal::qwen_mtplx::scheduler::mtplx_cpu_dispatch_counts_tenth_and_completes_after_failed_work ... ok
|
||||||
|
test engine::metal::qwen_mtplx::scheduler::mtplx_cpu_primitive_holds_data_and_temporaries_in_fifo_order ... ignored, requires Apple Metal shared storage, no GPU dispatch or model weights
|
||||||
|
test engine::metal::qwen_mtplx::compile_graph::mtplx_compile_simplify_preserves_reference_parent_rules ... ok
|
||||||
|
test engine::metal::qwen_mtplx::scheduler::mtplx_scheduler_cpu_events_propagate_errors_without_consuming_source ... ignored, requires Apple Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx::scheduler::mtplx_scheduler_streams_preserve_fifo_errors_and_shutdown ... ok
|
||||||
|
test engine::metal::qwen_mtplx::sdpa::mtplx_sdpa_vector_matches_reference ... ignored, requires Apple M5 Max; actual pinned fused SDPA kernels
|
||||||
|
test engine::metal::qwen_mtplx::session_cache::issue114_snapshot_stage_preserves_zero_bits ... ignored, Metal; exact pinned lazy/deep snapshot zero-sign law
|
||||||
|
test engine::metal::qwen_mtplx::session_cache::mtplx_full_installed_chat_matches_reference ... ignored, full installed model; external guard and DS4_QWEN38_CHAT_REFERENCE required
|
||||||
|
test engine::metal::qwen_mtplx::session_cache::mtplx_session_boundary_policy ... ok
|
||||||
|
test engine::metal::qwen_mtplx::session_cache::mtplx_session_final_state_requires_capture_and_safe_cache ... ok
|
||||||
|
test engine::metal::qwen_mtplx::session_checkpoint::qwen_checkpoint_roundtrip_and_failed_restore_preserve_live_state ... ignored, model-free Metal checkpoint; external supervisor and DS4_QWEN38_CHECKPOINT_DIR required
|
||||||
|
test engine::metal::qwen_mtplx::session_codec::mtplx_session_codec_host_import_matches_reference ... ignored, model-free Metal imports; external supervisor and DS4_HOST_IMPORT_REFERENCE required
|
||||||
|
test engine::metal::qwen_mtplx::session_codec::mtplx_session_codec_matches_original_blobs_and_restore ... ignored, tiny GPU arrays; external supervisor and DS4_SESSION_CODEC_REFERENCE required
|
||||||
|
test engine::metal::qwen_mtplx::sort::mtplx_sort_graph_matches_reference ... ignored, requires Apple Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx::sparse_attention::mtplx_sparse_attention_graph_matches_reference ... ignored, requires Apple M5; actual MTPLX sparse Decode and TensorOps Prefill consumers
|
||||||
|
test engine::metal::qwen_mtplx::speculative::mtplx_full_installed_mtp_generation_matches_reference ... ignored, full installed model; external guard and DS4_QWEN38_MTP_GENERATION_REFERENCE required
|
||||||
|
test engine::metal::profile::tests::profile_reports_locality_and_lru_hits ... ok
|
||||||
|
test engine::metal::qwen_mtplx::stream::mtplx_stream_routes_queues_events_and_finalizes_all_under_pressure ... ignored, requires Apple Metal, tiny model-free two-queue copy
|
||||||
|
test engine::metal::qwen_mtplx::stream::mtplx_stream_registry_preserves_thread_defaults_templates_and_clear ... ok
|
||||||
|
test engine::metal::qwen_mtplx::submission::mtplx_submission_attaches_at_flush_eval_finish_and_drop ... ignored, requires Apple Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx::unary::mtplx_graph_unary_preserves_reference_layout_and_values ... ignored, requires Apple Metal; graph unary layout, donation and kernel boundaries
|
||||||
|
test engine::metal::qwen_mtplx::verify_sdpa::issue113_head_chunk_outputs_and_dispatch ... ignored, Metal, pinned head-chunk numerical and dispatch fixtures; serial supervisor
|
||||||
|
test engine::metal::qwen_mtplx::verify_sdpa::issue113_plan_and_policy_match_pinned_oracle ... ok
|
||||||
|
test engine::metal::qwen_mtplx::views::mtplx_dtype_conversion_and_views_preserve_values_layout_and_donation ... ignored, requires Apple Metal; original conversion/view kernels and donation in lazy eval
|
||||||
|
test engine::metal::qwen_mtplx::views::mtplx_dynamic_slice_graph_matches_reference ... ignored, requires Metal and original dynamic slice receipts; no model weights
|
||||||
|
test engine::metal::qwen_mtplx::views::mtplx_reshape_shares_or_copies_in_the_connected_evaluator ... ignored, requires Apple Metal; original Reshape copy kernels and shared backing
|
||||||
|
test engine::metal::qwen_mtplx::views::mtplx_views_preserve_aliases_offsets_strides_and_gpu_output ... ignored, requires Apple Metal; connected zero-copy views and offset-aware original copy kernel
|
||||||
|
test engine::metal::qwen_mtplx::vision::qwen_vision_tower_matches_mtplx_image ... ignored, requires Metal and pinned Qwen vision artifacts/reference values
|
||||||
|
test engine::metal::qwen_mtplx::weights::mtplx_checkpoint_projection_fusions_match_reference ... ignored, requires Apple Metal; bounded lazy checkpoint Load and fusion
|
||||||
|
test engine::metal::qwen_mtplx::weights::mtplx_installed_checkpoint_fusion_metadata_matches_reference ... ignored, requires installed DS4_QWEN38_ARTIFACTS; headers only, never evaluate weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_affine_selection_preserves_reference_boundaries ... ok
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_canonical_hyper_and_gdn_kernels_match_reference ... ignored, requires Apple Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_canonical_source_bodies_preserve_pinned_hashes ... ok
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_connected_delta_update_matches_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_connected_gather_sort_matches_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_connected_gdn_decode_matches_model_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_connected_gdn_forward_matches_model_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_connected_gdn_staged_matches_model_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_connected_norm_modules_match_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_connected_router_matches_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_connected_sparse_moe_matches_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_connected_switch_glu_matches_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_gate_up_matches_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_qsa_cache_lifecycle_matches_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_qsa_compiled_backings_and_dynamic_copies_match_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_qsa_compiled_cache_routing_matches_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_qsa_compiled_cache_window_matches_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_qsa_compiled_capacity_matches_reference ... ok
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_qsa_compiled_indexer_modes_match_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_qsa_compiled_scalar_sources_match_reference ... ok
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_qsa_connected_cache_selection_matches_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_qsa_eager_topk_matches_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_qsa_f32_scores_match_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_qsa_indexer_preparation_matches_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_qsa_indexer_routes_match_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_qsa_prefill_matches_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_qsa_prefill_producer_dispatch_matches_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx::submission::mtplx_all_native_command_commits_pass_the_submission_hook ... ok
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_qsa_selector_matches_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_runtime_affine_matches_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_runtime_argsort_matches_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_runtime_gather_matches_reference ... ignored, requires Apple M5 Max Metal, no model weights
|
||||||
|
test engine::deepseek_vision::tests::issue100_ds4_preprocess_dimensions_and_limits ... ok
|
||||||
|
test engine::metal::tests::batch_raw_span_includes_the_chunk_and_previous_window ... ok
|
||||||
|
test engine::metal::tests::compression_schedule_tracks_the_deepseek_model_shape ... ok
|
||||||
|
test engine::metal::tests::decode_feature_policy_keeps_device_generations_and_rollbacks_separate ... ok
|
||||||
|
test engine::metal::tests::decode_raw_span_tracks_the_logical_sliding_window ... ok
|
||||||
|
test engine::metal::tests::directional_steering_matches_the_ds4_token_oracle ... ignored, requires the installed Flash GGUF, DS4 steering fixture, and a Metal device
|
||||||
|
test dev_brain::tests::parser_and_index_rebuild_ignore_links_in_code ... ok
|
||||||
|
test engine::metal::tests::dspark_confidence_quantizes_q8_input_like_ds4 ... ok
|
||||||
|
test engine::metal::tests::dspark_matches_original_summary_cycles ... ignored, requires installed 0731/DSpark and DS4SERVER_DSPARK_REFERENCE cycle recording
|
||||||
|
test engine::metal::tests::dspark_markov_activation_matches_q8_rounding ... ok
|
||||||
|
test engine::metal::tests::dspark_runs_a_target_owned_greedy_cycle ... ignored, requires the installed 0731 Flash and checkpoint-specific DSpark GGUF fixtures
|
||||||
|
test engine::metal::tests::dspark_scheduler_matches_the_ds4_default_window ... ok
|
||||||
|
test engine::metal::tests::dspark_verifier_selects_only_the_committed_row ... ok
|
||||||
|
test engine::metal::tests::flash_0731_long_context_crosses_indexed_prefill_boundary ... ignored, requires a 0731 Flash GGUF and an Apple M5 device
|
||||||
|
test engine::metal::tests::flash_0731_m5_decode_performance_gate ... ignored, requires a 0731 Flash GGUF and an Apple M5 device
|
||||||
|
test engine::metal::tests::flash_0731_resident_and_ssd_streaming_choose_the_same_tokens ... ignored, requires the installed 81 GiB Flash GGUF fixture and a Metal device
|
||||||
|
test engine::metal::tests::flash_0731_runs_exact_sampled_dspark ... ignored, requires the installed 0731 target and checkpoint-specific DSpark GGUF fixtures
|
||||||
|
test engine::metal::qwen_mtplx::scheduler::mtplx_scheduler_activity_preserves_reference_pressure_and_wait_rules ... ok
|
||||||
|
test engine::metal::tests::flash_0731_ssd_streaming_maps_and_seeds_batched_prefill_layers ... ignored, requires the installed 81 GiB Flash GGUF fixture and a Metal device
|
||||||
|
test engine::metal::tests::half_conversion_handles_normal_and_subnormal_values ... ok
|
||||||
|
test engine::metal::tests::pro_q4_model_spans_remain_isolated ... ok
|
||||||
|
test engine::metal::tests::pro_ssd_streaming_full_layer_and_selected_batch_agree ... ignored, requires the installed DeepSeek V4 Pro GGUF and Apple Metal
|
||||||
|
test engine::metal::tests::resident_multi_session_switching_preserves_each_kv_frontier ... ignored, requires the installed 81 GiB Flash GGUF fixture and a Metal device
|
||||||
|
test engine::metal::tests::generated_hotlists_match_each_model_shape ... ok
|
||||||
|
test engine::metal::tests::ssd_streaming_supports_dspark ... ignored, requires the installed 0731 Flash and checkpoint-specific DSpark GGUF fixtures
|
||||||
|
test engine::metal::tests::pro_resident_admission_includes_weights_context_and_scratch ... ok
|
||||||
|
test engine::metal::tests::streaming_memory_plan_matches_ds4_graph_formulas ... ok
|
||||||
|
test engine::metal::vision::tests::glm53_resize_matches_reference_token_grids ... ok
|
||||||
|
test engine::qwen::tests::pinned_artifact_set_loads_and_renders_goldens ... ignored, requires DS4_QWEN38_ARTIFACTS to point at the pinned 105 GB source
|
||||||
|
test engine::qwen::tests::pinned_qwen_metadata_matches_product_without_executor_state ... ignored, requires DS4_QWEN38_ARTIFACTS to point at the pinned 105 GB source
|
||||||
|
test engine::qwen_vision::qwen_image_cache_keys_distinguish_pixels_and_preserve_prior_text ... ok
|
||||||
|
test engine::metal::qwen_mtplx::sampling::mtplx_sampler_distribution_and_rng_match_reference ... ok
|
||||||
|
test engine::qwen_vision::qwen_vision_positions_and_resize_contract ... ok
|
||||||
|
test engine::qwen::tests::metadata_and_safetensors_boundaries_fail_closed ... ok
|
||||||
|
test engine::sampling_tests::checkpoint_rebuilds_explain_compatibility_and_history_misses ... ok
|
||||||
|
test engine::sampling_tests::checkpoint_tag_accepts_an_unchanged_prefix_before_reminders ... ok
|
||||||
|
test engine::sampling_tests::checkpoint_tag_covers_the_canonical_chat_state ... ok
|
||||||
|
test engine::sampling_tests::checkpoint_headers_identify_the_exact_model ... ok
|
||||||
|
test engine::sampling_tests::ds4_sampler_real_logits_cpu_benchmark ... ignored, CPU-only; requires DS4_SAMPLER_RECORDING with the recorded GLM logits
|
||||||
|
test engine::sampling_tests::exact_delta_sampling_accepts_or_corrects_the_draft ... ok
|
||||||
|
test engine::sampling_tests::generated_text_drains_incomplete_bytes_before_phase_changes_and_finish ... ok
|
||||||
|
test engine::sampling_tests::generated_text_preserves_thinking_utf8_and_split_stops ... ok
|
||||||
|
test engine::sampling_tests::metal_executes_real_flash_token ... ignored, requires the 80 GiB Flash checkpoint and Apple Metal
|
||||||
|
test engine::qwen::tests::frozen_inventory_and_memory_categories_are_exact ... ok
|
||||||
|
test engine::sampling_tests::qwen_rng_matches_numpy_pcg64 ... ok
|
||||||
|
test engine::sampling_tests::qwen_sampling_uses_mtplx_global_nucleus_mass_before_top_k ... ok
|
||||||
|
test engine::sampling_tests::resumed_prefill_crossover_matches_each_ds4_graph ... ok
|
||||||
|
test engine::sampling_tests::sampling_probabilities_match_ds4_filter_order ... ok
|
||||||
|
test engine::sampling_tests::split_utf8_token_bytes_are_joined_before_decoding ... ok
|
||||||
|
test engine::sampling_tests::top_k_and_stream_stops_are_applied_before_output ... ok
|
||||||
|
test engine::sampling_tests::workspace_instruction_identity_participates_in_append_only_kv_keys ... ok
|
||||||
|
test engine::sampling_tests::zero_temperature_is_greedy ... ok
|
||||||
|
test engine::tokenizer::issue111::issue111_ds4_families ... ignored, CPU-only; installed DS4 GGUFs and exact oracle IDs
|
||||||
|
test engine::tokenizer::issue111::issue111_every_unicode_scalar_split ... ignored, CPU-only; exhaustive pinned Qwen split receipt
|
||||||
|
test engine::tokenizer::issue111::issue111_qwen_tokens_and_requests ... ignored, CPU-only; installed Qwen tokenizer and pinned exact IDs
|
||||||
|
test engine::metal::qwen_mtplx::ngram::mtplx_ngram_cpu_rows_and_raw_history_match_reference ... ok
|
||||||
|
test engine::tokenizer::issue116::issue116_nfc_tokens_and_requests ... ignored, CPU-only; pinned NFC receipts and installed Qwen tokenizer
|
||||||
|
test engine::tokenizer::issue116::nfc_contract_and_composition ... ok
|
||||||
|
test engine::tokenizer::issue116::only_affected_qwen_history_changes_checkpoint_tags ... ok
|
||||||
|
test engine::tokenizer::issue117::direct_history_omits_reasoning_but_retains_the_scaffold ... ok
|
||||||
|
test engine::tokenizer::issue117::issue117_history_renders_tokens_and_context ... ignored, CPU-only; installed Qwen tokenizer and pinned history receipts
|
||||||
|
test engine::tokenizer::issue117::only_affected_histories_change_keys ... ok
|
||||||
|
test engine::tokenizer::issue117::reasoning_only_direct_turn_merges_surrounding_users ... ok
|
||||||
|
test engine::tokenizer::tests::character_helpers_are_total_at_every_byte_offset ... ok
|
||||||
|
test engine::tokenizer::tests::ds4_chat_matches_original_session_tokens ... ignored, CPU-only; requires DS4SERVER_CHAT_REFERENCE and its installed GGUF
|
||||||
|
test engine::tokenizer::tests::issue100_visual_text_preserves_image_boundaries ... ignored, CPU-only; requires the installed 0731 tokenizer shared with Vision-Exp
|
||||||
|
test engine::tokenizer::tests::qwen_checkpoint_continuation_matches_full_conversation ... ignored, requires the installed Qwen tokenizer, but no GPU or model weights
|
||||||
|
test engine::tokenizer::tests::qwen_plain_chat_matches_mtplx_server_tokens ... ignored, requires local tokenizer and tools/qwen-chat-reference.py output; no inference
|
||||||
|
test engine::tokenizer::tests::reasoning_prefixes_match_each_model_family_contract ... ok
|
||||||
|
test engine::validation::tests::configured_glm53_main_fixture_passes_the_exact_layout ... ok
|
||||||
|
test engine::validation::tests::configured_glm53_vision_fixture_passes_the_exact_layout ... ok
|
||||||
|
test engine::tokenizer::issue111::qwen_split_boundaries ... ok
|
||||||
|
test engine::validation::tests::installed_dspark_fixture_passes_the_target_layout ... ok
|
||||||
|
test engine::validation::tests::issue100_checkpoint_metadata_is_strict ... ignored, requires the pinned Vision-Exp encoder and DSpark support GGUFs
|
||||||
|
test engine::validation::tests::mxfp4_is_limited_to_routed_experts ... ok
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_qsa_prefill_workspace_matches_reference ... ok
|
||||||
|
test extensions::tests::duplicate_skill_names_and_update_rollback_are_deterministic ... ok
|
||||||
|
test engine::sampling_tests::probability_ratio_speculation_preserves_the_target_distribution ... ok
|
||||||
|
test extensions::tests::hook_output_is_bounded_typed_and_event_matched ... ok
|
||||||
|
test dev_brain::tests::skill_validation_and_default_guide_restore_are_strict ... ok
|
||||||
|
test extensions::tests::manifest_and_paths_are_validated_without_following_escapes ... ok
|
||||||
|
test extensions::tests::pinned_ponytail_runs_all_lifecycle_modes ... ignored, downloads the pinned Ponytail reference integration
|
||||||
|
test engine::metal::qwen_mtplx::array::mtplx_array_releases_sibling_cycles_and_deep_input_graphs ... ok
|
||||||
|
test extensions::tests::symlink_and_variable_escapes_are_rejected ... ok
|
||||||
|
test extensions::tests::registry_persists_enable_disable_errors_and_uninstall ... ok
|
||||||
|
test instructions::tests::changes_removals_resume_and_compaction_are_append_only ... ok
|
||||||
|
test instructions::tests::discovery_is_global_to_local_deduplicated_and_confined ... ok
|
||||||
|
test instructions::tests::empty_and_repaired_baselines_do_not_repeat_prompt_content ... ok
|
||||||
|
test instructions::tests::newly_crossed_scope_is_added_once_and_unavailable_content_is_retained ... ok
|
||||||
|
test metrics::tests::cache_report_splits_buckets_and_folds_index_files_into_their_checkpoint ... ok
|
||||||
|
test metrics::tests::decode_timer_excludes_later_work_and_resets_between_requests ... ok
|
||||||
|
test metrics::tests::prompt_timing_keeps_unmeasured_components_distinct_from_zero ... ok
|
||||||
|
test metrics::tests::removed_preview_metric_code_stays_decodable ... ok
|
||||||
|
test metrics::tests::snapshots_track_runtime_and_server_counters ... ok
|
||||||
|
test model::qwen::qwen_vision_is_an_independent_managed_artifact_set ... ok
|
||||||
|
test model::qwen::tests::embedded_manifest_is_the_frozen_complete_set ... ok
|
||||||
|
test model::qwen::tests::verification_rejects_hash_mismatch_and_honors_cancellation ... ok
|
||||||
|
test model::transfer::tests::cancellation_keeps_the_partial_file_for_the_next_run ... ok
|
||||||
|
test dev_brain::tests::validation_names_every_drifted_source_and_requires_semantic_refresh ... ok
|
||||||
|
test model::transfer::tests::catalog_and_checksum_verification_are_explicit ... ok
|
||||||
|
test model::transfer::tests::issue100_managed_intake ... ignored, requires all three pinned Vision-Exp GGUF artifacts; performs full SHA256 and intake validation
|
||||||
|
test instructions::tests::frames_escape_control_tags_and_enforce_utf8_byte_limits ... ok
|
||||||
|
test model::transfer::tests::qwen_set_verifies_reloads_and_removes_as_one_managed_artifact ... ignored, requires DS4_QWEN38_ARTIFACTS to point at the pinned 105 GB source
|
||||||
|
test model::transfer::tests::issue100_catalog_keeps_vision_and_dspark_checkpoint_specific ... ok
|
||||||
|
test model::transfer::tests::managed_artifact_inventory_and_delete_include_partial_files ... ok
|
||||||
|
test model::transfer::tests::verification_reports_bytes_read ... ok
|
||||||
|
test model_eval::tests::acceleration_receipt_reports_effective_sampling_contract ... ok
|
||||||
|
test model_eval::tests::acceleration_switch_uses_each_models_native_path ... ok
|
||||||
|
test model_eval::tests::canary_cross_phase_samples_keep_their_start_phase ... ok
|
||||||
|
test model::transfer::tests::restart_resumes_at_the_existing_partial_byte ... ok
|
||||||
|
test model_eval::tests::evaluations_exclude_overlapping_workers_and_release_on_exit ... ok
|
||||||
|
test model_eval::tests::external_canary_observes_phases_and_finishes_on_eof ... ignored, Metal probe only; DS4SERVER_CANARY_BINARY must point to the current release binary
|
||||||
|
test model_eval::tests::external_canary_protocol_accepts_only_known_phases ... ok
|
||||||
|
test model_eval::tests::parses_safe_defaults_and_overrides ... ok
|
||||||
|
test model_eval::tests::rejects_unbounded_or_invalid_limits ... ok
|
||||||
|
test model_eval::tests::repeated_prompts_form_one_ordered_conversation ... ok
|
||||||
|
test model_eval::tests::warmup_timing_separates_loading_without_inventing_missing_durations ... ok
|
||||||
|
test model_eval::tests::worker_progress_events_advance_the_watchdog ... ok
|
||||||
|
test native_edit::tests::edit_history_supports_undo_and_redo ... ok
|
||||||
|
test native_edit::tests::paste_replays_a_complete_command_shortcut ... ok
|
||||||
|
test native_media::tests::media_player_only_accepts_remote_http_urls ... ok
|
||||||
|
test native_menu::tests::edit_items_are_custom_menu_events ... ok
|
||||||
|
test native_menu::tests::user_guide_uses_unmodified_f1 ... ok
|
||||||
|
test native_menu::tests::view_items_keep_the_selected_pane_checked ... ok
|
||||||
|
test runtime::tests::dropping_active_generation_cancels_it ... ok
|
||||||
|
test runtime::tests::operation_failures_use_the_matching_event ... ok
|
||||||
|
test runtime::tests::operation_tracking_is_consistent ... ok
|
||||||
|
test runtime::tests::runtime_panic_recovery_accepts_the_next_operation ... ok
|
||||||
|
test server::request::tests::reasoning_requests_preserve_each_models_exact_modes ... ok
|
||||||
|
test server::tests::canonical_tool_replay_preserves_argument_types ... ok
|
||||||
|
test server::tests::connection_slots_enforce_the_limit_and_release_on_drop ... ok
|
||||||
|
test server::tests::content_arrays_match_ds4_text_projection ... ok
|
||||||
|
test server::tests::context_limit_errors_use_protocol_standard_fields ... ok
|
||||||
|
test server::tests::cors_headers_are_opt_in_and_cover_preflight ... ok
|
||||||
|
test server::tests::dsml_tool_stream_projects_incremental_json_arguments ... ok
|
||||||
|
test server::tests::generated_dsml_becomes_openai_tool_calls ... ok
|
||||||
|
test server::tests::generated_tool_parser_accepts_ds4_recovery_syntaxes ... ok
|
||||||
|
test server::tests::model_metadata_uses_the_requested_alias_and_default_token_limit ... ok
|
||||||
|
test server::tests::protocol_tool_results_require_live_or_replayed_call_ids ... ok
|
||||||
|
test server::tests::qwen_http_requests_keep_echoed_reasoning_without_tools ... ok
|
||||||
|
test dev_brain::tests::source_hash_drift_excludes_page_from_authoritative_queries ... ok
|
||||||
|
test server::tests::responses_parser_rejects_dropped_state_and_preserves_hosted_calls ... ok
|
||||||
|
test server::tests::sampled_tool_replay_survives_server_restart ... ok
|
||||||
|
test server::tests::streaming_errors_match_ds4_server_framing ... ok
|
||||||
|
test dev_brain::tests::validation_keeps_broken_links_editable_and_reports_provenance_errors ... ok
|
||||||
|
test server::tests::tool_prompt_matches_ds4_server_text ... ok
|
||||||
|
test server::tests::tool_stream_projects_ds4_recovery_syntaxes ... ok
|
||||||
|
test settings::tests::captured_ds4_cli_options_are_uniquely_classified ... ok
|
||||||
|
test settings::tests::effective_settings_build_one_engine_and_turn_configuration ... ok
|
||||||
|
test settings::tests::effective_settings_preserve_unset_model_defaults ... ok
|
||||||
|
test settings::tests::execution_settings_keep_ds4_automatic_values_and_glm_rules ... ok
|
||||||
|
test settings::tests::kv_cache_gates_follow_the_ds4_store_rules ... ok
|
||||||
|
test settings::tests::reasoning_selection_is_exact_and_model_specific ... ok
|
||||||
|
test settings::tests::runtime_settings_preserve_automatic_values_and_cross_field_rules ... ok
|
||||||
|
test settings::tests::speculative_settings_match_acceleration_defaults_and_dependencies ... ok
|
||||||
|
test dev_brain::tests::verified_skills_are_indexed_and_injected_on_demand ... ok
|
||||||
|
test server::tests::streaming_prefill_keeps_the_client_connection_alive ... ok
|
||||||
|
test dev_brain::tests::source_versions_follow_that_file_instead_of_the_whole_worktree ... ok
|
||||||
|
test model_eval::tests::cpu_accounting_matches_getrusage_units ... ok
|
||||||
|
test engine::validation::tests::installed_ds4_fixture_opens_and_renders_a_prompt ... ok
|
||||||
|
test model::transfer::tests::stalled_download_times_out_and_keeps_the_partial_file ... ok
|
||||||
|
test server::tests::request_deadline_stops_slow_clients ... ok
|
||||||
|
test engine::sampling_tests::ds4_sampler_matches_original_reference_tokens_and_rng ... ok
|
||||||
|
test extensions::tests::direct_hook_runner_isolated_environment_and_handles_failures ... ok
|
||||||
|
test agent::tests::execution_failures_and_timeouts_have_distinct_codes ... ok
|
||||||
|
kill: 21799: No such process
|
||||||
|
test agent::tests::dropping_session_tools_stops_live_jobs_and_removes_output ... ok
|
||||||
|
test engine::qwen_vision::qwen_image_preprocessing_matches_pillow_mtplx ... ok
|
||||||
|
test agent::tests::local_file_and_bash_tools_execute_in_the_project ... ok
|
||||||
|
test extensions::tests::lifecycle_matchers_preserve_declared_event_order ... ok
|
||||||
|
test engine::metal::qwen_mtplx_tests::mtplx_runtime_shaders_are_pinned ... ok
|
||||||
|
test extensions::tests::hook_failures_are_bounded_actionable_and_non_shell ... ok
|
||||||
|
test engine::metal::qwen_mtplx::sampling::mtplx_sampler_mixed_distributions_match_reference ... ok
|
||||||
|
|
||||||
|
test result: ok. 323 passed; 0 failed; 247 ignored; 0 measured; 0 filtered out; finished in 5.53s
|
||||||
|
|
||||||
|
Running unittests tools/qwen38-artifacts.rs (target/debug/deps/qwen38_artifacts-3a34dfcd9c4f3de8)
|
||||||
|
|
||||||
|
running 4 tests
|
||||||
|
test tests::artifact_names_cannot_escape_the_output_directory ... ok
|
||||||
|
test tests::affine_sample_digest_uses_packed_values_scales_and_biases ... ok
|
||||||
|
test tests::safetensors_validation_rejects_duplicate_names_and_invalid_ranges ... ok
|
||||||
|
test tests::artifact_download_resumes_an_authenticated_partial ... ok
|
||||||
|
|
||||||
|
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
|
||||||
|
|
||||||
|
Running unittests tools/test-supervisor.rs (target/debug/deps/test_supervisor-978addc9e5e8dbda)
|
||||||
|
|
||||||
|
running 4 tests
|
||||||
|
test supervisor::tests::direct_worker_arguments_are_not_rust_test_arguments ... ok
|
||||||
|
test supervisor::tests::split_records_cannot_be_interleaved_with_watchdog_samples ... ok
|
||||||
|
test supervisor::tests::split_probe_records_do_not_reset_progress ... ok
|
||||||
|
ready{"event":"gpu_canary_sample","ok":true}
|
||||||
|
{"event":"gpu_canary_sample","ok":true}
|
||||||
|
{"event":"gpu_canary_sample","ok":true}
|
||||||
|
{"event":"gpu_canary_sample","ok":true}
|
||||||
|
{"event":"gpu_canary_sample","ok":true}
|
||||||
|
{"event":"gpu_canary_sample","ok":true}
|
||||||
|
ready
|
||||||
|
{"event":"gpu_canary_sample","ok":true}
|
||||||
|
{"event":"gpu_canary_sample","ok":true}
|
||||||
|
{"event":"gpu_canary_sample","ok":true}
|
||||||
|
{"event":"gpu_canary_sample","ok":true}
|
||||||
|
{"event":"gpu_canary_sample","ok":true}
|
||||||
|
{"event":"gpu_canary_sample","ok":true}
|
||||||
|
progressprogressprogressprogressprogressprogressprogressprogresstest supervisor::tests::watchdog_stops_memory_and_silence_but_not_total_runtime ... ok
|
||||||
|
|
||||||
|
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 2.33s
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
running 1 test
|
||||||
|
test engine::tokenizer::tests::issue100_visual_text_preserves_image_boundaries ... ok
|
||||||
|
|
||||||
|
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 569 filtered out; finished in 0.10s
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 The ds4.c authors
|
||||||
|
Copyright (c) 2023-2026 The ggml authors
|
||||||
|
Copyright (c) 2023 DeepSeek
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
// Derived from antirez/ds4 0aaea5a238fb41a35106a551e73c8409dfb751ac (MIT).
|
||||||
|
// Copyright (c) 2026 The ds4.c authors; (c) 2023 DeepSeek.
|
||||||
|
// See DS4-LICENSE.txt for the complete license.
|
||||||
|
// DeepSeek V4 Flash Vision-Exp operations not covered by shared BF16 matmuls.
|
||||||
|
|
||||||
|
struct deepseek4_vision_rows_args {
|
||||||
|
uint width;
|
||||||
|
uint rows;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct deepseek4_vision_qkv_args {
|
||||||
|
uint rows;
|
||||||
|
uint grid_width;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct deepseek4_vision_align_args {
|
||||||
|
uint grid_height;
|
||||||
|
uint grid_width;
|
||||||
|
uint output_rows;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline float deepseek4_vision_round_bf16(float value) {
|
||||||
|
uint bits = as_type<uint>(value);
|
||||||
|
if ((bits & 0x7f800000u) == 0x7f800000u) return value;
|
||||||
|
bits += 0x00007fffu + ((bits >> 16u) & 1u);
|
||||||
|
return as_type<float>(bits & 0xffff0000u);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float deepseek4_vision_erf(float x) {
|
||||||
|
const float sign = x < 0.0f ? -1.0f : 1.0f;
|
||||||
|
const float a = abs(x);
|
||||||
|
const float t = 1.0f / (1.0f + 0.3275911f * a);
|
||||||
|
const float p = (((((1.061405429f * t - 1.453152027f) * t) +
|
||||||
|
1.421413741f) * t - 0.284496736f) * t +
|
||||||
|
0.254829592f) * t;
|
||||||
|
return sign * (1.0f - p * exp(-a * a));
|
||||||
|
}
|
||||||
|
|
||||||
|
kernel void kernel_deepseek4_vision_qkv_rope(
|
||||||
|
constant deepseek4_vision_qkv_args &args,
|
||||||
|
device const float *qkv,
|
||||||
|
device const ushort *bias,
|
||||||
|
device float *q,
|
||||||
|
device float *k,
|
||||||
|
device float *v,
|
||||||
|
uint2 group [[threadgroup_position_in_grid]],
|
||||||
|
ushort lane [[thread_index_in_simdgroup]]) {
|
||||||
|
const uint row = group.x;
|
||||||
|
const uint head = group.y;
|
||||||
|
if (row >= args.rows || head >= 16u) return;
|
||||||
|
const ulong qkv_base = (ulong)row * 3072u + (ulong)head * 64u;
|
||||||
|
const ulong out_base = (ulong)row * 1024u + (ulong)head * 64u;
|
||||||
|
const float q0 = deepseek4_vision_round_bf16(
|
||||||
|
qkv[qkv_base + lane] +
|
||||||
|
glm53_bf16_to_f32(bias[(ulong)head * 64u + lane]));
|
||||||
|
const float q1 = deepseek4_vision_round_bf16(
|
||||||
|
qkv[qkv_base + lane + 32u] +
|
||||||
|
glm53_bf16_to_f32(bias[(ulong)head * 64u + lane + 32u]));
|
||||||
|
const float k0 = deepseek4_vision_round_bf16(
|
||||||
|
qkv[qkv_base + 1024u + lane] +
|
||||||
|
glm53_bf16_to_f32(bias[1024u + (ulong)head * 64u + lane]));
|
||||||
|
const float k1 = deepseek4_vision_round_bf16(
|
||||||
|
qkv[qkv_base + 1024u + lane + 32u] +
|
||||||
|
glm53_bf16_to_f32(bias[1024u + (ulong)head * 64u + lane + 32u]));
|
||||||
|
const uint y = row / args.grid_width;
|
||||||
|
const uint x = row - y * args.grid_width;
|
||||||
|
const uint pos = lane < 16u ? y : x;
|
||||||
|
const uint freq_index = lane & 15u;
|
||||||
|
const float inv_freq = powr(10000.0f, -(float)freq_index / 16.0f);
|
||||||
|
const float angle = (float)pos * inv_freq;
|
||||||
|
const float cs = cos(angle);
|
||||||
|
const float sn = sin(angle);
|
||||||
|
q[out_base + lane] = deepseek4_vision_round_bf16(q0 * cs - q1 * sn);
|
||||||
|
q[out_base + lane + 32u] =
|
||||||
|
deepseek4_vision_round_bf16(q1 * cs + q0 * sn);
|
||||||
|
k[out_base + lane] = deepseek4_vision_round_bf16(k0 * cs - k1 * sn);
|
||||||
|
k[out_base + lane + 32u] =
|
||||||
|
deepseek4_vision_round_bf16(k1 * cs + k0 * sn);
|
||||||
|
v[out_base + lane] = deepseek4_vision_round_bf16(
|
||||||
|
qkv[qkv_base + 2048u + lane] +
|
||||||
|
glm53_bf16_to_f32(bias[2048u + (ulong)head * 64u + lane]));
|
||||||
|
v[out_base + lane + 32u] = deepseek4_vision_round_bf16(
|
||||||
|
qkv[qkv_base + 2048u + lane + 32u] +
|
||||||
|
glm53_bf16_to_f32(
|
||||||
|
bias[2048u + (ulong)head * 64u + lane + 32u]));
|
||||||
|
}
|
||||||
|
|
||||||
|
kernel void kernel_deepseek4_vision_round_bf16(
|
||||||
|
constant deepseek4_vision_rows_args &args,
|
||||||
|
device float *x,
|
||||||
|
uint2 gid [[thread_position_in_grid]]) {
|
||||||
|
if (gid.x >= args.width || gid.y >= args.rows) return;
|
||||||
|
const ulong off = (ulong)gid.y * args.width + gid.x;
|
||||||
|
x[off] = deepseek4_vision_round_bf16(x[off]);
|
||||||
|
}
|
||||||
|
|
||||||
|
kernel void kernel_deepseek4_vision_add_residual(
|
||||||
|
constant deepseek4_vision_rows_args &args,
|
||||||
|
device float *x,
|
||||||
|
device const float *residual,
|
||||||
|
uint2 gid [[thread_position_in_grid]]) {
|
||||||
|
if (gid.x >= args.width || gid.y >= args.rows) return;
|
||||||
|
const ulong off = (ulong)gid.y * args.width + gid.x;
|
||||||
|
x[off] = deepseek4_vision_round_bf16(x[off] + residual[off]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void deepseek4_vision_swiglu_split(
|
||||||
|
constant deepseek4_vision_rows_args &args,
|
||||||
|
device const float *gate_up,
|
||||||
|
device float *out,
|
||||||
|
uint2 gid,
|
||||||
|
bool round_silu) {
|
||||||
|
if (gid.x >= args.width || gid.y >= args.rows) return;
|
||||||
|
const ulong source = (ulong)gid.y * args.width * 2u + gid.x;
|
||||||
|
const float gate = gate_up[source];
|
||||||
|
const float up = gate_up[source + args.width];
|
||||||
|
float activated = gate / (1.0f + exp(-gate));
|
||||||
|
if (round_silu) activated = deepseek4_vision_round_bf16(activated);
|
||||||
|
out[(ulong)gid.y * args.width + gid.x] = deepseek4_vision_round_bf16(activated * up);
|
||||||
|
}
|
||||||
|
|
||||||
|
kernel void kernel_deepseek4_vision_swiglu_split(
|
||||||
|
constant deepseek4_vision_rows_args &args,
|
||||||
|
device const float *gate_up,
|
||||||
|
device float *out,
|
||||||
|
uint2 gid [[thread_position_in_grid]]) {
|
||||||
|
deepseek4_vision_swiglu_split(args, gate_up, out, gid, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Match F.unfold(x, 3, stride=3): channels are outermost, followed by the
|
||||||
|
* row-major 3x3 neighborhood. Values outside the ViT grid are zero. */
|
||||||
|
kernel void kernel_deepseek4_vision_aligner_reorder(
|
||||||
|
constant deepseek4_vision_align_args &args,
|
||||||
|
device const float *x,
|
||||||
|
device float *out,
|
||||||
|
uint2 gid [[thread_position_in_grid]]) {
|
||||||
|
const uint d = gid.x;
|
||||||
|
const uint row = gid.y;
|
||||||
|
if (d >= 9216u || row >= args.output_rows) return;
|
||||||
|
const uint output_width = (args.grid_width + 2u) / 3u;
|
||||||
|
const uint block_y = row / output_width;
|
||||||
|
const uint block_x = row - block_y * output_width;
|
||||||
|
const uint channel = d / 9u;
|
||||||
|
const uint within = d - channel * 9u;
|
||||||
|
const uint source_y = block_y * 3u + within / 3u;
|
||||||
|
const uint source_x = block_x * 3u + within % 3u;
|
||||||
|
float value = 0.0f;
|
||||||
|
if (source_y < args.grid_height && source_x < args.grid_width) {
|
||||||
|
const ulong source_row = (ulong)source_y * args.grid_width + source_x;
|
||||||
|
value = x[source_row * 1024u + channel];
|
||||||
|
}
|
||||||
|
out[(ulong)row * 9216u + d] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
kernel void kernel_deepseek4_vision_gelu_bias(
|
||||||
|
constant deepseek4_vision_rows_args &args,
|
||||||
|
device const float *x,
|
||||||
|
device const ushort *bias,
|
||||||
|
device float *out,
|
||||||
|
uint2 gid [[thread_position_in_grid]]) {
|
||||||
|
if (gid.x >= args.width || gid.y >= args.rows) return;
|
||||||
|
const ulong off = (ulong)gid.y * args.width + gid.x;
|
||||||
|
const float value = deepseek4_vision_round_bf16(
|
||||||
|
x[off] + glm53_bf16_to_f32(bias[gid.x]));
|
||||||
|
out[off] = deepseek4_vision_round_bf16(
|
||||||
|
0.5f * value *
|
||||||
|
(1.0f + deepseek4_vision_erf(value * 0.7071067811865475f)));
|
||||||
|
}
|
||||||
@@ -6894,3 +6894,65 @@ kernel void kernel_dsv4_softmax_pool_ratio4_direct(
|
|||||||
|
|
||||||
dst[ic * args.head_dim + id] = acc/sum;
|
dst[ic * args.head_dim + id] = acc/sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Vision-Exp routing from DS4 0aaea5a238fb41a35106a551e73c8409dfb751ac.
|
||||||
|
struct ds4_metal_args_dsv4_router_select_visual {
|
||||||
|
uint32_t hash_rows;
|
||||||
|
uint32_t vocab_size;
|
||||||
|
uint32_t n_tokens;
|
||||||
|
uint32_t has_bias;
|
||||||
|
uint32_t hash_mode;
|
||||||
|
};
|
||||||
|
/* Vision-Exp uses score-based visual routing even in the first three layers,
|
||||||
|
* where ordinary text rows use a token-id hash table. One threadgroup owns a
|
||||||
|
* row so the image/text decision is uniform across all barriers. */
|
||||||
|
kernel void kernel_dsv4_router_select_visual_batch(
|
||||||
|
constant ds4_metal_args_dsv4_router_select_visual & args,
|
||||||
|
device const float *probs,
|
||||||
|
device const float *bias,
|
||||||
|
device const float *visual_bias,
|
||||||
|
device const int32_t *hash,
|
||||||
|
device const int32_t *tokens,
|
||||||
|
device int32_t *selected,
|
||||||
|
threadgroup float *scratch [[threadgroup(0)]],
|
||||||
|
uint tid [[thread_position_in_threadgroup]],
|
||||||
|
uint row [[threadgroup_position_in_grid]]) {
|
||||||
|
if (tid >= 256u || row >= args.n_tokens) return;
|
||||||
|
|
||||||
|
const int32_t token = tokens[row];
|
||||||
|
const bool image = token >= 0 && (uint32_t)token >= args.vocab_size;
|
||||||
|
device int32_t *out = selected + (uint64_t)row * 6u;
|
||||||
|
if (args.hash_mode && !image) {
|
||||||
|
const uint hash_row = token >= 0 && (uint32_t)token < args.hash_rows
|
||||||
|
? (uint32_t)token : 0u;
|
||||||
|
if (tid < 6u) out[tid] = hash[(uint64_t)hash_row * 6u + tid];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
threadgroup float *scores = scratch;
|
||||||
|
threadgroup int32_t *indices = (threadgroup int32_t *)(scratch + 256u);
|
||||||
|
const float p = probs[(uint64_t)row * 256u + tid];
|
||||||
|
scores[tid] = p + (image ? visual_bias[tid]
|
||||||
|
: (args.has_bias ? bias[tid] : 0.0f));
|
||||||
|
indices[tid] = (int32_t)tid;
|
||||||
|
threadgroup_barrier(mem_flags::mem_threadgroup);
|
||||||
|
|
||||||
|
for (uint k = 2u; k <= 256u; k <<= 1u) {
|
||||||
|
for (uint j = k >> 1u; j > 0u; j >>= 1u) {
|
||||||
|
const uint other = tid ^ j;
|
||||||
|
if (other > tid) {
|
||||||
|
const bool descending = (tid & k) == 0u;
|
||||||
|
const int32_t a = indices[tid];
|
||||||
|
const int32_t b = indices[other];
|
||||||
|
const float sa = scores[(uint)a];
|
||||||
|
const float sb = scores[(uint)b];
|
||||||
|
if ((descending && sa < sb) || (!descending && sa > sb)) {
|
||||||
|
indices[tid] = b;
|
||||||
|
indices[other] = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
threadgroup_barrier(mem_flags::mem_threadgroup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tid < 6u) out[tid] = indices[tid];
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
ALTER TABLE sessions ADD COLUMN model_without_deepseek_vision TEXT
|
||||||
|
CHECK (model_without_deepseek_vision IS NULL OR model_without_deepseek_vision IN (
|
||||||
|
'deepseek-v4-flash-0731',
|
||||||
|
'deepseek-v4-pro',
|
||||||
|
'glm-5.2',
|
||||||
|
'glm-5.3-flash',
|
||||||
|
'qwen3.8-flash-next'
|
||||||
|
));
|
||||||
|
UPDATE sessions SET model_without_deepseek_vision = CASE
|
||||||
|
WHEN model = 'deepseek-v4-flash-vision-exp' THEN NULL
|
||||||
|
ELSE model
|
||||||
|
END;
|
||||||
|
ALTER TABLE sessions DROP COLUMN model;
|
||||||
|
ALTER TABLE sessions RENAME COLUMN model_without_deepseek_vision TO model;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
ALTER TABLE sessions ADD COLUMN model_with_deepseek_vision TEXT
|
||||||
|
CHECK (model_with_deepseek_vision IS NULL OR model_with_deepseek_vision IN (
|
||||||
|
'deepseek-v4-flash-0731',
|
||||||
|
'deepseek-v4-flash-vision-exp',
|
||||||
|
'deepseek-v4-pro',
|
||||||
|
'glm-5.2',
|
||||||
|
'glm-5.3-flash',
|
||||||
|
'qwen3.8-flash-next'
|
||||||
|
));
|
||||||
|
UPDATE sessions SET model_with_deepseek_vision = model;
|
||||||
|
ALTER TABLE sessions DROP COLUMN model;
|
||||||
|
ALTER TABLE sessions RENAME COLUMN model_with_deepseek_vision TO model;
|
||||||
@@ -2,6 +2,7 @@ MIT License
|
|||||||
|
|
||||||
Copyright (c) 2026 The ds4.c authors
|
Copyright (c) 2026 The ds4.c authors
|
||||||
Copyright (c) 2023-2026 The ggml authors
|
Copyright (c) 2023-2026 The ggml authors
|
||||||
|
Copyright (c) 2023 DeepSeek
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -12,3 +12,10 @@ interfaces and are not copies of DS4 application code.
|
|||||||
DS4Server builds and bundles this local snapshot; it does not read a sibling
|
DS4Server builds and bundles this local snapshot; it does not read a sibling
|
||||||
DS4 checkout. Rust owns the model, graph, session, sampling, and lifecycle.
|
DS4 checkout. Rust owns the model, graph, session, sampling, and lifecycle.
|
||||||
Objective-C remains only at the platform Metal boundary.
|
Objective-C remains only at the platform Metal boundary.
|
||||||
|
|
||||||
|
|
||||||
|
The Vision Experimental primitives added for issue #100 were reviewed against
|
||||||
|
DS4 `0aaea5a238fb41a35106a551e73c8409dfb751ac`. The new Metal file and visual
|
||||||
|
routing kernel follow that reference; image preprocessing, encoder graph,
|
||||||
|
visual prompt masks and session identities remain in Rust. See
|
||||||
|
`docs/issue100-vision/README.md` for the source and validation receipts.
|
||||||
+210
-14
@@ -4551,6 +4551,7 @@ static NSString *ds4_gpu_full_source(void) {
|
|||||||
@[@"DS4_METAL_DENSE_SOURCE", @"metal/dense.metal"],
|
@[@"DS4_METAL_DENSE_SOURCE", @"metal/dense.metal"],
|
||||||
@[@"DS4_METAL_GLM53_BF16_SOURCE", @"metal/glm53_bf16.metal"],
|
@[@"DS4_METAL_GLM53_BF16_SOURCE", @"metal/glm53_bf16.metal"],
|
||||||
@[@"DS4_METAL_GLM53_VISION_SOURCE", @"metal/glm53_vision.metal"],
|
@[@"DS4_METAL_GLM53_VISION_SOURCE", @"metal/glm53_vision.metal"],
|
||||||
|
@[@"DS4_METAL_DEEPSEEK4_VISION_SOURCE", @"metal/deepseek4_vision.metal"],
|
||||||
@[@"DS4_METAL_GLM53_KDA_SOURCE", @"metal/glm53_kda.metal"],
|
@[@"DS4_METAL_GLM53_KDA_SOURCE", @"metal/glm53_kda.metal"],
|
||||||
@[@"DS4_METAL_MOE_SOURCE", @"metal/moe.metal"],
|
@[@"DS4_METAL_MOE_SOURCE", @"metal/moe.metal"],
|
||||||
@[@"DS4_METAL_DSV4_HC_SOURCE", @"metal/dsv4_hc.metal"],
|
@[@"DS4_METAL_DSV4_HC_SOURCE", @"metal/dsv4_hc.metal"],
|
||||||
@@ -28972,7 +28973,8 @@ static int ds4_gpu_encode_flash_attention_decode_raw_batch_heads(
|
|||||||
uint32_t window,
|
uint32_t window,
|
||||||
uint32_t n_head,
|
uint32_t n_head,
|
||||||
uint32_t head_dim,
|
uint32_t head_dim,
|
||||||
bool noncausal) {
|
bool noncausal,
|
||||||
|
const uint16_t *visual_mask) {
|
||||||
if (head_dim != 512 || n_head == 0 || n_tokens == 0 ||
|
if (head_dim != 512 || n_head == 0 || n_tokens == 0 ||
|
||||||
n_raw == 0 || raw_cap < n_raw || raw_start >= raw_cap) {
|
n_raw == 0 || raw_cap < n_raw || raw_start >= raw_cap) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -29037,7 +29039,9 @@ static int ds4_gpu_encode_flash_attention_decode_raw_batch_heads(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (noncausal) {
|
if (visual_mask) {
|
||||||
|
memcpy([mask_buffer contents], visual_mask, mask_bytes);
|
||||||
|
} else if (noncausal) {
|
||||||
memset([mask_buffer contents], 0, mask_bytes);
|
memset([mask_buffer contents], 0, mask_bytes);
|
||||||
} else {
|
} else {
|
||||||
ds4_gpu_fill_raw_decode_batch_mask((uint16_t *)[mask_buffer contents],
|
ds4_gpu_fill_raw_decode_batch_mask((uint16_t *)[mask_buffer contents],
|
||||||
@@ -29192,7 +29196,8 @@ static int ds4_gpu_encode_flash_attention_decode_mixed_batch_heads(
|
|||||||
uint32_t window,
|
uint32_t window,
|
||||||
uint32_t ratio,
|
uint32_t ratio,
|
||||||
uint32_t n_head,
|
uint32_t n_head,
|
||||||
uint32_t head_dim) {
|
uint32_t head_dim,
|
||||||
|
const uint16_t *visual_mask) {
|
||||||
if (n_comp == 0) {
|
if (n_comp == 0) {
|
||||||
return ds4_gpu_encode_flash_attention_decode_raw_batch_heads(cb,
|
return ds4_gpu_encode_flash_attention_decode_raw_batch_heads(cb,
|
||||||
heads,
|
heads,
|
||||||
@@ -29208,7 +29213,7 @@ static int ds4_gpu_encode_flash_attention_decode_mixed_batch_heads(
|
|||||||
window,
|
window,
|
||||||
n_head,
|
n_head,
|
||||||
head_dim,
|
head_dim,
|
||||||
false);
|
false, visual_mask);
|
||||||
}
|
}
|
||||||
if (head_dim != 512 || n_head == 0 || n_tokens == 0 ||
|
if (head_dim != 512 || n_head == 0 || n_tokens == 0 ||
|
||||||
n_raw == 0 || raw_cap < n_raw || raw_start >= raw_cap ||
|
n_raw == 0 || raw_cap < n_raw || raw_start >= raw_cap ||
|
||||||
@@ -29290,6 +29295,9 @@ static int ds4_gpu_encode_flash_attention_decode_mixed_batch_heads(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (visual_mask) {
|
||||||
|
memcpy([mask_buffer contents], visual_mask, mask_bytes);
|
||||||
|
} else {
|
||||||
ds4_gpu_fill_mixed_decode_batch_mask((uint16_t *)[mask_buffer contents],
|
ds4_gpu_fill_mixed_decode_batch_mask((uint16_t *)[mask_buffer contents],
|
||||||
n_tokens,
|
n_tokens,
|
||||||
n_raw,
|
n_raw,
|
||||||
@@ -29297,6 +29305,7 @@ static int ds4_gpu_encode_flash_attention_decode_mixed_batch_heads(
|
|||||||
pos0,
|
pos0,
|
||||||
window,
|
window,
|
||||||
ratio);
|
ratio);
|
||||||
|
}
|
||||||
if (use_comp_mask) {
|
if (use_comp_mask) {
|
||||||
if (!ds4_gpu_encode_cpy_f32_f16_2d(cb,
|
if (!ds4_gpu_encode_cpy_f32_f16_2d(cb,
|
||||||
maskbuf,
|
maskbuf,
|
||||||
@@ -29618,7 +29627,7 @@ int ds4_gpu_attention_decode_raw_batch_heads_tensor(
|
|||||||
window,
|
window,
|
||||||
n_head,
|
n_head,
|
||||||
head_dim,
|
head_dim,
|
||||||
false)) {
|
false, NULL)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29680,7 +29689,7 @@ int ds4_gpu_attention_noncausal_raw_batch_heads_tensor(
|
|||||||
0,
|
0,
|
||||||
n_head,
|
n_head,
|
||||||
head_dim,
|
head_dim,
|
||||||
true)) {
|
true, NULL)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29755,7 +29764,7 @@ int ds4_gpu_attention_decode_mixed_batch_heads_tensor(
|
|||||||
window,
|
window,
|
||||||
ratio,
|
ratio,
|
||||||
n_head,
|
n_head,
|
||||||
head_dim)) {
|
head_dim, NULL)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33034,7 +33043,8 @@ static int ds4_gpu_encode_router_select(
|
|||||||
uint32_t n_expert_used,
|
uint32_t n_expert_used,
|
||||||
float expert_weight_scale,
|
float expert_weight_scale,
|
||||||
bool has_bias,
|
bool has_bias,
|
||||||
bool hash_mode) {
|
bool hash_mode,
|
||||||
|
const ds4_gpu_tensor *visual_bias, uint32_t vocab_size) {
|
||||||
id<MTLBuffer> selectedbuf = ds4_gpu_tensor_buffer(selected);
|
id<MTLBuffer> selectedbuf = ds4_gpu_tensor_buffer(selected);
|
||||||
id<MTLBuffer> weightsbuf = ds4_gpu_tensor_buffer(weights);
|
id<MTLBuffer> weightsbuf = ds4_gpu_tensor_buffer(weights);
|
||||||
id<MTLBuffer> probsbuf = ds4_gpu_tensor_buffer(probs);
|
id<MTLBuffer> probsbuf = ds4_gpu_tensor_buffer(probs);
|
||||||
@@ -33052,7 +33062,7 @@ static int ds4_gpu_encode_router_select(
|
|||||||
fabsf(expert_weight_scale - 1.5f) <= 1.0e-6f;
|
fabsf(expert_weight_scale - 1.5f) <= 1.0e-6f;
|
||||||
|
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
if (flash_router_fast_path &&
|
if (!visual_bias && flash_router_fast_path &&
|
||||||
!g_quality_mode && n_tokens == 1 &&
|
!g_quality_mode && n_tokens == 1 &&
|
||||||
getenv("DS4_METAL_DISABLE_ROUTER_SELECT_FUSION") == NULL) {
|
getenv("DS4_METAL_DISABLE_ROUTER_SELECT_FUSION") == NULL) {
|
||||||
const bool pre_m5_device =
|
const bool pre_m5_device =
|
||||||
@@ -33211,7 +33221,7 @@ static int ds4_gpu_encode_router_select(
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flash_router_fast_path && !g_quality_mode && n_tokens == 1) {
|
if (!visual_bias && flash_router_fast_path && !g_quality_mode && n_tokens == 1) {
|
||||||
id<MTLComputePipelineState> softplus_sqrt_pipeline =
|
id<MTLComputePipelineState> softplus_sqrt_pipeline =
|
||||||
ds4_gpu_hot_pipeline(g_dsv4_softplus_sqrt_pipeline,
|
ds4_gpu_hot_pipeline(g_dsv4_softplus_sqrt_pipeline,
|
||||||
"kernel_dsv4_softplus_sqrt_f32_4");
|
"kernel_dsv4_softplus_sqrt_f32_4");
|
||||||
@@ -33253,7 +33263,28 @@ static int ds4_gpu_encode_router_select(
|
|||||||
}
|
}
|
||||||
if (!ok) return 0;
|
if (!ok) return 0;
|
||||||
|
|
||||||
if (hash_mode) {
|
if (visual_bias) {
|
||||||
|
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline("kernel_dsv4_router_select_visual_batch");
|
||||||
|
if (!pipeline || !tokensbuf || n_expert != 256 || n_expert_used != 6 ||
|
||||||
|
ds4_gpu_tensor_bytes(visual_bias) < 256u * sizeof(float)) return 0;
|
||||||
|
const uint32_t args[5] = { hash_rows, vocab_size, n_tokens, has_bias, hash_mode };
|
||||||
|
const uint32_t zero = 0;
|
||||||
|
id<MTLComputeCommandEncoder> enc = ds4_gpu_compute_encoder(cb);
|
||||||
|
[enc setComputePipelineState:pipeline];
|
||||||
|
[enc setBytes:args length:sizeof(args) atIndex:0];
|
||||||
|
[enc setBuffer:probsbuf offset:probs_off atIndex:1];
|
||||||
|
if (has_bias) [enc setBuffer:biasbuf offset:bias_off atIndex:2];
|
||||||
|
else [enc setBytes:&zero length:sizeof(zero) atIndex:2];
|
||||||
|
[enc setBuffer:ds4_gpu_tensor_buffer(visual_bias) offset:ds4_gpu_tensor_offset(visual_bias) atIndex:3];
|
||||||
|
if (hash_mode) [enc setBuffer:hashbuf offset:hash_off atIndex:4];
|
||||||
|
else [enc setBytes:&zero length:sizeof(zero) atIndex:4];
|
||||||
|
[enc setBuffer:tokensbuf offset:tokens_off atIndex:5];
|
||||||
|
[enc setBuffer:selectedbuf offset:selected_off atIndex:6];
|
||||||
|
[enc setThreadgroupMemoryLength:2048 atIndex:0];
|
||||||
|
[enc dispatchThreadgroups:MTLSizeMake(n_tokens,1,1) threadsPerThreadgroup:MTLSizeMake(256,1,1)];
|
||||||
|
ds4_gpu_end_compute_encoder(cb,enc);
|
||||||
|
ok = 1;
|
||||||
|
} else if (hash_mode) {
|
||||||
ok = ds4_gpu_encode_get_rows_i32_token_rows(cb,
|
ok = ds4_gpu_encode_get_rows_i32_token_rows(cb,
|
||||||
hashbuf,
|
hashbuf,
|
||||||
hash_off,
|
hash_off,
|
||||||
@@ -33323,7 +33354,7 @@ static int ds4_gpu_encode_router_select(
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flash_router_fast_path && !g_quality_mode && n_tokens == 1) {
|
if (!visual_bias && flash_router_fast_path && !g_quality_mode && n_tokens == 1) {
|
||||||
id<MTLComputePipelineState> router_weights_pipeline =
|
id<MTLComputePipelineState> router_weights_pipeline =
|
||||||
ds4_gpu_hot_pipeline(g_dsv4_router_weights_one_pipeline,
|
ds4_gpu_hot_pipeline(g_dsv4_router_weights_one_pipeline,
|
||||||
"kernel_dsv4_router_weights_one");
|
"kernel_dsv4_router_weights_one");
|
||||||
@@ -39168,7 +39199,7 @@ int ds4_gpu_router_select_tensor(
|
|||||||
n_expert_used,
|
n_expert_used,
|
||||||
expert_weight_scale,
|
expert_weight_scale,
|
||||||
has_bias && !hash_mode,
|
has_bias && !hash_mode,
|
||||||
hash_mode);
|
hash_mode, NULL, 0);
|
||||||
if (!had_batch) {
|
if (!had_batch) {
|
||||||
ok = ds4_gpu_end_commands() != 0 && ok;
|
ok = ds4_gpu_end_commands() != 0 && ok;
|
||||||
}
|
}
|
||||||
@@ -39264,7 +39295,104 @@ int ds4_gpu_router_select_batch_tensor(
|
|||||||
n_expert_used,
|
n_expert_used,
|
||||||
expert_weight_scale,
|
expert_weight_scale,
|
||||||
has_bias && !hash_mode,
|
has_bias && !hash_mode,
|
||||||
hash_mode);
|
hash_mode, NULL, 0);
|
||||||
|
if (!had_batch) {
|
||||||
|
ok = ds4_gpu_end_commands() != 0 && ok;
|
||||||
|
}
|
||||||
|
if (!ok) return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ds4_gpu_router_select_visual_batch_tensor(
|
||||||
|
ds4_gpu_tensor *selected,
|
||||||
|
ds4_gpu_tensor *weights,
|
||||||
|
ds4_gpu_tensor *probs,
|
||||||
|
const void *model_map,
|
||||||
|
uint64_t model_size,
|
||||||
|
uint64_t bias_offset,
|
||||||
|
uint64_t hash_offset,
|
||||||
|
uint32_t hash_rows,
|
||||||
|
uint32_t n_expert_groups,
|
||||||
|
uint32_t n_group_used,
|
||||||
|
bool has_bias,
|
||||||
|
bool hash_mode,
|
||||||
|
const ds4_gpu_tensor *logits,
|
||||||
|
const ds4_gpu_tensor *tokens,
|
||||||
|
uint32_t n_expert,
|
||||||
|
uint32_t n_expert_used,
|
||||||
|
float expert_weight_scale,
|
||||||
|
uint32_t n_tokens,
|
||||||
|
const ds4_gpu_tensor *visual_bias, uint32_t vocab_size) {
|
||||||
|
if (!g_initialized && !ds4_gpu_init()) return 0;
|
||||||
|
if (!selected || !weights || !probs || !logits || !tokens || !model_map ||
|
||||||
|
n_expert == 0 || n_expert_used == 0 || n_tokens == 0) return 0;
|
||||||
|
if (n_expert_groups > 1u || n_group_used > 0u) {
|
||||||
|
fprintf(stderr, "ds4: Metal router group gating is not part of this DeepSeek V4 path\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@autoreleasepool {
|
||||||
|
id<MTLBuffer> logitsbuf = ds4_gpu_tensor_buffer(logits);
|
||||||
|
id<MTLBuffer> selectedbuf = ds4_gpu_tensor_buffer(selected);
|
||||||
|
id<MTLBuffer> weightsbuf = ds4_gpu_tensor_buffer(weights);
|
||||||
|
id<MTLBuffer> probsbuf = ds4_gpu_tensor_buffer(probs);
|
||||||
|
id<MTLBuffer> tokensbuf = ds4_gpu_tensor_buffer(tokens);
|
||||||
|
if (!logitsbuf || !selectedbuf || !weightsbuf || !probsbuf || !tokensbuf ||
|
||||||
|
ds4_gpu_tensor_bytes(logits) < (uint64_t)n_tokens * n_expert * sizeof(float) ||
|
||||||
|
ds4_gpu_tensor_bytes(selected) < (uint64_t)n_tokens * n_expert_used * sizeof(int) ||
|
||||||
|
ds4_gpu_tensor_bytes(weights) < (uint64_t)n_tokens * n_expert_used * sizeof(float) ||
|
||||||
|
ds4_gpu_tensor_bytes(probs) < (uint64_t)n_tokens * n_expert * sizeof(float) ||
|
||||||
|
ds4_gpu_tensor_bytes(tokens) < (uint64_t)n_tokens * sizeof(int32_t)) {
|
||||||
|
fprintf(stderr, "ds4: Metal router batch select received undersized buffers\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t bias_inner = 0;
|
||||||
|
uint64_t hash_inner = 0;
|
||||||
|
id<MTLBuffer> biasbuf = nil;
|
||||||
|
id<MTLBuffer> hashbuf = nil;
|
||||||
|
NSUInteger bias_set_offset = 0;
|
||||||
|
NSUInteger hash_set_offset = 0;
|
||||||
|
if (has_bias && !hash_mode) {
|
||||||
|
const uint64_t bias_bytes = (uint64_t)n_expert * sizeof(float);
|
||||||
|
biasbuf = ds4_gpu_wrap_model_range(model_map, model_size, bias_offset, bias_bytes, &bias_inner);
|
||||||
|
if (!biasbuf) return 0;
|
||||||
|
bias_set_offset = (NSUInteger)bias_inner;
|
||||||
|
}
|
||||||
|
if (hash_mode) {
|
||||||
|
const uint64_t hash_bytes = (uint64_t)hash_rows * n_expert_used * sizeof(int32_t);
|
||||||
|
hashbuf = ds4_gpu_wrap_model_range(model_map, model_size, hash_offset, hash_bytes, &hash_inner);
|
||||||
|
if (!hashbuf) return 0;
|
||||||
|
hash_set_offset = (NSUInteger)hash_inner;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool had_batch = g_batch_cb != nil;
|
||||||
|
if (!had_batch && ds4_gpu_begin_commands() == 0) return 0;
|
||||||
|
int owned = 0;
|
||||||
|
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
|
||||||
|
int ok = cb &&
|
||||||
|
ds4_gpu_encode_router_select(cb,
|
||||||
|
selected,
|
||||||
|
weights,
|
||||||
|
probs,
|
||||||
|
logitsbuf,
|
||||||
|
ds4_gpu_tensor_offset(logits),
|
||||||
|
biasbuf,
|
||||||
|
bias_set_offset,
|
||||||
|
hashbuf,
|
||||||
|
hash_set_offset,
|
||||||
|
tokensbuf,
|
||||||
|
ds4_gpu_tensor_offset(tokens),
|
||||||
|
NULL,
|
||||||
|
hash_rows,
|
||||||
|
n_tokens,
|
||||||
|
n_expert,
|
||||||
|
n_expert_used,
|
||||||
|
expert_weight_scale,
|
||||||
|
has_bias && !hash_mode,
|
||||||
|
hash_mode, visual_bias, vocab_size);
|
||||||
if (!had_batch) {
|
if (!had_batch) {
|
||||||
ok = ds4_gpu_end_commands() != 0 && ok;
|
ok = ds4_gpu_end_commands() != 0 && ok;
|
||||||
}
|
}
|
||||||
@@ -45809,3 +45937,71 @@ int ds4_gpu_glm53_kda_prefill(
|
|||||||
void ds4_gpu_set_glm_mtp_verify_mode(bool enabled) {
|
void ds4_gpu_set_glm_mtp_verify_mode(bool enabled) {
|
||||||
(void)enabled;
|
(void)enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Primitive dispatch only. The DeepSeek image graph and all layouts live in Rust.
|
||||||
|
int ds4_gpu_deepseek_vision_dispatch(
|
||||||
|
const char *kernel, const ds4_gpu_metal_binding *bindings, uint32_t count,
|
||||||
|
const uint32_t grid[3], const uint32_t threads[3],
|
||||||
|
uint32_t individual_threads, uint32_t shared_bytes) {
|
||||||
|
if (!kernel || !bindings || count == 0 || count > 31 || !grid || !threads ||
|
||||||
|
!grid[0] || !grid[1] || !grid[2] || !threads[0] || !threads[1] || !threads[2]) return 0;
|
||||||
|
if (!g_initialized && !ds4_gpu_init()) return 0;
|
||||||
|
id<MTLComputePipelineState> pipeline = ds4_gpu_get_pipeline(kernel);
|
||||||
|
if (!pipeline || (uint64_t)threads[0] * threads[1] * threads[2] > pipeline.maxTotalThreadsPerThreadgroup) return 0;
|
||||||
|
for (uint32_t i = 0; i < count; i++) {
|
||||||
|
const ds4_gpu_metal_binding *b = &bindings[i];
|
||||||
|
if (b->index > 30 || (b->tensor ? b->offset >= ds4_gpu_tensor_bytes(b->tensor)
|
||||||
|
: !b->bytes || b->size == 0 || b->size > 4096)) return 0;
|
||||||
|
}
|
||||||
|
int owned = 0;
|
||||||
|
id<MTLCommandBuffer> cb = ds4_gpu_command_buffer(&owned);
|
||||||
|
id<MTLComputeCommandEncoder> enc = cb ? ds4_gpu_compute_encoder(cb) : nil;
|
||||||
|
if (!enc) return 0;
|
||||||
|
[enc setComputePipelineState:pipeline];
|
||||||
|
for (uint32_t i = 0; i < count; i++) {
|
||||||
|
const ds4_gpu_metal_binding *b = &bindings[i];
|
||||||
|
if (b->tensor) {
|
||||||
|
[enc setBuffer:ds4_gpu_tensor_buffer(b->tensor)
|
||||||
|
offset:ds4_gpu_tensor_offset(b->tensor) + b->offset atIndex:b->index];
|
||||||
|
} else {
|
||||||
|
[enc setBytes:b->bytes length:b->size atIndex:b->index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (shared_bytes) [enc setThreadgroupMemoryLength:shared_bytes atIndex:0];
|
||||||
|
if (individual_threads) {
|
||||||
|
[enc dispatchThreads:MTLSizeMake(grid[0],grid[1],grid[2])
|
||||||
|
threadsPerThreadgroup:MTLSizeMake(threads[0],threads[1],threads[2])];
|
||||||
|
} else {
|
||||||
|
[enc dispatchThreadgroups:MTLSizeMake(grid[0],grid[1],grid[2])
|
||||||
|
threadsPerThreadgroup:MTLSizeMake(threads[0],threads[1],threads[2])];
|
||||||
|
}
|
||||||
|
ds4_gpu_end_compute_encoder(cb, enc);
|
||||||
|
return ds4_gpu_finish_command_buffer(cb, owned, "DeepSeek vision dispatch");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rust constructs the model-specific visual mask; this bridge only binds it
|
||||||
|
// to the same FlashAttention primitive used by ordinary DeepSeek prefill.
|
||||||
|
int ds4_gpu_attention_visual_batch_tensor(
|
||||||
|
ds4_gpu_tensor *heads, const void *map, uint64_t size, uint64_t sinks,
|
||||||
|
const ds4_gpu_tensor *q, const ds4_gpu_tensor *raw,
|
||||||
|
const ds4_gpu_tensor *comp, const ds4_gpu_tensor *comp_mask,
|
||||||
|
uint32_t use_mask, uint32_t rows, uint32_t pos,
|
||||||
|
uint32_t n_raw, uint32_t raw_cap, uint32_t raw_start,
|
||||||
|
uint32_t n_comp, uint32_t window, uint32_t ratio,
|
||||||
|
uint32_t n_head, uint32_t head_dim,
|
||||||
|
const uint16_t *mask, uint64_t mask_elements) {
|
||||||
|
if (!map || !heads || !q || !raw || !mask || !rows || !n_raw ||
|
||||||
|
raw_cap < n_raw || raw_start >= raw_cap || (n_comp && (!comp || !ratio)) ||
|
||||||
|
(use_mask && !comp_mask) || mask_elements != (uint64_t)rows * ((uint64_t)n_raw+n_comp) ||
|
||||||
|
sinks > size || (uint64_t)n_head * sizeof(float) > size-sinks) return 0;
|
||||||
|
if (!g_initialized && !ds4_gpu_init()) return 0;
|
||||||
|
uint64_t offset = 0;
|
||||||
|
id<MTLBuffer> weight = ds4_gpu_wrap_model_range(map,size,sinks,(uint64_t)n_head*sizeof(float),&offset);
|
||||||
|
if (!weight) return 0;
|
||||||
|
int owned=0;
|
||||||
|
id<MTLCommandBuffer> cb=ds4_gpu_command_buffer(&owned);
|
||||||
|
if (!cb || !ds4_gpu_encode_flash_attention_decode_mixed_batch_heads(
|
||||||
|
cb,heads,weight,offset,q,raw,comp,1,comp_mask,use_mask,
|
||||||
|
rows,pos,n_raw,raw_cap,raw_start,n_comp,window,ratio,n_head,head_dim,mask)) return 0;
|
||||||
|
return ds4_gpu_finish_command_buffer(cb,owned,"DeepSeek visual attention");
|
||||||
|
}
|
||||||
+1
-1
@@ -25,7 +25,7 @@ impl ModelPreferences {
|
|||||||
reasoning_mode: model.default_reasoning_mode(),
|
reasoning_mode: model.default_reasoning_mode(),
|
||||||
speculative: SpeculativePreferences {
|
speculative: SpeculativePreferences {
|
||||||
glm_mtp: model == ModelChoice::Qwen38FlashNext,
|
glm_mtp: model == ModelChoice::Qwen38FlashNext,
|
||||||
dspark_enabled: model == ModelChoice::DeepSeekV4Flash0731,
|
dspark_enabled: model.supports_dspark(),
|
||||||
..SpeculativePreferences::default()
|
..SpeculativePreferences::default()
|
||||||
},
|
},
|
||||||
ssd: SsdPreferences::default(),
|
ssd: SsdPreferences::default(),
|
||||||
|
|||||||
+20
-1
@@ -964,6 +964,14 @@ mod tests {
|
|||||||
crate::model::ModelChoice::DeepSeekV4Flash0731,
|
crate::model::ModelChoice::DeepSeekV4Flash0731,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let vision = database
|
||||||
|
.create_session(
|
||||||
|
project.id,
|
||||||
|
"Vision",
|
||||||
|
PermissionMode::Ai,
|
||||||
|
crate::model::ModelChoice::DeepSeekV4FlashVision,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
database
|
database
|
||||||
.connection
|
.connection
|
||||||
@@ -973,10 +981,21 @@ mod tests {
|
|||||||
.connection
|
.connection
|
||||||
.run_pending_migrations(MIGRATIONS)
|
.run_pending_migrations(MIGRATIONS)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let sessions = &database.load_projects().unwrap()[0].sessions;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
database.load_projects().unwrap()[0].sessions[0].model(),
|
sessions
|
||||||
|
.iter()
|
||||||
|
.find(|session| session.id == existing.id)
|
||||||
|
.unwrap()
|
||||||
|
.model(),
|
||||||
existing.model()
|
existing.model()
|
||||||
);
|
);
|
||||||
|
let downgraded = sessions
|
||||||
|
.iter()
|
||||||
|
.find(|session| session.id == vision.id)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(downgraded.title, "Vision");
|
||||||
|
assert_eq!(downgraded.model(), None);
|
||||||
|
|
||||||
for model in crate::model::MODEL_CHOICES {
|
for model in crate::model::MODEL_CHOICES {
|
||||||
let session = database
|
let session = database
|
||||||
|
|||||||
+85
-5
@@ -1,3 +1,4 @@
|
|||||||
|
mod deepseek_vision;
|
||||||
#[cfg(any(target_os = "macos", test))]
|
#[cfg(any(target_os = "macos", test))]
|
||||||
mod ds4_sampling;
|
mod ds4_sampling;
|
||||||
mod gguf;
|
mod gguf;
|
||||||
@@ -216,6 +217,12 @@ const PRO: Shape = Shape {
|
|||||||
..DEEPSEEK_BASE
|
..DEEPSEEK_BASE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const FLASH_VISION: Shape = Shape {
|
||||||
|
model: ModelChoice::DeepSeekV4FlashVision,
|
||||||
|
rms_epsilon: 1e-20,
|
||||||
|
..FLASH_0731
|
||||||
|
};
|
||||||
|
|
||||||
const GLM: Shape = Shape {
|
const GLM: Shape = Shape {
|
||||||
model: ModelChoice::Glm52,
|
model: ModelChoice::Glm52,
|
||||||
family: ModelFamily::Glm,
|
family: ModelFamily::Glm,
|
||||||
@@ -619,9 +626,14 @@ impl Model {
|
|||||||
messages: &[ChatTurn],
|
messages: &[ChatTurn],
|
||||||
reasoning: ReasoningMode,
|
reasoning: ReasoningMode,
|
||||||
) -> Vec<i32> {
|
) -> Vec<i32> {
|
||||||
|
if self.shape.model == ModelChoice::DeepSeekV4FlashVision {
|
||||||
|
self.tokenizer
|
||||||
|
.encode_deepseek_vision_conversation(system, messages, reasoning)
|
||||||
|
} else {
|
||||||
self.tokenizer
|
self.tokenizer
|
||||||
.encode_conversation(system, messages, reasoning)
|
.encode_conversation(system, messages, reasoning)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render_history(
|
fn render_history(
|
||||||
&self,
|
&self,
|
||||||
@@ -1594,13 +1606,63 @@ impl Generator {
|
|||||||
if encoded_images.next().is_some() {
|
if encoded_images.next().is_some() {
|
||||||
return Err("vision encoder returned too many embeddings".into());
|
return Err("vision encoder returned too many embeddings".into());
|
||||||
}
|
}
|
||||||
let tokens = self
|
// DS4 retains the generated token frontier when an authenticated
|
||||||
.executor
|
// conversation appends a turn. Re-tokenizing the assistant's visible
|
||||||
|
// text can change BPE boundaries and shift historical image blocks.
|
||||||
|
let continuation = match (&self.executor, messages.split_last()) {
|
||||||
|
(metal::Executor::DeepSeek(executor), Some((latest, history)))
|
||||||
|
if executor.model().shape.model == ModelChoice::DeepSeekV4FlashVision
|
||||||
|
&& (latest.user || latest.tool)
|
||||||
|
&& !history.is_empty()
|
||||||
|
&& self.executor.checkpoint_tag()
|
||||||
|
== conversation_tag(ModelFamily::DeepSeek, system, reasoning, history) =>
|
||||||
|
{
|
||||||
|
let old_count = messages[..history.len()]
|
||||||
|
.iter()
|
||||||
|
.map(|turn| turn.content.matches(VISION_DATA_START).count())
|
||||||
|
.sum::<usize>();
|
||||||
|
executor
|
||||||
|
.cached_vision_positions(&embeddings[..old_count])
|
||||||
|
.map(|positions| {
|
||||||
|
let mut tokens = self.executor.tokens().to_vec();
|
||||||
|
let cursor = tokens.len();
|
||||||
|
let latest = rendered.last().expect("latest turn exists");
|
||||||
|
let tokenizer = &executor.model().tokenizer;
|
||||||
|
tokens.extend(if latest.tool {
|
||||||
|
tokenizer.encode_deepseek_vision_tool_continuation(latest, reasoning)
|
||||||
|
} else {
|
||||||
|
tokenizer.encode_deepseek_vision_continuation(
|
||||||
|
&latest.content,
|
||||||
|
reasoning,
|
||||||
|
latest.skip_previous_eos,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
(tokens, cursor, positions)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
let (mut tokens, mut cursor, old_positions) = continuation.unwrap_or_else(|| {
|
||||||
|
(
|
||||||
|
self.executor
|
||||||
.model()
|
.model()
|
||||||
.render_conversation(system, &rendered, reasoning);
|
.render_conversation(system, &rendered, reasoning),
|
||||||
|
0,
|
||||||
|
Vec::new(),
|
||||||
|
)
|
||||||
|
});
|
||||||
let mut overlays = Vec::with_capacity(embeddings.len());
|
let mut overlays = Vec::with_capacity(embeddings.len());
|
||||||
let mut cursor = 0_usize;
|
for (index, mut embedding) in embeddings.into_iter().enumerate() {
|
||||||
for embedding in embeddings {
|
if let Some(&(start, count)) = old_positions.get(index)
|
||||||
|
&& let metal::Executor::DeepSeek(executor) = &self.executor
|
||||||
|
{
|
||||||
|
let block = executor.layout_vision(&mut embedding, start)?;
|
||||||
|
if tokens.get(start as usize..(start + count) as usize) != Some(block.as_slice()) {
|
||||||
|
return Err("cached DeepSeek image token layout is invalid".into());
|
||||||
|
}
|
||||||
|
overlays.push((start, embedding));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let count = embedding.tokens as usize;
|
let count = embedding.tokens as usize;
|
||||||
let relative = tokens[cursor..]
|
let relative = tokens[cursor..]
|
||||||
.windows(count + 2)
|
.windows(count + 2)
|
||||||
@@ -1612,6 +1674,17 @@ impl Generator {
|
|||||||
.all(|token| *token == VISION_IMAGE_TOKEN)
|
.all(|token| *token == VISION_IMAGE_TOKEN)
|
||||||
})
|
})
|
||||||
.ok_or("rendered prompt lost an image placeholder")?;
|
.ok_or("rendered prompt lost an image placeholder")?;
|
||||||
|
if let metal::Executor::DeepSeek(executor) = &self.executor {
|
||||||
|
let start = cursor + relative;
|
||||||
|
let position =
|
||||||
|
u32::try_from(start).map_err(|_| "image prompt position overflow")?;
|
||||||
|
let block = executor.layout_vision(&mut embedding, position)?;
|
||||||
|
let block_len = block.len();
|
||||||
|
tokens.splice(start..start + count + 2, block);
|
||||||
|
overlays.push((position, embedding));
|
||||||
|
cursor = start + block_len;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let start = cursor + relative + 1;
|
let start = cursor + relative + 1;
|
||||||
overlays.push((
|
overlays.push((
|
||||||
u32::try_from(start).map_err(|_| "image prompt position overflow")?,
|
u32::try_from(start).map_err(|_| "image prompt position overflow")?,
|
||||||
@@ -1662,8 +1735,15 @@ impl Generator {
|
|||||||
tokens.len()
|
tokens.len()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
let reused = if matches!(&self.executor, metal::Executor::DeepSeek(e) if e.model().shape.model == ModelChoice::DeepSeekV4FlashVision)
|
||||||
|
{
|
||||||
|
self.executor.set_vision_overlays(overlays)?;
|
||||||
|
self.executor.align_prompt(&tokens)?
|
||||||
|
} else {
|
||||||
let reused = self.executor.align_prompt(&tokens)?;
|
let reused = self.executor.align_prompt(&tokens)?;
|
||||||
self.executor.set_vision_overlays(overlays)?;
|
self.executor.set_vision_overlays(overlays)?;
|
||||||
|
reused
|
||||||
|
};
|
||||||
self.metrics.kv_prefix_reused(reused);
|
self.metrics.kv_prefix_reused(reused);
|
||||||
progress(self.executor.position(), self.executor.context(), None);
|
progress(self.executor.position(), self.executor.context(), None);
|
||||||
let seed = settings.seed.unwrap_or(0x4453_3453_4552_5645);
|
let seed = settings.seed.unwrap_or(0x4453_3453_4552_5645);
|
||||||
|
|||||||
@@ -0,0 +1,454 @@
|
|||||||
|
//! DeepSeek V4 Flash Vision-Exp image layout and prefill masks.
|
||||||
|
//! Behavioral reference: DS4 0aaea5a238fb41a35106a551e73c8409dfb751ac.
|
||||||
|
use image::{DynamicImage, ImageDecoder, ImageReader};
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
pub(super) const SOURCE_REVISION: &[u8] = b"e46e16bf6035c6f317eb2ac7458eb0362926d402";
|
||||||
|
pub(super) const START: i32 = 0;
|
||||||
|
pub(super) const PAD: i32 = 1;
|
||||||
|
pub(super) const IMAGE: i32 = 2;
|
||||||
|
pub(super) const NEWLINE: i32 = 3;
|
||||||
|
pub(super) const END: i32 = 4;
|
||||||
|
|
||||||
|
pub(super) struct Patches {
|
||||||
|
pub values: Vec<f32>,
|
||||||
|
pub fingerprint: [u8; 32],
|
||||||
|
pub width: u32,
|
||||||
|
pub height: u32,
|
||||||
|
pub content_width: u32,
|
||||||
|
pub content_height: u32,
|
||||||
|
pub grid_width: u32,
|
||||||
|
pub grid_height: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn decode(encoded: &[u8]) -> Result<Patches, String> {
|
||||||
|
if encoded.is_empty() || encoded.len() > 64 * 1024 * 1024 {
|
||||||
|
return Err("image is empty or exceeds the 64 MiB encoded limit".into());
|
||||||
|
}
|
||||||
|
let reader = ImageReader::new(Cursor::new(encoded))
|
||||||
|
.with_guessed_format()
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
let mut decoder = reader.into_decoder().map_err(|e| e.to_string())?;
|
||||||
|
let (width, height) = decoder.dimensions();
|
||||||
|
if width == 0
|
||||||
|
|| height == 0
|
||||||
|
|| width > 16384
|
||||||
|
|| height > 16384
|
||||||
|
|| u64::from(width) * u64::from(height) > 64 * 1024 * 1024
|
||||||
|
{
|
||||||
|
return Err("invalid DeepSeek image dimensions".into());
|
||||||
|
}
|
||||||
|
let orientation = decoder.orientation().map_err(|e| e.to_string())?;
|
||||||
|
let mut image = DynamicImage::from_decoder(decoder).map_err(|e| e.to_string())?;
|
||||||
|
image.apply_orientation(orientation);
|
||||||
|
let rgb = image.into_rgb8();
|
||||||
|
preprocess(rgb.as_raw(), rgb.width(), rgb.height())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn grid_tokens(height: u32, width: u32) -> u32 {
|
||||||
|
let h = (height / 14).div_ceil(3);
|
||||||
|
let w = (width / 14).div_ceil(3);
|
||||||
|
(h + h % 2) * (w + 1) + 2 + (h.div_ceil(2) * (w + 1) % 2) * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resize_shape(height: u32, width: u32) -> Result<(u32, u32), String> {
|
||||||
|
let mut pw = width.min(height * 8);
|
||||||
|
let mut ph = height;
|
||||||
|
if u64::from(pw) * u64::from(ph) < 147456 {
|
||||||
|
let scale = (147456.0 / (f64::from(pw) * f64::from(ph))).sqrt();
|
||||||
|
pw = ((f64::from(pw) * scale) as u32).max(1);
|
||||||
|
ph = ((f64::from(ph) * scale) as u32).max(1);
|
||||||
|
}
|
||||||
|
let mut bw = pw.div_ceil(14) * 14;
|
||||||
|
let mut bh = ph.div_ceil(14) * 14;
|
||||||
|
let mut budget = 381;
|
||||||
|
while grid_tokens(bh, bw) > 381 {
|
||||||
|
if budget <= 4 {
|
||||||
|
return Err("image cannot fit the DeepSeek vision token budget".into());
|
||||||
|
}
|
||||||
|
let ratio = f64::from(ph) / f64::from(pw);
|
||||||
|
let mw = ((f64::from(budget) - 2.0) / ratio + 0.25).sqrt() - 0.5;
|
||||||
|
let mh = mw * ratio;
|
||||||
|
if mw < 1.0 {
|
||||||
|
bw = 42;
|
||||||
|
bh = (((budget - 2) / 2) & !1) * 42;
|
||||||
|
} else if mh < 2.0 {
|
||||||
|
bh = 84;
|
||||||
|
bw = ((budget - 2) / 2 - 1) * 42;
|
||||||
|
if bw <= 42 {
|
||||||
|
return Err("image cannot fit the DeepSeek vision token budget".into());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mw = mw.floor() as u32;
|
||||||
|
let mh = mh.floor() as u32 & !1;
|
||||||
|
let scale =
|
||||||
|
(f64::from(mw) * 42.0 / f64::from(pw)).min(f64::from(mh) * 42.0 / f64::from(ph));
|
||||||
|
bw = (f64::from(pw) * scale / 14.0).floor() as u32 * 14;
|
||||||
|
bh = (f64::from(ph) * scale / 14.0).floor() as u32 * 14;
|
||||||
|
}
|
||||||
|
if bw == 0 || bh == 0 {
|
||||||
|
return Err("invalid DeepSeek resized image dimensions".into());
|
||||||
|
}
|
||||||
|
budget -= 1;
|
||||||
|
}
|
||||||
|
Ok((bh, bw))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn preprocess(rgb: &[u8], width: u32, height: u32) -> Result<Patches, String> {
|
||||||
|
if width == 0
|
||||||
|
|| height == 0
|
||||||
|
|| width > 16384
|
||||||
|
|| height > 16384
|
||||||
|
|| u64::from(width) * u64::from(height) > 64 * 1024 * 1024
|
||||||
|
|| rgb.len() != width as usize * height as usize * 3
|
||||||
|
{
|
||||||
|
return Err("invalid DeepSeek image dimensions".into());
|
||||||
|
}
|
||||||
|
let (bh, bw) = resize_shape(height, width)?;
|
||||||
|
if bw > 16384 || bh > 16384 || u64::from(bw) * u64::from(bh) > 64 * 1024 * 1024 {
|
||||||
|
return Err("resized DeepSeek image exceeds the decoded image limits".into());
|
||||||
|
}
|
||||||
|
let (cw, ch, ox, oy) = if width >= height * 8 {
|
||||||
|
(bw, bh, 0, 0)
|
||||||
|
} else {
|
||||||
|
let scale = (f64::from(bw) / f64::from(width)).min(f64::from(bh) / f64::from(height));
|
||||||
|
let cw = (f64::from(width) * scale)
|
||||||
|
.round_ties_even()
|
||||||
|
.clamp(1.0, f64::from(bw)) as u32;
|
||||||
|
let ch = (f64::from(height) * scale)
|
||||||
|
.round_ties_even()
|
||||||
|
.clamp(1.0, f64::from(bh)) as u32;
|
||||||
|
(
|
||||||
|
cw,
|
||||||
|
ch,
|
||||||
|
(f64::from(bw - cw) * 0.5).round_ties_even() as u32,
|
||||||
|
(f64::from(bh - ch) * 0.5).round_ties_even() as u32,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let mut canvas = vec![127.0_f32; bw as usize * bh as usize * 3];
|
||||||
|
let offset = (oy as usize * bw as usize + ox as usize) * 3;
|
||||||
|
super::metal::vision::resize_bicubic(rgb, width, height, &mut canvas[offset..], cw, ch, bw);
|
||||||
|
for value in &mut canvas {
|
||||||
|
*value = *value / 127.5 - 1.0;
|
||||||
|
}
|
||||||
|
let gw = bw / 14;
|
||||||
|
let gh = bh / 14;
|
||||||
|
let mut values = Vec::with_capacity(gw as usize * gh as usize * 588);
|
||||||
|
for py in 0..gh {
|
||||||
|
for px in 0..gw {
|
||||||
|
for c in 0..3 {
|
||||||
|
for y in 0..14 {
|
||||||
|
for x in 0..14 {
|
||||||
|
values.push(
|
||||||
|
canvas[((py * 14 + y) as usize * bw as usize + (px * 14 + x) as usize)
|
||||||
|
* 3
|
||||||
|
+ c],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut hash = Sha256::new();
|
||||||
|
hash.update(width.to_le_bytes());
|
||||||
|
hash.update(height.to_le_bytes());
|
||||||
|
hash.update(rgb);
|
||||||
|
Ok(Patches {
|
||||||
|
fingerprint: hash.finalize().into(),
|
||||||
|
values,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
content_width: cw,
|
||||||
|
content_height: ch,
|
||||||
|
grid_width: gw,
|
||||||
|
grid_height: gh,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct Layout {
|
||||||
|
pub types: Vec<i32>,
|
||||||
|
pub permutation: Vec<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn layout(height: u32, width: u32, position: u32) -> Result<Layout, String> {
|
||||||
|
if height == 0 || width == 0 {
|
||||||
|
return Err("invalid DeepSeek image token grid".into());
|
||||||
|
}
|
||||||
|
let rows = u64::from(height) + u64::from(height % 2);
|
||||||
|
let row_length = u64::from(width) + 1;
|
||||||
|
let leading = 3 - position % 4;
|
||||||
|
let trailing = ((rows / 2 * row_length) % 2) * 2;
|
||||||
|
let count = u64::from(leading) + 2 + rows * row_length + trailing;
|
||||||
|
if count > 384 {
|
||||||
|
return Err("DeepSeek image token layout exceeds its model limit".into());
|
||||||
|
}
|
||||||
|
let mut types = vec![PAD; leading as usize];
|
||||||
|
let mut permutation = Vec::with_capacity(height as usize * width as usize);
|
||||||
|
types.push(START);
|
||||||
|
for pair in 0..rows / 2 {
|
||||||
|
for column in 0..row_length {
|
||||||
|
for row in pair * 2..pair * 2 + 2 {
|
||||||
|
if row < u64::from(height) && column < u64::from(width) {
|
||||||
|
types.push(IMAGE);
|
||||||
|
permutation.push((row * u64::from(width) + column) as usize);
|
||||||
|
} else {
|
||||||
|
types.push(if row < u64::from(height) {
|
||||||
|
NEWLINE
|
||||||
|
} else {
|
||||||
|
PAD
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
types.extend(std::iter::repeat_n(PAD, trailing as usize));
|
||||||
|
types.push(END);
|
||||||
|
Ok(Layout { types, permutation })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub(super) struct Span {
|
||||||
|
pub block: usize,
|
||||||
|
pub start: usize,
|
||||||
|
pub end: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn spans(tokens: &[i32], vocab: i32) -> Result<Vec<Span>, String> {
|
||||||
|
let mut result = Vec::new();
|
||||||
|
let mut cursor = 0;
|
||||||
|
while cursor < tokens.len() {
|
||||||
|
if (0..vocab).contains(&tokens[cursor]) {
|
||||||
|
cursor += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let block = cursor;
|
||||||
|
while cursor < tokens.len() && tokens[cursor] == vocab + PAD {
|
||||||
|
cursor += 1;
|
||||||
|
}
|
||||||
|
if tokens.get(cursor) != Some(&(vocab + START)) {
|
||||||
|
return Err("malformed DeepSeek image block".into());
|
||||||
|
}
|
||||||
|
let start = cursor;
|
||||||
|
cursor += 1;
|
||||||
|
loop {
|
||||||
|
let token = *tokens
|
||||||
|
.get(cursor)
|
||||||
|
.ok_or("unterminated DeepSeek image block")?;
|
||||||
|
if token <= vocab + START || token > vocab + END {
|
||||||
|
return Err("malformed DeepSeek image block".into());
|
||||||
|
}
|
||||||
|
if token == vocab + END {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cursor += 1;
|
||||||
|
}
|
||||||
|
result.push(Span {
|
||||||
|
block,
|
||||||
|
start,
|
||||||
|
end: cursor,
|
||||||
|
});
|
||||||
|
cursor += 1;
|
||||||
|
}
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn prefill_chunk(tokens: &[i32], vocab: i32, cap: u32) -> Result<usize, String> {
|
||||||
|
let mut limit = tokens.len().min(cap as usize);
|
||||||
|
if limit == 0 {
|
||||||
|
return Err("empty DeepSeek prefill chunk".into());
|
||||||
|
}
|
||||||
|
for span in spans(tokens, vocab)? {
|
||||||
|
if span.block >= limit {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if span.end + 1 > limit {
|
||||||
|
if span.end + 1 > cap as usize {
|
||||||
|
if span.block == 0 {
|
||||||
|
return Err("DeepSeek image block exceeds prefill capacity".into());
|
||||||
|
}
|
||||||
|
limit = span.block;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
limit = span.end + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn attention_bounds(
|
||||||
|
tokens: &[i32],
|
||||||
|
vocab: i32,
|
||||||
|
pos: u32,
|
||||||
|
n_raw: u32,
|
||||||
|
window: u32,
|
||||||
|
) -> Result<Vec<u32>, String> {
|
||||||
|
let end = pos
|
||||||
|
.checked_add(
|
||||||
|
tokens
|
||||||
|
.len()
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| "vision attention length overflow")?,
|
||||||
|
)
|
||||||
|
.ok_or("vision attention position overflow")?;
|
||||||
|
if tokens.is_empty() || n_raw == 0 || n_raw > end {
|
||||||
|
return Err("invalid DeepSeek vision attention extent".into());
|
||||||
|
}
|
||||||
|
let first = end - n_raw;
|
||||||
|
let mut bounds = Vec::with_capacity(tokens.len() * 2);
|
||||||
|
for q in 0..tokens.len() as u32 {
|
||||||
|
let p = pos + q;
|
||||||
|
bounds.extend([
|
||||||
|
first.max(if window == 0 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
(p + 1).saturating_sub(window)
|
||||||
|
}),
|
||||||
|
p.min(end - 1),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
for span in spans(tokens, vocab)? {
|
||||||
|
for q in span.start..=span.end {
|
||||||
|
let p = pos + q as u32;
|
||||||
|
let left = (q - span.start).min(383) as u32;
|
||||||
|
let right = (span.end - q).min(384) as u32;
|
||||||
|
let add = if window != 0 && left >= window {
|
||||||
|
left - (window - 1)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
bounds[2 * q] = first.max(if window == 0 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
p.saturating_sub(window - 1 + add)
|
||||||
|
});
|
||||||
|
bounds[2 * q + 1] = (p + right).min(end - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(bounds)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn issue100_ds4_image_layout_and_attention_vectors() {
|
||||||
|
for (h, w, p, types, perm) in [
|
||||||
|
(
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
0,
|
||||||
|
vec![1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 3, 3, 4],
|
||||||
|
vec![0, 3, 1, 4, 2, 5],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
3,
|
||||||
|
2,
|
||||||
|
5,
|
||||||
|
vec![1, 1, 0, 2, 2, 2, 2, 3, 3, 2, 1, 2, 1, 3, 1, 4],
|
||||||
|
vec![0, 2, 1, 3, 4, 5],
|
||||||
|
),
|
||||||
|
(1, 1, 3, vec![0, 2, 1, 3, 1, 4], vec![0]),
|
||||||
|
] {
|
||||||
|
let actual = layout(h, w, p).unwrap();
|
||||||
|
assert_eq!(actual.types, types);
|
||||||
|
assert_eq!(actual.permutation, perm);
|
||||||
|
}
|
||||||
|
let tokens = [7, 101, 101, 100, 102, 103, 104, 8, 100, 101, 104, 9];
|
||||||
|
assert_eq!(
|
||||||
|
spans(&tokens, 100).unwrap(),
|
||||||
|
vec![
|
||||||
|
Span {
|
||||||
|
block: 1,
|
||||||
|
start: 3,
|
||||||
|
end: 6
|
||||||
|
},
|
||||||
|
Span {
|
||||||
|
block: 8,
|
||||||
|
start: 8,
|
||||||
|
end: 10
|
||||||
|
}
|
||||||
|
]
|
||||||
|
);
|
||||||
|
assert_eq!(prefill_chunk(&tokens, 100, 5).unwrap(), 1);
|
||||||
|
assert_eq!(prefill_chunk(&tokens[1..], 100, 6).unwrap(), 6);
|
||||||
|
assert!(prefill_chunk(&tokens[1..], 100, 5).is_err());
|
||||||
|
assert!(prefill_chunk(&tokens[4..], 100, 8).is_err());
|
||||||
|
for malformed in [
|
||||||
|
[101, 4, 5, 6],
|
||||||
|
[100, 4, 104, 6],
|
||||||
|
[100, 100, 104, 6],
|
||||||
|
[100, 102, 6, 7],
|
||||||
|
[100, 105, 6, 7],
|
||||||
|
[-1, 100, 104, 6],
|
||||||
|
] {
|
||||||
|
assert!(spans(&malformed, 100).is_err());
|
||||||
|
}
|
||||||
|
assert_eq!(
|
||||||
|
attention_bounds(
|
||||||
|
&[7, 101, 100, 102, 102, 102, 102, 102, 104, 8],
|
||||||
|
100,
|
||||||
|
10,
|
||||||
|
20,
|
||||||
|
4
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
vec![
|
||||||
|
7, 10, 8, 11, 9, 18, 10, 18, 11, 18, 12, 18, 12, 18, 12, 18, 12, 18, 16, 19
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn issue100_ds4_preprocess_dimensions_and_limits() {
|
||||||
|
let mut rgb = Vec::new();
|
||||||
|
for y in 0..9 {
|
||||||
|
for x in 0..17 {
|
||||||
|
rgb.extend([
|
||||||
|
(x * 13 + y * 3) as u8,
|
||||||
|
(x * 5 + y * 17) as u8,
|
||||||
|
(x * 7 + y * 11) as u8,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let p = preprocess(&rgb, 17, 9).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
(
|
||||||
|
p.grid_width,
|
||||||
|
p.grid_height,
|
||||||
|
p.content_width,
|
||||||
|
p.content_height
|
||||||
|
),
|
||||||
|
(38, 20, 529, 280)
|
||||||
|
);
|
||||||
|
assert_eq!(p.values.len(), 760 * 588);
|
||||||
|
assert!(
|
||||||
|
p.values
|
||||||
|
.iter()
|
||||||
|
.all(|v| v.is_finite() && (-1.0..=1.0).contains(v))
|
||||||
|
);
|
||||||
|
for (h, w) in [
|
||||||
|
(1, 1),
|
||||||
|
(1, 8),
|
||||||
|
(1, 16384),
|
||||||
|
(16384, 1),
|
||||||
|
(384, 384),
|
||||||
|
(1024, 1024),
|
||||||
|
(1080, 1920),
|
||||||
|
(4096, 4096),
|
||||||
|
] {
|
||||||
|
let (bh, bw) = resize_shape(h, w).unwrap();
|
||||||
|
assert!(grid_tokens(bh, bw) <= 381);
|
||||||
|
for start in 0..4 {
|
||||||
|
assert!(
|
||||||
|
layout((bh / 14).div_ceil(3), (bw / 14).div_ceil(3), start)
|
||||||
|
.unwrap()
|
||||||
|
.types
|
||||||
|
.len()
|
||||||
|
<= 384
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(preprocess(&[], 0, 1).is_err());
|
||||||
|
assert!(layout(384, 384, 0).is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
+211
-22
@@ -1,4 +1,5 @@
|
|||||||
mod checkpoint;
|
mod checkpoint;
|
||||||
|
mod deepseek_vision;
|
||||||
mod glm;
|
mod glm;
|
||||||
mod gpu;
|
mod gpu;
|
||||||
mod hotlist;
|
mod hotlist;
|
||||||
@@ -9,7 +10,7 @@ mod qwen;
|
|||||||
mod qwen_mtplx_tests;
|
mod qwen_mtplx_tests;
|
||||||
// Whole-turn BF16 backend; never mix its state with legacy FP32 scratch.
|
// Whole-turn BF16 backend; never mix its state with legacy FP32 scratch.
|
||||||
mod qwen_mtplx;
|
mod qwen_mtplx;
|
||||||
mod vision;
|
pub(super) mod vision;
|
||||||
pub(super) use vision::VisionEmbedding;
|
pub(super) use vision::VisionEmbedding;
|
||||||
|
|
||||||
use glm::GlmExecutor;
|
use glm::GlmExecutor;
|
||||||
@@ -108,7 +109,7 @@ fn environment_present(name: &CStr) -> bool {
|
|||||||
!unsafe { getenv(name.as_ptr()) }.is_null()
|
!unsafe { getenv(name.as_ptr()) }.is_null()
|
||||||
}
|
}
|
||||||
|
|
||||||
const SOURCES: [(&str, &str); 25] = [
|
const SOURCES: [(&str, &str); 26] = [
|
||||||
("DS4_METAL_FLASH_ATTN_SOURCE", "flash_attn.metal"),
|
("DS4_METAL_FLASH_ATTN_SOURCE", "flash_attn.metal"),
|
||||||
("DS4_METAL_DENSE_SOURCE", "dense.metal"),
|
("DS4_METAL_DENSE_SOURCE", "dense.metal"),
|
||||||
("DS4_METAL_MOE_SOURCE", "moe.metal"),
|
("DS4_METAL_MOE_SOURCE", "moe.metal"),
|
||||||
@@ -130,6 +131,10 @@ const SOURCES: [(&str, &str); 25] = [
|
|||||||
("DS4_METAL_SET_ROWS_SOURCE", "set_rows.metal"),
|
("DS4_METAL_SET_ROWS_SOURCE", "set_rows.metal"),
|
||||||
("DS4_METAL_GLM53_BF16_SOURCE", "glm53_bf16.metal"),
|
("DS4_METAL_GLM53_BF16_SOURCE", "glm53_bf16.metal"),
|
||||||
("DS4_METAL_GLM53_VISION_SOURCE", "glm53_vision.metal"),
|
("DS4_METAL_GLM53_VISION_SOURCE", "glm53_vision.metal"),
|
||||||
|
(
|
||||||
|
"DS4_METAL_DEEPSEEK4_VISION_SOURCE",
|
||||||
|
"deepseek4_vision.metal",
|
||||||
|
),
|
||||||
("DS4_METAL_GLM53_KDA_SOURCE", "glm53_kda.metal"),
|
("DS4_METAL_GLM53_KDA_SOURCE", "glm53_kda.metal"),
|
||||||
("DS4_METAL_QWEN38_SOURCE", "qwen38.metal"),
|
("DS4_METAL_QWEN38_SOURCE", "qwen38.metal"),
|
||||||
("DS4_METAL_MTPLX_QWEN_SOURCE", "mtplx_qwen.metal"),
|
("DS4_METAL_MTPLX_QWEN_SOURCE", "mtplx_qwen.metal"),
|
||||||
@@ -2409,7 +2414,7 @@ impl SsdPlan {
|
|||||||
}
|
}
|
||||||
let mut loaded = 0_u32;
|
let mut loaded = 0_u32;
|
||||||
let hotlist = match model.shape.model {
|
let hotlist = match model.shape.model {
|
||||||
ModelChoice::DeepSeekV4Flash0731 => hotlist::FLASH,
|
ModelChoice::DeepSeekV4Flash0731 | ModelChoice::DeepSeekV4FlashVision => hotlist::FLASH,
|
||||||
ModelChoice::DeepSeekV4Pro => hotlist::PRO,
|
ModelChoice::DeepSeekV4Pro => hotlist::PRO,
|
||||||
ModelChoice::Glm52 | ModelChoice::Glm53Flash => {
|
ModelChoice::Glm52 | ModelChoice::Glm53Flash => {
|
||||||
unreachable!("GLM uses its dedicated executor")
|
unreachable!("GLM uses its dedicated executor")
|
||||||
@@ -2766,7 +2771,7 @@ fn resident_deepseek_admission_bytes(
|
|||||||
prefill: u32,
|
prefill: u32,
|
||||||
) -> Result<u64, String> {
|
) -> Result<u64, String> {
|
||||||
resident_deepseek_admission_for_weights(
|
resident_deepseek_admission_for_weights(
|
||||||
model.main.len() - model.main.data_offset(),
|
model.main.len() - model.main.data_offset() + model.vision.as_ref().map_or(0, Gguf::len),
|
||||||
model.shape,
|
model.shape,
|
||||||
context,
|
context,
|
||||||
prefill,
|
prefill,
|
||||||
@@ -3228,6 +3233,10 @@ pub(super) struct ExecutionStats {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct DeepSeekExecutor {
|
pub(super) struct DeepSeekExecutor {
|
||||||
|
vision: Option<deepseek_vision::Encoder>,
|
||||||
|
visual_bias: Vec<Buffer>,
|
||||||
|
vision_overlays: Vec<deepseek_vision::Overlay>,
|
||||||
|
vision_identities: Vec<deepseek_vision::Identity>,
|
||||||
weights: Weights,
|
weights: Weights,
|
||||||
session: Session,
|
session: Session,
|
||||||
dspark: Option<Dspark>,
|
dspark: Option<Dspark>,
|
||||||
@@ -3255,6 +3264,7 @@ pub(super) struct DeepSeekExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct DeepSeekResidentState {
|
pub(super) struct DeepSeekResidentState {
|
||||||
|
vision_identities: Vec<deepseek_vision::Identity>,
|
||||||
session: Session,
|
session: Session,
|
||||||
dspark: Option<Dspark>,
|
dspark: Option<Dspark>,
|
||||||
logits: Vec<f32>,
|
logits: Vec<f32>,
|
||||||
@@ -3314,10 +3324,34 @@ impl DeepSeekExecutor {
|
|||||||
.map(|spans| (spans.ranges.as_slice(), spans.max_tensor_bytes));
|
.map(|spans| (spans.ranges.as_slice(), spans.max_tensor_bytes));
|
||||||
let admission = if let Some(plan) = &ssd_plan {
|
let admission = if let Some(plan) = &ssd_plan {
|
||||||
plan.admission_bytes
|
plan.admission_bytes
|
||||||
|
.checked_add(model.vision.as_ref().map_or(0, Gguf::len))
|
||||||
|
.ok_or("DeepSeek runtime memory size overflow")?
|
||||||
} else {
|
} else {
|
||||||
resident_deepseek_admission_bytes(&model, context, prefill_chunk)?
|
resident_deepseek_admission_bytes(&model, context, prefill_chunk)?
|
||||||
};
|
};
|
||||||
let context_handle = Context::open(&model, quality, ssd.enabled, admission, spans, false)?;
|
let context_handle = Context::open(
|
||||||
|
&model,
|
||||||
|
quality,
|
||||||
|
ssd.enabled,
|
||||||
|
admission,
|
||||||
|
spans,
|
||||||
|
speculative.keep_vision_loaded,
|
||||||
|
)?;
|
||||||
|
let vision = model
|
||||||
|
.vision
|
||||||
|
.as_ref()
|
||||||
|
.map(deepseek_vision::Encoder::bind)
|
||||||
|
.transpose()?;
|
||||||
|
let visual_bias = model
|
||||||
|
.vision
|
||||||
|
.as_ref()
|
||||||
|
.map(|v| {
|
||||||
|
(0..43)
|
||||||
|
.map(|i| deepseek_vision::buffer(v, &format!("layers.{i}.ffn.gate.bias_vl")))
|
||||||
|
.collect::<Result<Vec<_>, String>>()
|
||||||
|
})
|
||||||
|
.transpose()?
|
||||||
|
.unwrap_or_default();
|
||||||
let mxfp4_decode_fast_lookup =
|
let mxfp4_decode_fast_lookup =
|
||||||
mxfp4_decode_fast_lookup_allowed(&model, &weights, quality, ssd.enabled);
|
mxfp4_decode_fast_lookup_allowed(&model, &weights, quality, ssd.enabled);
|
||||||
let steering = Steering::load(&model, steering)?;
|
let steering = Steering::load(&model, steering)?;
|
||||||
@@ -3369,6 +3403,10 @@ impl DeepSeekExecutor {
|
|||||||
model.shape.experts_used,
|
model.shape.experts_used,
|
||||||
)?;
|
)?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
vision,
|
||||||
|
visual_bias,
|
||||||
|
vision_overlays: Vec::new(),
|
||||||
|
vision_identities: Vec::new(),
|
||||||
weights,
|
weights,
|
||||||
session,
|
session,
|
||||||
dspark,
|
dspark,
|
||||||
@@ -4036,6 +4074,9 @@ impl DeepSeekExecutor {
|
|||||||
&& !self.quality
|
&& !self.quality
|
||||||
&& !tokens.is_empty()
|
&& !tokens.is_empty()
|
||||||
&& tokens.len() <= streaming_decode_cap
|
&& tokens.len() <= streaming_decode_cap
|
||||||
|
&& !tokens
|
||||||
|
.iter()
|
||||||
|
.any(|token| *token >= self.model.shape.vocab as i32)
|
||||||
{
|
{
|
||||||
for (index, &token) in tokens.iter().enumerate() {
|
for (index, &token) in tokens.iter().enumerate() {
|
||||||
if !progress(self.session.position) {
|
if !progress(self.session.position) {
|
||||||
@@ -4045,7 +4086,12 @@ impl DeepSeekExecutor {
|
|||||||
}
|
}
|
||||||
return Ok(tokens.len());
|
return Ok(tokens.len());
|
||||||
}
|
}
|
||||||
if tokens.len() < 4 && self.session.position != 0 {
|
if tokens.len() < 4
|
||||||
|
&& self.session.position != 0
|
||||||
|
&& !tokens
|
||||||
|
.iter()
|
||||||
|
.any(|token| *token >= self.model.shape.vocab as i32)
|
||||||
|
{
|
||||||
for (index, &token) in tokens.iter().enumerate() {
|
for (index, &token) in tokens.iter().enumerate() {
|
||||||
self.eval(token)?;
|
self.eval(token)?;
|
||||||
if !progress(self.session.position) {
|
if !progress(self.session.position) {
|
||||||
@@ -4066,6 +4112,7 @@ impl DeepSeekExecutor {
|
|||||||
self.session.context
|
self.session.context
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
let resumed = self.session.position != 0;
|
||||||
let mut consumed = 0_usize;
|
let mut consumed = 0_usize;
|
||||||
while consumed < tokens.len() {
|
while consumed < tokens.len() {
|
||||||
if !progress(self.session.position) {
|
if !progress(self.session.position) {
|
||||||
@@ -4073,14 +4120,29 @@ impl DeepSeekExecutor {
|
|||||||
}
|
}
|
||||||
let pos = self.session.position;
|
let pos = self.session.position;
|
||||||
let mut cap = self.session.prefill_cap;
|
let mut cap = self.session.prefill_cap;
|
||||||
if pos != 0 {
|
if pos != 0 && (self.vision.is_none() || resumed) {
|
||||||
cap = cap.min(self.session.raw_cap);
|
cap = cap.min(self.session.raw_cap);
|
||||||
let offset = pos % self.session.prefill_cap;
|
let offset = pos % self.session.prefill_cap;
|
||||||
if offset != 0 {
|
if offset != 0 {
|
||||||
cap = cap.min(self.session.prefill_cap - offset);
|
cap = cap.min(self.session.prefill_cap - offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let rows = (tokens.len() - consumed).min(cap as usize);
|
let rows = if self.vision.is_some() {
|
||||||
|
if tokens[consumed] >= self.model.shape.vocab as i32 {
|
||||||
|
cap = if resumed {
|
||||||
|
self.session.prefill_cap.min(self.session.raw_cap)
|
||||||
|
} else {
|
||||||
|
self.session.prefill_cap
|
||||||
|
};
|
||||||
|
}
|
||||||
|
crate::engine::deepseek_vision::prefill_chunk(
|
||||||
|
&tokens[consumed..],
|
||||||
|
self.model.shape.vocab as i32,
|
||||||
|
cap,
|
||||||
|
)?
|
||||||
|
} else {
|
||||||
|
(tokens.len() - consumed).min(cap as usize)
|
||||||
|
};
|
||||||
self.eval_batch(&tokens[consumed..consumed + rows])?;
|
self.eval_batch(&tokens[consumed..consumed + rows])?;
|
||||||
consumed += rows;
|
consumed += rows;
|
||||||
if !progress(self.session.position) {
|
if !progress(self.session.position) {
|
||||||
@@ -4107,12 +4169,29 @@ impl DeepSeekExecutor {
|
|||||||
if rows == 0 || rows > self.session.prefill_cap {
|
if rows == 0 || rows > self.session.prefill_cap {
|
||||||
return Err("prefill batch exceeds the configured prefill workspace".into());
|
return Err("prefill batch exceeds the configured prefill workspace".into());
|
||||||
}
|
}
|
||||||
if tokens
|
let visual_tokens =
|
||||||
.iter()
|
self.vision.is_some() && tokens.iter().any(|t| *t >= self.model.shape.vocab as i32);
|
||||||
.any(|token| *token < 0 || *token as u64 >= self.model.shape.vocab)
|
if visual_tokens {
|
||||||
{
|
crate::engine::deepseek_vision::spans(tokens, self.model.shape.vocab as i32)?;
|
||||||
|
}
|
||||||
|
if tokens.iter().any(|token| {
|
||||||
|
*token < 0
|
||||||
|
|| *token as u64
|
||||||
|
>= self.model.shape.vocab + if self.vision.is_some() { 5 } else { 0 }
|
||||||
|
}) {
|
||||||
return Err("prefill contains a token outside the vocabulary".into());
|
return Err("prefill contains a token outside the vocabulary".into());
|
||||||
}
|
}
|
||||||
|
if visual_tokens
|
||||||
|
&& tokens.iter().enumerate().any(|(row, t)| {
|
||||||
|
*t >= self.model.shape.vocab as i32
|
||||||
|
&& !self.vision_overlays.iter().any(|o| {
|
||||||
|
let pos = self.session.position + row as u32;
|
||||||
|
pos >= o.identity.start && pos < o.identity.start + o.identity.count
|
||||||
|
})
|
||||||
|
})
|
||||||
|
{
|
||||||
|
return Err("DeepSeek image token has no embedding overlay".into());
|
||||||
|
}
|
||||||
let prepend_seed = collect_tops
|
let prepend_seed = collect_tops
|
||||||
&& self.session.position > 0
|
&& self.session.position > 0
|
||||||
&& rows < self.session.prefill_cap
|
&& rows < self.session.prefill_cap
|
||||||
@@ -4184,12 +4263,24 @@ impl DeepSeekExecutor {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
let pipelined_verifier = collect_tops && self.ssd.is_none() && self.profile.is_none();
|
let pipelined_verifier = collect_tops && self.ssd.is_none() && self.profile.is_none();
|
||||||
|
let embedding_tokens = if visual_tokens {
|
||||||
|
let buffer = Buffer::bytes(u64::from(rows) * 4)?;
|
||||||
|
buffer.write_i32(
|
||||||
|
&tokens
|
||||||
|
.iter()
|
||||||
|
.map(|t| if *t >= shape.vocab as i32 { 0 } else { *t })
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
)?;
|
||||||
|
Some(buffer)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
let mut commands = Some(Commands::begin()?);
|
let mut commands = Some(Commands::begin()?);
|
||||||
call(
|
call(
|
||||||
unsafe {
|
unsafe {
|
||||||
ds4_gpu_embed_tokens_hc_tensor(
|
ds4_gpu_embed_tokens_hc_tensor(
|
||||||
batch.current_hc.raw(),
|
batch.current_hc.raw(),
|
||||||
batch.tokens.raw(),
|
embedding_tokens.as_ref().unwrap_or(&batch.tokens).raw(),
|
||||||
map,
|
map,
|
||||||
size,
|
size,
|
||||||
self.weights.token_embedding.offset,
|
self.weights.token_embedding.offset,
|
||||||
@@ -4201,6 +4292,29 @@ impl DeepSeekExecutor {
|
|||||||
},
|
},
|
||||||
"batch token embedding",
|
"batch token embedding",
|
||||||
)?;
|
)?;
|
||||||
|
if visual_tokens {
|
||||||
|
for overlay in &self.vision_overlays {
|
||||||
|
let start = overlay.identity.start.max(pos);
|
||||||
|
let end = (overlay.identity.start + overlay.identity.count).min(pos + rows);
|
||||||
|
if start < end {
|
||||||
|
call(
|
||||||
|
unsafe {
|
||||||
|
ds4_gpu_glm53_scatter_image_hc(
|
||||||
|
batch.current_hc.raw(),
|
||||||
|
overlay.embedding.raw(),
|
||||||
|
start - pos,
|
||||||
|
start - overlay.identity.start,
|
||||||
|
end - start,
|
||||||
|
rows,
|
||||||
|
shape.embd as u32,
|
||||||
|
shape.hc as u32,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"DeepSeek image embedding overlay",
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if !pipelined_verifier {
|
if !pipelined_verifier {
|
||||||
commands
|
commands
|
||||||
.take()
|
.take()
|
||||||
@@ -4261,6 +4375,12 @@ impl DeepSeekExecutor {
|
|||||||
self.session.raw_cap,
|
self.session.raw_cap,
|
||||||
self.steering.as_ref(),
|
self.steering.as_ref(),
|
||||||
collect_tops.then_some(prefixes.as_mut_slice()),
|
collect_tops.then_some(prefixes.as_mut_slice()),
|
||||||
|
visual_tokens.then_some(tokens),
|
||||||
|
if visual_tokens {
|
||||||
|
self.visual_bias.get(index)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
)?;
|
)?;
|
||||||
if let Some(profile) = &mut self.profile {
|
if let Some(profile) = &mut self.profile {
|
||||||
profile.record(
|
profile.record(
|
||||||
@@ -4401,6 +4521,14 @@ impl DeepSeekExecutor {
|
|||||||
};
|
};
|
||||||
self.session.position += rows;
|
self.session.position += rows;
|
||||||
self.tokens.extend_from_slice(tokens);
|
self.tokens.extend_from_slice(tokens);
|
||||||
|
if !self.vision_overlays.is_empty() {
|
||||||
|
self.vision_identities = self
|
||||||
|
.vision_overlays
|
||||||
|
.iter()
|
||||||
|
.filter(|o| o.identity.start + o.identity.count <= self.session.position)
|
||||||
|
.map(|o| o.identity.clone())
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
if let Some(dspark) = &mut self.dspark {
|
if let Some(dspark) = &mut self.dspark {
|
||||||
// Keep the captured target rows until DS4's next proposal needs them.
|
// Keep the captured target rows until DS4's next proposal needs them.
|
||||||
dspark.pending_seed = Some((
|
dspark.pending_seed = Some((
|
||||||
@@ -4499,6 +4627,8 @@ impl DeepSeekExecutor {
|
|||||||
dspark.reset_cache();
|
dspark.reset_cache();
|
||||||
}
|
}
|
||||||
self.tokens.clear();
|
self.tokens.clear();
|
||||||
|
self.vision_identities.clear();
|
||||||
|
self.vision_overlays.clear();
|
||||||
self.checkpoint_tag = [0; 32];
|
self.checkpoint_tag = [0; 32];
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -4520,6 +4650,7 @@ impl DeepSeekExecutor {
|
|||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
Ok(DeepSeekResidentState {
|
Ok(DeepSeekResidentState {
|
||||||
|
vision_identities: Vec::new(),
|
||||||
session,
|
session,
|
||||||
dspark,
|
dspark,
|
||||||
logits: vec![0.0; self.model.shape.vocab as usize],
|
logits: vec![0.0; self.model.shape.vocab as usize],
|
||||||
@@ -4535,6 +4666,8 @@ impl DeepSeekExecutor {
|
|||||||
let mut incoming = state
|
let mut incoming = state
|
||||||
.take()
|
.take()
|
||||||
.map_or_else(|| self.blank_resident_state(), Ok)?;
|
.map_or_else(|| self.blank_resident_state(), Ok)?;
|
||||||
|
std::mem::swap(&mut self.vision_identities, &mut incoming.vision_identities);
|
||||||
|
self.vision_overlays.clear();
|
||||||
std::mem::swap(&mut self.session, &mut incoming.session);
|
std::mem::swap(&mut self.session, &mut incoming.session);
|
||||||
std::mem::swap(&mut self.dspark, &mut incoming.dspark);
|
std::mem::swap(&mut self.dspark, &mut incoming.dspark);
|
||||||
std::mem::swap(&mut self.logits, &mut incoming.logits);
|
std::mem::swap(&mut self.logits, &mut incoming.logits);
|
||||||
@@ -4549,7 +4682,9 @@ impl DeepSeekExecutor {
|
|||||||
dspark.begin_request();
|
dspark.begin_request();
|
||||||
}
|
}
|
||||||
if !tokens.starts_with(&self.tokens) {
|
if !tokens.starts_with(&self.tokens) {
|
||||||
|
let overlays = std::mem::take(&mut self.vision_overlays);
|
||||||
self.reset()?;
|
self.reset()?;
|
||||||
|
self.vision_overlays = overlays;
|
||||||
}
|
}
|
||||||
Ok(self.tokens.len())
|
Ok(self.tokens.len())
|
||||||
}
|
}
|
||||||
@@ -4900,8 +5035,9 @@ impl Executor {
|
|||||||
) -> Result<Vec<vision::VisionEmbedding>, String> {
|
) -> Result<Vec<vision::VisionEmbedding>, String> {
|
||||||
match self {
|
match self {
|
||||||
Self::Glm(executor) => executor.encode_visions(encoded),
|
Self::Glm(executor) => executor.encode_visions(encoded),
|
||||||
Self::DeepSeek(_) | Self::Qwen(_) | Self::QwenMtplx(_) => {
|
Self::DeepSeek(executor) => executor.encode_visions(encoded),
|
||||||
Err("vision input requires GLM 5.3 Flash".into())
|
Self::Qwen(_) | Self::QwenMtplx(_) => {
|
||||||
|
Err("vision input requires a supported vision model".into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4912,10 +5048,10 @@ impl Executor {
|
|||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
match self {
|
match self {
|
||||||
Self::Glm(executor) => executor.set_vision_overlays(overlays),
|
Self::Glm(executor) => executor.set_vision_overlays(overlays),
|
||||||
Self::DeepSeek(_) if overlays.is_empty() => Ok(()),
|
Self::DeepSeek(executor) => executor.set_vision_overlays(overlays),
|
||||||
Self::Qwen(_) | Self::QwenMtplx(_) if overlays.is_empty() => Ok(()),
|
Self::Qwen(_) | Self::QwenMtplx(_) if overlays.is_empty() => Ok(()),
|
||||||
Self::DeepSeek(_) | Self::Qwen(_) | Self::QwenMtplx(_) => {
|
Self::Qwen(_) | Self::QwenMtplx(_) => {
|
||||||
Err("vision input requires GLM 5.3 Flash".into())
|
Err("vision input requires a supported vision model".into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5574,6 +5710,8 @@ fn encode_batch_layer(
|
|||||||
raw_cap: u32,
|
raw_cap: u32,
|
||||||
steering: Option<&Steering>,
|
steering: Option<&Steering>,
|
||||||
mut prefixes: Option<&mut [SpecPrefixFrontier]>,
|
mut prefixes: Option<&mut [SpecPrefixFrontier]>,
|
||||||
|
visual_tokens: Option<&[i32]>,
|
||||||
|
visual_bias: Option<&Buffer>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let hc_dim = shape.hc * shape.embd;
|
let hc_dim = shape.hc * shape.embd;
|
||||||
let mix_hc = 2 * shape.hc + shape.hc * shape.hc;
|
let mix_hc = 2 * shape.hc + shape.hc * shape.hc;
|
||||||
@@ -5944,7 +6082,21 @@ fn encode_batch_layer(
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if pos == 0 && compressed_rows == 0 {
|
if let Some(tokens) = visual_tokens {
|
||||||
|
deepseek_vision::attention(
|
||||||
|
s,
|
||||||
|
state,
|
||||||
|
w,
|
||||||
|
shape,
|
||||||
|
map,
|
||||||
|
size,
|
||||||
|
tokens,
|
||||||
|
pos,
|
||||||
|
raw_cap,
|
||||||
|
compressed_rows,
|
||||||
|
ratio,
|
||||||
|
)?;
|
||||||
|
} else if pos == 0 && compressed_rows == 0 {
|
||||||
call(
|
call(
|
||||||
unsafe {
|
unsafe {
|
||||||
ds4_gpu_attention_prefill_raw_heads_tensor(
|
ds4_gpu_attention_prefill_raw_heads_tensor(
|
||||||
@@ -6324,6 +6476,35 @@ fn encode_batch_layer(
|
|||||||
map,
|
map,
|
||||||
size,
|
size,
|
||||||
)?;
|
)?;
|
||||||
|
if let Some(bias) = visual_bias {
|
||||||
|
call(
|
||||||
|
unsafe {
|
||||||
|
ds4_gpu_router_select_visual_batch_tensor(
|
||||||
|
s.router_selected.raw(),
|
||||||
|
s.router_weights.raw(),
|
||||||
|
s.router_probs.raw(),
|
||||||
|
map,
|
||||||
|
size,
|
||||||
|
w.router_bias.map_or(0, |value| value.offset),
|
||||||
|
w.router_hash.map_or(0, |value| value.offset),
|
||||||
|
w.router_hash.map_or(0, |value| value.dims[1] as u32),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
w.router_bias.is_some(),
|
||||||
|
w.router_hash.is_some(),
|
||||||
|
s.router_logits.raw(),
|
||||||
|
s.tokens.raw(),
|
||||||
|
shape.experts as u32,
|
||||||
|
shape.experts_used as u32,
|
||||||
|
shape.expert_weight_scale,
|
||||||
|
rows,
|
||||||
|
bias.raw(),
|
||||||
|
shape.vocab as u32,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"batch expert routing",
|
||||||
|
)?;
|
||||||
|
} else {
|
||||||
call(
|
call(
|
||||||
unsafe {
|
unsafe {
|
||||||
ds4_gpu_router_select_batch_tensor(
|
ds4_gpu_router_select_batch_tensor(
|
||||||
@@ -6349,6 +6530,7 @@ fn encode_batch_layer(
|
|||||||
},
|
},
|
||||||
"batch expert routing",
|
"batch expert routing",
|
||||||
)?;
|
)?;
|
||||||
|
}
|
||||||
let gate_row = w.expert_gate.bytes / (w.expert_gate.dims[1] * w.expert_gate.dims[2]);
|
let gate_row = w.expert_gate.bytes / (w.expert_gate.dims[1] * w.expert_gate.dims[2]);
|
||||||
let down_row = w.expert_down.bytes / (w.expert_down.dims[1] * w.expert_down.dims[2]);
|
let down_row = w.expert_down.bytes / (w.expert_down.dims[1] * w.expert_down.dims[2]);
|
||||||
let mut mid_f16 = false;
|
let mut mid_f16 = false;
|
||||||
@@ -8141,15 +8323,22 @@ fn update_compressor_stage(
|
|||||||
|
|
||||||
fn compression_ratio(shape: super::Shape, layer: u32) -> u32 {
|
fn compression_ratio(shape: super::Shape, layer: u32) -> u32 {
|
||||||
match shape.model {
|
match shape.model {
|
||||||
crate::model::ModelChoice::DeepSeekV4Flash0731 if layer < 2 => 0,
|
crate::model::ModelChoice::DeepSeekV4FlashVision
|
||||||
|
| crate::model::ModelChoice::DeepSeekV4Flash0731
|
||||||
|
if layer < 2 =>
|
||||||
|
{
|
||||||
|
0
|
||||||
|
}
|
||||||
crate::model::ModelChoice::DeepSeekV4Pro if layer < 2 => 128,
|
crate::model::ModelChoice::DeepSeekV4Pro if layer < 2 => 128,
|
||||||
crate::model::ModelChoice::DeepSeekV4Flash0731
|
crate::model::ModelChoice::DeepSeekV4FlashVision
|
||||||
|
| crate::model::ModelChoice::DeepSeekV4Flash0731
|
||||||
| crate::model::ModelChoice::DeepSeekV4Pro
|
| crate::model::ModelChoice::DeepSeekV4Pro
|
||||||
if layer.is_multiple_of(2) =>
|
if layer.is_multiple_of(2) =>
|
||||||
{
|
{
|
||||||
4
|
4
|
||||||
}
|
}
|
||||||
crate::model::ModelChoice::DeepSeekV4Flash0731
|
crate::model::ModelChoice::DeepSeekV4FlashVision
|
||||||
|
| crate::model::ModelChoice::DeepSeekV4Flash0731
|
||||||
| crate::model::ModelChoice::DeepSeekV4Pro => 128,
|
| crate::model::ModelChoice::DeepSeekV4Pro => 128,
|
||||||
crate::model::ModelChoice::Glm52
|
crate::model::ModelChoice::Glm52
|
||||||
| crate::model::ModelChoice::Glm53Flash
|
| crate::model::ModelChoice::Glm53Flash
|
||||||
|
|||||||
@@ -97,6 +97,15 @@ impl DeepSeekExecutor {
|
|||||||
for &token in &self.tokens {
|
for &token in &self.tokens {
|
||||||
write_u32(file, token as u32)?;
|
write_u32(file, token as u32)?;
|
||||||
}
|
}
|
||||||
|
if shape.model == ModelChoice::DeepSeekV4FlashVision {
|
||||||
|
write_u32(file, self.vision_identities.len() as u32)?;
|
||||||
|
for image in &self.vision_identities {
|
||||||
|
write_u32(file, image.start)?;
|
||||||
|
write_u32(file, image.count)?;
|
||||||
|
file.write_all(&image.fingerprint)
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
for &logit in &self.logits {
|
for &logit in &self.logits {
|
||||||
write_u32(file, logit.to_bits())?;
|
write_u32(file, logit.to_bits())?;
|
||||||
}
|
}
|
||||||
@@ -212,11 +221,44 @@ impl DeepSeekExecutor {
|
|||||||
let mut tokens = Vec::with_capacity(token_count as usize);
|
let mut tokens = Vec::with_capacity(token_count as usize);
|
||||||
for _ in 0..token_count {
|
for _ in 0..token_count {
|
||||||
let token = read_u32(file)?;
|
let token = read_u32(file)?;
|
||||||
if u64::from(token) >= shape.vocab {
|
if u64::from(token)
|
||||||
|
>= shape.vocab
|
||||||
|
+ if shape.model == ModelChoice::DeepSeekV4FlashVision {
|
||||||
|
5
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
{
|
||||||
return Err("KV checkpoint contains an invalid token".into());
|
return Err("KV checkpoint contains an invalid token".into());
|
||||||
}
|
}
|
||||||
tokens.push(token as i32);
|
tokens.push(token as i32);
|
||||||
}
|
}
|
||||||
|
let mut vision_identities = Vec::new();
|
||||||
|
if shape.model == ModelChoice::DeepSeekV4FlashVision {
|
||||||
|
let spans = crate::engine::deepseek_vision::spans(&tokens, shape.vocab as i32)?;
|
||||||
|
let count = read_u32(file)?;
|
||||||
|
if count as usize != spans.len() || count > 16 {
|
||||||
|
return Err("KV checkpoint image count is invalid".into());
|
||||||
|
}
|
||||||
|
for span in spans {
|
||||||
|
let start = read_u32(file)?;
|
||||||
|
let count = read_u32(file)?;
|
||||||
|
if start as usize != span.block
|
||||||
|
|| count as usize != span.end + 1 - span.block
|
||||||
|
|| count > 384
|
||||||
|
{
|
||||||
|
return Err("KV checkpoint image layout is invalid".into());
|
||||||
|
}
|
||||||
|
let mut fingerprint = [0; 32];
|
||||||
|
file.read_exact(&mut fingerprint)
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
vision_identities.push(deepseek_vision::Identity {
|
||||||
|
start,
|
||||||
|
count,
|
||||||
|
fingerprint,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
let mut logits = Vec::with_capacity(shape.vocab as usize);
|
let mut logits = Vec::with_capacity(shape.vocab as usize);
|
||||||
for _ in 0..shape.vocab {
|
for _ in 0..shape.vocab {
|
||||||
logits.push(f32::from_bits(read_u32(file)?));
|
logits.push(f32::from_bits(read_u32(file)?));
|
||||||
@@ -299,6 +341,8 @@ impl DeepSeekExecutor {
|
|||||||
}
|
}
|
||||||
self.session.position = token_count;
|
self.session.position = token_count;
|
||||||
self.tokens = tokens;
|
self.tokens = tokens;
|
||||||
|
self.vision_identities = vision_identities;
|
||||||
|
self.vision_overlays.clear();
|
||||||
self.logits = logits;
|
self.logits = logits;
|
||||||
self.checkpoint_tag = checkpoint_tag;
|
self.checkpoint_tag = checkpoint_tag;
|
||||||
if let Some(dspark) = &mut self.dspark {
|
if let Some(dspark) = &mut self.dspark {
|
||||||
|
|||||||
@@ -0,0 +1,617 @@
|
|||||||
|
//! Native Rust execution of DS4's Vision-Exp encoder, using the shared BF16
|
||||||
|
//! matmul and attention primitives and the checkpoint's exact rounding points.
|
||||||
|
use super::{Buffer, Commands, Gguf, call, gpu::*};
|
||||||
|
use crate::engine::deepseek_vision::{self as image, END, IMAGE, NEWLINE, PAD, START};
|
||||||
|
use std::ffi::CStr;
|
||||||
|
|
||||||
|
struct Layer {
|
||||||
|
norm1: Buffer,
|
||||||
|
qkv: u64,
|
||||||
|
qkv_bias: Buffer,
|
||||||
|
out: u64,
|
||||||
|
out_bias: Buffer,
|
||||||
|
norm2: Buffer,
|
||||||
|
up: u64,
|
||||||
|
down: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct Encoder {
|
||||||
|
patch: u64,
|
||||||
|
patch_bias: Buffer,
|
||||||
|
norm: Buffer,
|
||||||
|
align1: u64,
|
||||||
|
align1_bias: Buffer,
|
||||||
|
align2: u64,
|
||||||
|
align2_bias: Buffer,
|
||||||
|
layers: Vec<Layer>,
|
||||||
|
sentinels: [Vec<f32>; 5],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn buffer(model: &Gguf, name: &str) -> Result<Buffer, String> {
|
||||||
|
let bytes = model.tensor_data(name)?;
|
||||||
|
let result = Buffer::bytes(bytes.len() as u64)?;
|
||||||
|
result.write(0, bytes)?;
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn dispatch<T>(
|
||||||
|
kernel: &CStr,
|
||||||
|
args: &T,
|
||||||
|
buffers: &[&Buffer],
|
||||||
|
grid: [u32; 3],
|
||||||
|
threads: [u32; 3],
|
||||||
|
individual: bool,
|
||||||
|
shared: u32,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let mut bindings = vec![MetalBinding {
|
||||||
|
tensor: std::ptr::null(),
|
||||||
|
bytes: (args as *const T).cast(),
|
||||||
|
size: size_of::<T>() as u32,
|
||||||
|
index: 0,
|
||||||
|
offset: 0,
|
||||||
|
}];
|
||||||
|
bindings.extend(buffers.iter().enumerate().map(|(i, b)| MetalBinding {
|
||||||
|
tensor: b.raw(),
|
||||||
|
bytes: std::ptr::null(),
|
||||||
|
size: 0,
|
||||||
|
index: i as u32 + 1,
|
||||||
|
offset: 0,
|
||||||
|
}));
|
||||||
|
call(
|
||||||
|
unsafe {
|
||||||
|
ds4_gpu_deepseek_vision_dispatch(
|
||||||
|
kernel.as_ptr(),
|
||||||
|
bindings.as_ptr(),
|
||||||
|
bindings.len() as u32,
|
||||||
|
grid.as_ptr(),
|
||||||
|
threads.as_ptr(),
|
||||||
|
u32::from(individual),
|
||||||
|
shared,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"DeepSeek vision kernel",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rows(kernel: &CStr, width: u32, rows: u32, buffers: &[&Buffer]) -> Result<(), String> {
|
||||||
|
dispatch(
|
||||||
|
kernel,
|
||||||
|
&[width, rows],
|
||||||
|
buffers,
|
||||||
|
[width, rows, 1],
|
||||||
|
[width.min(256), 1, 1],
|
||||||
|
true,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fn round(x: &Buffer, width: u32, n: u32) -> Result<(), String> {
|
||||||
|
rows(c"kernel_deepseek4_vision_round_bf16", width, n, &[x])
|
||||||
|
}
|
||||||
|
fn norm(out: &Buffer, x: &Buffer, weight: &Buffer, n: u32) -> Result<(), String> {
|
||||||
|
dispatch(
|
||||||
|
c"kernel_glm53_vision_rms_bf16",
|
||||||
|
&[1024, n, 1e-6_f32.to_bits()],
|
||||||
|
&[x, weight, out],
|
||||||
|
[n, 1, 1],
|
||||||
|
[256, 1, 1],
|
||||||
|
false,
|
||||||
|
32,
|
||||||
|
)?;
|
||||||
|
round(out, 1024, n)
|
||||||
|
}
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn matmul(
|
||||||
|
model: &Gguf,
|
||||||
|
out: &Buffer,
|
||||||
|
weight: u64,
|
||||||
|
input: &Buffer,
|
||||||
|
dim: u32,
|
||||||
|
width: u32,
|
||||||
|
n: u32,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
call(
|
||||||
|
unsafe {
|
||||||
|
ds4_gpu_glm53_matmul_bf16(
|
||||||
|
out.raw(),
|
||||||
|
model.map_ptr().cast(),
|
||||||
|
model.len(),
|
||||||
|
weight,
|
||||||
|
dim,
|
||||||
|
width,
|
||||||
|
input.raw(),
|
||||||
|
n,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"DeepSeek vision BF16 projection",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encoder {
|
||||||
|
pub(super) fn bind(model: &Gguf) -> Result<Self, String> {
|
||||||
|
let offset = |name: &str| model.tensor(name).map(|t| t.offset);
|
||||||
|
let mut layers = Vec::with_capacity(32);
|
||||||
|
for i in 0..32 {
|
||||||
|
let b = |suffix: &str| buffer(model, &format!("vision.blocks.{i}.{suffix}"));
|
||||||
|
let o = |suffix: &str| offset(&format!("vision.blocks.{i}.{suffix}"));
|
||||||
|
layers.push(Layer {
|
||||||
|
norm1: b("norm1.weight")?,
|
||||||
|
qkv: o("attn.wqkv.weight")?,
|
||||||
|
qkv_bias: b("attn.wqkv.bias")?,
|
||||||
|
out: o("attn.wo.weight")?,
|
||||||
|
out_bias: b("attn.wo.bias")?,
|
||||||
|
norm2: b("norm2.weight")?,
|
||||||
|
up: o("mlp.w1.weight")?,
|
||||||
|
down: o("mlp.w2.weight")?,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let mut sentinels = std::array::from_fn(|_| Vec::new());
|
||||||
|
for (i, name) in [
|
||||||
|
(START, "image_start"),
|
||||||
|
(PAD, "image_pad"),
|
||||||
|
(NEWLINE, "image_newline"),
|
||||||
|
(END, "image_end"),
|
||||||
|
] {
|
||||||
|
sentinels[i as usize] = model
|
||||||
|
.tensor_data(name)?
|
||||||
|
.chunks_exact(2)
|
||||||
|
.map(|b| f32::from_bits(u32::from(u16::from_le_bytes([b[0], b[1]])) << 16))
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
patch: offset("vision.patch_embed.proj.weight")?,
|
||||||
|
patch_bias: buffer(model, "vision.patch_embed.proj.bias")?,
|
||||||
|
norm: buffer(model, "vision.norm.weight")?,
|
||||||
|
align1: offset("aligner.w1.weight")?,
|
||||||
|
align1_bias: buffer(model, "aligner.w1.bias")?,
|
||||||
|
align2: offset("aligner.w2.weight")?,
|
||||||
|
align2_bias: buffer(model, "aligner.w2.bias")?,
|
||||||
|
layers,
|
||||||
|
sentinels,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn encode(
|
||||||
|
&self,
|
||||||
|
model: &Gguf,
|
||||||
|
encoded: &[u8],
|
||||||
|
) -> Result<super::VisionEmbedding, String> {
|
||||||
|
let p = image::decode(encoded)?;
|
||||||
|
let n = p.grid_height * p.grid_width;
|
||||||
|
let aligned = p.grid_height.div_ceil(3) * p.grid_width.div_ceil(3);
|
||||||
|
let alloc = |width: u64| Buffer::floats(u64::from(n) * width);
|
||||||
|
let patch = alloc(588)?;
|
||||||
|
patch.write_f32(&p.values)?;
|
||||||
|
let mut cur = alloc(1024)?;
|
||||||
|
let mut tmp = alloc(1024)?;
|
||||||
|
let qkv = alloc(3072)?;
|
||||||
|
let q = alloc(1024)?;
|
||||||
|
let k = alloc(1024)?;
|
||||||
|
let v = alloc(1024)?;
|
||||||
|
let attn = alloc(1024)?;
|
||||||
|
let up = alloc(5632)?;
|
||||||
|
let mid = alloc(2816)?;
|
||||||
|
let aligned_input = Buffer::floats(u64::from(aligned) * 9216)?;
|
||||||
|
let aa = Buffer::floats(u64::from(aligned) * 4096)?;
|
||||||
|
let ab = Buffer::floats(u64::from(aligned) * 4096)?;
|
||||||
|
let commands = Commands::begin()?;
|
||||||
|
matmul(model, &cur, self.patch, &patch, 588, 1024, n)?;
|
||||||
|
rows(
|
||||||
|
c"kernel_glm53_vision_add_bias",
|
||||||
|
1024,
|
||||||
|
n,
|
||||||
|
&[&cur, &self.patch_bias],
|
||||||
|
)?;
|
||||||
|
round(&cur, 1024, n)?;
|
||||||
|
for layer in &self.layers {
|
||||||
|
norm(&tmp, &cur, &layer.norm1, n)?;
|
||||||
|
matmul(model, &qkv, layer.qkv, &tmp, 1024, 3072, n)?;
|
||||||
|
dispatch(
|
||||||
|
c"kernel_deepseek4_vision_qkv_rope",
|
||||||
|
&[n, p.grid_width],
|
||||||
|
&[&qkv, &layer.qkv_bias, &q, &k, &v],
|
||||||
|
[n, 16, 1],
|
||||||
|
[32, 1, 1],
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
)?;
|
||||||
|
dispatch(
|
||||||
|
c"kernel_glm53_vision_attention",
|
||||||
|
&[n, 0.125_f32.to_bits()],
|
||||||
|
&[&q, &k, &v, &attn],
|
||||||
|
[n, 16, 1],
|
||||||
|
[32, 1, 1],
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
)?;
|
||||||
|
round(&attn, 1024, n)?;
|
||||||
|
matmul(model, &tmp, layer.out, &attn, 1024, 1024, n)?;
|
||||||
|
rows(
|
||||||
|
c"kernel_glm53_vision_bias_residual",
|
||||||
|
1024,
|
||||||
|
n,
|
||||||
|
&[&tmp, &layer.out_bias, &cur],
|
||||||
|
)?;
|
||||||
|
round(&tmp, 1024, n)?;
|
||||||
|
std::mem::swap(&mut cur, &mut tmp);
|
||||||
|
norm(&tmp, &cur, &layer.norm2, n)?;
|
||||||
|
matmul(model, &up, layer.up, &tmp, 1024, 5632, n)?;
|
||||||
|
round(&up, 5632, n)?;
|
||||||
|
rows(
|
||||||
|
c"kernel_deepseek4_vision_swiglu_split",
|
||||||
|
2816,
|
||||||
|
n,
|
||||||
|
&[&up, &mid],
|
||||||
|
)?;
|
||||||
|
matmul(model, &tmp, layer.down, &mid, 2816, 1024, n)?;
|
||||||
|
round(&tmp, 1024, n)?;
|
||||||
|
rows(
|
||||||
|
c"kernel_deepseek4_vision_add_residual",
|
||||||
|
1024,
|
||||||
|
n,
|
||||||
|
&[&tmp, &cur],
|
||||||
|
)?;
|
||||||
|
std::mem::swap(&mut cur, &mut tmp);
|
||||||
|
}
|
||||||
|
norm(&tmp, &cur, &self.norm, n)?;
|
||||||
|
dispatch(
|
||||||
|
c"kernel_deepseek4_vision_aligner_reorder",
|
||||||
|
&[p.grid_height, p.grid_width, aligned],
|
||||||
|
&[&tmp, &aligned_input],
|
||||||
|
[9216, aligned, 1],
|
||||||
|
[256, 1, 1],
|
||||||
|
true,
|
||||||
|
0,
|
||||||
|
)?;
|
||||||
|
matmul(model, &aa, self.align1, &aligned_input, 9216, 4096, aligned)?;
|
||||||
|
rows(
|
||||||
|
c"kernel_deepseek4_vision_gelu_bias",
|
||||||
|
4096,
|
||||||
|
aligned,
|
||||||
|
&[&aa, &self.align1_bias, &ab],
|
||||||
|
)?;
|
||||||
|
matmul(model, &aa, self.align2, &ab, 4096, 4096, aligned)?;
|
||||||
|
rows(
|
||||||
|
c"kernel_glm53_vision_add_bias",
|
||||||
|
4096,
|
||||||
|
aligned,
|
||||||
|
&[&aa, &self.align2_bias],
|
||||||
|
)?;
|
||||||
|
round(&aa, 4096, aligned)?;
|
||||||
|
commands.finish()?;
|
||||||
|
let mut values = vec![0.0; aligned as usize * 4096];
|
||||||
|
aa.read_f32(&mut values)?;
|
||||||
|
Ok(super::VisionEmbedding {
|
||||||
|
fingerprint: p.fingerprint,
|
||||||
|
values,
|
||||||
|
tokens: aligned,
|
||||||
|
width: p.width,
|
||||||
|
height: p.height,
|
||||||
|
content_width: p.content_width,
|
||||||
|
content_height: p.content_height,
|
||||||
|
grid_width: p.grid_width.div_ceil(3),
|
||||||
|
grid_height: p.grid_height.div_ceil(3),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn layout(
|
||||||
|
&self,
|
||||||
|
embedding: &mut super::VisionEmbedding,
|
||||||
|
position: u32,
|
||||||
|
vocab: i32,
|
||||||
|
) -> Result<Vec<i32>, String> {
|
||||||
|
let layout = image::layout(embedding.grid_height, embedding.grid_width, position)?;
|
||||||
|
if embedding.tokens as usize != layout.permutation.len()
|
||||||
|
|| embedding.values.len() != layout.permutation.len() * 4096
|
||||||
|
{
|
||||||
|
return Err("invalid DeepSeek natural image embedding".into());
|
||||||
|
}
|
||||||
|
let mut values = Vec::with_capacity(layout.types.len() * 4096);
|
||||||
|
let mut images = layout.permutation.iter();
|
||||||
|
for &kind in &layout.types {
|
||||||
|
if kind == IMAGE {
|
||||||
|
let row = *images.next().ok_or("invalid DeepSeek image permutation")?;
|
||||||
|
values.extend_from_slice(&embedding.values[row * 4096..(row + 1) * 4096]);
|
||||||
|
} else {
|
||||||
|
values.extend_from_slice(&self.sentinels[kind as usize]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
embedding.values = values;
|
||||||
|
embedding.tokens = layout.types.len() as u32;
|
||||||
|
embedding.grid_height = 0;
|
||||||
|
embedding.grid_width = 0;
|
||||||
|
Ok(layout.types.into_iter().map(|t| t + vocab).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub(super) struct Identity {
|
||||||
|
pub start: u32,
|
||||||
|
pub count: u32,
|
||||||
|
pub fingerprint: [u8; 32],
|
||||||
|
}
|
||||||
|
pub(super) struct Overlay {
|
||||||
|
pub identity: Identity,
|
||||||
|
pub embedding: Buffer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl super::DeepSeekExecutor {
|
||||||
|
pub(in crate::engine) fn cached_vision_positions(
|
||||||
|
&self,
|
||||||
|
images: &[super::VisionEmbedding],
|
||||||
|
) -> Option<Vec<(u32, u32)>> {
|
||||||
|
(images.len() == self.vision_identities.len()
|
||||||
|
&& images
|
||||||
|
.iter()
|
||||||
|
.zip(&self.vision_identities)
|
||||||
|
.all(|(image, old)| {
|
||||||
|
image.fingerprint == old.fingerprint
|
||||||
|
&& image::layout(image.grid_height, image.grid_width, old.start)
|
||||||
|
.is_ok_and(|layout| layout.types.len() == old.count as usize)
|
||||||
|
}))
|
||||||
|
.then(|| {
|
||||||
|
self.vision_identities
|
||||||
|
.iter()
|
||||||
|
.map(|old| (old.start, old.count))
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in crate::engine) fn layout_vision(
|
||||||
|
&self,
|
||||||
|
embedding: &mut super::VisionEmbedding,
|
||||||
|
position: u32,
|
||||||
|
) -> Result<Vec<i32>, String> {
|
||||||
|
self.vision
|
||||||
|
.as_ref()
|
||||||
|
.ok_or("DeepSeek vision encoder is not loaded")?
|
||||||
|
.layout(embedding, position, self.model.shape.vocab as i32)
|
||||||
|
}
|
||||||
|
pub(super) fn encode_visions(
|
||||||
|
&self,
|
||||||
|
encoded: &[Vec<u8>],
|
||||||
|
) -> Result<Vec<super::VisionEmbedding>, String> {
|
||||||
|
let encoder = self
|
||||||
|
.vision
|
||||||
|
.as_ref()
|
||||||
|
.ok_or("DeepSeek vision encoder is not loaded")?;
|
||||||
|
let model = self
|
||||||
|
.model
|
||||||
|
.vision
|
||||||
|
.as_ref()
|
||||||
|
.ok_or("DeepSeek vision sidecar is not loaded")?;
|
||||||
|
if !self.speculative.keep_vision_loaded {
|
||||||
|
call(
|
||||||
|
unsafe {
|
||||||
|
ds4_gpu_set_transient_model_map_range(
|
||||||
|
model.map_ptr().cast(),
|
||||||
|
model.len(),
|
||||||
|
model.data_offset(),
|
||||||
|
model.len() - model.data_offset(),
|
||||||
|
model.max_tensor_bytes(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"mapping DeepSeek vision weights",
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
let result = encoded
|
||||||
|
.iter()
|
||||||
|
.map(|bytes| encoder.encode(model, bytes))
|
||||||
|
.collect();
|
||||||
|
let released = if self.speculative.keep_vision_loaded {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
call(
|
||||||
|
unsafe { ds4_gpu_release_transient_model_map(model.map_ptr().cast(), model.len()) },
|
||||||
|
"releasing DeepSeek vision weights",
|
||||||
|
)
|
||||||
|
};
|
||||||
|
match (result, released) {
|
||||||
|
(Ok(result), Ok(())) => Ok(result),
|
||||||
|
(Err(e), _) | (_, Err(e)) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub(super) fn set_vision_overlays(
|
||||||
|
&mut self,
|
||||||
|
overlays: Vec<(u32, super::VisionEmbedding)>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
if self.vision.is_none() && !overlays.is_empty() {
|
||||||
|
return Err("DeepSeek vision encoder is not loaded".into());
|
||||||
|
}
|
||||||
|
let mut next = Vec::with_capacity(overlays.len());
|
||||||
|
let mut end = 0;
|
||||||
|
for (start, image) in overlays {
|
||||||
|
if image.tokens == 0
|
||||||
|
|| image.tokens > 384
|
||||||
|
|| image.values.len() != image.tokens as usize * 4096
|
||||||
|
|| start < end
|
||||||
|
{
|
||||||
|
return Err("invalid DeepSeek image overlay".into());
|
||||||
|
}
|
||||||
|
end = start
|
||||||
|
.checked_add(image.tokens)
|
||||||
|
.ok_or("DeepSeek image position overflow")?;
|
||||||
|
if end > self.session.context {
|
||||||
|
return Err("DeepSeek image exceeds the session context".into());
|
||||||
|
}
|
||||||
|
let embedding = Buffer::floats(image.values.len() as u64)?;
|
||||||
|
embedding.write_f32(&image.values)?;
|
||||||
|
next.push(Overlay {
|
||||||
|
identity: Identity {
|
||||||
|
start,
|
||||||
|
count: image.tokens,
|
||||||
|
fingerprint: image.fingerprint,
|
||||||
|
},
|
||||||
|
embedding,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let matches = self.vision_identities.len() <= next.len()
|
||||||
|
&& self
|
||||||
|
.vision_identities
|
||||||
|
.iter()
|
||||||
|
.zip(&next)
|
||||||
|
.all(|(old, new)| old == &new.identity)
|
||||||
|
&& next
|
||||||
|
.get(self.vision_identities.len())
|
||||||
|
.is_none_or(|image| image.identity.start >= self.session.position);
|
||||||
|
if !matches {
|
||||||
|
self.reset()?;
|
||||||
|
}
|
||||||
|
self.vision_overlays = next;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub(super) fn attention(
|
||||||
|
s: &super::BatchScratch,
|
||||||
|
state: &super::LayerState,
|
||||||
|
w: &super::Layer,
|
||||||
|
shape: crate::engine::Shape,
|
||||||
|
map: *const std::ffi::c_void,
|
||||||
|
size: u64,
|
||||||
|
tokens: &[i32],
|
||||||
|
pos: u32,
|
||||||
|
raw_cap: u32,
|
||||||
|
n_comp: u32,
|
||||||
|
ratio: u32,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let n = tokens.len() as u32;
|
||||||
|
let (n_raw, raw_start) = if pos == 0 {
|
||||||
|
(n, 0)
|
||||||
|
} else {
|
||||||
|
super::raw_batch_span(pos, n, raw_cap, shape.sliding_window as u32)
|
||||||
|
};
|
||||||
|
let bounds = image::attention_bounds(
|
||||||
|
tokens,
|
||||||
|
shape.vocab as i32,
|
||||||
|
pos,
|
||||||
|
n_raw,
|
||||||
|
shape.sliding_window as u32,
|
||||||
|
)?;
|
||||||
|
let mut mask = Vec::with_capacity(n as usize * (n_raw + n_comp) as usize);
|
||||||
|
let first = pos + n - n_raw;
|
||||||
|
for row in 0..n {
|
||||||
|
for key in 0..n_raw {
|
||||||
|
mask.push(
|
||||||
|
if (bounds[row as usize * 2]..=bounds[row as usize * 2 + 1])
|
||||||
|
.contains(&(first + key))
|
||||||
|
{
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
0xfc00
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let visible = (pos + row + 1).checked_div(ratio).unwrap_or(0);
|
||||||
|
mask.extend((0..n_comp).map(|key| if key < visible { 0 } else { 0xfc00 }));
|
||||||
|
}
|
||||||
|
let indexed = ratio == 4 && n_comp > shape.indexer_top_k as u32;
|
||||||
|
let comp_mask = if indexed {
|
||||||
|
let indexer = state
|
||||||
|
.indexer
|
||||||
|
.as_ref()
|
||||||
|
.ok_or("DeepSeek visual indexer state is missing")?;
|
||||||
|
let scale = ((shape.indexer_heads * shape.indexer_head_dim) as f32)
|
||||||
|
.sqrt()
|
||||||
|
.recip();
|
||||||
|
call(
|
||||||
|
unsafe {
|
||||||
|
if pos == 0 {
|
||||||
|
ds4_gpu_indexer_scores_prefill_tensor(
|
||||||
|
s.indexer_scores.raw(),
|
||||||
|
s.indexer_q.raw(),
|
||||||
|
s.indexer_weights.raw(),
|
||||||
|
indexer.cache.raw(),
|
||||||
|
n_comp,
|
||||||
|
n,
|
||||||
|
shape.indexer_heads as u32,
|
||||||
|
shape.indexer_head_dim as u32,
|
||||||
|
ratio,
|
||||||
|
scale,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
ds4_gpu_indexer_scores_decode_batch_tensor(
|
||||||
|
s.indexer_scores.raw(),
|
||||||
|
s.indexer_q.raw(),
|
||||||
|
s.indexer_weights.raw(),
|
||||||
|
indexer.cache.raw(),
|
||||||
|
n_comp,
|
||||||
|
n,
|
||||||
|
pos,
|
||||||
|
shape.indexer_heads as u32,
|
||||||
|
shape.indexer_head_dim as u32,
|
||||||
|
ratio,
|
||||||
|
scale,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"DeepSeek visual indexer scores",
|
||||||
|
)?;
|
||||||
|
call(
|
||||||
|
unsafe {
|
||||||
|
ds4_gpu_indexer_topk_tensor(
|
||||||
|
s.indexer_selected.raw(),
|
||||||
|
s.indexer_scores.raw(),
|
||||||
|
n_comp,
|
||||||
|
n,
|
||||||
|
shape.indexer_top_k as u32,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"DeepSeek visual indexer top-k",
|
||||||
|
)?;
|
||||||
|
let mask = Buffer::floats(u64::from(n) * u64::from(n_comp))?;
|
||||||
|
call(
|
||||||
|
unsafe {
|
||||||
|
ds4_gpu_dsv4_topk_mask_tensor(
|
||||||
|
mask.raw(),
|
||||||
|
s.indexer_selected.raw(),
|
||||||
|
n_comp,
|
||||||
|
n,
|
||||||
|
shape.indexer_top_k as u32,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"DeepSeek visual compressed mask",
|
||||||
|
)?;
|
||||||
|
Some(mask)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
call(
|
||||||
|
unsafe {
|
||||||
|
ds4_gpu_attention_visual_batch_tensor(
|
||||||
|
s.heads.raw(),
|
||||||
|
map,
|
||||||
|
size,
|
||||||
|
w.attn_sinks.offset,
|
||||||
|
s.q.raw(),
|
||||||
|
state.raw_cache.raw(),
|
||||||
|
state
|
||||||
|
.compression
|
||||||
|
.as_ref()
|
||||||
|
.map_or(std::ptr::null(), |c| c.cache.raw()),
|
||||||
|
comp_mask
|
||||||
|
.as_ref()
|
||||||
|
.map_or(std::ptr::null(), |b| b.raw().cast_const()),
|
||||||
|
u32::from(indexed),
|
||||||
|
n,
|
||||||
|
pos,
|
||||||
|
n_raw,
|
||||||
|
raw_cap,
|
||||||
|
raw_start,
|
||||||
|
n_comp,
|
||||||
|
shape.sliding_window as u32,
|
||||||
|
ratio,
|
||||||
|
shape.heads as u32,
|
||||||
|
shape.head_dim as u32,
|
||||||
|
mask.as_ptr(),
|
||||||
|
mask.len() as u64,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"DeepSeek visual attention",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "issue100.rs"]
|
||||||
|
mod tests;
|
||||||
@@ -7196,6 +7196,9 @@ mod tests {
|
|||||||
let prefill_tps = prompt.len() as f64 / prefill_started.elapsed().as_secs_f64();
|
let prefill_tps = prompt.len() as f64 / prefill_started.elapsed().as_secs_f64();
|
||||||
let image_logits = executor.logits().to_vec();
|
let image_logits = executor.logits().to_vec();
|
||||||
let make_embedding = |values| crate::engine::metal::vision::VisionEmbedding {
|
let make_embedding = |values| crate::engine::metal::vision::VisionEmbedding {
|
||||||
|
fingerprint: [0; 32],
|
||||||
|
grid_width: 0,
|
||||||
|
grid_height: 0,
|
||||||
values,
|
values,
|
||||||
tokens: embedding_shape.0,
|
tokens: embedding_shape.0,
|
||||||
width: embedding_shape.1,
|
width: embedding_shape.1,
|
||||||
|
|||||||
@@ -125,6 +125,48 @@ pub(super) struct GpuBusyStats {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" {
|
unsafe extern "C" {
|
||||||
|
pub(super) fn ds4_gpu_dsv4_topk_mask_tensor(
|
||||||
|
mask: *mut GpuTensor,
|
||||||
|
topk: *const GpuTensor,
|
||||||
|
n_comp: u32,
|
||||||
|
n_tokens: u32,
|
||||||
|
top_k: u32,
|
||||||
|
) -> i32;
|
||||||
|
|
||||||
|
pub(super) fn ds4_gpu_attention_visual_batch_tensor(
|
||||||
|
heads: *mut GpuTensor,
|
||||||
|
map: *const c_void,
|
||||||
|
size: u64,
|
||||||
|
sinks: u64,
|
||||||
|
q: *const GpuTensor,
|
||||||
|
raw: *const GpuTensor,
|
||||||
|
comp: *const GpuTensor,
|
||||||
|
comp_mask: *const GpuTensor,
|
||||||
|
use_mask: u32,
|
||||||
|
rows: u32,
|
||||||
|
pos: u32,
|
||||||
|
n_raw: u32,
|
||||||
|
raw_cap: u32,
|
||||||
|
raw_start: u32,
|
||||||
|
n_comp: u32,
|
||||||
|
window: u32,
|
||||||
|
ratio: u32,
|
||||||
|
n_head: u32,
|
||||||
|
head_dim: u32,
|
||||||
|
mask: *const u16,
|
||||||
|
mask_elements: u64,
|
||||||
|
) -> i32;
|
||||||
|
|
||||||
|
pub(super) fn ds4_gpu_deepseek_vision_dispatch(
|
||||||
|
kernel: *const c_char,
|
||||||
|
bindings: *const MetalBinding,
|
||||||
|
count: u32,
|
||||||
|
grid: *const u32,
|
||||||
|
threads: *const u32,
|
||||||
|
individual_threads: u32,
|
||||||
|
shared_bytes: u32,
|
||||||
|
) -> i32;
|
||||||
|
|
||||||
pub(super) fn ds4_gpu_init() -> i32;
|
pub(super) fn ds4_gpu_init() -> i32;
|
||||||
pub(super) fn ds4_gpu_cleanup();
|
pub(super) fn ds4_gpu_cleanup();
|
||||||
pub(super) fn ds4_gpu_metal4_tensor_api_enabled() -> i32;
|
pub(super) fn ds4_gpu_metal4_tensor_api_enabled() -> i32;
|
||||||
@@ -1703,6 +1745,28 @@ unsafe extern "C" {
|
|||||||
scale: f32,
|
scale: f32,
|
||||||
rows: u32,
|
rows: u32,
|
||||||
) -> i32;
|
) -> i32;
|
||||||
|
pub(super) fn ds4_gpu_router_select_visual_batch_tensor(
|
||||||
|
selected: *mut GpuTensor,
|
||||||
|
weights: *mut GpuTensor,
|
||||||
|
probs: *mut GpuTensor,
|
||||||
|
map: *const c_void,
|
||||||
|
size: u64,
|
||||||
|
bias: u64,
|
||||||
|
hash: u64,
|
||||||
|
hash_rows: u32,
|
||||||
|
expert_groups: u32,
|
||||||
|
groups_used: u32,
|
||||||
|
has_bias: bool,
|
||||||
|
hash_mode: bool,
|
||||||
|
logits: *const GpuTensor,
|
||||||
|
tokens: *const GpuTensor,
|
||||||
|
experts: u32,
|
||||||
|
experts_used: u32,
|
||||||
|
scale: f32,
|
||||||
|
rows: u32,
|
||||||
|
visual_bias: *const GpuTensor,
|
||||||
|
vocab_size: u32,
|
||||||
|
) -> i32;
|
||||||
pub(super) fn ds4_gpu_routed_moe_batch_tensor(
|
pub(super) fn ds4_gpu_routed_moe_batch_tensor(
|
||||||
out: *mut GpuTensor,
|
out: *mut GpuTensor,
|
||||||
gate: *mut GpuTensor,
|
gate: *mut GpuTensor,
|
||||||
|
|||||||
@@ -0,0 +1,585 @@
|
|||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
#[ignore = "requires the pinned Vision-Exp sidecar and Apple Metal"]
|
||||||
|
fn issue100_encoder_real_weights() {
|
||||||
|
super::super::configure_sources().unwrap();
|
||||||
|
let root = std::env::var_os("DS4_ISSUE100_ARTIFACTS")
|
||||||
|
.map(std::path::PathBuf::from)
|
||||||
|
.unwrap_or_else(|| std::path::PathBuf::from("local-eval-results/issue100-artifacts"));
|
||||||
|
let path = root.join("DeepSeek-V4-Flash-Vision-Encoder.gguf");
|
||||||
|
crate::engine::validation::validate_vision_artifact(&path).unwrap();
|
||||||
|
let model = Gguf::open(&path).unwrap();
|
||||||
|
call(unsafe { ds4_gpu_init() }, "initializing vision test").unwrap();
|
||||||
|
call(
|
||||||
|
unsafe {
|
||||||
|
ds4_gpu_set_model_map_range(
|
||||||
|
model.map_ptr().cast(),
|
||||||
|
model.len(),
|
||||||
|
model.data_offset(),
|
||||||
|
model.len() - model.data_offset(),
|
||||||
|
model.max_tensor_bytes(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"mapping vision test weights",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let encoder = Encoder::bind(&model).unwrap();
|
||||||
|
let image_path = std::env::var_os("DS4_ISSUE100_IMAGE").expect("DS4_ISSUE100_IMAGE");
|
||||||
|
let encoded = std::fs::read(image_path).unwrap();
|
||||||
|
let patches = image::decode(&encoded).unwrap();
|
||||||
|
let embedding = encoder.encode(&model, &encoded).unwrap();
|
||||||
|
assert_eq!(embedding.values.len(), embedding.tokens as usize * 4096);
|
||||||
|
assert!(embedding.values.iter().all(|x| x.is_finite()));
|
||||||
|
let prefix = std::env::var_os("DS4_ISSUE100_ENCODER_OUTPUT")
|
||||||
|
.map(std::path::PathBuf::from)
|
||||||
|
.expect("DS4_ISSUE100_ENCODER_OUTPUT");
|
||||||
|
for (suffix, values) in [
|
||||||
|
("patches", &patches.values),
|
||||||
|
("embedding", &embedding.values),
|
||||||
|
] {
|
||||||
|
let bytes = values
|
||||||
|
.iter()
|
||||||
|
.flat_map(|v| v.to_le_bytes())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
std::fs::write(prefix.with_extension(format!("{suffix}.f32")), bytes).unwrap();
|
||||||
|
}
|
||||||
|
let info = serde_json::json!({"grid":[patches.grid_height,patches.grid_width],"aligned_grid":[embedding.grid_height,embedding.grid_width],"tokens":embedding.tokens,"width":embedding.width,"height":embedding.height,"values":embedding.values.len()});
|
||||||
|
std::fs::write(
|
||||||
|
prefix.with_extension("json"),
|
||||||
|
serde_json::to_vec_pretty(&info).unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
eprintln!("{info}");
|
||||||
|
drop(encoder);
|
||||||
|
unsafe { ds4_gpu_cleanup() };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore = "requires Apple Metal; isolated visual routing and attention primitives"]
|
||||||
|
fn issue100_visual_routing_and_attention() {
|
||||||
|
use crate::engine::deepseek_vision::attention_bounds;
|
||||||
|
super::super::configure_sources().unwrap();
|
||||||
|
call(unsafe { ds4_gpu_init() }, "initializing visual primitives").unwrap();
|
||||||
|
let mut map = memmap2::MmapMut::map_anon(16384).unwrap();
|
||||||
|
let hash_offset = 1024;
|
||||||
|
for expert in 0..256 {
|
||||||
|
let bias = if (30..36).contains(&expert) {
|
||||||
|
100.0 + expert as f32
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
map[expert * 4..expert * 4 + 4].copy_from_slice(&bias.to_le_bytes());
|
||||||
|
}
|
||||||
|
for token in 0..4 {
|
||||||
|
for expert in 0..6 {
|
||||||
|
let id = 60 + (token * 6 + expert) as i32;
|
||||||
|
let offset = hash_offset + (token * 6 + expert) * 4;
|
||||||
|
map[offset..offset + 4].copy_from_slice(&id.to_le_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
call(
|
||||||
|
unsafe {
|
||||||
|
ds4_gpu_set_model_map_range(
|
||||||
|
map.as_ptr().cast(),
|
||||||
|
map.len() as u64,
|
||||||
|
0,
|
||||||
|
map.len() as u64,
|
||||||
|
1024,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"mapping routing fixture",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let logits = Buffer::floats(4 * 256).unwrap();
|
||||||
|
let input = (0..4 * 256)
|
||||||
|
.map(|i| ((i % 256) as f32 - 128.0) / 128.0)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
logits.write_f32(&input).unwrap();
|
||||||
|
let tokens = Buffer::bytes(16).unwrap();
|
||||||
|
tokens.write_i32(&[1, 1000, 1002, 2]).unwrap();
|
||||||
|
let selected = Buffer::bytes(4 * 6 * 4).unwrap();
|
||||||
|
let weights = Buffer::floats(4 * 6).unwrap();
|
||||||
|
let probs = Buffer::floats(4 * 256).unwrap();
|
||||||
|
let bias = Buffer::floats(256).unwrap();
|
||||||
|
bias.write_f32(
|
||||||
|
&(0..256)
|
||||||
|
.map(|i| {
|
||||||
|
if (200..206).contains(&i) {
|
||||||
|
100.0 + i as f32
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
for hash_mode in [false, true] {
|
||||||
|
let commands = Commands::begin().unwrap();
|
||||||
|
call(
|
||||||
|
unsafe {
|
||||||
|
ds4_gpu_router_select_visual_batch_tensor(
|
||||||
|
selected.raw(),
|
||||||
|
weights.raw(),
|
||||||
|
probs.raw(),
|
||||||
|
map.as_ptr().cast(),
|
||||||
|
map.len() as u64,
|
||||||
|
0,
|
||||||
|
hash_offset as u64,
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
true,
|
||||||
|
hash_mode,
|
||||||
|
logits.raw(),
|
||||||
|
tokens.raw(),
|
||||||
|
256,
|
||||||
|
6,
|
||||||
|
1.5,
|
||||||
|
4,
|
||||||
|
bias.raw(),
|
||||||
|
1000,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"visual router fixture",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
commands.finish().unwrap();
|
||||||
|
let mut ids = vec![0; 24];
|
||||||
|
selected.read_i32(&mut ids).unwrap();
|
||||||
|
let mut actual_weights = vec![0.0; 24];
|
||||||
|
weights.read_f32(&mut actual_weights).unwrap();
|
||||||
|
for row in 0..4 {
|
||||||
|
let expected = if row == 1 || row == 2 {
|
||||||
|
vec![205, 204, 203, 202, 201, 200]
|
||||||
|
} else if hash_mode {
|
||||||
|
let token = if row == 0 { 1 } else { 2 };
|
||||||
|
(0..6).map(|e| 60 + token * 6 + e).collect()
|
||||||
|
} else {
|
||||||
|
vec![35, 34, 33, 32, 31, 30]
|
||||||
|
};
|
||||||
|
assert_eq!(&ids[row * 6..row * 6 + 6], expected);
|
||||||
|
let probability = expected
|
||||||
|
.iter()
|
||||||
|
.map(|&id| input[row * 256 + id as usize].exp().ln_1p().sqrt())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let sum = probability.iter().sum::<f32>().max(6.1035156e-5);
|
||||||
|
for i in 0..6 {
|
||||||
|
assert!((actual_weights[row * 6 + i] - probability[i] / sum * 1.5).abs() < 1e-6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Zero queries make the independent expected result the visible-key mean
|
||||||
|
// with one additional denominator term for the zero attention sink.
|
||||||
|
map[..8].fill(0);
|
||||||
|
let rows = 8_u32;
|
||||||
|
let heads = 2_u32;
|
||||||
|
let dim = 512_u32;
|
||||||
|
let pos = 10_u32;
|
||||||
|
let raw_cap = 32_u32;
|
||||||
|
let raw_start = 25_u32;
|
||||||
|
let n_raw = 18_u32;
|
||||||
|
let q = Buffer::floats(u64::from(rows * heads * dim)).unwrap();
|
||||||
|
q.write_f32(&vec![0.0; (rows * heads * dim) as usize])
|
||||||
|
.unwrap();
|
||||||
|
let raw = Buffer::floats(u64::from(raw_cap * dim)).unwrap();
|
||||||
|
let mut data = vec![0.0; (raw_cap * dim) as usize];
|
||||||
|
for key in 0..n_raw {
|
||||||
|
let physical = (raw_start + key) % raw_cap;
|
||||||
|
data[(physical * dim) as usize..((physical + 1) * dim) as usize].fill(key as f32 / 64.0);
|
||||||
|
}
|
||||||
|
raw.write_f32(&data).unwrap();
|
||||||
|
let comp = Buffer::bytes(4 * 512 * 2).unwrap();
|
||||||
|
// Exactly representable half values: 0.25, 0.5, 0.75, 1.0.
|
||||||
|
let mut comp_bytes = Vec::new();
|
||||||
|
for value in [0x3400_u16, 0x3800, 0x3a00, 0x3c00] {
|
||||||
|
for _ in 0..512 {
|
||||||
|
comp_bytes.extend(value.to_le_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
comp.write(0, &comp_bytes).unwrap();
|
||||||
|
let out = Buffer::floats(u64::from(rows * heads * dim)).unwrap();
|
||||||
|
let ids = [1, 1000, 1002, 1002, 1004, 2, 3, 4];
|
||||||
|
let bounds = attention_bounds(&ids, 1000, pos, n_raw, 3).unwrap();
|
||||||
|
let comp_mask = Buffer::floats(u64::from(rows) * 4).unwrap();
|
||||||
|
comp_mask
|
||||||
|
.write_f32(
|
||||||
|
&(0..rows)
|
||||||
|
.flat_map(|row| {
|
||||||
|
(0..4).map(move |key| {
|
||||||
|
if key < (pos + row + 1) / 4 && key % 2 == 0 {
|
||||||
|
0.0
|
||||||
|
} else {
|
||||||
|
f32::NEG_INFINITY
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
for (n_comp, indexed) in [(0_u32, false), (4, false), (4, true)] {
|
||||||
|
let mut mask = Vec::new();
|
||||||
|
let mut expected = Vec::new();
|
||||||
|
for row in 0..rows {
|
||||||
|
let mut values = Vec::new();
|
||||||
|
for key in 0..n_raw {
|
||||||
|
let visible =
|
||||||
|
(bounds[row as usize * 2]..=bounds[row as usize * 2 + 1]).contains(&key);
|
||||||
|
mask.push(if visible { 0_u16 } else { 0xfc00 });
|
||||||
|
if visible {
|
||||||
|
values.push(key as f32 / 64.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for key in 0..n_comp {
|
||||||
|
let visible = key < (pos + row + 1) / 4 && (!indexed || key % 2 == 0);
|
||||||
|
mask.push(if visible { 0 } else { 0xfc00 });
|
||||||
|
if visible {
|
||||||
|
values.push((key + 1) as f32 / 4.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expected.push(values.iter().sum::<f32>() / (values.len() + 1) as f32);
|
||||||
|
}
|
||||||
|
let commands = Commands::begin().unwrap();
|
||||||
|
call(
|
||||||
|
unsafe {
|
||||||
|
ds4_gpu_attention_visual_batch_tensor(
|
||||||
|
out.raw(),
|
||||||
|
map.as_ptr().cast(),
|
||||||
|
map.len() as u64,
|
||||||
|
0,
|
||||||
|
q.raw(),
|
||||||
|
raw.raw(),
|
||||||
|
comp.raw(),
|
||||||
|
if indexed {
|
||||||
|
comp_mask.raw().cast_const()
|
||||||
|
} else {
|
||||||
|
std::ptr::null()
|
||||||
|
},
|
||||||
|
u32::from(indexed),
|
||||||
|
rows,
|
||||||
|
pos,
|
||||||
|
n_raw,
|
||||||
|
raw_cap,
|
||||||
|
raw_start,
|
||||||
|
n_comp,
|
||||||
|
3,
|
||||||
|
if n_comp == 0 { 0 } else { 4 },
|
||||||
|
heads,
|
||||||
|
dim,
|
||||||
|
mask.as_ptr(),
|
||||||
|
mask.len() as u64,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
"visual attention fixture",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
commands.finish().unwrap();
|
||||||
|
let mut actual = vec![0.0; (rows * heads * dim) as usize];
|
||||||
|
out.read_f32(&mut actual).unwrap();
|
||||||
|
for (row, expected) in expected.iter().enumerate() {
|
||||||
|
for value in &actual[row * (heads * dim) as usize..(row + 1) * (heads * dim) as usize] {
|
||||||
|
assert!(
|
||||||
|
(value - expected).abs() < 0.0005,
|
||||||
|
"row {row}: {value} != {expected}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eprintln!(
|
||||||
|
"issue100 visual routing: hash/text/image rows and weights pass; raw/mixed/index-masked visual attention with wrapped KV passes"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore = "requires the verified Vision-Exp managed artifacts and 110 GiB of Apple unified memory"]
|
||||||
|
fn issue100_full_model_images_cache_and_dspark() {
|
||||||
|
use crate::engine::{ChatTurn, Generator, metal::Executor, vision_data_marker};
|
||||||
|
use crate::metrics::Metrics;
|
||||||
|
use crate::model::ModelChoice;
|
||||||
|
use crate::settings::{
|
||||||
|
GenerationPreferences, ReasoningMode, RuntimePreferences, effective_settings,
|
||||||
|
};
|
||||||
|
use base64::Engine as _;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::sync::{Arc, atomic::AtomicBool};
|
||||||
|
super::super::configure_sources().unwrap();
|
||||||
|
let root = Path::new("local-eval-results/issue100-managed");
|
||||||
|
let cache = Path::new("local-eval-results/issue100-full-model");
|
||||||
|
std::fs::create_dir_all(cache).unwrap();
|
||||||
|
let generation = GenerationPreferences {
|
||||||
|
context_tokens: 1024,
|
||||||
|
max_generated_tokens: 32,
|
||||||
|
system_prompt: String::new(),
|
||||||
|
temperature: Some(0.0),
|
||||||
|
reasoning_mode: ReasoningMode::Direct,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let mut runtime = RuntimePreferences::default();
|
||||||
|
runtime.execution.prefill_chunk = Some(384);
|
||||||
|
runtime.execution.power_percent = Some(100);
|
||||||
|
if std::env::var_os("DS4_ISSUE100_SSD").is_some() {
|
||||||
|
runtime.ssd.enabled = true;
|
||||||
|
runtime.ssd.cache = Some(crate::settings::StreamingCacheBudget::Experts(4096));
|
||||||
|
runtime.ssd.preload_experts = Some(4096);
|
||||||
|
}
|
||||||
|
runtime.speculative.dspark_enabled = true;
|
||||||
|
runtime.speculative.dspark_confidence_threshold = Some(0.0);
|
||||||
|
runtime.speculative.keep_vision_loaded = std::env::var_os("DS4_ISSUE100_KEEP_VISION").is_some();
|
||||||
|
let settings = effective_settings(
|
||||||
|
ModelChoice::DeepSeekV4FlashVision,
|
||||||
|
&generation,
|
||||||
|
&runtime,
|
||||||
|
root,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let mut generator = Generator::open(&settings.engine, Arc::new(Metrics::new(cache))).unwrap();
|
||||||
|
let image_marker = |name: &str| {
|
||||||
|
let bytes = std::fs::read(format!("docs/issue100-vision/images/{name}.png")).unwrap();
|
||||||
|
vision_data_marker(&format!(
|
||||||
|
"data:image/png;base64,{}",
|
||||||
|
base64::engine::general_purpose::STANDARD.encode(bytes)
|
||||||
|
))
|
||||||
|
};
|
||||||
|
let turn = |content: String, tool: bool| ChatTurn {
|
||||||
|
user: !tool,
|
||||||
|
tool,
|
||||||
|
system: false,
|
||||||
|
skip_previous_eos: false,
|
||||||
|
reasoning: None,
|
||||||
|
reasoning_complete: true,
|
||||||
|
content,
|
||||||
|
};
|
||||||
|
let red = turn(
|
||||||
|
format!(
|
||||||
|
"Describe the color and shape in this image in one sentence. {}",
|
||||||
|
image_marker("red-square")
|
||||||
|
),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
let blue = turn(
|
||||||
|
red.content
|
||||||
|
.replace(&image_marker("red-square"), &image_marker("blue-circle")),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
let (red_tokens, red_overlays) = generator
|
||||||
|
.render_multimodal_conversation("", std::slice::from_ref(&red), ReasoningMode::Direct)
|
||||||
|
.unwrap();
|
||||||
|
let (blue_tokens, blue_overlays) = generator
|
||||||
|
.render_multimodal_conversation("", std::slice::from_ref(&blue), ReasoningMode::Direct)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
red_tokens, blue_tokens,
|
||||||
|
"same-size images intentionally have identical synthetic tokens"
|
||||||
|
);
|
||||||
|
assert_eq!(red_overlays.len(), 1);
|
||||||
|
assert_eq!(blue_overlays.len(), 1);
|
||||||
|
let prompt_checkpoint = cache.join("prompt.kv");
|
||||||
|
let restored_checkpoint = cache.join("restored.kv");
|
||||||
|
let Executor::DeepSeek(executor) = &mut generator.executor else {
|
||||||
|
panic!("wrong executor")
|
||||||
|
};
|
||||||
|
executor.set_vision_overlays(red_overlays).unwrap();
|
||||||
|
executor.prefill(&red_tokens, |_| true).unwrap();
|
||||||
|
assert_eq!(executor.vision_identities.len(), 1);
|
||||||
|
let image_identity = executor.vision_identities.clone();
|
||||||
|
let prompt_logits = executor.logits.clone();
|
||||||
|
executor
|
||||||
|
.save_checkpoint(&prompt_checkpoint, [100; 32], &mut |_| {})
|
||||||
|
.unwrap();
|
||||||
|
executor.reset().unwrap();
|
||||||
|
assert!(
|
||||||
|
executor
|
||||||
|
.load_checkpoint(&prompt_checkpoint, &mut |_| {})
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(executor.logits, prompt_logits);
|
||||||
|
assert_eq!(executor.vision_identities, image_identity);
|
||||||
|
assert!(executor.vision_overlays.is_empty());
|
||||||
|
// Verifier-sized text batches after disk restoration must retain image
|
||||||
|
// identities even though no image embeddings are needed for this suffix.
|
||||||
|
executor.prefill(&[19923, 14, 2058, 3], |_| true).unwrap();
|
||||||
|
assert_eq!(executor.vision_identities, image_identity);
|
||||||
|
executor
|
||||||
|
.save_checkpoint(&restored_checkpoint, [100; 32], &mut |_| {})
|
||||||
|
.unwrap();
|
||||||
|
executor.reset().unwrap();
|
||||||
|
assert!(
|
||||||
|
executor
|
||||||
|
.load_checkpoint(&restored_checkpoint, &mut |_| {})
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(executor.vision_identities, image_identity);
|
||||||
|
executor.set_vision_overlays(blue_overlays).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
executor.session.position, 0,
|
||||||
|
"changed image must invalidate equal token prefix"
|
||||||
|
);
|
||||||
|
let cancelled = AtomicBool::new(false);
|
||||||
|
let run = |g: &mut Generator, messages: &[ChatTurn], checkpoint: &Path| {
|
||||||
|
g.generate(
|
||||||
|
checkpoint,
|
||||||
|
None,
|
||||||
|
messages,
|
||||||
|
&settings.turn,
|
||||||
|
&cancelled,
|
||||||
|
|_, _| {},
|
||||||
|
|_, _, _| {},
|
||||||
|
|_| {},
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
let first = run(
|
||||||
|
&mut generator,
|
||||||
|
std::slice::from_ref(&red),
|
||||||
|
&cache.join("red.kv"),
|
||||||
|
);
|
||||||
|
eprintln!("red: {}", first.message.content);
|
||||||
|
assert!(first.message.content.to_lowercase().contains("red"));
|
||||||
|
assert!(first.message.content.to_lowercase().contains("square"));
|
||||||
|
let stats = generator.executor.execution_stats();
|
||||||
|
assert!(
|
||||||
|
stats.drafted_tokens > 0,
|
||||||
|
"the matching DSpark must actually draft"
|
||||||
|
);
|
||||||
|
eprintln!(
|
||||||
|
"DSpark drafted={} accepted={}",
|
||||||
|
stats.drafted_tokens, stats.accepted_draft_tokens
|
||||||
|
);
|
||||||
|
let Executor::DeepSeek(executor) = &mut generator.executor else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
executor.reset().unwrap();
|
||||||
|
executor.dspark.as_mut().unwrap().strict = true;
|
||||||
|
let plain = run(
|
||||||
|
&mut generator,
|
||||||
|
std::slice::from_ref(&red),
|
||||||
|
&cache.join("red-target.kv"),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
plain.message.content, first.message.content,
|
||||||
|
"DSpark and target-only output"
|
||||||
|
);
|
||||||
|
std::fs::copy(cache.join("red-target.kv"), cache.join("red-disk.kv")).unwrap();
|
||||||
|
let history = [
|
||||||
|
red.clone(),
|
||||||
|
plain.message.clone(),
|
||||||
|
turn(
|
||||||
|
"What color was the shape? Answer with one word.".into(),
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
let warm = run(&mut generator, &history, &cache.join("red-target.kv"));
|
||||||
|
assert!(
|
||||||
|
warm.cached_tokens > 0,
|
||||||
|
"warm image history must reuse its KV prefix"
|
||||||
|
);
|
||||||
|
assert!(warm.message.content.to_lowercase().contains("red"));
|
||||||
|
generator.executor.reset().unwrap();
|
||||||
|
generator.checkpoint = None;
|
||||||
|
let restored = run(&mut generator, &history, &cache.join("red-disk.kv"));
|
||||||
|
assert!(
|
||||||
|
restored.cached_tokens > 0,
|
||||||
|
"disk image history must reuse its KV prefix"
|
||||||
|
);
|
||||||
|
assert_eq!(restored.message.content, warm.message.content);
|
||||||
|
let mut append_history = history.to_vec();
|
||||||
|
append_history.push(restored.message.clone());
|
||||||
|
append_history.push(turn(
|
||||||
|
format!(
|
||||||
|
"Name the color in this new image. {}",
|
||||||
|
image_marker("blue-circle")
|
||||||
|
),
|
||||||
|
false,
|
||||||
|
));
|
||||||
|
let appended = run(&mut generator, &append_history, &cache.join("red-disk.kv"));
|
||||||
|
assert!(
|
||||||
|
appended.cached_tokens > 0,
|
||||||
|
"appending an image must retain the previous image/text frontier"
|
||||||
|
);
|
||||||
|
assert!(appended.message.content.to_lowercase().contains("blue"));
|
||||||
|
append_history.push(appended.message.clone());
|
||||||
|
append_history.push(turn(
|
||||||
|
format!(
|
||||||
|
"The tool returned this image. Describe its color. {}",
|
||||||
|
image_marker("red-square")
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
));
|
||||||
|
let appended_tool = run(&mut generator, &append_history, &cache.join("red-disk.kv"));
|
||||||
|
assert!(
|
||||||
|
appended_tool.cached_tokens > 0,
|
||||||
|
"tool image append must retain the live frontier"
|
||||||
|
);
|
||||||
|
assert!(appended_tool.message.content.to_lowercase().contains("red"));
|
||||||
|
|
||||||
|
let changed = run(
|
||||||
|
&mut generator,
|
||||||
|
std::slice::from_ref(&blue),
|
||||||
|
&cache.join("blue.kv"),
|
||||||
|
);
|
||||||
|
eprintln!("blue: {}", changed.message.content);
|
||||||
|
assert!(changed.message.content.to_lowercase().contains("blue"));
|
||||||
|
assert!(changed.message.content.to_lowercase().contains("circle"));
|
||||||
|
let pair = turn(
|
||||||
|
format!(
|
||||||
|
"Name the color of the first shape, then the second shape. {} {}",
|
||||||
|
image_marker("red-square"),
|
||||||
|
image_marker("blue-circle")
|
||||||
|
),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
let both = run(&mut generator, &[pair], &cache.join("pair.kv"));
|
||||||
|
eprintln!("pair: {}", both.message.content);
|
||||||
|
let answer = both.message.content.to_lowercase();
|
||||||
|
assert!(answer.contains("red") && answer.contains("blue"));
|
||||||
|
assert!(answer.find("red").unwrap() < answer.find("blue").unwrap());
|
||||||
|
let swapped = turn(
|
||||||
|
format!(
|
||||||
|
"Name the color of the first shape, then the second shape. {} {}",
|
||||||
|
image_marker("blue-circle"),
|
||||||
|
image_marker("red-square")
|
||||||
|
),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
let (swapped_tokens, swapped_overlays) = generator
|
||||||
|
.render_multimodal_conversation("", std::slice::from_ref(&swapped), ReasoningMode::Direct)
|
||||||
|
.unwrap();
|
||||||
|
let Executor::DeepSeek(executor) = &mut generator.executor else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
assert!(
|
||||||
|
executor.tokens.starts_with(&swapped_tokens),
|
||||||
|
"swapping equal-sized images preserves the prompt IDs"
|
||||||
|
);
|
||||||
|
executor.set_vision_overlays(swapped_overlays).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
executor.session.position, 0,
|
||||||
|
"reordered images must invalidate cached KV"
|
||||||
|
);
|
||||||
|
|
||||||
|
let tool = [
|
||||||
|
turn(
|
||||||
|
"Name the color in the image returned by the tool.".into(),
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
turn(image_marker("blue-circle"), true),
|
||||||
|
];
|
||||||
|
let tool_output = run(&mut generator, &tool, &cache.join("tool.kv"));
|
||||||
|
eprintln!("tool image: {}", tool_output.message.content);
|
||||||
|
assert!(tool_output.message.content.to_lowercase().contains("blue"));
|
||||||
|
let text_only = run(
|
||||||
|
&mut generator,
|
||||||
|
&[turn(
|
||||||
|
"What is 2 + 2? Reply with just the number.".into(),
|
||||||
|
false,
|
||||||
|
)],
|
||||||
|
&cache.join("text.kv"),
|
||||||
|
);
|
||||||
|
assert!(text_only.message.content.contains('4'));
|
||||||
|
let Executor::DeepSeek(executor) = &generator.executor else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
assert!(executor.vision_identities.is_empty());
|
||||||
|
eprintln!(
|
||||||
|
"issue100: production rendering, image recognition, multi-image/tool input, same-layout image invalidation, disk/warm KV and DSpark target equivalence passed"
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -15,7 +15,10 @@ pub(super) struct VisionEncoder {
|
|||||||
|
|
||||||
pub(in crate::engine) struct VisionEmbedding {
|
pub(in crate::engine) struct VisionEmbedding {
|
||||||
pub(in crate::engine) values: Vec<f32>,
|
pub(in crate::engine) values: Vec<f32>,
|
||||||
|
pub(in crate::engine) fingerprint: [u8; 32],
|
||||||
pub(in crate::engine) tokens: u32,
|
pub(in crate::engine) tokens: u32,
|
||||||
|
pub(in crate::engine) grid_width: u32,
|
||||||
|
pub(in crate::engine) grid_height: u32,
|
||||||
pub(in crate::engine) width: u32,
|
pub(in crate::engine) width: u32,
|
||||||
pub(in crate::engine) height: u32,
|
pub(in crate::engine) height: u32,
|
||||||
pub(in crate::engine) content_width: u32,
|
pub(in crate::engine) content_width: u32,
|
||||||
@@ -100,6 +103,9 @@ impl VisionEncoder {
|
|||||||
"encoding a GLM 5.3 image",
|
"encoding a GLM 5.3 image",
|
||||||
)?;
|
)?;
|
||||||
Ok(VisionEmbedding {
|
Ok(VisionEmbedding {
|
||||||
|
fingerprint: [0; 32],
|
||||||
|
grid_width: 0,
|
||||||
|
grid_height: 0,
|
||||||
values,
|
values,
|
||||||
tokens,
|
tokens,
|
||||||
width,
|
width,
|
||||||
@@ -241,7 +247,7 @@ fn smart_resize(height: u32, width: u32) -> Result<(u32, u32), String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn resize_bicubic(
|
pub(in crate::engine) fn resize_bicubic(
|
||||||
source: &[u8],
|
source: &[u8],
|
||||||
source_width: u32,
|
source_width: u32,
|
||||||
source_height: u32,
|
source_height: u32,
|
||||||
|
|||||||
+188
-6
@@ -433,6 +433,32 @@ impl Tokenizer {
|
|||||||
messages: &[ChatTurn],
|
messages: &[ChatTurn],
|
||||||
reasoning: ReasoningMode,
|
reasoning: ReasoningMode,
|
||||||
continue_assistant: bool,
|
continue_assistant: bool,
|
||||||
|
) -> Vec<i32> {
|
||||||
|
self.encode_messages_inner(
|
||||||
|
system_prompt,
|
||||||
|
messages,
|
||||||
|
reasoning,
|
||||||
|
continue_assistant,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn encode_deepseek_vision_conversation(
|
||||||
|
&self,
|
||||||
|
system: &str,
|
||||||
|
messages: &[ChatTurn],
|
||||||
|
reasoning: ReasoningMode,
|
||||||
|
) -> Vec<i32> {
|
||||||
|
self.encode_messages_inner(system, messages, reasoning, true, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_messages_inner(
|
||||||
|
&self,
|
||||||
|
system_prompt: &str,
|
||||||
|
messages: &[ChatTurn],
|
||||||
|
reasoning: ReasoningMode,
|
||||||
|
continue_assistant: bool,
|
||||||
|
vision: bool,
|
||||||
) -> Vec<i32> {
|
) -> Vec<i32> {
|
||||||
if self.family == ModelFamily::Qwen {
|
if self.family == ModelFamily::Qwen {
|
||||||
return self.encode_qwen_messages(
|
return self.encode_qwen_messages(
|
||||||
@@ -481,13 +507,14 @@ impl Tokenizer {
|
|||||||
} else {
|
} else {
|
||||||
output.push(self.user);
|
output.push(self.user);
|
||||||
output.extend(self.tokenize("<tool_result>"));
|
output.extend(self.tokenize("<tool_result>"));
|
||||||
output.extend(
|
let content = message
|
||||||
self.tokenize(
|
|
||||||
&message
|
|
||||||
.content
|
.content
|
||||||
.replace("</tool_result>", "</tool_result>"),
|
.replace("</tool_result>", "</tool_result>");
|
||||||
),
|
if vision && content.contains(VISION_TOKEN_START) {
|
||||||
);
|
self.tokenize_vision_text(&content, &mut output);
|
||||||
|
} else {
|
||||||
|
output.extend(self.tokenize(&content));
|
||||||
|
}
|
||||||
output.extend(self.tokenize("</tool_result>"));
|
output.extend(self.tokenize("</tool_result>"));
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@@ -510,7 +537,11 @@ impl Tokenizer {
|
|||||||
output.push(self.think_end);
|
output.push(self.think_end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if vision && message.user && message.content.contains(VISION_TOKEN_START) {
|
||||||
|
self.tokenize_vision_text(&message.content, &mut output);
|
||||||
|
} else {
|
||||||
output.extend(self.tokenize_rendered(&message.content));
|
output.extend(self.tokenize_rendered(&message.content));
|
||||||
|
}
|
||||||
if !message.user
|
if !message.user
|
||||||
&& self.family == ModelFamily::DeepSeek
|
&& self.family == ModelFamily::DeepSeek
|
||||||
&& (continue_assistant || index + 1 < messages.len())
|
&& (continue_assistant || index + 1 < messages.len())
|
||||||
@@ -531,6 +562,28 @@ impl Tokenizer {
|
|||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tokenize_vision_text(&self, text: &str, output: &mut Vec<i32>) {
|
||||||
|
let mut rest = text;
|
||||||
|
while let Some(start) = rest.find(VISION_TOKEN_START) {
|
||||||
|
let tail = &rest[start + VISION_TOKEN_START.len()..];
|
||||||
|
let Some(end) = tail.find(VISION_TOKEN_END) else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
let Ok(count) = tail[..end].parse::<usize>() else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
if count > 384 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
output.extend(self.tokenize(&rest[..start]));
|
||||||
|
output.push(VISION_START_TOKEN);
|
||||||
|
output.extend(std::iter::repeat_n(VISION_IMAGE_TOKEN, count));
|
||||||
|
output.push(VISION_END_TOKEN);
|
||||||
|
rest = &tail[end + VISION_TOKEN_END.len()..];
|
||||||
|
}
|
||||||
|
output.extend(self.tokenize(rest));
|
||||||
|
}
|
||||||
|
|
||||||
fn encode_qwen_messages(
|
fn encode_qwen_messages(
|
||||||
&self,
|
&self,
|
||||||
system_prompt: Option<&str>,
|
system_prompt: Option<&str>,
|
||||||
@@ -646,6 +699,42 @@ impl Tokenizer {
|
|||||||
prompt: &str,
|
prompt: &str,
|
||||||
reasoning: ReasoningMode,
|
reasoning: ReasoningMode,
|
||||||
skip_previous_eos: bool,
|
skip_previous_eos: bool,
|
||||||
|
) -> Vec<i32> {
|
||||||
|
self.encode_continuation_inner(prompt, reasoning, skip_previous_eos, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn encode_deepseek_vision_continuation(
|
||||||
|
&self,
|
||||||
|
prompt: &str,
|
||||||
|
reasoning: ReasoningMode,
|
||||||
|
skip_previous_eos: bool,
|
||||||
|
) -> Vec<i32> {
|
||||||
|
self.encode_continuation_inner(prompt, reasoning, skip_previous_eos, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn encode_deepseek_vision_tool_continuation(
|
||||||
|
&self,
|
||||||
|
message: &ChatTurn,
|
||||||
|
reasoning: ReasoningMode,
|
||||||
|
) -> Vec<i32> {
|
||||||
|
let mut output =
|
||||||
|
self.encode_deepseek_vision_conversation("", std::slice::from_ref(message), reasoning);
|
||||||
|
// A standalone conversation starts with BOS. The live frontier instead
|
||||||
|
// needs the preceding assistant's EOS, just like a user continuation.
|
||||||
|
if message.skip_previous_eos {
|
||||||
|
output.remove(0);
|
||||||
|
} else {
|
||||||
|
output[0] = self.eos;
|
||||||
|
}
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_continuation_inner(
|
||||||
|
&self,
|
||||||
|
prompt: &str,
|
||||||
|
reasoning: ReasoningMode,
|
||||||
|
skip_previous_eos: bool,
|
||||||
|
vision: bool,
|
||||||
) -> Vec<i32> {
|
) -> Vec<i32> {
|
||||||
if self.family == ModelFamily::Qwen {
|
if self.family == ModelFamily::Qwen {
|
||||||
let messages = [ChatTurn {
|
let messages = [ChatTurn {
|
||||||
@@ -664,7 +753,11 @@ impl Tokenizer {
|
|||||||
output.push(self.eos);
|
output.push(self.eos);
|
||||||
}
|
}
|
||||||
output.push(self.user);
|
output.push(self.user);
|
||||||
|
if vision && prompt.contains(VISION_TOKEN_START) {
|
||||||
|
self.tokenize_vision_text(prompt, &mut output);
|
||||||
|
} else {
|
||||||
output.extend(self.tokenize_rendered(prompt));
|
output.extend(self.tokenize_rendered(prompt));
|
||||||
|
}
|
||||||
output.push(self.assistant);
|
output.push(self.assistant);
|
||||||
if reasoning != ReasoningMode::Direct {
|
if reasoning != ReasoningMode::Direct {
|
||||||
output.push(self.think_start);
|
output.push(self.think_start);
|
||||||
@@ -1238,6 +1331,95 @@ fn unicode_punctuation(cp: u32) -> bool {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore = "CPU-only; requires the installed 0731 tokenizer shared with Vision-Exp"]
|
||||||
|
fn issue100_visual_text_preserves_image_boundaries() {
|
||||||
|
use super::super::Model;
|
||||||
|
use crate::model::ModelChoice;
|
||||||
|
let path = crate::model::engine_artifacts(
|
||||||
|
ModelChoice::DeepSeekV4Flash0731,
|
||||||
|
false,
|
||||||
|
&crate::app::models_path(),
|
||||||
|
)
|
||||||
|
.model;
|
||||||
|
let model = Model::open_main(&path, ModelChoice::DeepSeekV4Flash0731).unwrap();
|
||||||
|
let tokenizer = &model.tokenizer;
|
||||||
|
let marker = format!("{VISION_TOKEN_START}2{VISION_TOKEN_END}");
|
||||||
|
let prompt = format!("before{marker}after");
|
||||||
|
for reasoning in ModelChoice::DeepSeekV4FlashVision.reasoning_modes() {
|
||||||
|
for skip_eos in [false, true] {
|
||||||
|
let mut expected = Vec::new();
|
||||||
|
if !skip_eos {
|
||||||
|
expected.push(tokenizer.eos);
|
||||||
|
}
|
||||||
|
expected.push(tokenizer.user);
|
||||||
|
expected.extend(tokenizer.tokenize("before"));
|
||||||
|
expected.extend([
|
||||||
|
VISION_START_TOKEN,
|
||||||
|
VISION_IMAGE_TOKEN,
|
||||||
|
VISION_IMAGE_TOKEN,
|
||||||
|
VISION_END_TOKEN,
|
||||||
|
]);
|
||||||
|
expected.extend(tokenizer.tokenize("after"));
|
||||||
|
expected.push(tokenizer.assistant);
|
||||||
|
expected.push(if *reasoning == ReasoningMode::Direct {
|
||||||
|
tokenizer.think_end
|
||||||
|
} else {
|
||||||
|
tokenizer.think_start
|
||||||
|
});
|
||||||
|
assert_eq!(
|
||||||
|
tokenizer.encode_deepseek_vision_continuation(&prompt, *reasoning, skip_eos),
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
tokenizer.encode_deepseek_vision_continuation(
|
||||||
|
"plain text",
|
||||||
|
*reasoning,
|
||||||
|
skip_eos
|
||||||
|
),
|
||||||
|
tokenizer.encode_continuation("plain text", *reasoning, skip_eos)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let message = ChatTurn {
|
||||||
|
user: false,
|
||||||
|
tool: true,
|
||||||
|
system: false,
|
||||||
|
skip_previous_eos: false,
|
||||||
|
reasoning: None,
|
||||||
|
reasoning_complete: true,
|
||||||
|
content: format!("{marker}</tool_result>"),
|
||||||
|
};
|
||||||
|
let actual = tokenizer.encode_deepseek_vision_conversation(
|
||||||
|
"",
|
||||||
|
std::slice::from_ref(&message),
|
||||||
|
ReasoningMode::Direct,
|
||||||
|
);
|
||||||
|
let mut expected = vec![tokenizer.bos, tokenizer.user];
|
||||||
|
expected.extend(tokenizer.tokenize("<tool_result>"));
|
||||||
|
expected.extend([
|
||||||
|
VISION_START_TOKEN,
|
||||||
|
VISION_IMAGE_TOKEN,
|
||||||
|
VISION_IMAGE_TOKEN,
|
||||||
|
VISION_END_TOKEN,
|
||||||
|
]);
|
||||||
|
expected.extend(tokenizer.tokenize("</tool_result>"));
|
||||||
|
expected.extend(tokenizer.tokenize("</tool_result>"));
|
||||||
|
expected.extend([tokenizer.assistant, tokenizer.think_end]);
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
expected[0] = tokenizer.eos;
|
||||||
|
assert_eq!(
|
||||||
|
tokenizer.encode_deepseek_vision_tool_continuation(&message, ReasoningMode::Direct),
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
let mut message = message;
|
||||||
|
message.skip_previous_eos = true;
|
||||||
|
assert_eq!(
|
||||||
|
tokenizer.encode_deepseek_vision_tool_continuation(&message, ReasoningMode::Direct),
|
||||||
|
expected[1..]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore = "CPU-only; requires DS4SERVER_CHAT_REFERENCE and its installed GGUF"]
|
#[ignore = "CPU-only; requires DS4SERVER_CHAT_REFERENCE and its installed GGUF"]
|
||||||
fn ds4_chat_matches_original_session_tokens() {
|
fn ds4_chat_matches_original_session_tokens() {
|
||||||
|
|||||||
+180
-5
@@ -9,6 +9,7 @@ pub(crate) fn validate_model_artifact(
|
|||||||
let model = Gguf::open(path)?;
|
let model = Gguf::open(path)?;
|
||||||
let shape = match expected {
|
let shape = match expected {
|
||||||
ModelChoice::DeepSeekV4Flash0731 => FLASH_0731,
|
ModelChoice::DeepSeekV4Flash0731 => FLASH_0731,
|
||||||
|
ModelChoice::DeepSeekV4FlashVision => FLASH_VISION,
|
||||||
ModelChoice::DeepSeekV4Pro
|
ModelChoice::DeepSeekV4Pro
|
||||||
| ModelChoice::Glm52
|
| ModelChoice::Glm52
|
||||||
| ModelChoice::Glm53Flash
|
| ModelChoice::Glm53Flash
|
||||||
@@ -40,6 +41,9 @@ pub(crate) fn validate_model_artifact(
|
|||||||
|
|
||||||
pub(crate) fn validate_vision_artifact(path: &Path) -> Result<(), String> {
|
pub(crate) fn validate_vision_artifact(path: &Path) -> Result<(), String> {
|
||||||
let model = Gguf::open(path)?;
|
let model = Gguf::open(path)?;
|
||||||
|
if model.bytes("general.architecture")? == b"deepseek4-vision" {
|
||||||
|
return validate_deepseek_vision(&model);
|
||||||
|
}
|
||||||
if model.bytes("general.architecture")? != b"glm5-next-vision" {
|
if model.bytes("general.architecture")? != b"glm5-next-vision" {
|
||||||
return Err("vision GGUF architecture is not glm5-next-vision".into());
|
return Err("vision GGUF architecture is not glm5-next-vision".into());
|
||||||
}
|
}
|
||||||
@@ -193,11 +197,21 @@ pub(super) fn validate_support(model: &Gguf, shape: &Shape) -> Result<SupportKin
|
|||||||
|
|
||||||
pub(super) fn validate_main(model: &Gguf, expected: ModelChoice) -> Result<Shape, String> {
|
pub(super) fn validate_main(model: &Gguf, expected: ModelChoice) -> Result<Shape, String> {
|
||||||
let architecture = model.bytes("general.architecture")?;
|
let architecture = model.bytes("general.architecture")?;
|
||||||
|
let variant = model.metadata.contains_key("deepseek4.checkpoint_variant");
|
||||||
|
if variant && (architecture != b"deepseek4" || model.u32("deepseek4.block_count")? != 43) {
|
||||||
|
return Err("Vision-Exp metadata requires a DeepSeek V4 Flash checkpoint".into());
|
||||||
|
}
|
||||||
let shape = match architecture {
|
let shape = match architecture {
|
||||||
b"glm-dsa" => GLM,
|
b"glm-dsa" => GLM,
|
||||||
b"glm5-next" => GLM53_FLASH,
|
b"glm5-next" => GLM53_FLASH,
|
||||||
_ => match (model.u32("deepseek4.block_count")?, expected) {
|
_ => match (model.u32("deepseek4.block_count")?, expected) {
|
||||||
(43, ModelChoice::DeepSeekV4Flash0731) => FLASH_0731,
|
(43, _) if variant => {
|
||||||
|
validate_vision_checkpoint(model)?;
|
||||||
|
if !model.boolean("deepseek4.vision.sidecar_required")? {
|
||||||
|
return Err("DeepSeek Vision-Exp requires its sidecar metadata".into());
|
||||||
|
}
|
||||||
|
FLASH_VISION
|
||||||
|
}
|
||||||
(43, _) => FLASH_0731,
|
(43, _) => FLASH_0731,
|
||||||
(61, _) => PRO,
|
(61, _) => PRO,
|
||||||
(layers, _) => return Err(format!("unsupported DeepSeek layer count: {layers}")),
|
(layers, _) => return Err(format!("unsupported DeepSeek layer count: {layers}")),
|
||||||
@@ -210,6 +224,14 @@ pub(super) fn validate_main(model: &Gguf, expected: ModelChoice) -> Result<Shape
|
|||||||
shape.model
|
shape.model
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
if shape.model == ModelChoice::DeepSeekV4FlashVision
|
||||||
|
&& model
|
||||||
|
.f32("deepseek4.attention.layer_norm_rms_epsilon")?
|
||||||
|
.to_bits()
|
||||||
|
!= shape.rms_epsilon.to_bits()
|
||||||
|
{
|
||||||
|
return Err("DeepSeek Vision-Exp RMS epsilon must be 1e-20".into());
|
||||||
|
}
|
||||||
validate_metadata(model, &shape)?;
|
validate_metadata(model, &shape)?;
|
||||||
validate_tensors(model, &shape)?;
|
validate_tensors(model, &shape)?;
|
||||||
Ok(shape)
|
Ok(shape)
|
||||||
@@ -825,9 +847,14 @@ fn validate_glm_tensors(model: &Gguf, shape: &Shape) -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn validate_dspark(model: &Gguf, shape: &Shape) -> Result<(), String> {
|
pub(super) fn validate_dspark(model: &Gguf, shape: &Shape) -> Result<(), String> {
|
||||||
if !matches!(shape.model, ModelChoice::DeepSeekV4Flash0731) {
|
if !shape.model.supports_dspark() {
|
||||||
return Err("DSpark support is available only for DeepSeek V4 Flash".into());
|
return Err("DSpark support is available only for DeepSeek V4 Flash".into());
|
||||||
}
|
}
|
||||||
|
if shape.model == ModelChoice::DeepSeekV4FlashVision {
|
||||||
|
validate_vision_checkpoint(model)?;
|
||||||
|
} else if model.metadata.contains_key("deepseek4.checkpoint_variant") {
|
||||||
|
return Err("Vision-Exp DSpark support cannot be used with another checkpoint".into());
|
||||||
|
}
|
||||||
let DsparkConfig {
|
let DsparkConfig {
|
||||||
block_size,
|
block_size,
|
||||||
markov_rank,
|
markov_rank,
|
||||||
@@ -1060,14 +1087,18 @@ fn float_eq(actual: f32, expected: f32) -> bool {
|
|||||||
|
|
||||||
fn compression_ratio(shape: &Shape, layer: u32) -> u32 {
|
fn compression_ratio(shape: &Shape, layer: u32) -> u32 {
|
||||||
match shape.model {
|
match shape.model {
|
||||||
ModelChoice::DeepSeekV4Flash0731 if layer < 2 => 0,
|
ModelChoice::DeepSeekV4FlashVision | ModelChoice::DeepSeekV4Flash0731 if layer < 2 => 0,
|
||||||
ModelChoice::DeepSeekV4Pro if layer < 2 => 128,
|
ModelChoice::DeepSeekV4Pro if layer < 2 => 128,
|
||||||
ModelChoice::DeepSeekV4Flash0731 | ModelChoice::DeepSeekV4Pro
|
ModelChoice::DeepSeekV4FlashVision
|
||||||
|
| ModelChoice::DeepSeekV4Flash0731
|
||||||
|
| ModelChoice::DeepSeekV4Pro
|
||||||
if layer.is_multiple_of(2) =>
|
if layer.is_multiple_of(2) =>
|
||||||
{
|
{
|
||||||
4
|
4
|
||||||
}
|
}
|
||||||
ModelChoice::DeepSeekV4Flash0731 | ModelChoice::DeepSeekV4Pro => 128,
|
ModelChoice::DeepSeekV4FlashVision
|
||||||
|
| ModelChoice::DeepSeekV4Flash0731
|
||||||
|
| ModelChoice::DeepSeekV4Pro => 128,
|
||||||
ModelChoice::Glm52 | ModelChoice::Glm53Flash | ModelChoice::Qwen38FlashNext => 0,
|
ModelChoice::Glm52 | ModelChoice::Glm53Flash | ModelChoice::Qwen38FlashNext => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1084,10 +1115,154 @@ fn first_u32s<'a>(model: &'a Gguf, keys: &[&str]) -> Result<&'a [u32], String> {
|
|||||||
.ok_or_else(|| format!("required DSpark metadata is missing: {}", keys[0]))
|
.ok_or_else(|| format!("required DSpark metadata is missing: {}", keys[0]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validate_vision_checkpoint(model: &Gguf) -> Result<(), String> {
|
||||||
|
if model.bytes("deepseek4.checkpoint_variant")? != b"vision-exp"
|
||||||
|
|| model.bytes("general.source.revision")? != deepseek_vision::SOURCE_REVISION
|
||||||
|
{
|
||||||
|
return Err(
|
||||||
|
"DeepSeek Vision-Exp checkpoint does not match the pinned source revision".into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_deepseek_vision(model: &Gguf) -> Result<(), String> {
|
||||||
|
if model.bytes("deepseek4-vision.checkpoint_variant")? != b"vision-exp"
|
||||||
|
|| model.bytes("general.source.revision")? != deepseek_vision::SOURCE_REVISION
|
||||||
|
{
|
||||||
|
return Err("DeepSeek vision encoder does not match the pinned checkpoint".into());
|
||||||
|
}
|
||||||
|
if model.tensors.len() != 316 {
|
||||||
|
return Err(format!(
|
||||||
|
"DeepSeek vision encoder has {} tensors, expected 316",
|
||||||
|
model.tensors.len()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
for (key, value) in [
|
||||||
|
("block_count", 32),
|
||||||
|
("embedding_length", 1024),
|
||||||
|
("feed_forward_length", 2816),
|
||||||
|
("attention.head_count", 16),
|
||||||
|
("projection_length", 4096),
|
||||||
|
("patch_size", 14),
|
||||||
|
("downsample_ratio", 3),
|
||||||
|
("image.max_tokens", 384),
|
||||||
|
("image.min_pixels", 147456),
|
||||||
|
("image.max_width_height_ratio", 8),
|
||||||
|
("language.block_count", 43),
|
||||||
|
("language.expert_count", 256),
|
||||||
|
] {
|
||||||
|
expect_u64(model, &format!("deepseek4-vision.{key}"), value)?;
|
||||||
|
}
|
||||||
|
expect_float(
|
||||||
|
model,
|
||||||
|
"deepseek4-vision.attention.layer_norm_rms_epsilon",
|
||||||
|
1e-6,
|
||||||
|
)?;
|
||||||
|
expect_float(model, "deepseek4-vision.rope.freq_base", 10000.0)?;
|
||||||
|
for (name, dims) in [
|
||||||
|
("vision.patch_embed.proj.weight", vec![588, 1024]),
|
||||||
|
("vision.patch_embed.proj.bias", vec![1024]),
|
||||||
|
("vision.norm.weight", vec![1024]),
|
||||||
|
("aligner.w1.weight", vec![9216, 4096]),
|
||||||
|
("aligner.w1.bias", vec![4096]),
|
||||||
|
("aligner.w2.weight", vec![4096, 4096]),
|
||||||
|
("aligner.w2.bias", vec![4096]),
|
||||||
|
("image_start", vec![4096]),
|
||||||
|
("image_pad", vec![4096]),
|
||||||
|
("image_newline", vec![4096]),
|
||||||
|
("image_end", vec![4096]),
|
||||||
|
] {
|
||||||
|
expect(model, name, &[BF16], &dims)?;
|
||||||
|
}
|
||||||
|
for layer in 0..32 {
|
||||||
|
for (suffix, dims) in [
|
||||||
|
("norm1.weight", vec![1024]),
|
||||||
|
("attn.wqkv.weight", vec![1024, 3072]),
|
||||||
|
("attn.wqkv.bias", vec![3072]),
|
||||||
|
("attn.wo.weight", vec![1024, 1024]),
|
||||||
|
("attn.wo.bias", vec![1024]),
|
||||||
|
("norm2.weight", vec![1024]),
|
||||||
|
("mlp.w1.weight", vec![1024, 5632]),
|
||||||
|
("mlp.w2.weight", vec![2816, 1024]),
|
||||||
|
] {
|
||||||
|
expect(
|
||||||
|
model,
|
||||||
|
&format!("vision.blocks.{layer}.{suffix}"),
|
||||||
|
&[BF16],
|
||||||
|
&dims,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for layer in 0..43 {
|
||||||
|
expect(
|
||||||
|
model,
|
||||||
|
&format!("layers.{layer}.ffn.gate.bias_vl"),
|
||||||
|
&[F32],
|
||||||
|
&[256],
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
for stage in 0..3 {
|
||||||
|
expect(
|
||||||
|
model,
|
||||||
|
&format!("mtp.{stage}.ffn.gate.bias_vl"),
|
||||||
|
&[F32],
|
||||||
|
&[256],
|
||||||
|
)?;
|
||||||
|
expect(
|
||||||
|
model,
|
||||||
|
&format!("layers.{stage}.ffn.gate.bias"),
|
||||||
|
&[F32],
|
||||||
|
&[256],
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore = "requires the pinned Vision-Exp encoder and DSpark support GGUFs"]
|
||||||
|
fn issue100_checkpoint_metadata_is_strict() {
|
||||||
|
let root = Path::new("local-eval-results/issue100-artifacts");
|
||||||
|
let mut sidecar = Gguf::open(&root.join("DeepSeek-V4-Flash-Vision-Encoder.gguf")).unwrap();
|
||||||
|
validate_deepseek_vision(&sidecar).unwrap();
|
||||||
|
let revision = sidecar
|
||||||
|
.metadata
|
||||||
|
.insert(
|
||||||
|
"general.source.revision".into(),
|
||||||
|
Value::Bytes(b"wrong".to_vec()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(validate_deepseek_vision(&sidecar).is_err());
|
||||||
|
sidecar
|
||||||
|
.metadata
|
||||||
|
.insert("general.source.revision".into(), revision);
|
||||||
|
sidecar.tensors.remove("layers.42.ffn.gate.bias_vl");
|
||||||
|
assert!(validate_deepseek_vision(&sidecar).is_err());
|
||||||
|
let mut support =
|
||||||
|
Gguf::open(&root.join("DeepSeek-V4-Flash-Vision-Exp-DSpark-support.gguf")).unwrap();
|
||||||
|
assert!(matches!(
|
||||||
|
validate_support(&support, &FLASH_VISION).unwrap(),
|
||||||
|
SupportKind::DSpark
|
||||||
|
));
|
||||||
|
assert!(validate_support(&support, &FLASH_0731).is_err());
|
||||||
|
support.metadata.remove("deepseek4.checkpoint_variant");
|
||||||
|
assert!(validate_support(&support, &FLASH_VISION).is_err());
|
||||||
|
let old = crate::model::engine_artifacts(
|
||||||
|
ModelChoice::DeepSeekV4Flash0731,
|
||||||
|
true,
|
||||||
|
&crate::app::models_path(),
|
||||||
|
)
|
||||||
|
.support
|
||||||
|
.unwrap();
|
||||||
|
if old.exists() {
|
||||||
|
assert!(validate_support(&Gguf::open(&old).unwrap(), &FLASH_VISION).is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn mxfp4_is_limited_to_routed_experts() {
|
fn mxfp4_is_limited_to_routed_experts() {
|
||||||
let tensor = Tensor {
|
let tensor = Tensor {
|
||||||
|
|||||||
@@ -938,6 +938,7 @@ fn model_code(model: ModelChoice) -> u8 {
|
|||||||
ModelChoice::DeepSeekV4Flash0731 => 4,
|
ModelChoice::DeepSeekV4Flash0731 => 4,
|
||||||
ModelChoice::Glm53Flash => 5,
|
ModelChoice::Glm53Flash => 5,
|
||||||
ModelChoice::Qwen38FlashNext => 6,
|
ModelChoice::Qwen38FlashNext => 6,
|
||||||
|
ModelChoice::DeepSeekV4FlashVision => 7,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -949,6 +950,7 @@ fn model_name(value: u8) -> &'static str {
|
|||||||
4 => "DeepSeek V4 Flash 0731",
|
4 => "DeepSeek V4 Flash 0731",
|
||||||
5 => "GLM 5.3 Flash Q2",
|
5 => "GLM 5.3 Flash Q2",
|
||||||
6 => "Qwen 3.8 Flash Next",
|
6 => "Qwen 3.8 Flash Next",
|
||||||
|
7 => "DeepSeek V4 Flash Vision Experimental",
|
||||||
_ => "No model loaded",
|
_ => "No model loaded",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+66
-5
@@ -10,16 +10,20 @@ use std::fmt;
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
pub(crate) const MODEL_CHOICES: [ModelChoice; 5] = [
|
pub(crate) const MODEL_CHOICES: [ModelChoice; 6] = [
|
||||||
ModelChoice::DeepSeekV4Flash0731,
|
ModelChoice::DeepSeekV4Flash0731,
|
||||||
|
ModelChoice::DeepSeekV4FlashVision,
|
||||||
ModelChoice::DeepSeekV4Pro,
|
ModelChoice::DeepSeekV4Pro,
|
||||||
ModelChoice::Glm52,
|
ModelChoice::Glm52,
|
||||||
ModelChoice::Glm53Flash,
|
ModelChoice::Glm53Flash,
|
||||||
ModelChoice::Qwen38FlashNext,
|
ModelChoice::Qwen38FlashNext,
|
||||||
];
|
];
|
||||||
pub(crate) const MANAGED_ARTIFACTS: [ManagedArtifactId; 8] = [
|
pub(crate) const MANAGED_ARTIFACTS: [ManagedArtifactId; 11] = [
|
||||||
ManagedArtifactId::DeepSeekV4Flash0731,
|
ManagedArtifactId::DeepSeekV4Flash0731,
|
||||||
ManagedArtifactId::DeepSeekV4Flash0731Dspark,
|
ManagedArtifactId::DeepSeekV4Flash0731Dspark,
|
||||||
|
ManagedArtifactId::DeepSeekV4FlashVision,
|
||||||
|
ManagedArtifactId::DeepSeekV4FlashVisionEncoder,
|
||||||
|
ManagedArtifactId::DeepSeekV4FlashVisionDspark,
|
||||||
ManagedArtifactId::DeepSeekV4Pro,
|
ManagedArtifactId::DeepSeekV4Pro,
|
||||||
ManagedArtifactId::Glm52,
|
ManagedArtifactId::Glm52,
|
||||||
ManagedArtifactId::Glm53Flash,
|
ManagedArtifactId::Glm53Flash,
|
||||||
@@ -50,6 +54,33 @@ const FLASH_0731_DSPARK: Artifact = Artifact {
|
|||||||
support: Some(true),
|
support: Some(true),
|
||||||
vision: false,
|
vision: false,
|
||||||
};
|
};
|
||||||
|
const FLASH_VISION: Artifact = Artifact {
|
||||||
|
label: "DeepSeek V4 Flash Vision Experimental model",
|
||||||
|
file_name: "DeepSeek-V4-Flash-Vision-Exp-IQ2XXS-w2Q2K-AProjQ8-SExpQ8-OutQ8.gguf",
|
||||||
|
repository: DEEPSEEK_REPOSITORY,
|
||||||
|
size: 86_720_111_776,
|
||||||
|
sha256: "8f2d42c0071ccf8a98f391cc2b835fd123f12330690b3059dbb7707920e5ad9e",
|
||||||
|
support: Some(false),
|
||||||
|
vision: false,
|
||||||
|
};
|
||||||
|
const FLASH_VISION_ENCODER: Artifact = Artifact {
|
||||||
|
label: "DeepSeek V4 Flash Vision Experimental encoder",
|
||||||
|
file_name: "DeepSeek-V4-Flash-Vision-Encoder.gguf",
|
||||||
|
repository: DEEPSEEK_REPOSITORY,
|
||||||
|
size: 932_857_760,
|
||||||
|
sha256: "00cd4d81a435364967400a95c42703343e11da6b6f18c5143fe76e1d94d5035f",
|
||||||
|
support: None,
|
||||||
|
vision: true,
|
||||||
|
};
|
||||||
|
const FLASH_VISION_DSPARK: Artifact = Artifact {
|
||||||
|
label: "DeepSeek V4 Flash Vision Experimental DSpark support",
|
||||||
|
file_name: "DeepSeek-V4-Flash-Vision-Exp-DSpark-support.gguf",
|
||||||
|
repository: DEEPSEEK_REPOSITORY,
|
||||||
|
size: 5_989_114_528,
|
||||||
|
sha256: "0807a67fd9ce5874bfc60d8d2461f50e11657e3dd94913d3473f85aa679bc877",
|
||||||
|
support: Some(true),
|
||||||
|
vision: false,
|
||||||
|
};
|
||||||
const PRO: Artifact = Artifact {
|
const PRO: Artifact = Artifact {
|
||||||
label: "DeepSeek V4 Pro 0813 model",
|
label: "DeepSeek V4 Pro 0813 model",
|
||||||
file_name: "DeepSeek-V4-Pro-IQ2XXS-w2Q2K-AProjQ8-SExpQ8-OutQ8-Instruct-imatrix-0813.gguf",
|
file_name: "DeepSeek-V4-Pro-IQ2XXS-w2Q2K-AProjQ8-SExpQ8-OutQ8-Instruct-imatrix-0813.gguf",
|
||||||
@@ -92,6 +123,8 @@ pub(crate) enum ModelChoice {
|
|||||||
#[default]
|
#[default]
|
||||||
#[serde(rename = "deepseek-v4-flash-0731")]
|
#[serde(rename = "deepseek-v4-flash-0731")]
|
||||||
DeepSeekV4Flash0731,
|
DeepSeekV4Flash0731,
|
||||||
|
#[serde(rename = "deepseek-v4-flash-vision-exp")]
|
||||||
|
DeepSeekV4FlashVision,
|
||||||
#[serde(rename = "deepseek-v4-pro")]
|
#[serde(rename = "deepseek-v4-pro")]
|
||||||
DeepSeekV4Pro,
|
DeepSeekV4Pro,
|
||||||
#[serde(rename = "glm-5.2")]
|
#[serde(rename = "glm-5.2")]
|
||||||
@@ -106,6 +139,7 @@ impl ModelChoice {
|
|||||||
pub(crate) fn id(self) -> &'static str {
|
pub(crate) fn id(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::DeepSeekV4Flash0731 => "deepseek-v4-flash-0731",
|
Self::DeepSeekV4Flash0731 => "deepseek-v4-flash-0731",
|
||||||
|
Self::DeepSeekV4FlashVision => "deepseek-v4-flash-vision-exp",
|
||||||
Self::DeepSeekV4Pro => "deepseek-v4-pro",
|
Self::DeepSeekV4Pro => "deepseek-v4-pro",
|
||||||
Self::Glm52 => "glm-5.2",
|
Self::Glm52 => "glm-5.2",
|
||||||
Self::Glm53Flash => "glm-5.3-flash",
|
Self::Glm53Flash => "glm-5.3-flash",
|
||||||
@@ -118,7 +152,10 @@ impl ModelChoice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn supports_dspark(self) -> bool {
|
pub(crate) fn supports_dspark(self) -> bool {
|
||||||
self == Self::DeepSeekV4Flash0731
|
matches!(
|
||||||
|
self,
|
||||||
|
Self::DeepSeekV4Flash0731 | Self::DeepSeekV4FlashVision
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_glm(self) -> bool {
|
pub(crate) fn is_glm(self) -> bool {
|
||||||
@@ -130,7 +167,10 @@ impl ModelChoice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn supports_vision(self) -> bool {
|
pub(crate) fn supports_vision(self) -> bool {
|
||||||
matches!(self, Self::Glm53Flash | Self::Qwen38FlashNext)
|
matches!(
|
||||||
|
self,
|
||||||
|
Self::DeepSeekV4FlashVision | Self::Glm53Flash | Self::Qwen38FlashNext
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn supports_integrated_mtp(self) -> bool {
|
pub(crate) fn supports_integrated_mtp(self) -> bool {
|
||||||
@@ -148,6 +188,7 @@ impl ModelChoice {
|
|||||||
fn main_artifact(self) -> &'static Artifact {
|
fn main_artifact(self) -> &'static Artifact {
|
||||||
match self {
|
match self {
|
||||||
Self::DeepSeekV4Flash0731 => &FLASH_0731,
|
Self::DeepSeekV4Flash0731 => &FLASH_0731,
|
||||||
|
Self::DeepSeekV4FlashVision => &FLASH_VISION,
|
||||||
Self::DeepSeekV4Pro => &PRO,
|
Self::DeepSeekV4Pro => &PRO,
|
||||||
Self::Glm52 => &GLM,
|
Self::Glm52 => &GLM,
|
||||||
Self::Glm53Flash => &GLM53_FLASH,
|
Self::Glm53Flash => &GLM53_FLASH,
|
||||||
@@ -158,12 +199,17 @@ impl ModelChoice {
|
|||||||
fn dspark_artifact(self) -> Option<&'static Artifact> {
|
fn dspark_artifact(self) -> Option<&'static Artifact> {
|
||||||
match self {
|
match self {
|
||||||
Self::DeepSeekV4Flash0731 => Some(&FLASH_0731_DSPARK),
|
Self::DeepSeekV4Flash0731 => Some(&FLASH_0731_DSPARK),
|
||||||
|
Self::DeepSeekV4FlashVision => Some(&FLASH_VISION_DSPARK),
|
||||||
Self::DeepSeekV4Pro | Self::Glm52 | Self::Glm53Flash | Self::Qwen38FlashNext => None,
|
Self::DeepSeekV4Pro | Self::Glm52 | Self::Glm53Flash | Self::Qwen38FlashNext => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vision_artifact(self) -> Option<&'static Artifact> {
|
fn vision_artifact(self) -> Option<&'static Artifact> {
|
||||||
(self == Self::Glm53Flash).then_some(&GLM53_FLASH_VISION)
|
match self {
|
||||||
|
Self::DeepSeekV4FlashVision => Some(&FLASH_VISION_ENCODER),
|
||||||
|
Self::Glm53Flash => Some(&GLM53_FLASH_VISION),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -252,6 +298,9 @@ pub(crate) fn validate_engine_artifacts(
|
|||||||
path.display()
|
path.display()
|
||||||
)),
|
)),
|
||||||
}?;
|
}?;
|
||||||
|
if model == ModelChoice::DeepSeekV4FlashVision && artifacts.vision.is_none() {
|
||||||
|
return Err(format!("{model} requires its vision encoder sidecar"));
|
||||||
|
}
|
||||||
if let Some(path) = artifacts.vision.as_deref() {
|
if let Some(path) = artifacts.vision.as_deref() {
|
||||||
model
|
model
|
||||||
.vision_artifact()
|
.vision_artifact()
|
||||||
@@ -263,6 +312,9 @@ pub(crate) fn validate_engine_artifacts(
|
|||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
pub(crate) enum ManagedArtifactId {
|
pub(crate) enum ManagedArtifactId {
|
||||||
|
DeepSeekV4FlashVision,
|
||||||
|
DeepSeekV4FlashVisionEncoder,
|
||||||
|
DeepSeekV4FlashVisionDspark,
|
||||||
DeepSeekV4Flash0731,
|
DeepSeekV4Flash0731,
|
||||||
DeepSeekV4Flash0731Dspark,
|
DeepSeekV4Flash0731Dspark,
|
||||||
DeepSeekV4Pro,
|
DeepSeekV4Pro,
|
||||||
@@ -283,6 +335,9 @@ impl ManagedArtifactId {
|
|||||||
Self::DeepSeekV4Flash0731 | Self::DeepSeekV4Flash0731Dspark => {
|
Self::DeepSeekV4Flash0731 | Self::DeepSeekV4Flash0731Dspark => {
|
||||||
ModelChoice::DeepSeekV4Flash0731
|
ModelChoice::DeepSeekV4Flash0731
|
||||||
}
|
}
|
||||||
|
Self::DeepSeekV4FlashVision
|
||||||
|
| Self::DeepSeekV4FlashVisionEncoder
|
||||||
|
| Self::DeepSeekV4FlashVisionDspark => ModelChoice::DeepSeekV4FlashVision,
|
||||||
Self::DeepSeekV4Pro => ModelChoice::DeepSeekV4Pro,
|
Self::DeepSeekV4Pro => ModelChoice::DeepSeekV4Pro,
|
||||||
Self::Glm52 => ModelChoice::Glm52,
|
Self::Glm52 => ModelChoice::Glm52,
|
||||||
Self::Glm53Flash | Self::Glm53FlashVision => ModelChoice::Glm53Flash,
|
Self::Glm53Flash | Self::Glm53FlashVision => ModelChoice::Glm53Flash,
|
||||||
@@ -293,7 +348,10 @@ impl ManagedArtifactId {
|
|||||||
fn artifact(self) -> &'static Artifact {
|
fn artifact(self) -> &'static Artifact {
|
||||||
match self {
|
match self {
|
||||||
Self::DeepSeekV4Flash0731 => &FLASH_0731,
|
Self::DeepSeekV4Flash0731 => &FLASH_0731,
|
||||||
|
Self::DeepSeekV4FlashVision => &FLASH_VISION,
|
||||||
Self::DeepSeekV4Flash0731Dspark => &FLASH_0731_DSPARK,
|
Self::DeepSeekV4Flash0731Dspark => &FLASH_0731_DSPARK,
|
||||||
|
Self::DeepSeekV4FlashVisionEncoder => &FLASH_VISION_ENCODER,
|
||||||
|
Self::DeepSeekV4FlashVisionDspark => &FLASH_VISION_DSPARK,
|
||||||
Self::DeepSeekV4Pro => &PRO,
|
Self::DeepSeekV4Pro => &PRO,
|
||||||
Self::Glm52 => &GLM,
|
Self::Glm52 => &GLM,
|
||||||
Self::Glm53Flash => &GLM53_FLASH,
|
Self::Glm53Flash => &GLM53_FLASH,
|
||||||
@@ -397,6 +455,7 @@ impl fmt::Display for ModelChoice {
|
|||||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
formatter.write_str(match self {
|
formatter.write_str(match self {
|
||||||
Self::DeepSeekV4Flash0731 => "DeepSeek V4 Flash 0731",
|
Self::DeepSeekV4Flash0731 => "DeepSeek V4 Flash 0731",
|
||||||
|
Self::DeepSeekV4FlashVision => "DeepSeek V4 Flash Vision Experimental",
|
||||||
Self::DeepSeekV4Pro => "DeepSeek V4 Pro 0813",
|
Self::DeepSeekV4Pro => "DeepSeek V4 Pro 0813",
|
||||||
Self::Glm52 => "GLM 5.2",
|
Self::Glm52 => "GLM 5.2",
|
||||||
Self::Glm53Flash => "GLM 5.3 Flash Q2",
|
Self::Glm53Flash => "GLM 5.3 Flash Q2",
|
||||||
@@ -537,6 +596,8 @@ pub(crate) fn installed_models(models_path: &Path) -> Vec<ModelChoice> {
|
|||||||
qwen::is_installed(&qwen::root(models_path))
|
qwen::is_installed(&qwen::root(models_path))
|
||||||
} else {
|
} else {
|
||||||
model.main_artifact().is_installed(*model, models_path)
|
model.main_artifact().is_installed(*model, models_path)
|
||||||
|
&& (*model != ModelChoice::DeepSeekV4FlashVision
|
||||||
|
|| FLASH_VISION_ENCODER.is_installed(*model, models_path))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
|||||||
+95
-2
@@ -492,8 +492,8 @@ mod tests {
|
|||||||
Some(ModelChoice::DeepSeekV4Flash0731)
|
Some(ModelChoice::DeepSeekV4Flash0731)
|
||||||
);
|
);
|
||||||
assert!(ModelChoice::from_id("unknown").is_none());
|
assert!(ModelChoice::from_id("unknown").is_none());
|
||||||
assert_eq!(MODEL_CHOICES.len(), 5);
|
assert_eq!(MODEL_CHOICES.len(), 6);
|
||||||
assert_eq!(MANAGED_ARTIFACTS.len(), 8);
|
assert_eq!(MANAGED_ARTIFACTS.len(), 11);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ModelChoice::from_id("qwen3.8-flash-next"),
|
ModelChoice::from_id("qwen3.8-flash-next"),
|
||||||
Some(ModelChoice::Qwen38FlashNext)
|
Some(ModelChoice::Qwen38FlashNext)
|
||||||
@@ -642,6 +642,99 @@ mod tests {
|
|||||||
fs::remove_dir_all(models).unwrap();
|
fs::remove_dir_all(models).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn issue100_catalog_keeps_vision_and_dspark_checkpoint_specific() {
|
||||||
|
let model = ModelChoice::DeepSeekV4FlashVision;
|
||||||
|
assert_eq!(ModelChoice::from_id(model.id()), Some(model));
|
||||||
|
assert_eq!(
|
||||||
|
serde_json::from_str::<ModelChoice>(&serde_json::to_string(&model).unwrap()).unwrap(),
|
||||||
|
model
|
||||||
|
);
|
||||||
|
assert!(model.supports_vision() && model.supports_dspark());
|
||||||
|
assert!(!model.supports_integrated_mtp());
|
||||||
|
assert_eq!(model.artifacts(true).count(), 3);
|
||||||
|
assert_eq!(model.artifacts(false).count(), 2);
|
||||||
|
assert_ne!(
|
||||||
|
model.dspark_artifact().unwrap().sha256,
|
||||||
|
FLASH_0731_DSPARK.sha256
|
||||||
|
);
|
||||||
|
let defaults = crate::config::ModelPreferences::defaults_for(model);
|
||||||
|
assert!(defaults.speculative.dspark_enabled);
|
||||||
|
assert!(defaults.speculative.validate(model).is_ok());
|
||||||
|
let root = std::env::temp_dir().join(format!("issue100-catalog-{}", std::process::id()));
|
||||||
|
fs::create_dir_all(root.join(model.id())).unwrap();
|
||||||
|
// Sparse files exercise catalog and marker bookkeeping only. Real GGUF
|
||||||
|
// content and hashes are checked by the separate managed-intake test.
|
||||||
|
for artifact in [&FLASH_VISION, &FLASH_VISION_DSPARK] {
|
||||||
|
File::create(artifact.path(model, &root))
|
||||||
|
.unwrap()
|
||||||
|
.set_len(artifact.size)
|
||||||
|
.unwrap();
|
||||||
|
mark_verified(model, artifact, &root).unwrap();
|
||||||
|
}
|
||||||
|
assert!(!installed_models(&root).contains(&model));
|
||||||
|
let missing = engine_artifacts(model, true, &root);
|
||||||
|
assert!(
|
||||||
|
validate_engine_artifacts(model, true, &missing)
|
||||||
|
.unwrap_err()
|
||||||
|
.contains("requires its vision encoder")
|
||||||
|
);
|
||||||
|
File::create(FLASH_VISION_ENCODER.path(model, &root))
|
||||||
|
.unwrap()
|
||||||
|
.set_len(FLASH_VISION_ENCODER.size)
|
||||||
|
.unwrap();
|
||||||
|
mark_verified(model, &FLASH_VISION_ENCODER, &root).unwrap();
|
||||||
|
assert_eq!(installed_models(&root), [model]);
|
||||||
|
let mut artifacts = engine_artifacts(model, true, &root);
|
||||||
|
validate_engine_artifacts(model, true, &artifacts).unwrap();
|
||||||
|
artifacts.support = Some(FLASH_0731_DSPARK.path(ModelChoice::DeepSeekV4Flash0731, &root));
|
||||||
|
assert!(
|
||||||
|
validate_engine_artifacts(model, true, &artifacts)
|
||||||
|
.unwrap_err()
|
||||||
|
.contains("not the expected")
|
||||||
|
);
|
||||||
|
artifacts = engine_artifacts(model, false, &root);
|
||||||
|
artifacts.vision = Some(GLM53_FLASH_VISION.path(ModelChoice::Glm53Flash, &root));
|
||||||
|
assert!(
|
||||||
|
validate_engine_artifacts(model, false, &artifacts)
|
||||||
|
.unwrap_err()
|
||||||
|
.contains("not the expected")
|
||||||
|
);
|
||||||
|
fs::remove_dir_all(root).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore = "requires all three pinned Vision-Exp GGUF artifacts; performs full SHA256 and intake validation"]
|
||||||
|
fn issue100_managed_intake() {
|
||||||
|
let source = Path::new("local-eval-results/issue100-artifacts");
|
||||||
|
let root = Path::new("local-eval-results/issue100-managed");
|
||||||
|
let model = ModelChoice::DeepSeekV4FlashVision;
|
||||||
|
fs::create_dir_all(root.join(model.id())).unwrap();
|
||||||
|
for id in [
|
||||||
|
ManagedArtifactId::DeepSeekV4FlashVisionEncoder,
|
||||||
|
ManagedArtifactId::DeepSeekV4FlashVisionDspark,
|
||||||
|
ManagedArtifactId::DeepSeekV4FlashVision,
|
||||||
|
] {
|
||||||
|
let artifact = id.artifact();
|
||||||
|
let destination = artifact.path(model, root);
|
||||||
|
if !destination.exists() {
|
||||||
|
fs::hard_link(source.join(artifact.file_name), &destination).unwrap();
|
||||||
|
}
|
||||||
|
let verified = AtomicU64::new(0);
|
||||||
|
assert_eq!(
|
||||||
|
validate_managed_artifact(id, root, &AtomicBool::new(false), &verified).unwrap(),
|
||||||
|
DownloadOutcome::Complete
|
||||||
|
);
|
||||||
|
assert_eq!(verified.load(Ordering::Relaxed), artifact.size);
|
||||||
|
eprintln!("verified {} {} bytes", artifact.file_name, artifact.size);
|
||||||
|
}
|
||||||
|
assert_eq!(installed_models(root), [model]);
|
||||||
|
for dspark in [false, true] {
|
||||||
|
validate_engine_artifacts(model, dspark, &engine_artifacts(model, dspark, root))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn verification_reports_bytes_read() {
|
fn verification_reports_bytes_read() {
|
||||||
let path = std::env::temp_dir().join(format!(
|
let path = std::env::temp_dir().join(format!(
|
||||||
|
|||||||
+1
-1
@@ -249,7 +249,7 @@ fn print_usage() {
|
|||||||
println!(
|
println!(
|
||||||
"Headless model evaluation\n\n\
|
"Headless model evaluation\n\n\
|
||||||
Usage: ds4-server model-eval --model MODEL [OPTIONS]\n\n\
|
Usage: ds4-server model-eval --model MODEL [OPTIONS]\n\n\
|
||||||
Models: deepseek-v4-flash-0731, deepseek-v4-pro, glm-5.2, glm-5.3-flash, qwen3.8-flash-next\n\n\
|
Models: deepseek-v4-flash-0731, deepseek-v4-flash-vision-exp, deepseek-v4-pro, glm-5.2, glm-5.3-flash, qwen3.8-flash-next\n\n\
|
||||||
Options:\n\
|
Options:\n\
|
||||||
--prompt TEXT Chat turn; repeat for one ongoing conversation\n\
|
--prompt TEXT Chat turn; repeat for one ongoing conversation\n\
|
||||||
(default: autonomous short story)\n\
|
(default: autonomous short story)\n\
|
||||||
|
|||||||
+7
-3
@@ -52,7 +52,7 @@ impl SpeculativePreferences {
|
|||||||
return Err("Integrated MTP is unavailable for the selected model.".into());
|
return Err("Integrated MTP is unavailable for the selected model.".into());
|
||||||
}
|
}
|
||||||
if self.keep_vision_loaded && !model.supports_vision() {
|
if self.keep_vision_loaded && !model.supports_vision() {
|
||||||
return Err("Persistent vision weights are available only for GLM 5.3 Flash and Qwen 3.8 Flash Next.".into());
|
return Err("Persistent vision weights require a supported vision model.".into());
|
||||||
}
|
}
|
||||||
if self.dspark_enabled && !model.supports_dspark() {
|
if self.dspark_enabled && !model.supports_dspark() {
|
||||||
return Err("DSpark is not available for the selected model.".into());
|
return Err("DSpark is not available for the selected model.".into());
|
||||||
@@ -543,7 +543,9 @@ impl ReasoningMode {
|
|||||||
impl ModelChoice {
|
impl ModelChoice {
|
||||||
pub(crate) fn reasoning_modes(self) -> &'static [ReasoningMode] {
|
pub(crate) fn reasoning_modes(self) -> &'static [ReasoningMode] {
|
||||||
match self {
|
match self {
|
||||||
Self::DeepSeekV4Flash0731 | Self::DeepSeekV4Pro => &DEEPSEEK_REASONING_MODES,
|
Self::DeepSeekV4Flash0731 | Self::DeepSeekV4FlashVision | Self::DeepSeekV4Pro => {
|
||||||
|
&DEEPSEEK_REASONING_MODES
|
||||||
|
}
|
||||||
Self::Glm52 => &GLM_52_REASONING_MODES,
|
Self::Glm52 => &GLM_52_REASONING_MODES,
|
||||||
Self::Glm53Flash => &GLM_53_REASONING_MODES,
|
Self::Glm53Flash => &GLM_53_REASONING_MODES,
|
||||||
Self::Qwen38FlashNext => &QWEN_REASONING_MODES,
|
Self::Qwen38FlashNext => &QWEN_REASONING_MODES,
|
||||||
@@ -552,7 +554,9 @@ impl ModelChoice {
|
|||||||
|
|
||||||
pub(crate) fn default_reasoning_mode(self) -> ReasoningMode {
|
pub(crate) fn default_reasoning_mode(self) -> ReasoningMode {
|
||||||
match self {
|
match self {
|
||||||
Self::DeepSeekV4Flash0731 | Self::DeepSeekV4Pro => ReasoningMode::Low,
|
Self::DeepSeekV4Flash0731 | Self::DeepSeekV4FlashVision | Self::DeepSeekV4Pro => {
|
||||||
|
ReasoningMode::Low
|
||||||
|
}
|
||||||
Self::Glm52 | Self::Glm53Flash => ReasoningMode::Max,
|
Self::Glm52 | Self::Glm53Flash => ReasoningMode::Max,
|
||||||
Self::Qwen38FlashNext => ReasoningMode::Low,
|
Self::Qwen38FlashNext => ReasoningMode::Low,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Reference-only check of Vision-Exp weights and the official PyTorch image graph.
|
||||||
|
|
||||||
|
Production code never invokes this script. Inputs are the pinned DS4 source and
|
||||||
|
DeepSeek source revisions recorded in docs/issue100-vision, and native dumps
|
||||||
|
from the ignored issue100_encoder_real_weights test.
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import importlib.util
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
import sys
|
||||||
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
|
||||||
|
p = argparse.ArgumentParser()
|
||||||
|
p.add_argument('--ds4-source', type=Path, required=True)
|
||||||
|
p.add_argument('--official-source', type=Path, required=True)
|
||||||
|
p.add_argument('--artifacts', type=Path, required=True)
|
||||||
|
p.add_argument('--native', type=Path, required=True)
|
||||||
|
p.add_argument('--image', type=Path, required=True)
|
||||||
|
p.add_argument('--device', default='mps')
|
||||||
|
a = p.parse_args()
|
||||||
|
parser_path = a.ds4_source / 'gguf-tools/mixed/splice_mixed_expert_layers_gguf.py'
|
||||||
|
spec = importlib.util.spec_from_file_location('ds4_gguf', parser_path)
|
||||||
|
gguf = importlib.util.module_from_spec(spec)
|
||||||
|
sys.modules[spec.name] = gguf
|
||||||
|
spec.loader.exec_module(gguf)
|
||||||
|
sys.path.insert(0, str(a.official_source / 'inference'))
|
||||||
|
from vision import ViT, Aligner
|
||||||
|
from image_processor import load_image, build_image_block
|
||||||
|
|
||||||
|
config = json.loads((a.official_source / 'config.json').read_text())
|
||||||
|
config['dim'] = config['hidden_size']
|
||||||
|
args = SimpleNamespace(**config)
|
||||||
|
file = a.artifacts / 'DeepSeek-V4-Flash-Vision-Encoder.gguf'
|
||||||
|
gguf.GGML_QUANT_SIZES[30] = (1, 2, "BF16") # GGML BF16, raw source weights.
|
||||||
|
info = gguf.parse_gguf(file)
|
||||||
|
torch.set_default_dtype(torch.bfloat16)
|
||||||
|
torch.set_default_device(a.device)
|
||||||
|
vit, aligner = ViT(args), Aligner(args)
|
||||||
|
with file.open('rb') as f:
|
||||||
|
def state(prefix):
|
||||||
|
result = {}
|
||||||
|
for name, t in info.tensor_by_name.items():
|
||||||
|
if not name.startswith(prefix):
|
||||||
|
continue
|
||||||
|
assert t.ggml_type == 30, (name, t.ggml_type)
|
||||||
|
f.seek(t.data_offset)
|
||||||
|
values = np.frombuffer(f.read(t.n_bytes), dtype='<u2').copy()
|
||||||
|
weight = torch.from_numpy(values).view(torch.bfloat16).reshape(tuple(reversed(t.dims)))
|
||||||
|
result[name.removeprefix(prefix)] = weight.to(a.device)
|
||||||
|
return result
|
||||||
|
vit.load_state_dict(state('vision.'), strict=True)
|
||||||
|
aligner.load_state_dict(state('aligner.'), strict=True)
|
||||||
|
vit.eval(); aligner.eval()
|
||||||
|
patches, h, w, lh, lw = load_image({'url': str(a.image)}, args)
|
||||||
|
meta = json.loads(a.native.with_suffix('.json').read_text())
|
||||||
|
assert meta['grid'] == [h, w]
|
||||||
|
assert meta['aligned_grid'] == [lh, lw]
|
||||||
|
with torch.inference_mode():
|
||||||
|
output = aligner(vit(patches.to(a.device), h, w), h, w).float().cpu().numpy()
|
||||||
|
native = np.fromfile(a.native.with_suffix('.embedding.f32'), dtype='<f4').reshape(output.shape)
|
||||||
|
error = native - output
|
||||||
|
result = {
|
||||||
|
'source_revision': 'e46e16bf6035c6f317eb2ac7458eb0362926d402',
|
||||||
|
'ds4_revision': '0aaea5a238fb41a35106a551e73c8409dfb751ac',
|
||||||
|
'torch': torch.__version__, 'device': a.device,
|
||||||
|
'shape': list(output.shape),
|
||||||
|
'max_abs': float(np.abs(error).max()),
|
||||||
|
'rmse': float(np.sqrt(np.mean(error**2))),
|
||||||
|
'reference_rms': float(np.sqrt(np.mean(output**2))),
|
||||||
|
'cosine': float(np.dot(native.ravel(), output.ravel()) / (np.linalg.norm(native) * np.linalg.norm(output))),
|
||||||
|
}
|
||||||
|
# DS4 uses FP32 patch inputs and its explicit BF16 rounding points, whereas
|
||||||
|
# the published Python graph uses BF16 module operations. Record that numerical
|
||||||
|
# comparison as such; it is not a claim of bitwise DS4 oracle equivalence.
|
||||||
|
assert np.isfinite(native).all() and np.isfinite(output).all()
|
||||||
|
assert result['cosine'] > .99, result
|
||||||
|
print(json.dumps(result), flush=True)
|
||||||
Reference in new issue
Block a user