Skip to main content

oxibrain_cli/cmd/
reextract.rs

1//! `oxibrain reextract` — re-extract all primary episodes with the configured
2//! extractor (DESIGN §12.4).
3//!
4//! Uses a new ExtractorConfig (new model/prompt/major = new cache keys), so old
5//! extraction cache entries are preserved (D8). Only uncached episodes are sent
6//! to the LLM. Requires a configured LLM provider (see `cmd::llm`).
7
8use crate::cmd::llm;
9use oxibrain::{Brain, BrainConfig};
10use oxibrain_ports::SystemClock;
11use std::path::Path;
12use std::sync::Arc;
13
14pub async fn run(dir: &Path, space: &str) -> anyhow::Result<()> {
15    let provider = llm::from_env().await?;
16    let clock = Arc::new(SystemClock);
17    let brain = match provider.tokenizer.clone() {
18        Some(tok) => {
19            Brain::with_llm_and_tokenizer(BrainConfig::at(dir), clock, provider.port.clone(), tok)
20                .await?
21        }
22        None => Brain::with_llm(BrainConfig::at(dir), clock, provider.port.clone()).await?,
23    };
24    let space_id = brain.ensure_space(space).await?;
25    let config = llm::config(
26        provider.model_id.clone(),
27        provider.mechanism,
28        provider.model_digest.clone(),
29        provider.profile_id(),
30    );
31    let summary = brain.reextract(&space_id, &config).await?;
32    println!(
33        "reextract: {} episodes done, {} failed, {} extracted, {} quarantined",
34        summary.episodes_done, summary.episodes_failed, summary.extracted, summary.quarantined
35    );
36    for (episode_id, error) in &summary.failures {
37        eprintln!("reextract: episode {episode_id} failed: {error}");
38    }
39    Ok(())
40}