feat(sprint2.5): логи, вынесение системного промпта, markdown-рендер
Три подряд доработки по плану Спринта 2.5. 1) Логи. Проблема: uvicorn ставит handlers на root-logger до того, как отработает наш lifespan, поэтому logging.basicConfig там был no-op, и logger.exception ничего не писал. Переносим basicConfig на уровень импорта main.py с force=True — наш StreamHandler перебивает uvicorn-овский root, остальные логгеры (uvicorn.access, uvicorn.error, alembic, chromadb) остаются со своими форматами. В lifespan basicConfig больше не зовётся. 2) Системный промпт вынесен из services/llm_client.py в prompts/system_prompt.md. LLMClient читает файл при импорте модуля через _load_system_prompt(); если файла нет — пустая строка + warning. Это задел под Спринт 3, где промпт будет редактируемым и версионируемым — физически положить его как файл дешевле, чем держать в исходниках. 3) Markdown в ответах ассистента. Подключены marked и DOMPurify с CDN в sandbox.html. Рендер через renderMd(text): marked.parse + DOMPurify.sanitize — защищает от <script> на случай, если LLM вернёт сырой HTML. Реплики пациента остаются plain text (esc). Добавлены стили для p/ul/ol/code/pre/a/h1-h3/blockquote внутри .msg.assistant, чтобы всё выглядело уместно в пузыре. Обёртка msg-body введена, чтобы разделить контент и msg-meta. План в SPRINTS.md уточнён по переиндексации — будет отдельный endpoint. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from alembic import command
|
||||
@@ -10,9 +11,21 @@ from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
from config import settings
|
||||
from services.embeddings import EmbeddingService
|
||||
from services.llm_client import LLMClient
|
||||
from services.vectorstore import VectorStoreService
|
||||
|
||||
# Настройка логов до импорта приложения: uvicorn ставит свои handlers
|
||||
# на root-logger, поэтому basicConfig в lifespan уже не срабатывает
|
||||
# (handlers есть — basicConfig no-op). force=True перебивает.
|
||||
logging.basicConfig(
|
||||
level=getattr(logging, settings.log_level.upper(), logging.INFO),
|
||||
format="%(asctime)s %(levelname)-7s %(name)s: %(message)s",
|
||||
datefmt="%H:%M:%S",
|
||||
handlers=[logging.StreamHandler(sys.stderr)],
|
||||
force=True,
|
||||
)
|
||||
|
||||
from services.embeddings import EmbeddingService # noqa: E402
|
||||
from services.llm_client import LLMClient # noqa: E402
|
||||
from services.vectorstore import VectorStoreService # noqa: E402
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -31,7 +44,6 @@ def _run_migrations() -> None:
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
global embedding_service, vectorstore_service, llm_client
|
||||
logging.basicConfig(level=getattr(logging, settings.log_level.upper(), logging.INFO))
|
||||
logger.info("Running DB migrations…")
|
||||
await asyncio.to_thread(_run_migrations)
|
||||
logger.info("Loading embedding model: %s", settings.embedding_model)
|
||||
|
||||
Reference in New Issue
Block a user