repositories / smith
smith
There are many coding harnesses - but this one is fast
owned by admin
smith-core/src/metadata.rs
Raw//! Typed metadata records and zero-allocation validation (`SMH-SPEC-SPEC0001`).
use serde::de::value::{Error as ValueError, StrDeserializer};
use serde::{Deserialize, Serialize};
use smith::error::MutationState;
use smith::id::ToolCallId;
use smith::structured::{AccessError, Kind, StructuredData, StructuredValue};
use crate::session::{COMPACTION_KIND, PROVIDER_HTTP_KIND, TOOL_MUTATION_KIND};
/// One tool completion's durable mutation state.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ToolMutationMetadata {
/// Identifier of the completed tool call.
pub call_id: ToolCallId,
/// Best-known host mutation outcome.
pub mutation_state: MutationState,
}
impl ToolMutationMetadata {
/// A typed mutation record.
#[must_use]
pub const fn new(call_id: ToolCallId, mutation_state: MutationState) -> Self {
Self {
call_id,
mutation_state,
}
}
}
/// A persisted compaction summary.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct CompactionMetadata {
/// Summary text.
pub summary: String,
/// Whether the producer knew the summary was complete.
#[serde(
default,
deserialize_with = "deserialize_complete",
skip_serializing_if = "Option::is_none"
)]
pub complete: Option<bool>,
}
fn deserialize_complete<'de, D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<Option<bool>, D::Error> {
bool::deserialize(deserializer).map(Some)
}
/// Encode a typed metadata record as structured data.
///
/// # Errors
///
/// Returns `ENTRY_DECODE` when the record cannot be represented as structured data.
pub fn encode<Record: Serialize>(record: &Record) -> smith::Result<StructuredData> {
StructuredData::from_serializable(record).map_err(|error| smith::SmithError::Session {
code: smith::error::SessionCode::EntryDecode,
message: format!("cannot encode metadata: {error}"),
})
}
/// Validate a reserved metadata record without materializing its structured tree.
///
/// Unknown extension kinds remain unrestricted structured data.
///
/// # Errors
///
/// Returns [`AccessError::Malformed`] when a reserved kind has an invalid shape.
pub fn validate<'a, Value: StructuredValue<'a>>(
kind: &str,
value: Value,
) -> Result<(), AccessError> {
match kind {
COMPACTION_KIND => compaction(value),
TOOL_MUTATION_KIND => tool_mutation(value),
PROVIDER_HTTP_KIND => provider_effect(value),
_ => Ok(()),
}
}
fn fields<'a, Value: StructuredValue<'a>>(value: Value) -> Result<Value::Fields, AccessError> {
value.fields()?.ok_or(AccessError::Malformed)
}
fn string<'a, Value: StructuredValue<'a>>(value: Value) -> Result<&'a str, AccessError> {
match value.kind()? {
Kind::String(value) => Ok(value),
_ => Err(AccessError::Malformed),
}
}
fn boolean<'a, Value: StructuredValue<'a>>(value: Value) -> Result<bool, AccessError> {
match value.kind()? {
Kind::Bool(value) => Ok(value),
_ => Err(AccessError::Malformed),
}
}
fn unsigned<'a, Value: StructuredValue<'a>>(value: Value) -> Result<u64, AccessError> {
match value.kind()? {
Kind::Unsigned(value) => Ok(value),
Kind::Signed(value) if value >= 0 => Ok(value.cast_unsigned()),
_ => Err(AccessError::Malformed),
}
}
fn scalar<'a, Record: Deserialize<'a>>(value: &str) -> Result<Record, AccessError> {
Record::deserialize(StrDeserializer::<ValueError>::new(value))
.map_err(|_| AccessError::Malformed)
}
fn compaction<'a, Value: StructuredValue<'a>>(value: Value) -> Result<(), AccessError> {
let mut summary = false;
for field in fields(value)? {
let (name, value) = field?;
match name {
"summary" => {
string(value)?;
summary = true;
}
"complete" => {
boolean(value)?;
}
_ => {}
}
}
summary.then_some(()).ok_or(AccessError::Malformed)
}
fn tool_mutation<'a, Value: StructuredValue<'a>>(value: Value) -> Result<(), AccessError> {
let mut call_id = false;
let mut mutation_state = false;
for field in fields(value)? {
let (name, value) = field?;
match name {
"call_id" if !call_id => {
let _: ToolCallId = scalar(string(value)?)?;
call_id = true;
}
"mutation_state" if !mutation_state => {
let _: MutationState = scalar(string(value)?)?;
mutation_state = true;
}
_ => return Err(AccessError::Malformed),
}
}
(call_id && mutation_state)
.then_some(())
.ok_or(AccessError::Malformed)
}
fn required<'a, Value: StructuredValue<'a>>(
value: Value,
name: &str,
) -> Result<Value, AccessError> {
value.field(name)?.ok_or(AccessError::Malformed)
}
fn exact_fields<'a, Value: StructuredValue<'a>>(
value: Value,
expected: &[&str],
) -> Result<(), AccessError> {
let fields = fields(value)?;
if fields.len() != expected.len() {
return Err(AccessError::Malformed);
}
for field in fields {
let (name, _) = field?;
if !expected.contains(&name) {
return Err(AccessError::Malformed);
}
}
Ok(())
}
fn provider_effect<'a, Value: StructuredValue<'a>>(value: Value) -> Result<(), AccessError> {
match string(required(value, "phase")?)? {
"intent" => {
exact_fields(
value,
&["phase", "method", "url", "headers", "request_bytes"],
)?;
if string(required(value, "method")?)? != "POST" {
return Err(AccessError::Malformed);
}
string(required(value, "url")?)?;
usize::try_from(unsigned(required(value, "request_bytes")?)?)
.map_err(|_| AccessError::Malformed)?;
let headers = required(value, "headers")?
.elements()?
.ok_or(AccessError::Malformed)?;
for header in headers {
header_fields(header?)?;
}
}
"outcome" => {
exact_fields(value, &["phase", "status", "response_bytes", "outcome"])?;
u16::try_from(unsigned(required(value, "status")?)?)
.map_err(|_| AccessError::Malformed)?;
unsigned(required(value, "response_bytes")?)?;
let _: smith::provider::ProviderEffectOutcome =
scalar(string(required(value, "outcome")?)?)?;
}
_ => return Err(AccessError::Malformed),
}
Ok(())
}
fn header_fields<'a, Value: StructuredValue<'a>>(value: Value) -> Result<(), AccessError> {
let mut name = false;
let mut header_value = false;
for field in fields(value)? {
let (field_name, field_value) = field?;
match field_name {
"name" if !name => {
string(field_value)?;
name = true;
}
"value" if !header_value => {
string(field_value)?;
header_value = true;
}
"name" | "value" => return Err(AccessError::Malformed),
_ => {}
}
}
(name && header_value)
.then_some(())
.ok_or(AccessError::Malformed)
}
#[cfg(test)]
mod tests {
//! SMH-SPEC-SPEC0001: Shared structured data.
use super::*;
#[cfg(target_arch = "x86_64")]
#[test]
fn metadata_layouts_are_exact() {
assert_eq!(std::mem::size_of::<ToolMutationMetadata>(), 17);
assert_eq!(std::mem::size_of::<CompactionMetadata>(), 32);
}
#[test]
fn compaction_record_deserialization_matches_present_boolean_shape() {
let legacy: CompactionMetadata = serde_json::from_str(r#"{"summary":"x"}"#).unwrap();
assert_eq!(legacy.complete, None);
assert!(
serde_json::from_str::<CompactionMetadata>(r#"{"summary":"x","complete":null}"#)
.is_err()
);
let complete: CompactionMetadata =
serde_json::from_str(r#"{"summary":"x","complete":true}"#).unwrap();
assert_eq!(complete.complete, Some(true));
}
#[test]
fn known_metadata_accepts_owned_shapes() {
let call_id = ToolCallId::from_u128(1);
for (kind, detail) in [
(
COMPACTION_KIND,
StructuredData::from_json(r#"{"summary":"legacy"}"#).unwrap(),
),
(
TOOL_MUTATION_KIND,
encode(&ToolMutationMetadata::new(call_id, MutationState::Applied))
.unwrap(),
),
(
PROVIDER_HTTP_KIND,
StructuredData::from_json(r#"{"phase":"intent","method":"POST","url":"","headers":[{"name":"n","value":"v","extension":null}],"request_bytes":0}"#).unwrap(),
),
(
PROVIDER_HTTP_KIND,
StructuredData::from_json(r#"{"phase":"outcome","status":0,"response_bytes":0,"outcome":"cancelled"}"#).unwrap(),
),
("extension", StructuredData::from_json(r#"[false,{"any":"shape"}]"#).unwrap()),
] {
assert!(validate(kind, detail.view()).is_ok(), "rejected {kind}");
}
}
#[test]
fn known_metadata_rejects_malformed_owned_shapes() {
let call_id = ToolCallId::from_u128(1);
let valid_call_id = serde_json::to_string(&call_id).unwrap();
let invalid = [
(COMPACTION_KIND, r#"{}"#.to_string()),
(COMPACTION_KIND, r#"{"summary":null}"#.to_string()),
(COMPACTION_KIND, r#"{"summary":"x","complete":null}"#.to_string()),
(TOOL_MUTATION_KIND, r#"{}"#.to_string()),
(TOOL_MUTATION_KIND, format!(r#"{{"call_id":{valid_call_id}}}"#)),
(TOOL_MUTATION_KIND, r#"{"mutation_state":"applied"}"#.to_string()),
(TOOL_MUTATION_KIND, format!(r#"{{"call_id":{valid_call_id},"mutation_state":"applied","extra":true}}"#)),
(TOOL_MUTATION_KIND, r#"{"call_id":"bad","mutation_state":"applied"}"#.to_string()),
(TOOL_MUTATION_KIND, format!(r#"{{"call_id":{valid_call_id},"mutation_state":"wrong"}}"#)),
(PROVIDER_HTTP_KIND, r#"{"method":"POST","url":"x","headers":[],"request_bytes":0}"#.to_string()),
(PROVIDER_HTTP_KIND, r#"{"phase":"intent","method":"GET","url":"x","headers":[],"request_bytes":0}"#.to_string()),
(PROVIDER_HTTP_KIND, r#"{"phase":"intent","method":"POST","url":"x","headers":[null],"request_bytes":0}"#.to_string()),
(PROVIDER_HTTP_KIND, r#"{"phase":"intent","method":"POST","url":"x","headers":[{"name":false,"value":"v"}],"request_bytes":0}"#.to_string()),
(PROVIDER_HTTP_KIND, r#"{"phase":"intent","method":"POST","url":"x","headers":[],"request_bytes":18446744073709551615,"extra":true}"#.to_string()),
(PROVIDER_HTTP_KIND, r#"{"phase":"outcome","status":65536,"response_bytes":0,"outcome":"completed"}"#.to_string()),
(PROVIDER_HTTP_KIND, r#"{"phase":"outcome","status":0,"response_bytes":-1,"outcome":"completed"}"#.to_string()),
(PROVIDER_HTTP_KIND, r#"{"phase":"outcome","status":0,"response_bytes":0,"outcome":"completed","extra":true}"#.to_string()),
];
for (kind, detail) in invalid {
let detail = StructuredData::from_json(&detail).unwrap();
assert!(
validate(kind, detail.view()).is_err(),
"accepted {kind}: {detail:?}"
);
}
}
}