openvibers/openvibe.ai Commits

live.moments.pick offers a way out to clips only (#277)

OP OpenVibers committed 10 days ago · 26f42f8be2 · 3 files, +14 −1

#273 let the pick answer that nothing is worth it, for pastes and clips alike. On the next run the model declined all
twelve VODs and the home got no new pastes. A paste needs one good frame; only a clip may come back empty. The
abstention sentence is now a variable the prepare hook fills for clips and leaves empty for pastes.

Changes

ddiff --git a/server/workflows/hooks.js b/server/workflows/hooks.js
iindex 64fc065..ddd90f5 100644
---- a/server/workflows/hooks.js
++++ b/server/workflows/hooks.js
@@@ -73,6 +73,7 @@ const MOMENT_FLAVOR = {
paste: 'This moment becomes a SCREENSHOT paste: pick something visually striking in a single frame — a face or reaction, a visual gag, something odd on screen, a scene change. What was said matters less than what is SEEN.',
clip: 'This moment becomes a 25-SECOND VIDEO CLIP: pick a beat that plays out over time — a line that lands, a reaction, a sound, chat exploding, something HAPPENING — not a static pretty frame.',
};
++const ABSTAIN_CLIP = 'If NOTHING here is clearly worth a clip (only routine talk with no reaction, a static screen, nothing happening, or only moments already used), say so instead of forcing a pick: a missed clip costs less than a bad one. Then answer {"t": null, "title": "", "desc": "", "why": "<a few words>"}. Otherwise: ';
const oneLine = (t, max) => String(t || '').replace(/\s+/g, ' ').trim().slice(0, max);
// Text shown to the public as a description: one paragraph of prose. A model that answers with a list, headings or
// bold labels ("- Streamer handle: x (@x).") has its markup and labels dropped, line by line, and headings left out,
@@@ -201,6 +202,9 @@ const PREPARE = {
clipped: times(input.clipped, 12),
spikes: times(input.spikes, 6),
flavor_hint: MOMENT_FLAVOR[input.flavor] || MOMENT_FLAVOR.paste,
++ // Only a clip may come back empty (C09): a paste needs one good frame, and offering it the same way out
++ // made the model decline all twelve VODs of the 11-03 run, leaving the home with no new pastes.
++ abstain_hint: input.flavor === 'clip' ? ABSTAIN_CLIP : '',
avoid: avoid.map(mmss).join(', '),
},
};
ddiff --git a/server/workflows/live.js b/server/workflows/live.js
iindex 5bfd7c8..db07283 100644
---- a/server/workflows/live.js
++++ b/server/workflows/live.js
@@@ -236,7 +236,7 @@ const templates = [
},
},
system_prompt: '',
-- user_prompt: 'Below is a single livestream VOD titled "{{title}}", described by its on-screen TIMELINE (visual scene notes) and its AUDIO TRANSCRIPT, each line timestamped [m:ss]. Titles and descriptions are about what happens.\n\nTIMELINE:\n{{timeline}}\n\nTRANSCRIPT:\n{{transcript}}\n\nSOUNDS HEARD (non-speech audio events):\n{{sounds}}\n\nViewers CLIPPED these timestamps (very strong "this was a highlight" signal): {{clipped}}\nChat activity SPIKED around: {{spikes}}\n\n{{flavor_hint}}\n{{#avoid}}ALREADY USED (a paste or clip exists there) — do NOT pick these or anything within 2 minutes of them: {{avoid}}. Find a DIFFERENT moment.{{/avoid}}\n\nFind the SINGLE most interesting/funny/dramatic/surprising/striking moment in this VOD with something clearly VISIBLE happening. Prefer moments backed by the clip/chat signals when they line up with something notable. NEVER pick a black/dark/loading/blank screen, an intro/BRB card, or a moment with no visible content or activity. If NOTHING here is clearly worth it — only routine talk with no reaction, a static screen, nothing happening, or only moments already used — say so instead of forcing a pick: a missed moment costs less than a bad one. Then answer {"t": null, "title": "", "desc": "", "why": "<a few words>"}. Otherwise return STRICT JSON only, nothing else: {"t": <seconds into the vod>, "title": "<specific punchy 3-8 word title, not the stream name, funny, never generic>", "desc": "<one vivid sentence describing the moment>"}',
++ user_prompt: 'Below is a single livestream VOD titled "{{title}}", described by its on-screen TIMELINE (visual scene notes) and its AUDIO TRANSCRIPT, each line timestamped [m:ss]. Titles and descriptions are about what happens.\n\nTIMELINE:\n{{timeline}}\n\nTRANSCRIPT:\n{{transcript}}\n\nSOUNDS HEARD (non-speech audio events):\n{{sounds}}\n\nViewers CLIPPED these timestamps (very strong "this was a highlight" signal): {{clipped}}\nChat activity SPIKED around: {{spikes}}\n\n{{flavor_hint}}\n{{#avoid}}ALREADY USED (a paste or clip exists there) — do NOT pick these or anything within 2 minutes of them: {{avoid}}. Find a DIFFERENT moment.{{/avoid}}\n\nFind the SINGLE most interesting/funny/dramatic/surprising/striking moment in this VOD with something clearly VISIBLE happening. Prefer moments backed by the clip/chat signals when they line up with something notable. NEVER pick a black/dark/loading/blank screen, an intro/BRB card, or a moment with no visible content or activity. {{abstain_hint}}Return STRICT JSON only, nothing else: {"t": <seconds into the vod>, "title": "<specific punchy 3-8 word title, not the stream name, funny, never generic>", "desc": "<one vivid sentence describing the moment>"}',
default_route: 'live.legacy', owner: 'live', metadata: { json_name: 'moment_pick' },
},
{
ddiff --git a/test/live-templates.test.js b/test/live-templates.test.js
iindex 260613e..96c956c 100644
---- a/test/live-templates.test.js
++++ b/test/live-templates.test.js
@@@ -30,6 +30,15 @@ t.test('the prompt renders as Live rendered it', () => {
assert.deepStrictEqual(PREPARE['live.moments.pick']({ title: 'x' }), { output: null }, 'nothing to work from: no run');
});
++t.test('only a clip is offered the way out (C09): a paste always names its frame', () => {
++ const clip = render(tpl.user_prompt, PREPARE['live.moments.pick']({ title: 'x', flavor: 'clip', timeline: [{ t: 1, text: 'a' }] }).vars);
++ const paste = render(tpl.user_prompt, PREPARE['live.moments.pick']({ title: 'x', flavor: 'paste', timeline: [{ t: 1, text: 'a' }] }).vars);
++ assert.match(clip, /If NOTHING here is clearly worth a clip[\s\S]*"t": null[\s\S]*Otherwise: Return STRICT JSON only/);
++ assert.ok(!/"t": null/.test(paste) && !/NOTHING here/.test(paste), 'no way out for a paste');
++ assert.match(paste, /Return STRICT JSON only, nothing else: \{"t": <seconds into the vod>/);
++ assert.ok(!/\{\{|\$\{/.test(clip + paste), 'nothing left unrendered');
++});
++
t.test('the answer is read like Live read it', () => {
const post = POSTPROCESS['live.moments.pick'];
assert.deepStrictEqual(post({ text: 'Sure! {"t": 95.7, "title": "\\"It Compiles\\"", "desc": "He jumps."}' }), { t: 95, title: 'It Compiles', desc: 'He jumps.' });