openvibers/openvibe.live Commits

Channel lines: never cut after an introduced noun (#783)

OP OpenVibers committed 3 days ago · 632bfcd59c · 2 files, +6 −2

Changes

ddiff --git a/server/ai/channel-line.js b/server/ai/channel-line.js
iindex 50ae8aa0..3de6b9ab 100644
---- a/server/ai/channel-line.js
++++ b/server/ai/channel-line.js
@@@ -27,6 +27,8 @@ const MAX_CONTENT = MAX_LEN - 'Streams '.length - 1; // 81
// Joining words a cut may not end on ("… sessions and", "… with a").
const TRAIL_WORD = /^(?:a|an|the|and|or|but|with|from|in|on|at|by|for|of|to|as|that|while|when|often|usually|frequently|typically|mostly|generally|sometimes|featuring|including|presenting)$/i;
++// A cut may not end on an introduced noun ("… and a dark", "… with a focus"): the phrase continues.
++const WEAK_END = /\b(?:a|an|the)\s+\S+$/i;
// The verb the card line opens with, wherever the overview puts it early ("the flaming feline streams …").
const VERB = /\b(?:streams?|broadcasts?|presents?|plays?|hosts?|covers?|shows?|builds?|creates?|chats?)\b/i;
// A description in the past tense is not the card's present tense: use the fallback shape instead.
@@@ -130,7 +132,7 @@ function fitContent(content) {
if (phrases.length > 1) {
let out = phrases[0], best = '';
for (let i = 1; i < phrases.length; i++) {
-- if (out.length <= MAX_CONTENT && (out.length >= 50 || NEXT_CLAUSE.test(phrases[i]))) best = out;
++ if (out.length <= MAX_CONTENT && (out.length >= 50 || NEXT_CLAUSE.test(phrases[i])) && !WEAK_END.test(out)) best = out;
const next = `${out}, ${phrases[i]}`;
if (next.length > MAX_CONTENT) break;
out = next;
@@@ -161,7 +163,8 @@ function shapeChannelLine({ username = null, displayName = null, overview = '' }
// A colon never reaches a card line; a comma keeps the sentence reading.
content = fitContent(content.replace(/:/g, ',')).replace(/[.!?…]+$/g, '').trim();
-- if (!content) continue;
++ // "as a car-streamer" is a complete role; other trailing "a …" is an unfinished phrase.
++ if (!content || (!/^as\s+(?:a|an)\s/i.test(content) && WEAK_END.test(content))) continue;
const line = `Streams ${content}.`;
if (line.length > MAX_LEN || !CHANNEL_LINE_RE.test(line)) continue;
// The owner's line (AGENTS.md, server/ai/explicit.js): people may be described; sex acts never are.
ddiff --git a/test/channel-line-shape.test.js b/test/channel-line-shape.test.js
iindex 564840e6..d1d4783a 100644
---- a/test/channel-line-shape.test.js
++++ b/test/channel-line-shape.test.js
@@@ -103,6 +103,7 @@ const TRIM = [
{ overview: 'mrcodygraves streams as an IRL creator, often centered in a dark, minimalist setup with a headset and a Champion shirt.', line: 'Streams as an IRL creator, often centered in a dark, minimalist setup.' },
{ overview: '1n1xx streams a mix of fast-paced first-person shooters and arcade-style Roblox-style obstacle courses, with on-screen chat.', line: 'Streams a mix of fast-paced first-person shooters.' },
{ overview: 'Bobdope streams as an irl content creator sharing underwater marine life footage, turtle and reef scenes, and other real-world aquatic views.', line: 'Streams as an irl content creator sharing underwater marine life footage.' },
++ { overview: 'Telegram_a1z69 streams in a desktop-focused setup, often featuring a dim mobile screen and a dark, indistinct background.', line: 'Streams in a desktop-focused setup, often featuring a dim mobile screen.' },
];
for (const t of TRIM) {
const line = shapeChannelLine({ username: 'x', displayName: 'x', overview: t.overview });