gendesign/tradein-mvp/backend/app/core/config.py
bot-backend 96f02d37f3
All checks were successful
Deploy Trade-In / changes (push) Successful in 5s
Deploy Trade-In / build-backend (push) Successful in 46s
Deploy Trade-In / build-frontend (push) Successful in 1m38s
Deploy Trade-In / deploy (push) Successful in 36s
feat(tradein): blend Avito IMV anchor into estimate + ДКП corridor advisory (#651, #652) (#674)
Co-authored-by: bot-backend <bot-backend@gendsgn.local>
Co-committed-by: bot-backend <bot-backend@gendsgn.local>
2026-05-29 17:42:06 +00:00

90 lines
5.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Минимальный settings для standalone trade-in MVP."""
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore")
# required — задаётся через env DATABASE_URL. Нет дефолта: fail-fast при старте
# если переменная не задана (C-3 security audit).
database_url: str
cors_origins: list[str] = ["http://localhost", "http://localhost:3000", "http://localhost:8080"]
environment: str = "dev"
# Geocoder. Env var name `YANDEX_GEOCODER_API_KEY` — consistent с scripts/
# backfill_house_coords.py + audit_address_mismatch.py + main backend
# OpenRouteService_API_KEY pattern. Renamed from YANDEX_GEOCODER_KEY (PR F).
yandex_geocoder_api_key: str | None = None # 25K req/day free после регистрации
yandex_suggest_key: str | None = None # для frontend autocomplete (proxy через backend)
# для User-Agent в Nominatim (Nominatim Usage Policy)
contact_email: str = "erginrajpopxbe@outlook.com"
# Public URL — для QR-кода в PDF, shareable links, etc.
public_url: str = "http://127.0.0.1:8080"
# GlitchTip DSN — мониторинг ошибок (Sentry-совместимый). #396.
# Пусто = мониторинг выключен (dev). В prod — env GLITCHTIP_DSN из .env.runtime.
glitchtip_dsn: str | None = None
# Ключ шифрования для pgp_sym_encrypt (Cian session cookies).
# Задаётся через env COOKIE_ENCRYPTION_KEY. Пусто = шифрование не работает.
cookie_encryption_key: str = ""
# Redis URL для hot-cache (Phase 3.2). Задаётся через env REDIS_URL.
redis_url: str = "redis://localhost:6379/0"
# Rate-limit публичного /api/* (per-IP sliding window). ENV: RATE_LIMIT,
# RATE_LIMIT_WINDOW_S. Не более rate_limit запросов за rate_limit_window_s
# секунд с одного IP. Аутентифицированный трафик (X-Authenticated-User от
# Caddy basic_auth) не лимитируется — см. ratelimit.py (#655).
rate_limit: int = 300
rate_limit_window_s: float = 60.0
# Password for tradein_fdw_reader role — used by backend startup to create/refresh
# USER MAPPING for postgres_fdw → gendesign DB (gendesign_remote server).
# Пусто = USER MAPPING не создаётся, gendesign_cad_buildings не работает (dev).
gendesign_fdw_password: str | None = None
# DaData /clean/address — обогащение target-адреса канонической формой,
# kadastr_num, ФИАС, координатами, метро. Используется в estimator для
# on-demand enrichment (PR Q1). Demo tier: 100 req/день. Если хотя бы один
# не задан — service возвращает None gracefully, estimator продолжает.
# ENV: DADATA_API_TOKEN, DADATA_API_SECRET.
dadata_api_token: str | None = None
dadata_api_secret: str | None = None
# ── #651: IMV / Yandex blend (killer accuracy fix) ──────────────────────
# Радиусная медиана ₽/м² системно недооценивает премиум/видовые квартиры
# (нет class/segment/IMV-коррекции → premium ~2x underestimate, case 50М vs
# факт ~100М). Если внешний якорь (Avito IMV recommended_price из
# house_imv_evaluations, либо Yandex sale) выше нашей медианы более чем в
# `threshold` раз — подмешиваем якорь к медиане с весом `weight` и
# расширяем верх диапазона. ОДНОНАПРАВЛЕННО: только повышаем (баг — занижение).
# Полностью за флагами — безопасно выкатить до демо; при отсутствии IMV/Yandex
# no-op (медиана не меняется).
estimate_imv_blend_enabled: bool = True
estimate_imv_blend_weight: float = 0.5 # вес якоря в blend: median*(1-w)+A*w
estimate_imv_blend_threshold: float = 1.15 # якорь должен быть > медианы ×1.15
# ── Estimate enrichment time-budgets (#654) ──────────────────────────────
# POST /estimate делает несколько ПОСЛЕДОВАТЕЛЬНЫХ блокирующих сетевых
# вызовов (geocode → Overpass → Yandex valuation → IMV → Cian). Yandex
# valuation (внутренний httpx timeout 30s) НЕ gated на наличие floor и
# выполняется на каждой оценке — главный подозреваемый на gateway-таймаут
# (Caddy 502/504). Эти budget'ы оборачивают самые медленные ungated-вызовы
# в asyncio.wait_for(): при превышении источник деградирует в None (тот же
# graceful-путь что и сетевая ошибка), а НЕ роняет весь /estimate в 5xx.
# Держать суммарный budget ниже Caddy read/write timeout (см.
# deploy/Caddyfile.tradein-fragment). ENV: ESTIMATE_YANDEX_VALUATION_TIMEOUT_S,
# ESTIMATE_GEOCODE_BUDGET_S, ESTIMATE_HOUSE_META_TIMEOUT_S.
estimate_yandex_valuation_timeout_s: float = 8.0
estimate_geocode_budget_s: float = 12.0
estimate_house_meta_timeout_s: float = 8.0
# Лимит успешных оценок trade-in за календарный месяц на аккаунт (#658).
# Конфигурируется через env ESTIMATE_QUOTA_LIMIT. Default 15.
estimate_quota_limit: int = 15
settings = Settings()