open-standards/jouleclaw/jouleclaw-rs/crates/jouleclaw-prism/tests/real_bonsai_cascade.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

132 lines
6 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! Cascade integration oracle: a real PrismML Bonsai-class model is
//! dispatchable as a `Tier` inside the joule cascade.
//!
//! Loads `Ternary-Bonsai-1.7B-Q2_0.gguf` once via
//! [`PrismTier::from_gguf`], registers it with a fresh
//! [`Cascade`], submits a text Query, and asserts:
//!
//! * the registered tier reports the expected `TierId::L3(...)` and a
//! non-`None` `Coord` (so the router can reason about it),
//! * `estimate_cost` returns a finite joule estimate at the ternary
//! rate (~40 mJ/tok scaled by `max_new_tokens`),
//! * `try_answer` produces `AnswerOutput::Text` containing "Paris",
//! * `joules_spent` is positive and bounded by the estimate,
//! * the `ExecutionTrace` records a `Hit`.
//!
//! This closes the loop the user named: "make the runtime interface so
//! the files can be used in pattern-lang." Bonsai is now a real
//! cascade tier, not just a loader-level proof.
//!
//! `#[ignore]` — needs the model on disk. Run:
//!
//! cargo test --release -p prism --test real_bonsai_cascade \
//! -- --ignored --nocapture
use jouleclaw_cascade::{
AnswerOutput, Cascade, ContextRef, JouleBudget, L3ModelId, QualityFloor,
Query, QueryInput, Tier, TierId, TraceOutcome,
};
use jouleclaw_prism::PrismTier;
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 bonsai_dispatches_as_prism_tier_in_cascade() {
let path = model_path();
eprintln!("loading {}", path);
let t0 = Instant::now();
// 16 tokens of headroom: Bonsai is a thinking-mode reasoner that
// emits `<think>...</think>` markers (3-4 tokens) before its
// answer. 8 tokens (the prior cap) leaves no room for "Paris" to
// appear past the markers. 16 keeps the *measured* cost — which
// for this model runs ~1.25× the static estimate — under
// `JouleBudget::standard()`'s 1 J ceiling, so the Runtime's
// budget guard (used by `real_bonsai_multitier`) won't reject.
let mut tier = PrismTier::from_gguf(7, &path, 128)
.expect("PrismTier::from_gguf")
.with_max_new_tokens(16);
eprintln!(" loaded + parsed in {:?}", t0.elapsed());
// Tier identity + coordinate — the router needs both.
assert_eq!(tier.id(), TierId::L3(L3ModelId(7)), "stable TierId");
let coord = tier.coord().expect("tier must report a coord");
eprintln!(" coord: {:?}", coord);
// Static joule estimate. For Bonsai (~43 mJ/tok) × 8 tokens this is
// ~340 mJ — comfortably inside `JouleBudget::standard().hard_limit`
// (1 J) and well outside `cheap()` (1 mJ), which is exactly how the
// router decides this tier is "L3-class".
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, latency {:?}, confidence_floor {}",
est.joules * 1e3, est.latency, est.confidence_floor);
assert!(est.joules.is_finite() && est.joules > 0.0);
assert!(est.joules < q.budget.hard_limit,
"estimate {:.3} J must fit hard limit {:.3} J", est.joules, q.budget.hard_limit);
// Register in a real Cascade and confirm visibility.
let mut cascade = Cascade::new();
cascade.register(Box::new(PrismTier::from_gguf(7, &path, 128)
.expect("PrismTier::from_gguf for cascade")
.with_max_new_tokens(16)));
let tier_ids = cascade.tier_ids();
assert_eq!(tier_ids, vec![TierId::L3(L3ModelId(7))]);
let coords = cascade.tier_coords();
assert!(coords[0].1.is_some(), "registered tier exposes a Coord");
drop(cascade); // We don't need the second copy past visibility check.
// Dispatch.
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!(" >>> tier_used={:?} joules_spent={:.3} mJ wall={:?}",
ans.tier_used, ans.joules_spent * 1e3, elapsed);
eprintln!(" >>> {:?}", s);
assert!(s.to_lowercase().contains("paris"),
"CASCADE ORACLE FAILED — got {:?}", s);
}
other => panic!("expected Text answer, got {:?}", other),
}
assert_eq!(ans.tier_used, TierId::L3(L3ModelId(7)));
assert!(ans.joules_spent > 0.0, "joule receipt must be positive");
// `joules_spent` is now the MEASURED kernel-reported total, not the
// static `estimate_cost` figure. They are *expected* to diverge —
// that divergence is exactly what the cascade's calibration ledger
// learns `learned_mu` from. The pre-dispatch estimate is for
// budgeting; the receipt is honest measurement. So we no longer
// assert `spent <= estimate`; we assert they differ (proving the
// measured path is wired, not just echoing the estimate) and that
// both are finite + positive.
let ratio = ans.joules_spent / est.joules;
eprintln!(" estimate={:.3} mJ measured={:.3} mJ ratio={:.2}x",
est.joules * 1e3, ans.joules_spent * 1e3, ratio);
assert!(ans.joules_spent.is_finite());
assert!((ans.joules_spent - est.joules).abs() > f64::EPSILON,
"measured joules must come from kernel accounting, not echo the \
static estimate (both were {:.6} mJ)", est.joules * 1e3);
// Trace must show a Hit (the answer wasn't refused or escalated).
assert!(matches!(ans.trace.attempts[0].outcome, TraceOutcome::Hit),
"ExecutionTrace should record a Hit, got {:?}", ans.trace.attempts);
eprintln!("VERDICT: Bonsai dispatchable as a cascade tier with an \
HONEST calibration receipt. Static estimate {:.3} mJ, measured \
{:.3} mJ ({:.2}x), wall {:?}, said 'Paris'.",
est.joules * 1e3, ans.joules_spent * 1e3, ratio, elapsed);
}