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}