Three crates ported. All 440 tests pass across Phase 1+2+3.
jouleclaw-backend-reference
Ported from pattern-lang/joule-backend-reference (~3556 LOC, 17 files).
Pure-Rust scalar kernels — the determinism oracle every other backend
validates against. Activation, conv1d, elementwise, lookup (incl. bit
and ternary), matmul (incl. bit, q8_0, stq1_0, ternary), norm, rope,
sample, shape, softmax.
jouleclaw-backend-apple
Ported from pattern-lang/joule-backend-apple (~1116 LOC, 4 files).
Accelerate (cblas_sgemm via direct FFI; build.rs links the
framework), Metal compute kernels, MPSMatrixMultiplication. On M5,
MPS matmul auto-routes through the GPU's Neural Accelerators
(Tensor APIs). Two Rust-2024 extern-block fixes (unsafe extern "C")
in accelerate.rs and mps_matmul.rs.
jouleclaw-runtime
Ported from pattern-lang/joule-runtime (~10218 LOC, 10 files).
The runtime kernel: chat, embed, generate, streaming, compile,
execute, validate, plus the speculative-decoding primitives
(drafter, PLD = prompt lookup decoding). Wires the cascade
(jouleclaw-cascade), the topology (jouleclaw-topology), the format
loader (jouleclaw-loader-gguf), and both backends together.
One Rust-2024 binding-mode fix in compile.rs (|(_, &d)| d == 0 →
|(_, d)| **d == 0). Three types in jouleclaw-loader-gguf
(KvQuant, KvSnapshot, ShortConvStateSnapshot) gained
serde::{Serialize, Deserialize} derives so the runtime's
ConversationCheckpoint can be serialized; serde added to the
loader-gguf Cargo.toml.
Day-one model coverage inherits everything mistral.rs's parent
loaders support — Llama (incl. Llama 4), Mistral (incl. Small 4),
Qwen (3, 3-VL, 3 MoE), Gemma (incl. Gemma 4 with MTP drafter), Phi
(incl. Phi-4 MM), DeepSeek V3, Granite 4 — through this runtime's
PLD + drafter paths.
31 integration test files cover: alias-energy, blocks, boot
validation, causal, chat, chunked prefill, concat-and-offset, embed,
end-to-end model, generation (incl. inplace), GQA, inplace-alias
proof, integration, KV cache (incl. inplace), multihead, prefix
cache, real Bonsai (incl. streaming), real Liquid LFM2, real Tencent
HYMT, real TinyLlama, repeat, rope-and-scaling, sampling features,
scatter (incl. inplace), shape, slice (incl. slice-attention),
streaming, text-to-logits, validation.
Phase 4 next: the model tiers — gemma4, liquid (CfC SSM), prism (1-bit
ternary BitNet), mrl (Matryoshka embeddings), lmm (vision/text/audio),
ebm (energy-based constraint satisfaction). All depend on the runtime
+ backends shipped in this commit.
Deferred to later phases: HAL from efficient-genai + the discrete-GPU
backends (CUDA, ROCm, Vulkan) — these are needed for Phase 5
omni-modal (diffusion, stable-diffusion, gaussian3d, video), not for
Phase 4 text/code/SSM model tiers which run on Apple + reference.
Co-Authored-By: Claude Opus 4.7 <[email protected]>
85 lines
3.4 KiB
Rust
85 lines
3.4 KiB
Rust
//! End-to-end streaming oracle: multi-token generation of
|
|
//! `Ternary-Bonsai-1.7B-Q2_0` through the actual KV-cache decode path
|
|
//! (`Conversation::extend` → `run_inplace_step` →
|
|
//! `build_decode_step_graph_inplace` → ternary kernels).
|
|
//!
|
|
//! `real_bonsai.rs` validates *prefill* only (one forward, one logit
|
|
//! row, one greedy token). This validates *generation* — repeated
|
|
//! decode steps consuming the cache and the ternary routing inside the
|
|
//! in-place block builder. If the decode path's `wmm` / `lookup_w`
|
|
//! routing is wrong, the continuation will be incoherent.
|
|
//!
|
|
//! `#[ignore]` — needs the model on disk. Run:
|
|
//!
|
|
//! cargo test --release -p jouleclaw-runtime --test real_bonsai_streaming \
|
|
//! -- --ignored --nocapture
|
|
|
|
use jouleclaw_loader_gguf::read_gguf_file;
|
|
use jouleclaw_loader_gguf::sample::SamplingConfig;
|
|
use jouleclaw_loader_gguf::tokenizer::Vocab;
|
|
use jouleclaw_runtime::generate::{GenerateConfig, KvCacheKind, TokenizerKind};
|
|
use jouleclaw_runtime::streaming::Conversation;
|
|
use jouleclaw_runtime::Runtime;
|
|
use std::time::Instant;
|
|
|
|
fn model_path() -> String {
|
|
std::env::var("JOULE_BONSAI")
|
|
.unwrap_or_else(|_| "../../models/ternary-bonsai-1.7b-q2_0.gguf".to_string())
|
|
}
|
|
|
|
#[test]
|
|
#[ignore]
|
|
fn real_bonsai_streams_paris() {
|
|
let path = model_path();
|
|
eprintln!("loading {}", path);
|
|
let t0 = Instant::now();
|
|
let model = read_gguf_file(&path).expect("read_gguf_file");
|
|
eprintln!(" parsed GGUF in {:?}", t0.elapsed());
|
|
let vocab = Vocab::from_gguf(&model).expect("Vocab::from_gguf");
|
|
|
|
// Reference-only runtime: the AppleAmx f32 drift is a documented
|
|
// pre-existing bug; the ternary kernels live only in the reference
|
|
// backend anyway, so this is the path that's actually under test.
|
|
let mut conv = Conversation::with_runtime(
|
|
&model, &vocab, 64, Runtime::reference_only(),
|
|
).expect("Conversation::with_runtime");
|
|
|
|
let cfg = GenerateConfig {
|
|
max_new_tokens: 8,
|
|
add_bos: false, // qwen2 BPE; no BOS
|
|
tokenizer_kind: TokenizerKind::Bpe,
|
|
cache_kind: KvCacheKind::InPlace,
|
|
sampling: SamplingConfig::greedy(),
|
|
max_seq: Some(64),
|
|
stop_strings: vec![],
|
|
};
|
|
|
|
let prompt = "The capital of France is";
|
|
let t_gen = Instant::now();
|
|
let mut tokens: Vec<u32> = Vec::new();
|
|
{
|
|
let stream = conv.extend(prompt, &cfg).expect("extend");
|
|
for st in stream {
|
|
let st = st.expect("decode step");
|
|
tokens.push(st.id);
|
|
}
|
|
}
|
|
let gen_time = t_gen.elapsed();
|
|
let cont = vocab.decode_bpe(&tokens);
|
|
eprintln!(" >>> generated {} tokens in {:?} ({:?}/tok)",
|
|
tokens.len(), gen_time, gen_time / tokens.len().max(1) as u32);
|
|
eprintln!(" >>> tokens = {:?}", tokens);
|
|
eprintln!(" >>> {:?}{}", prompt, cont);
|
|
|
|
// Streaming oracle: greedy generation must include "Paris" early.
|
|
// Same bar as the prefill test, applied to the cache-driven path.
|
|
assert!(
|
|
cont.to_lowercase().contains("paris"),
|
|
"STREAMING ORACLE FAILED — got {:?}. Decode/streaming path \
|
|
(build_decode_step_graph_inplace + ternary wmm/lookup_w) is \
|
|
incorrect.", cont);
|
|
|
|
let tok_per_s = tokens.len() as f64 / gen_time.as_secs_f64();
|
|
eprintln!("VERDICT: Bonsai streaming via in-place KV-cache decode \
|
|
produces 'Paris'. Throughput {:.2} tok/s.", tok_per_s);
|
|
}
|