use std::path::{Path, PathBuf};
pub const DEFAULT_PROVIDER: &str = "openai";
pub const DEFAULT_OPENAI_BASE_URL: &str = "http://localhost:11434/v1";
pub const DEFAULT_OPENAI_MODEL: &str = "kimi-k2.6:cloud";
pub const DEFAULT_MAGI_MELCHIOR: &str = "qwen3.5:397b-cloud";
pub const DEFAULT_MAGI_BALTHASAR: &str = "gpt-oss:120b-cloud";
pub const DEFAULT_MAGI_CASPAR: &str = "deepseek-v4-pro:cloud";
pub const DEFAULT_ANTHROPIC_MODEL: &str = "claude-sonnet-4-6";
pub const DEFAULT_EMBEDDING_MODEL: &str = "nomic-embed-text-v2-moe:latest";
pub fn no_config_notice() -> String {
format!(
"No magi.toml — using Ollama defaults ({base}, {model}, \
Melchior: {mel}, Balthasar: {bal}, Caspar: {cas}). Copy \
docs/magi.toml.example to customize, or set provider=\"anthropic\" \
for Anthropic.",
base = DEFAULT_OPENAI_BASE_URL,
model = DEFAULT_OPENAI_MODEL,
mel = DEFAULT_MAGI_MELCHIOR,
bal = DEFAULT_MAGI_BALTHASAR,
cas = DEFAULT_MAGI_CASPAR,
)
}
pub fn render_default_magi_toml() -> String {
use crate::memory::config::{EmbeddingConfig, MemoryConfig};
use std::fmt::Write as FmtWrite;
let mem = MemoryConfig::default();
let emb = EmbeddingConfig::default();
let markers: String = mem
.salience_markers
.iter()
.map(|s| format!("\"{}\"", s))
.collect::<Vec<_>>()
.join(", ");
let mut out = String::with_capacity(4096);
writeln!(
out,
"# Generated by magi-rs --init-config / /init-config — built-in Ollama-first defaults."
)
.unwrap();
writeln!(
out,
"# Edit to customize, or set provider = \"anthropic\" to use Anthropic instead."
)
.unwrap();
writeln!(out, "provider = \"{}\"", DEFAULT_PROVIDER).unwrap();
writeln!(out).unwrap();
writeln!(out, "[openai]").unwrap();
writeln!(out, "base_url = \"{}\"", DEFAULT_OPENAI_BASE_URL).unwrap();
writeln!(out, "model = \"{}\"", DEFAULT_OPENAI_MODEL).unwrap();
writeln!(out).unwrap();
writeln!(out, "[anthropic]").unwrap();
writeln!(out, "model = \"{}\"", DEFAULT_ANTHROPIC_MODEL).unwrap();
writeln!(out).unwrap();
writeln!(out, "[magi]").unwrap();
writeln!(out, "melchior_model = \"{}\"", DEFAULT_MAGI_MELCHIOR).unwrap();
writeln!(out, "balthasar_model = \"{}\"", DEFAULT_MAGI_BALTHASAR).unwrap();
writeln!(out, "caspar_model = \"{}\"", DEFAULT_MAGI_CASPAR).unwrap();
writeln!(
out,
"auto_approve = false \
# true = launch MAGI consult automatically (announces in TUI); \
false = ask before each autonomous launch (default). \
The explicit /consult TUI command is always user-initiated and never gated."
)
.unwrap();
writeln!(out).unwrap();
writeln!(
out,
"# ---------------------------------------------------------------------------"
)
.unwrap();
writeln!(
out,
"# [memory] — tiered memory subsystem (absent = built-in defaults apply)."
)
.unwrap();
writeln!(
out,
"# Default mode is \"selective\" (embedding-indexed, bounded context)."
)
.unwrap();
writeln!(
out,
"# Set mode = \"load_all\" to reproduce v0.6.0 behavior (benchmark control)."
)
.unwrap();
writeln!(out, "[memory]").unwrap();
writeln!(
out,
"mode = \"{}\" # selective | load_all (load_all = v0.6.0 control)",
mem.mode
)
.unwrap();
writeln!(
out,
"context_budget_tokens = {} # assembled-context token budget",
mem.context_budget_tokens
)
.unwrap();
writeln!(
out,
"distill_enabled = {} # false = zero LLM egress for distillation",
mem.distill_enabled
)
.unwrap();
writeln!(
out,
"# Retention: -1 = archive forever (never hard-delete), 0 = hard-delete on eviction,"
)
.unwrap();
writeln!(
out,
"# N>0 = hard-delete N days after eviction. For truly unlimited storage set BOTH"
)
.unwrap();
writeln!(
out,
"# evicted_retention_days = -1 AND max_records = 0 (max_records still prunes the"
)
.unwrap();
writeln!(
out,
"# weakest active records when the count exceeds the cap, even at -1)."
)
.unwrap();
writeln!(out, "evicted_retention_days = {} # -1 archive forever | 0 hard-delete now | N>0 delete after N days", mem.evicted_retention_days).unwrap();
writeln!(out, "max_records = {} # hard ceiling on active records; 0 = unlimited (opt-out the cap)", mem.max_records).unwrap();
writeln!(
out,
"# --- Advanced [memory] knobs (default values shown; uncomment to override) ---"
)
.unwrap();
writeln!(
out,
"# response_headroom_tokens = {} # tokens reserved for the model reply",
mem.response_headroom_tokens
)
.unwrap();
writeln!(
out,
"# safety_margin_ratio = {} # fraction of budget held back (heuristic guard)",
toml_f64(mem.safety_margin_ratio)
)
.unwrap();
writeln!(
out,
"# chars_per_token = {} # token heuristic; es ~3.5, code ~3.0, CJK ~2.0",
toml_f64(mem.chars_per_token)
)
.unwrap();
writeln!(
out,
"# oversized_turn_policy = \"{}\" # truncate | error",
mem.oversized_turn_policy
)
.unwrap();
writeln!(
out,
"# top_k = {} # retrieval candidate count",
mem.top_k
)
.unwrap();
writeln!(
out,
"# weight_similarity = {} # reranker weight on cosine similarity",
toml_f64(mem.weight_similarity)
)
.unwrap();
writeln!(
out,
"# weight_recency = {} # reranker weight on recency",
toml_f64(mem.weight_recency)
)
.unwrap();
writeln!(
out,
"# weight_salience = {} # reranker weight on salience",
toml_f64(mem.weight_salience)
)
.unwrap();
writeln!(
out,
"# default_salience = {} # base salience assigned at write",
toml_f64(mem.default_salience)
)
.unwrap();
writeln!(
out,
"# preference_salience = {} # protected floor for kind=preference",
toml_f64(mem.preference_salience)
)
.unwrap();
writeln!(
out,
"# protect_salience_threshold = {} # salience at/above which a memory is never evicted",
toml_f64(mem.protect_salience_threshold)
)
.unwrap();
writeln!(
out,
"# decay_half_life_days = {} # wall-clock recency half-life",
toml_f64(mem.decay_half_life_days)
)
.unwrap();
writeln!(
out,
"# access_saturation_cap = {} # cap on access-reinforcement contribution",
mem.access_saturation_cap
)
.unwrap();
writeln!(
out,
"# forget_strength_threshold = {} # strength below which a memory is forgettable",
toml_f64(mem.forget_strength_threshold)
)
.unwrap();
writeln!(
out,
"# supersede_similarity_threshold = {} # same-subject hard-supersession similarity gate",
toml_f64(mem.supersede_similarity_threshold)
)
.unwrap();
writeln!(
out,
"# distill_every_n_turns = {} # 0 = on-demand/session-close only",
mem.distill_every_n_turns
)
.unwrap();
writeln!(
out,
"# distill_on_session_close = {} # run distiller on session close",
mem.distill_on_session_close
)
.unwrap();
writeln!(
out,
"# profile_max_tokens = {} # always-injected preference profile token bound",
mem.profile_max_tokens
)
.unwrap();
writeln!(
out,
"# seed = {} # determinism for retrieval/decay/benchmark",
mem.seed
)
.unwrap();
writeln!(
out,
"# salience_markers = [{}] # substrings that lift salience at write",
markers
)
.unwrap();
writeln!(out, "# index = \"{}\" # exact (default, deterministic) | ann (build feature)", mem.index).unwrap();
writeln!(
out,
"# distill_max_batch_tokens = {} # per-run LLM payload cap (privacy)",
mem.distill_max_batch_tokens
)
.unwrap();
writeln!(
out,
"# supersede_max_candidate_pairs = {} # distiller hard-supersession pair cap per run",
mem.supersede_max_candidate_pairs
)
.unwrap();
writeln!(
out,
"# reembed_batch_size = {} # lazy re-embed throttle per pass",
mem.reembed_batch_size
)
.unwrap();
writeln!(
out,
"# max_evictions_per_pass = {} # clock-jump guard on evictions per pass",
mem.max_evictions_per_pass
)
.unwrap();
writeln!(
out,
"# migration_throttle_batch = {} # lazy migration batch size",
mem.migration_throttle_batch
)
.unwrap();
writeln!(out).unwrap();
writeln!(
out,
"# ---------------------------------------------------------------------------"
)
.unwrap();
writeln!(
out,
"# [embedding] — embedding provider (OpenAI-compatible, Ollama-first)."
)
.unwrap();
writeln!(out, "# Set model to your installed embedding model.").unwrap();
writeln!(out, "# Run: ollama pull {}", emb.model).unwrap();
writeln!(
out,
"# The embedding API key is read from OPENAI_API_KEY env only (never in this file)."
)
.unwrap();
writeln!(out, "[embedding]").unwrap();
writeln!(
out,
"base_url = \"{}\" # Ollama default; point elsewhere for cloud",
emb.base_url
)
.unwrap();
writeln!(
out,
"model = \"{}\" # CHANGE to your installed embedding model — run: ollama pull <model>",
emb.model
)
.unwrap();
writeln!(
out,
"dim = {} # 0 = autodetect the vector size from the first response",
emb.dim
)
.unwrap();
writeln!(out, "# --- Advanced [embedding] knobs ---").unwrap();
writeln!(
out,
"# provider = \"{}\" # embedding provider kind (openai-compatible)",
emb.provider
)
.unwrap();
writeln!(
out,
"# query_prefix = \"{}\" # prefix applied to query text before embedding",
emb.query_prefix
)
.unwrap();
writeln!(
out,
"# document_prefix = \"{}\" # prefix applied to stored text before embedding",
emb.document_prefix
)
.unwrap();
out
}
fn toml_f64(v: f64) -> String {
let s = format!("{}", v);
if s.contains('.') || s.contains('e') || s.contains('E') {
s
} else {
format!("{}.0", s)
}
}
pub fn write_default_config(dir: &Path) -> anyhow::Result<PathBuf> {
let path = dir.join("magi.toml");
match std::fs::OpenOptions::new()
.write(true)
.create_new(true)
.open(&path)
{
Ok(mut f) => {
use std::io::Write;
f.write_all(render_default_magi_toml().as_bytes())?;
Ok(path)
}
Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => anyhow::bail!(
"{} already exists — refusing to overwrite (edit it manually or delete it first)",
path.display()
),
Err(e) => Err(anyhow::anyhow!("failed to write {}: {e}", path.display())),
}
}
pub fn should_emit_default_notice(provider_kind: &str, magi_toml_exists: bool) -> bool {
provider_kind == "openai" && !magi_toml_exists
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_constants_are_the_ollama_first_profile() {
assert_eq!(DEFAULT_PROVIDER, "openai");
assert_eq!(DEFAULT_OPENAI_BASE_URL, "http://localhost:11434/v1");
assert_eq!(DEFAULT_OPENAI_MODEL, "kimi-k2.6:cloud");
assert_eq!(DEFAULT_MAGI_MELCHIOR, "qwen3.5:397b-cloud");
assert_eq!(DEFAULT_MAGI_BALTHASAR, "gpt-oss:120b-cloud");
assert_eq!(DEFAULT_MAGI_CASPAR, "deepseek-v4-pro:cloud");
assert_eq!(DEFAULT_ANTHROPIC_MODEL, "claude-sonnet-4-6");
}
#[test]
fn test_should_emit_default_notice_only_for_openai_without_file() {
assert!(should_emit_default_notice("openai", false));
assert!(!should_emit_default_notice("openai", true)); assert!(!should_emit_default_notice("anthropic", false)); assert!(!should_emit_default_notice("anthropic", true));
}
#[test]
fn test_no_config_notice_interpolates_all_defaults() {
let n = no_config_notice();
assert!(n.contains(DEFAULT_OPENAI_BASE_URL));
assert!(n.contains(DEFAULT_OPENAI_MODEL));
assert!(n.contains(DEFAULT_MAGI_MELCHIOR));
assert!(n.contains(DEFAULT_MAGI_BALTHASAR));
assert!(n.contains(DEFAULT_MAGI_CASPAR));
}
#[test]
fn test_render_default_magi_toml_interpolates_and_parses() {
let s = render_default_magi_toml();
assert!(s.contains(DEFAULT_OPENAI_BASE_URL));
assert!(s.contains(DEFAULT_OPENAI_MODEL));
assert!(s.contains(DEFAULT_MAGI_MELCHIOR));
assert!(s.contains(DEFAULT_MAGI_BALTHASAR));
assert!(s.contains(DEFAULT_MAGI_CASPAR));
let parsed = crate::config::MagiConfig::from_toml_str(&s).unwrap();
assert_eq!(parsed.provider.as_deref(), Some("openai"));
assert_eq!(
parsed.magi.melchior_model.as_deref(),
Some(DEFAULT_MAGI_MELCHIOR)
);
assert_eq!(parsed.memory.mode, "selective");
assert_eq!(parsed.embedding.model, DEFAULT_EMBEDDING_MODEL);
}
#[test]
fn test_write_default_config_writes_when_absent() {
let dir = tempfile::tempdir().unwrap();
let path = write_default_config(dir.path()).unwrap();
assert_eq!(path, dir.path().join("magi.toml"));
assert!(path.exists());
let body = std::fs::read_to_string(&path).unwrap();
assert!(body.contains(DEFAULT_OPENAI_MODEL));
}
#[test]
fn test_render_default_magi_toml_includes_memory_and_embedding_sections() {
let s = render_default_magi_toml();
assert!(
s.contains("[memory]"),
"generated toml must contain an active [memory] section"
);
assert!(
s.contains("[embedding]"),
"generated toml must contain an active [embedding] section"
);
assert!(
s.contains(DEFAULT_EMBEDDING_MODEL),
"generated toml must show the default embedding model ({DEFAULT_EMBEDDING_MODEL})"
);
let parsed = crate::config::MagiConfig::from_toml_str(&s)
.expect("render_default_magi_toml() must produce valid TOML");
assert_eq!(
parsed.provider.as_deref(),
Some("openai"),
"parsed provider must be 'openai'"
);
assert_eq!(
parsed.memory.mode, "selective",
"active [memory] section must parse mode as 'selective'"
);
assert_eq!(
parsed.embedding.model, DEFAULT_EMBEDDING_MODEL,
"active [embedding] section must parse model as the current default"
);
}
#[test]
fn test_write_default_config_refuses_to_overwrite() {
let dir = tempfile::tempdir().unwrap();
std::fs::write(dir.path().join("magi.toml"), "provider = \"anthropic\"").unwrap();
let err = write_default_config(dir.path()).unwrap_err();
assert!(err.to_string().contains("magi.toml"));
let body = std::fs::read_to_string(dir.path().join("magi.toml")).unwrap();
assert_eq!(body, "provider = \"anthropic\"");
}
#[test]
fn test_render_default_magi_toml_covers_all_field_names() {
let s = render_default_magi_toml();
let mem_fields = [
"mode",
"context_budget_tokens",
"response_headroom_tokens",
"safety_margin_ratio",
"chars_per_token",
"oversized_turn_policy",
"top_k",
"weight_similarity",
"weight_recency",
"weight_salience",
"default_salience",
"preference_salience",
"protect_salience_threshold",
"decay_half_life_days",
"access_saturation_cap",
"forget_strength_threshold",
"evicted_retention_days",
"max_records",
"supersede_similarity_threshold",
"distill_every_n_turns",
"distill_on_session_close",
"profile_max_tokens",
"seed",
"salience_markers",
"index",
"distill_max_batch_tokens",
"supersede_max_candidate_pairs",
"distill_enabled",
"reembed_batch_size",
"max_evictions_per_pass",
"migration_throttle_batch",
];
for field in &mem_fields {
assert!(
s.contains(field),
"render_default_magi_toml() is missing MemoryConfig field: {field}"
);
}
let emb_fields = [
"query_prefix",
"document_prefix",
"base_url",
"model",
"dim",
];
for field in &emb_fields {
assert!(
s.contains(field),
"render_default_magi_toml() is missing EmbeddingConfig field: {field}"
);
}
let parsed = crate::config::MagiConfig::from_toml_str(&s)
.expect("render_default_magi_toml() must produce valid TOML (commented lines inert)");
assert_eq!(parsed.memory.mode, "selective");
assert_eq!(parsed.embedding.model, DEFAULT_EMBEDDING_MODEL);
}
#[test]
fn test_config_example_has_no_secret_material() {
let s = std::fs::read_to_string(concat!(
env!("CARGO_MANIFEST_DIR"),
"/docs/magi.toml.example"
))
.unwrap();
let low = s.to_lowercase();
assert!(
!low.contains("api_key"),
"docs/magi.toml.example must not contain 'api_key'"
);
assert!(
!s.contains("sk-"),
"docs/magi.toml.example must not contain 'sk-' key prefix"
);
}
}