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
17 changes: 5 additions & 12 deletions api/summary.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
from fastapi import APIRouter
from fastapi import APIRouter, Header, HTTPException
from service.summary import call_assistant_summary
from config import settings
import requests
import json
from typing import List
from datetime import datetime
from pydantic import ValidationError
from fastapi import HTTPException
from pydantic import BaseModel, Field


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


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

async def call_assistant(roomId: str, userId: str = Header(alias="x-user-id")):
try:
message = call_assistant_summary(userId, roomId)
except ValidationError as e:
raise HTTPException(status_code=500, detail="Internal parsing error")
raise HTTPException(status_code=500, detail="Internal parsing error")

return {"message": message}
return {"message": message}
4 changes: 3 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ requests
openai==1.82.0
sentence-transformers
schedule
APScheduler>=3.11.0
APScheduler>=3.11.0
transformers==4.25.1
Flask
133 changes: 124 additions & 9 deletions service/auto.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from service.summary import (
extract_plain_reply,
get_summary,
get_quarter_summary,
AssistantSummary,
)
import requests
Expand All @@ -14,6 +15,7 @@
from openai import OpenAI
from datetime import datetime
from dotenv import load_dotenv
from transformers import pipeline

scheduler = AsyncIOScheduler()

Expand Down Expand Up @@ -52,6 +54,89 @@ def summary_scheduler(rooms):
# 채팅 서버로 요약본 업데이트
post_summary_update(room_id, gpt_response_str, parsing_json_response)

# kobart 요약 스케줄러 6시간마다 요약
def kobart_summary_message(quarter_message) -> str:
# 파이프라인 설정
pipe = pipeline(
task="text2text-generation",
model="sangsin/kobart-emotional",
tokenizer="sangsin/kobart-emotional",
device_map="auto",)

out = pipe(
quarter_message,
do_sample=False,
temperature=0.8,
max_length=128,
num_beams=5,
top_k=50,
top_p=0.9,
length_penalty=2.0,
eos_token_id=pipe.tokenizer.eos_token_id,
no_repeat_ngram_size=2,)[0]["generated_text"]
clean = out.split("\n")[0].strip()
return clean

def kobart_summary_scheduler(time: str) -> None:
# 채팅방 리스트 요청
rooms = requests.get(f"{settings.CHAT_SERVER_URL}/room/list/all", timeout=5).json()

# 모든 채팅방 순회
for room in rooms:
room_id = room["_id"]
resp = get_quarter_summary(room_id)
response_data = resp.json()

if "today_messages" not in response_data:
response_data["today_messages"] = [] #


if "quarter_messages" not in response_data:
response_data["quarter_messages"] = []
summary_obj: AssistantSummary = AssistantSummary.model_validate(response_data)

# 파싱
formatted_quarter_messages = [
f"[{msg.createdAt.strftime('%Y-%m-%d %H:%M:%S')}][{msg.sender.name}] \"{msg.content}\""
for msg in summary_obj.quarter_messages ]

if not formatted_quarter_messages:
print(f"[{room_id}] 요약할 분기 메시지가 없습니다. 요약 생략.")
quarter_summary_sentence = "이 시간대의 요약은 없습니다."
else:
long_message_string = "\n".join(formatted_quarter_messages)
# kobart에 message 전달 -> 요약본 반환
quarter_summary_sentence = kobart_summary_message(long_message_string)


if len(summary_obj.quarter) < 4:
# 현재 길이를 확인하고, 4개까지 부족한 만큼 빈 문자열로 채워 넣습니다.
summary_obj.quarter.extend([""] * (4 - len(summary_obj.quarter)))


# 하루의 몇 번째 요청인지 확인하는 로직
if time == "first":
summary_obj.quarter[0] = quarter_summary_sentence
elif time == "second":
summary_obj.quarter[1] = quarter_summary_sentence
elif time == "third":
summary_obj.quarter[2] = quarter_summary_sentence
elif time == "fourth":
summary_obj.quarter[3] = quarter_summary_sentence

# 6시간 요약 업데이트
body = {
"roomId": summary_obj.roomId,
"persona": [p.model_dump() for p in summary_obj.persona],
"summaries": summary_obj.summaries.model_dump(mode='json'),
"quarter" : summary_obj.quarter
}
requests.post(
f"{settings.CHAT_SERVER_URL}/summary/update", json=body, timeout=5
).raise_for_status()
print(f"[{room_id}] kobart 요약 업데이트 완료")



# 매일 채팅방 업데이트 post 함수
def post_summary_update(
Expand Down Expand Up @@ -88,18 +173,18 @@ def summary_prompt(response) -> list[dict]:
2. hobbies_and_interests
3. emotional_and_mental_state
4. relationship_context
5. social_activitie
5. social_activity
6. intimacy

### 요약
{response.summaries}

### 오늘 나눈 대화 내역
{response.today_messages}
### 오늘 나눈 대화 요약 내역
{response.quarter}


### TASK
1) 오늘 나눈 대화 내역을 읽고, 6개의 주제에 대한 내용을 추출하여 한 문장으로 요약한다.
1) 오늘 나눈 대화 요약 내역을 읽고, 6개의 주제에 대한 내용을 추출하여 한 문장으로 요약한다.
2) 만일 일부 주제에 대한 내용을 추출할 내용을 없을 시에는 "주제에 대한 대화 없음"이라고 저장한다.
3) 반드시 기존에 있는 요약 내용에 추가하는 것이다. 대답할 때 오늘 나눈 대화 내역에서 각 주제에 추출한 내용을 이미 있는 내용에 day리스트 맨 뒤에 추가만 할 것.
4) {datetime.now()}가 현재 시각임으로 last_update에 반영할 것.
Expand All @@ -118,6 +203,8 @@ def summary_prompt(response) -> list[dict]:
]




def weekly_summary_scheduler() -> None:
rooms = requests.get(f"{settings.CHAT_SERVER_URL}/room/list/all", timeout=5).json()

Expand Down Expand Up @@ -145,7 +232,7 @@ def weekly_summary_scheduler() -> None:
지난 일주일({week_start} ~ {week_end}) 각 주제별로 day 리스트에 쌓인 항목을 읽고,
각 주제에 대해 한 문장으로 요약해라라
- 입력 데이터(딕셔너리):
{{

"""
for t in topics:
day_list = getattr(summary_obj.summaries, t).day
Expand Down Expand Up @@ -197,8 +284,6 @@ def monthly_summary_scheduler() -> None:
week 리스트는 비웁니다.
"""
rooms = requests.get(f"{settings.CHAT_SERVER_URL}/room/list/all", timeout=5).json()

# 오늘의 월 레이블 (예: 5월)
today = datetime.now()
month_label = f"{today.month}월"

Expand Down Expand Up @@ -328,31 +413,61 @@ def yearly_summary_scheduler() -> None:
"daily_summary_job",
fetch_roomList,
dict(hour=2, minute=0, second=0),
(),
), # 매일 02:00:00
(
"weekly_summary_job",
weekly_summary_scheduler,
dict(day_of_week="mon", hour=3, minute=0),
(),
), # 매주 월 03:00
(
"monthly_summary_job",
monthly_summary_scheduler,
dict(day="1", hour=4, minute=0),
(),
), # 매달 1일 04:00
(
"yearly_summary_job",
yearly_summary_scheduler,
dict(month="1", day="1", hour=5, minute=0),
(),
), # 매년 1월1일 05:00


(
"kobart_first_summary_job",
kobart_summary_scheduler,
dict(hour=20, minute=24, second=0),
("first",),
), # 매일 23:55:00
(
"kobart_second_summary_job",
kobart_summary_scheduler,
dict(hour=5, minute=55, second=0),
("second",),
), # 매일 05:55:00
(
"kobart_third_summary_job",
kobart_summary_scheduler,
dict(hour=11, minute=55, second=0),
("third",),
), # 매일 11:55:00
(
"kobart_fourth_summary_job",
kobart_summary_scheduler,
dict(hour=17, minute=55, second=0),
("fourth",),
), # 매일 17:55:00
]

for job_id, func, cron_kwargs in jobs:
for job_id, func, cron_kwargs, args_tuple in jobs: # args_tuple 추가
trigger = CronTrigger(**cron_kwargs)
scheduler.add_job(
func,
trigger,
id=job_id,
args=args_tuple, # <-- 여기에 args 전달
replace_existing=True,
)

scheduler.start()
Empty file.
26 changes: 21 additions & 5 deletions service/summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import os
import re
import json
import time
from config import settings
from pydantic import BaseModel, Field
from openai import OpenAI
Expand All @@ -16,7 +15,7 @@ class Persona(BaseModel):
name: str
relationship: str | None = ""
speech_style: str | None = ""
preferences: str | None = ""
preferences: List[str]
critical_flags: List[str]


Expand Down Expand Up @@ -56,6 +55,8 @@ class AssistantSummary(BaseModel):
persona: List[Persona]
summaries: Summaries
today_messages: List[Message]
quarter_messages: List[Message] = None
quarter: List[str]


def extract_plain_reply(messages: str) -> str:
Expand Down Expand Up @@ -103,7 +104,7 @@ def user_prompt(response, userId) -> list[dict]:
### TASK
1) 상황 파악 → 사고 단계 기록
2) 반드시 요약에서 2개의 주제 "hobbies_and_interests"와 "relationship_context"를 먼저 파악하라.
3) 최근 대화에 가장 많은 비중을 두어 {speaker} 입장에서 마지막 발화에 자연스럽게 이어질 **한 문장**을 작성해야 한다.
3) 최근 대화에 가장 많은 비중을 두어 {speaker} 입장에서 마지막 발화에 자연스럽게 이어질 한 문장을 작성해야 한다.
3) 나머지 요약 내용도 최근 대화에 필요한 내용이라면 참고하여 최고의 한 문장으로 작성해라.
4) 문장을 총 7개 제안 → 셀프체크 후 3개 → 최종 1개 제안
5) JSON 만 출력
Expand All @@ -113,7 +114,7 @@ def user_prompt(response, userId) -> list[dict]:
return [
{
"role": "system",
"content": "너는 한국어 어르신 대화 코치이다. 두 어르신의 건강을 고려해 한 문장으로 답한다.",
"content": "너는 한국어 어르신 대화 코치이다. 두 어르신의 인간 관계를 고려하고 최근 대화에 큰 비중을 고려해 한 문장으로 답하자",
},
{"role": "user", "content": user_content},
]
Expand Down Expand Up @@ -144,6 +145,19 @@ def get_summary(roomId: str) -> requests.Response:
# chat 서버에 요약본 요청
url = f"{settings.CHAT_SERVER_URL}/summary"
response = requests.get(url, params={"roomId": roomId})
print(response)
if response.status_code == 404:
# 원하는 대로 처리 (빈 응답, 기본 메시지 반환 등)
raise ValueError(f"Summary not found for roomId={roomId}")
response.raise_for_status()

return response


def get_quarter_summary(roomId: str) -> requests.Response:
# chat 서버에 요약본 요청
url = f"{settings.CHAT_SERVER_URL}/summary/quarter"
response = requests.get(url, params={"roomId": roomId})
if response.status_code == 404:
# 원하는 대로 처리 (빈 응답, 기본 메시지 반환 등)
raise ValueError(f"Summary not found for roomId={roomId}")
Expand All @@ -153,10 +167,12 @@ def get_summary(roomId: str) -> requests.Response:


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

# chat 서버에 요약본 요청

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

Expand Down
Empty file added service/test2.py
Empty file.