llm_registry_api/graphql/
types.rs

1//! GraphQL schema types
2//!
3//! This module defines GraphQL types that wrap the core domain types.
4
5use async_graphql::{Enum, Object, SimpleObject};
6use chrono::{DateTime, Utc};
7use llm_registry_core::{Asset, AssetStatus, AssetType};
8use llm_registry_service::DependencyNode;
9
10/// GraphQL representation of an Asset
11#[derive(Clone)]
12pub struct GqlAsset(pub Asset);
13
14#[Object]
15impl GqlAsset {
16    /// Unique identifier for this asset
17    async fn id(&self) -> String {
18        self.0.id.to_string()
19    }
20
21    /// Asset type
22    async fn asset_type(&self) -> GqlAssetType {
23        GqlAssetType::from_core(&self.0.asset_type)
24    }
25
26    /// Asset name
27    async fn name(&self) -> &str {
28        &self.0.metadata.name
29    }
30
31    /// Asset version
32    async fn version(&self) -> String {
33        self.0.metadata.version.to_string()
34    }
35
36    /// Asset description
37    async fn description(&self) -> Option<&str> {
38        self.0.metadata.description.as_deref()
39    }
40
41    /// License identifier
42    async fn license(&self) -> Option<&str> {
43        self.0.metadata.license.as_deref()
44    }
45
46    /// Tags for categorization
47    async fn tags(&self) -> &[String] {
48        &self.0.metadata.tags
49    }
50
51    /// Key-value annotations
52    async fn annotations(&self) -> Vec<GqlAnnotation> {
53        self.0
54            .metadata
55            .annotations
56            .iter()
57            .map(|(k, v)| GqlAnnotation {
58                key: k.clone(),
59                value: v.clone(),
60            })
61            .collect()
62    }
63
64    /// File size in bytes
65    async fn size_bytes(&self) -> Option<u64> {
66        self.0.metadata.size_bytes
67    }
68
69    /// Content type / MIME type
70    async fn content_type(&self) -> Option<&str> {
71        self.0.metadata.content_type.as_deref()
72    }
73
74    /// Current status of the asset
75    async fn status(&self) -> GqlAssetStatus {
76        GqlAssetStatus::from_core(&self.0.status)
77    }
78
79    /// Storage path
80    async fn storage_path(&self) -> &str {
81        &self.0.storage.path
82    }
83
84    /// Storage URI
85    async fn storage_uri(&self) -> Option<&str> {
86        self.0.storage.uri.as_deref()
87    }
88
89    /// Checksum algorithm
90    async fn checksum_algorithm(&self) -> String {
91        self.0.checksum.algorithm.to_string()
92    }
93
94    /// Checksum value
95    async fn checksum_value(&self) -> &str {
96        &self.0.checksum.value
97    }
98
99    /// Number of dependencies
100    async fn dependency_count(&self) -> usize {
101        self.0.dependencies.len()
102    }
103
104    /// Creation timestamp
105    async fn created_at(&self) -> DateTime<Utc> {
106        self.0.created_at
107    }
108
109    /// Last update timestamp
110    async fn updated_at(&self) -> DateTime<Utc> {
111        self.0.updated_at
112    }
113
114    /// Deprecation timestamp
115    async fn deprecated_at(&self) -> Option<DateTime<Utc>> {
116        self.0.deprecated_at
117    }
118}
119
120/// GraphQL representation of a dependency node
121#[derive(Clone)]
122pub struct GqlDependencyNode {
123    node: DependencyNode,
124}
125
126impl From<DependencyNode> for GqlDependencyNode {
127    fn from(node: DependencyNode) -> Self {
128        GqlDependencyNode { node }
129    }
130}
131
132#[Object]
133impl GqlDependencyNode {
134    /// Asset ID
135    async fn asset_id(&self) -> String {
136        self.node.asset_id.to_string()
137    }
138
139    /// Asset name
140    async fn name(&self) -> &str {
141        &self.node.name
142    }
143
144    /// Asset version
145    async fn version(&self) -> String {
146        self.node.version.to_string()
147    }
148
149    /// Depth from root (0 = direct dependency)
150    async fn depth(&self) -> i32 {
151        self.node.depth
152    }
153
154    /// Number of dependencies this node has
155    async fn dependency_count(&self) -> usize {
156        self.node.dependencies.len()
157    }
158}
159
160/// GraphQL representation of asset type
161#[derive(Enum, Copy, Clone, Eq, PartialEq)]
162pub enum GqlAssetType {
163    /// Language model
164    Model,
165    /// ML pipeline
166    Pipeline,
167    /// Test suite
168    TestSuite,
169    /// Policy
170    Policy,
171    /// Dataset
172    Dataset,
173}
174
175impl GqlAssetType {
176    pub fn from_core(asset_type: &AssetType) -> Self {
177        match asset_type {
178            AssetType::Model => GqlAssetType::Model,
179            AssetType::Pipeline => GqlAssetType::Pipeline,
180            AssetType::TestSuite => GqlAssetType::TestSuite,
181            AssetType::Policy => GqlAssetType::Policy,
182            AssetType::Dataset => GqlAssetType::Dataset,
183            AssetType::Custom(_) => GqlAssetType::Model, // Default for custom types
184        }
185    }
186
187    pub fn to_core(&self) -> AssetType {
188        match self {
189            GqlAssetType::Model => AssetType::Model,
190            GqlAssetType::Pipeline => AssetType::Pipeline,
191            GqlAssetType::TestSuite => AssetType::TestSuite,
192            GqlAssetType::Policy => AssetType::Policy,
193            GqlAssetType::Dataset => AssetType::Dataset,
194        }
195    }
196}
197
198/// GraphQL representation of asset status
199#[derive(Enum, Copy, Clone, Eq, PartialEq)]
200pub enum GqlAssetStatus {
201    /// Asset is active and available
202    Active,
203    /// Asset is deprecated
204    Deprecated,
205    /// Asset is archived
206    Archived,
207    /// Asset is non-compliant
208    NonCompliant,
209}
210
211impl GqlAssetStatus {
212    pub fn from_core(status: &AssetStatus) -> Self {
213        match status {
214            AssetStatus::Active => GqlAssetStatus::Active,
215            AssetStatus::Deprecated => GqlAssetStatus::Deprecated,
216            AssetStatus::Archived => GqlAssetStatus::Archived,
217            AssetStatus::NonCompliant => GqlAssetStatus::NonCompliant,
218        }
219    }
220
221    pub fn to_core(&self) -> AssetStatus {
222        match self {
223            GqlAssetStatus::Active => AssetStatus::Active,
224            GqlAssetStatus::Deprecated => AssetStatus::Deprecated,
225            GqlAssetStatus::Archived => AssetStatus::Archived,
226            GqlAssetStatus::NonCompliant => AssetStatus::NonCompliant,
227        }
228    }
229}
230
231/// Key-value annotation
232#[derive(SimpleObject, Clone)]
233pub struct GqlAnnotation {
234    /// Annotation key
235    pub key: String,
236    /// Annotation value
237    pub value: String,
238}
239
240/// Paginated assets response
241#[derive(SimpleObject)]
242pub struct GqlAssetConnection {
243    /// List of assets
244    pub nodes: Vec<GqlAsset>,
245    /// Total count
246    pub total_count: i64,
247    /// Whether there are more results
248    pub has_next_page: bool,
249}
250
251/// Asset search filters
252#[derive(async_graphql::InputObject)]
253pub struct GqlAssetFilter {
254    /// Filter by asset type
255    pub asset_type: Option<GqlAssetType>,
256    /// Filter by status
257    pub status: Option<GqlAssetStatus>,
258    /// Filter by tag (must have all specified tags)
259    pub tags: Option<Vec<String>>,
260    /// Filter by name (partial match)
261    pub name: Option<String>,
262}
263
264/// Registration result
265#[derive(SimpleObject)]
266pub struct GqlRegisterResult {
267    /// The registered asset
268    pub asset: GqlAsset,
269    /// Success message
270    pub message: String,
271}
272
273/// Update result
274#[derive(SimpleObject)]
275pub struct GqlUpdateResult {
276    /// The updated asset
277    pub asset: GqlAsset,
278    /// Success message
279    pub message: String,
280}
281
282/// Delete result
283#[derive(SimpleObject)]
284pub struct GqlDeleteResult {
285    /// ID of deleted asset
286    pub asset_id: String,
287    /// Success message
288    pub message: String,
289}