- Add /documents router with webhook, upload, search, health endpoints - Create DocumentSyncService for indexing documents to vectors/graph - Add PaperlessClient for REST API integration - Configure dependency injection for Paperless client - Add document models for webhook payloads and responses - Event-driven architecture via Paperless workflow webhooks 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
11 KiB
Memory Management System - Implementation Plan
Overview
A three-tier memory architecture for Library Desk with intelligent orchestration:
| Tier | Storage | Purpose | TTL |
|---|---|---|---|
| Volatile | Qdrant (vectors) | Weather, news, financial, ephemeral context | 5min - 2hr |
| Documents | Paperless-ngx + ClamAV (host) | Git mirrors, PDFs, video, images | Permanent |
| Knowledge | Wiki + Neo4j | Personal dossiers, research, summaries | Permanent |
Implementation Priority: Cleanup → Volatile → Documents → Test Data Cleanup
Phase Status
| Phase | Status | Version |
|---|---|---|
| Phase 1: Cleanup System | ✅ Complete | v1.4.0 |
| Phase 2: Volatile Memory | ✅ Complete | v1.4.3 |
| Phase 3: Document Storage | ✅ Planned | See DOCUMENT_STORAGE_PLAN.md |
| Phase 4: Test Data Cleanup | ✅ Complete | v1.4.4 |
Phase 1: Cleanup System Completion ✅
Current State
- COMPLETE - All Phase 1 tasks implemented
- Redis timestamp tracking for last cleanup
- Bidirectional orphan detection between vectors and graph
- Scheduler integration endpoints ready
Tasks
1.1 Add Scheduler Integration Points ✅
Files: src/routers/maintenance.py
- Add
last_cleanuptimestamp tracking in Redis - Return cleanup stats in format scheduler can log
- Added
RedisDepto cleanup endpoints
1.2 Bidirectional Orphan Detection ✅
Files: src/services/graph_service.py, src/services/vector_service.py
find_documents_without_vectors()- graph nodes with no vectorsfind_chunks_without_graph_nodes()- vectors with no graph node- Updated maintenance endpoints to use bidirectional checks
- Added
chunks_without_graphanddocs_without_vectorsto response models
1.3 Scheduler Configuration ✅
Scheduler-side task definition:
{
"task_name": "library_reconcile_index",
"schedule": "0 4 * * *",
"endpoint": "POST /maintenance/reconcile-index?user=jpmschweitzer",
"description": "Daily index reconciliation - cleanup + reindex missing"
}
- Documented in
LIBRARIAN_INTEGRATION.md - Added
reconcile-indexendpoint (cleanup + reindex missing) - Lightweight health check mode for uptime monitoring
- Detailed health check mode for dashboards
Phase 2: Volatile Memory System ✅
Architecture (Final Implementation)
┌─────────────────┐ ┌──────────────┐ ┌─────────────────┐
│ Library-Desk │◄───│ Scheduler │───►│ External APIs │
│ │ │ │ │ (weather, news) │
│ VolatileCache │ │ Refresh │ └─────────────────┘
│ Service │ │ Jobs │
└────────┬────────┘ └──────────────┘
│
▼
┌─────────────────┐
│ Qdrant │
│ (volatile_{user})│
└─────────────────┘
Key design decisions:
- Vector storage in Qdrant (not Redis) for semantic search
- Collection per user:
volatile_{user} - TTL via
ttl_expirytimestamp in payload - Natural language conversion for embedding structured data
- Integrated into HybridRAG with priority boost
Endpoints (Implemented)
| Endpoint | Method | Purpose |
|---|---|---|
/volatile/search?q=... |
GET | Semantic search across volatile data |
/volatile/store?namespace=...&key=... |
POST | Store/update record |
/volatile/{namespace}/{key} |
GET | Retrieve specific record |
/volatile/{namespace}/{key} |
DELETE | Remove record |
/volatile/stats |
GET | Cache statistics |
/volatile/scheduled |
GET | Records needing refresh |
/volatile/namespaces |
GET | List available namespaces |
/maintenance/cleanup/volatile |
POST | Purge expired records |
Namespaces
| Namespace | Default TTL | Use Case |
|---|---|---|
| weather | 30 min | Current conditions, forecasts |
| news | 1 hour | Headlines, breaking news |
| financial | 5 min | Stock prices, exchange rates |
| transit | 5 min | Train/bus schedules, delays |
| traffic | 10 min | Commute times, road conditions |
| air_quality | 1 hour | Pollution, pollen counts |
| sports | 1 min | Live scores, matches |
| social | 10 min | Social notifications |
| system | 1 min | Service health status |
| context | 1 hour | Session state |
| custom | 1 hour | User-defined data |
Phase 3: Document Storage (Research + Implementation)
Research Scope
Evaluate FOSS self-hosted options for:
- Git repository mirroring
- PDF/document storage with metadata
- Image/video blob storage
- Full-text search capability
Constraints:
- Must be self-hosted, Docker-deployable
- Performance is priority (can wrap complexity in API)
- No cloud dependencies
Candidates to evaluate:
- MinIO (S3-compatible object storage) + metadata in Neo4j
- Paperless-ngx (document management with OCR)
- SeaweedFS (distributed file system)
- Custom: filesystem + Neo4j metadata
Category Descriptors
Wiki page structure for document collections:
# FastAPI Documentation
## Overview
[LLM-generated summary from web search about FastAPI]
## Collection Statistics
- **Documents**: 342 files
- **Last Sync**: 2025-12-24 03:30 UTC
- **Source**: github.com/tiangolo/fastapi
- **Coverage**: API reference, tutorials, deployment guides
## What's Included
[LLM summary of collection contents based on document analysis]
## Related Topics
- [[Python Web Frameworks]]
- [[REST API Design]]
Tasks
3.1 Storage Research
Deliverable: Evaluation document comparing options
3.2 Storage Service Implementation
New file: src/services/document_store_service.py
(Details pending research results)
3.3 Category Descriptor Generation
File: src/services/consolidation_service.py
Add LLM-powered category descriptor generation:
- Web search for topic overview
- Analyze collection contents
- Generate/update wiki page with template
Phase 4: LLM Tester Data Cleanup ✅
Problem
LLM testing creates accumulated cruft across the system:
- Wiki.js pages under
llm-tester/andllm_tester/paths - Graph nodes (Document, Entity) linked to test pages
- Vector chunks in Qdrant for test content
This data accumulates over time and clutters Wiki.js visually (no separate tenant scope for tests).
Solution
Add a maintenance endpoint to purge all LLM tester artifacts across wiki, graph, and vectors.
Tasks
4.1 Identify Test Data Patterns ✅
Patterns matched (security-restricted to test user namespace):
users/llm-tester/*users/llm_tester/*
4.2 Add Cleanup Endpoint ✅
File: src/routers/maintenance.py
@router.post("/cleanup/test-data")
async def cleanup_test_data(
dry_run: bool = Query(default=True),
wiki: WikiJSDep = None,
vector_service: VectorServiceDep = None,
graph_service: GraphServiceDep = None,
api_key: str = Depends(verify_api_key)
):
"""
Purge LLM tester data from wiki, graph, and vectors.
**Security**: Only deletes pages in the test user namespace:
- users/llm-tester/*
- users/llm_tester/*
Use dry_run=true to preview what would be deleted.
"""
4.3 Implementation Steps ✅
- Wiki cleanup: Delete pages via GraphQL mutation
- Graph cleanup: Delete Document nodes using
delete_page()method - Vector cleanup: Delete chunks using
delete_page_chunks()method
4.4 Scheduler Integration ✅
Recommended schedule: Weekly (Sunday 3:00 AM)
{
"task_name": "test_data_cleanup",
"schedule": "0 3 * * 0",
"endpoint": "POST /maintenance/cleanup/test-data?dry_run=false",
"description": "Weekly cleanup of LLM test data"
}
Files to Modify
src/routers/maintenance.py- Add cleanup endpointsrc/services/wiki_service.py- Add bulk delete by path pattern (if needed)src/services/graph_service.py- May need pattern-based node deletionsrc/services/vector_service.py- Add pattern-based chunk deletion
Files Modified/Created
Phase 1 (Cleanup) ✅
src/routers/maintenance.py- Timestamp tracking, cleanup endpointssrc/services/graph_service.py- Bidirectional validationsrc/services/vector_service.py- Cross-reference checksLIBRARIAN_INTEGRATION.md- Scheduler config docs
Phase 2 (Volatile) ✅
src/services/volatile_service.py- Qdrant-based volatile cachesrc/routers/volatile.py- Simplified endpointssrc/models/volatile.py- Namespaces and modelssrc/models/hybrid_rag.py- Volatile config optionssrc/services/hybrid_rag_service.py- Volatile integrationsrc/clients/qdrant_client.py- Expiry filter methodstests/test_volatile.py- 37 tests
Phase 3 (Documents)
docs/DOCUMENT_STORAGE_RESEARCH.md- NEWsrc/services/document_store_service.py- NEW (post-research)src/routers/documents.py- NEW (post-research)
Phase 4 (Test Data Cleanup)
src/routers/maintenance.py- Add cleanup endpointsrc/services/wiki_service.py- Bulk delete by path patternsrc/services/graph_service.py- Pattern-based node deletionsrc/services/vector_service.py- Pattern-based chunk deletion
Resolved Design Decisions
- Volatile Storage: Qdrant vectors (not Redis) for semantic search capability
- Collection Naming:
volatile_{user}for per-user isolation - TTL Mechanism:
ttl_expirytimestamp in payload, background cleanup job - HybridRAG Integration: Volatile as third source with RRF priority boost
- Biographer Qdrant: Same Qdrant instance, different collection
- Scheduler API: Has REST API for task registration
Future Consideration: Dedicated API Integrations
For volatile data where quality/consistency matters (weather, financial), consider:
- OpenWeatherMap API for weather (daily refresh cycle)
- Financial data API (Alpha Vantage, Yahoo Finance)
- News APIs (NewsAPI, GDELT)
- NOS.nl - Explicit source for Dutch news
This would live in a new src/clients/ module with:
weather_client.py- Daily refresh cyclefinancial_client.pynews_client.py- Include NOS.nl scraper/API for Dutch coverage
These provide structured, reliable data vs. SearXNG web scraping. Implementation deferred to later phase.
Refresh Schedules
Note: TTL should be longer than refresh interval to prevent data gaps.
| Volatile Type | TTL | Refresh Cycle | Refresh Interval | Sources |
|---|---|---|---|---|
| Weather | 86400s (24hr) | Daily | Every 24hr | OpenWeatherMap |
| Dutch News | 28800s (8hr) | 4x daily | Every 6hr | NOS.nl |
| Global News | 28800s (8hr) | 4x daily | Every 6hr | NewsAPI, GDELT |
| Financial | 600s (10min) | On-demand | N/A | Alpha Vantage |
TTL Logic:
- TTL = Refresh Interval × 1.5 (buffer for failed refreshes)
- On-demand data gets shorter TTL since it's fetched when needed