Skip to content

Commit e4f73cb

Browse files
authored
Merge pull request #22 from KKU-NoteFlow/feat/llm
summary
2 parents 11ea903 + 867ff79 commit e4f73cb

File tree

7 files changed

+697
-31
lines changed

7 files changed

+697
-31
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# Makefile
22
run:
3-
uvicorn main:app --host 0.0.0.0 --port 8080 --reload --http h11
3+
uvicorn main:app --host 0.0.0.0 --port 8080 --reload --http h11

models/checklist.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from sqlalchemy import Column, Integer, String, Enum, TIMESTAMP, text
2+
from sqlalchemy.orm import relationship
3+
4+
class User(Base):
5+
__tablename__ = "user"
6+
7+
u_id = Column(Integer, primary_key=True, autoincrement=True) # PK
8+
id = Column(String(50), unique=True, nullable=False) # 로그인 ID
9+
email = Column(String(150), unique=True, nullable=False)
10+
password = Column(String(255), nullable=False)
11+
provider = Column(
12+
Enum("local", "google", "kakao", "naver", name="provider_enum"),
13+
nullable=False,
14+
server_default="local",
15+
)
16+
created_at = Column(
17+
TIMESTAMP, nullable=False, server_default=text("CURRENT_TIMESTAMP")
18+
)
19+
updated_at = Column(
20+
TIMESTAMP,
21+
nullable=False,
22+
server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
23+
)
24+
25+
# 역참조: checklist 목록
26+
checklists = relationship("Checklist", back_populates="user", cascade="all, delete-orphan")

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@ langchain-community
3131
langchain-core
3232
langchain-openai
3333
langchain-ollama
34+
bitsandbytes

routers/checklist.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# 변경/설명:
2+
# - POST /checklists : 생성 전용
3+
# - PATCH /checklists/{id}/clear : is_clear 0/1 설정
4+
# - get_current_user는 user.u_id 제공 가정
5+
from fastapi import APIRouter, Depends, HTTPException, status
6+
from sqlalchemy.orm import Session
7+
8+
from app.schemas.checklist import ChecklistCreate, ChecklistClearUpdate
9+
from app.models import Checklist # Checklist(u_id, checklist_title, is_clear)
10+
from app.dependencies import get_db, get_current_user
11+
12+
router = APIRouter(prefix="/checklists", tags=["checklists"])
13+
14+
@router.post("", status_code=status.HTTP_201_CREATED)
15+
def create_checklist(
16+
req: ChecklistCreate,
17+
db: Session = Depends(get_db),
18+
user=Depends(get_current_user),
19+
):
20+
obj = Checklist(
21+
u_id=user.u_id, # ← 프로젝트의 사용자 키에 맞게
22+
checklist_title=req.checklist_title,
23+
is_clear=0 # 기본 0(미완)
24+
)
25+
db.add(obj)
26+
db.commit()
27+
db.refresh(obj)
28+
return {"id": obj.id, "checklist_title": obj.checklist_title, "is_clear": obj.is_clear}
29+
30+
@router.patch("/{checklist_id}/clear")
31+
def set_clear_state(
32+
checklist_id: int,
33+
req: ChecklistClearUpdate, # {"is_clear": 0 | 1}
34+
db: Session = Depends(get_db),
35+
user=Depends(get_current_user),
36+
):
37+
obj = (
38+
db.query(Checklist)
39+
.filter(Checklist.id == checklist_id, Checklist.u_id == user.u_id)
40+
.first()
41+
)
42+
if not obj:
43+
raise HTTPException(status_code=404, detail="Checklist not found")
44+
obj.is_clear = int(req.is_clear) # 0/1 저장
45+
db.commit()
46+
db.refresh(obj)
47+
return {"id": obj.id, "is_clear": obj.is_clear}

routers/note.py

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import os
22
from dotenv import load_dotenv
3-
from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks
3+
from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks, Query
44
from fastapi.responses import StreamingResponse
55
from sqlalchemy.orm import Session
66
from typing import List
@@ -145,20 +145,33 @@ def toggle_favorite(
145145
return note
146146

147147
def save_summary(note_id: int, text: str):
148+
"""Deprecated: kept for reference. No longer overwrites original note."""
148149
db2 = SessionLocal()
149150
try:
150151
tgt = db2.query(Note).filter(Note.id == note_id).first()
151-
if tgt:
152-
tgt.content = text
153-
tgt.updated_at = datetime.utcnow()
154-
db2.commit()
152+
if not tgt:
153+
return
154+
# Create a new note in the same folder with title '<original>요약'
155+
title = (tgt.title or "").strip() + "요약"
156+
if len(title) > 255:
157+
title = title[:255]
158+
new_note = Note(
159+
user_id=tgt.user_id,
160+
folder_id=tgt.folder_id,
161+
title=title,
162+
content=text,
163+
)
164+
db2.add(new_note)
165+
db2.commit()
155166
finally:
156167
db2.close()
157168

158169
@router.post("/notes/{note_id}/summarize")
159170
async def summarize_stream_langchain(
160171
note_id: int,
161172
background_tasks: BackgroundTasks,
173+
domain: str | None = Query(default=None, description="meeting | code | paper | general | auto(None)"),
174+
longdoc: bool = Query(default=True, description="Enable long-document map→reduce"),
162175
db: Session = Depends(get_db),
163176
user = Depends(get_current_user)
164177
):
@@ -168,15 +181,32 @@ async def summarize_stream_langchain(
168181

169182
async def event_gen():
170183
parts = []
171-
async for sse in stream_summary_with_langchain(note.content):
184+
async for sse in stream_summary_with_langchain(note.content, domain=domain, longdoc=longdoc):
172185
parts.append(sse.removeprefix("data: ").strip())
173186
yield sse.encode()
174187
full = "".join(parts).strip()
175188
if full:
176-
background_tasks.add_task(save_summary, note.id, full)
189+
# Create a new summary note in the same folder with title '<original>요약'
190+
title = (note.title or "").strip() + "요약"
191+
if len(title) > 255:
192+
title = title[:255]
193+
new_note = Note(
194+
user_id=user.u_id,
195+
folder_id=note.folder_id,
196+
title=title,
197+
content=full,
198+
)
199+
db.add(new_note)
200+
db.commit()
201+
db.refresh(new_note)
202+
try:
203+
# Optional: notify created note id
204+
yield f"data: SUMMARY_NOTE_ID:{new_note.id}\n\n".encode()
205+
except Exception:
206+
pass
177207

178208
return StreamingResponse(
179209
event_gen(),
180210
media_type="text/event-stream",
181211
headers={"Cache-Control": "no-cache"}
182-
)
212+
)

schemas/checklist.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
from pydantic import BaseModel, conint
3+
4+
class ChecklistCreate(BaseModel):
5+
checklist_title: str
6+
7+
class ChecklistClearUpdate(BaseModel):
8+
is_clear: conint(ge=0, le=1) # 0 또는 1만 허용

0 commit comments

Comments
 (0)