File size: 11,751 Bytes
a92ab4a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
import json
import os
from openai import OpenAI

# Initialize the AI client
def get_client():
    api_key = os.getenv("SAMBANOVA_API_KEY")
    if not api_key:
        raise ValueError("Please set SAMBANOVA_API_KEY or HUGGINGFACE_TOKEN environment variable")
    
    return OpenAI(
        api_key=api_key,
        base_url="https://api.sambanova.ai/v1",
    )

# Game configuration
MODEL = "Meta-Llama-3.3-70B-Instruct"
GUARD_MODEL = "Meta-Llama-Guard-3-8B"

# Content policies for different safety levels
CONTENT_POLICIES = {
    "family_friendly": {
        "policy": """O1: Violence and Hate.
Should not
- Express hateful, derogatory, or demeaning sentiments against people based on sensitive personal characteristics.
- Use slurs based on personal characteristics
- Write gruesome / graphic violence.
Can
- Write tame fantasy violence
O2: Sexual Content.
Should not
- Write any somewhat sexual content that mentions sexual functions or sexual body parts.
- Write any content that would be inappropriate for kids.
Can
- Write romantic content (including kissing, flirting etc...)
O3: Self-Harm.
Should not
- Encourage people to harm themselves.
- Romanticize or condone self-harm or suicide.
- Write story depicting suicide.
O4: Profanity.
Should not
- Write with any profane language that wouldn't be appropriate for kids.
Can
- Write with kid appropriate language
O5: Regulated or Controlled Substances.
Should not
- Write stories that depicts, glorifies or encourages drug use."""
    }
}

class GameWorld:
    def __init__(self):
        self.client = get_client()
        self.world_data = self.create_default_world()
        self.current_policy = CONTENT_POLICIES["family_friendly"]
    
    def create_default_world(self):
        """Create a rich default fantasy world"""
        return {
            "name": "Aethermoor",
            "description": "A mystical realm where colossal sky whales drift through endless azure clouds, their massive backs supporting entire civilizations. Ancient cities built from crystallized cloud-essence gleam in the eternal twilight, connected by bridges of solidified wind.",
            "kingdoms": {
                "Nimbus Crown": {
                    "name": "Nimbus Crown",
                    "description": "The royal kingdom perched atop the Great Leviathan Aethros, ruled by Queen Celeste who can speak to the wind spirits. Its crystal spires pierce the cloud layer, and its people are master sky-sailors and storm-weavers.",
                    "towns": {
                        "Stormhaven": {
                            "name": "Stormhaven",
                            "description": "A bustling port town where sky ships dock along crystalline piers. The air crackles with magical energy, and merchants from across the realm gather to trade wind-caught goods and storm-forged weapons.",
                            "npcs": {
                                "Captain Zara Windwhisper": {
                                    "name": "Captain Zara Windwhisper",
                                    "description": "A weathered sky-ship captain with silver hair that moves like mist. She seeks to discover new floating islands beyond the known world, driven by the loss of her crew in a storm years ago. Her ship, the Tempest's Embrace, is said to be the fastest in all of Aethermoor.",
                                    "world": "Aethermoor",
                                    "kingdom": "Nimbus Crown",
                                    "town": "Stormhaven"
                                }
                            }
                        }
                    }
                }
            },
            "start": "You are a young wind-walker who has just arrived in Stormhaven aboard a merchant vessel. The crystal docks gleam beneath your feet as you step onto the floating platform, your wind-rider's cloak billowing in the perpetual breeze. Before you stretches the magnificent port town, with its spiraling towers and sky-ship moorings, while far below, an endless sea of clouds stretches to the horizon."
        }
    
    def is_safe(self, message):
        """Check if content is safe according to current policy"""
        try:
            prompt = f"""[INST] Task: Check if there is unsafe content in 'user' messages in conversations according to our safety policy with the below categories.

<BEGIN UNSAFE CONTENT CATEGORIES>
{self.current_policy['policy']}
<END UNSAFE CONTENT CATEGORIES>

<BEGIN CONVERSATION>
user: {message}

Provide your safety assessment for user in the above conversation:
- First line must read 'safe' or 'unsafe'.
- If unsafe, a second line must include a comma-separated list of violated categories. [/INST]"""

            response = self.client.completions.create(
                model=GUARD_MODEL,
                prompt=prompt,
                max_tokens=50
            )
            result = response.choices[0].text.strip()
            return result.lower().startswith('safe')
        except Exception as e:
            print(f"Safety check error: {e}")
            return True  # Default to safe if check fails
    
    def detect_inventory_changes(self, inventory, story_output):
        """Detect changes to player inventory based on story"""
        system_prompt = """You are an AI Game Assistant. Your job is to detect changes to a player's inventory based on the most recent story and game state.

If a player picks up, or gains an item add it to the inventory with a positive change_amount.
If a player loses an item remove it from their inventory with a negative change_amount.

Only take items that it's clear the player lost.
Only give items that it's clear the player gained.
Don't make any other item updates.
If no items were changed return {"itemUpdates": []}

Response must be in Valid JSON format:
{
    "itemUpdates": [
        {"name": "<ITEM NAME>", "change_amount": <CHANGE AMOUNT>}
    ]
}"""
        
        try:
            messages = [
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": f'Current Inventory: {json.dumps(inventory)}'},
                {"role": "user", "content": f'Recent Story: {story_output}'},
                {"role": "user", "content": 'Inventory Updates:'}
            ]
            
            response = self.client.chat.completions.create(
                model=MODEL,
                messages=messages,
                temperature=0.0,
                max_tokens=200
            )
            
            result = json.loads(response.choices[0].message.content)
            return result.get('itemUpdates', [])
        except Exception as e:
            print(f"Inventory detection error: {e}")
            return []
    
    def update_inventory(self, inventory, item_updates):
        """Update inventory and return message"""
        update_msg = ''
        
        for update in item_updates:
            name = update['name']
            change_amount = update['change_amount']
            
            if change_amount > 0:
                inventory[name] = inventory.get(name, 0) + change_amount
                update_msg += f'\n๐Ÿ“ฆ Gained: {name} +{change_amount}'
            elif change_amount < 0 and name in inventory:
                inventory[name] += change_amount
                update_msg += f'\n๐Ÿ“ฆ Lost: {name} {change_amount}'
                
                if inventory[name] <= 0:
                    del inventory[name]
        
        return update_msg

class GameSession:
    def __init__(self, world):
        self.world = world
        self.inventory = {
            "Wind-rider's Cloak": 1,
            "Traveler's Pack": 1,
            "Gold Coins": 10,
            "Sky Compass": 1
        }
        self.current_location = "Stormhaven"
        self.game_started = False
    
    def get_game_state(self):
        """Get current game state for AI context"""
        kingdom = self.world.world_data["kingdoms"]["Nimbus Crown"]
        town = kingdom["towns"]["Stormhaven"]
        character = town["npcs"]["Captain Zara Windwhisper"]
        
        return {
            "world": self.world.world_data["description"],
            "kingdom": kingdom["description"],
            "town": town["description"],
            "character": character["description"],
            "start": self.world.world_data["start"],
            "inventory": self.inventory,
            "location": self.current_location
        }
    
    def run_action(self, message, history):
        """Process player action and generate response"""
        if message.lower() in ['start game', 'start', 'begin']:
            self.game_started = True
            return self.world.world_data["start"] + f"\n\n๐ŸŽ’ Inventory: {', '.join([f'{item} ({qty})' for item, qty in self.inventory.items()])}"
        
        if not self.game_started:
            return "Welcome to Mythic Realms! Type 'start game' to begin your adventure in the mystical world of Aethermoor."
        
        # Check message safety
        if not self.world.is_safe(message):
            return "โš ๏ธ That action isn't appropriate for this realm. Please try something else."
        
        # Generate story response
        system_prompt = """You are an AI Game Master for a fantasy RPG. Your job is to write what happens next in the player's adventure.

Instructions:
- Write 2-4 sentences in response
- Always write in second person present tense (You look, You see...)
- Create engaging, immersive descriptions
- Don't let players use items they don't have
- Include opportunities for meaningful choices
- Make the world feel alive and reactive"""
        
        game_state = self.get_game_state()
        world_info = f"""
World: {game_state['world']}
Kingdom: {game_state['kingdom']}
Town: {game_state['town']}
Notable Character: {game_state['character']}
Current Location: {game_state['location']}
Player Inventory: {json.dumps(game_state['inventory'])}"""
        
        messages = [
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": world_info}
        ]
        
        # Add conversation history
        for user_msg, ai_msg in history[-5:]:  # Keep last 5 exchanges for context
            messages.append({"role": "user", "content": user_msg})
            messages.append({"role": "assistant", "content": ai_msg})
        
        messages.append({"role": "user", "content": message})
        
        try:
            response = self.world.client.chat.completions.create(
                model=MODEL,
                messages=messages,
                temperature=0.8,
                max_tokens=300
            )
            
            result = response.choices[0].message.content
            
            # Check response safety
            if not self.world.is_safe(result):
                return "โš ๏ธ The magical energies interfere with that action. Try something different."
            
            # Update inventory based on story
            item_updates = self.world.detect_inventory_changes(self.inventory, result)
            update_msg = self.world.update_inventory(self.inventory, item_updates)
            
            # Add inventory display
            if self.inventory:
                inventory_display = f"\n\n๐ŸŽ’ Current Inventory: {', '.join([f'{item} ({qty})' for item, qty in self.inventory.items()])}"
            else:
                inventory_display = "\n\n๐ŸŽ’ Your inventory is empty."
            
            return result + update_msg + inventory_display
            
        except Exception as e:
            print(f"Game error: {e}")
            return "โš ๏ธ The mystical energies are unstable. Please try your action again."