gglib_core/ports/model_catalog.rs
1//! Model catalog port for listing and resolving models.
2//!
3//! This port defines the interface for querying the model catalog.
4//! It provides domain-level model information without exposing
5//! database or storage implementation details.
6
7use async_trait::async_trait;
8use std::fmt;
9use std::path::PathBuf;
10use thiserror::Error;
11
12/// Domain model summary for catalog operations (listing).
13///
14/// This is a domain type (not an `OpenAI` API type). The proxy layer
15/// is responsible for mapping this to OpenAI-compatible formats.
16///
17/// Note: Does NOT include `file_path` to avoid leaking filesystem details
18/// in catalog/listing operations.
19#[derive(Debug, Clone)]
20pub struct ModelSummary {
21 /// Database ID of the model.
22 pub id: u32,
23 /// Model name (used as identifier).
24 pub name: String,
25 /// Tags/labels associated with the model.
26 pub tags: Vec<String>,
27 /// Parameter count as string (e.g., "7B", "13B", "70B").
28 pub param_count: String,
29 /// Quantization type (e.g., "`Q4_K_M`", "`Q8_0`").
30 pub quantization: Option<String>,
31 /// Model architecture (e.g., "llama", "mistral", "qwen2").
32 pub architecture: Option<String>,
33 /// Unix timestamp when the model was added.
34 pub created_at: i64,
35 /// File size in bytes.
36 pub file_size: u64,
37}
38
39/// Launch specification for running a model.
40///
41/// Contains all information needed to actually launch a model,
42/// including the file path. Separate from `ModelSummary` to avoid
43/// leaking filesystem details in catalog operations.
44#[derive(Debug, Clone)]
45pub struct ModelLaunchSpec {
46 /// Database ID of the model.
47 pub id: u32,
48 /// Model name.
49 pub name: String,
50 /// Absolute path to the GGUF file.
51 pub file_path: PathBuf,
52 /// Tags/labels associated with the model.
53 pub tags: Vec<String>,
54 /// Model architecture (for runtime configuration).
55 pub architecture: Option<String>,
56 /// Maximum context length the model supports.
57 pub context_length: Option<u64>,
58}
59
60impl ModelSummary {
61 /// Create a description string for this model.
62 #[must_use]
63 pub fn description(&self) -> String {
64 let arch = self.architecture.as_deref().unwrap_or("unknown");
65 let quant = self.quantization.as_deref().unwrap_or("unknown");
66 format!("{} - {} parameters, {}", arch, self.param_count, quant)
67 }
68}
69
70/// Errors that can occur during catalog operations.
71#[derive(Debug, Error)]
72pub enum CatalogError {
73 /// Failed to query the catalog.
74 #[error("Failed to query catalog: {0}")]
75 QueryFailed(String),
76
77 /// Internal error during catalog operations.
78 #[error("Internal error: {0}")]
79 Internal(String),
80}
81
82/// Port for querying the model catalog.
83///
84/// This interface provides read-only access to the model catalog
85/// for listing and resolving models. It does not handle model
86/// registration or deletion.
87#[async_trait]
88pub trait ModelCatalogPort: Send + Sync + fmt::Debug {
89 /// List all models in the catalog.
90 ///
91 /// Returns a list of model summaries ordered by name.
92 ///
93 /// # Errors
94 ///
95 /// Returns `CatalogError` if the catalog cannot be queried.
96 async fn list_models(&self) -> Result<Vec<ModelSummary>, CatalogError>;
97
98 /// Resolve a model by name or alias.
99 ///
100 /// This method performs model resolution:
101 /// 1. Exact name match
102 /// 2. Case-insensitive name match
103 /// 3. Fuzzy/partial match (implementation-defined)
104 ///
105 /// Returns `None` if no matching model is found.
106 ///
107 /// # Arguments
108 ///
109 /// * `name` - Model name or alias to resolve
110 ///
111 /// # Errors
112 ///
113 /// Returns `CatalogError` if the catalog cannot be queried.
114 async fn resolve_model(&self, name: &str) -> Result<Option<ModelSummary>, CatalogError>;
115
116 /// Resolve a model for launching.
117 ///
118 /// Returns full launch specification including file path.
119 /// Use this when you need to actually run a model, not just list it.
120 ///
121 /// # Arguments
122 ///
123 /// * `name` - Model name or alias to resolve
124 ///
125 /// # Errors
126 ///
127 /// Returns `CatalogError` if the catalog cannot be queried.
128 async fn resolve_for_launch(&self, name: &str)
129 -> Result<Option<ModelLaunchSpec>, CatalogError>;
130}