Luigit
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:?}"
            );
        }
    }
}