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]>
130 lines
4.6 KiB
Python
130 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Dump the tensor that comes out of layer-0's attention sub-block —
|
|
the input to the FFN sub-block. Specifically:
|
|
|
|
attn_out = SelfOutput(disentangled_attn(embedding_output, ...),
|
|
residual=embedding_output)
|
|
= LayerNorm(attn_output_dense @ context + bias + embedding_output)
|
|
|
|
This is the verification target for Phase 4e: the Rust implementation
|
|
of disentangled attention + residual + LayerNorm must match this
|
|
within fp16-tolerance.
|
|
|
|
Also dumps:
|
|
- the attention_mask 4D shape (for cross-checking my mask logic)
|
|
- the relative_pos tensor (the bucketed [L, L] matrix)
|
|
- the rel_embeddings tensor *after* the encoder-level LayerNorm
|
|
(this is what disentangled_attention_bias actually consumes)
|
|
|
|
Usage:
|
|
python3 crates/joule-deberta/scripts/hf_reference_layer0_attention.py
|
|
|
|
Writes:
|
|
crates/joule-deberta/fixtures/layer0_attention.json
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import torch
|
|
from transformers import AutoModelForSequenceClassification, AutoTokenizer # type: ignore
|
|
|
|
MODEL_DIR = Path("models/deberta-v3-large-mnli")
|
|
OUT_PATH = Path("crates/joule-deberta/fixtures/layer0_attention.json")
|
|
|
|
PREMISE = "Paris is the capital of France."
|
|
HYPOTHESIS = "France's capital is Paris."
|
|
|
|
|
|
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))
|
|
model = AutoModelForSequenceClassification.from_pretrained(
|
|
str(MODEL_DIR), torch_dtype=torch.float32
|
|
)
|
|
model.eval()
|
|
|
|
enc = tok(
|
|
PREMISE, HYPOTHESIS,
|
|
add_special_tokens=True, truncation=False, padding=False,
|
|
return_attention_mask=True, return_tensors="pt",
|
|
)
|
|
input_ids = enc["input_ids"]
|
|
attention_mask = enc["attention_mask"]
|
|
|
|
encoder = model.deberta.encoder
|
|
emb_layer = model.deberta.embeddings
|
|
layer0 = encoder.layer[0]
|
|
|
|
with torch.no_grad():
|
|
# Embedding output (verified-matching in phase 4d).
|
|
word_embeds = emb_layer.word_embeddings(input_ids)
|
|
ln_out = emb_layer.LayerNorm(word_embeds)
|
|
mask_b = attention_mask.unsqueeze(-1).to(ln_out.dtype)
|
|
embedding_output = ln_out * mask_b
|
|
|
|
# Encoder-level prep.
|
|
extended_attention_mask = encoder.get_attention_mask(attention_mask)
|
|
relative_pos = encoder.get_rel_pos(embedding_output)
|
|
rel_embeddings = encoder.get_rel_embedding()
|
|
|
|
# Layer-0 attention only (not the FFN).
|
|
attn_output, _ = layer0.attention(
|
|
embedding_output,
|
|
extended_attention_mask,
|
|
output_attentions=False,
|
|
query_states=None,
|
|
relative_pos=relative_pos,
|
|
rel_embeddings=rel_embeddings,
|
|
)
|
|
|
|
# Strip batch dim.
|
|
embedding_output = embedding_output.squeeze(0)
|
|
attn_output = attn_output.squeeze(0)
|
|
extended_mask_shape = list(extended_attention_mask.shape)
|
|
rel_pos_flat = relative_pos.squeeze(0).flatten().to(torch.int64).tolist()
|
|
|
|
seq_len, hidden = attn_output.shape
|
|
|
|
OUT_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
with OUT_PATH.open("w") as f:
|
|
json.dump({
|
|
"model_source": str(MODEL_DIR),
|
|
"case_name": "simple_entail",
|
|
"premise": PREMISE,
|
|
"hypothesis": HYPOTHESIS,
|
|
"input_ids": input_ids.squeeze(0).tolist(),
|
|
"attention_mask": attention_mask.squeeze(0).tolist(),
|
|
"seq_len": seq_len,
|
|
"hidden_size": hidden,
|
|
"extended_attention_mask_shape": extended_mask_shape,
|
|
"relative_pos_flat": rel_pos_flat,
|
|
"relative_pos_shape": [relative_pos.shape[1], relative_pos.shape[2]],
|
|
"rel_embeddings_normed_flat":
|
|
rel_embeddings.flatten().tolist(),
|
|
"rel_embeddings_shape": list(rel_embeddings.shape),
|
|
"embedding_output_flat": embedding_output.flatten().tolist(),
|
|
"layer0_attn_output_flat": attn_output.flatten().tolist(),
|
|
"stats": {
|
|
"embed_abs_mean": float(embedding_output.abs().mean()),
|
|
"attn_out_abs_mean": float(attn_output.abs().mean()),
|
|
"attn_out_min": float(attn_output.min()),
|
|
"attn_out_max": float(attn_output.max()),
|
|
},
|
|
}, f)
|
|
print(
|
|
f"wrote {OUT_PATH}: seq_len={seq_len} hidden={hidden} "
|
|
f"attn_out_abs_mean={attn_output.abs().mean():.4f} "
|
|
f"rel_pos_shape={list(relative_pos.shape)} "
|
|
f"rel_embeddings_shape={list(rel_embeddings.shape)}"
|
|
)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|