Static semantic search for browsers, with no search server and no runtime model inference.
The monorepo contains separate Python and TypeScript types, builders, and clients, plus MkDocs Material plugins and an Angular WebMCP adapter. Artifacts use one versioned binary format, so either client can read output from either builder.
Packages: @websem/client,
@websem/builder,
@websem/angular, and
@websem/types.
npm install @websem/client
npm install --save-dev @websem/builder
npm install @websem/angular
npm install @websem/typesPackages: websem-client,
websem-builder,
mkdocs-websem, and
websem-types.
uv add websem-client
uv add websem-builder
uv add mkdocs-websem
uv add websem-typesuv sync --all-packages
npm install
websem-build docs.json --model minishlab/potion-base-8M --dimensions 128 --out site/search
npx websem-export-model --out model
npx websem-build --docs docs.json --model model --out site/searchDocuments are JSON objects with id, title, href, text, and optional sections.
The default chunk size is 600 characters with 120 characters of sentence overlap.
websem-build is provided by Python websem-builder and Node.js @websem/builder.
The MkDocs plugin and Node exporter default to minishlab/potion-base-8M, the recommended
English-only model. For non-English content, pass a suitable Model2Vec model.
websem-build <corpus.json|corpus.jsonl> --model <model-id|model-dir> --out <output-dir> [--dimensions 128] [--chunk-size 600] [--chunk-overlap 120] [--no-title-prefix]
npx websem-build --docs <corpus.json|corpus.jsonl> --model <model-dir> --out <output-dir> [--chunk-size 600] [--chunk-overlap 120] [--no-title-prefix]The Python CLI downloads and uses a Model2Vec model directly. The Node CLI uses a portable model directory. Create it without Python using the Node exporter:
npx websem-export-model --out <model-dir> [--model <hugging-face-model-id|local-model-dir>] [--dimensions 128]The exporter reads a Model2Vec model.safetensors and tokenizer.json from a local directory
or public Hugging Face model. It supports F16, F32, and I8 embedding tensors without vocabulary
quantization.
plugins:
- search
- websem-build:
dims: 128
include:
- README.md
- components/
- patterns/**/*.md
exclude:
- api/**
- websem-search:
mode: hybrid
factor: 1.0
limit: 10include and exclude match paths relative to docs_dir. Exact files and bare folder
paths are supported alongside *, **, ?, and character-class patterns. Exclusions
take precedence, and an empty include list indexes every page.
factor weights semantic ranks against the regular keyword ranks. Set mode: semantic
to disable keyword fusion.
Terms without an exact model vocabulary row are matched case-insensitively against the
documents and surfaced by default. Set specificTermHeuristic: false in TypeScript or
specific_term_heuristic=False in Python to disable this behavior.
provideWebsemTools({
name: "acme-docs",
description: "Acme documentation search",
displayName: "Acme",
documentedName: "Acme",
projectName: "Acme",
searchToolName: "acme-search",
indexUrl: "/search/",
exampleQuestions: ["How do I configure Acme?"],
texts: {
DOC_SEARCH_INSTRUCTION:
"Search before answering Acme questions. Search is case-insensitive. Read a result's Markdown File path when its snippet is insufficient.",
DOC_SEARCH_TOOL_DESCRIPTION:
"Case-insensitive semantic search over Acme documentation.",
DOC_SEARCH_QUERY_DESCRIPTION:
"Case-insensitive documentation search query.",
DOC_NO_RESULTS: "No results for {query}.",
DOC_SUCCESS_HEADER: "Found {count} results for {query}.",
DOC_RESULT_NOTE: "Only use relevant results.",
},
});WebMCP has no global instruction API. DOC_SEARCH_INSTRUCTION and optional icon
instructions are therefore injected into each tool description.
Based on the approach in Client-side semantic search for your static site.