diff --git a/backend/app/api/routes/chat.py b/backend/app/api/routes/chat.py index f200b95..e6e3e9b 100644 --- a/backend/app/api/routes/chat.py +++ b/backend/app/api/routes/chat.py @@ -6,7 +6,7 @@ from pydantic import BaseModel, Field from sse_starlette.sse import EventSourceResponse -from app.services.llm import get_english_translation, get_response_stream_async +from app.services.llm import get_english_translation, get_response_stream_async, generate_chat_title_async from app.services.embedding import embedding_service from app.services.database import get_client from app.core.rate_limit import limiter @@ -14,6 +14,8 @@ router = APIRouter() logger = logging.getLogger(__name__) +DEFAULT_CHAT_TITLE = "새로운 대화" + class HistoryMessage(BaseModel): role: str content: str @@ -22,6 +24,9 @@ class ChatRequest(BaseModel): query: str history: List[HistoryMessage] = Field(default_factory=list) +class TitleRequest(BaseModel): + query: str = Field(..., max_length=1024) + def _search_documents(query_vector): return get_client().rpc( 'match_documents', @@ -127,3 +132,31 @@ async def chat_endpoint(request: Request, chat_request: ChatRequest): Endpoint for accepting chat queries and returning a text/event-stream response. """ return EventSourceResponse(generate_chat_events(request, chat_request.query, chat_request.history)) + +@router.post("/title") +@limiter.limit("5/minute") +async def chat_title_endpoint(request: Request, title_request: TitleRequest): + """ + Endpoint for generating a short chat room title based on the first user query. + """ + query = title_request.query.strip() + if not query: + return {"title": DEFAULT_CHAT_TITLE} + + try: + title = await asyncio.wait_for(generate_chat_title_async(query), timeout=10.0) + # Handle case where LLM returns something too long or with quotes + title = title.replace('"', '').replace("'", "").strip() + if not title: + return {"title": DEFAULT_CHAT_TITLE} + MAX_TITLE_LEN = 20 + ELLIPSIS = "..." + if len(title) > MAX_TITLE_LEN: + title = title[: MAX_TITLE_LEN - len(ELLIPSIS)] + ELLIPSIS + return {"title": title} + except asyncio.TimeoutError: + logger.warning("Timeout generating chat title") + return {"title": DEFAULT_CHAT_TITLE} + except Exception: + logger.exception("Failed to generate chat title") + return {"title": DEFAULT_CHAT_TITLE} diff --git a/backend/app/services/llm.py b/backend/app/services/llm.py index c6183b2..6682f21 100644 --- a/backend/app/services/llm.py +++ b/backend/app/services/llm.py @@ -120,3 +120,19 @@ async def get_response_stream_async(context: str, query: str, history: str = "") chain = prompt | get_llm() | StrOutputParser() async for chunk in chain.astream({"context": context, "chat_history": history, "query": query}): yield chunk + +title_prompt = PromptTemplate.from_template( + """주어진 질문을 기반으로 철학적인 대화방 제목을 15자 이내로 지어줘. + 부연 설명 없이 제목만 출력해. + + 질문: {query} + 제목: """ +) + +async def generate_chat_title_async(query: str) -> str: + """ + Generates a short chat title based on the user's first query using Gemini. + """ + chain = title_prompt | get_llm() | StrOutputParser() + title = await chain.ainvoke({"query": query}) + return title.strip() diff --git a/backend/data/books_mapping.json b/backend/data/books_mapping.json index 7bd9fc8..05afbfc 100644 --- a/backend/data/books_mapping.json +++ b/backend/data/books_mapping.json @@ -17,7 +17,7 @@ "translated_author": "데이비드 흄", "aladin_data": { "title": "인간이란 무엇인가 - 오성 정념 도덕 본성론", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=4359030&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=4359030&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/435/90/coversum/8949705206_1.jpg", "author": "데이비드 흄 (지은이), 김성숙 (옮긴이)", "isbn": "9788949705200" @@ -29,7 +29,7 @@ "translated_author": "메리 울스턴크래프트", "aladin_data": { "title": "여권의 옹호", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=45690064&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=45690064&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/4569/0/coversum/8994054596_1.jpg", "author": "메리 울스턴크래프트 (지은이), 손영미 (옮긴이)", "isbn": "9788994054599" @@ -41,7 +41,7 @@ "translated_author": "프리드리히 니체", "aladin_data": { "title": "차라투스트라는 이렇게 말했다", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=454014&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=454014&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/45/40/coversum/s352934786_1.jpg", "author": "프리드리히 니체 (지은이), 장희창 (옮긴이)", "isbn": "9788937460944" @@ -59,7 +59,7 @@ "translated_author": "존 로크", "aladin_data": { "title": "존 로크의 인간 오성론 읽기", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=90592125&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=90592125&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/9059/21/coversum/k092535101_1.jpg", "author": "안병웅 (지은이)", "isbn": "9791185136318" @@ -71,7 +71,7 @@ "translated_author": "플라톤", "aladin_data": { "title": "소크라테스의 변명 - 크리톤 파이돈 향연, 문예교양선서 30", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=224035&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=224035&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/22/40/coversum/8931003714_3.jpg", "author": "플라톤 (지은이), 황문수 (옮긴이)", "isbn": "9788931003710" @@ -83,7 +83,7 @@ "translated_author": "플라톤", "aladin_data": { "title": "소크라테스의 변명·크리톤·파이돈·향연 (그리스어 원전 완역본) - 플라톤의 대화편", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=216792703&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=216792703&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/21679/27/coversum/k252636705_1.jpg", "author": "플라톤 (지은이), 박문재 (옮긴이)", "isbn": "9791190398039" @@ -95,7 +95,7 @@ "translated_author": "제임스 앨런", "aladin_data": { "title": "제임스 앨런 원인과 결과의 법칙 - 사람은 생각하는 대로 살게 된다", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=345588057&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=345588057&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/34558/80/coversum/k732933167_1.jpg", "author": "제임스 알렌 (지은이), 박선영 (옮긴이)", "isbn": "9791171177660" @@ -113,7 +113,7 @@ "translated_author": "프리드리히 니체", "aladin_data": { "title": "선악의 저편", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=174923171&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=174923171&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/17492/31/coversum/8957336117_1.jpg", "author": "프리드리히 니체 (지은이), 박찬국 (옮긴이)", "isbn": "9788957336113" @@ -125,7 +125,7 @@ "translated_author": "마르쿠스 툴리우스 키케로", "aladin_data": { "title": "투스쿨룸 대화", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=286336783&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=286336783&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/28633/67/coversum/8957337679_1.jpg", "author": "마르쿠스 툴리우스 키케로 (지은이), 김남우 (옮긴이)", "isbn": "9788957337677" @@ -137,7 +137,7 @@ "translated_author": "마르쿠스 툴리우스 키케로", "aladin_data": { "title": "키케로의 의무론 - 그의 아들에게 보낸 편지", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=852420&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=852420&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/85/24/coversum/8930606245_1.jpg", "author": "마르쿠스 툴리우스 키케로 (지은이), 허승일 (옮긴이)", "isbn": "9788930606240" @@ -149,7 +149,7 @@ "translated_author": "존 듀이", "aladin_data": { "title": "민주주의와 교육", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=922961&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=922961&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/92/29/coversum/8925400669_2.jpg", "author": "존 듀이 (지은이), 이홍우 (옮긴이)", "isbn": "9788925400662" @@ -173,7 +173,7 @@ "translated_author": "르네 데카르트", "aladin_data": { "title": "방법서설 - 이성을 잘 인도하고 학문에서 진리를 찾기 위한", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=347983217&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=347983217&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/34798/32/coversum/k152933225_1.jpg", "author": "르네 데카르트 (지은이), 이재훈 (옮긴이)", "isbn": "9791170872443" @@ -185,7 +185,7 @@ "translated_author": "프리드리히 니체", "aladin_data": { "title": "이 사람을 보라", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=302356844&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=302356844&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/30235/68/coversum/8957338195_1.jpg", "author": "프리드리히 니체 (지은이), 박찬국 (옮긴이)", "isbn": "9788957338193" @@ -197,7 +197,7 @@ "translated_author": "랠프 월도 에머슨", "aladin_data": { "title": "에머슨 수상록", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=100408&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=100408&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/10/4/coversum/8972432954_2.jpg", "author": "랄프 왈도 에머슨 (지은이)", "isbn": "9788972432951" @@ -209,7 +209,7 @@ "translated_author": "아르투어 쇼펜하우어", "aladin_data": { "title": "쇼펜하우어의 행복론과 인생론", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=308665960&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=308665960&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/30866/59/coversum/8932440093_1.jpg", "author": "아르투어 쇼펜하우어 (지은이), 홍성광 (옮긴이)", "isbn": "9788932440095" @@ -221,7 +221,7 @@ "translated_author": "베네딕투스 데 스피노자", "aladin_data": { "title": "에티카 - 개정판", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=993377&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=993377&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/99/33/coversum/8930625460_2.jpg", "author": "베네딕투스 데 스피노자 (지은이), 강영계 (옮긴이)", "isbn": "9788930625463" @@ -239,7 +239,7 @@ "translated_author": "플라톤", "aladin_data": { "title": "에우튀프론", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=272171162&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=272171162&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/27217/11/coversum/8957337342_1.jpg", "author": "플라톤 (지은이), 강성훈 (옮긴이)", "isbn": "9788957337349" @@ -251,7 +251,7 @@ "translated_author": "임마누엘 칸트", "aladin_data": { "title": "윤리형이상학 정초 - 개정2판", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=168355651&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=168355651&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/16835/56/coversum/8957336036_1.jpg", "author": "임마누엘 칸트 (지은이), 백종현 (옮긴이)", "isbn": "9788957336038" @@ -269,7 +269,7 @@ "translated_author": "플라톤", "aladin_data": { "title": "고르기아스", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=265344583&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=265344583&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/26534/45/coversum/8957337210_1.jpg", "author": "플라톤 (지은이), 김인곤 (옮긴이)", "isbn": "9788957337219" @@ -287,7 +287,7 @@ "translated_author": "프리드리히 니체", "aladin_data": { "title": "인간적인 너무나 인간적인 1", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=279599&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=279599&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/27/95/coversum/8970132619_3.jpg", "author": "프리드리히 니체 (지은이), 김미기 (옮긴이)", "isbn": "9788970132617" @@ -305,7 +305,7 @@ "translated_author": "플라톤", "aladin_data": { "title": "플라톤의 법률", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=4646467&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=4646467&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/464/64/coversum/8930606296_1.jpg", "author": "플라톤 (지은이), 박종현 (옮긴이)", "isbn": "9788930606295" @@ -317,7 +317,7 @@ "translated_author": "토마스 홉스", "aladin_data": { "title": "리바이어던 1 - 교회국가 및 시민국가의 재료와 형태 및 권력", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=2480851&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=2480851&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/248/8/coversum/s392037901_2.jpg", "author": "토마스 홉스 (지은이), 진석용 (옮긴이)", "isbn": "9788930083379" @@ -329,7 +329,7 @@ "translated_author": "마르쿠스 아우렐리우스", "aladin_data": { "title": "명상록 - 삶과 죽음을 고뇌한 어느 철학자 황제의 가장 사적인 기록", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=384592083&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=384592083&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/38459/20/coversum/k062135812_1.jpg", "author": "마르쿠스 아우렐리우스 (지은이), 정미화 (옮긴이), 그레고리 헤이스 (해제)", "isbn": "9791168273993" @@ -341,7 +341,7 @@ "translated_author": "랄프 왈도 에머슨", "aladin_data": { "title": "랄프 왈도 에머슨 : 자연", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=39251790&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=39251790&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/3925/17/coversum/8956607648_1.jpg", "author": "랄프 왈도 에머슨 (지은이), 서동석 (옮긴이)", "isbn": "9788956607641" @@ -353,7 +353,7 @@ "translated_author": "토마스 칼라일", "aladin_data": { "title": "영웅숭배론", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=313531822&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=313531822&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/31353/18/coversum/8935678147_1.jpg", "author": "토머스 칼라일 (지은이), 박상익 (옮긴이)", "isbn": "9788935678143" @@ -365,7 +365,7 @@ "translated_author": "존 스튜어트 밀", "aladin_data": { "title": "초판본 자유론 - 1859년 오리지널 초판본 표지 디자인", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=381938135&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=381938135&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/38193/81/coversum/k302034718_1.jpg", "author": "존 스튜어트 밀 (지은이), 김희상 (옮긴이)", "isbn": "9791175241961" @@ -377,7 +377,7 @@ "translated_author": "헨리 데이비드 소로", "aladin_data": { "title": "월든·시민 불복종 (합본 완역본)", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=284194464&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=284194464&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/28419/44/coversum/k742835213_1.jpg", "author": "헨리 데이비드 소로 (지은이), 이종인 (옮긴이), 허버트 웬델 글리슨 (사진)", "isbn": "9791139700503" @@ -389,7 +389,7 @@ "translated_author": "루크레티우스", "aladin_data": { "title": "사물의 본성에 관하여", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=14599483&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=14599483&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/1459/94/coversum/8957332227_1.jpg", "author": "루크레티우스 (지은이), 강대진 (옮긴이)", "isbn": "9788957332221" @@ -401,7 +401,7 @@ "translated_author": "카를 폰 클라우제비츠", "aladin_data": { "title": "전쟁론 - 전면완역개정판", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=86524117&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=86524117&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/8652/41/coversum/8961951424_1.jpg", "author": "카알 폰 클라우제비츠 (지은이), 김만수 (옮긴이)", "isbn": "9788961951425" @@ -413,7 +413,7 @@ "translated_author": "블레즈 파스칼", "aladin_data": { "title": "파스칼의 팡세", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=367576319&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=367576319&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/36757/63/coversum/k952030294_1.jpg", "author": "블레즈 파스칼 (지은이), 강현규 (엮은이), 이선미 (옮긴이)", "isbn": "9791160029529" @@ -425,7 +425,7 @@ "translated_author": "임마누엘 칸트", "aladin_data": { "title": "영구 평화론 - 하나의 철학적 기획, 개정판", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=2881780&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=2881780&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/288/17/coversum/8930610439_1.jpg", "author": "임마누엘 칸트 (지은이), 이한구 (옮긴이)", "isbn": "9788930610438" @@ -437,7 +437,7 @@ "translated_author": "플라톤", "aladin_data": { "title": "소크라테스의 변명·크리톤·파이돈·향연 (그리스어 원전 완역본) - 플라톤의 대화편", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=216792703&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=216792703&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/21679/27/coversum/k252636705_1.jpg", "author": "플라톤 (지은이), 박문재 (옮긴이)", "isbn": "9791190398039" @@ -449,7 +449,7 @@ "translated_author": "플라톤", "aladin_data": { "title": "파이드로스 - 그리스어 원전 번역판", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=1820615&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=1820615&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/182/6/coversum/8931005881_2.jpg", "author": "플라톤 (지은이), 조대호 (옮긴이)", "isbn": "9788931005882" @@ -467,7 +467,7 @@ "translated_author": "플루타르코스", "aladin_data": { "title": "플루타르코스 영웅전", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=6970308&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=6970308&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/697/3/coversum/8991290337_2.jpg", "author": "플루타르코스 (지은이), 천병희 (옮긴이)", "isbn": "9788991290334" @@ -479,7 +479,7 @@ "translated_author": "아리스토텔레스", "aladin_data": { "title": "정치학", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=4399813&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=4399813&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/439/98/coversum/8991290280_1.jpg", "author": "아리스토텔레스 (지은이), 천병희 (옮긴이)", "isbn": "9788991290280" @@ -503,7 +503,7 @@ "translated_author": "카를 구스타프 융", "aladin_data": { "title": "칼 융 무의식의 심리학", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=300205010&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=300205010&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/30020/50/coversum/k222838355_1.jpg", "author": "칼 구스타프 융 (지은이), 정명진 (옮긴이)", "isbn": "9791159201479" @@ -533,7 +533,7 @@ "translated_author": "존 로크", "aladin_data": { "title": "통치론 - 시민정부의 참된 기원, 범위 및 그 목적에 관한 시론", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=301106377&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=301106377&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/30110/63/coversum/897291780x_1.jpg", "author": "존 로크 (지은이), 강정인, 문지영 (옮긴이)", "isbn": "9788972917809" @@ -545,7 +545,7 @@ "translated_author": "헤르만 헤세", "aladin_data": { "title": "싯다르타", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=329596&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=329596&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/32/95/coversum/s062934786_1.jpg", "author": "헤르만 헤세 (지은이), 박병덕 (옮긴이)", "isbn": "9788937460586" @@ -557,7 +557,7 @@ "translated_author": "손무", "aladin_data": { "title": "손자병법 - 이겨놓고 싸우는 인생의 지혜", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=372980631&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=372980631&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/37298/6/coversum/k292031545_1.jpg", "author": "손무 (지은이), 소준섭 (옮긴이)", "isbn": "9791139728002" @@ -569,7 +569,7 @@ "translated_author": "플라톤", "aladin_data": { "title": "소크라테스의 변명·크리톤·파이돈·향연 (그리스어 원전 완역본) - 플라톤의 대화편", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=216792703&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=216792703&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/21679/27/coversum/k252636705_1.jpg", "author": "플라톤 (지은이), 박문재 (옮긴이)", "isbn": "9791190398039" @@ -587,7 +587,7 @@ "translated_author": "프리드리히 빌헬름 니체", "aladin_data": { "title": "안티크리스트", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=35425033&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=35425033&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/3542/50/coversum/8957333444_1.jpg", "author": "프리드리히 니체 (지은이), 박찬국 (옮긴이)", "isbn": "9788957333440" @@ -599,7 +599,7 @@ "translated_author": "프리드리히 빌헬름 니체", "aladin_data": { "title": "비극의 탄생", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=988511&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=988511&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/98/85/coversum/8957331077_1.jpg", "author": "프리드리히 니체 (지은이), 박찬국 (옮긴이)", "isbn": "9788957331071" @@ -611,7 +611,7 @@ "translated_author": "프리드리히 니체", "aladin_data": { "title": "우상의 황혼", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=64193963&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=64193963&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/6419/39/coversum/8957334513_1.jpg", "author": "프리드리히 니체 (지은이), 박찬국 (옮긴이)", "isbn": "9788957334515" @@ -635,7 +635,7 @@ "translated_author": "카를 마르크스, 프리드리히 엥겔스", "aladin_data": { "title": "공산당 선언", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=143257420&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=143257420&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/14325/74/coversum/k172532941_1.jpg", "author": "카를 마르크스, 프리드리히 엥겔스 (지은이), 심철민 (옮긴이)", "isbn": "9791187036548" @@ -653,7 +653,7 @@ "translated_author": "보에티우스", "aladin_data": { "title": "철학의 위안 - 완역본", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=147121964&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=147121964&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/14712/19/coversum/k002532053_2.jpg", "author": "아니키우스 보이티우스 (지은이), 박문재 (옮긴이)", "isbn": "9791187142430" @@ -665,7 +665,7 @@ "translated_author": "이마누엘 칸트", "aladin_data": { "title": "순수이성비판 1", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=669748&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=669748&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/66/97/coversum/8957330836_1.jpg", "author": "임마누엘 칸트 (지은이), 백종현 (옮긴이)", "isbn": "9788957330838" @@ -677,7 +677,7 @@ "translated_author": "에픽테토스", "aladin_data": { "title": "[POD] 에픽테토스 편람 (스토아 사상 철학자) : The Enchiridion (영어원서)", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=231320657&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=231320657&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/23132/6/coversum/k022637708_1.jpg", "author": "에픽테토스 (지은이)", "isbn": "9791127295479" @@ -695,7 +695,7 @@ "translated_author": "아르투어 쇼펜하우어", "aladin_data": { "title": "쇼펜하우어의 행복론과 인생론", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=308665960&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=308665960&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/30866/59/coversum/8932440093_1.jpg", "author": "아르투어 쇼펜하우어 (지은이), 홍성광 (옮긴이)", "isbn": "9788932440095" @@ -707,7 +707,7 @@ "translated_author": "아리스토텔레스", "aladin_data": { "title": "니코마코스 윤리학 - 그리스어 원전 번역, 개정판", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=31685631&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=31685631&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/3168/56/coversum/8991290523_3.jpg", "author": "아리스토텔레스 (지은이), 천병희 (옮긴이)", "isbn": "9788991290525" @@ -719,7 +719,7 @@ "translated_author": "프리드리히 니체", "aladin_data": { "title": "도덕의 계보", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=274647853&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=274647853&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/27464/78/coversum/8957337350_1.jpg", "author": "프리드리히 니체 (지은이), 박찬국 (옮긴이)", "isbn": "9788957337356" @@ -761,7 +761,7 @@ "translated_author": "윌리엄 블레이크", "aladin_data": { "title": "천국과 지옥의 결혼", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=141182&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=141182&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/14/11/coversum/8937418460_2.jpg", "author": "윌리엄 블레이크 (지은이), 김종철 (옮긴이)", "isbn": "9788937418464" @@ -773,7 +773,7 @@ "translated_author": "마르쿠스 아우렐리우스", "aladin_data": { "title": "명상록 - 삶과 죽음을 고뇌한 어느 철학자 황제의 가장 사적인 기록", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=384592083&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=384592083&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/38459/20/coversum/k062135812_1.jpg", "author": "마르쿠스 아우렐리우스 (지은이), 정미화 (옮긴이), 그레고리 헤이스 (해제)", "isbn": "9791168273993" @@ -785,7 +785,7 @@ "translated_author": "아리스토텔레스", "aladin_data": { "title": "아리스토텔레스 시학 (그리스어 원전 완역본)", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=265596201&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=265596201&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/26559/62/coversum/k392738937_1.jpg", "author": "아리스토텔레스 (지은이), 박문재 (옮긴이)", "isbn": "9791166812453" @@ -797,7 +797,7 @@ "translated_author": "니콜로 마키아벨리", "aladin_data": { "title": "초판본 군주론 - 오리지널 초판본 표지디자인", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=249432298&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=249432298&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/24943/22/coversum/k032632692_2.jpg", "author": "니콜로 마키아벨리 (지은이), 이시연 (옮긴이)", "isbn": "9791164453085" @@ -815,7 +815,7 @@ "translated_author": "버트런드 러셀", "aladin_data": { "title": "철학의 문제들 - 전면 개역판", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=385198660&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=385198660&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/38519/86/coversum/8961474928_1.jpg", "author": "버트런드 러셀 (지은이), 박영태 (옮긴이)", "isbn": "9788961474924" @@ -827,7 +827,7 @@ "translated_author": "칼릴 지브란", "aladin_data": { "title": "예언자", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=129499645&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=129499645&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/12949/96/coversum/k672532485_1.jpg", "author": "칼릴 지브란 (지은이), 류시화 (옮긴이)", "isbn": "9791186686294" @@ -839,7 +839,7 @@ "translated_author": "플라톤", "aladin_data": { "title": "플라톤의 국가·정체(政體) - 개정 증보판", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=16812&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=16812&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/1/68/coversum/8930606237_2.jpg", "author": "플라톤 (지은이), 박종현 (옮긴이)", "isbn": "9788930606233" @@ -851,7 +851,7 @@ "translated_author": "플라톤", "aladin_data": { "title": "플라톤의 국가·정체(政體) - 개정 증보판", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=16812&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=16812&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/1/68/coversum/8930606237_2.jpg", "author": "플라톤 (지은이), 박종현 (옮긴이)", "isbn": "9788930606233" @@ -875,7 +875,7 @@ "translated_author": "장 자크 루소", "aladin_data": { "title": "사회계약론 - 자유와 평등을 위한 약속", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=139172090&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=139172090&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/13917/20/coversum/8961672398_1.jpg", "author": "장 자크 루소 (지은이), 권혁 (옮긴이)", "isbn": "9788961672399" @@ -911,7 +911,7 @@ "translated_author": "아르투어 쇼펜하우어", "aladin_data": { "title": "의지와 표상으로서의 세계", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=95607072&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=95607072&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/9560/70/coversum/8949714221_2.jpg", "author": "아르투어 쇼펜하우어 (지은이), 권기철 (옮긴이)", "isbn": "9788949714226" @@ -923,7 +923,7 @@ "translated_author": "프리드리히 니체", "aladin_data": { "title": "차라투스트라는 이렇게 말했다", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=454014&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=454014&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/45/40/coversum/s352934786_1.jpg", "author": "프리드리히 니체 (지은이), 장희창 (옮긴이)", "isbn": "9788937460944" @@ -935,7 +935,7 @@ "translated_author": "존 스튜어트 밀", "aladin_data": { "title": "공리주의", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=243048009&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=243048009&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/24304/80/coversum/k452630592_1.jpg", "author": "존 스튜어트 밀 (지은이), 이종인 (옮긴이)", "isbn": "9791190878142" @@ -947,7 +947,7 @@ "translated_author": "토머스 모어", "aladin_data": { "title": "유토피아", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=565805&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=565805&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/56/58/coversum/8974832534_1.jpg", "author": "토머스 모어 (지은이), 나종일 (옮긴이)", "isbn": "9788974832537" @@ -959,7 +959,7 @@ "translated_author": "헨리 데이비드 소로", "aladin_data": { "title": "월든 - 완결판", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=12840843&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=12840843&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/1284/8/coversum/8956605416_3.jpg", "author": "헨리 데이비드 소로 (지은이), 강승영 (옮긴이)", "isbn": "9788956605418" @@ -971,7 +971,7 @@ "translated_author": "레프 톨스토이", "aladin_data": { "title": "예술이란 무엇인가", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=319767632&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=319767632&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/31976/76/coversum/k312834367_1.jpg", "author": "레프 니콜라예비치 톨스토이 (지은이), 이강은 (옮긴이)", "isbn": "9791166891694" @@ -983,7 +983,7 @@ "translated_author": "유향", "aladin_data": { "title": "신서 1", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=6171917&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=6171917&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/617/19/coversum/8949705818_1.jpg", "author": "유향 (지은이), 임동석 (옮긴이)", "isbn": "9788949705811" @@ -995,7 +995,7 @@ "translated_author": "고염무", "aladin_data": { "title": "원서발췌 일지록", - "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=383905503&partner=openAPI&start=api", + "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=383905503&partner=openAPI&start=api", "thumbnail": "https://image.aladin.co.kr/product/38390/55/coversum/k522135566_1.jpg", "author": "고염무 (지은이), 윤대식 (옮긴이)", "isbn": "9791143017086" diff --git a/backend/scripts/generate_book_mapping.py b/backend/scripts/generate_book_mapping.py index 6af9e45..6f905c7 100644 --- a/backend/scripts/generate_book_mapping.py +++ b/backend/scripts/generate_book_mapping.py @@ -111,11 +111,15 @@ async def translate_book_info(file_name: str) -> dict: current_key_idx += 1 else: print(f"Failed to parse LLM translation for {file_name}: {e}") - current_key_idx += 1 + break # If all keys exhausted or other error, fallback print(f"LLM Failed for {file_name}, falling back to Kyobo Search...") - return await kyobo_fallback(file_name, "") + name_without_ext = Path(file_name).stem + parts = name_without_ext.rsplit(" by ", 1) + fallback_title = parts[0].strip() + fallback_author = parts[1].strip() if len(parts) == 2 else "" + return await kyobo_fallback(fallback_title, fallback_author) async def search_aladin(title: str, author: str) -> dict: if not ALADIN_API_KEY: @@ -139,7 +143,7 @@ def fetch(): item = items[0] return { "title": item.get("title", ""), - "link": item.get("link", ""), + "link": item.get("link", "").replace("&", "&"), "thumbnail": item.get("cover", ""), "author": item.get("author", ""), "isbn": item.get("isbn13", "") diff --git a/backend/scripts/generate_sql_updates.py b/backend/scripts/generate_sql_updates.py index 369c518..427d406 100644 --- a/backend/scripts/generate_sql_updates.py +++ b/backend/scripts/generate_sql_updates.py @@ -12,9 +12,9 @@ def generate_sql(): # 1. Create a B-Tree index on the title field to make string matching instant # instead of doing a full sequential table scan sql_statements = [ + "CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_documents_book_title ON documents ((metadata->'book_info'->>'title'));\n\n", "BEGIN;\n", - "CREATE INDEX IF NOT EXISTS idx_documents_book_title ON documents ((metadata->'book_info'->>'title'));\n", - "SET statement_timeout = '120s'; -- Increase timeout to be safe\n" + "SET LOCAL statement_timeout = '120s'; -- Increase timeout to be safe for this transaction\n" ] for book in mapping_data: diff --git a/backend/scripts/update_db_metadata.py b/backend/scripts/update_db_metadata.py index bd77027..2a2bddd 100644 --- a/backend/scripts/update_db_metadata.py +++ b/backend/scripts/update_db_metadata.py @@ -85,6 +85,10 @@ def update_database(): doc_id = doc['id'] metadata = doc['metadata'] + if not isinstance(metadata, dict): + print(f"Skipping doc {doc_id}: metadata is not a dict") + continue + # The DB stores the title we want to match inside metadata->book_info->title db_title = metadata.get('book_info', {}).get('title', '') @@ -119,7 +123,7 @@ def update_doc(doc): return True except Exception as e: if attempt < max_retries - 1: - sleep(0.5 * (attempt + 1)) # Exponential backoff + sleep(0.5 * (2 ** attempt)) # Exponential backoff continue print(f"Error updating {doc['id']}: {e}") return False diff --git a/backend/update_metadata.sql b/backend/update_metadata.sql index 51b2a24..4114ba9 100644 --- a/backend/update_metadata.sql +++ b/backend/update_metadata.sql @@ -1,10 +1,10 @@ -CREATE INDEX IF NOT EXISTS idx_documents_book_title ON documents ((metadata->'book_info'->>'title')); +CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_documents_book_title ON documents ((metadata->'book_info'->>'title')); -SET statement_timeout = '120s'; -- Increase timeout to be safe - BEGIN; +SET LOCAL statement_timeout = '120s'; -- Increase timeout to be safe for this transaction + UPDATE documents SET metadata = metadata || '{"kr_title": "역설의 예산 1권", "thumbnail": "", "link": ""}'::jsonb WHERE metadata->'book_info'->>'title' = 'Korean Translation of A Budget of Paradoxes Volume I'; diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index 6d5a49a..394593f 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -3,12 +3,14 @@ import { useState, useCallback } from "react"; import { Sidebar } from "../components/sidebar/Sidebar"; import { ChatMain } from "../components/chat/ChatMain"; -import { Message } from "../types/chat"; +import { Message, DocumentMetadata } from "../types/chat"; export default function Home() { const [messages, setMessages] = useState([]); const [isSubmitting, setIsSubmitting] = useState(false); const [isSidebarOpen, setIsSidebarOpen] = useState(false); + const [chatTitle, setChatTitle] = useState("새로운 대화"); + const [activeMetadata, setActiveMetadata] = useState([]); const processLine = useCallback((line: string, eventObj: { current: string }, aiMsgId: string): boolean => { if (line.startsWith("event: ")) { @@ -67,13 +69,28 @@ export default function Home() { setMessages((prev) => [...prev, newUserMsg, placeholderAiMsg]); setIsSubmitting(true); + const isFirstMessage = messages.length === 0; + const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL || "http://localhost:8000"; + + if (isFirstMessage) { + fetch(`${baseUrl}/api/v1/chat/title`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ query: query }) + }) + .then(res => res.json()) + .then(data => { + if (data.title) setChatTitle(data.title); + }) + .catch(err => console.error("Failed to fetch title:", err)); + } + try { const historyToSend = messages.slice(-10).map(msg => ({ role: msg.role, content: msg.content })); - const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL || "http://localhost:8000"; const res = await fetch(`${baseUrl}/api/v1/chat`, { method: "POST", headers: { "Content-Type": "application/json" }, @@ -152,13 +169,19 @@ export default function Home() { return (
- setIsSidebarOpen(false)} /> + setIsSidebarOpen(false)} /> setMessages([])} + onClearChat={() => { + setMessages([]); + setChatTitle("새로운 대화"); + setActiveMetadata([]); + }} onMenuClick={() => setIsSidebarOpen(true)} + onVisibleMessageChange={setActiveMetadata} />
); diff --git a/frontend/components/chat/ChatMain.tsx b/frontend/components/chat/ChatMain.tsx index c0b87cb..c2a7ed1 100644 --- a/frontend/components/chat/ChatMain.tsx +++ b/frontend/components/chat/ChatMain.tsx @@ -4,17 +4,19 @@ import { Share, Plus, Menu } from "lucide-react"; import { useRef, useEffect, useState } from "react"; import { MessageList } from "./MessageList"; import { FloatingInput } from "./FloatingInput"; -import { Message } from "../../types/chat"; +import { Message, DocumentMetadata } from "../../types/chat"; interface ChatMainProps { messages: Message[]; + chatTitle?: string; onSendMessage: (query: string) => void; isSubmitting: boolean; onClearChat: () => void; onMenuClick?: () => void; + onVisibleMessageChange?: (meta: DocumentMetadata[]) => void; } -export function ChatMain({ messages, onSendMessage, isSubmitting, onClearChat, onMenuClick }: ChatMainProps) { +export function ChatMain({ messages, chatTitle = "새로운 대화", onSendMessage, isSubmitting, onClearChat, onMenuClick, onVisibleMessageChange }: ChatMainProps) { const messagesEndRef = useRef(null); const [shouldAutoScroll, setShouldAutoScroll] = useState(true); const [startTime, setStartTime] = useState(""); @@ -51,7 +53,7 @@ export function ChatMain({ messages, onSendMessage, isSubmitting, onClearChat, o
-

미덕에 관한 대화

+

{chatTitle}

세션 시작: {mounted ? startTime : ""}

@@ -69,7 +71,7 @@ export function ChatMain({ messages, onSendMessage, isSubmitting, onClearChat, o {/* Scrollable Message Area */}
- +
diff --git a/frontend/components/chat/FloatingInput.tsx b/frontend/components/chat/FloatingInput.tsx index de19f8f..c00ff25 100644 --- a/frontend/components/chat/FloatingInput.tsx +++ b/frontend/components/chat/FloatingInput.tsx @@ -37,7 +37,7 @@ export function FloatingInput({ onSendMessage, isSubmitting }: FloatingInputProp