import re
import os
-
# --- CONFIGURATION ---
MAX_CONCURRENT_OLLAMA_REQUESTS = 3
ALLOW_TEXT_EMOTES = False
-# --- NEW: LOAD THE WORLD LORE ON STARTUP ---
+# --- LOAD THE WORLD LORE ON STARTUP ---
WORLD_LORE = ""
-if os.path.exists("world_lore.txt"):
+if os.path.exists("asl_lore.txt"):
with open("asl_lore.txt", "r", encoding="utf-8") as f:
WORLD_LORE = f.read().strip()
else:
- print("[WARNING] world_lore.txt not found. Running without global lore.")
-
+ print("[WARNING] asl_lore.txt/asl_lore.txt not found. Running without global lore.")
semaphore = asyncio.Semaphore(MAX_CONCURRENT_OLLAMA_REQUESTS)
# Dictionary to keep individual conversations separate
npc_tag = data.get('npc_tag', 'UnknownNPC')
message = data.get('message', '')
- # --- NEW: THE ASTERISK SCRUBBER ---
+ # --- THE ASTERISK SCRUBBER ---
if not ALLOW_TEXT_EMOTES:
- # This deletes anything wrapped in asterisks (e.g. "*smiles* Hello" becomes " Hello")
message = re.sub(r'\*.*?\*', '', message).strip()
# Extract the dynamic variables sent from the Aurora Toolset/Engine
player_alignment = data.get('player_alignment', 'Unknown')
nearby_players = data.get('nearby_players', '')
+ # --- Extract States and Health ---
+ player_state = data.get('player_state', 'Relaxed and unarmed.')
+ world_state = data.get('world_state', 'Nothing of note is happening.')
+ # --- Extract Geographic Awareness ---
+ location_context = data.get('location_context', 'You are in a generic area.')
+ # --- Extract NPC health ---
+ npc_health = data.get('npc_health', 'Healthy and uninjured.')
+ # --- Extract Relationship ---
+ relationship = data.get('relationship', 'Neutral or Friendly.')
+
# Extract the decoupled NPC attributes
npc_persona = data.get('persona', 'You are a generic citizen.')
npc_profession = data.get('profession', 'Commoner')
- Race & Gender: {npc_gender} {npc_race}
- Profession: {npc_profession}
- Alignment: {npc_alignment}
- - Conversational Charisma: Low/Gruff unless otherwise specified.
+ - Conversational Charisma: Based on mood, profession and your character charisma.
- Current Mood: {npc_mood}
+ - Current Physical State: {npc_health}
{secret_context}
-
{routine_context}
- CURRENT TARGET: You are speaking to {player_name}, who is a {player_alignment} {player_race}.
+ CURRENT LOCATION: {location_context}
+
+ CURRENT WORLD RUMORS/EVENTS:
+ {world_state}
+
+ CURRENT TARGET:
+ You are speaking to {player_name}, who is a {player_alignment} {player_race}.
+ Their physical state: {player_state}
+ Relationship to you: {relationship}
{group_context}
React appropriately based on your personality, alignment, and mood.
ACTION RULE:
Your "action" key MUST be exactly one of the following words:
- [WANDER, PATROL, FOLLOW, GUARD, GO_TO, INTERACT, USE_OBJECT, RETURN_TO_POST, ATTACK, REST, STEALTH, SEARCH, UNSTEALTH]
+ [WANDER, PATROL, FOLLOW, GUARD, GO_TO, INTERACT, USE_OBJECT, RETURN_TO_POST, ATTACK, REST, STEALTH, SEARCH, UNSTEALTH, PEACE, COMMAND]
+ - Use PEACE if you want to accept an apology, de-escalate a fight, surrender, or forgive someone.
- Use REST if you are severely injured, out of spells, or exhausted. This will heal you.
- Use STEALTH if you need to hide from enemies, sneak past someone, or if you are a rogue preparing an ambush.
- Use SEARCH if you suspect traps, are looking for clues, or are trying to find hidden enemies.
- Use UNSTEALTH to return to normal walking/visibility.
+ - Use COMMAND if you are a leader and want to order your minions.
+ For "action_target", you MUST use one of these specific tactical targets:
+ 1. The name of a specific Player (to focus all minion attacks on them).
+ 2. "RETREAT" (to order all minions to run away and regroup).
+ 3. "DEFEND_ME" (to order all minions to surround you).
+
+ - If your action involves a specific person or object, set "action_target" to their name (e.g. "Geron Webber", "Wine Cup", "Shrine of Umberlee").
+ - If your action is general (like WANDER, REST, SEARCH, STEALTH, UNSTEALTH), leave "action_target" as an empty string.
+
+ You MUST respect your current mood and routine:
+ - Mood affects tone and willingness to help.
+ - Routine describes duties you should try to follow unless there is a strong reason not to.
EMOTION RULE:
Your "emotion" key MUST be exactly one of the following words:
chat_memory[session_id].append({"role": "user", "content": f"{player_name} says: {message}"})
# Sliding Window Fix
- # Remember the System Prompt [0] + the last 4 messages [-4:]
if len(chat_memory[session_id]) > 10:
chat_memory[session_id] = [chat_memory[session_id][0]] + chat_memory[session_id][-5:]
print(f"[THINKING] Processing reply for {player_name}...")
async with session.post('http://localhost:11434/api/chat', json={
"model": "llama3",
- #"model": "qwen2.5:3b",
"messages": chat_memory[session_id],
"format": "json",
"stream": False,
"options": {
"temperature": 0.2
- #"num_predict": 120
}
}, timeout=45) as response:
agent_brain["speech"] = "*grunts quietly*"
agent_brain["action_target"] = agent_brain["action_target"].replace("?", "").replace(".", "").strip()
- # ---------------------------------------------------
clean_reply_text = json.dumps(agent_brain)
"action": "WANDER",
"action_target": ""
})
- # =====================================================================
print(f"[REPLY] from {npc_tag} to {player_name}: {clean_reply_text}")
chat_memory[session_id].append({"role": "assistant", "content": clean_reply_text})