Skip to main content

lean_ctx_protocol/hive/
model_catalog.rs

1// SPDX-License-Identifier: Apache-2.0
2//! mz6/mz14 subscription model catalog, as Hive serves it
3//! (`leanctx.hive.model-catalog/v1`; source of truth: leanctx-hive
4//! `crates/hive-wire/src/models.rs`). Every model names the **origin** of its
5//! weights and each **host** (provider, country); `direct` lists vendors' own
6//! APIs, which are never part of the subscription and are opt-in only.
7//!
8//! The engine bundles a baseline copy (`rust/data/model_catalog.json`,
9//! generated by `scripts/sync-model-catalog.py`) for offline labels and to
10//! recognise a vendor-direct host. The types are `deny_unknown_fields`, like
11//! hive-wire's, so the two ship in lockstep.
12
13use serde::{Deserialize, Serialize};
14
15pub use super::effective_policy::is_country;
16
17/// `schema` of the catalog document.
18pub const CATALOG_SCHEMA: &str = "leanctx.hive.model-catalog/v1";
19/// Prefix of every subscription model id.
20pub const MODEL_ID_PREFIX: &str = "leanctx/";
21
22#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
23#[serde(deny_unknown_fields)]
24pub struct ModelCatalog {
25    pub schema: String,
26    pub catalog_version: String,
27    pub price_version: String,
28    #[serde(default)]
29    pub models: Vec<CatalogModel>,
30    #[serde(default)]
31    pub direct: Vec<DirectVendor>,
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
35#[serde(rename_all = "snake_case")]
36pub enum ModelStatus {
37    Available,
38    Unpriced,
39}
40
41#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
42#[serde(deny_unknown_fields)]
43pub struct CatalogModel {
44    pub id: String,
45    pub display_name: String,
46    pub vendor: String,
47    /// ISO 3166-1 alpha-2 of where the weights come from.
48    pub origin_country: String,
49    #[serde(default, skip_serializing_if = "Option::is_none")]
50    pub license: Option<String>,
51    pub status: ModelStatus,
52    pub plans: Vec<String>,
53    #[serde(default, skip_serializing_if = "Option::is_none")]
54    pub context_window: Option<u32>,
55    /// Ordered: the fallback order.
56    pub hosts: Vec<CatalogHost>,
57}
58
59#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
60#[serde(deny_unknown_fields)]
61pub struct CatalogHost {
62    pub provider: String,
63    #[serde(default, skip_serializing_if = "Option::is_none")]
64    pub upstream_model: Option<String>,
65    /// ISO 3166-1 alpha-2 of where the requests are processed.
66    pub host_country: String,
67    #[serde(default, skip_serializing_if = "Option::is_none")]
68    pub host_region: Option<String>,
69    #[serde(default, skip_serializing_if = "Option::is_none")]
70    pub retention: Option<String>,
71    #[serde(default, skip_serializing_if = "Option::is_none")]
72    pub price_in_per_mtok_micros: Option<u64>,
73    #[serde(default, skip_serializing_if = "Option::is_none")]
74    pub price_cached_in_per_mtok_micros: Option<u64>,
75    #[serde(default, skip_serializing_if = "Option::is_none")]
76    pub price_out_per_mtok_micros: Option<u64>,
77}
78
79#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
80#[serde(deny_unknown_fields)]
81pub struct DirectVendor {
82    pub vendor: String,
83    /// API hostnames of this vendor's own API (lowercase).
84    pub hosts: Vec<String>,
85    pub host_country: String,
86}
87
88impl ModelCatalog {
89    /// Parse and check the document: schema, country codes, at least one
90    /// host per model. A catalog that fails is never used (fail closed).
91    pub fn from_json(text: &str) -> Result<Self, String> {
92        let catalog: Self =
93            serde_json::from_str(text).map_err(|e| format!("model catalog: {e}"))?;
94        if catalog.schema != CATALOG_SCHEMA {
95            return Err(format!("model catalog: schema {:?}", catalog.schema));
96        }
97        for m in &catalog.models {
98            if !is_country(&m.origin_country)
99                || m.hosts.is_empty()
100                || m.hosts.iter().any(|h| !is_country(&h.host_country))
101            {
102                return Err(format!("model catalog: model {}", m.id));
103            }
104        }
105        if let Some(d) = catalog
106            .direct
107            .iter()
108            .find(|d| !is_country(&d.host_country) || d.hosts.is_empty())
109        {
110            return Err(format!("model catalog: direct {}", d.vendor));
111        }
112        Ok(catalog)
113    }
114
115    /// The model `id`, if listed.
116    #[must_use]
117    pub fn model(&self, id: &str) -> Option<&CatalogModel> {
118        self.models.iter().find(|m| m.id == id)
119    }
120
121    /// The vendor whose own API `host` is (exact or a subdomain of a listed
122    /// host; case-insensitive, trailing dot ignored).
123    #[must_use]
124    pub fn direct_vendor(&self, host: &str) -> Option<&DirectVendor> {
125        let host = host.trim_end_matches('.').to_ascii_lowercase();
126        self.direct.iter().find(|d| {
127            d.hosts.iter().any(|h| {
128                host == *h
129                    || host
130                        .strip_suffix(h.as_str())
131                        .is_some_and(|p| p.ends_with('.'))
132            })
133        })
134    }
135}
136
137impl CatalogModel {
138    /// The display label (design §7.1), e.g.
139    /// `origin CN (Z.ai (Zhipu)) · hosted US (nebius)`.
140    #[must_use]
141    pub fn origin_host_label(&self) -> String {
142        let hosts: Vec<String> = self
143            .hosts
144            .iter()
145            .map(|h| {
146                let mut s = format!("{} ({}", h.host_country, h.provider);
147                if let Some(r) = &h.host_region {
148                    s.push_str(", ");
149                    s.push_str(r);
150                }
151                s.push(')');
152                if h.retention.as_deref() == Some("zero") {
153                    s.push_str(" · zero retention");
154                }
155                s
156            })
157            .collect();
158        format!(
159            "origin {} ({}) · hosted {}",
160            self.origin_country,
161            self.vendor,
162            hosts.join(" / ")
163        )
164    }
165}