Spaces:
Sleeping
Sleeping
| import os | |
| from fastapi import FastAPI, Request, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from manager.dialogue_manager import handle_dialogue | |
| from rag.rag_generator import chroma_initialized, load_game_docs_from_disk, add_docs | |
| from contextlib import asynccontextmanager | |
| from models.model_loader import load_emotion_model, load_fallback_model, load_embedder | |
| from schemas import AskReq, AskRes | |
| from pathlib import Path | |
| from rag.rag_generator import set_embedder | |
| # ๋ชจ๋ธ ์ด๋ฆ | |
| EMOTION_MODEL_NAME = "tae898/emoberta-base-ko" | |
| FALLBACK_MODEL_NAME = "skt/ko-gpt-trinity-1.2B-v0.5" | |
| EMBEDDER_MODEL_NAME = "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2" | |
| # ์ ๋ ๊ฒฝ๋ก ๊ธฐ์ค ๋ชจ๋ธ ๋๋ ํ ๋ฆฌ ์ค์ | |
| BASE_DIR = Path(__file__).resolve().parent # ai_server/ | |
| EMOTION_MODEL_DIR = Path(os.getenv("EMOTION_MODEL_DIR", BASE_DIR / "models" / "emotion-classification-model")) | |
| FALLBACK_MODEL_DIR = Path(os.getenv("FALLBACK_MODEL_DIR", BASE_DIR / "models" / "fallback-npc-model")) | |
| EMBEDDER_MODEL_DIR = Path(os.getenv("EMBEDDER_MODEL_DIR", BASE_DIR / "models" / "sentence-embedder")) | |
| async def lifespan(app: FastAPI): | |
| # Emotion | |
| emo_tokenizer, emo_model = load_emotion_model(EMOTION_MODEL_NAME, EMOTION_MODEL_DIR) | |
| app.state.emotion_tokenizer = emo_tokenizer | |
| app.state.emotion_model = emo_model | |
| # Fallback | |
| fb_tokenizer, fb_model = load_fallback_model(FALLBACK_MODEL_NAME, FALLBACK_MODEL_DIR) | |
| app.state.fallback_tokenizer = fb_tokenizer | |
| app.state.fallback_model = fb_model | |
| # Embedder | |
| embedder = load_embedder(EMBEDDER_MODEL_NAME, EMBEDDER_MODEL_DIR) | |
| app.state.embedder = embedder | |
| set_embedder(embedder) # ์ถ๊ฐ | |
| print("โ ๋ชจ๋ ๋ชจ๋ธ ๋ก๋ฉ ์๋ฃ") | |
| # RAG ์ด๊ธฐํ | |
| docs_path = BASE_DIR / "rag" / "docs" | |
| if not chroma_initialized(): | |
| docs = load_game_docs_from_disk(str(docs_path)) | |
| add_docs(docs) | |
| print(f"โ RAG ๋ฌธ์ {len(docs)}๊ฐ ์ฝ์ ์๋ฃ") | |
| else: | |
| print("๐ RAG DB ์ด๋ฏธ ์ด๊ธฐํ๋จ") | |
| yield # ์ฑ ์คํ | |
| print("๐ ์๋ฒ ์ข ๋ฃ ์ค...") | |
| app = FastAPI(title="ai-server", lifespan=lifespan) | |
| # CORS ์ค์ (game-server์์ ์์ฒญ ๊ฐ๋ฅํ๋๋ก) | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["https://fpsgame-rrbc.onrender.com"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| async def ask(request: Request, req: AskReq): | |
| context = req.context or {} | |
| npc_config = context.npc_config | |
| if not (req.session_id and req.npc_id and req.user_input and npc_config): | |
| raise HTTPException(status_code=400, detail="missing fields") | |
| result = await handle_dialogue( | |
| request=request, | |
| session_id=req.session_id, | |
| npc_id=req.npc_id, | |
| user_input=req.user_input, | |
| context=context.dict(), | |
| npc_config=npc_config.dict() | |
| ) | |
| return result | |
| async def wake(request: Request): | |
| body = await request.json() | |
| session_id = body.get("session_id", "unknown") | |
| print(f"๐ก Wake signal received for session: {session_id}") | |
| return {"status": "awake", "session_id": session_id} | |
| ''' | |
| ์ต์ข gameโserver โ aiโserver ์์ฒญ ์์ | |
| { | |
| "session_id": "abc123", | |
| "npc_id": "mother_abandoned_factory", | |
| "user_input": "์! ๋จธ๋ฆฌ๊ฐโฆ ๊ธฐ์ต์ด ๋ ์ฌ๋์ด์.", | |
| /* game-server์์ ํํฐ๋งํ ํ์/์ ํ require ์์๋ง ํฌํจ */ | |
| "context": { | |
| "require": { | |
| "items": ["photo_forgotten_party"], // ํ์/์ ํ ๊ตฌ๋ถ์ npc_config.json์์ | |
| "actions": ["visited_factory"], | |
| "game_state": ["box_opened"], // ํ์ ์ | |
| "delta": { "trust": 0.35, "relationship": 0.1 } | |
| }, | |
| "player_state": { | |
| "level": 7, | |
| "reputation": "helpful", | |
| "location": "map1" | |
| /* ์ ์ฒด ์ธ๋ฒคํ ๋ฆฌ/ํ๋ ๋ก๊ทธ๋ ํ์ ์ ๋ณ๋ ์ ๋ฌ */ | |
| }, | |
| "game_state": { | |
| "current_quest": "search_jason", | |
| "quest_stage": "in_progress", | |
| "location": "map1", | |
| "time_of_day": "evening" | |
| }, | |
| "npc_state": { | |
| "id": "mother_abandoned_factory", | |
| "name": "์ค๋น์", | |
| "persona_name": "Silvia", | |
| "dialogue_style": "emotional", | |
| "relationship": 0.35, | |
| "npc_mood": "grief" | |
| }, | |
| "dialogue_history": [ | |
| { | |
| "player": "ํน์ ์ด ๊ณต์ฅ์์ ๋ณธ ๊ฑธ ๋งํด์ค์.", | |
| "npc": "๊ทธ๋ ์ ๋ ์ฌ๋ฆฌ๋ ๊ฒ ๋๋ฌด ํ๋ค์ด์." | |
| } | |
| ] | |
| } | |
| } | |
| ''' | |
| ''' | |
| { | |
| "session_id": "abc123", | |
| "npc_id": "mother_abandoned_factory", | |
| "user_input": "์! ๋จธ๋ฆฌ๊ฐโฆ ๊ธฐ์ต์ด ๋ ์ฌ๋์ด์.", | |
| "precheck_passed": true, | |
| "context": { | |
| "player_status": { | |
| "level": 7, | |
| "reputation": "helpful", | |
| "location": "map1", | |
| "trigger_items": ["photo_forgotten_party"], // game-server์์ ์กฐ๊ฑด ํํฐ ํ key๋ก ๋ณํ | |
| "trigger_actions": ["visited_factory"] // ๋ง์ฐฌ๊ฐ์ง๋ก key ๋ฌธ์์ด | |
| /* ์๋ณธ ์ ์ฒด inventory/actions ๋ฐฐ์ด์ ์๋น์ค ํ์ ์ ๋ณ๋ ์ ๋ฌ ๊ฐ๋ฅ | |
| ํ์ง๋ง ai-server ์กฐ๊ฑด ํ์ ์๋ trigger_*๋ง ์ฌ์ฉ */ | |
| }, | |
| "game_state": { | |
| "current_quest": "search_jason", | |
| "quest_stage": "in_progress", | |
| "location": "map1", | |
| "time_of_day": "evening" | |
| }, | |
| "npc_config": { | |
| "id": "mother_abandoned_factory", | |
| "name": "์ค๋น์", | |
| "persona_name": "Silvia", | |
| "dialogue_style": "emotional", | |
| "relationship": 0.35, | |
| "npc_mood": "grief", | |
| "trigger_values": { | |
| "in_progress": ["๊ธฐ์ต", "์ฌ์ง", "ํํฐ"] | |
| }, | |
| "trigger_definitions": { | |
| "in_progress": { | |
| "required_text": ["๊ธฐ์ต", "์ฌ์ง"], | |
| "required_items": ["photo_forgotten_party"], // trigger_items์ ๋งค์นญ | |
| "required_actions": ["visited_factory"], // trigger_actions์ ๋งค์นญ | |
| "emotion_threshold": { "sad": 0.2 }, | |
| "fallback_style": { | |
| "style": "guarded", | |
| "npc_emotion": "suspicious" | |
| } | |
| } | |
| } | |
| }, | |
| "dialogue_history": [ | |
| { | |
| "player": "ํน์ ์ด ๊ณต์ฅ์์ ๋ณธ ๊ฑธ ๋งํด์ค์.", | |
| "npc": "๊ทธ๋ ์ ๋ ์ฌ๋ฆฌ๋ ๊ฒ ๋๋ฌด ํ๋ค์ด์." | |
| } | |
| ] | |
| } | |
| } | |
| ------------------------------------------------------------------------------------------------------ | |
| ์ด์ game-server ์์ฒญ ๊ตฌ์กฐ ์์: | |
| { | |
| "session_id": "abc123", | |
| "npc_id": "mother_abandoned_factory", | |
| "user_input": "์! ๋จธ๋ฆฌ๊ฐโฆ ๊ธฐ์ต์ด ๋ ์ฌ๋์ด์.", | |
| "context": { | |
| "player_status": { | |
| "level": 7, | |
| "reputation": "helpful", | |
| "location": "map1", | |
| "items": ["photo_forgotten_party"], | |
| "actions": ["visited_factory", "talked_to_guard"] | |
| }, | |
| "game_state": { | |
| "current_quest": "search_jason", | |
| "quest_stage": "in_progress", | |
| "location": "map1", | |
| "time_of_day": "evening" | |
| }, | |
| "npc_config": { | |
| "id": "mother_abandoned_factory", | |
| "name": "์ค๋น์", | |
| "persona_name": "Silvia", | |
| "dialogue_style": "emotional", | |
| "relationship": 0.35, | |
| "npc_mood": "grief", | |
| "trigger_values": { | |
| "in_progress": ["๊ธฐ์ต", "์ฌ์ง", "ํํฐ"] | |
| }, | |
| "trigger_definitions": { | |
| "in_progress": { | |
| "required_text": ["๊ธฐ์ต", "์ฌ์ง"], | |
| "emotion_threshold": {"sad": 0.2}, | |
| "fallback_style": {"style": "guarded", "npc_emotion": "suspicious"} | |
| } | |
| } | |
| }, | |
| "dialogue_history": [ | |
| {"player": "ํน์ ์ด ๊ณต์ฅ์์ ๋ณธ ๊ฑธ ๋งํด์ค์.", "npc": "๊ทธ๋ ์ ๋ ์ฌ๋ฆฌ๋ ๊ฒ ๋๋ฌด ํ๋ค์ด์."} | |
| ] | |
| } | |
| } | |
| ''' |