Overhaul docs retrieval and web search quality
Replace the abandoned llms-txt-mcp/Chroma docs backend with an in-repo MCP service: SQLite WAL + FTS5 + sentence-transformer embeddings, transactional source replacement, persisted state across restarts, singleflight refresh with conditional requests, hybrid lexical/semantic ranking with exact-duplicate collapse, source/host filters, and explicit-by-default content retrieval. Add docs_rebuild and a docs-rebuild CLI command. Add deterministic llms-full.txt snapshot generation for machine-local menus with hash-validated provenance manifests; lifecycle commands promote a local menu to its snapshot only when the manifest validates. Switch public source profiles to content-bearing llms-full.txt feeds. Improve web search: bounded provider fallback with per-attempt diagnostics and cancellation, an optional Brave Search API provider, strict SearXNG engine selection, capped link/media extraction, and a real engine=browser renderer that routes every request through the existing SSRF vetting while blocking WebSockets, non-GET traffic, and private destinations. Extend release checks with offline unit suites and isolated candidate container tests for both images.
This commit is contained in:
99
docker/docs/tests/test_store.py
Normal file
99
docker/docs/tests/test_store.py
Normal file
@@ -0,0 +1,99 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
|
||||
from context_docs.models import PreparedDocument, SourceUpdate
|
||||
from context_docs.store import IndexStore
|
||||
|
||||
|
||||
def document(identifier: str, source: str, title: str, content: str) -> PreparedDocument:
|
||||
return PreparedDocument(
|
||||
id=identifier,
|
||||
configured_source=source,
|
||||
resolved_source=source,
|
||||
source_host="example.test",
|
||||
canonical_url=f"https://docs.example.test/{identifier}",
|
||||
canonical_host="docs.example.test",
|
||||
title=title,
|
||||
description="",
|
||||
heading_path=title,
|
||||
content=content,
|
||||
content_hash=identifier,
|
||||
embedding=np.asarray([1.0, 0.0, 0.0, 0.0], dtype=np.float32),
|
||||
)
|
||||
|
||||
|
||||
def update(source: str, documents: list[PreparedDocument], checked_at: float = 100.0) -> SourceUpdate:
|
||||
return SourceUpdate(
|
||||
configured_source=source,
|
||||
resolved_source=source,
|
||||
etag=None,
|
||||
last_modified=None,
|
||||
body_hash="body-hash",
|
||||
raw_body="# Fixture",
|
||||
parser_fingerprint="parser-v1",
|
||||
embedding_fingerprint="fake-embedder-v1",
|
||||
checked_at=checked_at,
|
||||
indexed_at=checked_at,
|
||||
stale_at=checked_at + 3600,
|
||||
documents=documents,
|
||||
)
|
||||
|
||||
|
||||
class StoreTest(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.tmp = tempfile.TemporaryDirectory()
|
||||
self.path = Path(self.tmp.name) / "docs.sqlite3"
|
||||
self.source = "https://example.test/llms.txt"
|
||||
self.store = IndexStore(self.path)
|
||||
self.store.configure_sources([self.source])
|
||||
|
||||
def tearDown(self) -> None:
|
||||
self.store.close()
|
||||
self.tmp.cleanup()
|
||||
|
||||
def test_replacement_removes_old_only_documents(self) -> None:
|
||||
self.store.replace_source(update(self.source, [document("old", self.source, "Old", "old content")]))
|
||||
self.store.replace_source(update(self.source, [document("new", self.source, "New", "new content")]))
|
||||
|
||||
self.assertIsNone(self.store.get_document("old"))
|
||||
self.assertEqual("new content", self.store.get_document("new").content)
|
||||
|
||||
def test_failed_replacement_rolls_back_to_previous_generation(self) -> None:
|
||||
self.store.replace_source(update(self.source, [document("old", self.source, "Old", "old content")]))
|
||||
self.store.connection.execute(
|
||||
"CREATE TRIGGER reject_failure BEFORE INSERT ON documents "
|
||||
"WHEN NEW.title = 'FAIL' BEGIN SELECT RAISE(ABORT, 'injected failure'); END"
|
||||
)
|
||||
|
||||
with self.assertRaisesRegex(Exception, "injected failure"):
|
||||
self.store.replace_source(update(self.source, [document("bad", self.source, "FAIL", "bad")]))
|
||||
|
||||
self.assertEqual("old content", self.store.get_document("old").content)
|
||||
self.assertIsNone(self.store.get_document("bad"))
|
||||
|
||||
def test_restart_loads_persisted_state_without_network(self) -> None:
|
||||
self.store.replace_source(update(self.source, [document("persisted", self.source, "Persisted", "saved")]))
|
||||
self.store.close()
|
||||
|
||||
self.store = IndexStore(self.path)
|
||||
self.store.configure_sources([self.source])
|
||||
|
||||
states = self.store.list_sources()
|
||||
self.assertEqual(1, states[0].doc_count)
|
||||
self.assertEqual("saved", self.store.get_document("persisted").content)
|
||||
|
||||
def test_removed_source_is_not_searchable_or_retrievable(self) -> None:
|
||||
self.store.replace_source(update(self.source, [document("retired", self.source, "Retired", "identifier")]))
|
||||
self.store.configure_sources([])
|
||||
|
||||
self.assertIsNone(self.store.get_document("retired"))
|
||||
self.assertEqual([], self.store.lexical_search("identifier", limit=5))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user