Skip to content

Commit 860b3bf

Browse files
authored
Merge pull request #36 from 9git9git/SCRUM-140-BE-분석-페이지-서비스-API-개발
Scrum 140 be 분석 페이지 서비스 api 개발
2 parents 9d8090c + 97749d5 commit 860b3bf

16 files changed

Lines changed: 1870 additions & 109 deletions

app/api/v1/endpoints/analyze.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
from fastapi import APIRouter, Depends, HTTPException, status
2+
from sqlalchemy.ext.asyncio import AsyncSession
3+
from uuid import UUID
4+
from app.schemas.base import ResponseBase
5+
from app.schemas.comprehensive_evaluation import ComprehensiveEvaluationResponse
6+
from app.schemas.recommended_challenge import RecommendedChallengeResponse
7+
from app.services.analyze import (
8+
get_or_create_today_comprehensive_evaluation,
9+
get_or_create_today_recommended_challenges,
10+
)
11+
from app.db.session import get_db
12+
from typing import List
13+
14+
router = APIRouter()
15+
16+
17+
@router.get(
18+
"/today",
19+
response_model=ResponseBase[ComprehensiveEvaluationResponse],
20+
)
21+
async def get_today_analysis(
22+
user_id: UUID,
23+
db: AsyncSession = Depends(get_db),
24+
) -> ResponseBase[ComprehensiveEvaluationResponse]:
25+
"""
26+
오늘의 종합 평가를 가져오거나 생성합니다.
27+
"""
28+
try:
29+
result = await get_or_create_today_comprehensive_evaluation(db, user_id)
30+
return ResponseBase(status_code=status.HTTP_200_OK, data=result)
31+
except HTTPException as e:
32+
return ResponseBase(status_code=e.status_code, error=e.detail)
33+
except Exception as e:
34+
return ResponseBase(
35+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, error=str(e)
36+
)
37+
38+
39+
@router.get(
40+
"/today/challenges",
41+
response_model=ResponseBase[List[RecommendedChallengeResponse]],
42+
)
43+
async def get_today_challenges(
44+
user_id: UUID,
45+
db: AsyncSession = Depends(get_db),
46+
) -> ResponseBase[List[RecommendedChallengeResponse]]:
47+
"""
48+
오늘의 추천 도전과제를 가져오거나 생성합니다.
49+
"""
50+
try:
51+
result = await get_or_create_today_recommended_challenges(db, user_id)
52+
return ResponseBase(status_code=status.HTTP_200_OK, data=result)
53+
except HTTPException as e:
54+
return ResponseBase(status_code=e.status_code, error=e.detail)
55+
except Exception as e:
56+
return ResponseBase(
57+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, error=str(e)
58+
)

app/api/v1/endpoints/recommended_challenge.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,13 @@ async def get_recommended_challenge(
7272
)
7373
async def post_recommended_challenge(
7474
user_id: UUID,
75-
progress_id: UUID,
7675
category_id: UUID,
7776
challenge_data: RecommendedChallengeCreate,
7877
db: AsyncSession = Depends(get_db),
7978
) -> ResponseBase[RecommendedChallengeResponse]:
8079
try:
8180
challenge = await add_recommended_challenge(
82-
db, user_id, progress_id, category_id, challenge_data
81+
db, user_id, category_id, challenge_data
8382
)
8483
return ResponseBase(status_code=status.HTTP_201_CREATED, data=challenge)
8584
except HTTPException as e:

app/api/v1/router.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
progress,
1616
todo,
1717
main,
18+
analyze,
1819
)
1920

2021
router = APIRouter()
@@ -77,3 +78,8 @@
7778
prefix="/users/{user_id}",
7879
tags=["user_characters"], # or "characters" if you want to group together
7980
)
81+
router.include_router(
82+
analyze.router,
83+
prefix="/users/{user_id}/analyze",
84+
tags=["analyze"],
85+
)

app/core/config.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,13 @@ class Settings(BaseSettings):
1414
# 쿠키 보안 설정
1515
IS_SECURE: bool
1616

17-
model_config = ConfigDict(env_file=".env", env_file_encoding='utf-8', extra="allow")
17+
# Azure OpenAI API 설정
18+
AZURE_OPENAI_API_KEY: str
19+
AZURE_OPENAI_ENDPOINT: str
20+
AZURE_OPENAI_DEPLOYMENT_NAME: str
21+
AZURE_OPENAI_API_VERSION: str
22+
23+
model_config = ConfigDict(env_file=".env", env_file_encoding="utf-8", extra="allow")
1824

1925

2026
settings = Settings()

app/crud/recommended_challenge.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,11 @@
1717
async def create_recommended_challenge(
1818
db: AsyncSession,
1919
user_id: UUID,
20-
progress_id: UUID,
2120
category_id: UUID,
2221
challenge_data: RecommendedChallengeCreate,
2322
) -> RecommendedChallengeResponse:
2423
db_challenge = RecommendedChallenge(
2524
user_id=user_id,
26-
progress_id=progress_id,
2725
category_id=category_id,
2826
progress_rate=challenge_data.progressRate,
2927
challenge_task=challenge_data.challengeTask,

app/llm/agent_router.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
from app.llm.tools import detect_intent, detect_goal_function
22
from app.llm.goal_agents import (
3-
get_english_response, get_coding_response, get_fitness_response,
4-
get_english_question, get_coding_question,
5-
get_english_info, get_coding_info, get_fitness_info,
6-
get_english_mentalcare, get_coding_mentalcare, get_fitness_mentalcare,
3+
get_english_response,
4+
get_coding_response,
5+
get_fitness_response,
6+
get_english_question,
7+
get_coding_question,
8+
get_english_info,
9+
get_coding_info,
10+
get_fitness_info,
11+
get_english_mentalcare,
12+
get_coding_mentalcare,
13+
get_fitness_mentalcare,
714
)
815

916
VALID_GOALS = ["영어", "코딩", "운동"]

app/llm/goal_agents.py

Lines changed: 49 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
# 통합 프롬프트 템플릿
88

99

10-
general_prompt = PromptTemplate.from_template("""
10+
general_prompt = PromptTemplate.from_template(
11+
"""
1112
당신은 {goal} 분야의 따뜻하고 실용적인 전문가입니다.
1213
1314
[사용자 요청]
@@ -17,73 +18,88 @@
1718
요청 유형: {intent}
1819
1920
{extra_instruction}
20-
""")
21-
21+
"""
22+
)
2223

2324

2425
# 일반 응답 (피드백, 로드맵)
2526
def get_response(goal: str, user_input: str, intent: str) -> str:
26-
instruction = "사용자의 질문에 전문가로서 조언이나 피드백을 제공해주세요." if intent == "피드백" else \
27-
"사용자의 목표에 맞는 단계별 학습 로드맵이나 실천 계획을 제시해주세요." if intent == "로드맵" else \
28-
"질문에 대해 상황을 이해하고 적절한 조언을 제공해주세요."
27+
instruction = (
28+
"사용자의 질문에 전문가로서 조언이나 피드백을 제공해주세요."
29+
if intent == "피드백"
30+
else (
31+
"사용자의 목표에 맞는 단계별 학습 로드맵이나 실천 계획을 제시해주세요."
32+
if intent == "로드맵"
33+
else "질문에 대해 상황을 이해하고 적절한 조언을 제공해주세요."
34+
)
35+
)
36+
37+
return llm.invoke(
38+
general_prompt.format(
39+
goal=goal,
40+
user_input=user_input,
41+
intent=intent,
42+
extra_instruction=instruction,
43+
)
44+
).content.strip()
2945

30-
return llm.invoke(general_prompt.format(
31-
goal=goal,
32-
user_input=user_input,
33-
intent=intent,
34-
extra_instruction=instruction
35-
)).content.strip()
3646

3747
get_english_response = lambda u, i: get_response("영어", u, i)
3848
get_coding_response = lambda u, i: get_response("코딩", u, i)
3949
get_fitness_response = lambda u, i: get_response("운동", u, i)
4050

4151

42-
4352
# 문제 생성
4453
def get_question(goal: str, user_input: str) -> str:
4554
instruction = f"{goal} 분야의 간단한 퀴즈나 연습 문제를 생성해주세요. 상황이나 감정도 반영해주세요."
46-
return llm.invoke(general_prompt.format(
47-
goal=goal,
48-
user_input=user_input,
49-
intent="문제 생성",
50-
extra_instruction=instruction
51-
)).content.strip()
55+
return llm.invoke(
56+
general_prompt.format(
57+
goal=goal,
58+
user_input=user_input,
59+
intent="문제 생성",
60+
extra_instruction=instruction,
61+
)
62+
).content.strip()
63+
5264

5365
get_english_question = lambda u: get_question("영어", u)
5466
get_coding_question = lambda u: get_question("코딩", u)
5567

5668

57-
5869
# 정보 제공
5970
def get_info(goal: str, user_input: str) -> str:
6071
instruction = f"{goal}과 관련된 시험, 자격증, 학습 팁, 활동 정보 등을 전문가 입장에서 자세히 제공해주세요."
61-
return llm.invoke(general_prompt.format(
62-
goal=goal,
63-
user_input=user_input,
64-
intent="정보 제공",
65-
extra_instruction=instruction
66-
)).content.strip()
72+
return llm.invoke(
73+
general_prompt.format(
74+
goal=goal,
75+
user_input=user_input,
76+
intent="정보 제공",
77+
extra_instruction=instruction,
78+
)
79+
).content.strip()
80+
6781

6882
get_english_info = lambda u: get_info("영어", u)
6983
get_coding_info = lambda u: get_info("코딩", u)
7084
get_fitness_info = lambda u: get_info("운동", u)
7185

7286

73-
7487
# 멘탈케어 응답
7588
def get_mentalcare(goal: str, user_input: str) -> str:
7689
instruction = f"""
7790
당신은 {goal} 분야의 학습이나 실천 과정에서 지친 사람에게
7891
공감과 위로, 회복을 돕는 따뜻한 메시지를 전달하는 전문가입니다.
7992
현재 감정과 상황을 고려하여 부담스럽지 않게 응원해주세요.
8093
"""
81-
return llm.invoke(general_prompt.format(
82-
goal=goal,
83-
user_input=user_input,
84-
intent="멘탈케어",
85-
extra_instruction=instruction
86-
)).content.strip()
94+
return llm.invoke(
95+
general_prompt.format(
96+
goal=goal,
97+
user_input=user_input,
98+
intent="멘탈케어",
99+
extra_instruction=instruction,
100+
)
101+
).content.strip()
102+
87103

88104
get_english_mentalcare = lambda u: get_mentalcare("영어", u)
89105
get_coding_mentalcare = lambda u: get_mentalcare("코딩", u)

app/llm/llm_provider.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
21
from functools import lru_cache
32
from langchain_community.chat_models import AzureChatOpenAI
43
from app.core.config import settings
54

5+
66
@lru_cache()
77
def get_agent():
88
return AzureChatOpenAI(
99
azure_deployment=settings.AZURE_OPENAI_DEPLOYMENT_NAME,
1010
azure_endpoint=settings.AZURE_OPENAI_ENDPOINT,
1111
openai_api_key=settings.AZURE_OPENAI_API_KEY,
1212
openai_api_version=settings.AZURE_OPENAI_API_VERSION,
13-
temperature=0.5
13+
temperature=0.5,
1414
)
15-

0 commit comments

Comments
 (0)