Spaces:
Running
Running
File size: 11,237 Bytes
7cfe55a dde6bd9 b03bfdc 1a6fb12 f1c565e dde6bd9 7cfe55a a3830f2 b03bfdc 6fe0d0c 7cfe55a dde6bd9 73db488 b03bfdc 6fe0d0c dde6bd9 b03bfdc f1c565e b03bfdc 1a6fb12 f1c565e d57920f f1c565e d57920f dde6bd9 f1c565e dde6bd9 f1c565e 1a6fb12 9df6a7e dde6bd9 6fe0d0c dde6bd9 3e5e60d dde6bd9 6fe0d0c d57920f 6fe0d0c 3e5e60d 6fe0d0c d57920f 6fe0d0c d57920f d505821 6fe0d0c 2cd8c90 09e5b85 1a6fb12 00b7ce9 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 |
# 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"
)
@npc_bp.route("/")
def dashboard():
return render_template("dashboard.html")
# ์๋ฎฌ๋ ์ด์
์ ์์ํ๋ API
@npc_bp.route("/api/initialize_simulation", methods=['POST'])
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
@npc_bp.route("/api/world_state", methods=['GET'])
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 })
@npc_bp.route("/api/toggle_simulation", methods=['POST'])
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})
@npc_bp.route("/api/manual_tick", methods=['POST'])
def manual_tick():
# ์๋ ํฑ์ ์๋ฎฌ๋ ์ด์
์ด ๋ฉ์ถฐ์์ ๋๋ง ์๋ํ๋๋ก ํจ
if simulation_core.simulation_paused:
simulation_core.tick_simulation()
else:
simulation_core.add_log("๊ฒฝ๊ณ : ์๋ฎฌ๋ ์ด์
์ด ์คํ ์ค์ผ ๋๋ ์๋ ํฑ์ ํ ์ ์์ต๋๋ค.")
return get_world_state()
@npc_bp.route("/api/inject_event", methods=['POST'])
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์ ์ถ๊ฐํ๋ฉด ๋ ์์ ํฉ๋๋ค.
@npc_bp.route("/api/npc_details/<npc_name>", methods=['GET'])
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)
@npc_bp.route("/api/force_relationship", methods=['POST'])
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
@npc_bp.route("/api/orchestrate_conversation", methods=['Post'])
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
@npc_bp.route("/api/player_response", methods=['POST'])
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})
@npc_bp.route("/api/toggle_player", methods = ['POST'])
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})
@npc_bp.route("/api/set_llm_config", methods=['POST'])
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 |