Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 0 additions & 83 deletions app/adapter/request_batch.py

This file was deleted.

30 changes: 15 additions & 15 deletions app/adapter/request_single.py → app/adapter/request_to_gpt.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
import asyncio
import os
from functools import lru_cache

from fastapi import HTTPException
from openai import APITimeoutError
from openai import AsyncOpenAI, APITimeoutError

from app.client.oepn_ai import get_gpt_client
from app.util.logger import logger


async def request_responses_output_text_async(gpt_request: dict, timeout: float) -> str:
"""Responses API 요청을 비동기로 처리하며 타임아웃을 적용한다."""
try:
return await asyncio.wait_for(
asyncio.to_thread(request_responses_output_text, gpt_request),
timeout=timeout,
)
except asyncio.TimeoutError:
logger.error(f"Single request timed out after {timeout} seconds.")
raise HTTPException(status_code=408, detail="Request Timeout")
@lru_cache(maxsize=1)
def get_gpt_client() -> AsyncOpenAI:
"""OpenAI 비동기 클라이언트를 캐싱하여(싱글톤처럼) 제공한다."""
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
raise RuntimeError("OPENAI_API_KEY 환경변수가 설정되어 있지 않습니다.")

return AsyncOpenAI(api_key=api_key, max_retries=0)


def request_responses_output_text(gpt_request: dict) -> str:
async def request_to_gpt_returning_text(gpt_request: dict, timeout: int) -> str:
"""Responses API로 단건 요청을 전송하고 텍스트만 추출한다."""
try:
resp = get_gpt_client().responses.create(**gpt_request)
client = get_gpt_client()
client = client.with_options(timeout=float(timeout))
resp = await client.responses.create(**gpt_request)
except APITimeoutError:
logger.error("OpenAI API Timeout")
raise HTTPException(status_code=429, detail="OpenAI API Timeout")
Expand Down
Empty file removed app/client/__init__.py
Empty file.
18 changes: 0 additions & 18 deletions app/client/oepn_ai.py

This file was deleted.

4 changes: 1 addition & 3 deletions app/dto/model/problem_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@

class Selection(BaseModel):
content: str = Field(description="선택지 내용입니다.")
correct: bool = Field(
description="정답 여부입니다. 정답이면 True, 오답이면 False입니다."
)
correct: bool = Field(description="정답 여부입니다. 정답이면 True, 오답이면 False입니다.")


class Problem(BaseModel):
Expand Down
9 changes: 9 additions & 0 deletions app/dto/response/error_response.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from typing import Literal

from pydantic import BaseModel


class ErrorResponse(BaseModel):
type: Literal["error"] = "error"
code: int
message: str
12 changes: 8 additions & 4 deletions app/dto/response/generate_response.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
from typing import List
from typing import List, Literal

from pydantic import BaseModel

from app.dto.model.problem_set import Selection


class ProblemResponse(BaseModel):
class ProblemDTO(BaseModel):
number: int
title: str
selections: List[Selection]
explanation: str
referencedPages: List[int]


class GenerateResponse(BaseModel):
quiz: List[ProblemResponse]
class ProblemSetDTO(BaseModel):
quiz: List[ProblemDTO]


class GenerateResponse(ProblemSetDTO):
type: Literal["quiz"] = "quiz"
23 changes: 7 additions & 16 deletions app/router/generate_router.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,24 @@
from fastapi import APIRouter, Depends
from fastapi import APIRouter
from starlette.responses import StreamingResponse

from app.dto.request.generate_request import GenerateRequest
from app.dto.request.specific_explanation_request import SpecificExplanationRequest
from app.dto.response.generate_response import GenerateResponse
from app.dto.response.specific_explanation_response import SpecificExplanationResponse
from app.service.explanation_service import ExplanationService
from app.service.generate_service import GenerateService

router = APIRouter()


def get_generate_service():
return GenerateService()


def get_explanation_service():
return ExplanationService()


@router.post("/generation")
async def generate(
request: GenerateRequest, generate_service=Depends(get_generate_service)
) -> GenerateResponse:
return await generate_service.generate(request)
async def generate(request: GenerateRequest) -> StreamingResponse:
return StreamingResponse(
GenerateService.generate(request), media_type="application/x-ndjson"
)


@router.post("/specific-explanation")
async def generate_specific_explanation(
request: SpecificExplanationRequest,
explanation_service=Depends(get_explanation_service),
) -> SpecificExplanationResponse:
return await explanation_service.generate_specific_explanation(request)
return await ExplanationService.generate_specific_explanation(request)
7 changes: 2 additions & 5 deletions app/service/explanation_service.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from app.adapter.request_single import request_responses_output_text_async
from app.adapter.request_to_gpt import request_to_gpt_returning_text
from app.dto.request.specific_explanation_request import SpecificExplanationRequest
from app.dto.response.specific_explanation_response import SpecificExplanationResponse
from app.util.logger import logger
Expand All @@ -10,7 +10,6 @@ class ExplanationService:
async def generate_specific_explanation(
specific_explanation_request: SpecificExplanationRequest,
):

title = specific_explanation_request.title
selections = specific_explanation_request.selections

Expand Down Expand Up @@ -56,9 +55,7 @@ async def generate_specific_explanation(
}

with log_elapsed(logger, "request_specific_explanation_with_search"):
combined_text = await request_responses_output_text_async(
gpt_content, timeout=60
)
combined_text = await request_to_gpt_returning_text(gpt_content, timeout=30)
combined_text = (combined_text or "").strip()

references = []
Expand Down
Loading