Skip to content
This repository was archived by the owner on Apr 25, 2026. It is now read-only.
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
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"
1 change: 0 additions & 1 deletion app/service/explanation_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
31 changes: 16 additions & 15 deletions app/service/generate_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@

import fitz
import requests
from fastapi import HTTPException
from langchain_core.output_parsers import JsonOutputParser

from app.adapter.request_to_gpt import request_to_gpt_returning_text
from app.dto.model.problem_set import ProblemSet
from app.dto.request.generate_request import GenerateRequest
from app.dto.response.error_response import ErrorResponse
from app.dto.response.generate_response import (
GenerateResponse,
ProblemResponse,
ProblemSetDTO,
ProblemDTO, GenerateResponse,
)
from app.prompt import prompt_factory
from app.util.create_chunks import create_page_chunks
Expand Down Expand Up @@ -121,7 +121,7 @@ async def generate(generate_request: GenerateRequest):
try:
for completed_task in asyncio.as_completed(tasks):
try:
result: Optional[GenerateResponse] = await completed_task
result: Optional[ProblemSetDTO] = await completed_task

if result:
for quiz in result.quiz:
Expand All @@ -131,21 +131,22 @@ async def generate(generate_request: GenerateRequest):

except Exception as e:
logger.error(f"Task processing failed: {e}")
# 하나가 실패해도 전체 스트림을 끊지 않고 다음 퀴즈 생성을 기다림
continue
status_code = getattr(e, "status_code", 500)
yield ErrorResponse(
code=status_code, message=str(e)
).model_dump_json() + "\n"

except Exception as e:
logger.error(f"Critical streaming error: {e}")
# 여기서 에러를 던지면 클라이언트(Spring)는 연결이 끊긴 것으로 인식
raise HTTPException(status_code=500, detail="Streaming process failed")
yield ErrorResponse(code=500, message=str(e)).model_dump_json() + "\n"


async def process_single_chunk(
gpt_request: dict,
parser: JsonOutputParser,
referenced_pages: List[int],
quiz_type: str,
) -> Optional[GenerateResponse]:
gpt_request: dict,
parser: JsonOutputParser,
referenced_pages: List[int],
quiz_type: str,
) -> Optional[ProblemSetDTO]:
with log_elapsed(logger, "request_generate_quiz"):
try:
text_response = await request_to_gpt_returning_text(
Expand Down Expand Up @@ -180,7 +181,7 @@ async def process_single_chunk(
random.shuffle(selections)

problem_responses.append(
ProblemResponse(
ProblemDTO(
number=0,
title=q.get("title"),
selections=selections,
Expand All @@ -194,7 +195,7 @@ async def process_single_chunk(

except Exception as e:
logger.error(f"Chunk processing error: {e}")
return None
raise e


def _extract_filename(uploaded_url: str) -> str:
Expand Down
1 change: 0 additions & 1 deletion app/util/create_chunks.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ def create_page_chunks(
total_quiz_count: int,
max_chunk_count: int,
) -> List[ChunkInfo]:

# 청크 별 퀴즈 개수 분배
chunks: List[ChunkInfo] = []
for i in range(total_quiz_count):
Expand Down