D-26 requires `make test` to pass with no network; T-55's audit measured 426
passed/29 skipped with network vs. 412 passed/23 skipped/20 ERRORS inside an
unprivileged network namespace. All 20 errors trace to a real Bolt connection
opened at fixture setup (neo4j_client -> client.connect()), not to test logic.
The ticket's own summary said all 20 were in test_entity_linking.py; tracing
the actual error list showed only 5 were (TestEntityLinkingIntegration,
TestEntityLinkingMultiTenancy, plus the trailing module-level cleanup test).
The other 15 are every test in test_hybrid_rag.py, whose hybrid_rag_service
fixture resolves graph_service -> neo4j_client regardless of what the test
body itself exercises -- including the RRF-fusion and context-formatting
classes that read as pure logic. There is no unit/integration split inside
that file without restructuring its fixture graph, which is out of scope
here; the whole module is marked instead of picking classes apart from
underneath a shared fixture chain.
The fix is the mechanism this repo already had and had never wired to a
target: tests/conftest.py's `integration` pytest marker plus its
RUN_INTEGRATION_TESTS/TEST_TENANT gate (test_integration.py,
test_tenant_isolation_live.py, test_quality_report_live.py and
TestWikiChangeListenerIntegration already used it). Applying the same marker
here means `make test` skips these 20 the same way it already skipped the
other 23 -- no file move, no new fixture layer, matching repo precedent
exactly rather than inventing a second convention beside it.
`make test-integration` is the D-26 home: sets RUN_INTEGRATION_TESTS=1,
selects `-m integration`, and treats pytest's own "no tests collected" exit
code (5) as a hard failure rather than a pass, so a marker that gets renamed
or lost fails loudly instead of the target quietly collecting zero and going
green.
Verified (unshare -rn sh -c 'ip link set lo up; ...' after confirming the
positive control -- a live :8089 returning HTTP 200 outside returns curl exit
7 inside):
make test, no network: 412 passed, 43 skipped, exit 0 (was 20 ERRORS)
make test, with network: 412 passed, 43 skipped, exit 0 (unchanged; the 14
of these 20 that were previously counted in the 426 passed now skip by
default -- reclassified, not lost; the other 6 already skipped for an
unrelated reason before this change)
make test-integration, these 20, with network: 14 passed, 6 skipped
(test_wiki_page's own pytest.skip when it can't create a wiki page -- a
pre-existing soft-skip, unrelated to this change), 0 failed, exit 0
make test-integration mutated to select a nonexistent marker: FAIL,
"selected 0 tests", exit 2 -- confirmed loud, then reverted
Not fixed here: the other 23 tests already carrying `integration` include
three files (test_integration.py, test_tenant_isolation_live.py,
test_quality_report_live.py) that fail under `make test-integration` today
because they call the local dev server on :8778, which was not running in
this session -- a pre-existing "never proven runnable" gap this same ticket
family exists to find, but a different set of tests than the one measured
here.
Co-Authored-By: Claude <noreply@anthropic.com>
Library Desk - API Coordination Service
FastAPI service that coordinates all Library operations
Overview
Library Desk is the central coordination layer for The Library system, providing a unified API for:
- HybridRAG Queries - Combines Neo4j (structure) + Qdrant (semantics) + SearXNG (web)
- Document Ingestion - Parse, chunk, embed, and index documents
- Entity Extraction - NLP to identify classes, functions, concepts
- Relationship Mapping - Link entities in Neo4j knowledge graph
- Mind Map Generation - Query Neo4j graph → Render D3.js visualizations
- Wiki.js Proxy - CRUD operations for dossiers
- Deduplication - Vector similarity + graph analysis
Architecture
Library Desk API (FastAPI)
├── Neo4j (knowledge graph)
├── Qdrant (vector search)
├── Wiki.js (wiki operations)
├── SearXNG (web search)
├── Ollama (embeddings)
└── Redis (caching)
Requirements
- Python: 3.12+
- Dependencies: See
requirements.txt
Configuration
Environment variables (set in Portainer stack):
# Required
LIBRARY_API_KEY=<generate-with-openssl-rand-hex-32>
NEO4J_PASSWORD=<neo4j-password>
WIKIJS_API_KEY=<from-wiki-admin-panel>
# Optional (defaults provided)
NEO4J_URI=bolt://neo4j:7687
NEO4J_USER=neo4j
QDRANT_HOST=qdrant
QDRANT_PORT=6333
WIKIJS_URL=http://wiki:3000
SEARXNG_URL=http://searxng:8080
OLLAMA_URL=http://ollama:11434
OLLAMA_MODEL=mistral-nemo-large:latest
OLLAMA_EMBEDDING_MODEL=nomic-embed-text
REDIS_HOST=redis-shared
REDIS_PORT=6379
REDIS_DB=2
API Endpoints
System
GET /- Root endpointGET /health- Health check (public)GET /stats- System statistics (authenticated)
Query (All require API key)
POST /query/hybrid- HybridRAG (graph + vector + web)POST /query/semantic- Vector search onlyPOST /query/graph- Graph traversal onlyGET /query/related/{id}- Find related content
Content Management (Future)
POST /ingest/document- Index new documentPOST /ingest/wiki-page- Sync Wiki.js pagePOST /wiki/dossier- Create dossier (proxies to Wiki.js)PUT /wiki/dossier/{id}- Update dossierDELETE /wiki/dossier/{id}- Delete dossier
Graph Operations (Future)
GET /graph/entities- List entitiesGET /graph/mindmap/{id}- Generate mind mapPOST /graph/query- Execute Cypher query
Deduplication (Future)
POST /deduplicate/find- Find duplicatesPOST /deduplicate/merge- Merge duplicates
Development
Local Setup
# Create virtual environment
python3 -m venv venv
source venv/bin/activate
# Install dependencies
pip install -r requirements.txt
# Run development server
uvicorn src.main:app --reload --host 0.0.0.0 --port 8089
Project Structure
Following FastAPI Best Practices:
library-desk/
├── src/
│ ├── __init__.py # Package initialization
│ ├── main.py # FastAPI application
│ ├── config.py # Pydantic settings
│ └── [future modules] # Domain-specific modules
├── requirements.txt # Python dependencies
└── README.md # This file
Future structure (as features are added):
library-desk/
├── src/
│ ├── query/ # Query domain
│ │ ├── router.py
│ │ ├── schemas.py
│ │ ├── service.py
│ │ └── dependencies.py
│ ├── graph/ # Graph domain
│ ├── wiki/ # Wiki domain
│ └── ingest/ # Ingestion domain
Best Practices Implemented
✅ Async routes for I/O operations
✅ Dependency injection for configuration and auth
✅ Pydantic models for request/response validation
✅ Modular settings using Pydantic Settings
✅ API key authentication with Bearer tokens
✅ OpenAPI documentation auto-generated
✅ Proper logging with structured format
✅ CORS middleware configured
✅ Health checks for monitoring
✅ Minor version locking (~=) in requirements
✅ CVE-checked dependencies (Dec 2025)
API Documentation
Once running, access:
- Interactive docs: http://localhost:8089/docs
- ReDoc: http://localhost:8089/redoc
- OpenAPI spec: http://localhost:8089/openapi.json
Authentication
All protected endpoints require a Bearer token:
curl -H "Authorization: Bearer ${LIBRARY_API_KEY}" \
http://localhost:8089/stats
Testing
# Run tests (when implemented)
pytest
# With coverage
pytest --cov=src --cov-report=term
Deployment
Deployed via Portainer stack: /stacks/library-desk.yml
The container:
- Runs on port 8089
- Auto-creates venv on startup
- Installs dependencies from requirements.txt
- Starts uvicorn with 2 workers
- Mounts source code for live editing
Monitoring
- Uptime Kuma: Monitor
/healthendpoint - Logs:
docker logs library-desk - Stats:
GET /stats(requires API key)
Scheduler Integration
See LIBRARIAN_INTEGRATION.md for details on how The Scheduler (Librarian) integrates with Library Desk for automated documentation indexing.
Key Workflow:
- Scheduler mirrors docs to Gitea (daily 03:00)
- Scheduler syncs to Library Desk (daily 03:30)
- Library Desk ingests, chunks, embeds, and indexes
- Content becomes searchable via HybridRAG
Future Enhancements
- Implement HybridRAG query logic
- Add Neo4j connection pooling
- Add Qdrant client initialization
- Implement Wiki.js API proxy
- Add entity extraction (spaCy/NLP)
- Implement mind map generation
- Add deduplication logic
- Implement Scheduler integration endpoints (see LIBRARIAN_INTEGRATION.md)
- Add comprehensive tests
- Add rate limiting
- Add request tracing
References
- FastAPI Best Practices
- FastAPI Documentation
- Pydantic Documentation
- Neo4j Python Driver
- Qdrant Python Client
License
Part of Portainer Core infrastructure.
Support
- Check logs:
docker logs library-desk - Health check:
curl http://localhost:8089/health - API docs: http://localhost:8089/docs