improve reasoning toggle tests and refactor model capability checks

This commit is contained in:
2026-09-21 21:46:22 +02:00
parent 6cb0cbcee0
commit a8e348e0c7
8 changed files with 217 additions and 75 deletions
+76 -4
View File
@@ -4,7 +4,7 @@ import {createServer} from "node:http";
import {createApp} from "../src/app.js";
import {createChatService} from "../src/services/chatService.js";
import {runMigrations} from "../src/db/schema.js";
import {SearXNGSearchProvider, WebSearchError, buildWebSearchContext, buildWebSearchQuery, decideWebSearch, selectSearchResults} from "../src/services/webSearchService.js";
import {SearXNGSearchProvider, WebSearchError, buildWebSearchContext, buildWebSearchQuery, decideWebSearch, selectSearchResults, retrieveWebSearch} from "../src/services/webSearchService.js";
import {createModelService} from "../src/services/modelService.js";
import {formatChatMessage} from "../src/services/chatMessageHelpers.js";
import {appendSources, createWebSearchToggle} from "../public/app/webSearch.js";
@@ -57,7 +57,8 @@ test("SearXNG encodes queries, limits results and treats result data as untruste
});
test("search decisions and source selection use only relevant, renumbered evidence", () => {
assert.deepEqual(decideWebSearch("What date is it today?"), {needsSearch: false, intent: "runtime"});
assert.equal(decideWebSearch("What date is it today?").shouldSearch, false);
assert.equal(decideWebSearch("What date is it today?").intent, "runtime");
assert.equal(decideWebSearch("What is the latest Node.js release?").needsSearch, true);
assert.equal(decideWebSearch("What happened on 10.09.2025?").intent, "news");
const selected = selectSearchResults("What happened on 10.09.2025?", [
@@ -195,11 +196,11 @@ test("chat search integrates with streaming, persistence, personas, settings, re
assert.deepEqual(events.map((event) => event.event), ["status", "status", "status", "chunk", "chunk", "done"]);
assert.equal(events[0].data.status, "accepted");
assert.equal(events[1].data.status, "searching");
assert.deepEqual(events[2].data, {status: "generating", sourceCount: 1});
assert.deepEqual(events[2].data, {status: "generating", searchState: "succeeded", sourceCount: 1});
assert.deepEqual(events.at(-1).data.sources, [source]);
assert.equal(searchCount, 1);
assert.equal(lastSearchQuery, "major news events September 10, 2025 2025-09-10");
assert.equal(lastSearchCategory, "news");
assert.equal(lastSearchCategory, null);
const call = calls.at(-1);
assert.equal(call.model, "chosen:model");
assert.deepEqual(call.options.generation, {temperature: 0.4, contextLength: 4096, responseLength: 300});
@@ -286,6 +287,77 @@ test("chat search integrates with streaming, persistence, personas, settings, re
assert.ok(chatService.listActiveBranchMessages(localChat.id).length >= 4);
});
test("explicit search intent overrides smart skipping and preserves the requested year", () => {
for (const question of [
"Search the internet for the average lifespan in 2026",
"Search the web for what is 2 + 2",
"Look this up online", "Find sources for life expectancy",
"Check online for life expectancy", "Use the internet for this",
"Find the latest information on life expectancy", "Can you search for life expectancy?",
"Google this", "Find current information on life expectancy",
"Search the internet for what date it is today",
"Explain recursion and look it up online"
]) {
const decision = decideWebSearch(question);
assert.equal(decision.explicitSearchIntent, true, question);
assert.equal(decision.shouldSearch, true, question);
assert.equal(decideWebSearch(question, false).shouldSearch, false);
}
for (const question of ["What is 2 + 2?", "What date is it today?", "Rewrite this sentence", "Explain recursion"]) {
const decision = decideWebSearch(question);
assert.equal(decision.explicitSearchIntent, false);
assert.equal(decision.automaticSearchDecision, false);
assert.equal(decision.shouldSearch, false);
}
assert.equal(buildWebSearchQuery("Search the internet for the average lifespan in 2026"), "the average life expectancy in 2026");
});
test("date evidence admits individual events, rejects empty/wrong dates, and bounds fallback searches", async () => {
const question = "what happened on 10.09.2025?";
const useful = [
{title: "Satellite launched", url: "https://space.example/launch", content: "On September 10, 2025, researchers launched a satellite into orbit."},
{title: "Agreement signed", url: "https://diplomacy.example/deal", content: "Officials signed a cooperation agreement on 10 September 2025."}
];
const weak = [
{title: "September 10, 2025 major world news", url: "https://empty.example", content: ""},
{title: "October 9, 2025 world news", url: "https://wrong.example", content: "On October 9, 2025, officials signed an unrelated agreement."},
{title: "On This Day", url: "https://calendar.example", content: "Browse the latest headlines and on this day pages for September 10, 2025."}
];
assert.deepEqual(selectSearchResults(question, weak), []);
const selected = selectSearchResults(question, [...weak, ...useful]);
assert.equal(selected.length, 2);
assert.deepEqual(selected.map(source => source.snippet), useful.map(item => item.content));
const queries = [];
const search = await retrieveWebSearch(question, {async search(query, options) {
queries.push(query);
assert.equal(options.category, undefined);
return queries.length === 1 ? weak : [...useful, useful[0]];
}});
assert.equal(search.state, "succeeded");
assert.equal(queries.length, 2);
assert.ok(queries.every(query => query.includes("2025")));
assert.deepEqual(search.sources, selected);
let duplicateCalls = 0;
const refreshed = await retrieveWebSearch(question, {async search() {
duplicateCalls++;
return duplicateCalls === 1
? [{...useful[0], content: "Browse the latest headlines and on this day pages."}]
: [useful[0]];
}});
assert.equal(refreshed.sources[0].snippet, useful[0].content);
let calls = 0;
await retrieveWebSearch(question, {async search() { calls++; return useful; }});
assert.equal(calls, 1);
for (const [results, state] of [[[], "no_results"], [weak, "no_relevant_results"]]) {
const outcome = await retrieveWebSearch(question, {async search() { return results; }});
assert.equal(outcome.state, state);
assert.equal(outcome.queries.length, 2);
}
const skipped = await retrieveWebSearch("What is 2 + 2?", {search() { assert.fail("Must not search"); }});
assert.equal(skipped.state, "skipped");
assert.equal(skipped.sources, null);
});
class Element {
constructor(tag) { this.tag = tag; this.children = []; this.attributes = {}; this.listeners = {}; }
append(...nodes) { this.children.push(...nodes); }