use crate::engine::SymbolicAnswer;
use crate::event_log::EventLog;
use crate::solver_handlers::{finalize_simple, try_concept_lookup};
use crate::solver_helpers::last_assistant_turn;
pub fn try_how_it_works(
prompt: &str,
normalized: &str,
log: &mut EventLog,
) -> Option<SymbolicAnswer> {
let is_how_it_works = normalized == "how it works?"
|| normalized == "how it works"
|| normalized == "how does it work?"
|| normalized == "how does it work"
|| normalized.starts_with("how does it work")
|| normalized.starts_with("how it works")
|| normalized.starts_with("how does ")
&& (normalized.ends_with(" work?") || normalized.ends_with(" work"));
if !is_how_it_works {
return None;
}
log.append("followup:how_it_works", normalized.to_owned());
let subject = extract_how_it_works_subject(normalized);
if let Some(ref term) = subject {
use crate::concepts::{extract_concept_query, lookup_concept_query};
if let Some(query) = extract_concept_query(&format!("what is {term}")) {
if lookup_concept_query(&query).is_some() {
log.append("followup:subject", format!("inline:{term}"));
return try_concept_lookup(&format!("what is {term}"), log);
}
}
}
if let Some(prior) = last_assistant_turn(log).map(str::to_owned) {
log.append("followup:prior_turn", "assistant".to_owned());
if let Some(term) = extract_topic_from_prior_reply(&prior) {
use crate::concepts::{extract_concept_query, lookup_concept_query};
if let Some(query) = extract_concept_query(&format!("what is {term}")) {
if lookup_concept_query(&query).is_some() {
log.append("followup:subject", format!("prior_reply:{term}"));
return try_concept_lookup(&format!("what is {term}"), log);
}
}
let body = format!(
"To explain how {term} works: I know the term from the prior conversation \
but do not have a detailed symbolic rule for it yet. Add a Links Notation \
fact with the mechanism description, then ask again."
);
log.append("followup:subject", format!("prior_reply_no_record:{term}"));
return Some(finalize_simple(
prompt,
log,
"concept_elaboration_missing",
"response:concept_elaboration_missing",
&body,
0.3,
));
}
}
let body = String::from(
"I answered that way because the prompt matched a deterministic Links Notation rule. \
To ask about a specific topic, try \"how does X work?\" where X is a concept I know \
(e.g. \"how does Wikipedia work?\"). The evidence and trace events are appended to \
the log; see the trace link for the full chain.",
);
Some(finalize_simple(
prompt,
log,
"meta_explanation",
"response:meta_explanation",
&body,
0.5,
))
}
fn extract_how_it_works_subject(normalized: &str) -> Option<String> {
if let Some(rest) = normalized.strip_prefix("how does ") {
let term = rest.trim_end_matches('?').trim_end_matches(" work").trim();
if !term.is_empty() && term != "it" {
return Some(term.to_owned());
}
}
None
}
fn extract_topic_from_prior_reply(reply: &str) -> Option<String> {
let first_line = reply.lines().next().unwrap_or("").trim();
if let Some(paren_pos) = first_line.find('(') {
let candidate = first_line[..paren_pos].trim();
if !candidate.is_empty() {
return Some(candidate.to_lowercase());
}
}
let stop_words = [
"I", "The", "A", "An", "In", "To", "For", "Of", "And", "Or", "Source",
];
for word in reply.split_whitespace() {
let clean = word.trim_matches(|c: char| !c.is_alphanumeric());
if clean.len() >= 2
&& clean.chars().next().is_some_and(char::is_uppercase)
&& !stop_words.contains(&clean)
{
return Some(clean.to_lowercase());
}
}
None
}