openvibers/openvibe.chat Commits

Vibe: when OpenVibe has nothing, say so and go to the web (#257)

OP OpenVibers committed 15 days ago · 1b82b94bec · 2 files, +12 −2

Vibe listed five unrelated audio tools for a keyboard deal. When
search_openvibe finds nothing, its note now tells the model to say so in a
sentence, never to list unrelated results, and to run web_search for the
same request.

Changes

ddiff --git a/server/rooms/ai-tools.js b/server/rooms/ai-tools.js
iindex 894d6ad..8fa8a28 100644
---- a/server/rooms/ai-tools.js
++++ b/server/rooms/ai-tools.js
@@@ -99,7 +99,7 @@ async function searchOpenvibe(args, { fetchImpl = null, searchClient = null } =
try {
const body = await fabricSearch.query({ q: query, scopes: ['network'], limit, signal }, { client: searchClient, fetchImpl });
const results = (Array.isArray(body.results) ? body.results : []).slice(0, limit).map((r) => fabricSearch.resultOf(r));
-- return { query, citations: fabricSearch.citationsOf(results), results, note: results.length ? 'Cite each result with its url, provider and date.' : 'Nothing on OpenVibe matched.' };
++ return { query, citations: fabricSearch.citationsOf(results), results, note: results.length ? 'Cite each result with its url, provider and date.' : NOTHING };
} catch (err) {
if (!fabricSearch.isDown(err)) {
const status = err && err.status;
@@@ -113,7 +113,7 @@ async function searchOpenvibe(args, { fetchImpl = null, searchClient = null } =
if (!res.ok) return { error: `OpenVibe.Search answered ${res.status}` };
const old = await res.json();
const results = (Array.isArray(old.results) ? old.results : []).slice(0, limit).map((r) => fabricSearch.resultOf(r, { fallback: true }));
-- return { query, citations: fabricSearch.citationsOf(results), results, fallback: true, note: results.length ? 'Cite each result with its url and date.' : 'Nothing on OpenVibe matched.' };
++ return { query, citations: fabricSearch.citationsOf(results), results, fallback: true, note: results.length ? 'Cite each result with its url and date.' : NOTHING };
} catch {
return { error: 'OpenVibe.Search is unreachable' };
}
@@@ -142,6 +142,11 @@ async function readPage(args, { fetchImpl = globalThis.fetch } = {}) {
} finally { clearTimeout(timer); }
}
++// 10-30: Vibe answered "Find me a good deal on a mechanical keyboard" with five audio tools Search had returned (its
++// nearest vectors, unrelated). Search now answers nothing for an unrelated query; this tells the model what to do then.
++const NOTHING = 'Nothing on OpenVibe matched this. Say so in one plain sentence, never list unrelated results as if they were answers, '
++ + 'and then use web_search for the same request (add "deal" or "price" for a shopping request) and answer from that.';
++
function currentTime(args, { now = Date.now() } = {}) {
let tz = String(args.timezone || 'UTC').trim() || 'UTC';
try { new Intl.DateTimeFormat('en-US', { timeZone: tz }); } catch { tz = 'UTC'; }
ddiff --git a/test/room-ai-tools.test.js b/test/room-ai-tools.test.js
iindex a5791b1..8439f78 100644
---- a/test/room-ai-tools.test.js
++++ b/test/room-ai-tools.test.js
@@@ -106,6 +106,11 @@ t('the tool list and the runners: bounded, fair, refusing what is not theirs', a
assert.ok(seenOld[1].startsWith(`${config.ai.searchUrl}/api/v1/search?`));
assert.deepStrictEqual([foundOld.results[0].summary, foundOld.results[0].url, foundOld.results[0].published_at], ['An air mattress', 'https://openvibe.deals/d/x', '2026-10-26T00:00:00Z']);
assert.deepStrictEqual([foundOld.citations[0].url, foundOld.citations[0].published_at, foundOld.fallback], ['https://openvibe.deals/d/x', '2026-10-26T00:00:00Z', true]);
++ // Nothing matched: the note tells the model to say so and go to the web, never to list strangers (10-30).
++ const none = await aiTools._runners.search_openvibe({ query: 'mechanical keyboard' }, { fetchImpl: async () => new Response(JSON.stringify({ results: [] }), { status: 200, headers: { 'content-type': 'application/json' } }) });
++ assert.deepStrictEqual(none.results, []);
++ assert.match(none.note, /never list unrelated results/);
++ assert.match(none.note, /web_search/);
});
t('the fabric gets Chat\'s service token and the person as X-OV-Subject, and its provider and dates survive', async () => {