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
20 changes: 20 additions & 0 deletions api/summary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from fastapi import APIRouter
from service.summary import call_assistant_summary, AssistantSummary
from config import settings
import requests
import json
from pydantic import ValidationError
from fastapi import HTTPException

router = APIRouter(tags=["Summary"])


@router.get("/assistant")
async def call_assistant(userId: int, roomId: str):

try:
message = call_assistant_summary(userId, roomId)
except ValidationError as e:
raise HTTPException(status_code=500, detail="Internal parsing error")

return {"message": message}
3 changes: 2 additions & 1 deletion main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from fastapi import FastAPI
from api import health, icebreaking
from api import health, icebreaking, summary

app = FastAPI(root_path="/ai")

app.include_router(health.router)
app.include_router(icebreaking.router)
app.include_router(summary.router)
165 changes: 165 additions & 0 deletions service/summary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import requests
import os
from config import settings
from pydantic import BaseModel, Field
from openai import OpenAI
from datetime import datetime
from typing import List
from dotenv import load_dotenv


class Persona(BaseModel):
id: int
name: str
relationship: str | None = ""
speech_style: str | None = ""
preferences: str | None = ""
critical_flags: List[str]


class _SummaryPeriod(BaseModel):
year: List[str]
month: List[str]
week: List[str]
day: List[str]
last_update: datetime


class Summaries(BaseModel):
health_status: _SummaryPeriod
emotional_and_mental_state: _SummaryPeriod
hobbies_and_interests: _SummaryPeriod
relationship_context: _SummaryPeriod
social_activities: _SummaryPeriod
intimacy: _SummaryPeriod


class Sender(BaseModel):
id: int
name: str


class Message(BaseModel):
roomId: str
sender: Sender
content: str
isRead: bool
createdAt: datetime
updatedAt: datetime


class AssistantSummary(BaseModel):
roomId: str
persona: List[Persona]
summaries: Summaries
today_messages: List[Message]


# 프롬프팅 반환
def user_prompt(response, userId) -> list[dict]:

# 현재 대화추천을 요청한 사용자가 누구인지 판단하는 로직
id = userId
if userId == response.persona[0].id:
speaker = response.persona[0].name
speaker_speech_style = response.persona[0].speech_style
else:
speaker = response.persona[1].name
speaker_speech_style = response.persona[1].speech_style

# open ai에 질의 메시지
user_content = f"""
### ROLE
이번 문장은 {speaker}의 말투({speaker_speech_style})로 작성한다.

### CRITICAL_FLAGS
{response.persona[0].name}의 {response.persona[0].critical_flags}
{response.persona[1].name}의 {response.persona[1].critical_flags}

### 요약
{response.summaries}

### 최근 대화
{response.today_messages}

### TASK
1) 상황 파악 → 사고 단계 기록
2) {speaker} 입장에서 한 문장 작성
3) JSON 만 출력
json
{{ "reply": "..." }}
"""
return [
{
"role": "system",
"content": "너는 한국어 어르신 대화 코치이다. 두 어르신의 건강을 고려해 한 문장으로 답한다.",
},
{"role": "user", "content": user_content},
]


### OpenAI 호출
def get_reply(response, userId) -> str:

# 프롬프트 생성성
messages = user_prompt(response, userId)

# open 인스턴스 생성
load_dotenv()
OpenAI.api_key = os.getenv("OPENAI_API_KEY")
client = OpenAI(api_key=OpenAI.api_key)

# gpt에 요청
gpt_response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
temperature=0.5,
)
return gpt_response.choices[0].message.content.strip()


# 프론트에서 대답 time out으로 추천대화 get 요청
def call_assistant_summary(userId: int, roomId: str) -> str:

# chat 서버에 요약본 요청
url = f"{settings.CHAT_SERVER_URL}/summary"
response = requests.get(url, params={"roomId": roomId})
if response.status_code == 404:
# 원하는 대로 처리 (빈 응답, 기본 메시지 반환 등)
raise ValueError(f"Summary not found for roomId={roomId}")
response.raise_for_status()

# 파싱 모듈
parsing_json_response = AssistantSummary.model_validate(response.json())

# open ai 호출 -> 추천 대화 반환
choice_response = get_reply(parsing_json_response, userId)
return choice_response # open ai content 반환


# AssistantSummary(
# roomId='6835cbb953943262d4a7e260',
# persona=[
# Persona(id='1', name='영서', relationship='', speech_style='', preferences='', critical_flags=[]),
# Persona(id='2', name='로봇', relationship='', speech_style='', preferences='', critical_flags=[])
# ],
# summaries=Summaries(
# health_status=_SummaryPeriod(year=[], month=[], week=[], day=[], last_update=datetime.datetime(2025, 5, 27, 14, 27, 6, 16)),
# emotional_and_mental_state=_SummaryPeriod(...),
# hobbies_and_interests=_SummaryPeriod(...),
# relationship_context=_SummaryPeriod(...),
# social_activities=_SummaryPeriod(...),
# intimacy=_SummaryPeriod(...)
# ),
# today_messages=[
# Message(
# roomId='6835cbb953943262d4a7e260',
# sender=Sender(id='2', name='로봇'),
# content='안녕 영서야',
# isRead=True,
# createdAt=datetime.datetime(2025, 5, 27, 14, 34, 33, 50),
# updatedAt=datetime.datetime(2025, 5, 27, 14, 34, 33, 50)
# ),
# Message(...),
# ]
# )