feat(03-04): retire flat-file settings; wire per-user AI config via DB lookup

- config.py: Remove SETTINGS_FILE, DEFAULT_SYSTEM_PROMPT, DEFAULT_SETTINGS
  constants; add system_prompt, default_ai_provider, default_ai_model to Settings
- services/classifier.py: Add _DEFAULT_SYSTEM_PROMPT module constant; classify_document
  and suggest_topics_for_document accept ai_provider/ai_model kwargs; no longer calls
  storage.load_settings() — uses app_settings defaults with DB-supplied overrides (D-14, D-15)
- services/storage.py: Delete load_settings, save_settings, mask_api_key, settings_masked;
  remove from __all__; remove import copy, json, DEFAULT_SETTINGS, SETTINGS_FILE (D-12)
- tasks/document_tasks.py: _run resolves user.ai_provider/ai_model via session.get(User,
  doc.user_id) and passes through to classifier; task signature unchanged (T-03-19)
- api/settings.py: Deleted — /api/settings endpoint removed (D-12)
- main.py: Remove settings_router import and include_router call
- tests/test_settings.py: Replace all tests with test_settings_endpoint_removed (404, green)
- tests/test_classifier.py: Implement test_per_user_provider, test_celery_task_uses_user_provider,
  test_default_provider_fallback; remove xfail markers (DOC-03, DOC-05)
This commit is contained in:
curo1305
2026-05-23 20:32:55 +02:00
parent aadc69fea0
commit 6849ebd1e6
8 changed files with 193 additions and 316 deletions
+5 -39
View File
@@ -1,5 +1,3 @@
from pathlib import Path
from pydantic_settings import BaseSettings, SettingsConfigDict
@@ -13,9 +11,6 @@ class Settings(BaseSettings):
env_list_separator=",",
)
# Data directory — used only for the flat-file settings.json path (Phase 1)
data_dir: str = "/app/data"
# PostgreSQL
database_url: str = "postgresql+psycopg://docuvault_app:changeme_app@postgres:5432/docuvault"
database_migrate_url: str = "postgresql+psycopg://docuvault_migrate:changeme_migrate@postgres:5432/docuvault"
@@ -56,39 +51,10 @@ class Settings(BaseSettings):
# Frontend URL — used to build password reset links (D-02, D-03)
frontend_url: str = "http://localhost:5173"
# AI classification defaults (Phase 3 — D-13, D-15)
system_prompt: str = "" # SYSTEM_PROMPT env var; hardcoded fallback lives in classifier.py
default_ai_provider: str = "ollama" # DEFAULT_AI_PROVIDER env var
default_ai_model: str = "llama3.2" # DEFAULT_AI_MODEL env var
settings = Settings()
# SETTINGS_FILE: still flat-file in Phase 1; migrates to users.ai_provider in Phase 2
SETTINGS_FILE = Path(settings.data_dir) / "settings.json"
DEFAULT_SYSTEM_PROMPT = """You are a document classification assistant. When given a document's text content and a list of existing topics, you must:
1. Assign the document to one or more relevant topics from the list.
2. If no existing topics fit well, suggest new topic names.
Return ONLY valid JSON in this exact format, with no additional text or explanation:
{"assigned_topics": ["topic1"], "new_topic_suggestions": ["new topic name"]}
If the document fits no topics and you have no suggestions, return: {"assigned_topics": [], "new_topic_suggestions": []}"""
DEFAULT_SETTINGS = {
"system_prompt": DEFAULT_SYSTEM_PROMPT,
"active_provider": "lmstudio",
"providers": {
"anthropic": {
"api_key": "",
"model": "claude-sonnet-4-6"
},
"openai": {
"api_key": "",
"model": "gpt-4o",
"base_url": None
},
"ollama": {
"base_url": "http://host.docker.internal:11434",
"model": "llama3.2"
},
"lmstudio": {
"base_url": "http://host.docker.internal:1234",
"model": "gemma-4-e4b-it"
}
}
}