forked from kent/consciousness
config: global writable AppConfig; learn settings live there
Runtime-mutable settings (F6's threshold knob, the generate-alternates toggle, anything else that comes along) were ending up as mirrored fields on MindState — each new config setting grew MindState::new's signature and added a clone+sync path. Wrong home. MindState is ephemeral session state, not a config projection. Give AppConfig the same treatment the memory Config has: install it into a global RwLock<AppConfig> at startup via load_app, read through config::app() (returns a read guard), mutate through update_app. The config_writer functions now write to disk AND update the cache atomically, so the one-stop-shop call keeps both in sync. Also while in here: - learn.generate_alternates moves from a sentinel file (~/.consciousness/cache/finetune-alternates, "exists = enabled") into the config under the learn section. On first run with this build, if the sentinel file still exists Mind::new flips the config value to true and removes it. Drops alternates_enabled()/set_alternates(). - Default threshold 0.0000001 → 1.0. With the timestamp filter removed the previous value was letting essentially everything through; 1.0 is a sane "nothing gets through unless you actually want it" default. - score_finetune_candidates takes generate_alternates as a parameter instead of reading a global — caller snapshots the config values once at the top of start_finetune_scoring so the async task doesn't need to hold the config read lock across awaits. - MindState.learn_threshold / learn_generate_alternates gone; the SetLearn* command handlers now just delegate to config_writer. Kent noted RwLock<Arc<AppConfig>> (the pattern used by the memory Config global) is pointless here — nobody needs a snapshot-after- release, reads are short — so this uses a plain RwLock<AppConfig> and returns a read guard. Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
parent
343e43afab
commit
313f85f34a
5 changed files with 102 additions and 58 deletions
|
|
@ -331,13 +331,21 @@ pub struct LearnConfig {
|
|||
/// fine-tuning candidates. Lower = more sensitive.
|
||||
#[serde(default = "default_learn_threshold")]
|
||||
pub threshold: f64,
|
||||
/// Whether to generate "what would the model have said without
|
||||
/// memories" alternates alongside each scoring run. Expensive —
|
||||
/// one full streaming generation per candidate.
|
||||
#[serde(default)]
|
||||
pub generate_alternates: bool,
|
||||
}
|
||||
|
||||
fn default_learn_threshold() -> f64 { 0.0000001 }
|
||||
fn default_learn_threshold() -> f64 { 1.0 }
|
||||
|
||||
impl Default for LearnConfig {
|
||||
fn default() -> Self {
|
||||
Self { threshold: default_learn_threshold() }
|
||||
Self {
|
||||
threshold: default_learn_threshold(),
|
||||
generate_alternates: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -573,12 +581,46 @@ fn build_figment(cli: &crate::user::CliArgs) -> Figment {
|
|||
}
|
||||
|
||||
/// Load just the AppConfig — no validation, no prompt assembly.
|
||||
/// Also installs the loaded AppConfig into the global cache so
|
||||
/// `config::app()` is available everywhere.
|
||||
pub fn load_app(cli: &crate::user::CliArgs) -> Result<(AppConfig, Figment)> {
|
||||
let figment = build_figment(cli);
|
||||
let app: AppConfig = figment.extract().context("Failed to load configuration")?;
|
||||
install_app(app.clone());
|
||||
Ok((app, figment))
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Global AppConfig cache (writable, for runtime-mutable settings
|
||||
// like learn.threshold that F6 edits via config_writer).
|
||||
// ============================================================
|
||||
|
||||
static APP_CONFIG: OnceLock<RwLock<AppConfig>> = OnceLock::new();
|
||||
|
||||
fn install_app(app: AppConfig) {
|
||||
let slot = APP_CONFIG.get_or_init(|| RwLock::new(app.clone()));
|
||||
*slot.write().unwrap() = app;
|
||||
}
|
||||
|
||||
/// Current AppConfig, held under a read lock. Reads should be brief
|
||||
/// (no holding across await / long work) to avoid starving writers.
|
||||
/// Panics if called before load_app — which runs once at startup.
|
||||
pub fn app() -> std::sync::RwLockReadGuard<'static, AppConfig> {
|
||||
APP_CONFIG
|
||||
.get()
|
||||
.expect("config::app() called before load_app()")
|
||||
.read()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Mutate the cached AppConfig in place. Used by config_writer to keep
|
||||
/// the in-memory view in sync with disk after surgical edits to
|
||||
/// ~/.consciousness/config.json5.
|
||||
pub fn update_app(f: impl FnOnce(&mut AppConfig)) {
|
||||
let slot = APP_CONFIG.get().expect("update_app before load_app");
|
||||
f(&mut *slot.write().unwrap());
|
||||
}
|
||||
|
||||
/// Load the full config: figment → AppConfig → resolve backend → assemble prompts.
|
||||
pub async fn load_session(cli: &crate::user::CliArgs) -> Result<(SessionConfig, Figment)> {
|
||||
let (app, figment) = load_app(cli)?;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue