feat(model-server): source Hugging Face (-hf) + options structurées llama.cpp + migration store V2

Backend du support natif d'une source modèle Hugging Face en alternative au
chemin .gguf local pour le serveur llama.cpp intégré, et refonte des options.

- domaine : ModelSource {LocalPath|HuggingFace} + HfModelRef,
  LlamaCppOptions {host,gpu_layers,context_size,jinja}, invariant auto_start
  exigeant une source, validate_free_args (rejet des flags réservés).
- infra : build_argv partagé avec build_spawn_spec, émission -hf vs --model,
  ordre argv figé ; migration model-servers.json V1 -> V2.
- app-tauri : DTO V2 (modelSource, compat modelPath, conflit = INVALID),
  commande preview_model_server_command.

QA : cargo test ciblés verts (domain 7, application 8, infra model_server 4,
app-tauri dto 5). Échecs des suites complètes infra/app-tauri environnementaux
(sandbox bind/socket), hors périmètre.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-12 14:31:54 +02:00
parent bc96285fa5
commit 38aecf2cac
11 changed files with 820 additions and 102 deletions

View File

@ -7,6 +7,7 @@ use std::time::Duration;
use domain::events::DomainEvent;
use domain::model_server::{
LocalModelServerConfig, ModelServerLifecycleStatus, ModelServerReady, ModelServerStatus,
ModelSource,
};
use domain::ports::{
EventBus, FileSystem, ManagedProcess, ManagedProcessHandle, ModelServerError, ModelServerProbe,
@ -332,11 +333,14 @@ impl EnsureLocalModelServer {
&self,
config: &LocalModelServerConfig,
) -> Result<(), AppError> {
let Some(path) = config.model.path.as_ref() else {
let err = ModelServerError::PathNotAccessible("model.path missing".to_owned());
let Some(ModelSource::LocalPath { path }) = config.model.source.as_ref() else {
return Ok(());
};
if path.as_str().is_empty() {
let err = ModelServerError::PathNotAccessible("model.source.path missing".to_owned());
self.publish_failure(config.id, &err);
return Err(err.into());
};
}
match self
.fs
.exists(&RemotePath::new(path.as_str().to_owned()))