lean-ctx-protocol 0.7.0

Open wire contracts for lean-ctx: events, observations, assignments, evidence gaps.
Documentation
// SPDX-License-Identifier: Apache-2.0
//! mz6/mz14 subscription model catalog, as Hive serves it
//! (`leanctx.hive.model-catalog/v1`; source of truth: leanctx-hive
//! `crates/hive-wire/src/models.rs`). Every model names the **origin** of its
//! weights and each **host** (provider, country); `direct` lists vendors' own
//! APIs, which are never part of the subscription and are opt-in only.
//!
//! The engine bundles a baseline copy (`rust/data/model_catalog.json`,
//! generated by `scripts/sync-model-catalog.py`) for offline labels and to
//! recognise a vendor-direct host. The types are `deny_unknown_fields`, like
//! hive-wire's, so the two ship in lockstep.

use serde::{Deserialize, Serialize};

pub use super::effective_policy::is_country;

/// `schema` of the catalog document.
pub const CATALOG_SCHEMA: &str = "leanctx.hive.model-catalog/v1";
/// Prefix of every subscription model id.
pub const MODEL_ID_PREFIX: &str = "leanctx/";

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ModelCatalog {
    pub schema: String,
    pub catalog_version: String,
    pub price_version: String,
    #[serde(default)]
    pub models: Vec<CatalogModel>,
    #[serde(default)]
    pub direct: Vec<DirectVendor>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ModelStatus {
    Available,
    Unpriced,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CatalogModel {
    pub id: String,
    pub display_name: String,
    pub vendor: String,
    /// ISO 3166-1 alpha-2 of where the weights come from.
    pub origin_country: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub license: Option<String>,
    pub status: ModelStatus,
    pub plans: Vec<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub context_window: Option<u32>,
    /// Ordered: the fallback order.
    pub hosts: Vec<CatalogHost>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CatalogHost {
    pub provider: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub upstream_model: Option<String>,
    /// ISO 3166-1 alpha-2 of where the requests are processed.
    pub host_country: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub host_region: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub retention: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub price_in_per_mtok_micros: Option<u64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub price_cached_in_per_mtok_micros: Option<u64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub price_out_per_mtok_micros: Option<u64>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct DirectVendor {
    pub vendor: String,
    /// API hostnames of this vendor's own API (lowercase).
    pub hosts: Vec<String>,
    pub host_country: String,
}

impl ModelCatalog {
    /// Parse and check the document: schema, country codes, at least one
    /// host per model. A catalog that fails is never used (fail closed).
    pub fn from_json(text: &str) -> Result<Self, String> {
        let catalog: Self =
            serde_json::from_str(text).map_err(|e| format!("model catalog: {e}"))?;
        if catalog.schema != CATALOG_SCHEMA {
            return Err(format!("model catalog: schema {:?}", catalog.schema));
        }
        for m in &catalog.models {
            if !is_country(&m.origin_country)
                || m.hosts.is_empty()
                || m.hosts.iter().any(|h| !is_country(&h.host_country))
            {
                return Err(format!("model catalog: model {}", m.id));
            }
        }
        if let Some(d) = catalog
            .direct
            .iter()
            .find(|d| !is_country(&d.host_country) || d.hosts.is_empty())
        {
            return Err(format!("model catalog: direct {}", d.vendor));
        }
        Ok(catalog)
    }

    /// The model `id`, if listed.
    #[must_use]
    pub fn model(&self, id: &str) -> Option<&CatalogModel> {
        self.models.iter().find(|m| m.id == id)
    }

    /// The vendor whose own API `host` is (exact or a subdomain of a listed
    /// host; case-insensitive, trailing dot ignored).
    #[must_use]
    pub fn direct_vendor(&self, host: &str) -> Option<&DirectVendor> {
        let host = host.trim_end_matches('.').to_ascii_lowercase();
        self.direct.iter().find(|d| {
            d.hosts.iter().any(|h| {
                host == *h
                    || host
                        .strip_suffix(h.as_str())
                        .is_some_and(|p| p.ends_with('.'))
            })
        })
    }
}

impl CatalogModel {
    /// The display label (design §7.1), e.g.
    /// `origin CN (Z.ai (Zhipu)) · hosted US (nebius)`.
    #[must_use]
    pub fn origin_host_label(&self) -> String {
        let hosts: Vec<String> = self
            .hosts
            .iter()
            .map(|h| {
                let mut s = format!("{} ({}", h.host_country, h.provider);
                if let Some(r) = &h.host_region {
                    s.push_str(", ");
                    s.push_str(r);
                }
                s.push(')');
                if h.retention.as_deref() == Some("zero") {
                    s.push_str(" · zero retention");
                }
                s
            })
            .collect();
        format!(
            "origin {} ({}) · hosted {}",
            self.origin_country,
            self.vendor,
            hosts.join(" / ")
        )
    }
}