|
|
""" |
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app = Flask(__name__) |
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
|
|
|
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): |
|
|
@wraps(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')}") |
|
|
|
|
|
|
|
|
if request.path.startswith('/api/device/'): |
|
|
logger.info(f"์ฅ์น API ์์ฒญ: {request.path} - ์ธ์ฆ ์ ์ธ") |
|
|
return f(*args, **kwargs) |
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
@app.errorhandler(404) |
|
|
def not_found(e): |
|
|
|
|
|
if request.path.startswith('/api/'): |
|
|
return jsonify({"success": False, "error": "์์ฒญํ API ์๋ํฌ์ธํธ๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค."}), 404 |
|
|
|
|
|
return "ํ์ด์ง๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.", 404 |
|
|
|
|
|
@app.errorhandler(500) |
|
|
def internal_error(e): |
|
|
|
|
|
if request.path.startswith('/api/'): |
|
|
return jsonify({"success": False, "error": "์๋ฒ ๋ด๋ถ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค."}), 500 |
|
|
|
|
|
return "์๋ฒ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.", 500 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/static/<path:path>') |
|
|
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 = [] |
|
|
|
|
|
|
|
|
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) |
|
|
|