Skip to main content

gglib_core/ports/
mod.rs

1#![doc = include_str!("README.md")]
2pub mod agent;
3pub mod benchmark;
4pub mod cache_metrics_sink;
5pub mod chat_history;
6pub mod download;
7pub mod download_event_emitter;
8pub mod download_manager;
9pub mod download_state;
10pub mod event_emitter;
11pub mod gguf_parser;
12pub mod huggingface;
13pub mod llm_completion;
14pub mod mcp_dto;
15pub mod mcp_error;
16pub mod mcp_repository;
17pub mod model_catalog;
18pub mod model_registrar;
19pub mod model_repository;
20pub mod model_runtime;
21pub mod process_runner;
22pub mod retry_observer;
23pub mod server_health;
24pub mod server_log_sink;
25pub mod settings_repository;
26pub mod system_probe;
27pub mod tool_executor_filter;
28pub mod tool_support;
29
30use std::sync::Arc;
31use thiserror::Error;
32
33// Re-export agent port types for convenience
34pub use agent::{AgentError, AgentLoopPort, AgentRunOutput, ToolExecutorPort};
35// Re-export LLM completion port (LlmStreamEvent lives in domain::agent)
36pub use llm_completion::LlmCompletionPort;
37// Re-export tool-executor filter decorators
38pub use tool_executor_filter::{EmptyToolExecutor, FilteredToolExecutor, TOOL_NOT_AVAILABLE_MSG};
39
40// Re-export repository traits for convenience
41pub use benchmark::BenchmarkRepositoryPort;
42pub use cache_metrics_sink::CacheMetricsSink;
43pub use chat_history::{ChatHistoryError, ChatHistoryRepository};
44pub use download::{QuantizationResolver, Resolution, ResolvedFile};
45pub use download_event_emitter::{AppEventBridge, DownloadEventEmitterPort, NoopDownloadEmitter};
46pub use download_manager::{DownloadManagerConfig, DownloadManagerPort, DownloadRequest};
47pub use download_state::DownloadStateRepositoryPort;
48pub use event_emitter::{AppEventEmitter, NoopEmitter};
49pub use gguf_parser::{
50    GgufCapabilities, GgufMetadata, GgufParseError, GgufParserPort, NoopGgufParser,
51};
52pub use huggingface::{
53    HfClientPort, HfFileInfo, HfPortError, HfQuantInfo, HfRepoInfo, HfSearchOptions, HfSearchResult,
54};
55pub use mcp_dto::{ResolutionAttempt, ResolutionStatus};
56pub use mcp_error::{McpErrorCategory, McpErrorInfo, McpServiceError};
57pub use mcp_repository::{McpRepositoryError, McpServerRepository};
58pub use model_catalog::{CatalogError, ModelCatalogPort, ModelLaunchSpec, ModelSummary};
59pub use model_registrar::{CompletedDownload, ModelRegistrarPort};
60pub use model_repository::ModelRepository;
61pub use model_runtime::{
62    Admission, AdmissionLease, AdmissionRelease, LaunchOverrides, ModelRuntimeError,
63    ModelRuntimePort, NoopModelRuntime, PinnedSpec, RunningTarget, RuntimeErrorEnvelope,
64};
65pub use process_runner::{ProcessHandle, ProcessRunner, ServerConfig};
66pub use retry_observer::RetryObserver;
67pub use server_health::ServerHealthStatus;
68pub use server_log_sink::ServerLogSinkPort;
69pub use settings_repository::SettingsRepository;
70pub use system_probe::{SystemProbeError, SystemProbePort, SystemProbeResult};
71pub use tool_support::{
72    ModelSource, ToolFormat, ToolSupportDetection, ToolSupportDetectionInput,
73    ToolSupportDetectorPort,
74};
75
76/// Container for all repository trait objects.
77///
78/// This struct provides a consistent way to wire repositories across adapters
79/// without coupling them to concrete implementations. It lives in `gglib-core`
80/// so that `AppCore` can accept it without depending on `gglib-db`.
81///
82/// # Example
83///
84/// ```ignore
85/// // In gglib-db factory:
86/// pub fn build_repos(pool: &SqlitePool) -> Repos { ... }
87///
88/// // In adapter bootstrap:
89/// let repos = gglib_db::factory::build_repos(&pool);
90/// let core = AppCore::new(repos);
91/// ```
92#[derive(Clone)]
93pub struct Repos {
94    /// Model repository for CRUD operations on models.
95    pub models: Arc<dyn ModelRepository>,
96    /// Settings repository for application settings.
97    pub settings: Arc<dyn SettingsRepository>,
98    /// MCP server repository for MCP server configurations.
99    pub mcp_servers: Arc<dyn McpServerRepository>,
100    /// Chat history repository for conversations and messages.
101    pub chat_history: Arc<dyn ChatHistoryRepository>,
102}
103
104impl Repos {
105    /// Create a new Repos container.
106    pub fn new(
107        models: Arc<dyn ModelRepository>,
108        settings: Arc<dyn SettingsRepository>,
109        mcp_servers: Arc<dyn McpServerRepository>,
110        chat_history: Arc<dyn ChatHistoryRepository>,
111    ) -> Self {
112        Self {
113            models,
114            settings,
115            mcp_servers,
116            chat_history,
117        }
118    }
119}
120
121/// Domain-specific errors for repository operations.
122///
123/// This error type abstracts away storage implementation details (e.g., sqlx errors)
124/// and provides a clean interface for services to handle storage failures.
125#[derive(Debug, Error)]
126pub enum RepositoryError {
127    /// The requested entity was not found.
128    #[error("Not found: {0}")]
129    NotFound(String),
130
131    /// An entity with the same identifier already exists.
132    #[error("Already exists: {0}")]
133    AlreadyExists(String),
134
135    /// Storage backend error (database, filesystem, etc.).
136    #[error("Storage error: {0}")]
137    Storage(String),
138
139    /// Serialization or deserialization failed.
140    #[error("Serialization error: {0}")]
141    Serialization(String),
142
143    /// A constraint was violated (e.g., foreign key, unique constraint).
144    #[error("Constraint violation: {0}")]
145    Constraint(String),
146}
147
148/// Domain-specific errors for process runner operations.
149///
150/// This error type abstracts away process management implementation details
151/// and provides a clean interface for services to handle process failures.
152#[derive(Debug, Error)]
153pub enum ProcessError {
154    /// Failed to start the process.
155    #[error("Failed to start: {0}")]
156    StartFailed(String),
157
158    /// Failed to stop the process.
159    #[error("Failed to stop: {0}")]
160    StopFailed(String),
161
162    /// The process is not running.
163    #[error("Process not running: {0}")]
164    NotRunning(String),
165
166    /// Health check failed.
167    #[error("Health check failed: {0}")]
168    HealthCheckFailed(String),
169
170    /// Configuration error.
171    #[error("Configuration error: {0}")]
172    Configuration(String),
173
174    /// Resource exhaustion (e.g., no available ports).
175    #[error("Resource exhaustion: {0}")]
176    ResourceExhausted(String),
177
178    /// Internal process error.
179    #[error("Internal error: {0}")]
180    Internal(String),
181}
182
183/// Core error type for semantic domain errors.
184///
185/// This is the canonical error type used across the core domain.
186/// Adapters should map this to their own error types (HTTP status codes,
187/// CLI exit codes, Tauri serialized errors).
188#[derive(Debug, Error)]
189pub enum CoreError {
190    /// Repository operation failed.
191    #[error(transparent)]
192    Repository(#[from] RepositoryError),
193
194    /// Process operation failed.
195    #[error(transparent)]
196    Process(#[from] ProcessError),
197
198    /// Settings validation error.
199    #[error(transparent)]
200    Settings(#[from] crate::settings::SettingsError),
201
202    /// Validation error (invalid input).
203    #[error("Validation error: {0}")]
204    Validation(String),
205
206    /// Configuration error.
207    #[error("Configuration error: {0}")]
208    Configuration(String),
209
210    /// External service error.
211    #[error("External service error: {0}")]
212    ExternalService(String),
213
214    /// Internal error (unexpected condition).
215    #[error("Internal error: {0}")]
216    Internal(String),
217}