cc-lb-plugin-wire 0.7.0

Wire types and schema versioning contract for cc-lb wasm plugin authors.
Documentation
use cc_lb_plugin_wire::{
    WireSchema,
    v1::{
        self, CachePricingSummary, CachePricingSummaryRef, FilterRequest as FilterRequestV1,
        FilterRequestRef as FilterRequestRefV1, Principal, PrincipalRef,
    },
    v2::{FilterRequest as FilterRequestV2, FilterRequestRef as FilterRequestRefV2},
};
use rkyv::rancor::Error;

const FILTER_V1_DESCRIPTOR: &str = "FilterRequest{body:Box<[u8]>,candidates:Box<[UpstreamCandidate]>,headers:Box<[Header]>,method:Box<str>,path:Box<str>,principal:Principal,query:Option<Box<str>>,request_id:Box<str>,thread_id:Option<Box<str>>}";
const FILTER_REF_V1_DESCRIPTOR: &str = "FilterRequestRef{body:&[u8],candidates:&[UpstreamCandidateRef],headers:&[HeaderRef],method:&str,path:&str,principal:PrincipalRef,query:Option<QueryRef>,request_id:&str,thread_id:Option<QueryRef>}";
const FILTER_V1_FINGERPRINT: [u8; 32] = [
    226, 221, 217, 195, 65, 143, 91, 90, 171, 208, 226, 181, 225, 86, 195, 26, 222, 246, 184, 214,
    5, 146, 238, 113, 240, 1, 155, 170, 208, 149, 217, 141,
];
const FILTER_V2_FINGERPRINT: [u8; 32] = [
    242, 15, 102, 149, 66, 52, 156, 0, 42, 205, 4, 178, 48, 143, 240, 216, 56, 153, 191, 12, 181,
    161, 123, 129, 140, 29, 147, 138, 221, 165, 40, 52,
];

fn principal() -> Principal {
    Principal {
        id: Box::from("tenant-a"),
        kind: Box::from("api_key"),
        claims: Box::new([]),
    }
}

fn cache_pricing() -> CachePricingSummary {
    CachePricingSummary {
        status: Box::from("known"),
        input_micros_per_million: Some(5_000_000),
        cache_creation_5m_micros_per_million: Some(6_250_000),
        cache_creation_1h_micros_per_million: Some(10_000_000),
        cache_read_micros_per_million: Some(500_000),
    }
}

fn cache_pricing_ref() -> CachePricingSummaryRef<'static> {
    CachePricingSummaryRef {
        status: "known",
        input_micros_per_million: Some(5_000_000),
        cache_creation_5m_micros_per_million: Some(6_250_000),
        cache_creation_1h_micros_per_million: Some(10_000_000),
        cache_read_micros_per_million: Some(500_000),
    }
}

#[test]
fn filter_v1_keeps_published_descriptor_and_has_no_service_tier() {
    assert_eq!(
        <FilterRequestV1 as WireSchema>::DESCRIPTOR,
        FILTER_V1_DESCRIPTOR
    );
    assert_eq!(
        <FilterRequestRefV1<'_> as WireSchema>::DESCRIPTOR,
        FILTER_REF_V1_DESCRIPTOR
    );
    assert_eq!(
        <FilterRequestV1 as WireSchema>::FINGERPRINT,
        FILTER_V1_FINGERPRINT
    );

    let request = FilterRequestV1 {
        request_id: Box::from("v1-request"),
        thread_id: None,
        canonical_model_id: Box::from("claude-test"),
        cache_pricing: cache_pricing(),
        method: Box::from("POST"),
        path: Box::from("/v1/messages"),
        query: None,
        headers: Box::new([]),
        body: Box::from(&b"{}"[..]),
        principal: principal(),
        candidates: Box::new([]),
    };

    let bytes = rkyv::to_bytes::<Error>(&request).expect("encode V1");
    rkyv::access::<v1::ArchivedFilterRequest, Error>(&bytes).expect("access V1");
}

#[test]
fn filter_v2_owned_request_round_trips_service_tier() {
    let request = FilterRequestV2 {
        request_id: Box::from("v2-request"),
        thread_id: None,
        service_tier: Some(Box::from("priority")),
        canonical_model_id: Box::from("claude-test"),
        cache_pricing: cache_pricing(),
        method: Box::from("POST"),
        path: Box::from("/v1/messages"),
        query: None,
        headers: Box::new([]),
        body: Box::from(&b"{}"[..]),
        principal: principal(),
        candidates: Box::new([]),
    };

    let bytes = rkyv::to_bytes::<Error>(&request).expect("encode V2");
    let archived = rkyv::access::<cc_lb_plugin_wire::v2::ArchivedFilterRequest, Error>(&bytes)
        .expect("access V2");
    let tier: Option<&str> = archived.service_tier.as_ref().map(|value| &**value);

    assert_eq!(tier, Some("priority"));
    assert!(<FilterRequestV2 as WireSchema>::DESCRIPTOR.contains("service_tier"));
    assert_eq!(
        <FilterRequestV2 as WireSchema>::FINGERPRINT,
        FILTER_V2_FINGERPRINT
    );
    assert_ne!(
        <FilterRequestV1 as WireSchema>::FINGERPRINT,
        <FilterRequestV2 as WireSchema>::FINGERPRINT
    );
}

#[test]
fn filter_v2_borrowed_request_encodes_to_owned_layout() {
    let request = FilterRequestRefV2 {
        request_id: "v2-request",
        thread_id: None,
        service_tier: Some(v1::QueryRef { value: "priority" }),
        canonical_model_id: "claude-test",
        cache_pricing: cache_pricing_ref(),
        method: "POST",
        path: "/v1/messages",
        query: None,
        headers: &[],
        body: b"{}",
        principal: PrincipalRef {
            id: "tenant-a",
            kind: "api_key",
            claims: &[],
        },
        candidates: &[],
    };

    let bytes = rkyv::to_bytes::<Error>(&request).expect("encode borrowed V2");
    let archived = rkyv::access::<cc_lb_plugin_wire::v2::ArchivedFilterRequest, Error>(&bytes)
        .expect("access owned V2");
    let tier: Option<&str> = archived.service_tier.as_ref().map(|value| &**value);

    assert_eq!(tier, Some("priority"));
}