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