gglib_core/ports/model_registrar.rs
1//! Model registrar port definition.
2//!
3//! This port defines the interface for registering downloaded models
4//! in the database. It breaks the circular dependency between download
5//! and core services by allowing the download crate to depend on a trait
6//! rather than concrete `AppCore`.
7
8use async_trait::async_trait;
9use std::path::Path;
10
11use super::RepositoryError;
12use super::download::ResolvedFile;
13use crate::domain::Model;
14use crate::download::Quantization;
15
16/// Information about a completed download for model registration.
17///
18/// This is a pure data transfer object containing all information
19/// needed to register a model after download completes.
20#[derive(Debug, Clone)]
21pub struct CompletedDownload {
22 /// Path to the primary downloaded file (first shard for sharded models).
23 pub primary_path: std::path::PathBuf,
24 /// All downloaded file paths (multiple for sharded models).
25 pub all_paths: Vec<std::path::PathBuf>,
26 /// The resolved quantization.
27 pub quantization: Quantization,
28 /// Repository ID (e.g., "unsloth/Llama-3-GGUF").
29 pub repo_id: String,
30 /// Commit SHA at time of download.
31 pub commit_sha: String,
32 /// Whether this was a sharded download.
33 pub is_sharded: bool,
34 /// Total bytes downloaded.
35 pub total_bytes: u64,
36 /// Ordered list of all file paths for sharded models (None for single-file models).
37 pub file_paths: Option<Vec<std::path::PathBuf>>,
38 /// `HuggingFace` tags for the model.
39 pub hf_tags: Vec<String>,
40 /// File entries with OIDs from `HuggingFace` (for `model_files` table).
41 pub hf_file_entries: Vec<ResolvedFile>,
42}
43
44impl CompletedDownload {
45 /// Get the primary file path for database registration.
46 ///
47 /// For sharded models, this returns the first shard path
48 /// (required by llama-server for loading split models).
49 pub fn db_path(&self) -> &Path {
50 &self.primary_path
51 }
52}
53
54/// Port for registering downloaded models in the database.
55///
56/// This trait is implemented by core services and injected into
57/// the download manager, allowing model registration without
58/// coupling to `AppCore` directly.
59///
60/// # Usage
61///
62/// ```ignore
63/// let registrar: Arc<dyn ModelRegistrarPort> = /* ... */;
64/// let download = CompletedDownload { ... };
65/// let model = registrar.register_model(&download).await?;
66/// ```
67#[async_trait]
68pub trait ModelRegistrarPort: Send + Sync {
69 /// Register a downloaded model in the database.
70 ///
71 /// Parses GGUF metadata from the downloaded file and creates a database entry.
72 /// For sharded models, the primary (first shard) path is used for registration.
73 ///
74 /// # Arguments
75 ///
76 /// * `download` - The completed download information
77 ///
78 /// # Returns
79 ///
80 /// Returns the created `Model` on success.
81 async fn register_model(&self, download: &CompletedDownload) -> Result<Model, RepositoryError>;
82
83 /// Register a model using raw path parameters.
84 ///
85 /// This is a simpler interface for cases where you have the file path
86 /// but not the full download metadata.
87 ///
88 /// # Arguments
89 ///
90 /// * `repo_id` - `HuggingFace` repository ID
91 /// * `commit_sha` - Git commit SHA
92 /// * `file_path` - Path to the GGUF file
93 /// * `quantization` - Quantization type as string
94 ///
95 /// # Returns
96 ///
97 /// Returns the created `Model` on success.
98 async fn register_model_from_path(
99 &self,
100 repo_id: &str,
101 commit_sha: &str,
102 file_path: &Path,
103 quantization: &str,
104 ) -> Result<Model, RepositoryError>;
105}