Spaces:
Sleeping
Sleeping
""" | |
RAG ๊ฒ์ ์ฑ๋ด ์น ์ ํ๋ฆฌ์ผ์ด์ (์ฅ์น ๊ด๋ฆฌ ๊ธฐ๋ฅ ํตํฉ) | |
""" | |
import os | |
import logging | |
import threading | |
from datetime import datetime, timedelta | |
from flask import Flask, send_from_directory, jsonify | |
from dotenv import load_dotenv | |
from functools import wraps | |
from flask_cors import CORS | |
# ๋ก๊ฑฐ ์ค์ | |
logging.basicConfig( | |
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', | |
level=logging.DEBUG | |
) | |
logger = logging.getLogger(__name__) | |
# ํ๊ฒฝ ๋ณ์ ๋ก๋ | |
load_dotenv() | |
# ํ๊ฒฝ ๋ณ์ ๋ก๋ ์ํ ํ์ธ ๋ฐ ๋ก๊น | |
ADMIN_USERNAME = os.getenv('ADMIN_USERNAME') | |
ADMIN_PASSWORD = os.getenv('ADMIN_PASSWORD') | |
DEVICE_SERVER_URL = os.getenv('DEVICE_SERVER_URL', 'http://localhost:5050') | |
logger.info(f"==== ํ๊ฒฝ ๋ณ์ ๋ก๋ ์ํ ====") | |
logger.info(f"ADMIN_USERNAME ์ค์ ์ฌ๋ถ: {ADMIN_USERNAME is not None}") | |
logger.info(f"ADMIN_PASSWORD ์ค์ ์ฌ๋ถ: {ADMIN_PASSWORD is not None}") | |
logger.info(f"DEVICE_SERVER_URL: {DEVICE_SERVER_URL}") | |
# ํ๊ฒฝ ๋ณ์๊ฐ ์์ผ๋ฉด ๊ธฐ๋ณธ๊ฐ ์ค์ | |
if not ADMIN_USERNAME: | |
ADMIN_USERNAME = 'admin' | |
logger.warning("ADMIN_USERNAME ํ๊ฒฝ๋ณ์๊ฐ ์์ด ๊ธฐ๋ณธ๊ฐ 'admin'์ผ๋ก ์ค์ ํฉ๋๋ค.") | |
if not ADMIN_PASSWORD: | |
ADMIN_PASSWORD = 'rag12345' | |
logger.warning("ADMIN_PASSWORD ํ๊ฒฝ๋ณ์๊ฐ ์์ด ๊ธฐ๋ณธ๊ฐ 'rag12345'๋ก ์ค์ ํฉ๋๋ค.") | |
class MockComponent: pass | |
# --- ๋ก์ปฌ ๋ชจ๋ ์ํฌํธ --- | |
try: | |
from utils.vito_stt import VitoSTT | |
from utils.llm_interface import LLMInterface | |
from utils.document_processor import DocumentProcessor | |
from retrieval.vector_retriever import VectorRetriever | |
from retrieval.reranker import ReRanker | |
except ImportError as e: | |
logger.error(f"๋ก์ปฌ ๋ชจ๋ ์ํฌํธ ์คํจ: {e}. utils ๋ฐ retrieval ํจํค์ง๊ฐ ์ฌ๋ฐ๋ฅธ ๊ฒฝ๋ก์ ์๋์ง ํ์ธํ์ธ์.") | |
VitoSTT = LLMInterface = DocumentProcessor = VectorRetriever = ReRanker = MockComponent | |
# --- ๋ก์ปฌ ๋ชจ๋ ์ํฌํธ ๋ --- | |
# Flask ์ฑ ์ด๊ธฐํ | |
app = Flask(__name__) | |
# CORS ์ค์ - ๋ชจ๋ ๋๋ฉ์ธ์์์ ์์ฒญ ํ์ฉ | |
CORS(app, supports_credentials=True) | |
# ์ธ์ ์ค์ | |
app.secret_key = os.getenv('FLASK_SECRET_KEY', 'rag_chatbot_fixed_secret_key_12345') | |
# --- ์ธ์ ์ฟ ํค ์ค์ --- | |
app.config['SESSION_COOKIE_SECURE'] = True | |
app.config['SESSION_COOKIE_HTTPONLY'] = True | |
app.config['SESSION_COOKIE_SAMESITE'] = 'None' | |
app.config['SESSION_COOKIE_DOMAIN'] = None | |
app.config['SESSION_COOKIE_PATH'] = '/' | |
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=1) | |
# --- ์ธ์ ์ฟ ํค ์ค์ ๋ --- | |
# ์ต๋ ํ์ผ ํฌ๊ธฐ ์ค์ (10MB) | |
app.config['MAX_CONTENT_LENGTH'] = 10 * 1024 * 1024 | |
# ์ ํ๋ฆฌ์ผ์ด์ ํ์ผ ๊ธฐ์ค ์๋ ๊ฒฝ๋ก ์ค์ | |
APP_ROOT = os.path.dirname(os.path.abspath(__file__)) | |
app.config['UPLOAD_FOLDER'] = os.path.join(APP_ROOT, 'uploads') | |
app.config['DATA_FOLDER'] = os.path.join(APP_ROOT, '..', 'data') | |
app.config['INDEX_PATH'] = os.path.join(APP_ROOT, '..', 'data', 'index') | |
# ํ์ํ ํด๋ ์์ฑ | |
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) | |
os.makedirs(app.config['DATA_FOLDER'], exist_ok=True) | |
os.makedirs(app.config['INDEX_PATH'], exist_ok=True) | |
# --- ์ ์ญ ๊ฐ์ฒด ์ด๊ธฐํ --- | |
try: | |
llm_interface = LLMInterface(default_llm="openai") | |
stt_client = VitoSTT() | |
except NameError: | |
logger.warning("LLM ๋๋ STT ์ธํฐํ์ด์ค ์ด๊ธฐํ ์คํจ. Mock ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํฉ๋๋ค.") | |
llm_interface = MockComponent() | |
stt_client = MockComponent() | |
base_retriever = None | |
retriever = None | |
app_ready = False # ์ฑ ์ด๊ธฐํ ์ํ ํ๋๊ทธ | |
# --- ์ ์ญ ๊ฐ์ฒด ์ด๊ธฐํ ๋ --- | |
# --- ์ธ์ฆ ๋ฐ์ฝ๋ ์ดํฐ --- | |
def login_required(f): | |
def decorated_function(*args, **kwargs): | |
from flask import request, session, redirect, url_for | |
logger.info(f"----------- ์ธ์ฆ ํ์ ํ์ด์ง ์ ๊ทผ ์๋: {request.path} -----------") | |
logger.info(f"ํ์ฌ ํ๋ผ์คํฌ ์ธ์ ๊ฐ์ฒด: {session}") | |
logger.info(f"ํ์ฌ ์ธ์ ์ํ: logged_in={session.get('logged_in', False)}, username={session.get('username', 'None')}") | |
logger.info(f"์์ฒญ์ ์ธ์ ์ฟ ํค ๊ฐ: {request.cookies.get('session', 'None')}") | |
# API ์์ฒญ์ด๊ณ ํด๋ผ์ด์ธํธ์์ ์ค๋ ๊ฒฝ์ฐ ์ธ์ฆ ๋ฌด์ (์์ ์กฐ์น) | |
if request.path.startswith('/api/device/'): | |
logger.info(f"์ฅ์น API ์์ฒญ: {request.path} - ์ธ์ฆ ์ ์ธ") | |
return f(*args, **kwargs) | |
# Flask ์ธ์ ์ 'logged_in' ํค๊ฐ ์๋์ง ์ง์ ํ์ธ | |
if 'logged_in' not in session: | |
logger.warning(f"ํ๋ผ์คํฌ ์ธ์ ์ 'logged_in' ์์. ๋ก๊ทธ์ธ ํ์ด์ง๋ก ๋ฆฌ๋๋ ์ .") | |
return redirect(url_for('login', next=request.url)) | |
logger.info(f"์ธ์ฆ ์ฑ๊ณต: {session.get('username', 'unknown')} ์ฌ์ฉ์๊ฐ {request.path} ์ ๊ทผ") | |
return f(*args, **kwargs) | |
return decorated_function | |
# --- ์ธ์ฆ ๋ฐ์ฝ๋ ์ดํฐ ๋ --- | |
# --- ์ค๋ฅ ํธ๋ค๋ฌ ์ถ๊ฐ --- | |
def not_found(e): | |
# ํด๋ผ์ด์ธํธ๊ฐ JSON์ ๊ธฐ๋ํ๋ API ํธ์ถ์ธ ๊ฒฝ์ฐ JSON ์๋ต | |
if request.path.startswith('/api/'): | |
return jsonify({"success": False, "error": "์์ฒญํ API ์๋ํฌ์ธํธ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค."}), 404 | |
# ์ผ๋ฐ ์น ํ์ด์ง ์์ฒญ์ธ ๊ฒฝ์ฐ HTML ์๋ต | |
return "ํ์ด์ง๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.", 404 | |
def internal_error(e): | |
# ํด๋ผ์ด์ธํธ๊ฐ JSON์ ๊ธฐ๋ํ๋ API ํธ์ถ์ธ ๊ฒฝ์ฐ JSON ์๋ต | |
if request.path.startswith('/api/'): | |
return jsonify({"success": False, "error": "์๋ฒ ๋ด๋ถ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค."}), 500 | |
# ์ผ๋ฐ ์น ํ์ด์ง ์์ฒญ์ธ ๊ฒฝ์ฐ HTML ์๋ต | |
return "์๋ฒ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.", 500 | |
# --- ์ค๋ฅ ํธ๋ค๋ฌ ๋ --- | |
# --- ์ ์ ํ์ผ ์๋น --- | |
def send_static(path): | |
return send_from_directory('static', path) | |
# --- ๋ฐฑ๊ทธ๋ผ์ด๋ ์ด๊ธฐํ ํจ์ --- | |
def background_init(): | |
"""๋ฐฑ๊ทธ๋ผ์ด๋์์ ๊ฒ์๊ธฐ ์ด๊ธฐํ ์ํ""" | |
global app_ready, retriever, base_retriever | |
# ์ฆ์ ์ฑ ์ฌ์ฉ ๊ฐ๋ฅ ์ํ๋ก ์ค์ | |
app_ready = True | |
logger.info("์ฑ์ ์ฆ์ ์ฌ์ฉ ๊ฐ๋ฅ ์ํ๋ก ์ค์ (app_ready=True)") | |
try: | |
from app.init_retriever import init_retriever | |
# ๊ธฐ๋ณธ ๊ฒ์๊ธฐ ์ด๊ธฐํ (๋ณดํ) | |
if base_retriever is None: | |
base_retriever = MockComponent() | |
if hasattr(base_retriever, 'documents'): | |
base_retriever.documents = [] | |
# ์์ retriever ์ค์ | |
if retriever is None: | |
retriever = MockComponent() | |
if not hasattr(retriever, 'search'): | |
retriever.search = lambda query, **kwargs: [] | |
# ์๋ฒ ๋ฉ ์บ์ ํ์ผ ๊ฒฝ๋ก | |
cache_path = os.path.join(app.config['INDEX_PATH'], "cached_embeddings.gz") | |
# ์บ์๋ ์๋ฒ ๋ฉ ๋ก๋ ์๋ | |
try: | |
from app.init_retriever import load_embeddings | |
cached_retriever = load_embeddings(cache_path) | |
if cached_retriever: | |
# ์บ์๋ ๋ฐ์ดํฐ๊ฐ ์์ผ๋ฉด ๋ฐ๋ก ์ฌ์ฉ | |
base_retriever = cached_retriever | |
# ์ฌ์์ํ ๊ฒ์๊ธฐ ์ด๊ธฐํ | |
retriever = ReRanker( | |
base_retriever=base_retriever, | |
rerank_fn=lambda query, results: results, | |
rerank_field="text" | |
) | |
logger.info("์บ์๋ ์๋ฒ ๋ฉ์ผ๋ก ๊ฒ์๊ธฐ ์ด๊ธฐํ ์๋ฃ (๋น ๋ฅธ ์์)") | |
else: | |
# ์บ์๋ ๋ฐ์ดํฐ๊ฐ ์์ผ๋ฉด ์ ์ฒด ์ด๊ธฐํ ์งํ | |
logger.info("์บ์๋ ์๋ฒ ๋ฉ์ด ์์ด ์ ์ฒด ์ด๊ธฐํ ์์") | |
retriever = init_retriever(app, base_retriever, retriever, ReRanker) | |
logger.info("์ ์ฒด ์ด๊ธฐํ ์๋ฃ") | |
except ImportError: | |
logger.warning("์๋ฒ ๋ฉ ์บ์ ๋ชจ๋์ ์ฐพ์ ์ ์์ต๋๋ค. ์ ์ฒด ์ด๊ธฐํ๋ฅผ ์งํํฉ๋๋ค.") | |
retriever = init_retriever(app, base_retriever, retriever, ReRanker) | |
logger.info("์ฑ ์ด๊ธฐํ ์๋ฃ (๋ชจ๋ ์ปดํฌ๋ํธ ์ค๋น๋จ)") | |
except Exception as e: | |
logger.error(f"์ฑ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ด๊ธฐํ ์ค ์ฌ๊ฐํ ์ค๋ฅ ๋ฐ์: {e}", exc_info=True) | |
# ์ด๊ธฐํ ์คํจ ์ ๊ธฐ๋ณธ ๊ฐ์ฒด ์์ฑ | |
if base_retriever is None: | |
base_retriever = MockComponent() | |
if hasattr(base_retriever, 'documents'): | |
base_retriever.documents = [] | |
if retriever is None: | |
retriever = MockComponent() | |
if not hasattr(retriever, 'search'): | |
retriever.search = lambda query, **kwargs: [] | |
logger.warning("์ด๊ธฐํ ์ค ์ค๋ฅ๊ฐ ์์ง๋ง ์ฑ์ ๊ณ์ ์ฌ์ฉ ๊ฐ๋ฅํฉ๋๋ค.") | |
# --- ๋ผ์ฐํธ ๋ฑ๋ก --- | |
def register_all_routes(): | |
try: | |
# ๊ธฐ๋ณธ ๋ผ์ฐํธ ๋ฑ๋ก | |
from app.app_routes import register_routes | |
register_routes( | |
app, login_required, llm_interface, retriever, stt_client, | |
DocumentProcessor, base_retriever, app_ready, | |
ADMIN_USERNAME, ADMIN_PASSWORD, DEVICE_SERVER_URL | |
) | |
# ์ฅ์น ๊ด๋ฆฌ ๋ผ์ฐํธ ๋ฑ๋ก | |
from app.app_device_routes import register_device_routes | |
register_device_routes(app, login_required, DEVICE_SERVER_URL) | |
logger.info("๋ชจ๋ ๋ผ์ฐํธ ๋ฑ๋ก ์๋ฃ") | |
except ImportError as e: | |
logger.error(f"๋ผ์ฐํธ ๋ชจ๋ ์ํฌํธ ์คํจ: {e}") | |
except Exception as e: | |
logger.error(f"๋ผ์ฐํธ ๋ฑ๋ก ์ค ์ค๋ฅ ๋ฐ์: {e}", exc_info=True) | |
# --- ์ฑ ์ด๊ธฐํ ๋ฐ ์คํ --- | |
def initialize_app(): | |
# ๋ฐฑ๊ทธ๋ผ์ด๋ ์ด๊ธฐํ ์ค๋ ๋ ์์ | |
init_thread = threading.Thread(target=background_init) | |
init_thread.daemon = True | |
init_thread.start() | |
# ๋ผ์ฐํธ ๋ฑ๋ก | |
register_all_routes() | |
logger.info("์ฑ ์ด๊ธฐํ ์๋ฃ") | |
# ์ฑ ์ด๊ธฐํ ์คํ | |
initialize_app() | |
# --- ์ฑ ์คํ (์ง์ ์คํ ์) --- | |
if __name__ == '__main__': | |
logger.info("Flask ์ฑ์ ์ง์ ์คํํฉ๋๋ค (๊ฐ๋ฐ์ฉ ์๋ฒ).") | |
port = int(os.environ.get("PORT", 7860)) | |
logger.info(f"์๋ฒ๋ฅผ http://0.0.0.0:{port} ์์ ์์ํฉ๋๋ค.") | |
app.run(debug=True, host='0.0.0.0', port=port) | |