Конфигурация и домен¶
Эти модули не запускают роли сами. Они определяют язык системы: настройки, валидные структуры, persistence-модели, hashes, ошибки и логи.
- Вход
- environment variables, внешние payloads, значения домена
- Выход
- типизированные Settings/Pydantic models/SQLAlchemy models
- Побочные эффекты
- нет при импорте; DB connection создаётся только явной функцией
- Потребители
- crawler, indexer, API, CLI, migrations и tests
config.py — единая конфигурация¶
Settings наследует BaseSettings: имя поля database_url автоматически соответствует DATABASE_URL. .env читается как дополнительный источник, лишние переменные игнорируются, а ограничения проверяются до создания соединений.
Группы полей:
- runtime:
app_env,log_level; - dependencies:
database_url,nats_url,ollama_url; - AI: режим, модели, размерность, batch, threshold и timeout;
- crawler: директория, лимит байт, concurrency, retry/delay;
- sources: base URL, query/page size, Contracts Finder cooldown;
- NATS: stream, subject, consumer, ACK policy.
embedding_dimensions имеет тип int, чтобы принять строку "1024" из environment, и отдельный validator запрещает любое значение кроме 1024 — оно зафиксировано DDL VECTOR(1024). get_settings() кэширует один объект на процесс.
schemas.py — boundary-контракты¶
| Тип | Где появляется | Ответственность |
|---|---|---|
AttachmentRecordV1 |
adapters → crawler | нормализованная ссылка на вложение |
TenderRecordV1 |
adapters → crawler | единая закупка независимо от источника |
TenderChangedV1 |
crawler → NATS → indexer | ссылка на конкретную версию tender |
SearchRequest, AskRequest |
HTTP input | query и разные пределы top-k |
SearchResult/Response |
retrieval output | безопасный fragment без internal fields |
TenderDetails |
detail endpoint | карточка и публичные attachment metadata |
ErrorResponse |
exception handlers | стабильная ошибка и correlation id |
Pydantic нужен на границе, где данные ещё не заслуживают доверия. Внутри SQLAlchemy row уже защищён DDL и сервисными инвариантами.
models.py и db.py — persistence¶
models.py объявляет ровно пять application tables. Relationships помогают загрузить source/attachments/chunks, но таблицы создаёт Alembic, а не metadata.create_all.
create_engine() включает pool_pre_ping и recycle. create_session_factory() создаёт независимые async sessions с expire_on_commit=False. session_scope() показывает общий commit/rollback pattern, хотя сервисы часто управляют транзакцией явно.
hashing.py — идентичность версии¶
flowchart LR
RECORD["TenderRecordV1"] --> CANON["canonical_tender_payload"]
CANON --> JSON["JSON sort_keys + compact separators"]
JSON --> SHA["SHA-256 content_hash"]
canonical_tender_payload() включает значимые нормализованные поля и сортирует attachments. Timestamps обработки, DB UUID и index status в hash не входят. build_chunk_key() делает стабильный ключ из tender, attachment, позиции, hash текста и embedding model.
errors.py — контролируемые отказы¶
AppError несёт машинный code, безопасный message, HTTP status и optional details. Специализации отделяют unavailable dependency, source HTTP, attachment, extraction и неверный AI response. FastAPI переводит их в один ErrorResponse; worker использует тип ошибки, чтобы выбрать NAK или TERM.
logging.py — наблюдаемость без секретов¶
JsonFormatter пишет UTC timestamp, level, logger, message и доступные correlation fields. mask_mapping() рекурсивно скрывает API keys, authorization, password и key hash.
Правило зависимостей¶
Core может знать о Pydantic/SQLAlchemy, но не импортирует entrypoints. schemas.py не зависит от models.py; адаптеры возвращают schema, сервис преобразует её в model. Это предотвращает смешивание внешнего контракта и структуры БД.