- router: 채팅 라우터/프로토콜을 negotiation/ 에서 chat/ 으로 분리(git mv, URL 유지). chat_protocol.py → chat/protocol.py (폴더당 protocol.py convention 복원), router 등록·import 갱신. - chat_service: 순수 헬퍼/매퍼를 모듈 함수 → 클래스 @staticmethod 로 이동하고 클래스 상단에 집약. - protocol: Req 모델 + 코드/enum 필드에 Field(description=...) 추가(Swagger 노출), Req 문자열 필드에 max_length(DB 컬럼 정합) 추가 → 초과 입력이 DB 오류 대신 422. - models: updated_at 에 onupdate 추가 → UPDATE 시 자동 갱신(negodata convention 일치). - tests: negotiation reject e2e 6케이스 + chat 순수 헬퍼 단위 4케이스 추가(45→55 passed). - enums: 장황한 주석/docstring 축약(코드값 변경 없음). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
59 lines
2.4 KiB
Python
59 lines
2.4 KiB
Python
from fastapi import APIRouter, Depends
|
|
from fastapi.security import HTTPAuthorizationCredentials
|
|
|
|
from common.models.gmodel import UserInfo
|
|
from router.v1.validator.dependencies import IsValidAccessToken, RemoveNoneResponse, security
|
|
from services.chat_service import ChatService
|
|
from .protocol import Req_ChatSend, Res_ChatInit, Res_ChatMessages, Res_ChatSend
|
|
|
|
# URL 은 협상 세션의 하위 리소스라 prefix 는 /v1/negotiation 유지(파일만 chat/ 로 분리).
|
|
router = APIRouter(prefix="/v1/negotiation", tags=["Chat"], responses={404: {"description": "Not found"}})
|
|
|
|
|
|
@router.get(
|
|
path="/sessions/{session_id}/chat/init",
|
|
response_model=Res_ChatInit,
|
|
summary="채팅 진입(상품·견적 메타)",
|
|
description="채팅 화면 진입용. 상품/견적 정보 + 현재 세션 상태 + 마감 시각(타이머)을 반환한다. 소유(공급사) 검증.",
|
|
)
|
|
async def chat_init(
|
|
session_id: str,
|
|
user_info: UserInfo = Depends(IsValidAccessToken),
|
|
credentials: HTTPAuthorizationCredentials = Depends(security),
|
|
service: ChatService = Depends(),
|
|
):
|
|
return RemoveNoneResponse(await service.init(user_info, credentials.credentials, session_id))
|
|
|
|
|
|
@router.get(
|
|
path="/sessions/{session_id}/chat/messages",
|
|
response_model=Res_ChatMessages,
|
|
summary="대화 히스토리",
|
|
description="세션의 대화 말풍선 목록(seq 오름차순). 비어 있고 협상중이면 오프닝 메시지를 생성해 포함한다.",
|
|
)
|
|
async def chat_messages(
|
|
session_id: str,
|
|
user_info: UserInfo = Depends(IsValidAccessToken),
|
|
credentials: HTTPAuthorizationCredentials = Depends(security),
|
|
service: ChatService = Depends(),
|
|
):
|
|
return RemoveNoneResponse(await service.messages(user_info, credentials.credentials, session_id))
|
|
|
|
|
|
@router.post(
|
|
path="/sessions/{session_id}/chat/send",
|
|
response_model=Res_ChatSend,
|
|
summary="협상 한 턴 전송",
|
|
description="유저 입력을 보내고 agent 가 만든 봇 응답 1건을 반환한다(append-only). 종료 시 세션 입찰을 확정한다.",
|
|
)
|
|
async def chat_send(
|
|
session_id: str,
|
|
req: Req_ChatSend,
|
|
user_info: UserInfo = Depends(IsValidAccessToken),
|
|
credentials: HTTPAuthorizationCredentials = Depends(security),
|
|
service: ChatService = Depends(),
|
|
):
|
|
return RemoveNoneResponse(
|
|
await service.send(user_info, credentials.credentials, session_id, req.user_input_type, req.user_input)
|
|
)
|