Spaces:
Build error
Build error
File size: 5,495 Bytes
9b9d44e eda02a7 9b9d44e eda02a7 9b9d44e eda02a7 9b9d44e eda02a7 9b9d44e eda02a7 9b9d44e eda02a7 9b9d44e eda02a7 9b9d44e eda02a7 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# ./backend/app/main.py
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles # StaticFiles 임포트
import os
from pydantic import BaseModel
import httpx
from youtube_parser import process_youtube_video_data
from rag_core import perform_rag_query
app = FastAPI()
# CORS 설정: 프론트엔드와 백엔드가 다른 포트에서 실행될 때 필요
origins = [
"http://localhost:8080", # Vue 개발 서버 기본 포트
"http://localhost:5173", # Vue Vite 개발 서버 기본 포트
"https://sodagraph-po.hf.space", # 여러분의 Hugging Face Space URL
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 경로 설정
current_file_dir = os.path.dirname(os.path.abspath(__file__))
project_root_dir = os.path.join(current_file_dir, "..", "..")
static_files_dir = os.path.join(project_root_dir, "static")
# OLLAMA_API_BASE_URL 환경 변수 설정
OLLAMA_API_BASE_URL = os.getenv("OLLAMA_API_BASE_URL", "http://127.0.0.1:11434")
async def generate_answer_with_ollama(model_name: str, prompt: str) -> str:
"""
Ollama 서버에 질의하여 답변을 생성합니다.
"""
url = f"{OLLAMA_API_BASE_URL}/api/generate"
headers = {"Content-Type": "application/json"}
data = {
"model": model_name,
"prompt": prompt,
"stream": False # 스트리밍을 사용하지 않고 한 번에 답변을 받습니다.
}
print(f"INFO: Ollama API 호출 시작. 모델: {model_name}")
print(f"INFO: 프롬프트 미리보기: {prompt[:200]}...")
try:
async with httpx.AsyncClient(timeout=600.0) as client:
response = await client.post(url, headers=headers, json=data)
response.raise_for_status()
response_data = response.json()
full_response = response_data.get("response", "").strip()
return full_response
except httpx.HTTPStatusError as e:
print(f"ERROR: Ollama API 호출 실패: {e}")
raise HTTPException(status_code=500, detail="Ollama API 호출 실패")
except httpx.RequestError as e:
print(f"ERROR: 네트워크 오류: {e}")
raise HTTPException(status_code=500, detail="네트워크 오류가 발생했습니다. 잠시 후 다시 시도해주세요.")
except Exception as e:
print(f"ERROR: 알 수 없는 오류: {e}")
raise HTTPException(status_code=500, detail="알 수 없는 오류가 발생했습니다. 잠시 후 다시 시도해주세요.")
class VideoProcessRequest(BaseModel):
video_url: str
query: str
# 사용자가 사용할 Ollama 모델 이름을 지정할 수 있도록 추가
ollama_model_name: str = "hf.co/DevQuasar/naver-hyperclovax.HyperCLOVAX-SEED-Text-Instruct-0.5B-GGUF:F16"
# ✅ 유튜브 영상 처리 API
@app.post("/api/process_youtube_video")
async def process_youtube_video(request: VideoProcessRequest):
try:
processed_chunks_with_timestamps = await process_youtube_video_data(request.video_url)
if not processed_chunks_with_timestamps:
return {"message": "자막 또는 내용을 추출할 수 없습니다.", "results": []}
# 1. RAG 검색 수행
rag_results = await perform_rag_query(
chunks_with_timestamps=processed_chunks_with_timestamps,
query=request.query,
top_k=50
)
if not rag_results:
return {
"status": "error",
"message": "검색 결과가 없습니다.",
"video_url": request.video_url,
"query": request.query,
"results": []
}
# 2. 검색 결과를 프롬프트에 추가
context = "\\n\\n".join([chunk["text"] for chunk in rag_results])
prompt = f"다음 정보와 대화 내용을 참고하여 사용자의 질문에 답변하세요. 제공된 정보에서 답을 찾을 수 없다면 '정보 부족'이라고 명시하세요.\\n\\n참고 정보:\\n{context}\\n\\n사용자 질문: {request.query}\\n\\n답변:"
# 3. Ollama 모델에 질의하여 답변 생성
generated_answer = await generate_answer_with_ollama(
model_name=request.ollama_model_name,
prompt=prompt
)
return {
"status": "success",
"message": "성공적으로 영상을 처리하고 RAG 검색을 수행했습니다.",
"video_url": request.video_url,
"query": request.query,
"ollama_model_used": request.ollama_model_name,
"retrieved_chunks": rag_results,
"generated_answer": generated_answer
}
except Exception as e:
print(f"ERROR: 서버 처리 중 오류 발생: {str(e)}")
raise HTTPException(status_code=500, detail="서버 처리 중 오류가 발생했습니다. 잠시 후 다시 시도해주세요.")
# ✅ 정적 파일은 마지막에 mount
app.mount("/", StaticFiles(directory=static_files_dir, html=True), name="static")
# 서버 실행을 위한 메인 진입점 (Docker에서는 Uvicorn이 직접 호출하므로 필수는 아님)
if __name__ == "__main__":
import uvicorn
import os
port = int(os.environ.get("PORT", 7860)) # Hugging Face가 전달하는 포트를 우선 사용
uvicorn.run(app, host="0.0.0.0", port=port) |