Spaces:
Sleeping
Sleeping
# portfolio/npc_social_network/routes/npc_route.py | |
from flask import Blueprint, render_template, request, jsonify, url_for | |
from .. import simulation_core | |
from ..models.gemini_setup import initialize_model | |
import os | |
npc_bp = Blueprint( | |
"npc_social", | |
__name__, | |
static_folder="../static", | |
static_url_path="/npc_social_network" | |
) | |
def dashboard(): | |
return render_template("dashboard.html") | |
# ์๋ฎฌ๋ ์ด์ ์ ์์ํ๋ API | |
def api_initialize_simulation(): | |
"""API ํค๋ฅผ ๋ฐ์ ์๋ฎฌ๋ ์ด์ ์ ์ด๊ธฐํํ๊ณ ์์ํ๋ API""" | |
from ..models.gemini_setup import initialize_model | |
data = request.json | |
model_name = data.get("model_name", "gemini-2.0-flash") | |
api_key = data.get("api_key") | |
if not api_key: | |
return jsonify({"success": False, "error": "API ํค๋ฅผ ์ ๋ ฅํด์ฃผ์ธ์."}), 400 | |
# 1. ๋ชจ๋ธ ์ด๊ธฐํ ์๋ | |
new_model = initialize_model(model_name, api_key) | |
if new_model: | |
# 2. ์ฑ๊ณต์, ์ ์ญ ๋ชจ๋ธ์ ์ค์ ํ๊ณ ์๋ฎฌ๋ ์ด์ ์ ์์ | |
simulation_core.active_llm_model = new_model | |
if not simulation_core.simulation_initialized: | |
simulation_core.initialize_simulation() | |
simulation_core.start_simulation_loop() | |
return jsonify({"success": True, "message": f"'{model_name}' ๋ชจ๋ธ๋ก ์๋ฎฌ๋ ์ด์ ์ ์์ํฉ๋๋ค."}) | |
else: | |
return jsonify({"success": False, "error": "API ํค๊ฐ ์ ํจํ์ง ์๊ฑฐ๋ ๋ชจ๋ธ ์ด๊ธฐํ์ ์คํจํ์ต๋๋ค."}), 400 | |
def get_world_state(): | |
if not simulation_core.simulation_initialized: | |
return jsonify({"status": "needs_initialization", "log": simulation_core.event_log}) | |
with simulation_core.simulation_lock: | |
if not simulation_core.npc_manager: | |
return jsonify({"error": "Simulation not started"}), 500 | |
all_npcs = list(simulation_core.npc_manager.all()) | |
current_log = list(simulation_core.event_log) | |
is_paused = simulation_core.simulation_paused | |
nodes = [] | |
for npc in all_npcs: | |
# ๊ฐ NPC์ ์์ด ID๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ด๋ฏธ์ง ํ์ผ๋ช ์ ์์ฑํฉ๋๋ค. (์: elin -> elin.png) | |
image_filename = f"images/npc/{npc.name}.png" | |
# if not os.path.exists(image_filename): | |
# image_filename = f"images/npc/npc.png" | |
nodes.append({ | |
# id๋ ์์ด๋ก, label์ ํ๊ธ๋ก ๋ถ๋ฆฌ | |
"id": npc.name, | |
"label": npc.korean_name, | |
"shape": "image", # ๋ ธ๋ ๋ชจ์์ 'image'๋ก ์ง์ ํฉ๋๋ค. | |
"image": url_for('npc_social.static', filename=image_filename), # ์ฌ๋ฐ๋ฅธ ์ด๋ฏธ์ง ๊ฒฝ๋ก๋ฅผ ์์ฑํฉ๋๋ค. | |
"size": 40 # ์ด๋ฏธ์ง ํฌ๊ธฐ๋ฅผ ์ ์ ํ ์กฐ์ ํฉ๋๋ค. | |
}) | |
edges = [] | |
drawn_relations = set() | |
for npc in all_npcs: | |
for target_name, profile in npc.relationships.relationships.items(): | |
from_id = npc.name | |
to_id = target_name | |
relation_pair = tuple(sorted((from_id, to_id))) | |
if relation_pair in drawn_relations: continue | |
color = {"best friend": "#00E676", "friend": "#4CAF50", "acquaintance": "#81D4FA", | |
"nuisance": "#FFC107", "rival": "#FF9800", "enemy": "#F44336", | |
"stranger":"#BDBDBD"}.get(profile.type, "gray") | |
edges.append({ | |
"from": from_id, "to": to_id, | |
"label": f"{profile.type} ({profile.score:.1f})", | |
"color": color | |
}) | |
drawn_relations.add(relation_pair) | |
# ํ๋ ์ด์ด ์ ๋ ฅ ๋๊ธฐ ์ํ ์ ๋ณด | |
waiting_for_player = False | |
player_conversation_info = None | |
if simulation_core.conversation_manager and simulation_core.conversation_manager.is_conversation_active(): | |
conv = simulation_core.conversation_manager.active_conversation | |
if conv.waiting_for_player: | |
waiting_for_player = True | |
# ํ๋ ์ด์ด์๊ฒ ๋ง์ ๊ฑด NPC์ ์ ๋ณด ์ฐพ๊ธฐ | |
other_npc = conv.participants[1 - conv.turn_index] | |
player_conversation_info = { | |
"npc_name": other_npc.korean_name, | |
"last_utterance": conv.conversation_history[-1] if conv.conversation_history else "" | |
} | |
return jsonify({ "nodes": nodes, "edges": edges, "log": current_log, "paused": is_paused, "waiting_for_player": waiting_for_player, "player_conversation": player_conversation_info }) | |
def toggle_simulation(): | |
with simulation_core.simulation_lock: | |
simulation_core.simulation_paused = not simulation_core.simulation_paused | |
status = "์ ์ง๋จ" if simulation_core.simulation_paused else "์คํ ์ค" | |
simulation_core.add_log(f"์๋ฎฌ๋ ์ด์ ์ด {status} ์ํ๋ก ๋ณ๊ฒฝ๋์์ต๋๋ค.") | |
is_paused = simulation_core.simulation_paused | |
return jsonify({"paused": is_paused}) | |
def manual_tick(): | |
# ์๋ ํฑ์ ์๋ฎฌ๋ ์ด์ ์ด ๋ฉ์ถฐ์์ ๋๋ง ์๋ํ๋๋ก ํจ | |
if simulation_core.simulation_paused: | |
simulation_core.tick_simulation() | |
else: | |
simulation_core.add_log("๊ฒฝ๊ณ : ์๋ฎฌ๋ ์ด์ ์ด ์คํ ์ค์ผ ๋๋ ์๋ ํฑ์ ํ ์ ์์ต๋๋ค.") | |
return get_world_state() | |
def inject_event(): | |
"""ํ๋ ์ด์ด๊ฐ NPC์๊ฒ '๊ณผ๊ฑฐ ๊ธฐ์ต'์ ์์ฐ์ค๋ฝ๊ฒ ์ฃผ์ ํ๋ ํจ์""" | |
data = request.json | |
npc_name, event_text = data.get("npc_name"), data.get("event_text") | |
with simulation_core.simulation_lock: | |
npc = simulation_core.npc_manager.get_npc_by_korean_name(npc_name) | |
if npc and event_text: | |
npc.remember(content=event_text, importance=8, emotion="nostalgia", memory_type="Recalled_Past") # ์ด๋ฒคํธ ํ์ : ์๊ธฐ๋ ๊ณผ๊ฑฐ | |
simulation_core.add_log(f"ํ๋ ์ด์ด ๊ฐ์ -> {npc_name}์๊ฒ '์๊ธฐ๋ ๊ณผ๊ฑฐ' ์ฃผ์ : '{event_text}'") | |
return jsonify({"success": True}) | |
return jsonify({"success": False, "error": "Invalid data"}), 400 | |
# get_npc_details ์ ๊ฐ์ ๋๋จธ์ง ์ฝ๊ธฐ ์ ์ฉ API๋ Lock์ ์ถ๊ฐํ๋ฉด ๋ ์์ ํฉ๋๋ค. | |
def get_npc_details(npc_name): | |
with simulation_core.simulation_lock: | |
npc = simulation_core.npc_manager.get_npc_by_korean_name(npc_name) | |
if not npc: | |
return jsonify({"error": "NPC not found"}), 404 | |
details = { | |
"name": npc.korean_name, "age": npc.age, "job": npc.job, | |
"personality_summary": npc.personality.get_narrative_summary(), | |
"emotions": npc.get_composite_emotion_state(top_n=5), | |
"goals": npc.planner.current_goal.description if npc.planner.has_active_plan() else "ํน๋ณํ ๋ชฉํ ์์", | |
"memories": [mem.content for mem in npc.memory_store.get_recent_memories(limit=10)] | |
} | |
return jsonify(details) | |
def force_relationship(): | |
"""๋ NPC์ ๊ด๊ณ๋ฅผ ๊ฐ์ ๋ก ์ค์ ํ๋ API""" | |
data = request.json | |
npc1_name = data.get("npc1_name") | |
npc2_name = data.get("npc2_name") | |
relationship_type = data.get("relationship_type") | |
if not all([npc1_name, npc2_name, relationship_type]) or npc1_name == npc2_name: | |
return jsonify({"success": False, "error": "Invalid data"}), 400 | |
with simulation_core.simulation_lock: | |
npc1 = simulation_core.npc_manager.get_npc_by_korean_name(npc1_name) | |
npc2 = simulation_core.npc_manager.get_npc_by_korean_name(npc2_name) | |
if npc1 and npc2: | |
# ๊ด๊ณ๋ ์ํธ์์ฉ์ด๋ฏ๋ก, ์์ชฝ ๋ชจ๋์๊ฒ ๊ด๊ณ๋ฅผ ์ค์ | |
npc1.relationships.set_relationship(npc2.name, relationship_type) | |
npc2.relationships.set_relationship(npc1.name, relationship_type) | |
return jsonify({"success": True}) | |
return jsonify({"success": False, "error": "NPC not found"}), 404 | |
def orchestrate_conversation(): | |
"""ํ๋ ์ด์ด๊ฐ ์ง์ ํ ๋ NPC์ ์ํฉ์ผ๋ก ๋ํ๋ฅผ ์์์ํค๋ API""" | |
data = request.json | |
npc1_name = data.get("npc1_name") | |
npc2_name = data.get("npc2_name") | |
situation = data.get("situation") # ๋ํ ํต์ฌ 'topic' | |
if not all([npc1_name, npc2_name, situation]) or npc1_name == npc2_name: | |
return jsonify({"success": False, "error": "Invalid data"}), 400 | |
# ๋ฝ์ ๊ฑธ์ง ์๊ณ ๋งค๋์ ์ ๋ฐ๋ก ์์ฒญ | |
npc1 = simulation_core.npc_manager.get_npc_by_korean_name(npc1_name) | |
npc2 = simulation_core.npc_manager.get_npc_by_korean_name(npc2_name) | |
if npc1 and npc2: | |
# ๊ธฐ์กด์ ๋ํ ์์ ํจ์ ์ฌํ์ฉ | |
simulation_core.conversation_manager.start_conversation(npc1, npc2, topic=situation) | |
return jsonify({"success": True}) | |
return jsonify({"success": False, "error": "NPC not found"}), 404 | |
def player_response(): | |
"""ํ๋ ์ด์ด์ ๋ํ ์๋ต์ ๋ฐ์ '๋๊ธฐ์ด'์ ์ถ๊ฐ, ์ฒ๋ฆฌ๋ simulation_loop์์ ์งํ""" | |
data = request.json | |
utterance = data.get("utterance") | |
if not utterance: | |
return jsonify({"success": False, "error": "Invalid data"}), 400 | |
simulation_core.set_player_utterance(utterance) | |
return jsonify({"success": True}) | |
def toggle_player(): | |
"""ํ๋ ์ด์ด์ ํ์ฑํ ์ํ๋ฅผ ํ ๊ธํ๋ API""" | |
with simulation_core.simulation_lock: | |
# npc_manager์ ์ํ๋ฅผ ์ง์ ๋ณ๊ฒฝ | |
simulation_core.npc_manager.set_player_active(not simulation_core.npc_manager.player_is_active) | |
is_active = simulation_core.npc_manager.player_is_active | |
status_text = "ํ์ฑํ" if is_active else "๋นํ์ฑํ" | |
simulation_core.add_log(f"ํ๋ ์ด์ด ์ํ๊ฐ '{status_text}'๋ก ๋ณ๊ฒฝ๋์์ต๋๋ค.") | |
return jsonify({"success": True, "player_is_active": is_active}) | |
def set_llm_config(): | |
"""UI๋ก๋ถํฐ ๋ฐ์ ๋ชจ๋ธ๊ณผ API ํค๋ก LLM์ ์ค์๊ฐ์ผ๋ก ์ฌ์ค์ ํ๋ API""" | |
from ..models.gemini_setup import initialize_model | |
data = request.json | |
model_name = data.get("model_name") | |
api_key = data.get("api_key") | |
if not all([model_name, api_key]): | |
return jsonify({"success": False, "error": "๋ชจ๋ธ๊ณผ API ํค๋ฅผ ๋ชจ๋ ์ ๋ ฅํด์ฃผ์ธ์."}), 400 | |
# ์๋ก์ด ์ค์ ์ผ๋ก ๋ชจ๋ธ์ ์ด๊ธฐํ ์๋ | |
new_model = initialize_model(model_name, api_key) | |
if new_model: | |
# ์ฑ๊ณต ์, simulation_core์ ์ ์ญ ๋ชจ๋ธ ๊ฐ์ฒด๋ฅผ ์๋ก์ด ๋ชจ๋ธ๋ก ๊ต์ฒด | |
simulation_core.active_llm_model = new_model | |
return jsonify({"success": True}) | |
else: | |
return jsonify({"success": False, "error": "API ํค๊ฐ ์ ํจํ์ง ์๊ฑฐ๋ ๋ชจ๋ธ ์ด๊ธฐํ์ ์คํจํ์ต๋๋ค."}), 400 |