openvibers/openvibe.chat Commits
Vibe: when OpenVibe has nothing, say so and go to the web (#257)
Changes
@@@ -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'; }@@@ -106,6 +106,11 @@ t('the tool list and the runners: bounded, fair, refusing what is not theirs', aassert.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 () => {
@@ -99,7 +99,7 @@ async function searchOpenvibe(args, { fetchImpl = null, searchClient = null } =
@@ -99,7 +99,7 @@ async function searchOpenvibe(args, { fetchImpl = null, searchClient = null } =
try {
try {
const body = await fabricSearch.query({ q: query, scopes: ['network'], limit, signal }, { client: searchClient, fetchImpl });
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));
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) {
} catch (err) {
if (!fabricSearch.isDown(err)) {
if (!fabricSearch.isDown(err)) {
const status = err && err.status;
const status = err && err.status;
@@ -113,7 +113,7 @@ async function searchOpenvibe(args, { fetchImpl = null, searchClient = null } =
@@ -113,7 +113,7 @@ async function searchOpenvibe(args, { fetchImpl = null, searchClient = null } =
if (!res.ok) return { error: `OpenVibe.Search answered ${res.status}` };
if (!res.ok) return { error: `OpenVibe.Search answered ${res.status}` };
const old = await res.json();
const old = await res.json();
const results = (Array.isArray(old.results) ? old.results : []).slice(0, limit).map((r) => fabricSearch.resultOf(r, { fallback: true }));
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 {
} catch {
return { error: 'OpenVibe.Search is unreachable' };
return { error: 'OpenVibe.Search is unreachable' };
}
}
@@ -142,6 +142,11 @@ async function readPage(args, { fetchImpl = globalThis.fetch } = {}) {
@@ -142,6 +142,11 @@ async function readPage(args, { fetchImpl = globalThis.fetch } = {}) {
} finally { clearTimeout(timer); }
} 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() } = {}) {
function currentTime(args, { now = Date.now() } = {}) {
let tz = String(args.timezone || 'UTC').trim() || 'UTC';
let tz = String(args.timezone || 'UTC').trim() || 'UTC';
try { new Intl.DateTimeFormat('en-US', { timeZone: tz }); } catch { tz = 'UTC'; }
try { new Intl.DateTimeFormat('en-US', { timeZone: tz }); } catch { tz = 'UTC'; }
@@ -106,6 +106,11 @@ t('the tool list and the runners: bounded, fair, refusing what is not theirs', a
@@ -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.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.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]);
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 () => {
t('the fabric gets Chat\'s service token and the person as X-OV-Subject, and its provider and dates survive', async () => {