Roland-1 commited on
Commit
625185a
·
1 Parent(s): 7912450

Added the app

Browse files
Files changed (3) hide show
  1. .env +1 -0
  2. .gradio/certificate.pem +31 -0
  3. app.py +271 -0
.env ADDED
@@ -0,0 +1 @@
 
 
1
+ OPENAI_API_KEY=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
.gradio/certificate.pem ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -----BEGIN CERTIFICATE-----
2
+ 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
31
+ -----END CERTIFICATE-----
app.py ADDED
@@ -0,0 +1,271 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Core LlamaIndex imports
2
+ from llama_index.core import (
3
+ VectorStoreIndex,
4
+ SimpleDirectoryReader,
5
+ Document,
6
+ Settings
7
+ )
8
+ from llama_index.llms.openai import OpenAI
9
+ from llama_index.core.memory import ChatMemoryBuffer
10
+
11
+ # Python standard library imports
12
+ import os
13
+ import re
14
+ import sys
15
+ from datetime import datetime
16
+ from typing import List, Dict
17
+
18
+ # Third-party imports
19
+ from dotenv import load_dotenv
20
+ import gradio as gr
21
+
22
+ # Load environment variables
23
+ load_dotenv()
24
+
25
+ # Check for API key
26
+ if not os.getenv("OPENAI_API_KEY"):
27
+ print("Error: OpenAI API key not found in .env file")
28
+ print("Please create a .env file with your API key: API_KEY=your_key_here")
29
+ sys.exit(1)
30
+
31
+ # Configure global settings
32
+ Settings.llm = OpenAI(api_key=os.getenv("OPENAI_API_KEY"), model="gpt-4o-mini")
33
+ Settings.chunk_size = 1024
34
+ Settings.chunk_overlap = 20
35
+
36
+ class PersonalityAnalyzer:
37
+ def __init__(self):
38
+ self.memory = ChatMemoryBuffer.from_defaults(token_limit=4096)
39
+
40
+ def analyze_mbti(self, conversation_history: List[Dict]) -> str:
41
+ # Create a document from conversation history
42
+ history_text = self._format_history_for_analysis(conversation_history)
43
+ doc = Document(text=history_text)
44
+
45
+ # Create an index for the conversation history
46
+ index = VectorStoreIndex.from_documents([doc])
47
+
48
+ # Create a query engine
49
+ query_engine = index.as_query_engine()
50
+
51
+ prompt = """
52
+ Based on the conversation history, analyze the player's likely MBTI personality type.
53
+ Consider:
54
+ - How they approach problems (direct vs. creative)
55
+ - Their interaction style with NPCs
56
+ - Whether they seek information or action
57
+ - How they make decisions
58
+
59
+ Provide the most likely MBTI type with reasoning based on their actions.
60
+ """
61
+
62
+ response = query_engine.query(prompt)
63
+ return str(response)
64
+
65
+ def analyze_enneagram(self, conversation_history: List[Dict]) -> str:
66
+ # Create a document from conversation history
67
+ history_text = self._format_history_for_analysis(conversation_history)
68
+ doc = Document(text=history_text)
69
+
70
+ # Create an index for the conversation history
71
+ index = VectorStoreIndex.from_documents([doc])
72
+
73
+ # Create a query engine
74
+ query_engine = index.as_query_engine()
75
+
76
+ prompt = """
77
+ Based on the conversation history, analyze the player's likely Enneagram type.
78
+ Consider:
79
+ - Their core motivations and fears shown through actions
80
+ - What rewards or outcomes they seek
81
+ - How they handle challenges
82
+ - Their interaction patterns with the world
83
+
84
+ Provide the most likely Enneagram type with reasoning based on their actions.
85
+ """
86
+
87
+ response = query_engine.query(prompt)
88
+ return str(response)
89
+
90
+ def _format_history_for_analysis(self, history: List[Dict]) -> str:
91
+ return "\n".join([
92
+ f"Player: {h['action']}\nContext: {h['context']}\nResponse: {h['response']}"
93
+ for h in history
94
+ ])
95
+
96
+ class WorldState:
97
+ def __init__(self):
98
+ self.inventory: List[str] = []
99
+ self.relationships: Dict[str, int] = {} # NPC relationships
100
+ self.current_context: str = ""
101
+ self.significant_choices: List[Dict] = [] # Tracks important decisions
102
+ self.conversation_history: List[Dict] = []
103
+ self.discovered_locations: set = set()
104
+ self.active_quests: List[Dict] = []
105
+
106
+ def add_to_history(self, action: str, context: str, response: str):
107
+ self.conversation_history.append({
108
+ "timestamp": datetime.now().isoformat(),
109
+ "action": action,
110
+ "context": context,
111
+ "response": response
112
+ })
113
+
114
+ class StoryEngine:
115
+ def __init__(self):
116
+ self.memory = ChatMemoryBuffer.from_defaults(token_limit=4096)
117
+
118
+ def generate_response(self, action: str, world_state: WorldState) -> str:
119
+ # Build context from recent history
120
+ recent_history = world_state.conversation_history[-5:] if world_state.conversation_history else []
121
+ history_text = "\n".join([
122
+ f"Player: {h['action']}\nGame: {h['response']}"
123
+ for h in recent_history
124
+ ])
125
+
126
+ context = f"""
127
+ Current game state:
128
+ Inventory: {world_state.inventory}
129
+ Active quests: {world_state.active_quests}
130
+ Discovered locations: {world_state.discovered_locations}
131
+
132
+ Recent history:
133
+ {history_text}
134
+ """
135
+
136
+ doc = Document(text=context)
137
+ index = VectorStoreIndex.from_documents([doc])
138
+
139
+ query_engine = index.as_query_engine()
140
+
141
+ prompt = f"""
142
+ Based on the current game state and player's action: "{action}"
143
+ Aims to capture the personality type of the user in terms of MBTI (present scenarios/quests that may reveal that) (DON'T Explicitly mention that)
144
+
145
+ Generate a response that:
146
+ 2. Acknowledges the player's action
147
+ 3. Describes the consequences
148
+ 4. Updates the world state if needed
149
+ 5. Introduces potential opportunities or challenges
150
+ 6. Maintains internal consistency with previous events
151
+ 7. Is short and doesn't contain unnecessary details or elaborate language
152
+
153
+ [Important] Don't explicitly mention world state items.
154
+ [Important] Try to present scenarios or developments which aims to reveal personality traits of the player.
155
+ [Important] Response should be engaging, consequential and open-ended. It shouldn't lead the player at all, don't mention ANY call to action or what a player does or feel without an explicit prompt from the user.
156
+ """
157
+
158
+ response = query_engine.query(prompt)
159
+ return str(response)
160
+
161
+ class TextAdventure:
162
+ def __init__(self):
163
+ self.story_engine = StoryEngine()
164
+ self.personality_analyzer = PersonalityAnalyzer()
165
+ self.world_state = WorldState()
166
+
167
+ def _extract_game_updates(self, response: str) -> None:
168
+ """Parse response for any updates to world state"""
169
+ # Look for inventory changes
170
+ inventory_matches = re.findall(r"You (picked up|received|found|lost|dropped) (a|an|the) ([^\.]+)", response.lower())
171
+ for action, _, item in inventory_matches:
172
+ if action in ['picked up', 'received', 'found']:
173
+ self.world_state.inventory.append(item.strip())
174
+ elif action in ['lost', 'dropped']:
175
+ if item.strip() in self.world_state.inventory:
176
+ self.world_state.inventory.remove(item.strip())
177
+
178
+ # Look for new locations
179
+ location_matches = re.findall(r"You (discovered|arrived at|entered) (the|a|an) ([^\.]+)", response.lower())
180
+ for _, _, location in location_matches:
181
+ self.world_state.discovered_locations.add(location.strip())
182
+
183
+ def gradio_play(self, action, history):
184
+ if action.lower() in ['quit', 'exit', 'bye']:
185
+ self._show_personality_analysis()
186
+ return history + [[action, "Game ended. Thanks for playing!"]], history + [[action, "Game ended. Thanks for playing!"]]
187
+
188
+ if action.lower() in ['inventory', 'i']:
189
+ inventory_text = "\nInventory: " + (", ".join(self.world_state.inventory) or "empty")
190
+ return history + [[action, inventory_text]], history + [[action, inventory_text]]
191
+
192
+ if action.lower() in ['help', 'h', '?']:
193
+ help_text = self._get_help_text()
194
+ return history + [[action, help_text]], history + [[action, help_text]]
195
+
196
+ response = self.story_engine.generate_response(action, self.world_state)
197
+
198
+ # Update world state
199
+ self.world_state.add_to_history(action, self.world_state.current_context, response)
200
+ self._extract_game_updates(response)
201
+
202
+ # Periodically show personality insights
203
+ if len(self.world_state.conversation_history) % 10 == 0:
204
+ response += "\n\n" + self._get_personality_analysis()
205
+
206
+ return history + [[action, response]], history + [[action, response]]
207
+
208
+ def _get_help_text(self):
209
+ return """
210
+ Game Help:
211
+ - Type any action you want to try
212
+ - Use 'inventory' or 'i' to check your items
213
+ - Type 'quit' to end the game
214
+ - The story adapts to your choices
215
+ - Your actions reveal your personality
216
+ """
217
+
218
+ def _get_personality_analysis(self):
219
+ if len(self.world_state.conversation_history) < 3:
220
+ return "Need more interactions for personality analysis..."
221
+
222
+ mbti = self.personality_analyzer.analyze_mbti(self.world_state.conversation_history)
223
+ enneagram = self.personality_analyzer.analyze_enneagram(self.world_state.conversation_history)
224
+
225
+ return f"""
226
+ === Personality Analysis ===
227
+
228
+ MBTI Analysis:
229
+ {mbti}
230
+
231
+ Enneagram Analysis:
232
+ {enneagram}
233
+
234
+ =========================
235
+ """
236
+
237
+ if __name__ == "__main__":
238
+ game = TextAdventure()
239
+
240
+ with gr.Blocks() as demo:
241
+ chatbot = gr.Chatbot([], elem_id="chatbot",height=500)
242
+
243
+ with gr.Row():
244
+ with gr.Column(scale=0.85):
245
+ txt = gr.Textbox(
246
+ show_label=False,
247
+ placeholder="Enter your action...",container=False
248
+ )
249
+ with gr.Column(scale=0.15, min_width=0):
250
+ btn = gr.Button("Send")
251
+
252
+ btn.click(game.gradio_play, [txt, chatbot], [chatbot, chatbot])
253
+ txt.submit(game.gradio_play, [txt, chatbot], [chatbot, chatbot])
254
+
255
+ gr.Markdown(
256
+ """
257
+ Welcome to the Open-Ended Personality Adventure! This is a completely free-form adventure where you can try anything you want. Your choices and actions will reveal aspects of your personality.
258
+
259
+ You find yourself standing in a mysterious forest clearing on a misty morning. What would you like to do?
260
+
261
+ Game Help:
262
+ - Type any action you want to try
263
+ - Use 'inventory' or 'i' to check your items
264
+ - Type 'quit' to end the game
265
+ - The story adapts to your choices
266
+ - Your actions reveal your personality
267
+
268
+ """
269
+ )
270
+
271
+ demo.launch(share=True)