All checks were successful
joule-code conformance gate / joule-moq-relay, its own energy on a counter (macos) (pull_request) Successful in 43s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 1m30s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 12s
standards workspaces build + test / jouledesk-rs (jouledesk/jouledesk-rs, jouledesk-rs, macos) (pull_request) Successful in 1m46s
joule-code conformance gate / joule-moq-relay, every target (pull_request) Successful in 4m2s
joule-code conformance gate / Build + round-trip C1-C4 vectors (pull_request) Successful in 2m55s
joule-code conformance gate / joule-moq-ingest, every target (pull_request) Successful in 6m0s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 5m36s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 10m55s
Builds jouleclaw-energy with `smc` in the two consumers that bill work with the host meter. On macOS 27, where IOReport's CPU rails are frozen for an unprivileged reader, their host meter becomes the SMC's whole-machine power integral (ModelBased, domain System) instead of the load-average estimator; a live IOReport counter still comes first. - jouledesk-github: `detect_meter` picks the SMC; replies say "modeled by `apple-smc`" and seal ModelBased. Work shorter than the SMC's 2 s minimum window is posted as not metered, with why, as it already was for any meter. The model-subprocess test calibrated its stand-in to about a second of CPU, under that window; it now runs a second past the host meter's minimum window at the loop's measured rate, so it holds for every meter. - Joule Code L3 example: `host_counter` adds the SMC between IOReport and the estimator. A counter's reading after a window shorter than its minimum is no longer taken (`read_after`): the receipt falls back to the token estimate, as it does with no counter. The host test burns past the counter's minimum and asserts the grade each meter gives; a new test covers the window rule. Not changed: crates that enable `ioreport` only for this process's own counters and refuse the span meter's host fallback (jouleclaw-loader-gguf, wai quantum-meter and ops-gpu-bench) or only print it (jouleclaw-runtime's example), where the SMC would start a sampler nothing reads; mesh-meter, whose HwMeter does not consult a counter's minimum window, so short served requests would carry ModelBased figures below the SMC's 2 s floor. On an M3 Ultra Mac Studio (IOReport CPU rails frozen): jouledesk-github seals work at ModelBased by `apple-smc` and the model-subprocess rounds read the model's own CPU (21 J against its own 21 J count) plus the SMC's figure, posted as an estimate; the L3 host test meters its burn at ModelBased. Tests pass on macOS (jouledesk-github, l3-jouleclaw) and Linux (l3-jouleclaw, token estimate); clippy is clean for jouledesk-github. l3-jouleclaw's clippy reports one manual_char_comparison in src/llama_backend.rs, which this change does not touch and CI does not lint. Co-Authored-By: Claude Opus 5.5 <[email protected]>
40 lines
2.4 KiB
TOML
40 lines
2.4 KiB
TOML
# Standalone example (own workspace) — does NOT join joule-code-rs's workspace, so
|
|
# the standard's lib stays free of a JouleClaw dependency. This is a *consumer
|
|
# binding*: how to wire JouleClaw's L3 (Model) tier into the Joule Code cascade.
|
|
[workspace]
|
|
|
|
[package]
|
|
name = "l3-jouleclaw-binding"
|
|
version = "0.1.0"
|
|
edition = "2021"
|
|
license = "Apache-2.0"
|
|
description = "Example binding: JouleClaw's L3 model tier (jouleclaw-llm LlmBackend) as a Joule Code cascade FnStage. Verified model-free via EchoBackend; a real backend (vLLM, or jouleclaw-runtime GGUF) drops into the same slot."
|
|
publish = false
|
|
|
|
[dependencies]
|
|
joule-code-schema = { path = "../../joule-code-rs/crates/joule-code-schema" }
|
|
joule-code-cargo = { path = "../../joule-code-rs/crates/joule-code-cargo" }
|
|
joule-code-receipt = { path = "../../joule-code-rs/crates/joule-code-receipt" }
|
|
joule-code-cascade = { path = "../../joule-code-rs/crates/joule-code-cascade" }
|
|
jouleclaw-llm = { path = "../../../jouleclaw/jouleclaw-rs/crates/jouleclaw-llm" }
|
|
jouleclaw-energy = { path = "../../../jouleclaw/jouleclaw-rs/crates/jouleclaw-energy", features = ["ioreport", "smc"] }
|
|
# JouleClaw's own from-scratch GGUF tensor runtime (no llama.cpp, no FFI) —
|
|
# the in-process L3 backend.
|
|
jouleclaw-runtime = { path = "../../../jouleclaw/jouleclaw-rs/crates/jouleclaw-runtime" }
|
|
jouleclaw-loader-gguf = { path = "../../../jouleclaw/jouleclaw-rs/crates/jouleclaw-loader-gguf", features = ["rayon", "fabric"] }
|
|
# The grades (`Measured` / `Benchmarked` / `Estimated`) the fabric ledger prices
|
|
# a call from, which the L3 stage grades the call's projected energy by.
|
|
jouleclaw-fabric = { path = "../../../jouleclaw/jouleclaw-rs/crates/jouleclaw-fabric" }
|
|
# The GPU load the in-process meter's GPU-counter liveness probe runs
|
|
# (`gpu_liveness_load`): the `run` binary meters the in-process engine on this
|
|
# process's own CPU and GPU (`jouleclaw_energy::span::ProcessSpanMeter`).
|
|
jouleclaw-backend-apple = { path = "../../../jouleclaw/jouleclaw-rs/crates/jouleclaw-backend-apple" }
|
|
# The llama engine's subprocess, read on its own process tree where the host
|
|
# counts each process's CPU energy (macOS): `TreeMeter`.
|
|
sandbox-meter = { path = "../../../sandbox/sandbox-rs/crates/sandbox-meter" }
|
|
serde_json = "1"
|
|
|
|
# macOS: the llama engine's process is waited for without being reaped
|
|
# (`waitid` with `WNOWAIT`), so the tree meter reads its exited count.
|
|
[target.'cfg(target_os = "macos")'.dependencies]
|
|
libc = "0.2"
|