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
@@ -1,6 +1,7 @@
import IconTile from "@/src/components/general/icon-tile";
import extensionIcon from "@/src/components/general/extension-icon";
import type { BotExtension } from "@/src/types";
import extensionStateNote from "@/src/utils/extension-state-note";
import SettingsListRow from "./settings-list-row";
import SettingsRowAction from "./settings-row-action";
@@ -17,23 +18,15 @@ const ExtensionHrefs = {
mcp: "/admin/settings/mcps",
tool: "/admin/settings/tools",
skill: "/admin/settings/skills",
knowledgebase: "/admin/settings/knowledgebases",
} as const;
/**
* One MCP server, tool or skill a bot or workspace has, and whether it's
* working. A skill has nothing to connect or run, so it never reports a
* failure.
* One MCP server, tool, skill or knowledgebase a bot or workspace has,
* and whether it's working (`extensionStateNote`).
*/
export default function ExtensionRow({ item, busy, onRemove }: Props) {
const state = !item.enabled
? "Off for every bot"
: item.kind == "skill"
? ""
: item.state == "error"
? "Not working"
: item.state == "untested"
? "Not checked yet"
: "";
const state = extensionStateNote({ item });
return (
<SettingsListRow