Add knowledgebases

Knowledgebases give bots RAG over documents, scoped like extensions: to
particular bots, to a workspace, or to every bot, with their own button
beside Tools in the composer. Documents are chunked and embedded by the
file-ingest worker with the install's embedding model, which is now an
install-wide setting rather than a per-bot one.

- Word search runs on an FTS5 index ranked by BM25, with a scored LIKE
  fallback for scripts written without spaces.
- Changing the embedding model re-indexes documents automatically.
- Ingest jobs wait without spending attempts while every embedding model
  rests, fail at once on errors retrying can't fix, and keep passages
  searchable by words when the model refuses.
- Settings > Knowledgebases shows each knowledgebase's and document's
  index state: spinner, warning, grey check (words only) or green check.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-26 16:41:01 +01:00
co-authored by Claude Opus 5.5
parent 2259b6895f
commit e7b23b7454
111 changed files with 4573 additions and 148 deletions
+17 -2
View File
@@ -4,7 +4,9 @@
* the others are helpers it calls on. `automatic` ones pick a free model
* themselves when none is picked; the rest (transcription, speech,
* embeddings) are not available until one is, since free picks there
* rarely work.
* rarely work. `install_wide` ones take only the default setting (Settings
* > Models): no bot or workspace can pick its own, since every vector they
* write must come from the same model to be compared.
*/
export const Harnesses = [
{
@@ -13,6 +15,7 @@ export const Harnesses = [
automatic: true,
description: `Chats, plans and works on computers. Every bot thinks with it.`,
required: true,
install_wide: false,
},
{
title: "Media reading",
@@ -20,6 +23,7 @@ export const Harnesses = [
automatic: true,
description: `Reads images, scans and screenshots for the text model when it can't see them itself.`,
required: false,
install_wide: false,
},
{
title: "Transcription",
@@ -27,6 +31,7 @@ export const Harnesses = [
automatic: false,
description: `Turns voice messages and recordings into text.`,
required: false,
install_wide: false,
},
{
title: "Speech",
@@ -34,6 +39,7 @@ export const Harnesses = [
automatic: false,
description: `Reads bot replies aloud.`,
required: false,
install_wide: false,
},
{
title: "Image generation",
@@ -41,18 +47,27 @@ export const Harnesses = [
automatic: true,
description: `Makes images when a bot is asked for one.`,
required: false,
install_wide: false,
},
{
title: "Embeddings",
value: "embedding",
automatic: false,
description: `Turns the files bots save into vectors, so they can search them. Without one, searching falls back to plain text matching.`,
description: `Turns the files bots save and every knowledgebase into vectors, so bots search them by meaning. One model for the whole install. Without one, searching falls back to plain text matching.`,
required: false,
install_wide: true,
},
] as const;
export type HarnessKind = (typeof Harnesses)[number]["value"];
/**
* Whether a harness takes only the install's default setting.
*/
export function isInstallWideHarness({ harness }: { harness: HarnessKind }) {
return Boolean(Harnesses.find((h) => h.value == harness)?.install_wide);
}
/**
* The `app_settings` key holding the install's own limit on model turns in
* one run (a task). Unset means `AppData.HarnessMaxToolSteps`.