The fabric arc reaches the standard's receipts. The hybrid
backend wraps every complete() in an attribution-ledger
window: GFLOPs recorded per fabric member that ACTUALLY ran
each matmul, priced by the machine's IOReport calibration.
The summed figure rides LlmResponse.energy_joules, and the
L3 stage now grades it correctly per SPEC §6:
backend reports a figure → estimated_uj, ModelBased
(a calibrated per-operation
model — precisely what §6
defines ModelBased to mean)
no figure → catalog constant, Estimator
(the honest old behavior,
unchanged for Echo/llama.cpp)
Live, real checkpoint (Mellum2, fabric ON):
"The capital of France is" → " Paris. \nParis is"
fabric-priced energy: 113.0 J (ModelBased)
A coding-cascade L3 receipt now carries energy from the
silicon that actually executed the call — attributed per
member, priced by a calibration metered on this machine —
instead of a 1.0 J placeholder. The provenance ladder is
exercised end to end: Estimator (catalog) → ModelBased
(fabric ledger × calibration) → HwShunt (a meter wrapping
the whole call, as the joule-fix harness already does).
Test updated to the new contract: Some(>0) with the fabric
on, honest None without — both branches printed.
Co-Authored-By: Claude Fable 5 <[email protected]>
139 lines
5.4 KiB
Rust
139 lines
5.4 KiB
Rust
//! Example binding — JouleClaw's **L3 (Model)** tier as a Joule Code cascade stage.
|
|
//!
|
|
//! This shows how a consumer plugs the JouleClaw agent schema's local-model
|
|
//! layer ([`jouleclaw_llm::LlmBackend`]) into the Joule Code cascade
|
|
//! ([`joule_code_cascade::Stage`]) at tier L3. A coding request that misses the
|
|
//! cheaper tiers (L0 cache, L1 lawful, L2 embed) is generated by a local model
|
|
//! here, producing a content-addressed [`CodingOp`] and a joules-class L3
|
|
//! [`Receipt`].
|
|
//!
|
|
//! It is verified **model-free** with [`EchoBackend`] (a deterministic backend
|
|
//! JouleClaw ships for exactly this). Real backends implement the same
|
|
//! `LlmBackend` trait and drop into the same slot:
|
|
//!
|
|
//! - [`llama_backend::LlamaCompletionBackend`] — shells out to `llama.cpp`.
|
|
//! - [`runtime_backend::JouleclawRuntimeBackend`] — runs the model **in-process
|
|
//! on JouleClaw's own from-scratch GGUF tensor runtime** (`jouleclaw-runtime`),
|
|
//! no external binary and no FFI.
|
|
//! - [`hybrid_backend::HybridBackend`] — runs the Mamba2/attention **hybrid**
|
|
//! safetensors families (NemotronH, Granite-4.0-H, Falcon-H1) in-process via
|
|
//! `jouleclaw_loader_gguf::hybrid_lm`, family auto-detected from
|
|
//! `config.json`.
|
|
//!
|
|
//! In production the cheapest capable backend is chosen by
|
|
//! `jouleclaw_dispatch::ModelBank`'s cost table.
|
|
|
|
pub mod hybrid_backend;
|
|
pub mod llama_backend;
|
|
pub mod runtime_backend;
|
|
|
|
use joule_code_cargo::{Anchor, CodingOp, Edit};
|
|
use joule_code_cascade::{FnStage, Request, Resolution, Stage};
|
|
use joule_code_receipt::{Energy, Provenance, Receipt};
|
|
use joule_code_schema::{ContentId, Tier, ALG_ED25519};
|
|
use jouleclaw_llm::{EchoBackend, LlmBackend, LlmRequest};
|
|
|
|
/// Build an L3 cascade [`Stage`] backed by any JouleClaw [`LlmBackend`]. The
|
|
/// stage generates a coding operation from the request intent and reports the
|
|
/// backend's joule cost as the receipt energy (tier L3, joules class).
|
|
pub fn jouleclaw_l3_stage(backend: Box<dyn LlmBackend>) -> impl Stage {
|
|
FnStage::new(Tier::L3, move |req: &Request| {
|
|
let response = backend
|
|
.complete(&LlmRequest::from_prompt(req.intent.clone(), 256))
|
|
.ok()?;
|
|
|
|
// Reference materialization: the model's output becomes one edit. A
|
|
// production binding would parse structured edits from the model.
|
|
let edit = Edit {
|
|
path: "OUTPUT.md".into(),
|
|
anchor: Anchor::ByteRange { start: 0, end: 0 },
|
|
replacement: response.text.clone().into_bytes(),
|
|
};
|
|
let op = CodingOp {
|
|
alg: ALG_ED25519,
|
|
prior: ContentId::of_bytes(req.prior.as_bytes()),
|
|
edits: vec![edit],
|
|
author: [0u8; 32],
|
|
intent: req.intent.clone(),
|
|
tier: Tier::L3,
|
|
};
|
|
let op_id = op.op_id().ok()?;
|
|
|
|
// L3 energy, by best available provenance: when the
|
|
// backend reports a per-call figure (the hybrid
|
|
// backend's fabric ledger — GFLOPs attributed per
|
|
// member, priced by THIS machine's IOReport
|
|
// calibration), that is a calibrated per-operation
|
|
// model: ModelBased per SPEC §6. Otherwise the
|
|
// catalog constant, honestly tagged Estimator.
|
|
let (estimated_uj, provenance) = match response.energy_joules {
|
|
Some(j) => ((j * 1_000_000.0) as u64, Provenance::ModelBased),
|
|
None => (
|
|
(backend.typical_joules_per_call() * 1_000_000.0) as u64,
|
|
Provenance::Estimator,
|
|
),
|
|
};
|
|
|
|
Some(Resolution {
|
|
op: Some(op),
|
|
answer: Some(response.text),
|
|
receipt: Receipt {
|
|
op_id,
|
|
tier: Tier::L3,
|
|
energy: Energy {
|
|
measured_uj: None,
|
|
estimated_uj,
|
|
provenance,
|
|
},
|
|
sequence: 0,
|
|
nonce: [0u8; 8],
|
|
},
|
|
})
|
|
})
|
|
}
|
|
|
|
/// Convenience: an L3 stage backed by the model-free [`EchoBackend`] (for tests
|
|
/// and smoke runs without a real model).
|
|
pub fn echo_l3_stage() -> impl Stage {
|
|
jouleclaw_l3_stage(Box::new(EchoBackend::new()))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use joule_code_cascade::{Cascade, L0Cache};
|
|
|
|
#[test]
|
|
fn l3_stage_generates_op_and_joules_receipt() {
|
|
let stage = echo_l3_stage();
|
|
let res = stage
|
|
.try_resolve(&Request {
|
|
prior: "b3:AAAA".into(),
|
|
intent: "write a hello function".into(),
|
|
})
|
|
.expect("L3 resolved");
|
|
assert_eq!(res.receipt.tier, Tier::L3);
|
|
assert!(res.op.is_some());
|
|
// EchoBackend prefixes the prompt; the model output flowed through.
|
|
assert!(res.answer.unwrap().contains("hello"));
|
|
// Joules-class energy, honestly tagged as an estimate.
|
|
assert!(res.receipt.energy.estimated_uj > 0);
|
|
assert_eq!(res.receipt.energy.provenance, Provenance::Estimator);
|
|
}
|
|
|
|
#[test]
|
|
fn cascade_falls_through_l0_to_l3() {
|
|
// Empty L0 cache in front of the L3 model stage: L0 misses, L3 closes.
|
|
let cascade = Cascade::new([
|
|
Box::new(L0Cache::new()) as Box<dyn Stage>,
|
|
Box::new(echo_l3_stage()) as Box<dyn Stage>,
|
|
]);
|
|
let res = cascade
|
|
.resolve(&Request {
|
|
prior: "b3:AAAA".into(),
|
|
intent: "refactor this".into(),
|
|
})
|
|
.expect("resolved");
|
|
assert_eq!(res.receipt.tier, Tier::L3);
|
|
}
|
|
}
|