use std::collections::BTreeMap;
use rag_rat_core::Config;
use rag_rat_core::query::memory::{RepoMemoryBindTarget, RepoMemoryCreate};
use rag_rat_core::query::symbol::SymbolSelector;
use serde::Deserialize;
use crate::cli::DumpVerifyPacksArgs;
use crate::open_index;
#[derive(Deserialize)]
struct Spec {
memories: Vec<SpecMemory>,
dump: Vec<String>,
}
#[derive(Deserialize)]
struct SpecMemory {
eval_id: String,
kind: String,
title: String,
body: String,
#[serde(default = "default_confidence")]
confidence: String,
#[serde(default)]
binding: Option<Binding>,
}
#[derive(Deserialize)]
struct Binding {
kind: String,
value: String,
}
fn default_confidence() -> String {
"high".to_string()
}
fn bind_target(
db: &rag_rat_core::IndexDatabase,
eval_id: &str,
binding: &Option<Binding>,
) -> anyhow::Result<RepoMemoryBindTarget> {
let Some(binding) = binding else {
return Ok(RepoMemoryBindTarget::default());
};
Ok(match binding.kind.as_str() {
"path" => RepoMemoryBindTarget { path: Some(binding.value.clone()), ..Default::default() },
"dir" => RepoMemoryBindTarget { dir: Some(binding.value.clone()), ..Default::default() },
"logical_symbol" => {
let selector = SymbolSelector {
logical_symbol_id: None,
symbol_id: None,
symbol_path: Some(binding.value.clone()),
symbol: None,
language: None,
allow_ambiguous: false,
limit: 1,
};
let hit = db
.select_symbol_for_bind(&selector)?
.map_err(|d| {
anyhow::anyhow!("`{eval_id}` symbol `{}` is ambiguous: {d:?}", binding.value)
})?
.ok_or_else(|| {
anyhow::anyhow!(
"`{eval_id}` symbol `{}` did not resolve in this index",
binding.value
)
})?;
RepoMemoryBindTarget {
symbol_id: Some(hit.symbol_id),
logical_symbol_id: hit.logical_symbol_id,
..Default::default()
}
},
other => anyhow::bail!("`{eval_id}` has unknown binding kind `{other}`"),
})
}
pub(crate) fn dump_verify_packs(config: &Config, args: &DumpVerifyPacksArgs) -> anyhow::Result<()> {
let spec: Spec = serde_json::from_str(&std::fs::read_to_string(&args.spec)?)?;
let lock_repo = rag_rat_core::locks::write_lock_repo_id(config);
let _lock = rag_rat_core::locks::WriteLock::acquire_blocking(&config.database, &lock_repo)?;
let db = open_index(config)?;
let mut minted: BTreeMap<&str, String> = BTreeMap::new();
for m in &spec.memories {
let bind = bind_target(&db, &m.eval_id, &m.binding)?;
let result = db
.memory_create(RepoMemoryCreate {
kind: m.kind.clone(),
title: m.title.clone(),
body: m.body.clone(),
confidence: m.confidence.clone(),
created_by: Some("eval".to_string()),
source: Some("imported".to_string()),
payload_json: None,
tags: Vec::new(),
bind,
})
.map_err(|e| anyhow::anyhow!("inserting eval memory `{}`: {e}", m.eval_id))?;
minted.insert(&m.eval_id, result.memory.memory_id);
}
let mut packs: BTreeMap<String, String> = BTreeMap::new();
for eval_id in &spec.dump {
let memory_id = minted
.get(eval_id.as_str())
.ok_or_else(|| anyhow::anyhow!("dump id `{eval_id}` is not one of spec.memories"))?;
let pack = db.dream_render_pack(memory_id)?;
packs.insert(format!("{eval_id}|{}", args.root_label), pack);
}
std::fs::write(&args.out, serde_json::to_string_pretty(&packs)?)?;
eprintln!("wrote {} packs (root {}) to {}", packs.len(), args.root_label, args.out.display());
Ok(())
}