1. kei_client.py: Kei API가 마크다운 리스트(- ) 접두사로 JSON 응답 시 전처리하여 파싱 2. image_utils.py: base_path+상대경로 이중 시 파일명 rglob 재탐색 3. design_director.py: - conclusion 꼭지 → footer zone + conclusion-accent-bar 코드 레벨 강제 - _validate_height_budget(): zone별 height_cost 합산 검증, 초과 시 큰 블록 자동 교체 - Opus 추천 프롬프트에 zone 배정 규칙 명시 (conclusion→footer 등) 4. main.py: 서버 startup 시 FAISS 인덱스 + bge-m3 모델 미리 로드 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
73 lines
2.1 KiB
Python
73 lines
2.1 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import logging
|
|
|
|
from fastapi import FastAPI
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.responses import FileResponse
|
|
from fastapi.staticfiles import StaticFiles
|
|
from pathlib import Path
|
|
from pydantic import BaseModel
|
|
from sse_starlette.sse import EventSourceResponse
|
|
|
|
from src.config import settings
|
|
from src.pipeline import generate_slide
|
|
|
|
logging.basicConfig(level=getattr(logging, settings.log_level, logging.DEBUG))
|
|
logger = logging.getLogger(__name__)
|
|
|
|
app = FastAPI(title="Design Agent", version="0.1.0")
|
|
|
|
|
|
@app.on_event("startup")
|
|
async def startup_preload():
|
|
"""서버 시작 시 FAISS 인덱스 + 임베딩 모델 미리 로드."""
|
|
try:
|
|
from src.block_search import _ensure_loaded
|
|
_ensure_loaded()
|
|
logger.info("FAISS 인덱스 + bge-m3 모델 미리 로드 완료")
|
|
except Exception as e:
|
|
logger.warning(f"FAISS 미리 로드 실패 (첫 요청 시 로드): {e}")
|
|
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=["http://localhost:5174", "http://localhost:5173"],
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
# 정적 파일 서빙 (CSS, 폰트 등)
|
|
static_dir = Path(__file__).parent.parent / "static"
|
|
if static_dir.exists():
|
|
app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
|
|
|
|
|
|
class SlideRequest(BaseModel):
|
|
content: str
|
|
base_path: str = "" # 이미지 기준 폴더 경로 (선택, 로컬 경로)
|
|
|
|
|
|
@app.get("/api/health")
|
|
async def health():
|
|
return {"status": "ok", "service": "design-agent"}
|
|
|
|
|
|
@app.post("/api/generate")
|
|
async def generate(req: SlideRequest):
|
|
"""콘텐츠 → 슬라이드 생성 (SSE 스트리밍)."""
|
|
async def event_stream():
|
|
async for event in generate_slide(req.content, base_path=req.base_path):
|
|
yield {
|
|
"event": event["event"],
|
|
"data": json.dumps(event["data"], ensure_ascii=False),
|
|
}
|
|
|
|
return EventSourceResponse(event_stream())
|
|
|
|
|
|
@app.get("/")
|
|
async def frontend():
|
|
"""프론트엔드 UI — static/index.html 서빙."""
|
|
return FileResponse(str(static_dir / "index.html"), media_type="text/html")
|