const MAX_FALLBACK_LEN: usize = 60;
const BANNER_TITLE_PREFIXES: &[&str] = &[
"message sent, but generation was interrupted",
"消息已发送,但生成中断",
];
const BANNER_DETAILS_LABEL_PREFIXES: &[&str] = &["technical details", "技术详情"];
pub fn heuristic_title(first_user_text: &str) -> String {
let cleaned = strip_code_fences(first_user_text);
let stripped = strip_pasted_failure_banner(&cleaned);
let content = if stripped.trim().is_empty() {
cleaned.as_str()
} else {
stripped.trim()
};
let collapsed: String = content.split_whitespace().collect::<Vec<_>>().join(" ");
let first_sentence = collapsed.split(['.', '!', '?', '\n']).next().unwrap_or("");
truncate_at_word_boundary(first_sentence, MAX_FALLBACK_LEN)
.trim_end_matches([' ', ',', ';', ':', '-'])
.trim()
.to_string()
}
fn truncate_at_word_boundary(text: &str, max_chars: usize) -> &str {
let Some((hard_cut, next_char)) = text.char_indices().nth(max_chars) else {
return text;
};
let head = &text[..hard_cut];
if next_char.is_whitespace() {
return head;
}
match head.rfind(char::is_whitespace) {
Some(word_cut) if word_cut > 0 => &head[..word_cut],
_ => head,
}
}
fn strip_pasted_failure_banner(text: &str) -> String {
let lines: Vec<&str> = text.lines().collect();
let Some(start) = lines.iter().position(|line| !line.trim().is_empty()) else {
return String::new();
};
let has_prefix = |line: &str, prefixes: &[&str]| {
let normalized = line.trim().to_lowercase();
prefixes.iter().any(|prefix| normalized.starts_with(prefix))
};
if !has_prefix(lines[start], BANNER_TITLE_PREFIXES) {
return text.to_string();
}
let banner_line = lines[start].trim().to_lowercase();
let joined_details = BANNER_DETAILS_LABEL_PREFIXES
.iter()
.any(|label| banner_line.contains(label));
let mut skip_body = joined_details;
let mut index = start + 1;
while index < lines.len() {
let line = lines[index];
if line.trim().is_empty() {
index += 1;
break;
}
if !skip_body && has_prefix(line, BANNER_DETAILS_LABEL_PREFIXES) {
skip_body = true;
index += 1;
continue;
}
if !skip_body {
break;
}
index += 1;
}
lines[index..].join("\n")
}
fn strip_code_fences(text: &str) -> String {
let mut result = String::with_capacity(text.len());
let mut in_fence = false;
for line in text.lines() {
if line.trim_start().starts_with("```") {
in_fence = !in_fence;
continue;
}
if !in_fence {
result.push_str(line);
result.push('\n');
}
}
result
}