refactor(server): plumb Arc<Settings> through workers and routes
Config sheds its 14 Bucket-B fields (retention, whisper, ollama, llm) and gains a sibling Arc<Settings> in AppState, loaded from settings.toml at startup via Settings::load_or_default. Workers (transcribe, analyze, recovery, auto_trigger) and routes (health, bulk, case_actions, user_web) take settings as a separate parameter or State<> extractor; Config::llm_configured moves to Settings::llm_configured. TestConfig::build_pair() returns (Arc<Config>, Arc<Settings>); the new create_router_with_settings / create_router_and_session_store_with_settings helpers wire both into integration tests that exercise LLM/Whisper/Ollama. The legacy single-Arc create_router falls back to Settings::default() so the 17 non-LLM tests stay untouched. Verified: cargo build clean, clippy --all-targets clean, 318 tests pass.
This commit is contained in:
+3
-63
@@ -75,6 +75,9 @@ struct UsersFile {
|
||||
user: Vec<User>,
|
||||
}
|
||||
|
||||
/// Bootstrap config loaded once from `.env` at startup. Runtime-tunable
|
||||
/// values (retention, whisper/ollama/llm endpoints, prompts) live in
|
||||
/// `Settings` (loaded from `settings.toml`).
|
||||
pub struct Config {
|
||||
// Phase 1 — required
|
||||
pub server_port: u16,
|
||||
@@ -86,24 +89,6 @@ pub struct Config {
|
||||
pub api_keys: HashMap<String, String>, // api_key_value → slug
|
||||
|
||||
// Phase 2+ — optional with defaults
|
||||
pub retention_audio_days: u32,
|
||||
pub retention_transcript_days: u32,
|
||||
pub retention_document_days: u32,
|
||||
pub whisper_url: String,
|
||||
pub whisper_timeout_seconds: u64,
|
||||
pub ollama_url: String,
|
||||
pub ollama_model: String,
|
||||
pub ollama_keep_alive: u32,
|
||||
pub llm_url: String,
|
||||
pub llm_api_key: String,
|
||||
pub llm_model: String,
|
||||
pub llm_temperature: f32,
|
||||
pub llm_timeout_seconds: u64,
|
||||
/// System prompt sent to the analysis LLM. Defaults to
|
||||
/// `crate::analyze::prompt::SYSTEM_PROMPT` if `LLM_SYSTEM_PROMPT` is unset
|
||||
/// or empty. Runtime-configurable so admins can iterate and use the
|
||||
/// "Neu analysieren" button to re-run cases.
|
||||
pub llm_system_prompt: String,
|
||||
/// Directory containing gazetteer `*.txt` files (anatomy, substances,
|
||||
/// medications). Loaded once at startup; a missing directory is a
|
||||
/// non-fatal WARN — analysis runs without proper-name correction.
|
||||
@@ -135,27 +120,6 @@ impl Config {
|
||||
users,
|
||||
api_keys,
|
||||
|
||||
retention_audio_days: optional_env_parsed("RETENTION_AUDIO_DAYS", 30),
|
||||
retention_transcript_days: optional_env_parsed("RETENTION_TRANSCRIPT_DAYS", 30),
|
||||
retention_document_days: optional_env_parsed("RETENTION_DOCUMENT_DAYS", 0),
|
||||
whisper_url: optional_env("WHISPER_URL", "http://localhost:10300"),
|
||||
whisper_timeout_seconds: optional_env_parsed("WHISPER_TIMEOUT_SECONDS", 120),
|
||||
ollama_url: optional_env("OLLAMA_URL", "http://localhost:11434"),
|
||||
ollama_model: optional_env("OLLAMA_MODEL", "gemma3:4b"),
|
||||
ollama_keep_alive: optional_env_parsed("OLLAMA_KEEP_ALIVE", 0),
|
||||
llm_url: optional_env("LLM_URL", ""),
|
||||
llm_api_key: optional_env("LLM_API_KEY", ""),
|
||||
llm_model: optional_env("LLM_MODEL", ""),
|
||||
llm_temperature: optional_env_parsed("LLM_TEMPERATURE", 0.0),
|
||||
llm_timeout_seconds: optional_env_parsed("LLM_TIMEOUT_SECONDS", 180),
|
||||
llm_system_prompt: {
|
||||
let v = optional_env("LLM_SYSTEM_PROMPT", "");
|
||||
if v.is_empty() {
|
||||
crate::analyze::prompt::SYSTEM_PROMPT.to_string()
|
||||
} else {
|
||||
v
|
||||
}
|
||||
},
|
||||
vocab_dir: PathBuf::from(optional_env("VOCAB_DIR", "./vocab")),
|
||||
hunspell_dict_path: PathBuf::from(optional_env(
|
||||
"HUNSPELL_DICT",
|
||||
@@ -166,16 +130,6 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
/// True iff the external analysis LLM is configured. `llm_api_key` is
|
||||
/// intentionally **not** required — Ollama's OpenAI-compatible endpoint
|
||||
/// at `/v1/chat/completions` accepts unauthenticated requests and is a
|
||||
/// supported deployment target. For hosted providers (Ionos, OpenAI) the
|
||||
/// key remains necessary in practice; the provider will reject with 401
|
||||
/// if it is missing, which is correct feedback.
|
||||
pub fn llm_configured(&self) -> bool {
|
||||
!self.llm_url.is_empty() && !self.llm_model.is_empty()
|
||||
}
|
||||
|
||||
/// Sane defaults for integration tests. Not a `Default` impl on purpose:
|
||||
/// production code must go through `from_env()`, and `Default::default()`
|
||||
/// carries an implicit "safe fallback" connotation this value does not
|
||||
@@ -199,20 +153,6 @@ impl Config {
|
||||
log_max_days: 90,
|
||||
users: Vec::new(),
|
||||
api_keys: HashMap::new(),
|
||||
retention_audio_days: 30,
|
||||
retention_transcript_days: 30,
|
||||
retention_document_days: 0,
|
||||
whisper_url: "http://localhost:10300".into(),
|
||||
whisper_timeout_seconds: 120,
|
||||
ollama_url: "http://localhost:11434".into(),
|
||||
ollama_model: "gemma3:4b".into(),
|
||||
ollama_keep_alive: 0,
|
||||
llm_url: String::new(),
|
||||
llm_api_key: String::new(),
|
||||
llm_model: String::new(),
|
||||
llm_temperature: 0.0,
|
||||
llm_timeout_seconds: 180,
|
||||
llm_system_prompt: crate::analyze::prompt::SYSTEM_PROMPT.to_string(),
|
||||
vocab_dir: PathBuf::new(),
|
||||
hunspell_dict_path: PathBuf::new(),
|
||||
session_timeout_hours: 8,
|
||||
|
||||
Reference in New Issue
Block a user