pub(crate) const ENTITY_DESCRIPTION_SYSTEM_PROMPT: &str = "You are a knowledge graph annotator. From the entity and the evidence in the user message, write a concise one-sentence description (10-20 words) explaining what the entity IS and WHY it matters in its real domain.\n\n\
Grounding rules:\n\
- Use ONLY facts stated in the provided evidence.\n\
- If the evidence does not support any factual statement about this entity, set `sufficient_evidence` to false and `description` to null. Abstaining is the correct answer, never a failure.\n\
- NEVER infer a profession, nationality, employer or biography from a personal name. A name alone is not evidence.\n\
- NEVER fall back to a software, product, framework or configuration-file frame when the evidence does not mention one.\n\
- Write the description in the SAME language as the evidence.";
pub(crate) fn entity_description_domain_section(domain: &str) -> String {
let d = domain.trim();
if d.is_empty() || d.eq_ignore_ascii_case("auto") || d.eq_ignore_ascii_case("none") {
return String::new();
}
format!(
"Operator domain hint: {d}. Prefer facts consistent with this domain when supported by evidence; do not invent details outside the evidence.\n\n"
)
}
pub(crate) fn entity_description_user_text(
entity_name: &str,
entity_type: &str,
domain_section: &str,
corpus_section: &str,
) -> String {
format!(
"Entity name: {entity_name}\nEntity type: {entity_type}\n\n{domain_section}{corpus_section}"
)
}
pub(crate) fn edge_endpoints_section(
source_name: &str,
source_description: Option<&str>,
target_name: &str,
target_description: Option<&str>,
) -> String {
let mut text = String::with_capacity(128);
text.push_str(&format!("Source entity: {source_name}\n"));
if let Some(d) = source_description.map(str::trim).filter(|d| !d.is_empty()) {
text.push_str(&format!("Source description: {d}\n"));
}
text.push_str(&format!("Target entity: {target_name}\n"));
if let Some(d) = target_description.map(str::trim).filter(|d| !d.is_empty()) {
text.push_str(&format!("Target description: {d}\n"));
}
text
}
pub(crate) fn entity_type_validate_user_text(
entity_name: &str,
current_type: &str,
description: Option<&str>,
evidence: &str,
) -> String {
let mut text = format!("Entity name: {entity_name}\nCurrent type: {current_type}\n");
if let Some(desc) = description.map(str::trim).filter(|d| !d.is_empty()) {
text.push_str(&format!("Stored description: {desc}\n"));
}
let evidence = evidence.trim();
if !evidence.is_empty() {
text.push_str(&format!(
"\nEvidence (ground truth; judge only from these facts):\n{evidence}\n"
));
}
text
}
pub(crate) fn resolve_entity_description_domain(cli: &str) -> String {
let c = cli.trim();
if !c.is_empty() && !c.eq_ignore_ascii_case("auto") {
return c.to_string();
}
crate::config::get_setting("enrich.entity_description.domain")
.ok()
.flatten()
.filter(|s| !s.trim().is_empty())
.unwrap_or_else(|| "auto".to_string())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn domain_section_empty_for_auto() {
assert!(entity_description_domain_section("auto").is_empty());
assert!(entity_description_domain_section("none").is_empty());
assert!(entity_description_domain_section("").is_empty());
}
#[test]
fn domain_section_includes_label() {
let s = entity_description_domain_section("fiscal");
assert!(s.contains("fiscal"));
assert!(s.contains("Operator domain hint"));
}
#[test]
fn the_user_text_carries_the_evidence() {
let text = entity_type_validate_user_text(
"rd_gs",
"concept",
Some("prefix of the generated folders"),
"Linked memory bodies:\nrd_gs marks the generated tree",
);
assert!(text.contains("rd_gs"), "entity name must survive: {text}");
assert!(
text.contains("concept"),
"current type must survive: {text}"
);
assert!(
text.contains("prefix of the generated folders"),
"stored description must reach the model: {text}"
);
assert!(
text.contains("rd_gs marks the generated tree"),
"corpus evidence must reach the model: {text}"
);
}
#[test]
fn an_absent_description_leaves_no_empty_heading() {
for missing in [None, Some(""), Some(" ")] {
let text = entity_type_validate_user_text("x", "concept", missing, "");
assert!(
!text.contains("Stored description:"),
"empty description must not print a heading: {text:?}"
);
assert!(
!text.contains("Evidence"),
"empty evidence must not print a heading: {text:?}"
);
}
}
}