Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions contracts/contracts.nt
Original file line number Diff line number Diff line change
Expand Up @@ -13998,6 +13998,11 @@
<https://ont.paiml.dev/v1alpha1/contract/tracing-observability-v1> <https://ont.paiml.dev/v1alpha1/id> "tracing-observability-v1"^^<http://www.w3.org/2001/XMLSchema#string> .
<https://ont.paiml.dev/v1alpha1/contract/tracing-observability-v1> <https://ont.paiml.dev/v1alpha1/name> "tracing-observability"^^<http://www.w3.org/2001/XMLSchema#string> .
<https://ont.paiml.dev/v1alpha1/contract/tracing-observability-v1> <https://ont.paiml.dev/v1alpha1/version> "1.0.0"^^<http://www.w3.org/2001/XMLSchema#string> .
<https://ont.paiml.dev/v1alpha1/contract/train-loader-try-load-apr-v1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/ns/prov#Entity> .
<https://ont.paiml.dev/v1alpha1/contract/train-loader-try-load-apr-v1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://ont.paiml.dev/v1alpha1/Contract> .
<https://ont.paiml.dev/v1alpha1/contract/train-loader-try-load-apr-v1> <https://ont.paiml.dev/v1alpha1/file> "contracts/train-loader-try-load-apr-v1.yaml"^^<http://www.w3.org/2001/XMLSchema#string> .
<https://ont.paiml.dev/v1alpha1/contract/train-loader-try-load-apr-v1> <https://ont.paiml.dev/v1alpha1/id> "train-loader-try-load-apr-v1"^^<http://www.w3.org/2001/XMLSchema#string> .
<https://ont.paiml.dev/v1alpha1/contract/train-loader-try-load-apr-v1> <https://ont.paiml.dev/v1alpha1/kind> "pattern"^^<http://www.w3.org/2001/XMLSchema#string> .
<https://ont.paiml.dev/v1alpha1/contract/train-test-split-ceil-v1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/ns/prov#Entity> .
<https://ont.paiml.dev/v1alpha1/contract/train-test-split-ceil-v1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://ont.paiml.dev/v1alpha1/Contract> .
<https://ont.paiml.dev/v1alpha1/contract/train-test-split-ceil-v1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://ont.paiml.dev/v1alpha1/Kernel> .
Expand Down
68 changes: 34 additions & 34 deletions contracts/shapes.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -1983,11 +1983,11 @@
] ;
sh:property [
sh:path <https://ont.paiml.dev/v1alpha1/model/parityReceipt> ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/ladder-green/node> ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/ladder-green/node/parityReceipt> ;
] ;
.

<https://ont.paiml.dev/v1alpha1/shape/ladder-green/node> a sh:NodeShape ;
<https://ont.paiml.dev/v1alpha1/shape/ladder-green/node/parityReceipt> a sh:NodeShape ;
sh:property [
sh:path <https://ont.paiml.dev/v1alpha1/model/cc> ;
sh:in ( "8.9" "12.1" ) ;
Expand Down Expand Up @@ -2067,11 +2067,11 @@
sh:maxCount 1 ;
sh:class <https://ont.paiml.dev/v1alpha1/parity/Comparator> ;
sh:nodeKind sh:IRI ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/parity-receipt-complete/node> ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/parity-receipt-complete/node/comparator> ;
] ;
.

<https://ont.paiml.dev/v1alpha1/shape/parity-receipt-complete/node> a sh:NodeShape ;
<https://ont.paiml.dev/v1alpha1/shape/parity-receipt-complete/node/comparator> a sh:NodeShape ;
sh:property [
sh:path <https://ont.paiml.dev/v1alpha1/parity/kind> ;
sh:minCount 1 ;
Expand All @@ -2084,11 +2084,11 @@
sh:targetClass <https://ont.paiml.dev/v1alpha1/parity/SelfComparedReceipt> ;
sh:property [
sh:path <https://ont.paiml.dev/v1alpha1/parity/comparator> ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/parity-comparator-self/node> ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/parity-comparator-self/node/comparator> ;
] ;
.

<https://ont.paiml.dev/v1alpha1/shape/parity-comparator-self/node> a sh:NodeShape ;
<https://ont.paiml.dev/v1alpha1/shape/parity-comparator-self/node/comparator> a sh:NodeShape ;
sh:property [
sh:path <https://ont.paiml.dev/v1alpha1/parity/reason> ;
sh:minCount 1 ;
Expand All @@ -2103,11 +2103,11 @@
sh:targetClass <https://ont.paiml.dev/v1alpha1/parity/OracleComparedReceipt> ;
sh:property [
sh:path <https://ont.paiml.dev/v1alpha1/parity/comparator> ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/parity-comparator-oracle/node> ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/parity-comparator-oracle/node/comparator> ;
] ;
.

<https://ont.paiml.dev/v1alpha1/shape/parity-comparator-oracle/node> a sh:NodeShape ;
<https://ont.paiml.dev/v1alpha1/shape/parity-comparator-oracle/node/comparator> a sh:NodeShape ;
sh:property [
sh:path <https://ont.paiml.dev/v1alpha1/parity/comparatorSha> ;
sh:minCount 1 ;
Expand Down Expand Up @@ -2194,7 +2194,7 @@
sh:minCount 1 ;
sh:maxCount 1 ;
sh:nodeKind sh:IRI ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.release/node> ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.release/node/dogfoodReceipt> ;
] ;
sh:property [
sh:path <https://ont.paiml.dev/v1alpha1/release/surface> ;
Expand All @@ -2217,7 +2217,7 @@
] ;
.

<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.release/node> a sh:NodeShape ;
<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.release/node/dogfoodReceipt> a sh:NodeShape ;
sh:property [
sh:path <https://ont.paiml.dev/v1alpha1/release/verdict> ;
sh:minCount 1 ;
Expand All @@ -2239,7 +2239,7 @@
sh:minCount 1 ;
sh:maxCount 1 ;
sh:nodeKind sh:IRI ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.host/node> ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.host/node/hostReceipt> ;
] ;
sh:property [
sh:path <https://ont.paiml.dev/v1alpha1/release/holds> ;
Expand All @@ -2257,7 +2257,7 @@
sh:path <https://ont.paiml.dev/v1alpha1/release/kernelReceipt> ;
sh:minCount 1 ;
sh:nodeKind sh:IRI ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.host/node> ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.host/node/kernelReceipt> ;
] ;
sh:property [
sh:path <https://ont.paiml.dev/v1alpha1/release/dispatchKernel> ;
Expand All @@ -2269,7 +2269,7 @@
] ;
.

<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.host/node> a sh:NodeShape ;
<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.host/node/hostReceipt> a sh:NodeShape ;
sh:property [
sh:path <https://ont.paiml.dev/v1alpha1/release/schema> ;
sh:minCount 1 ;
Expand All @@ -2283,7 +2283,7 @@
] ;
.

<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.host/node> a sh:NodeShape ;
<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.host/node/kernelReceipt> a sh:NodeShape ;
sh:property [
sh:path <https://ont.paiml.dev/v1alpha1/release/fresh> ;
sh:minCount 1 ;
Expand Down Expand Up @@ -2347,11 +2347,11 @@
sh:path <https://ont.paiml.dev/v1alpha1/release/row> ;
sh:minCount 1 ;
sh:nodeKind sh:IRI ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.tokenizer/node> ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.tokenizer/node/row> ;
] ;
.

<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.tokenizer/node> a sh:NodeShape ;
<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.tokenizer/node/row> a sh:NodeShape ;
sh:property [
sh:path <https://ont.paiml.dev/v1alpha1/release/verdict> ;
sh:minCount 1 ;
Expand Down Expand Up @@ -2392,11 +2392,11 @@
sh:path <https://ont.paiml.dev/v1alpha1/release/row> ;
sh:minCount 1 ;
sh:nodeKind sh:IRI ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.kernel/node> ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.kernel/node/row> ;
] ;
.

<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.kernel/node> a sh:NodeShape ;
<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.kernel/node/row> a sh:NodeShape ;
sh:property [
sh:path <https://ont.paiml.dev/v1alpha1/release/verdict> ;
sh:minCount 1 ;
Expand Down Expand Up @@ -2448,11 +2448,11 @@
sh:path <https://ont.paiml.dev/v1alpha1/release/cruxCell> ;
sh:minCount 1 ;
sh:nodeKind sh:IRI ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.crux/node> ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.crux/node/cruxCell> ;
] ;
.

<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.crux/node> a sh:NodeShape ;
<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.crux/node/cruxCell> a sh:NodeShape ;
sh:property [
sh:path <https://ont.paiml.dev/v1alpha1/release/verdict> ;
sh:minCount 1 ;
Expand Down Expand Up @@ -2511,11 +2511,11 @@
sh:minCount 1 ;
sh:maxCount 1 ;
sh:nodeKind sh:IRI ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.refusal/node> ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.refusal/node/row> ;
] ;
.

<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.refusal/node> a sh:NodeShape ;
<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.refusal/node/row> a sh:NodeShape ;
sh:property [
sh:path <https://ont.paiml.dev/v1alpha1/release/verdict> ;
sh:minCount 1 ;
Expand Down Expand Up @@ -2543,11 +2543,11 @@
sh:minCount 1 ;
sh:maxCount 1 ;
sh:nodeKind sh:IRI ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.probe/node> ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.probe/node/row> ;
] ;
.

<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.probe/node> a sh:NodeShape ;
<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.probe/node/row> a sh:NodeShape ;
sh:property [
sh:path <https://ont.paiml.dev/v1alpha1/release/verdict> ;
sh:minCount 1 ;
Expand All @@ -2569,11 +2569,11 @@
sh:minCount 1 ;
sh:maxCount 1 ;
sh:nodeKind sh:IRI ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.model-cell/node> ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.model-cell/node/row> ;
] ;
.

<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.model-cell/node> a sh:NodeShape ;
<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.model-cell/node/row> a sh:NodeShape ;
sh:property [
sh:path <https://ont.paiml.dev/v1alpha1/release/verdict> ;
sh:minCount 1 ;
Expand All @@ -2595,11 +2595,11 @@
sh:minCount 1 ;
sh:maxCount 1 ;
sh:nodeKind sh:IRI ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.effect-cell/node> ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.effect-cell/node/row> ;
] ;
.

<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.effect-cell/node> a sh:NodeShape ;
<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1.effect-cell/node/row> a sh:NodeShape ;
sh:property [
sh:path <https://ont.paiml.dev/v1alpha1/release/verdict> ;
sh:minCount 1 ;
Expand All @@ -2626,11 +2626,11 @@
sh:minCount 1 ;
sh:maxCount 1 ;
sh:nodeKind sh:IRI ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1/node> ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1/node/row> ;
] ;
.

<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1/node> a sh:NodeShape ;
<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v1/node/row> a sh:NodeShape ;
sh:property [
sh:path <https://ont.paiml.dev/v1alpha1/release/verdict> ;
sh:minCount 1 ;
Expand Down Expand Up @@ -2721,7 +2721,7 @@
sh:minCount 1 ;
sh:class <https://ont.paiml.dev/v1alpha1/release/KernelParityCell> ;
sh:nodeKind sh:IRI ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v2.model/node> ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v2.model/node/usesKernel> ;
] ;
sh:property [
sh:path <https://ont.paiml.dev/v1alpha1/release/unregisteredQtype> ;
Expand All @@ -2733,7 +2733,7 @@
] ;
.

<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v2.model/node> a sh:NodeShape ;
<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v2.model/node/usesKernel> a sh:NodeShape ;
sh:property [
sh:path <https://ont.paiml.dev/v1alpha1/release/verdict> ;
sh:minCount 1 ;
Expand Down Expand Up @@ -2768,11 +2768,11 @@
sh:maxCount 1 ;
sh:class <https://ont.paiml.dev/v1alpha1/release/SmokeCell> ;
sh:nodeKind sh:IRI ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v2.model-smoke/node> ;
sh:node <https://ont.paiml.dev/v1alpha1/shape/release-readiness-v2.model-smoke/node/smoke> ;
] ;
.

<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v2.model-smoke/node> a sh:NodeShape ;
<https://ont.paiml.dev/v1alpha1/shape/release-readiness-v2.model-smoke/node/smoke> a sh:NodeShape ;
sh:property [
sh:path <https://ont.paiml.dev/v1alpha1/release/verdict> ;
sh:minCount 1 ;
Expand Down
36 changes: 36 additions & 0 deletions contracts/train-loader-try-load-apr-v1.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
metadata:
version: 1.0.0
created: '2026-10-18'
author: PAIML Engineering
description: "config::train::loader try_load_apr — loading a transformer from an APR file or checkpoint directory: unreadable or absent input is None, the checkpoint step comes from the checkpoint_step metadata and otherwise from the model-step-N file name, a directory resolves to its newest step, and the weights round-trip."
kind: pattern
references:
- "crates/aprender-train/src/config/train/loader/data.rs — try_load_apr, try_load_apr_for_inference, find_latest_apr_checkpoint"

equations:
try_load_apr_step_and_weights:
formula: "step == metadata.checkpoint_step if present else parse(file name model-step-N.apr) else 0; a directory picks max N; absent, corrupt or empty-dir input yields None"
domain: "a path (APR file or directory) and a TransformerConfig"
codomain: "Option<(Option<Transformer>, usize)>"
invariants:
- "Metadata step takes precedence over the file-name step"
- "Loaded embedding weights equal the saved ones"

proof_obligations:
- id: TLA-INV-001
type: invariant
property: "Metadata checkpoint_step wins over the file-name step"
formal: "∀ t ∈ {test_try_load_apr_reads_step_from_metadata_over_filename}: exit(t) = 0"
applies_to: try_load_apr_step_and_weights
- id: TLA-INV-002
type: invariant
property: "A directory resolves to its newest model-step-N.apr"
formal: "∀ t ∈ {test_try_load_apr_directory_picks_latest_step_from_filename}: exit(t) = 0"
applies_to: try_load_apr_step_and_weights

falsification_tests:
- id: FALSIFY-TLA-001
rule: "metadata key ignored"
prediction: "reading a different metadata key makes the step fall back to the file name and turns the metadata test RED"
test: "cargo test -p aprender-train --lib test_try_load_apr_reads_step_from_metadata_over_filename"
if_fails: "the checkpoint_step metadata path is unguarded"
75 changes: 75 additions & 0 deletions crates/aprender-train/src/config/train/loader/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2096,3 +2096,78 @@ fn test_tabular_shapes_name_the_offending_batch_index() {
let err = validate_tabular_batch_shapes(&batches, 8).expect_err("batch 2 must be rejected");
assert!(err.to_string().contains("batch 2"), "error must name the batch: {err}");
}

// ── try_load_apr (via try_load_apr_for_inference) ────────────────────────

/// Write `model` as an APR file at `path` with the given extra metadata.
fn write_tiny_apr(path: &std::path::Path, model: &Transformer, meta: &[(&str, &str)]) {
use aprender::serialization::apr::AprWriter;
let mut w = AprWriter::new();
for (k, v) in meta {
w.set_metadata(k.to_string(), serde_json::json!(v));
}
for (name, t) in model.named_parameters() {
let data = t.data();
w.add_tensor_f32(name, vec![data.len()], data.as_slice().expect("contiguous"));
}
w.write(path).expect("write apr");
}

#[test]
fn test_try_load_apr_rejects_missing_path_and_corrupt_file() {
let cfg = TransformerConfig::tiny();
let dir = tempfile::tempdir().expect("tempdir");
assert!(try_load_apr_for_inference(&dir.path().join("absent.apr"), &cfg).is_none());
let bad = dir.path().join("bad.apr");
std::fs::write(&bad, b"not an apr file").expect("write");
assert!(try_load_apr_for_inference(&bad, &cfg).is_none());
// Empty directory: no checkpoint to pick.
assert!(try_load_apr_for_inference(dir.path(), &cfg).is_none());
}

#[test]
fn test_try_load_apr_reads_step_from_metadata_over_filename() {
let cfg = TransformerConfig::tiny();
let model = Transformer::new(&cfg);
let dir = tempfile::tempdir().expect("tempdir");
let p = dir.path().join("model-step-5.apr");
write_tiny_apr(&p, &model, &[("checkpoint_step", "42")]);
let (loaded, step) = try_load_apr_for_inference(&p, &cfg).expect("loads");
assert_eq!(step, 42, "metadata step wins over filename step");
assert_eq!(loaded.expect("model").named_parameters().len(), model.named_parameters().len());
}

#[test]
fn test_try_load_apr_directory_picks_latest_step_from_filename() {
let cfg = TransformerConfig::tiny();
let model = Transformer::new(&cfg);
let dir = tempfile::tempdir().expect("tempdir");
write_tiny_apr(&dir.path().join("model-step-3.apr"), &model, &[]);
write_tiny_apr(&dir.path().join("model-step-12.apr"), &model, &[]);
let (_, step) = try_load_apr_for_inference(dir.path(), &cfg).expect("loads");
assert_eq!(step, 12, "no metadata: step parsed from the newest file name");
}

#[test]
fn test_try_load_apr_round_trips_weights_with_training_tensor_present() {
let cfg = TransformerConfig::tiny();
let model = Transformer::new(&cfg);
let dir = tempfile::tempdir().expect("tempdir");
let p = dir.path().join("model.apr");
{
use aprender::serialization::apr::AprWriter;
let mut w = AprWriter::new();
for (name, t) in model.named_parameters() {
let d = t.data();
w.add_tensor_f32(name, vec![d.len()], d.as_slice().expect("contiguous"));
}
w.add_tensor_f32("__training__.opt_state", vec![2], &[1.0, 2.0]);
w.write(&p).expect("write");
}
let (loaded, step) = try_load_apr_for_inference(&p, &cfg).expect("loads");
assert_eq!(step, 0, "model.apr carries no step");
let loaded = loaded.expect("model");
let a = model.embed_tokens.weight.data();
let b = loaded.embed_tokens.weight.data();
assert_eq!(a.as_slice(), b.as_slice(), "embedding weights round-trip");
}
Loading