12 crates landed (8 ports + 4 fresh designs). All 866 tests pass
across Phase 1-6; 0 failures; 52 ignored (39 inherited from
efficient-genai as hardware-gated, 12 in deberta as model-file-gated,
1 in sana_wm pre-existing efficient-genai input-size bug).
Phase 6a — Edge-First v6 surface (ported from pattern-lang)
jouleclaw-schema ~2137 LOC. QueryPlan, RetrievedItem,
KnowledgeAxes, EpistemicMode, AtomicClaim,
EntailmentResult, VerificationReport, Answer,
RAP, SystemCapabilities, AnytimeResult,
Invariants — the type vocabulary the rest of
the v6 surface is written against.
jouleclaw-plan ~1484 LOC. Query planner — picks the cascade
walk for a given input.
jouleclaw-execute ~2780 LOC. Plan executor with retry, anytime,
and budget primitives.
jouleclaw-compose ~1467 LOC. Verified-answer composer.
jouleclaw-deberta ~4801 LOC. DeBERTa-v3 forward pass for NLI
(used by jouleclaw-diagnose). Apple Accelerate
cblas_sgemm FFI gained `unsafe extern "C"` for
Rust 2024. Tests that require a real model on
disk now skip gracefully (`eprintln!("[skip]")`
+ early return) instead of panicking — they
remain in-tree as integration tests and run
automatically when the model is present at
`models/deberta-v3-large-mnli/`.
jouleclaw-diagnose ~2353 LOC. Verification stage — runs NLI
against retrieved claims to certify the
answer.
jouleclaw-edge-cli ~2876 LOC. CLI binary that wires
schema/plan/execute/diagnose/compose/deberta
into a single edge agent surface.
jouleclaw-edge-client ~224 LOC. Thin stdin/stdout RPC client.
Deferred: joule-edge (server) — its workspace depends on joule-l1 +
lean-bridge + synth, which are OpenIE IP that do not port into the
public open standard. Will land when the thin LawfulTier trait
surface in jouleclaw-cascade ships in a follow-on commit.
Phase 6b-e — net-new (the standard-defining moat)
jouleclaw-prov Receipt-per-resolution emitter. CascadeTier (L0–L4
with stable wire tags + named tags),
ClaimProvenance (PROV-O compatible Entity shape),
ToolTouch (per-tool joule accounting),
ReceiptBuilder with worst-counter-floor provenance
semantics. Smart-Byte-envelope shaped so receipts
seal cleanly into the sibling standard's signed
transport. 6 tests passing.
jouleclaw-fresh L3.5 cascade stage — fresh retrieval with
provenance. SearchProvider trait (Brave / Tavily /
Exa / Serper plug in here), Transport trait (keeps
reqwest out of the standard), TrustTable
(bootstrap from Wikipedia perennial-sources;
normalises `www.` prefix), FreshFetch orchestrator,
`provenance_envelope()` that wraps a RetrievedClaim
into a jouleclaw-prov ClaimProvenance ready to
embed in a cascade receipt. 6 tests passing.
jouleclaw-mcp MCP tool surface with joule metering. MeteredTool
trait + `dispatch_metered()` brackets every tool
call with energy counter reads and emits a
ToolTouch into the running receipt. WireEncoding
(JsonRpc / Cbor) + `negotiate()` defines the
`joule-mcp` capability profile —
`x-jouleclaw/joule-mcp@1` is the canonical
negotiation tag; both endpoints advertise it to
opt into CBOR, otherwise stays JSON-RPC for
compat. 5 tests passing.
jouleclaw-cli `jclaw` binary — the Pi-class minimal harness.
v0.1 ships `version`, `tier-walk`, `receipt`,
`meter` subcommands plus a heuristic router that
picks a tier from input shape. Receipt builder is
live; energy counter binding is a deployment
choice (feature-gated per platform). Hot-reload +
tree-forked sessions + full MCP client land
later — v0.1 proves the type vocabulary and the
end-to-end receipt emission. 4 tests passing.
Phase 6 cumulative
17 → 29 crates in the workspace.
~155k → ~170k LOC.
440 → 866 tests passing.
Net-new code surface: ~1500 LOC of standard-defining moat
(jouleclaw-prov + fresh + mcp + cli).
Next: SPEC.md + CHARTER + conformance/v1 wire vectors + the
memory entry. Then promote to a public release tag.
Co-Authored-By: Claude Opus 4.7 <[email protected]>
129 lines
4.4 KiB
Python
129 lines
4.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate the Rust-side tokenizer fixture by running HF's
|
|
AutoTokenizer over a fixed list of (premise, hypothesis) pairs and
|
|
dumping the canonical token ids + attention masks to JSON.
|
|
|
|
The Rust tests load that JSON and assert byte-identical output from
|
|
our tokenizer wrapper. Anytime we adjust normalization, special-token
|
|
handling, or template processing, we re-run this and the diff is
|
|
the contract change.
|
|
|
|
Usage:
|
|
python3 crates/joule-deberta/scripts/hf_reference_tokenizer.py
|
|
|
|
Writes:
|
|
crates/joule-deberta/fixtures/tokenizer_pairs.json
|
|
|
|
Requires:
|
|
transformers, sentencepiece (both already installed locally per
|
|
the Phase 4a setup).
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from transformers import AutoTokenizer # type: ignore
|
|
|
|
MODEL_DIR = Path("models/deberta-v3-large-mnli")
|
|
OUT_PATH = Path("crates/joule-deberta/fixtures/tokenizer_pairs.json")
|
|
|
|
# Pairs picked to exercise: short canonical NLI, multi-sentence
|
|
# premise, punctuation/contraction edge cases, all-uppercase, mixed
|
|
# scripts (none of these models support extended Unicode in NLI but
|
|
# tokenization shouldn't break), and a "Paris is the capital of
|
|
# France" pair that mirrors the spec's running example.
|
|
TEST_PAIRS = [
|
|
{
|
|
"name": "simple_entail",
|
|
"premise": "Paris is the capital of France.",
|
|
"hypothesis": "France's capital is Paris.",
|
|
},
|
|
{
|
|
"name": "simple_contradict",
|
|
"premise": "The cat sat on the mat.",
|
|
"hypothesis": "There was no cat anywhere.",
|
|
},
|
|
{
|
|
"name": "neutral",
|
|
"premise": "Marie Curie won the Nobel Prize in Physics in 1903.",
|
|
"hypothesis": "She also worked on radioactive materials.",
|
|
},
|
|
{
|
|
"name": "contraction_punct",
|
|
"premise": "It's the world's biggest waterfall, isn't it?",
|
|
"hypothesis": "Niagara Falls is the world's largest waterfall.",
|
|
},
|
|
{
|
|
"name": "all_upper",
|
|
"premise": "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG.",
|
|
"hypothesis": "A fox jumped over a dog.",
|
|
},
|
|
{
|
|
"name": "long_premise_short_hyp",
|
|
"premise": (
|
|
"Deep Space 1 was launched on October 24, 1998, as the first mission "
|
|
"of NASA's New Millennium Program. It tested twelve advanced "
|
|
"technologies, including the Remote Agent autonomous control system, "
|
|
"the first time an AI system had primary command of a spacecraft."
|
|
),
|
|
"hypothesis": "Deep Space 1 was launched in 1998.",
|
|
},
|
|
]
|
|
|
|
|
|
def main() -> int:
|
|
if not MODEL_DIR.exists():
|
|
print(f"ERR: model dir {MODEL_DIR} not found", file=sys.stderr)
|
|
return 1
|
|
|
|
tok = AutoTokenizer.from_pretrained(str(MODEL_DIR))
|
|
out = {
|
|
"tokenizer_source": str(MODEL_DIR),
|
|
"tokenizer_class": tok.__class__.__name__,
|
|
"vocab_size": tok.vocab_size,
|
|
"model_max_length": getattr(tok, "model_max_length", None),
|
|
"special_tokens": {
|
|
"cls": tok.cls_token,
|
|
"sep": tok.sep_token,
|
|
"pad": tok.pad_token,
|
|
"unk": tok.unk_token,
|
|
"cls_id": tok.cls_token_id,
|
|
"sep_id": tok.sep_token_id,
|
|
"pad_id": tok.pad_token_id,
|
|
"unk_id": tok.unk_token_id,
|
|
},
|
|
"pairs": [],
|
|
}
|
|
for case in TEST_PAIRS:
|
|
# `encode_plus(text, text_pair)` gives us the canonical NLI
|
|
# encoding: [CLS] premise [SEP] hypothesis [SEP]. We don't
|
|
# truncate or pad here — we want the raw, unpadded ids so
|
|
# the Rust side can match exactly.
|
|
enc = tok(
|
|
case["premise"],
|
|
case["hypothesis"],
|
|
add_special_tokens=True,
|
|
truncation=False,
|
|
padding=False,
|
|
return_attention_mask=True,
|
|
return_tensors=None,
|
|
)
|
|
out["pairs"].append({
|
|
"name": case["name"],
|
|
"premise": case["premise"],
|
|
"hypothesis": case["hypothesis"],
|
|
"token_ids": enc["input_ids"],
|
|
"attention_mask": enc["attention_mask"],
|
|
"token_strings": tok.convert_ids_to_tokens(enc["input_ids"]),
|
|
})
|
|
|
|
OUT_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
OUT_PATH.write_text(json.dumps(out, ensure_ascii=False, indent=2))
|
|
print(f"wrote {OUT_PATH} with {len(TEST_PAIRS)} pairs")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|