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:
@ -162,7 +162,7 @@ impl Default for ReadinessPolicy {
|
||||
Self {
|
||||
attempts: 20,
|
||||
backoff: Duration::from_millis(250),
|
||||
warmup_deadline: Duration::from_secs(120),
|
||||
warmup_deadline: Duration::from_secs(600),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -261,6 +261,15 @@ impl EnsureLocalModelServer {
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the readiness policy effective for a persisted server config.
|
||||
///
|
||||
/// The config may override only the warmup deadline; probe cadence remains
|
||||
/// owned by the application policy.
|
||||
#[must_use]
|
||||
pub fn effective_readiness_policy(&self, config: &LocalModelServerConfig) -> ReadinessPolicy {
|
||||
self.readiness_for(config)
|
||||
}
|
||||
|
||||
/// Overrides the long Hugging Face download/preparation deadline.
|
||||
#[must_use]
|
||||
pub fn with_hf_download_deadline(mut self, deadline: Duration) -> Self {
|
||||
@ -440,8 +449,9 @@ impl EnsureLocalModelServer {
|
||||
handle: &ManagedProcessHandle,
|
||||
hf_source: Option<String>,
|
||||
) -> Result<EnsureLocalModelServerOutput, AppError> {
|
||||
let readiness = self.readiness_for(config);
|
||||
let mut attempts = 0usize;
|
||||
let deadline = Instant::now() + self.readiness.warmup_deadline;
|
||||
let deadline = Instant::now() + readiness.warmup_deadline;
|
||||
loop {
|
||||
match self.probe.probe(&config.endpoint).await {
|
||||
Err(err) => {
|
||||
@ -462,7 +472,7 @@ impl EnsureLocalModelServer {
|
||||
|
||||
match self.process.status(handle).await {
|
||||
Ok(ProcessStatus::Running) => {
|
||||
if attempts.saturating_add(1) == self.readiness.attempts {
|
||||
if attempts.saturating_add(1) == readiness.attempts {
|
||||
if let Some(source) = hf_source.as_ref() {
|
||||
self.publish(
|
||||
config.id,
|
||||
@ -496,14 +506,24 @@ impl EnsureLocalModelServer {
|
||||
self.stop_started_server(config.id, handle).await;
|
||||
return self.fail(config.id, err);
|
||||
}
|
||||
if !self.readiness.backoff.is_zero() {
|
||||
tokio::time::sleep(self.readiness.backoff).await;
|
||||
if !readiness.backoff.is_zero() {
|
||||
tokio::time::sleep(readiness.backoff).await;
|
||||
} else {
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn readiness_for(&self, config: &LocalModelServerConfig) -> ReadinessPolicy {
|
||||
ReadinessPolicy {
|
||||
warmup_deadline: config
|
||||
.warmup_deadline_secs
|
||||
.map(Duration::from_secs)
|
||||
.unwrap_or(self.readiness.warmup_deadline),
|
||||
..self.readiness
|
||||
}
|
||||
}
|
||||
|
||||
/// Stops active servers whose policy is [`StopPolicy::StopOnAppExit`].
|
||||
///
|
||||
/// # Errors
|
||||
|
||||
Reference in New Issue
Block a user