use super::thread::RagMode;
pub(super) const CATEGORY: &str = "research";
const EST_USD_PER_1K_TOKENS: f64 = 0.003;
pub(super) fn estimate_cost(prompt: &str, response: &str) -> f64 {
let chars = (prompt.len() + response.len()) as f64;
let tokens = chars / 4.0;
(tokens / 1000.0) * EST_USD_PER_1K_TOKENS
}
pub(super) fn system_prompt(rag_mode: RagMode, rag_context: Option<&str>) -> String {
let mut s = String::from(
"You are a research assistant helping a writer populate their knowledge base. \
Answer accurately. When uncertain about specific facts, say so explicitly. \
Do NOT fabricate citations, authors, dates, or statistics.",
);
match rag_context {
Some(ctx) if !ctx.trim().is_empty() => {
s.push_str("\n\nThe author has these existing facts in their knowledge base:\n");
s.push_str(ctx.trim());
}
_ => {}
}
if rag_mode == RagMode::FactsOnly {
s.push_str(
"\n\nAnswer using only the provided context. If the answer is not in the provided \
context, say so.",
);
}
s
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cost_scales_with_length() {
let small = estimate_cost("hi", "there");
let big = estimate_cost(&"x".repeat(4000), &"y".repeat(4000));
assert!(big > small);
assert!(small >= 0.0);
}
#[test]
fn facts_only_adds_closed_instruction() {
let p = system_prompt(RagMode::FactsOnly, Some("a fact"));
assert!(p.contains("only the provided context"));
let p2 = system_prompt(RagMode::FactsPlusFull, Some("a fact"));
assert!(!p2.contains("only the provided context"));
assert!(p2.contains("a fact"));
}
#[test]
fn no_context_section_when_empty() {
let p = system_prompt(RagMode::FactsPlusFull, None);
assert!(!p.contains("existing facts"));
}
}