openvibers/openvibe.live Commits

openvibe-sdk v0.62.0 (was v0.61.0) (#320)

OP OpenVibers committed 21 days ago · aab3501644 · 6 files, +16 −11

* openvibe-sdk v0.62.0 (was v0.61.0)

Opened by scripts/pin-bump.js in OpenVibers/OpenVibe.Shared after the v0.62.0 release.

* Pronouns are a guessed gender too: stored AI text that only says he or his is repaired

SDK 0.62.0's noLooks turns he/she/his/her into the person/their. Live's repair scan now finds rows by those
words as well (the repair scope changes, so it runs once more at boot), and the test expects the new wording.

Changes

ddiff --git a/README.md b/README.md
iindex 32f31cf7..f3831a44 100644
---- a/README.md
++++ b/README.md
@@@ -176,6 +176,6 @@ Minimum `.env`: `BASE_URL`, `JWT_SECRET`, `OV_NETWORK_URL`, `OV_NETWORK_INTERNAL
<!-- versions:start -->
- openvibe-contracts: v0.209.0
--- openvibe-sdk: v0.61.0
++- openvibe-sdk: v0.62.0
- openvibe-shared: v4.9.0
<!-- versions:end -->
ddiff --git a/STATUS.json b/STATUS.json
iindex 298d5a8e..23b4d9c5 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -6,7 +6,7 @@
"port": 3000,
"domain": "openvibe.live (www, ingest, whip)",
"contracts": "openvibe-contracts v0.209.0",
-- "sdk": "openvibe-sdk v0.61.0",
++ "sdk": "openvibe-sdk v0.62.0",
"shared": "openvibe-shared v4.9.0",
"capabilities": [
"live.chat_context.read",
ddiff --git a/package-lock.json b/package-lock.json
iindex c5eff20e..5bb10626 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -24,7 +24,7 @@
"node-media-server": "^2.7.1",
"onnxruntime-node": "^1.20.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.209.0",
-- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.61.0",
++ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.62.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.9.0",
"pg": "^8.23.1",
"sanitize-html": "^2.17.7",
@@@ -2660,9 +2660,9 @@
}
},
"node_modules/openvibe-sdk": {
-- "version": "0.61.0",
-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.61.0",
-- "integrity": "sha512-rhQKwM+yzVjhgUPTeqMOkL9N38fbSy8JYiEbJ8SwZk8LGcusde6LoQNhQ8UUxMJIwyY7xR1Rp5NVd6XRaVgTQQ==",
++ "version": "0.62.0",
++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.62.0",
++ "integrity": "sha512-s2KejVS38zK6tKBZUUWfah6RAfddUuHs3EmzrPxar2YhHUQtiZZp19v0eTQ7NKwKGDaTQY+t8Pf3k+ao11m+7g==",
"license": "MIT",
"bin": {
"openvibe-download": "bin/openvibe-download.js"
ddiff --git a/package.json b/package.json
iindex 589a71ef..c5b21f45 100644
---- a/package.json
++++ b/package.json
@@@ -43,7 +43,7 @@
"node-media-server": "^2.7.1",
"onnxruntime-node": "^1.20.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.209.0",
-- "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.61.0",
++ "openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.62.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.9.0",
"pg": "^8.23.1",
"sanitize-html": "^2.17.7",
ddiff --git a/server/db/database.js b/server/db/database.js
iindex 61541968..5027f712 100644
---- a/server/db/database.js
++++ b/server/db/database.js
@@@ -650,7 +650,7 @@ function _extractDescFromMaybeJson(text) {
// written, so the scan only needs to run again when the scrub rules change: this hash of their source says when.
let _looksRulesVersion = null;
// What the repair covers is part of its version: a change in scope runs the scan again even when the rules did not change.
--const _LOOKS_REPAIR_SCOPE = 'full and short columns, each on its own (2026-10-22)';
++const _LOOKS_REPAIR_SCOPE = 'full and short columns, each on its own; pronouns (2026-10-23)';
function looksRulesVersion() {
if (!_looksRulesVersion) _looksRulesVersion = require('crypto').createHash('sha1').update([_LOOKS_REPAIR_SCOPE, _LOOKS_RULES].join('|')).digest('hex').slice(0, 12);
return _looksRulesVersion;
@@@ -698,7 +698,7 @@ async function cleanupMalformedAiText({ force = false } = {}) {
// prompts forbade it): the person becomes "the streamer" and the card line follows.
let looksDone = false;
try { looksDone = !force && (await getState('ai_looks_repair_rules')) === looksRulesVersion(); } catch { looksDone = false; }
-- const looks = "~* '(shirtless|topless|bare-chested|young|teen|little|bearded|bald|blond|haired|tattooed|muscular|heavyset|older|elderly|middle-aged|skinny|slim|chubby|attractive|pretty|handsome|cute|boy|girl|kid|child|youth)|\\m(wear|dressed|sporting|shirt|t-shirt|tee|tank top|hoodie|sweat|jacket|coat|vest|beanie|cap|hat|helmet|headset|headphones|sunglasses|glasses|outfit|dress|jersey|suit|robe|bandana|scar|mask|attire|apparel|beard|mustache|moustache|goatee|stubble|tattoo|hair|ponytail|braids|dreadlocks|piercing|freckles|makeup|[a-z-]+-wearing)|\\m(man|woman|men|women|guy|guys|gal|lady|ladies|gentleman|gentlemen|male|female)\\M'";
++ const looks = "~* '(shirtless|topless|bare-chested|young|teen|little|bearded|bald|blond|haired|tattooed|muscular|heavyset|older|elderly|middle-aged|skinny|slim|chubby|attractive|pretty|handsome|cute|boy|girl|kid|child|youth)|\\m(wear|dressed|sporting|shirt|t-shirt|tee|tank top|hoodie|sweat|jacket|coat|vest|beanie|cap|hat|helmet|headset|headphones|sunglasses|glasses|outfit|dress|jersey|suit|robe|bandana|scar|mask|attire|apparel|beard|mustache|moustache|goatee|stubble|tattoo|hair|ponytail|braids|dreadlocks|piercing|freckles|makeup|[a-z-]+-wearing)|\\m(man|woman|men|women|guy|guys|gal|lady|ladies|gentleman|gentlemen|male|female|he|she|him|his|her|hers|himself|herself)\\M'";
for (const [table, key, col, short] of [['stream_memories', 'id', 'description', null], ['streams', 'id', 'ai_overview', 'ai_overview_short'],
['vod_ai_state', 'vod_id', 'ai_overview', 'ai_overview_short'], ['clip_ai_state', 'clip_id', 'ai_overview', 'ai_overview_short'],
['streamer_overviews', 'user_id', 'overview', 'overview_short']]) {
ddiff --git a/test/ai-text-no-looks.test.js b/test/ai-text-no-looks.test.js
iindex 1bec373a..76df23aa 100644
---- a/test/ai-text-no-looks.test.js
++++ b/test/ai-text-no-looks.test.js
@@@ -36,7 +36,7 @@ const db = require('../server/db/database');
['Panelists discuss while wearing headsets and microphones, with a trophy nearby.', 'Panelists discuss, with a trophy nearby.'],
['A man wearing sunglasses on his head looks down at the camera.', 'A person looks down at the camera.'],
['A collage of a man in a white shirt and tie wearing dark sunglasses in the left panel.', 'A collage of a person in the left panel.'],
-- ['Behind him a green mat; he wears a blue jacket and is leaning to the left.', 'Behind him a green mat; he is leaning to the left.'],
++ ['Behind him a green mat; he wears a blue jacket and is leaning to the left.', 'Behind them a green mat; the person is leaning to the left.'],
['Two men lift weights, one shirtless and sweating.', 'Two people lift weights, one sweating.'],
['Two men stand in an alley, one and drinking from a bottle while the other wears a dusty t-shirt; both look tired.', 'Two people stand in an alley, one drinking from a bottle; both look tired.'],
['A man wearing a green bucket hat with frog-like eyes sits indoors.', 'A person sits indoors.'],
@@@ -98,6 +98,11 @@ const db = require('../server/db/database');
await db.setState('ai_looks_repair_rules', 'an older rule set');
await db.cleanupMalformedAiText();
assert.strictEqual((await db.get('SELECT description FROM stream_memories WHERE offset_seconds = 9240')).description, 'A person cooks pasta.');
++ // A pronoun is a guessed gender too (SDK 0.62.0): a row whose only gendered word is "he" or "his" is repaired.
++ await db.run('INSERT INTO stream_memories (stream_id, user_id, offset_seconds, description) VALUES (?, ?, ?, ?)', [sid, uid, 9300, 'The streamer says he will check his settings.']);
++ await db.setState('ai_looks_repair_rules', 'an older rule set');
++ await db.cleanupMalformedAiText();
++ assert.strictEqual((await db.get('SELECT description FROM stream_memories WHERE offset_seconds = 9300')).description, 'The streamer says the person will check their settings.');
// A card line stored without its full text (or derived from an older one) is repaired on its own.
await db.run('INSERT INTO clip_ai_state (clip_id, ai_overview_short) VALUES (?, ?)', [990001, 'The video shows a man wearing a headset in a kitchen.']);
await db.run('INSERT INTO clip_ai_state (clip_id, ai_overview_short) VALUES (?, ?)', [990002, 'A person adjusts their hair or clothing near the counter.']);
@@@ -105,7 +110,7 @@ const db = require('../server/db/database');
await db.cleanupMalformedAiText();
assert.strictEqual((await db.get('SELECT ai_overview_short FROM clip_ai_state WHERE clip_id = 990001')).ai_overview_short, 'The video shows a person in a kitchen.');
assert.strictEqual((await db.get('SELECT ai_overview_short FROM clip_ai_state WHERE clip_id = 990002')).ai_overview_short, ' ', 'a line that is all looks becomes the nothing-to-say sentinel');
-- assert.strictEqual((await db.get('SELECT description FROM stream_memories WHERE offset_seconds = 9000')).description, 'The streamer rests his chin on his hand.');
++ assert.strictEqual((await db.get('SELECT description FROM stream_memories WHERE offset_seconds = 9000')).description, 'The streamer rests their chin on their hand.');
assert.deepStrictEqual(Object.values(await db.get('SELECT ai_overview, ai_overview_short FROM streams WHERE id = ?', [sid])), ['The streamer plays Fortnite.', 'The streamer plays Fortnite.']);
assert.strictEqual((await db.get('SELECT description FROM stream_memories WHERE offset_seconds = 9060')).description, 'An older version of the game loads.', 'looks words about things stay');