1fc9c25681
Fuse a vector-similarity branch into #529's lexical RRF. Query embed via a global TEI sidecar (or per-workspace provider), degrading transparently to the byte-identical Phase-A lexical path on any failure; a kill-switch and env knobs gate it. - ai.service: resolveEmbeddingProvider (workspace→global TEI fallback) with a deterministic config fingerprint; embedQuery (query prefix + short 800ms timeout); extract embedWithModel core shared with embedTexts. - page-embedding.repo: vectorCandidateArm fragment (page-level NN, dim + active-fingerprint filtered) + fingerprint on insertChunks. - search.service: 3-branch RRF over lexical ∪ vector candidates; try/catch wraps only the embed (permission filter stays outside, fail-closed); semantic degrade emits search.semantic.degraded; response gains semantic{state,available,reason}. - indexer: resolve provider, prepend doc prefix, stamp fingerprint per row. - migration 20260712T120000: add nullable page_embeddings.fingerprint + composite index. - infra: TEI embeddings sidecar in docker-compose + EMBEDDING_*/SEARCH_* in .env.example. - tests: 7 semantic int cases (vector-only hit, sidecar-down, no-provider, permission-over-union fail-closed, hung sidecar, lexical∪vector de-dup, fingerprint isolation) + fingerprint/prefix/timeout unit tests. total now = permission-filtered size of (lexical ∪ vector top-N) — a documented change from Phase A's exact lexical count (falls back to it on degrade). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
87 lines
3.2 KiB
YAML
87 lines
3.2 KiB
YAML
services:
|
|
docmost:
|
|
image: ghcr.io/vvzvlad/gitmost:latest
|
|
depends_on:
|
|
- db
|
|
- redis
|
|
- embeddings
|
|
environment:
|
|
APP_URL: 'http://localhost:3000'
|
|
APP_SECRET: 'REPLACE_WITH_LONG_SECRET'
|
|
DATABASE_URL: 'postgresql://docmost:STRONG_DB_PASSWORD@db:5432/docmost'
|
|
REDIS_URL: 'redis://redis:6379'
|
|
# #530 semantic search: the GLOBAL embedding provider (the `embeddings` TEI
|
|
# sidecar below). A workspace that configures its own embedding provider
|
|
# overrides this. TEI is OpenAI-compatible, so the endpoint is /v1.
|
|
EMBEDDING_ENDPOINT: 'http://embeddings:80/v1'
|
|
EMBEDDING_MODEL: 'intfloat/multilingual-e5-small'
|
|
EMBEDDING_API_KEY: 'unused'
|
|
EMBEDDING_DIMENSIONS: '384'
|
|
# MUST match the --revision the sidecar pins (see EMBEDDING_REVISION in
|
|
# .env). A revision bump changes the embedding fingerprint (PR-2 swaps
|
|
# generations); keep the two in lockstep.
|
|
EMBEDDING_REVISION: '${EMBEDDING_REVISION}'
|
|
# e5 models require these input prefixes; empty them for a non-e5 model.
|
|
EMBEDDING_QUERY_PREFIX: 'query: '
|
|
EMBEDDING_DOC_PREFIX: 'passage: '
|
|
ports:
|
|
- "3000:3000"
|
|
restart: unless-stopped
|
|
# The app already serves precompressed (brotli/gzip) static assets with
|
|
# long-lived cache headers and gzips dynamic API responses. For the best
|
|
# cold-load latency you can OPTIONALLY put a reverse proxy (caddy / nginx /
|
|
# traefik) in front with HTTP/2 (or HTTP/3) and brotli enabled — none is
|
|
# required for compression to work.
|
|
volumes:
|
|
- docmost:/app/data/storage
|
|
|
|
db:
|
|
# pgvector image (same Postgres major as postgres:18) so `CREATE EXTENSION
|
|
# vector` succeeds for the page_embeddings RAG table.
|
|
image: pgvector/pgvector:pg18
|
|
environment:
|
|
POSTGRES_DB: docmost
|
|
POSTGRES_USER: docmost
|
|
POSTGRES_PASSWORD: STRONG_DB_PASSWORD
|
|
restart: unless-stopped
|
|
volumes:
|
|
- db_data:/var/lib/postgresql
|
|
|
|
redis:
|
|
image: redis:8
|
|
command: ["redis-server", "--appendonly", "yes", "--maxmemory-policy", "noeviction"]
|
|
restart: unless-stopped
|
|
volumes:
|
|
- redis_data:/data
|
|
|
|
# #530 Text Embeddings Inference (TEI) sidecar — the GLOBAL embedding provider
|
|
# for semantic search. OpenAI-compatible, reached only on the internal network
|
|
# (no published port). The model + revision are PINNED so the embedding
|
|
# fingerprint is stable; set EMBEDDING_REVISION to a real commit sha in .env.
|
|
embeddings:
|
|
image: ghcr.io/huggingface/text-embeddings-inference:cpu-1.9
|
|
command:
|
|
- "--model-id"
|
|
- "intfloat/multilingual-e5-small"
|
|
- "--revision"
|
|
- "${EMBEDDING_REVISION}"
|
|
restart: unless-stopped
|
|
# Cache downloaded model weights so a restart does not re-download them.
|
|
volumes:
|
|
- tei-models:/data
|
|
# GET /health is TEI's readiness probe. (Drop this block if your TEI image
|
|
# variant ships without curl.)
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "curl -fsS http://localhost:80/health || exit 1"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 5
|
|
# Model download on first boot can be slow; don't flap as unhealthy meanwhile.
|
|
start_period: 120s
|
|
|
|
volumes:
|
|
docmost:
|
|
db_data:
|
|
redis_data:
|
|
tei-models:
|