feat: add Phase F.1 memory infrastructure

Add multi-tenancy support and memory storage infrastructure:

- Add ContextVar-based request context (src/core/context.py)
  - Async-safe user/conversation tracking via contextvars
  - RequestContext manager for clean setup/teardown
  - get_user(), get_conversation_id() helpers

- Add multi-tenancy utilities (src/core/multi_tenancy.py)
  - User ID sanitization for collection/key names
  - get_memory_collection_name(), get_session_key() helpers

- Add Ollama embedding client (src/core/embeddings.py)
  - nomic-embed-text model (768 dimensions)
  - embed(), embed_batch(), health_check() methods

- Add Qdrant client wrapper (src/core/qdrant.py)
  - Per-user collection pattern: memories_{user}
  - upsert_memory(), search_memories(), delete_memory()
  - Type-based filtering support

- Add Redis memory cache (src/core/memory_cache.py)
  - Session context with 24h TTL
  - Recent entities tracking
  - Separate from benchmarks (db=2)

- Update config with memory settings
  - QDRANT_HOST, QDRANT_PORT, QDRANT_EMBEDDING_DIM
  - OLLAMA_EMBEDDING_MODEL
  - REDIS_MEMORY_DB, REDIS_MEMORY_TTL_HOURS

- Add user field to ResponseRequest (OpenAI standard)
- Set context in router, reset in finally block
- Update librarian client to use get_user() (12 methods)

All 333 unit tests pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-13 17:27:51 +01:00
co-authored by Claude Opus 4.5
parent c049c1e354
commit 4c6ac89808
11 changed files with 1465 additions and 24 deletions
+41 -1
View File
@@ -124,6 +124,36 @@ class Config(BaseSettings):
description="Library-Desk request timeout in seconds"
)
# Qdrant Configuration (Memory vector storage)
QDRANT_HOST: str = Field(
default="localhost",
description="Qdrant server host"
)
QDRANT_PORT: int = Field(
default=6333,
description="Qdrant server port"
)
QDRANT_EMBEDDING_DIM: int = Field(
default=768,
description="Embedding dimension (768 for nomic-embed-text)"
)
# Ollama Embedding Configuration
OLLAMA_EMBEDDING_MODEL: str = Field(
default="nomic-embed-text",
description="Ollama model for embeddings"
)
# Redis Memory Database (separate from benchmarks)
REDIS_MEMORY_DB: int = Field(
default=2,
description="Redis database number for memory cache"
)
REDIS_MEMORY_TTL_HOURS: int = Field(
default=24,
description="TTL for session context in hours"
)
# Logging
LOG_LEVEL: str = Field(default="INFO", description="Logging level")
ENABLE_BENCHMARKS: bool = Field(default=True, description="Enable performance benchmarking")
@@ -139,9 +169,19 @@ class Config(BaseSettings):
@property
def redis_url(self) -> str:
"""Construct Redis connection URL."""
"""Construct Redis connection URL for benchmarks."""
return f"redis://{self.REDIS_HOST}:{self.REDIS_PORT}/{self.REDIS_DB}"
@property
def redis_memory_url(self) -> str:
"""Construct Redis connection URL for memory cache."""
return f"redis://{self.REDIS_HOST}:{self.REDIS_PORT}/{self.REDIS_MEMORY_DB}"
@property
def qdrant_url(self) -> str:
"""Construct Qdrant server URL."""
return f"http://{self.QDRANT_HOST}:{self.QDRANT_PORT}"
@property
def log_format(self) -> str:
"""