gbrabbit's picture
Auto commit at 25-2025-08 23:59:36
b4bf4f7
"""
Document processing and RAG router for Lily LLM API
"""
from fastapi import APIRouter, HTTPException, UploadFile, File, Form
from typing import Optional, List
import logging
import time
import os
import uuid
from ...models.schemas import (
DocumentUploadResponse, RAGQueryRequest, RAGQueryResponse,
DocumentProcessResponse, MultimodalRAGResponse
)
from ...services.session_registry import set_user_for_room, set_flag_for_room, set_last_document_for_room
logger = logging.getLogger(__name__)
router = APIRouter()
@router.post("/document/upload", response_model=DocumentUploadResponse)
async def upload_document(
file: UploadFile = File(...),
user_id: str = Form("anonymous"),
room_id: str = Form("default")
):
"""๋ฌธ์„œ ์—…๋กœ๋“œ ๋ฐ ์ฒ˜๋ฆฌ"""
try:
start_time = time.time()
# ํŒŒ์ผ ์ฝ๊ธฐ ๋ฐ ์ž„์‹œ ์ €์žฅ (ํŒŒ์ผ ๊ฒฝ๋กœ ๊ธฐ๋ฐ˜ ์ฒ˜๋ฆฌ๊ธฐ ํ˜ธํ™˜)
content = await file.read()
filename = file.filename
temp_dir = os.path.join("data", "uploads")
os.makedirs(temp_dir, exist_ok=True)
temp_name = f"{int(time.time()*1000)}_{uuid.uuid4().hex}_{filename}"
temp_path = os.path.join(temp_dir, temp_name)
with open(temp_path, "wb") as f:
f.write(content)
# ๋ฌธ์„œ ์ฒ˜๋ฆฌ๊ธฐ ์‚ฌ์šฉ (์šฐ์„  RAG์— ์ €์žฅ ํฌํ•จ ๊ฒฝ๋กœ)
try:
from lily_llm_core.rag_processor import rag_processor
document_id = f"doc_{int(time.time()*1000)}_{uuid.uuid4().hex}"
result = rag_processor.process_and_store_document(
user_id=user_id,
document_id=document_id,
file_path=temp_path,
)
# ์—…๋กœ๋“œ ์‹œ ๋ฐฉ-์‚ฌ์šฉ์ž ๋งคํ•‘ ์ €์žฅ (ํ›„์† ์ƒ์„ฑ์—์„œ ์ž๋™ ๋ณด์ •)
try:
set_user_for_room(room_id, user_id)
except Exception:
pass
if result.get("success"):
processing_time = time.time() - start_time
# ์—…๋กœ๋“œ ์งํ›„, ๊ฐ™์€ ๋ฐฉ์—์„œ ๋‹ค์Œ 1ํšŒ ์ƒ์„ฑ์€ ์ด๋ฏธ์ง€ ๋ณต๊ตฌ๋ฅผ ํ—ˆ์šฉ
try:
set_flag_for_room(room_id, "use_rag_images_once", True)
except Exception:
pass
# ๋ฐฉ๋ณ„ ๋งˆ์ง€๋ง‰ ๋ฌธ์„œID ๊ธฐ๋ก (ํ›„์† ์š”์ฒญ์—์„œ ๊ธฐ๋ณธ ๋ฌธ์„œ ์„ ํƒ)
try:
set_last_document_for_room(room_id, result.get("document_id", document_id))
except Exception:
pass
return DocumentUploadResponse(
success=True,
document_id=result.get("document_id", document_id),
message="๋ฌธ์„œ ์—…๋กœ๋“œ ๋ฐ ์ฒ˜๋ฆฌ ์™„๋ฃŒ",
chunks=result.get("chunks", 0),
latex_count=result.get("latex_count", 0),
auto_response=result.get("auto_response")
)
else:
return DocumentUploadResponse(
success=False,
document_id="",
message="๋ฌธ์„œ ์ฒ˜๋ฆฌ ์‹คํŒจ",
error=result.get("error", "Unknown error")
)
except ImportError:
# ํด๋ฐฑ: ์ˆœ์ˆ˜ ๋ฌธ์„œ ํŒŒ์„œ๋กœ ์ฒ˜๋ฆฌ๋งŒ ์ˆ˜ํ–‰
try:
from lily_llm_core.document_processor import document_processor
docs = document_processor.process_document(temp_path)
processing_time = time.time() - start_time
return DocumentUploadResponse(
success=True,
document_id="",
message="๋ฌธ์„œ ์—…๋กœ๋“œ ๋ฐ ์ฒ˜๋ฆฌ ์™„๋ฃŒ (๋ฒกํ„ฐ ์ €์žฅ ๋ฏธ์ˆ˜ํ–‰)",
chunks=len(docs) if docs else 0,
latex_count=0,
auto_response=None
)
except Exception as e:
return DocumentUploadResponse(
success=False,
document_id="",
message="๋ฌธ์„œ ์ฒ˜๋ฆฌ๊ธฐ import ์‹คํŒจ",
error=str(e)
)
except Exception as e:
logger.error(f"๋ฌธ์„œ ์—…๋กœ๋“œ ์‹คํŒจ: {e}")
return DocumentUploadResponse(
success=False,
document_id="",
message="๋ฌธ์„œ ์—…๋กœ๋“œ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ",
error=str(e)
)
@router.post("/document/upload/", response_model=DocumentUploadResponse, include_in_schema=False)
async def upload_document_trailing_slash(
file: UploadFile = File(...),
user_id: str = Form("anonymous"),
room_id: str = Form("default")
):
# ์Šฌ๋ž˜์‹œ ์œ ๋ฌด ๋ชจ๋‘ ํ—ˆ์šฉ (FastAPI 307 ๋ฆฌ๋””๋ ‰์…˜ ํšŒํ”ผ)
return await upload_document(file=file, user_id=user_id, room_id=room_id)
@router.post("/rag/query", response_model=RAGQueryResponse)
async def rag_query(
query: str = Form(...),
user_id: str = Form("anonymous"),
room_id: str = Form("default"),
max_results: int = Form(5),
include_sources: bool = Form(True)
):
"""RAG ์ฟผ๋ฆฌ ์ฒ˜๋ฆฌ"""
try:
start_time = time.time()
try:
from lily_llm_core.rag_processor import rag_processor
# RAG ์ฟผ๋ฆฌ ์‹คํ–‰
result = rag_processor.query(
query=query,
user_id=user_id,
room_id=room_id,
max_results=max_results,
include_sources=include_sources
)
if result.get("success"):
processing_time = time.time() - start_time
return RAGQueryResponse(
success=True,
response=result.get("response", ""),
sources=result.get("sources", []),
search_results=len(result.get("sources", [])),
processing_time=processing_time
)
else:
return RAGQueryResponse(
success=False,
response="",
sources=[],
search_results=0,
processing_time=0,
error=result.get("error", "RAG ์ฟผ๋ฆฌ ์‹คํŒจ")
)
except ImportError:
return RAGQueryResponse(
success=False,
response="",
sources=[],
search_results=0,
processing_time=0,
error="RAG processor not available"
)
except Exception as e:
logger.error(f"RAG ์ฟผ๋ฆฌ ์‹คํŒจ: {e}")
return RAGQueryResponse(
success=False,
response="",
sources=[],
search_results=0,
processing_time=0,
error=str(e)
)
@router.post("/rag/generate", response_model=RAGQueryResponse)
async def rag_generate(
prompt: str = Form(...),
user_id: str = Form("anonymous"),
room_id: str = Form("default"),
max_results: int = Form(5)
):
"""RAG ๊ธฐ๋ฐ˜ ํ…์ŠคํŠธ ์ƒ์„ฑ"""
try:
start_time = time.time()
try:
from lily_llm_core.rag_processor import rag_processor
# RAG ์ƒ์„ฑ ์‹คํ–‰
result = rag_processor.generate_with_context(
prompt=prompt,
user_id=user_id,
room_id=room_id,
max_results=max_results
)
if result.get("success"):
processing_time = time.time() - start_time
return RAGQueryResponse(
success=True,
response=result.get("response", ""),
sources=result.get("sources", []),
search_results=len(result.get("sources", [])),
processing_time=processing_time
)
else:
return RAGQueryResponse(
success=False,
response="",
sources=[],
search_results=0,
processing_time=0,
error=result.get("error", "RAG ์ƒ์„ฑ ์‹คํŒจ")
)
except ImportError:
return RAGQueryResponse(
success=False,
response="",
sources=[],
search_results=0,
processing_time=0,
error="RAG processor not available"
)
except Exception as e:
logger.error(f"RAG ์ƒ์„ฑ ์‹คํŒจ: {e}")
return RAGQueryResponse(
success=False,
response="",
sources=[],
search_results=0,
processing_time=0,
error=str(e)
)
@router.post("/rag/summary")
async def generate_rag_summary(
user_id: str = Form("anonymous"),
room_id: str = Form("default")
):
"""RAG ๋ฌธ์„œ ์š”์•ฝ ์ƒ์„ฑ"""
try:
try:
from lily_llm_core.rag_processor import rag_processor
# RAG ์š”์•ฝ ์ƒ์„ฑ
result = rag_processor.generate_summary(
user_id=user_id,
room_id=room_id
)
if result.get("success"):
return {"status": "success", "summary": result.get("summary", "")}
else:
raise HTTPException(status_code=500, detail=result.get("error", "RAG ์š”์•ฝ ์ƒ์„ฑ ์‹คํŒจ"))
except ImportError:
raise HTTPException(status_code=500, detail="RAG processor not available")
except Exception as e:
logger.error(f"RAG ์š”์•ฝ ์ƒ์„ฑ ์‹คํŒจ: {e}")
raise HTTPException(status_code=500, detail=f"RAG ์š”์•ฝ ์ƒ์„ฑ ์‹คํŒจ: {str(e)}")
@router.post("/rag/clear")
async def clear_rag_context(
user_id: str = Form("anonymous"),
room_id: str = Form("default")
):
"""RAG ์ปจํ…์ŠคํŠธ ์ •๋ฆฌ"""
try:
try:
from lily_llm_core.rag_processor import rag_processor
# RAG ์ปจํ…์ŠคํŠธ ์ •๋ฆฌ
success = rag_processor.clear_context(
user_id=user_id,
room_id=room_id
)
if success:
return {"status": "success", "message": "RAG ์ปจํ…์ŠคํŠธ ์ •๋ฆฌ ์™„๋ฃŒ"}
else:
raise HTTPException(status_code=500, detail="RAG ์ปจํ…์ŠคํŠธ ์ •๋ฆฌ ์‹คํŒจ")
except ImportError:
raise HTTPException(status_code=500, detail="RAG processor not available")
except Exception as e:
logger.error(f"RAG ์ปจํ…์ŠคํŠธ ์ •๋ฆฌ ์‹คํŒจ: {e}")
raise HTTPException(status_code=500, detail=f"RAG ์ปจํ…์ŠคํŠธ ์ •๋ฆฌ ์‹คํŒจ: {str(e)}")
@router.post("/rag/batch-process")
async def batch_process_documents(
files: List[UploadFile] = File(...),
user_id: str = Form("anonymous"),
room_id: str = Form("default")
):
"""์—ฌ๋Ÿฌ ๋ฌธ์„œ ์ผ๊ด„ ์ฒ˜๋ฆฌ"""
try:
start_time = time.time()
results = []
try:
from lily_llm_core.rag_processor import rag_processor
for file in files:
content = await file.read()
filename = file.filename
# ์ž„์‹œ ์ €์žฅ ํ›„ RAG์— ์ €์žฅ ํฌํ•จ ์ฒ˜๋ฆฌ
temp_dir = os.path.join("data", "uploads")
os.makedirs(temp_dir, exist_ok=True)
temp_name = f"{int(time.time()*1000)}_{uuid.uuid4().hex}_{filename}"
temp_path = os.path.join(temp_dir, temp_name)
with open(temp_path, "wb") as f:
f.write(content)
document_id = f"doc_{int(time.time()*1000)}_{uuid.uuid4().hex}"
result = rag_processor.process_and_store_document(
user_id=user_id,
document_id=document_id,
file_path=temp_path,
)
results.append({
"filename": filename,
"success": result.get("success", False),
"document_id": result.get("document_id", document_id),
"chunks": result.get("chunks", 0),
"error": result.get("error")
})
processing_time = time.time() - start_time
return {
"status": "success",
"results": results,
"total_files": len(files),
"processing_time": processing_time
}
except ImportError:
# ํด๋ฐฑ: ์ €์žฅ ์—†์ด ์ฒ˜๋ฆฌ๋งŒ ์ˆ˜ํ–‰
try:
from lily_llm_core.document_processor import document_processor
for file in files:
content = await file.read()
filename = file.filename
temp_dir = os.path.join("data", "uploads")
os.makedirs(temp_dir, exist_ok=True)
temp_name = f"{int(time.time()*1000)}_{uuid.uuid4().hex}_{filename}"
temp_path = os.path.join(temp_dir, temp_name)
with open(temp_path, "wb") as f:
f.write(content)
docs = document_processor.process_document(temp_path)
results.append({
"filename": filename,
"success": bool(docs),
"document_id": "",
"chunks": len(docs) if docs else 0,
"error": None if docs else "processing failed"
})
processing_time = time.time() - start_time
return {
"status": "success",
"results": results,
"total_files": len(files),
"processing_time": processing_time
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
except Exception as e:
logger.error(f"์ผ๊ด„ ๋ฌธ์„œ ์ฒ˜๋ฆฌ ์‹คํŒจ: {e}")
raise HTTPException(status_code=500, detail=f"์ผ๊ด„ ๋ฌธ์„œ ์ฒ˜๋ฆฌ ์‹คํŒจ: {str(e)}")
@router.get("/rag/search-history")
async def search_rag_history(
user_id: str = "anonymous",
room_id: str = "default",
query: str = "",
limit: int = 10
):
"""RAG ๊ฒ€์ƒ‰ ํžˆ์Šคํ† ๋ฆฌ ์กฐํšŒ"""
try:
try:
from lily_llm_core.rag_processor import rag_processor
# RAG ๊ฒ€์ƒ‰ ํžˆ์Šคํ† ๋ฆฌ ์กฐํšŒ
history = rag_processor.get_search_history(
user_id=user_id,
room_id=room_id,
query=query,
limit=limit
)
return {"status": "success", "history": history}
except ImportError:
raise HTTPException(status_code=500, detail="RAG processor not available")
except Exception as e:
logger.error(f"RAG ๊ฒ€์ƒ‰ ํžˆ์Šคํ† ๋ฆฌ ์กฐํšŒ ์‹คํŒจ: {e}")
raise HTTPException(status_code=500, detail=f"RAG ๊ฒ€์ƒ‰ ํžˆ์Šคํ† ๋ฆฌ ์กฐํšŒ ์‹คํŒจ: {str(e)}")
@router.post("/multimodal-rag/upload")
async def upload_multimodal_document(
file: UploadFile = File(...),
user_id: str = Form("anonymous"),
room_id: str = Form("default")
):
"""๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋ฌธ์„œ ์—…๋กœ๋“œ"""
try:
start_time = time.time()
# ํŒŒ์ผ ์ฝ๊ธฐ
content = await file.read()
filename = file.filename
try:
from lily_llm_core.hybrid_rag_processor import hybrid_rag_processor
# ์ž„์‹œ ์ €์žฅ ํ›„ ํŒŒ์ผ ๊ฒฝ๋กœ ๊ธฐ๋ฐ˜ ์ฒ˜๋ฆฌ
temp_dir = os.path.join("data", "uploads")
os.makedirs(temp_dir, exist_ok=True)
temp_name = f"{int(time.time()*1000)}_{uuid.uuid4().hex}_{filename}"
temp_path = os.path.join(temp_dir, temp_name)
with open(temp_path, "wb") as f:
f.write(content)
result = hybrid_rag_processor.process_document(
file_path=temp_path,
user_id=user_id,
room_id=room_id
)
if result.get("success"):
processing_time = time.time() - start_time
return {
"status": "success",
"document_id": result.get("document_id", ""),
"processing_time": processing_time,
"message": "๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋ฌธ์„œ ์—…๋กœ๋“œ ์™„๋ฃŒ"
}
else:
raise HTTPException(status_code=500, detail=result.get("error", "๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋ฌธ์„œ ์ฒ˜๋ฆฌ ์‹คํŒจ"))
except ImportError:
raise HTTPException(status_code=500, detail="Hybrid RAG processor not available")
except Exception as e:
logger.error(f"๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋ฌธ์„œ ์—…๋กœ๋“œ ์‹คํŒจ: {e}")
raise HTTPException(status_code=500, detail=f"๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ๋ฌธ์„œ ์—…๋กœ๋“œ ์‹คํŒจ: {str(e)}")
@router.post("/multimodal-rag/generate", response_model=MultimodalRAGResponse)
async def generate_multimodal_rag(
prompt: str = Form(...),
user_id: str = Form("anonymous"),
room_id: str = Form("default")
):
"""๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ RAG ๊ธฐ๋ฐ˜ ํ…์ŠคํŠธ ์ƒ์„ฑ"""
try:
start_time = time.time()
try:
from lily_llm_core.hybrid_rag_processor import hybrid_rag_processor
# ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ RAG ์ƒ์„ฑ
result = hybrid_rag_processor.generate(
prompt=prompt,
user_id=user_id,
room_id=room_id
)
if result.get("success"):
processing_time = time.time() - start_time
return MultimodalRAGResponse(
success=True,
response=result.get("response", ""),
image_processed=result.get("image_processed", False),
processing_time=processing_time
)
else:
return MultimodalRAGResponse(
success=False,
response="",
image_processed=False,
processing_time=0,
error=result.get("error", "๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ RAG ์ƒ์„ฑ ์‹คํŒจ")
)
except ImportError:
return MultimodalRAGResponse(
success=False,
response="",
image_processed=False,
processing_time=0,
error="Hybrid RAG processor not available"
)
except Exception as e:
logger.error(f"๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ RAG ์ƒ์„ฑ ์‹คํŒจ: {e}")
return MultimodalRAGResponse(
success=False,
response="",
image_processed=False,
processing_time=0,
error=str(e)
)