| Crates.io | lnmp-core |
| lib.rs | lnmp-core |
| version | 0.5.16 |
| created_at | 2025-11-19 11:11:04.691846+00 |
| updated_at | 2025-12-19 10:03:41.610773+00 |
| description | Core type definitions for LNMP (LLM Native Minimal Protocol) |
| homepage | |
| repository | https://github.com/lnmplang/lnmp-protocol |
| max_upload_size | |
| id | 1939907 |
| size | 276,708 |
Core type definitions for LNMP (LLM Native Minimal Protocol) v0.5.13.
FID Registry: All examples use official Field IDs from
registry/fids.yaml.
This crate provides the fundamental data structures for representing LNMP data:
FieldId - Type alias for field identifiers (u16, range 0-65535)LnmpValue - Enum representing all supported value types (including nested structures and generic arrays)RecordBuilder - Fluent API for constructing canonical records with automatic field sortingLnmpField - A field ID and value pairLnmpRecord - A collection of fields representing a complete recordSemanticChecksum - SC32 checksum computation for semantic fidelity (v0.3)TypeHint - Type annotations for explicit typing (including nested types and generic arrays)LnmpProfile - Strictness profiles (Loose, Standard, Strict) for validation and canonical enforcementStrictDeterministicConfig - Fine-grained control over determinism requirementsAdd to your Cargo.toml:
[dependencies]
lnmp-core = { path = "path/to/lnmp-core" }
This crate includes several examples in the examples/ directory:
Run examples with:
cargo run --example basic_record -p lnmp-core
cargo run --example nested_structures -p lnmp-core
use lnmp_core::{LnmpField, LnmpRecord, LnmpValue};
// Create a new record
let mut record = LnmpRecord::new();
// Add fields with different value types
record.add_field(LnmpField {
fid: 12,
value: LnmpValue::Int(14532),
});
record.add_field(LnmpField {
fid: 7,
value: LnmpValue::Bool(true),
});
record.add_field(LnmpField {
fid: 20,
value: LnmpValue::String("Halil".to_string()),
});
record.add_field(LnmpField {
fid: 23,
value: LnmpValue::StringArray(vec![
"admin".to_string(),
"dev".to_string(),
]),
});
// Access fields
if let Some(field) = record.get_field(12) {
println!("User ID: {:?}", field.value);
}
// Iterate over all fields
for field in record.fields() {
println!("F{} = {:?}", field.fid, field.value);
}
// Get field count
println!("Total fields: {}", record.fields().len());
Represents all supported value types in LNMP v0.3:
pub enum LnmpValue {
Int(i64), // Integer values
Float(f64), // Floating-point values
Bool(bool), // Boolean values (true/false)
String(String), // String values
StringArray(Vec<String>), // Arrays of strings
IntArray(Vec<i64>), // Arrays of integers (v0.5.4)
FloatArray(Vec<f64>), // Arrays of floats (v0.5.4)
BoolArray(Vec<bool>), // Arrays of booleans (v0.5.4)
NestedRecord(Box<LnmpRecord>), // Nested records (v0.3)
NestedArray(Vec<LnmpRecord>), // Arrays of records (v0.3)
}
| Type Code | Description | Example |
|---|---|---|
:sa |
String Array | StringArray(["a", "b"]) |
:ia |
Int Array | IntArray([1, 2, 3]) |
:fa |
Float Array | FloatArray([1.1, 2.2]) |
:ba |
Bool Array | BoolArray([true, false]) |
:r |
Nested Record | NestedRecord(...) |
v0.3 Nested Structure Support:
use lnmp_core::{LnmpField, LnmpRecord, LnmpValue};
// Create a nested record (F70=nested_data from registry)
let mut inner_record = LnmpRecord::new();
inner_record.add_field(LnmpField {
fid: 20, // F20=name
value: LnmpValue::String("nested".to_string()),
});
let mut outer_record = LnmpRecord::new();
outer_record.add_field(LnmpField {
fid: 70, // F70=nested_data
value: LnmpValue::NestedRecord(Box::new(inner_record)),
});
// Create a record array (F71=record_list from registry)
let mut record1 = LnmpRecord::new();
record1.add_field(LnmpField { fid: 12, value: LnmpValue::Int(1) }); // F12=user_id
let mut record2 = LnmpRecord::new();
record2.add_field(LnmpField { fid: 12, value: LnmpValue::Int(2) }); // F12=user_id
let mut parent = LnmpRecord::new();
parent.add_field(LnmpField {
fid: 71, // F71=record_list
value: LnmpValue::NestedArray(vec![record1, record2]),
});
A single field assignment consisting of a field ID and value:
pub struct LnmpField {
pub fid: FieldId, // Field identifier (0-65535)
pub value: LnmpValue, // Field value
}
A collection of fields representing a complete LNMP record:
impl LnmpRecord {
pub fn new() -> Self;
pub fn add_field(&mut self, field: LnmpField);
pub fn get_field(&self, fid: FieldId) -> Option<&LnmpField>;
pub fn fields(&self) -> &[LnmpField];
pub fn into_fields(self) -> Vec<LnmpField>;
}
Fluent API for constructing records with automatic canonical ordering:
use lnmp_core::{RecordBuilder, LnmpField, LnmpValue};
// Builder automatically sorts fields by FID
let record = RecordBuilder::new()
.add_field(LnmpField { fid: 23, value: LnmpValue::Int(3) })
.add_field(LnmpField { fid: 7, value: LnmpValue::Bool(true) })
.add_field(LnmpField { fid: 12, value: LnmpValue::Int(2) })
.build();
// Fields are stored in canonical order: 7, 12, 23
assert_eq!(record.fields()[0].fid, 7);
// Alternative: construct from unsorted fields
let fields = vec![
LnmpField { fid: 30, value: LnmpValue::Int(1) }, // F30=count
LnmpField { fid: 12, value: LnmpValue::Int(2) }, // F12=user_id
];
let record = RecordBuilder::from_fields(fields);
Type annotations for explicit typing (v0.2+):
pub enum TypeHint {
Int, // :i
Float, // :f
Bool, // :b
String, // :s
StringArray, // :sa
IntArray, // :ia (v0.5.4)
FloatArray, // :fa (v0.5.4)
BoolArray, // :ba (v0.5.4)
Record, // :r (v0.3)
RecordArray, // :ra (v0.3)
}
Compute and validate semantic checksums (SC32) for drift prevention:
use lnmp_core::{SemanticChecksum, TypeHint, LnmpValue};
// Compute checksum
let checksum = SemanticChecksum::compute(
12, // field ID
TypeHint::Int,
&LnmpValue::Int(14532)
);
// Validate checksum
let is_valid = SemanticChecksum::validate(
12,
TypeHint::Int,
&LnmpValue::Int(14532),
checksum
);
// Format as hex string
let hex = SemanticChecksum::format(checksum); // "36AAE667"
Support for hierarchical data modeling:
F70={F12=1;F7=1} - Records within records (F70=nested_data)F71=[{F12=1},{F12=2}] - Arrays of records (F71=record_list)value.validate_structure() ensures integrity32-bit checksums for preventing LLM input drift:
Canonical transformations for semantic equivalence:
true/false/yes/no/1/0 → 1 or 0-0.0 → 0.0, 3.140 → 3.14LNMP-Core provides strong deterministic guarantees to prevent LLM drift and ensure semantic consistency:
The sorted_fields() method provides canonical representation where fields are sorted by FID (Field ID):
let mut record = LnmpRecord::new();
record.add_field(LnmpField { fid: 23, value: LnmpValue::Int(3) });
record.add_field(LnmpField { fid: 7, value: LnmpValue::Int(1) });
record.add_field(LnmpField { fid: 12, value: LnmpValue::Int(2) });
// Insertion order: 23, 7, 12
assert_eq!(record.fields()[0].fid, 23);
assert_eq!(record.fields()[1].fid, 7);
// Canonical order (sorted by FID): 7, 12, 23
let sorted = record.sorted_fields();
assert_eq!(sorted[0].fid, 7);
assert_eq!(sorted[1].fid, 12);
assert_eq!(sorted[2].fid, 23);
Key Points:
fields() returns fields in insertion ordersorted_fields() returns fields in canonical order (sorted by FID)lnmp-codec) use sorted_fields() for outputSemanticChecksum uses sorted_fields() for deterministic hashingThe SemanticChecksum system ensures the same semantic data always produces the same checksum, regardless of field insertion order:
use lnmp_core::{LnmpRecord, LnmpField, LnmpValue, TypeHint};
use lnmp_core::checksum::SemanticChecksum;
// Record 1: fields added in order 12, 7
let mut rec1 = LnmpRecord::new();
rec1.add_field(LnmpField { fid: 12, value: LnmpValue::Int(100) });
rec1.add_field(LnmpField { fid: 7, value: LnmpValue::Bool(true) });
// Record 2: fields added in order 7, 12 (different!)
let mut rec2 = LnmpRecord::new();
rec2.add_field(LnmpField { fid: 7, value: LnmpValue::Bool(true) });
rec2.add_field(LnmpField { fid: 12, value: LnmpValue::Int(100) });
// When serialized for checksum, both use sorted_fields internally
// So nested records with different insertion order produce SAME checksum
let val1 = LnmpValue::NestedRecord(Box::new(rec1));
let val2 = LnmpValue::NestedRecord(Box::new(rec2));
let cs1 = SemanticChecksum::compute(70, Some(TypeHint::Record), &val1); // F70=nested_data
let cs2 = SemanticChecksum::compute(70, Some(TypeHint::Record), &val2);
assert_eq!(cs1, cs2); // ✅ Same checksum despite different insertion order!
Normalization Rules:
1 (true) or 0 (false)-0.0 normalized to 0.0, trailing zeros removed (e.g., 3.140 → 3.14)All encoders in lnmp-codec produce canonical output:
use lnmp_codec::Encoder;
// Fields added in non-canonical order
let mut record = LnmpRecord::new();
record.add_field(LnmpField { fid: 23, value: LnmpValue::Int(3) });
record.add_field(LnmpField { fid: 7, value: LnmpValue::Int(1) });
let encoder = Encoder::new();
let output = encoder.encode(&record);
// Output is ALWAYS sorted by FID (canonical):
// F7=1
// F23=3
Binary Encoding:
sorted_fields()For Deterministic Behavior:
sorted_fields() when comparing records semanticallySemanticChecksum to detect driftWhen Order Matters:
fields() to preserve insertion orderPartialEq compares in insertion order (structural equality)For semantic comparison that ignores field order:
use lnmp_core::{LnmpRecord, LnmpField, LnmpValue};
let mut rec1 = RecordBuilder::new()
.add_field(LnmpField { fid: 12, value: LnmpValue::Int(100) })
.add_field(LnmpField { fid: 7, value: LnmpValue::Bool(true) })
.build();
let mut rec2 = RecordBuilder::new()
.add_field(LnmpField { fid: 7, value: LnmpValue::Bool(true) })
.add_field(LnmpField { fid: 12, value: LnmpValue::Int(100) })
.build();
// Structural equality: false (different field order)
assert_ne!(rec1, rec2);
// Canonical equality: true (same fields, semantically)
assert!(rec1.canonical_eq(&rec2));
The core types are fully compatible with the v0.4 binary protocol format:
For binary encoding/decoding, use the lnmp-codec crate:
use lnmp_codec::binary::{BinaryEncoder, BinaryDecoder};
use lnmp_core::{LnmpRecord, LnmpField, LnmpValue};
let mut record = LnmpRecord::new();
record.add_field(LnmpField {
fid: 12,
value: LnmpValue::Int(14532),
});
// Encode to binary
let encoder = BinaryEncoder::new();
let binary = encoder.encode(&record).unwrap();
// Decode from binary
let decoder = BinaryDecoder::new();
let decoded_record = decoder.decode(&binary).unwrap();
Validate fields against the official FID registry at runtime:
use lnmp_core::registry::{embedded_registry, FidRegistry, ValidationResult};
// Load embedded registry
let registry = embedded_registry();
// Validate a field
let result = registry.validate_field(&field);
match result {
ValidationResult::Valid => println!("OK"),
ValidationResult::TypeMismatch { fid, expected, found } => {
println!("F{}: expected {:?}, found {:?}", fid, expected, found);
}
ValidationResult::UnknownFid { fid, range } => {
println!("F{} not in registry ({:?} range)", fid, range);
}
_ => {}
}
Manage FID registry versions across multiple peers:
use lnmp_core::registry::RegistrySync;
let mut sync = RegistrySync::with_embedded();
// Track peer registry versions
sync.register_peer("peer-1".into(), "0.9.0".into());
sync.register_peer("peer-2".into(), "1.1.0".into());
// Check version comparison
if sync.is_ahead_of("peer-1") {
let fids_to_send = sync.delta_fids_for("peer-1");
println!("Need to sync {} FIDs to peer-1", fids_to_send.len());
}
if sync.is_behind("peer-2") {
println!("Request registry update from peer-2");
}
v0.3 is backward compatible with v0.2. New features:
LnmpValue::NestedRecord and LnmpValue::NestedArray variantsTypeHint::Record and TypeHint::RecordArray variantsSemanticChecksum module for checksum computationdepth() and validate_structure() methods on LnmpValuev0.4 adds binary protocol support (via lnmp-codec) with no changes to core types.
v0.5.14 adds:
FidRegistry with runtime validationRegistrySync for multi-peer version trackingembedded_registry() for compile-time embedded FIDsExisting v0.2 code continues to work without changes.
MIT OR Apache-2.0