fix(model-server): deadline de warmup configurable pour le cold-start llama.cpp (#55)
Corrige la régression high « Error on loading local model » : le premier chargement du serveur modèle local (cold-start llama.cpp) dépassait la fenêtre de readiness et échouait. Le warmup dispose désormais d'un deadline par défaut de 600 s, surchargable par config optionnelle `warmup_deadline_secs` (validée dans [30, 1800]). - domain: champ `warmup_deadline_secs: Option<u64>` + validation de borne - application: policy de readiness effective (défaut 600 s, override par config) - app-tauri: DTO `warmupDeadlineSecs` - infrastructure: application de la deadline effective au warmup Verdict QA (vert) : domain 252, application 81 + 22 model_server, app-tauri dto_model_servers 6, infrastructure model_server 2, build OK. Contrat readiness couvert sur ports mockés. Caveat : le cold-start end-to-end réel (llama-server) sort du sandbox de test -> vérification manuelle utilisateur restante, non couverte par les tests unitaires. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -1057,6 +1057,9 @@ pub struct ModelServerConfigDto {
|
||||
pub auto_start: bool,
|
||||
/// Stop policy.
|
||||
pub stop_policy: StopPolicyDto,
|
||||
/// Optional readiness warmup deadline override in seconds.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub warmup_deadline_secs: Option<u64>,
|
||||
}
|
||||
|
||||
impl ModelServerConfigDto {
|
||||
@ -1080,6 +1083,7 @@ impl ModelServerConfigDto {
|
||||
args: config.args,
|
||||
auto_start: config.auto_start,
|
||||
stop_policy: config.stop_policy.into(),
|
||||
warmup_deadline_secs: config.warmup_deadline_secs,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1122,6 +1126,7 @@ impl ModelServerConfigDto {
|
||||
self.auto_start,
|
||||
self.stop_policy.into(),
|
||||
)
|
||||
.and_then(|config| config.with_warmup_deadline_secs(self.warmup_deadline_secs))
|
||||
.map_err(invalid_domain_error)
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,6 +59,7 @@ fn model_server_dto_serialises_flat_camelcase_wire_shape() {
|
||||
assert_eq!(value["contextSize"], 4096);
|
||||
assert_eq!(value["jinja"], true);
|
||||
assert_eq!(value["stopPolicy"], "stopOnAppExit");
|
||||
assert!(value.get("warmupDeadlineSecs").is_none());
|
||||
assert!(value.get("model").is_none(), "domain model must not leak");
|
||||
assert!(
|
||||
value.get("endpoint").is_none(),
|
||||
@ -101,6 +102,7 @@ fn model_server_dto_deserialises_flat_wire_shape_and_generates_model_id() {
|
||||
assert_eq!(input.config.options.gpu_layers, Some(35));
|
||||
assert_eq!(input.config.options.context_size, Some(4096));
|
||||
assert_eq!(input.config.options.host, "127.0.0.1");
|
||||
assert_eq!(input.config.warmup_deadline_secs, None);
|
||||
assert!(!input.config.options.jinja);
|
||||
assert_eq!(input.config.model.served_name, "qwen3-coder");
|
||||
assert_eq!(input.config.model.label, "qwen3-coder");
|
||||
@ -129,12 +131,42 @@ fn model_server_dto_preserves_existing_internal_model_id_on_upsert() {
|
||||
args: Vec::new(),
|
||||
auto_start: true,
|
||||
stop_policy: StopPolicyDto::StopOnAppExit,
|
||||
warmup_deadline_secs: Some(900),
|
||||
};
|
||||
|
||||
let config = dto.into_domain(Some(&existing)).unwrap();
|
||||
|
||||
assert_eq!(config.model.id, "stable-internal-id");
|
||||
assert_eq!(config.model.served_name, "qwen3-coder-updated");
|
||||
assert_eq!(config.warmup_deadline_secs, Some(900));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn model_server_dto_rejects_invalid_warmup_deadline_secs() {
|
||||
let raw = json!({
|
||||
"id": sid(40).to_string(),
|
||||
"kind": "llamaCpp",
|
||||
"name": "Local Qwen",
|
||||
"baseURL": "http://localhost:8085",
|
||||
"port": 8085,
|
||||
"modelPath": "/models/qwen.gguf",
|
||||
"servedModelName": "qwen3-coder",
|
||||
"host": "127.0.0.1",
|
||||
"jinja": false,
|
||||
"args": [],
|
||||
"autoStart": true,
|
||||
"stopPolicy": "stopOnAppExit",
|
||||
"warmupDeadlineSecs": 29
|
||||
});
|
||||
|
||||
let request = SaveModelServerRequestDto {
|
||||
config: serde_json::from_value(raw).unwrap(),
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
save_model_server_input(request, None).unwrap_err().code,
|
||||
"INVALID"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user