import logging from fastapi import APIRouter, HTTPException from fastapi.concurrency import run_in_threadpool from app.schemas.concept import ConceptInput, ConceptOutput from app.services.generative import geometry from app.services.generative.geometry import ParcelGeometryError logger = logging.getLogger(__name__) router = APIRouter() @router.post("", response_model=ConceptOutput) async def create_concept(payload: ConceptInput) -> ConceptOutput: """Generate 3 building variants for the given parcel polygon. Stage 1a: Shapely parse + buildable area (setback) + placement grid. Stage 1b: greedy section placement with STRtree collisions (3 strategies). Stage 1c: real ТЭП + financial model attached to each variant. A degenerate parcel (setback consumes everything, malformed geometry) yields a 422 rather than empty variants — that is a bad request, not a valid empty result. """ try: # geometry.generate — синхронный CPU-bound (Shapely/STRtree), мостим через # run_in_threadpool, чтобы НЕ блокировать event loop (тот же приём, что и в chat.py). variants = await run_in_threadpool(geometry.generate, payload) except ParcelGeometryError as exc: logger.warning("concept generation rejected parcel: %s", exc) raise HTTPException(status_code=422, detail=str(exc)) from exc return ConceptOutput(variants=variants)