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
4 changes: 3 additions & 1 deletion ai/src/ai_server/model/messages/feedback.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ class GenerateFeedbackRequest(BaseModel):
mode: InterviewMode
job_category: Literal["FRONTEND", "BACKEND", "INFRA", "DBA"]
total_question_count: int | None = None
end_reason: Literal["USER_REQUEST", "MAX_QUESTIONS_REACHED"] | None = None
# 종료 사유(USER_REQUEST/MAX_QUESTIONS_REACHED/POOL_EXHAUSTED 등). 프롬프트 컨텍스트로만
# 쓰이므로 자유 문자열로 둬 Core 가 사유를 추가해도 피드백 파싱이 깨지지 않게 한다.
end_reason: str | None = None
messages: list[FeedbackMessageItem] = Field(default_factory=list)
context_document_ids: list[int] = Field(default_factory=list)
voice_analysis_summary: VoiceAnalysisSummary | None = None
Expand Down
23 changes: 22 additions & 1 deletion ai/tests/test_feedback_consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def _envelope(
*,
context_documents: list[int] | None = None,
voice_analysis_summary: dict | None = None,
end_reason: str = "MAX_QUESTIONS_REACHED",
) -> bytes:
env = {
"messageId": "fb-1",
Expand All @@ -65,7 +66,7 @@ def _envelope(
"mode": "TECHNICAL",
"jobCategory": "BACKEND",
"totalQuestionCount": 2,
"endReason": "MAX_QUESTIONS_REACHED",
"endReason": end_reason,
"messages": [
{
"id": 100,
Expand Down Expand Up @@ -131,6 +132,26 @@ async def test_consumer_generates_feedback_and_publishes_callback():
assert publisher.publish.await_args.kwargs["message_type"] == "callback.feedback"


@pytest.mark.asyncio
async def test_consumer_accepts_pool_exhausted_end_reason():
# 회귀: Core 가 POOL_EXHAUSTED 로 종료해도 파싱 실패(DLQ) 없이 피드백 생성돼야 한다.
generator = _generator()
publisher = MagicMock()
publisher.publish = AsyncMock()
consumer = FeedbackConsumer(
generator=generator,
publisher=publisher,
idempotency=LruIdempotencyStore(max_size=10),
callback_routing_key="callback.feedback",
core_client=MagicMock(),
embedder=None,
)
await consumer.handle(_StubMessage(_envelope(end_reason="POOL_EXHAUSTED")))

generator.generate.assert_awaited_once()
publisher.publish.assert_awaited_once()


@pytest.mark.asyncio
async def test_consumer_calls_rag_when_documents_and_embedder_present():
generator = _generator()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.stackup.stackup.session.application.event;

// 세션 종료(COMPLETED 전이) commit 후 발화. FeedbackRequester 가 수신.
// 사유: USER_REQUEST | MAX_QUESTIONS_REACHED
// 사유: USER_REQUEST | MAX_QUESTIONS_REACHED | POOL_EXHAUSTED
public record SessionEndedEvent(
Long userId,
Long sessionId,
Expand Down