openvibers/openvibe.chat Commits
Gate Vibe web search behind owner switch (#461)
Changes
@@@ -236,6 +236,8 @@ module.exports = {// Vibe, OpenVibe's assistant (server/rooms/vibe.js): on unless CHAT_VIBE=0 (it also needs AI members on); a person// gets this many answers a UTC day across all their Vibe chats.vibe: process.env.CHAT_VIBE !== '0',++ // C3 web search in Vibe's composer (the existing Search Fabric tool and cited results); owner enables it explicitly.++ vibeWebSearch: bool(process.env.CHAT_VIBE_WEB_SEARCH_ENABLED, false),vibeRepliesPerDay: Math.max(1, int(process.env.CHAT_VIBE_REPLIES_PER_DAY, 100)),},bench: {@@@ -115,7 +115,7 @@ const hasMemory = (member, room, memory) => !!memory && ownChat(member, room);const definitions = (member, room = null, { memory = false } = {}) => [...BASE,...(member && member.link_service === 'food' && member.link_resource ? [PANTRY] : []),-- ...(isVibe(member) ? [WEB] : []),++ ...(isVibe(member) && require('../config').ai.vibeWebSearch ? [WEB] : []),...(isWorkspace(room) ? [QUEUE, QUEUE_STATUS] : []),...(hasMemory(member, room, memory) ? [REMEMBER, FORGET] : []),...(ownChat(member, room) ? [SET_UP_AGENT, MY_AGENTS, SET_REMINDER, MY_REMINDERS, STOP_REMINDER, MY_DAY, START_TIMER, STOP_TIMER] : []),@@@ -444,7 +444,7 @@ const RUNNERS = { search_openvibe: searchOpenvibe, read_page: readPage, web_sear*/async function runWithResult(name, raw, ctx = {}) {const runner = RUNNERS[name];-- if (!runner || (name === 'household_pantry' && !(ctx.member && ctx.member.link_service === 'food')) || (name === 'web_search' && !isVibe(ctx.member))++ if (!runner || (name === 'household_pantry' && !(ctx.member && ctx.member.link_service === 'food')) || (name === 'web_search' && !(isVibe(ctx.member) && require('../config').ai.vibeWebSearch))|| ((name === 'queue_task' || name === 'queue_status') && !isWorkspace(ctx.room))|| ((name === 'remember' || name === 'forget') && !(hasMemory(ctx.member, ctx.room, ctx.memory) && ctx.user))|| (['set_up_agent', 'my_agents', 'set_reminder', 'my_reminders', 'stop_reminder', 'my_day', 'start_timer', 'stop_timer'].includes(name) && !(ownChat(ctx.member, ctx.room) && ctx.user))) {@@@ -25,6 +25,7 @@ t('boot', async () => {aiClient = require('../server/ai/client');aiTools = require('../server/rooms/ai-tools');config = require('../server/config');++ config.ai.vibeWebSearch = true; // enable the owner-gated Search Fabric tool for these fixturesann = h.addUser('ann', { subject: ids.newId('user') });await h.ctx.upsertUser(h.live.users.get(ann.id));aiClient._setClient({@@@ -45,6 +46,11 @@ t('boot', async () => {t('the tool list and the runners: bounded, fair, refusing what is not theirs', async () => {const names = aiTools.definitions({}).map((d) => d.function.name);assert.deepStrictEqual(names, ['search_openvibe', 'read_page', 'current_time', 'roll_dice', 'pick_one'], 'no pantry for an unlinked member');++ config.ai.vibeWebSearch = false;++ assert.ok(!aiTools.definitions({ handle: 'vibe' }).some((d) => d.function.name === 'web_search'), 'the web tool is absent while its owner switch is off');++ const refused = await aiTools.runWithResult('web_search', '{"query":"latest"}', { member: { handle: 'vibe' } });++ assert.match(refused.text, /no tool called web_search/, 'the runner also enforces the owner switch');++ config.ai.vibeWebSearch = true;// Both search tools take the Search Fabric's freshness window (10-30): "today" and "recently" reach Search.const schemaOf = (name) => aiTools.definitions({ handle: 'vibe' }).find((d) => d.function.name === name).function.parameters;for (const name of ['search_openvibe', 'web_search']) {
@@ -236,6 +236,8 @@ module.exports = {
@@ -236,6 +236,8 @@ module.exports = {
// Vibe, OpenVibe's assistant (server/rooms/vibe.js): on unless CHAT_VIBE=0 (it also needs AI members on); a person
// Vibe, OpenVibe's assistant (server/rooms/vibe.js): on unless CHAT_VIBE=0 (it also needs AI members on); a person
// gets this many answers a UTC day across all their Vibe chats.
// gets this many answers a UTC day across all their Vibe chats.
vibe: process.env.CHAT_VIBE !== '0',
vibe: process.env.CHAT_VIBE !== '0',
+ // C3 web search in Vibe's composer (the existing Search Fabric tool and cited results); owner enables it explicitly.
+ vibeWebSearch: bool(process.env.CHAT_VIBE_WEB_SEARCH_ENABLED, false),
vibeRepliesPerDay: Math.max(1, int(process.env.CHAT_VIBE_REPLIES_PER_DAY, 100)),
vibeRepliesPerDay: Math.max(1, int(process.env.CHAT_VIBE_REPLIES_PER_DAY, 100)),
},
},
bench: {
bench: {
@@ -115,7 +115,7 @@ const hasMemory = (member, room, memory) => !!memory && ownChat(member, room);
@@ -115,7 +115,7 @@ const hasMemory = (member, room, memory) => !!memory && ownChat(member, room);
const definitions = (member, room = null, { memory = false } = {}) => [
const definitions = (member, room = null, { memory = false } = {}) => [
...BASE,
...BASE,
...(member && member.link_service === 'food' && member.link_resource ? [PANTRY] : []),
...(member && member.link_service === 'food' && member.link_resource ? [PANTRY] : []),
- ...(isVibe(member) ? [WEB] : []),
+ ...(isVibe(member) && require('../config').ai.vibeWebSearch ? [WEB] : []),
...(isWorkspace(room) ? [QUEUE, QUEUE_STATUS] : []),
...(isWorkspace(room) ? [QUEUE, QUEUE_STATUS] : []),
...(hasMemory(member, room, memory) ? [REMEMBER, FORGET] : []),
...(hasMemory(member, room, memory) ? [REMEMBER, FORGET] : []),
...(ownChat(member, room) ? [SET_UP_AGENT, MY_AGENTS, SET_REMINDER, MY_REMINDERS, STOP_REMINDER, MY_DAY, START_TIMER, STOP_TIMER] : []),
...(ownChat(member, room) ? [SET_UP_AGENT, MY_AGENTS, SET_REMINDER, MY_REMINDERS, STOP_REMINDER, MY_DAY, START_TIMER, STOP_TIMER] : []),
@@ -444,7 +444,7 @@ const RUNNERS = { search_openvibe: searchOpenvibe, read_page: readPage, web_sear
@@ -444,7 +444,7 @@ const RUNNERS = { search_openvibe: searchOpenvibe, read_page: readPage, web_sear
*/
*/
async function runWithResult(name, raw, ctx = {}) {
async function runWithResult(name, raw, ctx = {}) {
const runner = RUNNERS[name];
const runner = RUNNERS[name];
- if (!runner || (name === 'household_pantry' && !(ctx.member && ctx.member.link_service === 'food')) || (name === 'web_search' && !isVibe(ctx.member))
+ if (!runner || (name === 'household_pantry' && !(ctx.member && ctx.member.link_service === 'food')) || (name === 'web_search' && !(isVibe(ctx.member) && require('../config').ai.vibeWebSearch))
|| ((name === 'queue_task' || name === 'queue_status') && !isWorkspace(ctx.room))
|| ((name === 'queue_task' || name === 'queue_status') && !isWorkspace(ctx.room))
|| ((name === 'remember' || name === 'forget') && !(hasMemory(ctx.member, ctx.room, ctx.memory) && ctx.user))
|| ((name === 'remember' || name === 'forget') && !(hasMemory(ctx.member, ctx.room, ctx.memory) && ctx.user))
|| (['set_up_agent', 'my_agents', 'set_reminder', 'my_reminders', 'stop_reminder', 'my_day', 'start_timer', 'stop_timer'].includes(name) && !(ownChat(ctx.member, ctx.room) && ctx.user))) {
|| (['set_up_agent', 'my_agents', 'set_reminder', 'my_reminders', 'stop_reminder', 'my_day', 'start_timer', 'stop_timer'].includes(name) && !(ownChat(ctx.member, ctx.room) && ctx.user))) {
@@ -25,6 +25,7 @@ t('boot', async () => {
@@ -25,6 +25,7 @@ t('boot', async () => {
aiClient = require('../server/ai/client');
aiClient = require('../server/ai/client');
aiTools = require('../server/rooms/ai-tools');
aiTools = require('../server/rooms/ai-tools');
config = require('../server/config');
config = require('../server/config');
+ config.ai.vibeWebSearch = true; // enable the owner-gated Search Fabric tool for these fixtures
ann = h.addUser('ann', { subject: ids.newId('user') });
ann = h.addUser('ann', { subject: ids.newId('user') });
await h.ctx.upsertUser(h.live.users.get(ann.id));
await h.ctx.upsertUser(h.live.users.get(ann.id));
aiClient._setClient({
aiClient._setClient({
@@ -45,6 +46,11 @@ t('boot', async () => {
@@ -45,6 +46,11 @@ t('boot', async () => {
t('the tool list and the runners: bounded, fair, refusing what is not theirs', async () => {
t('the tool list and the runners: bounded, fair, refusing what is not theirs', async () => {
const names = aiTools.definitions({}).map((d) => d.function.name);
const names = aiTools.definitions({}).map((d) => d.function.name);
assert.deepStrictEqual(names, ['search_openvibe', 'read_page', 'current_time', 'roll_dice', 'pick_one'], 'no pantry for an unlinked member');
assert.deepStrictEqual(names, ['search_openvibe', 'read_page', 'current_time', 'roll_dice', 'pick_one'], 'no pantry for an unlinked member');
+ config.ai.vibeWebSearch = false;
+ assert.ok(!aiTools.definitions({ handle: 'vibe' }).some((d) => d.function.name === 'web_search'), 'the web tool is absent while its owner switch is off');
+ const refused = await aiTools.runWithResult('web_search', '{"query":"latest"}', { member: { handle: 'vibe' } });
+ assert.match(refused.text, /no tool called web_search/, 'the runner also enforces the owner switch');
+ config.ai.vibeWebSearch = true;
// Both search tools take the Search Fabric's freshness window (10-30): "today" and "recently" reach Search.
// Both search tools take the Search Fabric's freshness window (10-30): "today" and "recently" reach Search.
const schemaOf = (name) => aiTools.definitions({ handle: 'vibe' }).find((d) => d.function.name === name).function.parameters;
const schemaOf = (name) => aiTools.definitions({ handle: 'vibe' }).find((d) => d.function.name === name).function.parameters;
for (const name of ['search_openvibe', 'web_search']) {
for (const name of ['search_openvibe', 'web_search']) {