# main.py - 에러 수정 버전
import os
import sys
from datetime import datetime
from dotenv import load_dotenv
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse, HTMLResponse
from fastapi.staticfiles import StaticFiles
# 환경 변수 로드
load_dotenv()
# 🌍 환경 감지
class EnvironmentDetector:
@staticmethod
def detect_environment():
"""실행 환경 자동 감지"""
if os.getenv("SPACE_ID") or os.getenv("SPACE_AUTHOR_NAME"):
return "huggingface"
elif os.getenv("LOCAL_DEV") == "true" or os.getenv("DEVELOPMENT_MODE") == "true":
return "local_dev"
elif os.getenv("PRODUCTION") == "true":
return "production"
else:
return "default"
@staticmethod
def get_environment_config(env_type: str) -> dict:
"""환경별 설정 반환"""
configs = {
"huggingface": {
"debug": False,
"reload": False,
"log_level": "info",
"cors_origins": ["*"],
"description": "🤗 허깅페이스 Spaces에서 실행 중",
"features": {
"static_files": True,
"api_docs": True,
"debug_routes": False
}
},
"local_dev": {
"debug": True,
"reload": True,
"log_level": "debug",
"cors_origins": ["*"],
"description": "🏠 로컬 개발 환경에서 실행 중",
"features": {
"static_files": True,
"api_docs": True,
"debug_routes": True
}
},
"production": {
"debug": False,
"reload": False,
"log_level": "warning",
"cors_origins": ["https://yourdomain.com"],
"description": "🏭 프로덕션 환경에서 실행 중",
"features": {
"static_files": True,
"api_docs": False,
"debug_routes": False
}
},
"default": {
"debug": False,
"reload": False,
"log_level": "info",
"cors_origins": ["*"],
"description": "🔧 기본 환경에서 실행 중",
"features": {
"static_files": True,
"api_docs": True,
"debug_routes": False
}
}
}
return configs.get(env_type, configs["default"])
# 환경 감지 및 설정
ENVIRONMENT = EnvironmentDetector.detect_environment()
CONFIG = EnvironmentDetector.get_environment_config(ENVIRONMENT)
print(f"🌍 감지된 환경: {ENVIRONMENT}")
print(f"📋 설정: {CONFIG['description']}")
# FastAPI 앱 생성 (환경별 설정 적용)
app = FastAPI(
title="💙 마음이 - 청소년 상담 챗봇",
description=f"13-19세 청소년을 위한 AI 공감 상담사 ({CONFIG['description']})",
version=os.getenv("VERSION", "2.0.0"),
docs_url="/docs" if CONFIG["features"]["api_docs"] else None,
redoc_url="/redoc" if CONFIG["features"]["api_docs"] else None,
debug=CONFIG["debug"]
)
# CORS 설정 (환경별)
app.add_middleware(
CORSMiddleware,
allow_origins=CONFIG["cors_origins"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
def create_default_html(file_path: str):
"""기본 HTML 파일 생성"""
html_content = get_default_html()
with open(file_path, "w", encoding="utf-8") as f:
f.write(html_content)
def get_default_html() -> str:
"""환경별 기본 HTML 반환"""
env_emoji = {
"huggingface": "🤗",
"local_dev": "🏠",
"production": "🏭",
"default": "🔧"
}
return f'''
마음이 AI | {CONFIG['description']}
💙 마음이 AI
{env_emoji.get(ENVIRONMENT, "🔧")} {CONFIG['description']}
청소년 공감 상담 챗봇이 곧 시작됩니다!
시스템 초기화 중...
{"" if not CONFIG['debug'] else '
🔧 개발 모드 - 디버그 정보 활성화
'}
'''
def add_demo_routes():
"""데모용 라우터 추가 (함수 정의)"""
@app.post("/api/v1/chat/teen-chat")
async def demo_chat(request: dict):
return {
"response": f"안녕! 마음이가 곧 준비될 예정이야. ({ENVIRONMENT} 환경) 💙",
"status": "demo_mode",
"environment": ENVIRONMENT
}
@app.post("/api/v1/chat/teen-chat-enhanced")
async def demo_chat_enhanced(request: dict):
return {
"response": f"마음이가 준비 중이야! 조금만 기다려줘. ({ENVIRONMENT} 환경) 💙",
"status": "demo_mode",
"environment": ENVIRONMENT
}
# 정적 파일 서빙 (환경별)
if CONFIG["features"]["static_files"]:
try:
static_dir = "static"
if not os.path.exists(static_dir):
os.makedirs(static_dir)
index_path = os.path.join(static_dir, "index.html")
if not os.path.exists(index_path):
create_default_html(index_path)
app.mount("/static", StaticFiles(directory=static_dir), name="static")
print("✅ 정적 파일 서빙 설정 완료")
except Exception as e:
print(f"⚠️ 정적 파일 설정 실패: {e}")
# 라우터 등록 (오류 처리 강화)
try:
from src.api import chat, openai, vector
app.include_router(chat.router, prefix="/api/v1/chat", tags=["💙 Teen Chat"])
app.include_router(openai.router, prefix="/api/v1/openai", tags=["🤖 OpenAI GPT-4"])
app.include_router(vector.router, prefix="/api/v1/vector", tags=["🗄️ Vector Store"])
print("✅ API 라우터 등록 완료")
except ImportError as e:
print(f"⚠️ API 라우터 import 실패: {e}")
print("🔄 데모 모드로 실행합니다.")
add_demo_routes()
except Exception as e:
print(f"⚠️ API 라우터 등록 중 예상치 못한 오류: {e}")
print("🔄 데모 모드로 실행합니다.")
add_demo_routes()
# 기본 라우트들
@app.get("/", response_class=HTMLResponse)
async def root():
"""메인 페이지"""
html_file_path = "static/index.html"
if os.path.exists(html_file_path):
with open(html_file_path, "r", encoding="utf-8") as f:
return HTMLResponse(content=f.read())
return HTMLResponse(content=get_default_html())
@app.get("/api/v1/health")
async def health_check():
"""헬스 체크 (환경 정보 포함)"""
return {
"status": "healthy",
"environment": ENVIRONMENT,
"config": CONFIG['description'],
"features": CONFIG['features'],
"timestamp": datetime.now().isoformat(),
"python_version": sys.version.split()[0],
"debug_mode": CONFIG["debug"]
}
@app.get("/api/v1/environment")
async def get_environment_info():
"""환경 정보 조회"""
return {
"environment": ENVIRONMENT,
"config": CONFIG,
"env_vars": {
"SPACE_ID": bool(os.getenv("SPACE_ID")),
"LOCAL_DEV": os.getenv("LOCAL_DEV"),
"DEVELOPMENT_MODE": os.getenv("DEVELOPMENT_MODE"),
"PRODUCTION": os.getenv("PRODUCTION"),
"OPENAI_API_KEY_SET": bool(os.getenv("OPENAI_API_KEY"))
}
}
# 디버그 라우트 (개발 환경에서만)
if CONFIG["features"]["debug_routes"]:
@app.get("/api/v1/debug/reload")
async def debug_reload():
"""개발용: 서버 재시작 없이 모듈 리로드"""
return {"message": "개발 모드에서만 사용 가능", "environment": ENVIRONMENT}
@app.get("/api/v1/debug/logs")
async def debug_logs():
"""개발용: 최근 로그 조회"""
try:
with open("/app/logs/app.log", "r") as f:
logs = f.readlines()[-50:] # 최근 50줄
return {"logs": logs}
except FileNotFoundError:
return {"logs": ["로그 파일이 없습니다."]}
# 환경별 실행 (스크립트로 직접 실행할 때)
if __name__ == "__main__":
import uvicorn
port = int(os.getenv("API_PORT", 7860))
print(f"🚀 {CONFIG['description']} 서버 시작")
print(f"📍 포트: {port}")
print(f"🔧 디버그 모드: {CONFIG['debug']}")
print(f"🔄 리로드: {CONFIG['reload']}")
uvicorn.run(
"main:app" if not CONFIG['reload'] else app,
host="0.0.0.0",
port=port,
reload=CONFIG['reload'],
log_level=CONFIG['log_level']
)