feat(engine): claude -p 상주 멀티턴 엔진 게이트웨이 + 실동작 검증

- engine_gateway/gateway.py: 회기당 claude -p 상주 프로세스(stream-json), 턴 직렬, budget 제한
- 세션 생성/턴/종료 HTTP API(FastAPI, :9099)
- 검증: 멀티턴 컨텍스트 유지 + prompt caching 재사용(턴2 +$0.07) 실동작 확인
This commit is contained in:
Yun Chan 2026-06-25 21:43:27 +09:00
parent d5b86c5f89
commit 84eb6e2173
20 changed files with 2038 additions and 1 deletions

View file

@ -0,0 +1,84 @@
"""인증 라우트 — BFF OAuth 2.1 Auth Code + PKCE(S256) 스텁.
마스터플랜 §5: BFF + OAuth 2.1, 토큰은 서버(Redis)에만, 브라우저엔 __Host- HttpOnly 쿠키.
미성년 사례데이터 + 상담 민감정보 -> XSS 토큰탈취 원천 차단.
1 = Google OIDC 단독, 한신대 SSO 2(R11, Authlib provider 추상화 ).
파일은 라우트 시그니처 + 흐름 + TODO. 실제 OAuth 교환/Redis 세션은 Phase 2 트랙 B.
"""
from __future__ import annotations
from typing import Annotated, Optional
from fastapi import APIRouter, HTTPException, Query, Response, status
from fastapi.responses import RedirectResponse
from pydantic import BaseModel
from ..config import settings
from ..deps import CurrentPrincipal
router = APIRouter(prefix="/auth", tags=["auth"])
class MeResponse(BaseModel):
user_id: str
role: str
cohort_ids: list[str]
@router.get("/login")
async def login(
provider: Annotated[str, Query()] = "google",
) -> RedirectResponse:
"""OAuth Auth Code + PKCE 시작 (BFF).
절차:
1. code_verifier 생성 -> S256 code_challenge
2. state(CSRF) + verifier 서버 세션(Redis) 저장
3. provider authorize URL 302 (Google OIDC 1)
TODO: Authlib provider 추상화 + Redis state 저장. 현재 스텁 501.
"""
if provider != "google":
# 한신대 SSO 는 2차 (R11)
raise HTTPException(status.HTTP_501_NOT_IMPLEMENTED, detail=f"provider {provider} not yet supported")
raise HTTPException(status.HTTP_501_NOT_IMPLEMENTED, detail="OAuth login TODO (Phase 2 트랙 B)")
@router.get("/callback")
async def callback(
response: Response,
code: Annotated[Optional[str], Query()] = None,
state: Annotated[Optional[str], Query()] = None,
) -> RedirectResponse:
"""OAuth 콜백 — code -> token 교환 후 서버 세션 발급.
절차:
1. state 검증 (Redis 저장값과 대조, CSRF)
2. code + code_verifier token 교환 (PKCE)
3. id_token 검증 -> user upsert -> role/cohort 매핑
4. Redis 세션 생성 -> __Host- HttpOnly Secure SameSite=Lax 쿠키 set
5. IRB 동의 미이행 동의 게이트로 리다이렉트 (마스터플랜 §7)
TODO: 전체 교환 구현. 현재 스텁 501.
"""
raise HTTPException(status.HTTP_501_NOT_IMPLEMENTED, detail="OAuth callback TODO (Phase 2 트랙 B)")
@router.post("/logout")
async def logout(response: Response) -> dict[str, bool]:
"""세션 무효화 (Redis 삭제 + 쿠키 만료). IRB 철회 즉시 무효화 경로 겸용.
TODO: Redis 세션 삭제. 현재 쿠키 만료만.
"""
response.delete_cookie(settings.cookie_name, httponly=True, secure=settings.is_prod, samesite="lax")
return {"ok": True}
@router.get("/me", response_model=MeResponse)
async def me(principal: CurrentPrincipal) -> MeResponse:
"""현재 세션 주체 (프론트 부트스트랩용). 미인증이면 deps 에서 401."""
return MeResponse(
user_id=principal.user_id,
role=principal.role.value,
cohort_ids=principal.cohort_ids,
)