//! L3 on JouleClaw's **own from-scratch GGUF runtime** — the in-process path. //! //! Proves the headline of this binding: a real local GGUF model, executed on //! `jouleclaw-runtime` (no llama.cpp, no FFI), closes a Joule Code cascade at //! tier L3 with a content-addressed `CodingOp` and a joules-class receipt. //! //! `#[ignore]` because it needs a real model on disk. Run with: //! //! JOULE_BONSAI=/path/to/model.gguf \ //! cargo test -p l3-jouleclaw-binding --release --test runtime_l3 -- --ignored --nocapture //! //! (defaults to `../../../pattern-lang/models/ternary-bonsai-1.7b-q2_0.gguf`). use joule_code_cascade::{Cascade, L0Cache, Request, Stage}; use joule_code_schema::Tier; use jouleclaw_llm::{LlmBackend, LlmRequest}; use l3_jouleclaw_binding::jouleclaw_l3_stage; use l3_jouleclaw_binding::runtime_backend::JouleclawRuntimeBackend; fn model_path() -> String { std::env::var("JOULE_BONSAI").unwrap_or_else(|_| { "../../../pattern-lang/models/ternary-bonsai-1.7b-q2_0.gguf".to_string() }) } #[test] #[ignore] fn runtime_backend_completes_in_process() { let backend = JouleclawRuntimeBackend::from_path(model_path()).expect("load model on jouleclaw-runtime"); eprintln!("model: {}", backend.model_name()); // Greedy, short — keep the reference-backend forward passes bounded. let mut req = LlmRequest::from_prompt("The capital of France is", 4); req.temperature = 0.0; let resp = backend.complete(&req).expect("complete"); eprintln!(" in={} out={} text={:?}", resp.input_tokens, resp.output_tokens, resp.text); assert!(resp.input_tokens > 0, "prompt tokenized"); assert!(resp.output_tokens > 0, "produced output tokens"); assert!(!resp.text.is_empty(), "produced text"); // No per-call meter on the generate path — honestly None (the run binary // meters host energy around the call). assert!(resp.energy_joules.is_none()); } #[test] #[ignore] fn cascade_closes_at_l3_on_jouleclaw_runtime() { let backend = JouleclawRuntimeBackend::from_path(model_path()).expect("load model on jouleclaw-runtime"); let model_name = backend.model_name().to_string(); // Empty L0 cache in front of the in-process L3 model stage: L0 misses, the // from-scratch runtime closes the request at L3. let cascade = Cascade::new([ Box::new(L0Cache::new()) as Box, Box::new(jouleclaw_l3_stage(Box::new(backend))) as Box, ]); let res = cascade .resolve(&Request { prior: "b3:scratch".into(), intent: "Write a Rust function add(a,b).".into(), }) .expect("cascade resolved"); eprintln!("model: {model_name}"); eprintln!("tier : {}", res.receipt.tier); eprintln!("op_id: {}", res.op.as_ref().unwrap().op_id().unwrap()); eprintln!("answer:\n{}", res.answer.as_deref().unwrap_or("")); assert_eq!(res.receipt.tier, Tier::L3, "closed at L3"); assert!(res.op.is_some(), "produced a coding op"); assert!( res.answer.map(|a| !a.is_empty()).unwrap_or(false), "the from-scratch runtime produced an answer" ); }