All checks were successful
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 29s
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 3m3s
standards workspaces build + test / jcp-rs (jcp-conformance, jcp/jcp-rs, -p jcp-energy --features nvml, jcp-rs, linux) (pull_request) Successful in 2m46s
standards workspaces build + test / joule-ui-rs (joule-ui/joule-ui-rs, joule-ui-rs, linux) (pull_request) Successful in 1m17s
standards workspaces build + test / joulehook-rs (joulehook/joulehook-rs, joulehook-rs, linux) (pull_request) Successful in 1m35s
standards workspaces build + test / proof-rs (proof-conformance, proof/proof-rs, proof-rs, linux, true) (pull_request) Successful in 1m39s
standards workspaces build + test / sandbox-rs (sandbox-conformance, sandbox/sandbox-rs, sandbox-rs, linux) (pull_request) Successful in 2m51s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 9m1s
standards workspaces build + test / jouleclaw-rs (-nodebug, jouleclaw/jouleclaw-rs, -p jouleclaw-energy --features rapl,nvml, jouleclaw-rs, linux-arm64, -C target-feature=+fp16) (pull_request) Successful in 6m29s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 2m39s
standards workspaces build + test / joulecontract-rs (joulecontract/joulecontract-rs, joulecontract-rs, linux-arm64, true) (pull_request) Successful in 1m33s
standards workspaces build + test / jouledesk-rs (jouledesk/jouledesk-rs, jouledesk-rs, macos) (pull_request) Successful in 2m3s
standards workspaces build + test / mesh-rs (mesh/mesh-rs, -p mesh-engine-gguf -p mesh-openai --features mesh-engine-gguf/gguf,mesh-openai/server, mesh-rs, linux-arm64) (pull_request) Successful in 1m26s
CI caught what the previous commit's verification could not: `jcp-efficiency`
is a **path dependency of three other workspaces**, and
`cargo test --workspace` inside `jcp/jcp-rs` never compiles them. The
jouledesk-rs, sandbox-rs and joulehook-rs legs all failed to build.
The consumers are:
jouledesk/.../jouledesk/Cargo.toml
jouledesk/.../jouledesk-livekit/Cargo.toml
jouledesk/.../jouledesk-github/Cargo.toml
sandbox/.../sandbox-tee/Cargo.toml
joulehook/.../joulehook-meter/Cargo.toml
Five call sites read a figure that is now an `Option`:
- `jouledesk/src/lib.rs` — the assertion that waste inflates
joules-per-completed-unit. Two units completed there, so the ratio is
defined; `.expect` now records that rather than hiding it.
- `jouledesk/examples/channel.rs`, `examples/room.rs` and
`sandbox-tee/examples/attested_efficiency.rs` — `println!` of the figure.
Each prints "—" when undefined, for the same reason `session.rs` does:
0 is the optimum of a cost-per-result figure, so printing it would name
the cheapest possible cost for work that delivered none.
- `joulehook-meter` — `assert_eq!` on a receipt field; 900 tokens were
accepted, so the figure is present and the assertion takes `Some`.
`Efficiency`'s own fields did not change, so the `Efficiency { .. }`
literals in all four consumers are untouched; only reads of
`EfficiencyReceipt::uj_per_accepted_token` and `Totals::uj_per_completed_unit`
needed work.
Verified by building and testing **each** of the four workspaces that
compile this crate — jcp-rs, jouledesk-rs, sandbox-rs, joulehook-rs — rather
than only the one holding the edit: `cargo check --workspace --all-targets`
and `cargo test --workspace` exit 0 in all four, with 0 failed suites.
Co-Authored-By: Claude Opus 5 <[email protected]>
235 lines
9.3 KiB
Rust
235 lines
9.3 KiB
Rust
//! JouleHook — the meter-seam binding (`joulehook/1`, SPEC §4 Proxy).
|
|
//!
|
|
//! The gate seals every *tool* call as a metered transaction; the meter does the
|
|
//! same for the *model* call. Two composable primitives a proxy (or a host's
|
|
//! request/response hooks) wires around the model endpoint:
|
|
//!
|
|
//! - [`inject_ceiling`] — write a JouleHook grant's remaining joule budget into the
|
|
//! request's `metadata.joule_ceiling_uj`, never *raising* a tighter ceiling. This
|
|
//! is what turns *metered* into *capped*: over budget, Mesh refuses the request
|
|
//! before it reaches the provider.
|
|
//! - [`bridge_receipt`] — read Mesh's response `usage` (tokens + `usage.energy`) and
|
|
//! seal it as an `efficiency/1` receipt, carrying the honest provenance straight
|
|
//! through from `usage.energy.provenance`.
|
|
//!
|
|
//! The authoritative energy figure comes from Mesh's meter, not an estimate — this
|
|
//! binding only carries it into the same receipt surface the gate uses.
|
|
|
|
use ed25519_dalek::SigningKey;
|
|
use jcp_efficiency::Efficiency;
|
|
use jcp_schema::{ContentId, Energy};
|
|
use serde_json::Value;
|
|
|
|
/// Write `budget_uj` into `request.metadata.joule_ceiling_uj`, clamped so an
|
|
/// existing *tighter* ceiling is preserved (`min`). The request is returned with the
|
|
/// ceiling set; a non-object request is returned unchanged.
|
|
pub fn inject_ceiling(mut request: Value, budget_uj: u64) -> Value {
|
|
if let Some(obj) = request.as_object_mut() {
|
|
let metadata = obj
|
|
.entry("metadata")
|
|
.or_insert_with(|| Value::Object(Default::default()));
|
|
if let Some(m) = metadata.as_object_mut() {
|
|
let ceiling = m
|
|
.get("joule_ceiling_uj")
|
|
.and_then(Value::as_u64)
|
|
.map_or(budget_uj, |existing| existing.min(budget_uj));
|
|
m.insert("joule_ceiling_uj".into(), Value::from(ceiling));
|
|
}
|
|
}
|
|
request
|
|
}
|
|
|
|
/// The reference meter steward (a fixed dev key). A production deployment supplies
|
|
/// its own steward key + DID.
|
|
pub const METER_STEWARD: &str = "did:jcp:joulehook-meter";
|
|
|
|
/// The reference meter steward signing key.
|
|
pub fn meter_steward_sk() -> SigningKey {
|
|
SigningKey::from_bytes(&[0x6e; 32])
|
|
}
|
|
|
|
/// [`bridge_receipt`] under the reference meter steward, seq 0 / genesis prev — the
|
|
/// convenience the proxy and CLI use so callers don't wrangle keys.
|
|
pub fn bridge_receipt_default(
|
|
response: &Value,
|
|
task: &str,
|
|
completed: bool,
|
|
verdict: &str,
|
|
wall_ns: u64,
|
|
) -> Option<Vec<u8>> {
|
|
bridge_receipt(
|
|
response,
|
|
task,
|
|
completed,
|
|
verdict,
|
|
wall_ns,
|
|
&meter_steward_sk(),
|
|
METER_STEWARD,
|
|
0,
|
|
&ContentId([0u8; 32]),
|
|
)
|
|
}
|
|
|
|
/// Map Mesh's honest provenance label onto a [`jcp_schema::Energy`]. Mesh writes
|
|
/// the family's wire labels (`HwShunt`, `ModelBased`, `Estimator` — its
|
|
/// `Provenance::jcr1_label`); the lower-case display spelling (`hw_shunt`,
|
|
/// `model_based`, `estimator`) that JouleClaw's energy crate prints is read as the
|
|
/// same grade. Any other label falls to the estimator floor rather than
|
|
/// overclaiming a measurement.
|
|
fn energy_from(uj: u64, provenance: &str) -> Energy {
|
|
match provenance {
|
|
"HwShunt" | "hw_shunt" => Energy::measured(uj),
|
|
"ModelBased" | "model_based" => Energy::modeled(uj),
|
|
_ => Energy::estimator(uj),
|
|
}
|
|
}
|
|
|
|
/// Seal a completed model serve as an `efficiency/1` receipt from Mesh's response
|
|
/// `usage`. Returns `None` if the response carries no `usage.energy` block (i.e. it
|
|
/// did not come through a Mesh meter — this binding does not invent an energy
|
|
/// figure). `completed`/`verdict` are the caller's outcome anchor for the serve.
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub fn bridge_receipt(
|
|
response: &Value,
|
|
task: &str,
|
|
completed: bool,
|
|
verdict: &str,
|
|
wall_ns: u64,
|
|
steward_sk: &SigningKey,
|
|
steward: &str,
|
|
seq: u64,
|
|
prev: &ContentId,
|
|
) -> Option<Vec<u8>> {
|
|
let usage = response.get("usage")?;
|
|
let energy = usage.get("energy")?;
|
|
let uj = energy.get("uj").and_then(Value::as_u64)?;
|
|
let provenance = energy
|
|
.get("provenance")
|
|
.and_then(Value::as_str)
|
|
.unwrap_or("estimator");
|
|
let eff = Efficiency {
|
|
task: task.to_string(),
|
|
completed,
|
|
verdict: verdict.to_string(),
|
|
tokens_in: usage.get("prompt_tokens").and_then(Value::as_u64).unwrap_or(0),
|
|
tokens_out: usage
|
|
.get("completion_tokens")
|
|
.and_then(Value::as_u64)
|
|
.unwrap_or(0),
|
|
energy: energy_from(uj, provenance),
|
|
wall_ns,
|
|
value_uusd: None,
|
|
energy_attestation: None,
|
|
};
|
|
Some(eff.seal(seq, prev, steward_sk, steward))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn injects_the_ceiling_when_absent() {
|
|
let req = serde_json::json!({ "model": "m", "messages": [] });
|
|
let out = inject_ceiling(req, 5_000_000);
|
|
assert_eq!(out["metadata"]["joule_ceiling_uj"], 5_000_000);
|
|
}
|
|
|
|
#[test]
|
|
fn never_raises_a_tighter_existing_ceiling() {
|
|
// A caller asking for ≤ 1 mJ must not be loosened to the grant's 5 mJ.
|
|
let req = serde_json::json!({ "metadata": { "joule_ceiling_uj": 1_000_000 } });
|
|
let out = inject_ceiling(req, 5_000_000);
|
|
assert_eq!(out["metadata"]["joule_ceiling_uj"], 1_000_000);
|
|
}
|
|
|
|
#[test]
|
|
fn lowers_a_looser_existing_ceiling_to_the_budget() {
|
|
let req = serde_json::json!({ "metadata": { "joule_ceiling_uj": 9_000_000 } });
|
|
let out = inject_ceiling(req, 5_000_000);
|
|
assert_eq!(out["metadata"]["joule_ceiling_uj"], 5_000_000);
|
|
}
|
|
|
|
#[test]
|
|
fn bridges_mesh_usage_into_a_verifiable_efficiency_receipt() {
|
|
let sk = SigningKey::from_bytes(&[0x6e; 32]);
|
|
let steward = "did:jcp:joulehook-meter";
|
|
let response = serde_json::json!({
|
|
"usage": {
|
|
"prompt_tokens": 8000,
|
|
"completion_tokens": 900,
|
|
"total_tokens": 8900,
|
|
"energy": { "uj": 2_700_000, "uj_per_accepted_token": 3000, "provenance": "ModelBased" }
|
|
}
|
|
});
|
|
let sealed = bridge_receipt(
|
|
&response, "fix-auth-bug", true, "tests-pass", 42_000_000_000,
|
|
&sk, steward, 0, &ContentId([0u8; 32]),
|
|
)
|
|
.expect("a usage.energy block seals");
|
|
|
|
let pk = sk.verifying_key().to_bytes();
|
|
let r = jcp_efficiency::verify(&sealed, steward, &pk).expect("efficiency/1 verifies");
|
|
assert_eq!(r.tokens_out, 900);
|
|
assert_eq!(r.energy_uj, 2_700_000);
|
|
assert_eq!(r.uj_per_accepted_token, Some(2_700_000 / 900)); // 3000 µJ/accepted token
|
|
assert_eq!(r.provenance, "ModelBased");
|
|
}
|
|
|
|
/// Bridge a real Mesh response — `mesh-openai`'s chat completion, its
|
|
/// `usage.energy.provenance` written by Mesh's own wire label — and return
|
|
/// the provenance the sealed receipt carries.
|
|
fn bridged_grade_of_mesh_output(p: mesh_schema::Provenance) -> String {
|
|
let response = mesh_openai::chat_completion_response(
|
|
"chatcmpl-1", "m", 1_700_000_000, "ok", 12, 3, 66_900_000, Some(22_300_000),
|
|
p.jcr1_label(),
|
|
);
|
|
let sealed = bridge_receipt_default(&response, "t", true, "ok", 0).expect("a usage.energy block seals");
|
|
let pk = meter_steward_sk().verifying_key().to_bytes();
|
|
jcp_efficiency::verify(&sealed, METER_STEWARD, &pk).expect("efficiency/1 verifies").provenance
|
|
}
|
|
|
|
/// Mesh writes its grades as `HwShunt` / `ModelBased` / `Estimator`. Each must
|
|
/// reach the receipt as itself: a Mesh figure is neither under-graded to the
|
|
/// estimator floor nor raised.
|
|
#[test]
|
|
fn mesh_grades_reach_the_receipt_unchanged() {
|
|
use mesh_schema::Provenance as Mesh;
|
|
for p in [Mesh::HwShunt, Mesh::ModelBased, Mesh::Estimator] {
|
|
assert_eq!(bridged_grade_of_mesh_output(p), p.jcr1_label(), "Mesh's {p:?} figure");
|
|
}
|
|
}
|
|
|
|
/// The lower-case spelling (`hw_shunt` / `model_based` / `estimator`, the
|
|
/// JouleClaw energy crate's display form) is still read as the same grade, and
|
|
/// a label the family does not write falls to the estimator floor.
|
|
#[test]
|
|
fn lower_case_labels_and_unknown_labels() {
|
|
for (label, grade) in [
|
|
("hw_shunt", "HwShunt"),
|
|
("model_based", "ModelBased"),
|
|
("estimator", "Estimator"),
|
|
("measured", "Estimator"),
|
|
("hwshunt", "Estimator"),
|
|
("", "Estimator"),
|
|
] {
|
|
let response = mesh_openai::chat_completion_response(
|
|
"chatcmpl-1", "m", 1_700_000_000, "ok", 12, 3, 66_900_000, None, label,
|
|
);
|
|
let sealed = bridge_receipt_default(&response, "t", true, "ok", 0).expect("seals");
|
|
let pk = meter_steward_sk().verifying_key().to_bytes();
|
|
let r = jcp_efficiency::verify(&sealed, METER_STEWARD, &pk).expect("verifies");
|
|
assert_eq!(r.provenance, grade, "label {label:?}");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn no_energy_block_seals_nothing() {
|
|
let sk = SigningKey::from_bytes(&[0x6e; 32]);
|
|
let response = serde_json::json!({ "usage": { "prompt_tokens": 1, "completion_tokens": 1 } });
|
|
assert!(bridge_receipt(
|
|
&response, "t", true, "ok", 0, &sk, "did:jcp:joulehook-meter", 0, &ContentId([0u8; 32])
|
|
)
|
|
.is_none());
|
|
}
|
|
}
|