use uuid::Uuid;
use crate::store::NodeKind;
use crate::store::Store;
use crate::store::hierarchy::Hierarchy;
pub(super) struct FactEntry {
pub id: Uuid,
pub location: String,
pub text: String,
}
pub(super) const TRUTH_CHUNK: usize = 8;
pub(super) fn gather_facts(store: &Store, h: &Hierarchy, book_id: Uuid) -> Vec<FactEntry> {
let mut out = Vec::new();
for id in h.collect_subtree(book_id) {
let Some(node) = h.get(id) else { continue };
if node.kind != NodeKind::Paragraph {
continue;
}
let text = match store.get_content(id) {
Ok(Some(bytes)) => String::from_utf8_lossy(&bytes).trim().to_string(),
_ => String::new(),
};
if text.is_empty() {
continue;
}
out.push(FactEntry { id, location: h.slug_path(node), text });
}
out
}
pub(super) fn truth_system(language: &str) -> String {
format!(
"You are fact-checking statements from a writer's reference database against your \
general knowledge. For EACH numbered statement, judge its real-world factual accuracy. \
Respond with one line per statement, in this exact shape:\n\
<number>. ACCURATE | DUBIOUS | INACCURATE — <short reason>\n\
Be concise. Do not add commentary outside the per-statement lines. \
Write the reasons in {language}."
)
}
pub(super) fn truth_user(chunk: &[&FactEntry], base: usize) -> String {
let mut s = String::from("Statements:\n");
for (i, f) in chunk.iter().enumerate() {
s.push_str(&format!("{}. {}\n", base + i + 1, f.text));
}
s
}
pub(super) fn consistency_system(language: &str) -> String {
format!(
"You are checking a writer's reference database for internal consistency. Below are \
numbered facts. Identify every PAIR of facts that CONTRADICT each other. Respond with \
one line per contradicting pair, in this exact shape:\n\
<a> ⇄ <b> — <what conflicts>\n\
If there are no contradictions, reply exactly: No contradictions found. \
Write the explanations in {language}."
)
}
pub(super) fn consistency_user(facts: &[FactEntry]) -> String {
let mut s = String::from("Facts:\n");
for (i, f) in facts.iter().enumerate() {
s.push_str(&format!("{}. {}\n", i + 1, f.text));
}
s
}
#[cfg(test)]
mod tests {
use super::*;
fn fe(text: &str) -> FactEntry {
FactEntry { id: Uuid::nil(), location: "facts/x".into(), text: text.into() }
}
#[test]
fn truth_user_numbers_from_base() {
let a = fe("A"); let b = fe("B");
let refs = vec![&a, &b];
let u = truth_user(&refs, 8);
assert!(u.contains("9. A"));
assert!(u.contains("10. B"));
}
#[test]
fn prompts_carry_language() {
assert!(truth_system("Russian").contains("Russian"));
assert!(consistency_system("Russian").contains("Russian"));
}
#[test]
fn consistency_user_numbers_from_one() {
let facts = vec![fe("A"), fe("B")];
let u = consistency_user(&facts);
assert!(u.contains("1. A"));
assert!(u.contains("2. B"));
}
}