proteinea / src /lib /task-title.ts
Mahmoud Eljendy
feat: Antibody Studio — AI-native antibody design workspace by Proteinea
30cc31a
/**
* Infer a short, pragmatic task title from the first user message.
*
* Biomni renames tasks within a few seconds of the first exchange — "Trastuzumab
* HER2+ Breast Cancer Trials", "TCGA-BRCA RNA-seq Differential Expression", etc.
* This is a small, heuristic version that runs instantly on the client: grab the
* longest run of content words and compress them.
*/
const STOPWORDS = new Set([
"a","an","the","and","or","but","of","for","in","on","at","to","with","by","from",
"is","are","was","were","be","being","been","have","has","had","do","does","did",
"can","could","should","would","will","may","might","must","shall",
"i","we","you","they","it","this","that","these","those","my","our","your","their",
"please","kindly","help","me","us","need","want","like","get","give","tell","show",
"using","based","above","below","about","into","than","then","as","so","also",
"trial","run","pipeline","task","question","query",
]);
/** Split to words, drop stopwords, keep scientific tokens (all-caps, digits, hyphens). */
function tokenize(text: string): string[] {
return text
.replace(/[\r\n]+/g, " ")
.split(/[^A-Za-z0-9+\-./]+/)
.filter(Boolean);
}
export function inferTaskTitle(firstUserMessage: string, maxChars = 48): string {
if (!firstUserMessage) return "Untitled Session";
// Strip "TRIAL N (x):" style prefixes
const stripped = firstUserMessage.replace(/^\s*TRIAL\s+\d+\s*\([^)]*\)\s*:\s*/i, "").trim();
const tokens = tokenize(stripped);
const kept: string[] = [];
for (const t of tokens) {
const lower = t.toLowerCase();
if (STOPWORDS.has(lower)) continue;
// Keep acronyms as-is; otherwise Title Case words
const preserved = /^[A-Z0-9][A-Z0-9+./\-]+$/.test(t)
? t
: t.charAt(0).toUpperCase() + t.slice(1).toLowerCase();
kept.push(preserved);
if (kept.join(" ").length >= maxChars) break;
}
if (kept.length === 0) {
// Fallback: first 6 raw words
const fallback = stripped.split(/\s+/).slice(0, 6).join(" ");
return truncate(fallback || "Untitled Session", maxChars);
}
return truncate(kept.join(" "), maxChars);
}
function truncate(s: string, max: number): string {
return s.length <= max ? s : s.slice(0, max - 1).trimEnd() + "…";
}