Skip to main content

rlx_pkg/
manifest.rs

1// RLX — versatile ML compiler + runtime.
2// Copyright (C) 2026 Eugene Hauptmann, Nataliya Kosmyna.
3// SPDX-License-Identifier: MIT OR Apache-2.0
4
5//! Root `rlx.json` package manifest.
6
7use serde::{Deserialize, Serialize};
8use std::collections::BTreeMap;
9
10/// Package format identifier (`"rlxp"`).
11pub const FORMAT_NAME: &str = "rlxp";
12
13/// Current structural schema version.
14pub const FORMAT_VERSION: u32 = 1;
15
16/// Minimum loader `compat_version` this crate supports.
17pub const COMPAT_VERSION: u32 = 1;
18
19/// Graph member reference inside the pack.
20#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
21pub struct GraphRef {
22    pub path: String,
23    /// `bincode_graph_v1` today.
24    pub encoding: String,
25}
26
27/// Weights catalog pointer.
28#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
29pub struct WeightsRef {
30    pub index: String,
31}
32
33/// Named sidecar blob.
34#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
35pub struct SidecarRef {
36    pub id: String,
37    pub path: String,
38    #[serde(default, skip_serializing_if = "Option::is_none")]
39    pub media_type: Option<String>,
40    #[serde(default)]
41    pub codec: crate::tier::Codec,
42}
43
44/// Optional distribution section.
45#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
46pub struct DistRef {
47    #[serde(default, skip_serializing_if = "Option::is_none")]
48    pub placement: Option<String>,
49}
50
51/// Root package manifest (`rlx.json`).
52#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
53pub struct Manifest {
54    pub format: String,
55    pub format_version: u32,
56    pub compat_version: u32,
57    pub name: String,
58    #[serde(default, skip_serializing_if = "Option::is_none")]
59    pub producer: Option<String>,
60    #[serde(default, skip_serializing_if = "Vec::is_empty")]
61    pub features: Vec<String>,
62    pub graph: GraphRef,
63    #[serde(default, skip_serializing_if = "Option::is_none")]
64    pub weights: Option<WeightsRef>,
65    #[serde(default, skip_serializing_if = "Vec::is_empty")]
66    pub sidecars: Vec<SidecarRef>,
67    #[serde(default, skip_serializing_if = "Option::is_none")]
68    pub dist: Option<DistRef>,
69    #[serde(default, skip_serializing_if = "Option::is_none")]
70    pub created_unix: Option<u64>,
71    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
72    pub extensions: BTreeMap<String, serde_json::Value>,
73}
74
75impl Manifest {
76    /// Build a v1 manifest for a named model.
77    pub fn new_v1(name: impl Into<String>) -> Self {
78        Self {
79            format: FORMAT_NAME.to_string(),
80            format_version: FORMAT_VERSION,
81            compat_version: COMPAT_VERSION,
82            name: name.into(),
83            producer: None,
84            features: Vec::new(),
85            graph: GraphRef {
86                path: "graph/mir.bin".into(),
87                encoding: "bincode_graph_v1".into(),
88            },
89            weights: Some(WeightsRef {
90                index: "weights/index.json".into(),
91            }),
92            sidecars: Vec::new(),
93            dist: None,
94            created_unix: None,
95            extensions: BTreeMap::new(),
96        }
97    }
98
99    /// Reject unsupported format / compat versions.
100    pub fn validate(&self) -> anyhow::Result<()> {
101        if self.format != FORMAT_NAME {
102            anyhow::bail!(
103                "unknown package format {:?} (expected {:?})",
104                self.format,
105                FORMAT_NAME
106            );
107        }
108        if self.format_version > FORMAT_VERSION {
109            anyhow::bail!(
110                "package format_version {} newer than loader {}",
111                self.format_version,
112                FORMAT_VERSION
113            );
114        }
115        if self.compat_version > COMPAT_VERSION {
116            anyhow::bail!(
117                "package requires compat_version {} (loader supports {})",
118                self.compat_version,
119                COMPAT_VERSION
120            );
121        }
122        if self.graph.encoding != "bincode_graph_v1" && self.graph.encoding != "none" {
123            anyhow::bail!("unsupported graph encoding {:?}", self.graph.encoding);
124        }
125        Ok(())
126    }
127}