open-standards/jouleclaw/jouleclaw-rs/crates/jouleclaw-prism/tests/real_lfm2.rs
Transaction Science 05dd2e0c4c jouleclaw Phase 4: model tiers — liquid + prism + mrl + lmm + ebm
Five tier crates ported. 589 tests pass across Phase 1-4, zero
failures, zero warnings.

jouleclaw-liquid
  Ported from pattern-lang/liquid (~1123 LOC, 5 files).
  L3 tier — Closed-form Continuous-time (CfC) recurrent language
  models. The SSM class with O(L) state evolution vs transformer
  O(L²) attention. This is the v0.3 "SSM bet" from the architecture
  proposal — already shipping in the JouleClaw reference impl on
  day one.

jouleclaw-prism
  Ported from pattern-lang/prism (~3066 LOC, 6 files).
  L3 tier — 1-bit / ternary BitNet-class kernels. Ternary transformer
  forward pass. The lowest-joule local-model tier. Provides the
  TernaryDecoder backbone that jouleclaw-lmm builds on.

jouleclaw-mrl
  Ported from pattern-lang/mrl (~1464 LOC, 7 files).
  L2 tier — Matryoshka Representation Learning nested embeddings.
  Sub-millijoule retrieval tier. Embedder + GGUF embedder +
  matryoshka truncation + nearest-neighbor picker + retrieval.

jouleclaw-lmm
  Ported from pattern-lang/lmm (~1819 LOC, 6 files).
  L3 tier — Large Multimodal Model. Vision + text + audio pipeline
  on top of jouleclaw-prism's TernaryDecoder backbone. Real VL path
  (LFM2.5-VL preprocessing via the `image` crate, JPEG/PNG only —
  keeps the dep footprint minimal). The internal `prism::` references
  rewritten to `jouleclaw_prism::` during port.

jouleclaw-ebm
  Ported from pattern-lang/ebm (~1738 LOC, 7 files).
  Energy-based constraint-satisfaction tier — SAT, n-queens, sudoku,
  graph coloring, generic solver. Deterministic compute priced in
  joules, sits at the L1/L3 boundary depending on the problem class.

Bare crate-name references in integration tests fixed:
  `use liquid::...` → `use jouleclaw_liquid::...` (and same for the
  other 4 ported tiers). Initial sed pass only caught `joule_*`
  patterns; this second pass caught the bare-name ones.

gemma4 deferred. It depends on `joule-l1` and `pattern-core`
(OpenIE IP that doesn't port). Will land in Phase 6 once the
thin `LawfulTier` trait surface ships in jouleclaw-cascade or a
new jouleclaw-l1 crate so consumers can plug their own lawful
lexicon in.

Phase 5 next: omni-modal generation — diffusion (40+ samplers from
efficient-genai's ALGORITHM_CATALOG), Stable Diffusion / SDXL / SD3 /
Flux paths, MusicGen, Whisper, Gaussian3D, video synthesis,
cross-modal fusion. Source codebase: efficient-genai.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-26 17:39:05 -04:00

91 lines
3.6 KiB
Rust

//! End-to-end oracle: LFM2-350M (Liquid AI hybrid arch — alternating
//! attention + shortconv recurrent layers) dispatched as a `PrismTier`
//! in the cascade.
//!
//! What this exercises that no prior test did:
//! * the shortconv decode-mode dispatch in
//! `build_decode_step_graph_inplace_const_block`
//! * the per-layer `ShortConvStateCache` take/replace dance through
//! `run_inplace_step_cached`
//! * LFM2's `token_embd_norm.weight` final norm (not `output_norm`)
//! * streaming over a hybrid graph where each layer's input/output
//! name set depends on whether the layer is recurrent or attention
//!
//! Smoke gate — not caption-quality. If the streaming shortconv state
//! is wrong, the model emits garbage (low-vocab IDs, NaN logits, or
//! catastrophic divergence after a few tokens). We assert the easier
//! invariants here and let the deeper correctness oracle live in
//! `lmm`'s `real_lfm2_vl.rs`.
//!
//! `#[ignore]` — needs the model on disk.
//!
//! cargo test --release -p prism --test real_lfm2 \
//! -- --ignored --nocapture
use jouleclaw_cascade::{
AnswerOutput, ContextRef, JouleBudget, QualityFloor,
Query, QueryInput, Tier,
};
use jouleclaw_prism::PrismTier;
use std::time::Instant;
fn model_path() -> String {
std::env::var("JOULE_LFM2")
.unwrap_or_else(|_| "../../models/lfm2-350m-q8_0.gguf".to_string())
}
#[test]
#[ignore]
fn lfm2_350m_dispatches_through_prism_tier() {
let path = model_path();
eprintln!("loading {}", path);
let t0 = Instant::now();
let mut tier = PrismTier::from_gguf(11, &path, 128)
.expect("PrismTier::from_gguf on LFM2-350M")
.with_max_new_tokens(16);
eprintln!(" loaded + parsed in {:?}", t0.elapsed());
let coord = tier.coord().expect("tier must report a coord");
eprintln!(" coord: {:?}", coord);
let q = Query {
input: QueryInput::Text("The capital of France is".into()),
budget: JouleBudget::standard(),
quality: QualityFloor::any(),
context: ContextRef::fresh(),
deadline: None,
};
let est = tier.estimate_cost(&q).expect("estimate_cost on Text");
eprintln!(" estimate: {:.3} mJ", est.joules * 1e3);
assert!(est.joules.is_finite() && est.joules > 0.0);
let t_dispatch = Instant::now();
let ans = tier.try_answer(&q, q.budget.hard_limit).expect("try_answer");
let elapsed = t_dispatch.elapsed();
match &ans.output {
AnswerOutput::Text(s) => {
eprintln!(" >>> joules_spent={:.3} mJ wall={:?}",
ans.joules_spent * 1e3, elapsed);
eprintln!(" >>> {:?}", s);
// Real shortconv-state or FFN-dispatch bugs produce
// degenerate repetition ("Reg Reg Reg regardless...") or
// empty strings. A model that knows the capital of France
// should mention "Paris" — the same gate Bonsai's cascade
// test uses.
assert!(s.to_lowercase().contains("paris"),
"LFM2 streaming-decode did not produce 'Paris' — \
likely a shortconv/state bug. got {:?}", s);
}
other => panic!("expected Text answer, got {:?}", other),
}
assert!(ans.joules_spent.is_finite() && ans.joules_spent > 0.0,
"joule receipt must be positive and finite");
eprintln!("VERDICT: LFM2-350M (hybrid attn+shortconv) ran end-to-end \
through PrismTier. Streaming shortconv state cache is live: \
prefill → decode loop produced finite logits, sampled in-vocab \
ids, decoded to text. Measured {:.3} mJ in {:?}.",
ans.joules_spent * 1e3, elapsed);
}