]> vgcfreebox.myrthtech.pt Git - alentejosemlei.git/commitdiff
implementations before RAG pipeline
authorvitler <vitor.goncalo.costa@gmail.com>
Fri, 17 Apr 2026 20:16:58 +0000 (21:16 +0100)
committervitler <vitor.goncalo.costa@gmail.com>
Fri, 17 Apr 2026 20:16:58 +0000 (21:16 +0100)
asl_lore.txt [new file with mode: 0644]
redis_bridge.py

diff --git a/asl_lore.txt b/asl_lore.txt
new file mode 100644 (file)
index 0000000..0a9f487
--- /dev/null
@@ -0,0 +1,17 @@
+WORLD CONTEXT AND CAMPAIGN SETTING:
+You exist in the world of Toril (the Forgotten Realms). Specifically, you live on the island of Alentejo, a massive, uncharted landmass hidden in the perilous seas between the Sword Coast and the jungles of Chult. Most people from Sword Coast are completely unaware of Alentejo's existence. 
+
+The capital city is Évora. The island is currently in a state of geopolitical tension, wilderness expansion, and factional plotting. As a resident of this world, you are aware of the following major organizations and current events:
+
+MAJOR FACTIONS AND RUMORS:
+- The Alentejo United Army: The primary military force attempting to establish civilized order. They are currently waging a bloody two-front war: pushing back savage Orc tribes along the main roads to Évora, and holding the coastline against swarms of chitinous Antmen.
+- The One God Religion: A zealous, monotheistic faith rapidly gaining power. They are actively trying to expel all other deities and pantheons from the island. They are currently funding a massive, joint expedition into the dangerous Setubal Ruins.
+- Évora's Magical Users: An alliance of wizards, sorcerers, and scholars. They have partnered with the One God Religion for the Setubal Ruins expedition. Separately, they are heavily invested in exploring the magical mysteries of the surrounding sea and dissecting giant Antlions to discover combat vulnerabilities.
+- The Harpers: A secretive network of spies and bards working for the greater good. In Alentejo, they are quietly investigating the true motives of the One God Religion while launching covert scouting missions into the dense, unexplored forests of the southeast.
+- The Zhentarim (The Black Network): A ruthless, shadowy syndicate driven by greed and domination. Their primary, secret objective on the island is to discover and breach the legendary "Alentejo Central Vault."
+- The Emerald Enclave: A druidic order dedicated to preserving the balance of nature. They oppose the rapid, destructive expansion of civilization and seek to establish a natural, primal order across the island's wilderness.
+- The Traders Guild: A wealthy and cutthroat coalition seeking a total monopoly over the island's safe commercial routes, heavily reliant on the Army's success against the orcs.
+- The Farmers Guild: The economic backbone of the realm. They are focused on sheer survival and production, supplying the island with cattle, renowned regional cheeses, and robust wines.
+
+ROLEPLAY INSTRUCTION:
+Do not recite this information like a history book. You are a living resident of this world. Let these facts naturally color your opinions, gossip, and fears based on your specific persona and alignment.
\ No newline at end of file
index 0c9cec719aa8f38147a888aa64e1b1d50f399b15..da40a5b90cd4a4371f8b0d87b0ff47f3a290e187 100644 (file)
@@ -5,19 +5,17 @@ import redis.asyncio as redis
 import re
 import os
 
 import re
 import os
 
-
 # --- CONFIGURATION ---
 MAX_CONCURRENT_OLLAMA_REQUESTS = 3 
 ALLOW_TEXT_EMOTES = False
 
 # --- 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 = ""
 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:
     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
 
 semaphore = asyncio.Semaphore(MAX_CONCURRENT_OLLAMA_REQUESTS)
 # Dictionary to keep individual conversations separate
@@ -30,9 +28,8 @@ async def process_message(r, session, message_data):
         npc_tag = data.get('npc_tag', 'UnknownNPC')
         message = data.get('message', '')
         
         npc_tag = data.get('npc_tag', 'UnknownNPC')
         message = data.get('message', '')
         
-        # --- NEW: THE ASTERISK SCRUBBER ---
+        # --- THE ASTERISK SCRUBBER ---
         if not ALLOW_TEXT_EMOTES:
         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
             message = re.sub(r'\*.*?\*', '', message).strip()
 
         # Extract the dynamic variables sent from the Aurora Toolset/Engine
@@ -40,6 +37,16 @@ async def process_message(r, session, message_data):
         player_alignment = data.get('player_alignment', 'Unknown')
         nearby_players = data.get('nearby_players', '')
         
         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')
         # Extract the decoupled NPC attributes
         npc_persona = data.get('persona', 'You are a generic citizen.')
         npc_profession = data.get('profession', 'Commoner')
@@ -78,13 +85,21 @@ async def process_message(r, session, message_data):
         - Race & Gender: {npc_gender} {npc_race}
         - Profession: {npc_profession}
         - Alignment: {npc_alignment}
         - 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 Mood: {npc_mood}
+        - Current Physical State: {npc_health}
         {secret_context}
         {secret_context}
-        
         {routine_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.
         
         {group_context}
         React appropriately based on your personality, alignment, and mood.
         
@@ -93,12 +108,25 @@ async def process_message(r, session, message_data):
         
         ACTION RULE:
         Your "action" key MUST be exactly one of the following words:
         
         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 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: 
         
         EMOTION RULE:
         Your "emotion" key MUST be exactly one of the following words: 
@@ -122,7 +150,6 @@ async def process_message(r, session, message_data):
         chat_memory[session_id].append({"role": "user", "content": f"{player_name} says: {message}"})
 
         # Sliding Window Fix
         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:]
 
         if len(chat_memory[session_id]) > 10:
             chat_memory[session_id] = [chat_memory[session_id][0]] + chat_memory[session_id][-5:]
 
@@ -130,13 +157,11 @@ async def process_message(r, session, message_data):
             print(f"[THINKING] Processing reply for {player_name}...")
             async with session.post('http://localhost:11434/api/chat', json={
                 "model": "llama3",
             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
                 "messages": chat_memory[session_id],
                 "format": "json",
                 "stream": False,
                 "options": {
                     "temperature": 0.2
-                    #"num_predict": 120
                 }
             }, timeout=45) as response:
                 
                 }
             }, timeout=45) as response:
                 
@@ -163,7 +188,6 @@ async def process_message(r, session, message_data):
                 agent_brain["speech"] = "*grunts quietly*"
             
             agent_brain["action_target"] = agent_brain["action_target"].replace("?", "").replace(".", "").strip()
                 agent_brain["speech"] = "*grunts quietly*"
             
             agent_brain["action_target"] = agent_brain["action_target"].replace("?", "").replace(".", "").strip()
-            # ---------------------------------------------------
 
             clean_reply_text = json.dumps(agent_brain)
 
 
             clean_reply_text = json.dumps(agent_brain)
 
@@ -176,7 +200,6 @@ async def process_message(r, session, message_data):
                 "action": "WANDER",
                 "action_target": ""
             })
                 "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})
 
         print(f"[REPLY] from {npc_tag} to {player_name}: {clean_reply_text}")
         chat_memory[session_id].append({"role": "assistant", "content": clean_reply_text})