Add loopd configuration with every M2a limit

Implemented-By: Laguna S 2.1 (OpenCode)
This commit is contained in:
2026-09-17 23:07:38 -07:00
parent a0495ea36a
commit 6e8e24794d
8 changed files with 318 additions and 0 deletions
+123
View File
@@ -0,0 +1,123 @@
//! `loopd` configuration: read `config.toml` into a typed `Config`.
//!
//! This is our own format, so unknown keys are errors in every table: a
//! misspelt `liveness_ms` that silently fell back to its default would be a
//! limit the owner believes is set and is not.
use serde::Deserialize;
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Infer {
pub socket: std::path::PathBuf,
pub model: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Slots {
pub main: u32,
pub background: u32,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Expect {
pub template_sha256: proto::Hash32,
pub n_ctx: u64,
pub slots: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Deserialize)]
#[serde(deny_unknown_fields, default)]
pub struct Sampling {
pub temperature: f64,
pub top_p: f64,
pub top_k: u32,
}
impl Default for Sampling {
fn default() -> Self {
Self {
temperature: 0.6,
top_p: 0.95,
top_k: 20,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(deny_unknown_fields, default)]
pub struct Limits {
pub poll_ms: u64,
pub busy_wait_ms: u64,
pub load_wait_ms: u64,
pub idle_grace_ms: u64,
pub liveness_ms: u64,
pub thinking_cap: u64,
pub thinking_overrun: u64,
pub max_tokens: u64,
pub queue_len: usize,
pub retry_attempts: u32,
pub retry_backoff_ms: Vec<u64>,
pub retry_window_ms: u64,
}
impl Default for Limits {
fn default() -> Self {
Self {
poll_ms: 5_000,
busy_wait_ms: 600_000,
load_wait_ms: 180_000,
idle_grace_ms: 30_000,
liveness_ms: 30_000,
thinking_cap: 4_096,
thinking_overrun: 256,
max_tokens: 8_192,
queue_len: 8,
retry_attempts: 4,
retry_backoff_ms: vec![2_000, 8_000, 30_000],
retry_window_ms: 300_000,
}
}
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
pub infer: Infer,
pub slots: Slots,
pub expect: Expect,
#[serde(default)]
pub sampling: Sampling,
#[serde(default)]
pub limits: Limits,
}
#[derive(Debug)]
pub enum ConfigError {
Read(std::path::PathBuf, std::io::Error),
Parse(std::path::PathBuf, toml::de::Error),
}
impl std::fmt::Display for ConfigError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
ConfigError::Read(path, err) => write!(f, "{}: {err}", path.display()),
ConfigError::Parse(path, err) => write!(f, "{}: {err}", path.display()),
}
}
}
impl std::error::Error for ConfigError {}
impl Config {
pub fn parse(text: &str) -> Result<Config, toml::de::Error> {
toml::from_str(text)
}
pub fn load(path: &std::path::Path) -> Result<Config, ConfigError> {
let text =
std::fs::read_to_string(path).map_err(|e| ConfigError::Read(path.to_path_buf(), e))?;
toml::from_str(&text).map_err(|e| ConfigError::Parse(path.to_path_buf(), e))
}
}