openvibers/openvibe.network Commits

Actor runs its model calls through OpenVibe.AI (plan L55 C3) (#478)

OP OpenVibers committed 12 days ago · 35229b5b36 · 2 files, +15 −0

A person chooses the model their tasks run on, with their own provider key (sealed in OpenVibe.AI) or with OpenVibe's. Actor asks AI's OpenAI-compatible endpoint (workflow ai.complete) to run the call. Actor holds no key of its own, so it takes no consent grant (ai.credential.manage): it may not store a person's key in AI.

Changes

ddiff --git a/server/identity/principals.js b/server/identity/principals.js
iindex 8277e5c1..fe5daac1 100644
---- a/server/identity/principals.js
++++ b/server/identity/principals.js
@@@ -471,6 +471,10 @@ const DEFAULT_GRANTS = [
// OpenAI-compatible harnesses). Nothing else of AI's.
['codes', 'ai.credential.manage', 'openvibe.ai', []],
['codes', 'ai.run.create', 'openvibe.ai', ['ai.messages', 'ai.complete']],
++ // Actor (plan L55 C3): a person chooses the model their tasks run on, with their own provider key when they have
++ // one (the key lives in OpenVibe.AI, never in Actor). Actor runs the call through AI's OpenAI-compatible endpoint
++ // (workflow ai.complete), the same one Codes' harnesses use. Nothing else of AI's.
++ ['actor', 'ai.run.create', 'openvibe.ai', ['ai.complete']],
// A streamer's daily AI-viewer budget is an AI quota on their attribution (live:user:<id>), which Live sets.
['live', 'ai.quota.attribution.manage', 'openvibe.ai', []],
// Wave 3: producers publish to OpenVibe.Events (their own source only, enforced by Events).
ddiff --git a/test/run-tokens.test.js b/test/run-tokens.test.js
iindex fd62cc40..3b019c7e 100644
---- a/test/run-tokens.test.js
++++ b/test/run-tokens.test.js
@@@ -76,6 +76,17 @@ const runTokens = require('../server/identity/run-tokens');
const codesGrants = await principals.grantsFor(db, 'codes', 'openvibe.ai');
assert.deepStrictEqual(codesGrants.map((g) => g.capability).sort(), ['ai.credential.manage', 'ai.run.create']);
++ // ── The grants Actor holds for OpenVibe.AI (plan L55 C3) ──
++ // A person chooses the model their tasks run on, with their own key (sealed in AI) or OpenVibe's. Actor runs
++ // the call through AI's OpenAI-compatible endpoint (workflow ai.complete) and nothing else, and it never
++ // holds a key of its own: no consent grant, so it may not store a person's key in AI.
++ assert.ok(has('actor', 'ai.run.create', 'openvibe.ai'), 'Actor runs AI\'s OpenAI-compatible endpoint');
++ assert.ok(!has('actor', 'ai.credential.manage', 'openvibe.ai'), 'Actor never stores a person\'s key in AI');
++ const actorGrant = principals.DEFAULT_GRANTS.find((g) => g[0] === 'actor' && g[1] === 'ai.run.create' && g[2] === 'openvibe.ai');
++ assert.deepStrictEqual(actorGrant[3], ['ai.complete'], 'AI\'s OpenAI-compatible workflow only');
++ const actorGrants = await principals.grantsFor(db, 'actor', 'openvibe.ai');
++ assert.deepStrictEqual(actorGrants.map((g) => g.capability).sort(), ['ai.run.create']);
++
// ── A run token: the person it acts for, and how long it lives ──
let t = await runToken(OWNER);
assert.strictEqual(t.status, 200, JSON.stringify(t.body));