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
+4 -2
View File
@@ -105,7 +105,9 @@ Pure chat message, retry, memory, summary, and persona-state formatting lives in
The globe button next to the composer enables search for one message. It defaults to off and resets once the server accepts the message. The browser sends `webSearch: true` (or `false`) through the existing authenticated `POST /chat/stream`; `POST /chat` supports the same optional boolean. Omitting it preserves the normal chat flow. No generic tool/agent system is needed.
The existing chat service saves the user message, performs one server-side SearXNG search using the current message (whitespace normalized, at most 500 characters), then supplies up to five numbered results as a separate context alongside the existing history, model, personas and generation settings. Instructions require numbered citations, acknowledgement of insufficient results and treating all result content as untrusted data. This is a basic prompt-injection precaution, not a guarantee that a model will follow instructions. No result pages are fetched.
Search uses `shouldSearch = webSearchEnabled && (explicitSearchIntent || automaticSearchDecision)`. Deterministic commands such as “search the internet”, “look this up online”, and “find sources” override smart skipping. Arithmetic, rewriting, explanations and questions answered by the server clock can otherwise skip search. Query cleanup removes command wording while preserving dates and years; “average lifespan” is normalized to “average life expectancy”.
The chat service retrieves up to 15 candidates per query and selects 1–5 useful sources dynamically, rejecting empty evidence and wrong-date results. SearXNG `content` maps to `snippet` (an existing non-empty snippet is also accepted). Exact-date event searches use the general index to include archives and try at most one alternate query if the first returns no usable evidence; candidates are merged and deduplicated. Current-news requests retain the news category. The same numbered selected titles, URLs and snippets go immediately before the current question in Ollama's context, into the response, and into storage. Retrieval finishes before either reasoning or answer streaming starts. Instructions require citations, supported partial answers rather than blanket claims of missing details, and honest disclosure when evidence is insufficient. Result content remains untrusted data; no result pages are fetched.
Set environment variables in the server/container environment, or load the example explicitly with `node --env-file=.env server.js` (the existing npm start command does not load `.env` automatically):
@@ -129,7 +131,7 @@ The provider calls `GET {SEARXNG_URL}/search?q=...&format=json` using encoded qu
The server-side `WebSearchProvider` JSDoc contract is `search(query, {signal, limit}?) -> Promise<SearchResult[]>`; `SearXNGSearchProvider` is the initial implementation. Results have `{title, url, snippet}`. This repository uses JavaScript ES modules, so types are documented with JSDoc rather than introducing a TypeScript build.
Search adds SSE `status` events: `{status: "searching"}` after message acceptance and `{status: "generating", sourceCount}` before model generation. Existing `chunk` events are unchanged. `done` adds optional `sources` to `{reply}`; the non-streaming response does likewise. Empty results produce `sources: []` and an explicit insufficient-results instruction. Failures produce the existing error event (HTTP 503 on `/chat`); no model answer is generated after search failure. The saved failed user message remains visible, and the composer restores its text for resending without search.
Search emits `{status: "searching"}` only when retrieval actually runs, then `{status: "generating", searchState, sourceCount?}` before model generation. The UI initially says “Preparing response…” so skipped searches never claim to search. `searchState` distinguishes `skipped`, `no_results`, `no_relevant_results`, `succeeded`, and `failed`. Successful `done` events and non-streaming responses carry this state and optional `sources`; search failures carry `searchState: "failed"` in the error response. No usable evidence produces `sources: []` and instructions to say that a search found insufficient relevant evidence, without fake citations. Initial retrieval failures use the existing error event (HTTP 503 on `/chat`) and do not generate an answer; an optional fallback failure preserves the successful initial search. The saved failed user message remains visible, and the composer restores its text for resending without search.
Sources appear below assistant messages as numbered links. They persist in a nullable SQLite `chat_messages.sources` JSON column, added automatically on startup using the existing additive schema mechanism; no manual migration is needed. Old messages remain valid. Sources are preserved in JSON exports/imports and branches. Regenerating an answer reuses its stored results without performing another search; a fresh search requires sending another message with the toggle enabled.