alexchilton Claude commited on
Commit
d853461
·
1 Parent(s): 3b03013

feat: Add character-aware gameplay and Gradio web UI (Phase 8)

Browse files

Major Features:
- 🌐 Gradio web interface (app_gradio.py) for browser-based gameplay
- 🎭 Character-aware dialogue system (play_with_character.py)
- 🎲 Pre-made characters: Thorin Stormshield (Fighter) & Elara Moonwhisper (Wizard)
- 📊 Live character stats displayed during gameplay
- ⚡ Command system: /stats, /context, /rag, /character

Technical Improvements:
- Suppressed tokenizer parallelism warning
- Fixed first/second person context ("The player is X" → AI uses "you")
- Dynamic character support (load from JSON or create new)
- Three play modes: Web UI, CLI, or legacy GM dialogue

Documentation Updates:
- README: Comprehensive gameplay instructions for Thorin and Elara
- README: Full commands reference (/context, /rag, /stats)
- README: Gradio and CLI interface documentation
- plan_progress.md: Added Phase 8 (Game Mechanics Engine)
- plan_progress.md: Documented hybrid AI + Rules Engine architecture (Option B)
- requirements.txt: Added gradio>=4.0.0

Architecture Notes (Phase 8):
- Discovered AI reliability issues (ignores valid spells, allows invalid ones)
- Selected hybrid approach: Rules engine intercepts actions before AI narration
- Future work: Spell validation, HP tracking, combat mechanics

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

Files changed (5) hide show
  1. README.md +163 -39
  2. app_gradio.py +328 -0
  3. plan_progress.md +118 -7
  4. play_with_character.py +269 -0
  5. requirements.txt +3 -0
README.md CHANGED
@@ -196,66 +196,188 @@ Sage | Neutral Good
196
 
197
  #### Step 4: Play D&D with AI Game Master
198
 
199
- Run an interactive D&D session with RAG-enhanced AI GM:
 
 
 
 
 
 
 
 
 
 
200
 
201
  ```bash
202
- python run_gm_dialogue.py
203
  ```
204
 
205
- **Prerequisites:**
206
- - Install Ollama: https://ollama.ai
207
- - Download RPG model: `ollama pull hf.co/Chun121/Qwen3-4B-RPG-Roleplay-V2:Q4_K_M`
208
 
209
- **What this does:**
210
- - Interactive D&D game session with AI Dungeon Master
211
- - RAG-powered rule lookups in real-time
212
- - GM searches ChromaDB for spells, monsters, classes when relevant
213
- - Conversation history and context management
214
- - Commands for scene setting, history review, and more
 
 
 
 
 
 
215
 
216
- **Example session:**
217
  ```
218
- 🎲 D&D GAME MASTER - RAG-Enhanced AI Dungeon Master
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  Model: hf.co/Chun121/Qwen3-4B-RPG-Roleplay-V2:Q4_K_M
220
 
221
- Type /help for commands or just start playing!
222
  ======================================================================
223
 
224
- 🎲 You: I cast fireball at the goblins
 
 
 
 
 
 
 
 
 
 
 
 
225
 
226
- 🎭 GM: Roll for your attack! The spell requires a DC 15 Dexterity saving
227
- throw from each goblin. Roll 8d6 for fire damage. Each goblin in the
228
- 20-foot radius must make their save - on a success, they take half damage.
229
 
230
- 🎲 You: I investigate the room
 
 
 
 
 
231
 
232
- 🎭 GM: Make an Investigation check (roll d20 + INT modifier). I'll set
233
- the DC based on what you're looking for...
 
 
 
 
 
 
234
  ```
235
 
236
- **Available Commands:**
237
  ```
238
- /help - Show available commands
239
- /context <text> - Set the current scene/context
240
  /history - Show conversation history
241
- /rag <query> - Test RAG search (see what the GM knows)
242
  /save <file> - Save session to JSON
243
  /quit - Exit the game
244
  ```
245
 
246
- **How It Works:**
247
- 1. Player enters action or question
248
- 2. GM searches ChromaDB for relevant spells/monsters/rules
249
- 3. RAG results are injected into the LLM prompt
250
- 4. AI GM generates response using accurate D&D 5e rules
251
- 5. Response is shown to player with proper mechanics
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
 
253
- **Tips:**
254
- - Be specific: "I cast fireball" vs "I attack"
255
- - The GM will ask for dice rolls when needed
256
- - Use `/context` to set the scene for better immersion
257
- - Use `/rag` to check if the GM has specific rule information
258
- - The system works best with clear, action-oriented inputs
 
 
 
 
 
 
 
 
 
259
 
260
  #### Step 5: Run Interactive Searches (Optional)
261
 
@@ -364,8 +486,10 @@ You should see the Qwen3-4B-RPG model in the list.
364
 
365
  ├── chromadb/ # Vector database (created on init)
366
  ├── initialize_rag.py # Main initialization script ⭐
367
- ├── query_rag.py # Interactive query CLI ⭐ NEW!
368
- ├── test_all_collections.py # Comprehensive test suite ⭐ NEW!
 
 
369
  ├── test_spell_search.py # Manual search testing
370
  ├── create_character.py # Character creator launcher
371
  ├── run_gm_dialogue.py # AI GM dialogue launcher
 
196
 
197
  #### Step 4: Play D&D with AI Game Master
198
 
199
+ **Prerequisites:**
200
+ - Install Ollama: https://ollama.ai
201
+ - Download RPG model: `ollama pull hf.co/Chun121/Qwen3-4B-RPG-Roleplay-V2:Q4_K_M`
202
+
203
+ You have **two ways** to play:
204
+
205
+ ---
206
+
207
+ ### Option A: 🌐 Web Interface (Gradio) **⭐ RECOMMENDED**
208
+
209
+ Launch the web UI for the best experience:
210
 
211
  ```bash
212
+ python app_gradio.py
213
  ```
214
 
215
+ Then open http://localhost:7860 in your browser.
 
 
216
 
217
+ **Features:**
218
+ - 🎭 **Pre-made Characters**: Play as Thorin Stormshield (Dwarf Fighter) or Elara Moonwhisper (Elf Wizard)
219
+ - 💬 **Chat Interface**: Clean conversation view with the AI GM
220
+ - 📊 **Character Sheet**: Live character stats displayed in sidebar
221
+ - **Quick Commands**: Built-in buttons for common actions
222
+ - 🎲 **RAG Search**: Test spell/monster lookups directly in the UI
223
+
224
+ **Quick Start:**
225
+ 1. Select a character from dropdown (Thorin or Elara)
226
+ 2. Click "Load Character"
227
+ 3. Type your action in the chat box
228
+ 4. The GM responds with RAG-enhanced D&D rules!
229
 
230
+ **Example Actions:**
231
  ```
232
+ # As Thorin (Fighter):
233
+ I draw my longsword and look for enemies
234
+ I attack with my weapon
235
+ /stats
236
+
237
+ # As Elara (Wizard):
238
+ I cast Magic Missile at the goblin
239
+ I look through my spellbook
240
+ /rag Magic Missile
241
+ ```
242
+
243
+ ---
244
+
245
+ ### Option B: 💻 Command Line Interface
246
+
247
+ For terminal lovers, use the CLI version:
248
+
249
+ ```bash
250
+ python play_with_character.py
251
+ ```
252
+
253
+ **Character Selection:**
254
+ ```
255
+ 1. Create new character (full interactive creation)
256
+ 2. Load existing character (from JSON file)
257
+ 3. Use test character (quick start with Thorin)
258
+ ```
259
+
260
+ **Available Characters:**
261
+
262
+ **Thorin Stormshield** - Level 3 Dwarf Fighter
263
+ - HP: 28 | AC: 18 | Proficiency: +2
264
+ - STR 16 (+3) | DEX 12 (+1) | CON 16 (+3)
265
+ - Equipment: Longsword, Shield, Plate Armor
266
+ - Perfect for: Melee combat, tanking, straightforward gameplay
267
+
268
+ **Elara Moonwhisper** - Level 2 Elf Wizard
269
+ - HP: 14 | AC: 12 | Proficiency: +2
270
+ - INT 17 (+3) | DEX 14 (+2) | CON 12 (+1)
271
+ - Spells: Fire Bolt, Mage Hand, Magic Missile, Shield
272
+ - Equipment: Quarterstaff, Spellbook, Component Pouch
273
+ - Perfect for: Spellcasting, strategic gameplay, testing RAG
274
+
275
+ **Example Session:**
276
+ ```
277
+ 🎲 D&D GAME SESSION - Playing as Elara Moonwhisper
278
+ ======================================================================
279
+ Character: Elara Moonwhisper (Elf Wizard)
280
  Model: hf.co/Chun121/Qwen3-4B-RPG-Roleplay-V2:Q4_K_M
281
 
282
+ Type /help for commands or start playing!
283
  ======================================================================
284
 
285
+ 🎲 Elara Moonwhisper: I look around the tavern
286
+
287
+ 🎭 GM: The tavern is dimly lit by flickering torches. You notice a group
288
+ of rough-looking adventurers in the corner, and a hooded figure sitting
289
+ alone by the fireplace...
290
+
291
+ 🎲 Elara Moonwhisper: /stats
292
+
293
+ 📊 Elara Moonwhisper | HP: 14 | AC: 12 | Prof: +2
294
+ STR -1 | DEX +2 | CON +1 | INT +3 | WIS +1 | CHA +0
295
+ ```
296
+
297
+ ---
298
 
299
+ ### 🎮 Commands Reference
 
 
300
 
301
+ **Character Commands:**
302
+ ```
303
+ /character - Show full character sheet
304
+ /stats - Quick stats view (HP, AC, modifiers)
305
+ /context - View current scene and character context
306
+ ```
307
 
308
+ **RAG Commands:**
309
+ ```
310
+ /rag <query> - Search D&D knowledge base
311
+ Examples:
312
+ /rag Magic Missile - Look up spell details
313
+ /rag Goblin - Look up monster stats
314
+ /rag Fighter - Look up class features
315
+ /rag Elf - Look up race traits
316
  ```
317
 
318
+ **Session Commands:**
319
  ```
320
+ /help - Show all available commands
 
321
  /history - Show conversation history
 
322
  /save <file> - Save session to JSON
323
  /quit - Exit the game
324
  ```
325
 
326
+ ---
327
+
328
+ ### 💡 How It Works
329
+
330
+ **Character-Aware Gameplay:**
331
+ 1. Select or create a character (Thorin, Elara, or custom)
332
+ 2. Character stats, spells, and equipment are passed to the GM
333
+ 3. GM knows YOUR character and references it in responses
334
+
335
+ **RAG Integration:**
336
+ 1. You type an action (e.g., "I cast Magic Missile")
337
+ 2. System searches ChromaDB for relevant spells/monsters/rules
338
+ 3. RAG results are injected into the AI GM's context
339
+ 4. GM generates response using accurate D&D 5e rules
340
+ 5. Response references your character's abilities
341
+
342
+ **Example RAG Flow:**
343
+ ```
344
+ Input: "I cast Magic Missile at the goblin"
345
+
346
+ RAG Search: Finds "Magic Missile" spell
347
+
348
+ Context: "Elara (Wizard) has Magic Missile in spell list"
349
+
350
+ Context: "Magic Missile: 1st-level, 3 darts, 1d4+1 force damage each"
351
+
352
+ AI GM Response: Uses accurate spell mechanics + your character's stats
353
+ ```
354
+
355
+ ---
356
+
357
+ ### 🎯 Gameplay Tips
358
+
359
+ **For Best Results:**
360
+ - **Be Specific**: "I cast Fire Bolt at the closest goblin" vs "I attack"
361
+ - **Use RAG**: Test `/rag <spell>` before casting to see what the GM knows
362
+ - **Check Context**: Use `/context` to see what the GM knows about your character
363
+ - **View Stats**: Use `/stats` during combat to remember your modifiers
364
+ - **Roleplay**: The GM responds to narrative actions ("I cautiously enter the room")
365
 
366
+ **Testing Spells with Elara:**
367
+ ```
368
+ /rag Magic Missile # See full spell description
369
+ I cast Magic Missile # GM should apply 1st-level spell rules
370
+ /rag Fireball # Search for a spell Elara doesn't know
371
+ I cast Fireball # GM behavior (should it allow this?)
372
+ ```
373
+
374
+ **Combat with Thorin:**
375
+ ```
376
+ /stats # Check your attack bonus
377
+ I attack with my longsword # GM will ask for d20 roll
378
+ I use my shield # GM applies AC bonus
379
+ I use Second Wind # GM applies fighter class feature
380
+ ```
381
 
382
  #### Step 5: Run Interactive Searches (Optional)
383
 
 
486
 
487
  ├── chromadb/ # Vector database (created on init)
488
  ├── initialize_rag.py # Main initialization script ⭐
489
+ ├── app_gradio.py # Gradio web UI ⭐ NEW!
490
+ ├── play_with_character.py # Character-aware gameplay CLI ⭐ NEW!
491
+ ├── query_rag.py # Interactive query CLI ⭐
492
+ ├── test_all_collections.py # Comprehensive test suite ⭐
493
  ├── test_spell_search.py # Manual search testing
494
  ├── create_character.py # Character creator launcher
495
  ├── run_gm_dialogue.py # AI GM dialogue launcher
app_gradio.py ADDED
@@ -0,0 +1,328 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ D&D Character-Aware Game - Gradio Web Interface
4
+
5
+ Web UI for playing D&D with RAG-enhanced AI GM and character tracking.
6
+ """
7
+
8
+ import os
9
+ # Suppress tokenizer warning
10
+ os.environ['TOKENIZERS_PARALLELISM'] = 'false'
11
+
12
+ import gradio as gr
13
+ import json
14
+ from pathlib import Path
15
+
16
+ # Add project to path
17
+ import sys
18
+ sys.path.insert(0, str(Path(__file__).parent))
19
+
20
+ from dnd_rag_system.core.chroma_manager import ChromaDBManager
21
+ from dnd_rag_system.systems.character_creator import Character
22
+ from dnd_rag_system.systems.gm_dialogue import GameMaster
23
+
24
+
25
+ # Initialize system
26
+ print("🎲 Initializing D&D RAG System...")
27
+ db = ChromaDBManager()
28
+ gm = GameMaster(db)
29
+
30
+ # Pre-made characters
31
+ THORIN = Character(
32
+ name="Thorin Stormshield",
33
+ race="Dwarf",
34
+ character_class="Fighter",
35
+ level=3,
36
+ strength=16,
37
+ dexterity=12,
38
+ constitution=16,
39
+ intelligence=10,
40
+ wisdom=13,
41
+ charisma=8,
42
+ hit_points=28,
43
+ armor_class=18,
44
+ proficiency_bonus=2,
45
+ background="Soldier",
46
+ alignment="Lawful Good",
47
+ race_traits=["Dwarven Resilience", "Darkvision"],
48
+ class_features=["Fighting Style: Defense", "Second Wind", "Action Surge"],
49
+ proficiencies=["All armor", "All weapons", "Shields"],
50
+ equipment=["Longsword", "Shield", "Plate Armor", "Backpack", "50 GP"],
51
+ spells=[]
52
+ )
53
+
54
+ ELARA = Character(
55
+ name="Elara Moonwhisper",
56
+ race="Elf",
57
+ character_class="Wizard",
58
+ level=2,
59
+ strength=8,
60
+ dexterity=14,
61
+ constitution=12,
62
+ intelligence=17,
63
+ wisdom=13,
64
+ charisma=10,
65
+ hit_points=14,
66
+ armor_class=12,
67
+ proficiency_bonus=2,
68
+ background="Sage",
69
+ alignment="Neutral Good",
70
+ race_traits=["Darkvision", "Fey Ancestry"],
71
+ class_features=["Spellcasting", "Arcane Recovery"],
72
+ proficiencies=["Daggers", "Quarterstaffs", "Light crossbows"],
73
+ equipment=["Quarterstaff", "Spellbook", "Component Pouch", "Scholar's Pack"],
74
+ spells=["Fire Bolt", "Mage Hand", "Magic Missile", "Shield"]
75
+ )
76
+
77
+ # Global state
78
+ current_character = None
79
+ conversation_history = []
80
+
81
+
82
+ def load_character(character_choice):
83
+ """Load selected character and update context."""
84
+ global current_character, conversation_history
85
+
86
+ conversation_history = []
87
+
88
+ if character_choice == "Thorin Stormshield (Dwarf Fighter)":
89
+ current_character = THORIN
90
+ else:
91
+ current_character = ELARA
92
+
93
+ # Set GM context
94
+ char = current_character
95
+ mods = char.get_modifiers()
96
+
97
+ context = f"""The player is {char.name}, a level {char.level} {char.race} {char.character_class}.
98
+
99
+ PLAYER CHARACTER STATS:
100
+ - HP: {char.hit_points}/{char.hit_points} | AC: {char.armor_class} | Prof Bonus: +{char.proficiency_bonus}
101
+ - STR: {char.strength} ({mods['strength']:+d}) | DEX: {char.dexterity} ({mods['dexterity']:+d}) | CON: {char.constitution} ({mods['constitution']:+d})
102
+ - INT: {char.intelligence} ({mods['intelligence']:+d}) | WIS: {char.wisdom} ({mods['wisdom']:+d}) | CHA: {char.charisma} ({mods['charisma']:+d})
103
+
104
+ EQUIPMENT: {', '.join(char.equipment[:5])}
105
+ """
106
+
107
+ if char.spells:
108
+ context += f"\nSPELLS: {', '.join(char.spells[:5])}"
109
+
110
+ gm.set_context(context)
111
+
112
+ # Return character sheet
113
+ return format_character_sheet(), "", []
114
+
115
+
116
+ def format_character_sheet():
117
+ """Format character sheet for display."""
118
+ if not current_character:
119
+ return "No character selected"
120
+
121
+ char = current_character
122
+ mods = char.get_modifiers()
123
+
124
+ sheet = f"""# {char.name}
125
+ **{char.race} {char.character_class}, Level {char.level}**
126
+ *{char.background} | {char.alignment}*
127
+
128
+ ---
129
+
130
+ ### Combat Stats
131
+ - **HP**: {char.hit_points}
132
+ - **AC**: {char.armor_class}
133
+ - **Proficiency Bonus**: +{char.proficiency_bonus}
134
+
135
+ ### Ability Scores
136
+ | Ability | Score | Modifier |
137
+ |---------|-------|----------|
138
+ | STR | {char.strength} | {mods['strength']:+d} |
139
+ | DEX | {char.dexterity} | {mods['dexterity']:+d} |
140
+ | CON | {char.constitution} | {mods['constitution']:+d} |
141
+ | INT | {char.intelligence} | {mods['intelligence']:+d} |
142
+ | WIS | {char.wisdom} | {mods['wisdom']:+d} |
143
+ | CHA | {char.charisma} | {mods['charisma']:+d} |
144
+
145
+ ### Equipment
146
+ {chr(10).join('- ' + item for item in char.equipment)}
147
+
148
+ """
149
+
150
+ if char.spells:
151
+ sheet += f"""### Spells
152
+ {chr(10).join('- ' + spell for spell in char.spells)}
153
+ """
154
+
155
+ return sheet
156
+
157
+
158
+ def chat(message, history):
159
+ """Handle chat messages."""
160
+ global conversation_history
161
+
162
+ if not current_character:
163
+ return history + [("Please select a character first!", "")]
164
+
165
+ if not message.strip():
166
+ return history
167
+
168
+ # Handle special commands
169
+ if message.startswith("/"):
170
+ cmd = message.lower().strip()
171
+
172
+ if cmd == "/help":
173
+ help_text = """**Available Commands:**
174
+ - `/help` - Show this help
175
+ - `/context` - Show current scene context
176
+ - `/stats` - Show character stats
177
+ - `/rag <query>` - Search D&D rules (e.g., `/rag fireball`)
178
+
179
+ Otherwise, just type your action and press Enter!"""
180
+ return history + [(message, help_text)]
181
+
182
+ elif cmd == "/context":
183
+ context_text = gm.session.context
184
+ return history + [(message, f"**Current Context:**\n\n{context_text}")]
185
+
186
+ elif cmd == "/stats":
187
+ stats = format_character_sheet()
188
+ return history + [(message, stats)]
189
+
190
+ elif cmd.startswith("/rag "):
191
+ query = cmd[5:].strip()
192
+ if query:
193
+ results = gm.search_rag(query, n_results=2)
194
+ formatted = gm.format_rag_context(results)
195
+ return history + [(message, f"**RAG Search Results:**\n\n{formatted}")]
196
+ else:
197
+ return history + [(message, "Usage: `/rag <query>` (e.g., `/rag magic missile`)")]
198
+
199
+ else:
200
+ return history + [(message, f"Unknown command: {cmd}\nType `/help` for available commands")]
201
+
202
+ # Generate GM response
203
+ try:
204
+ response = gm.generate_response(message, use_rag=True)
205
+ conversation_history.append((message, response))
206
+ return history + [(message, response)]
207
+ except Exception as e:
208
+ error_msg = f"Error: {str(e)}\n\nMake sure Ollama is running and the model is installed:\n`ollama pull hf.co/Chun121/Qwen3-4B-RPG-Roleplay-V2:Q4_K_M`"
209
+ return history + [(message, error_msg)]
210
+
211
+
212
+ def clear_history():
213
+ """Clear conversation history."""
214
+ global conversation_history
215
+ conversation_history = []
216
+ return []
217
+
218
+
219
+ # Create Gradio interface
220
+ with gr.Blocks(title="D&D RAG Game Master") as demo:
221
+ gr.Markdown("""
222
+ # 🎲 D&D Character-Aware Game Master
223
+
224
+ Play D&D with an AI Game Master powered by RAG (Retrieval-Augmented Generation) and the Qwen3-4B-RPG-Roleplay model.
225
+
226
+ **How to Play:**
227
+ 1. Select a character
228
+ 2. Type your actions (e.g., "I look around", "I cast Magic Missile at the goblin")
229
+ 3. Use `/help` to see available commands
230
+ """)
231
+
232
+ with gr.Row():
233
+ with gr.Column(scale=1):
234
+ gr.Markdown("## Character Selection")
235
+
236
+ character_dropdown = gr.Dropdown(
237
+ choices=["Thorin Stormshield (Dwarf Fighter)", "Elara Moonwhisper (Elf Wizard)"],
238
+ value="Thorin Stormshield (Dwarf Fighter)",
239
+ label="Choose Your Character"
240
+ )
241
+
242
+ load_btn = gr.Button("Load Character", variant="primary")
243
+
244
+ gr.Markdown("---")
245
+
246
+ character_sheet = gr.Markdown(format_character_sheet())
247
+
248
+ with gr.Column(scale=2):
249
+ gr.Markdown("## Game Session")
250
+
251
+ chatbot = gr.Chatbot(
252
+ height=500,
253
+ label="Game Master",
254
+ show_label=True,
255
+ avatar_images=(None, "🎭")
256
+ )
257
+
258
+ with gr.Row():
259
+ msg_input = gr.Textbox(
260
+ placeholder="Type your action or command here... (e.g., 'I look around' or '/help')",
261
+ label="Your Action",
262
+ show_label=False,
263
+ scale=4
264
+ )
265
+ submit_btn = gr.Button("Send", variant="primary", scale=1)
266
+
267
+ with gr.Row():
268
+ clear_btn = gr.Button("Clear History")
269
+ gr.Markdown("**Quick Commands:** `/help` | `/context` | `/stats` | `/rag <query>`")
270
+
271
+ gr.Markdown("""
272
+ ---
273
+
274
+ ### 📖 Example Actions
275
+ - "I look around and check my surroundings"
276
+ - "I draw my weapon and prepare for combat"
277
+ - "I cast Magic Missile at the goblin" (Elara)
278
+ - "I attack with my longsword" (Thorin)
279
+
280
+ ### 🔍 RAG Commands
281
+ - `/rag Magic Missile` - Look up spell details
282
+ - `/rag Goblin` - Look up monster stats
283
+ - `/rag Fighter` - Look up class features
284
+
285
+ **Powered by:** ChromaDB RAG + Ollama (Qwen3-4B-RPG-Roleplay-V2)
286
+ """)
287
+
288
+ # Event handlers
289
+ load_btn.click(
290
+ load_character,
291
+ inputs=[character_dropdown],
292
+ outputs=[character_sheet, msg_input, chatbot]
293
+ )
294
+
295
+ submit_btn.click(
296
+ chat,
297
+ inputs=[msg_input, chatbot],
298
+ outputs=[chatbot]
299
+ ).then(
300
+ lambda: "",
301
+ outputs=[msg_input]
302
+ )
303
+
304
+ msg_input.submit(
305
+ chat,
306
+ inputs=[msg_input, chatbot],
307
+ outputs=[chatbot]
308
+ ).then(
309
+ lambda: "",
310
+ outputs=[msg_input]
311
+ )
312
+
313
+ clear_btn.click(
314
+ clear_history,
315
+ outputs=[chatbot]
316
+ )
317
+
318
+
319
+ if __name__ == "__main__":
320
+ # Load default character
321
+ load_character("Thorin Stormshield (Dwarf Fighter)")
322
+
323
+ # Launch
324
+ demo.launch(
325
+ server_name="0.0.0.0",
326
+ server_port=7860,
327
+ share=False
328
+ )
plan_progress.md CHANGED
@@ -17,6 +17,7 @@
17
  | **Phase 5: GM Dialogue** | ✅ Complete | 2/2 | RAG-enhanced AI GM working |
18
  | **Phase 6: Character Creation** | ✅ Complete | 2/2 | Full character creator with RAG |
19
  | **Phase 7: Testing & Validation** | ✅ Complete | 3/3 | 26+ comprehensive tests passing |
 
20
 
21
  **Legend**: ✅ Complete | 🚧 In Progress | ⏳ Pending | ❌ Blocked
22
 
@@ -223,6 +224,97 @@ python query_rag.py --monster "dragon" # Search monsters
223
 
224
  ---
225
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
  ## 📦 Supporting Files ✅ COMPLETE
227
 
228
  ### ✅ Dependencies
@@ -295,10 +387,20 @@ python query_rag.py --monster "dragon" # Search monsters
295
  5. ✅ **Interactive Query Tool** - New CLI for exploring the RAG system
296
  6. ✅ **Comprehensive Tests** - 26+ automated tests validating all functionality
297
 
298
- ### Known Issues
299
- - None currently
 
 
 
 
 
 
 
 
 
 
300
 
301
- ### Future Enhancements
302
  - ⏳ **Subrace Support**: High Elf, Mountain Dwarf, etc. with specific abilities
303
  - ⏳ **Advanced Filtering**: Search by CR range, spell level range, class, type
304
  - ⏳ **Web UI**: Web interface for GM dialogue
@@ -318,7 +420,10 @@ python query_rag.py --monster "dragon" # Search monsters
318
  | 2024-11-06 15:00 | Phase 3-6 complete (initialization, query, GM, character creator) |
319
  | 2024-11-06 18:00 | **Major upgrades**: Name weighting, race extraction, comprehensive tests |
320
  | 2024-11-06 20:00 | **Phase 7 complete**: All tests passing, documentation updated |
321
- | 2024-11-06 21:00 | **PROJECT COMPLETE** - Production ready! |
 
 
 
322
 
323
  ---
324
 
@@ -354,9 +459,15 @@ python query_rag.py --monster "dragon" # Search monsters
354
 
355
  ---
356
 
357
- **Status**: **PRODUCTION READY**
358
- **Next Steps**: Deploy, gather user feedback, plan Phase 8 enhancements
 
 
 
 
 
 
359
 
360
  ---
361
 
362
- **Last Updated**: November 6, 2024 21:00
 
17
  | **Phase 5: GM Dialogue** | ✅ Complete | 2/2 | RAG-enhanced AI GM working |
18
  | **Phase 6: Character Creation** | ✅ Complete | 2/2 | Full character creator with RAG |
19
  | **Phase 7: Testing & Validation** | ✅ Complete | 3/3 | 26+ comprehensive tests passing |
20
+ | **Phase 8: Game Mechanics Engine** | 🚧 In Progress | 0/5 | Character-aware gameplay enhancements |
21
 
22
  **Legend**: ✅ Complete | 🚧 In Progress | ⏳ Pending | ❌ Blocked
23
 
 
224
 
225
  ---
226
 
227
+ ## 🎮 Phase 8: Game Mechanics Engine 🚧 IN PROGRESS
228
+
229
+ **Goal**: Transform AI from rule-maker to narrator by implementing programmatic game mechanics
230
+
231
+ ### ✅ 8.0 Character-Aware Dialogue System **⭐ NEW!**
232
+ **File**: `play_with_character.py`
233
+ - [x] Load or create characters for gameplay
234
+ - [x] Character context passed to GM (stats, equipment, spells)
235
+ - [x] Three character modes: Create new, Load JSON, Quick test
236
+ - [x] Commands: `/character`, `/stats`, `/context` for character info
237
+ - [x] Fixed tokenizer warning suppression
238
+ - [x] Dynamic character support (not hardcoded to one character)
239
+ - [x] Proper first/second person context ("The player is X" → AI uses "you")
240
+ - [x] Integration testing completed (Dec 1, 2024)
241
+
242
+ ### 🚧 8.1 Spell System Enhancement
243
+ **File**: `play_with_character.py` (to be refactored)
244
+ - [ ] Programmatic spell validation (check if player owns spell)
245
+ - [ ] Spell slot tracking by level (1st: 3 slots, 2nd: 2 slots, etc.)
246
+ - [ ] Auto-decrement slots when casting
247
+ - [ ] Rest mechanic to restore slots
248
+ - [ ] Spell lookup from RAG before AI narration
249
+
250
+ ### 🚧 8.2 Combat Mechanics
251
+ **Status**: ⏳ Pending
252
+ - [ ] HP tracking and damage application
253
+ - [ ] Attack roll automation (d20 + modifiers)
254
+ - [ ] Damage roll automation (weapon dice + STR/DEX)
255
+ - [ ] AC checks (hit/miss determination)
256
+ - [ ] Death saves and unconsciousness
257
+
258
+ ### 🚧 8.3 Turn & Initiative System
259
+ **Status**: ⏳ Pending
260
+ - [ ] Initiative roller (d20 + DEX modifier)
261
+ - [ ] Turn order tracking
262
+ - [ ] Action economy (action, bonus action, movement, reaction)
263
+ - [ ] Combat state management
264
+
265
+ ### 🚧 8.4 Inventory System
266
+ **Status**: ⏳ Pending
267
+ - [ ] Add/remove items programmatically
268
+ - [ ] Equipment weight tracking
269
+ - [ ] Item usage validation
270
+ - [ ] Gold/currency management
271
+
272
+ ### 🚧 8.5 Integration & Testing
273
+ **Status**: ⏳ Pending
274
+ - [ ] Test spell casting flow end-to-end
275
+ - [ ] Test combat scenarios
276
+ - [ ] Test inventory management
277
+ - [ ] Update documentation
278
+
279
+ ---
280
+
281
+ ## 🏗️ Phase 8 Architecture Notes
282
+
283
+ ### Option B: Hybrid AI + Rules Engine (SELECTED)
284
+
285
+ **Problem**: AI is unreliable at following D&D rules consistently
286
+ - Ignores spells player casts
287
+ - Allows spells player doesn't know
288
+ - Doesn't track resources (HP, spell slots)
289
+ - Makes up mechanics on the fly
290
+
291
+ **Solution**: Intercept player actions BEFORE AI sees them
292
+
293
+ **Flow**:
294
+ 1. **Player Input**: "I cast Magic Missile at the goblin"
295
+ 2. **Rules Engine** (Python code):
296
+ - Parse: Detect spell casting intent
297
+ - Validate: Check if player owns "Magic Missile" ✓
298
+ - Validate: Check if player has 1st-level spell slot ✓
299
+ - Retrieve: Get spell details from RAG (3 darts, 1d4+1 each)
300
+ - Roll: 3d4+3 = 11 damage (programmatically)
301
+ - Deduct: Spell slot consumed
302
+ - Update: Target HP reduced by 11
303
+ 3. **AI Prompt**: "You successfully cast Magic Missile dealing 11 force damage to the goblin. The goblin now has 5 HP remaining. Describe the magical missiles striking the goblin."
304
+ 4. **AI Response**: (Just narrates the flavor, mechanics already handled)
305
+
306
+ **Benefits**:
307
+ - AI becomes a **narrator**, not a **rules engine**
308
+ - Mechanics are deterministic and accurate
309
+ - AI can focus on storytelling
310
+ - Players can trust the rules
311
+
312
+ **Alternatives Rejected**:
313
+ - **Option A** (Pure AI): Too unreliable, tested and failed
314
+ - **Option C** (Post-process AI): Too hard to fix bad outputs
315
+
316
+ ---
317
+
318
  ## 📦 Supporting Files ✅ COMPLETE
319
 
320
  ### ✅ Dependencies
 
387
  5. ✅ **Interactive Query Tool** - New CLI for exploring the RAG system
388
  6. ✅ **Comprehensive Tests** - 26+ automated tests validating all functionality
389
 
390
+ ### Known Issues (Phase 8 Discovery - Dec 1, 2024)
391
+ - **AI Unreliability**: Pure AI approach fails to consistently enforce D&D rules
392
+ - Ignores valid spell casts (Magic Missile cast was turned into melee combat)
393
+ - Allows invalid spells (Let Elara cast Fireball, which she doesn't know)
394
+ - No resource tracking (spell slots, HP, gold)
395
+ - **Solution**: Moving to Hybrid Architecture (Option B) with programmatic rules engine
396
+
397
+ ### Current Work (Phase 8)
398
+ - 🚧 **Spell Validation System**: Programmatically check spell ownership before AI generation
399
+ - 🚧 **Resource Tracking**: HP, spell slots, inventory management
400
+ - 🚧 **Combat Mechanics**: Attack/damage rolls, initiative, turn tracking
401
+ - 🚧 **Rules Engine**: Intercept player actions, apply mechanics, then AI narrates
402
 
403
+ ### Future Enhancements (Post-Phase 8)
404
  - ⏳ **Subrace Support**: High Elf, Mountain Dwarf, etc. with specific abilities
405
  - ⏳ **Advanced Filtering**: Search by CR range, spell level range, class, type
406
  - ⏳ **Web UI**: Web interface for GM dialogue
 
420
  | 2024-11-06 15:00 | Phase 3-6 complete (initialization, query, GM, character creator) |
421
  | 2024-11-06 18:00 | **Major upgrades**: Name weighting, race extraction, comprehensive tests |
422
  | 2024-11-06 20:00 | **Phase 7 complete**: All tests passing, documentation updated |
423
+ | 2024-11-06 21:00 | **V1.0 COMPLETE** - Production ready! |
424
+ | 2024-12-01 18:00 | **Phase 8 started**: Character-aware dialogue system created |
425
+ | 2024-12-01 19:00 | Testing reveals AI reliability issues with game mechanics |
426
+ | 2024-12-01 19:30 | **Architecture decision**: Option B (Hybrid Rules Engine) selected |
427
 
428
  ---
429
 
 
459
 
460
  ---
461
 
462
+ **Status**: 🚧 **V2.0 IN DEVELOPMENT** (Phase 8: Game Mechanics Engine)
463
+ **Current Focus**: Implementing hybrid AI + Rules Engine architecture
464
+ **Next Steps**:
465
+ 1. Spell validation system
466
+ 2. Spell slot tracking
467
+ 3. HP and damage mechanics
468
+ 4. Combat turn system
469
+ 5. Inventory management
470
 
471
  ---
472
 
473
+ **Last Updated**: December 1, 2024 19:30
play_with_character.py ADDED
@@ -0,0 +1,269 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ D&D Character-Aware Game Session
4
+
5
+ Integrated test script that:
6
+ 1. Creates or loads a character
7
+ 2. Starts a GM dialogue session with character context
8
+ 3. Allows playing with character stats visible to GM
9
+ """
10
+
11
+ import os
12
+ # Suppress tokenizer warning when forking subprocess
13
+ os.environ['TOKENIZERS_PARALLELISM'] = 'false'
14
+
15
+ import sys
16
+ import json
17
+ from pathlib import Path
18
+
19
+ # Add project to path
20
+ sys.path.insert(0, str(Path(__file__).parent))
21
+
22
+ from dnd_rag_system.core.chroma_manager import ChromaDBManager
23
+ from dnd_rag_system.systems.character_creator import CharacterCreator, Character
24
+ from dnd_rag_system.systems.gm_dialogue import GameMaster, InteractiveGM
25
+
26
+
27
+ class CharacterAwareGM(InteractiveGM):
28
+ """
29
+ Enhanced GM that knows about the player's character.
30
+
31
+ Extends InteractiveGM to include character information in prompts.
32
+ """
33
+
34
+ def __init__(self, gm: GameMaster, character: Character):
35
+ """Initialize with character context."""
36
+ super().__init__(gm)
37
+ self.character = character
38
+
39
+ # Set initial scene with character info
40
+ self._initialize_character_context()
41
+
42
+ def _initialize_character_context(self):
43
+ """Add character to GM's context."""
44
+ char = self.character
45
+ mods = char.get_modifiers()
46
+
47
+ context = f"""The player is {char.name}, a level {char.level} {char.race} {char.character_class}.
48
+
49
+ PLAYER CHARACTER STATS:
50
+ - HP: {char.hit_points}/{char.hit_points} | AC: {char.armor_class} | Prof Bonus: +{char.proficiency_bonus}
51
+ - STR: {char.strength} ({mods['strength']:+d}) | DEX: {char.dexterity} ({mods['dexterity']:+d}) | CON: {char.constitution} ({mods['constitution']:+d})
52
+ - INT: {char.intelligence} ({mods['intelligence']:+d}) | WIS: {char.wisdom} ({mods['wisdom']:+d}) | CHA: {char.charisma} ({mods['charisma']:+d})
53
+
54
+ EQUIPMENT: {', '.join(char.equipment[:5])}
55
+ """
56
+
57
+ if char.spells:
58
+ context += f"\nSPELLS: {', '.join(char.spells[:5])}"
59
+
60
+ self.gm.set_context(context)
61
+
62
+ def print_help(self):
63
+ """Override help to add character commands."""
64
+ super().print_help()
65
+ print("\nCHARACTER COMMANDS:")
66
+ print(" /character - Show character sheet")
67
+ print(" /stats - Quick stats view")
68
+ print()
69
+
70
+ def run(self):
71
+ """Override run to handle character commands."""
72
+ print("\n" + "="*70)
73
+ print(f"🎲 D&D GAME SESSION - Playing as {self.character.name}")
74
+ print("="*70)
75
+ print(f"Character: {self.character.name} ({self.character.race} {self.character.character_class})")
76
+ print(f"Model: {self.gm.model_name}")
77
+ print(f"Database: {self.gm.db.persist_dir}")
78
+ print("\nType /help for commands or start playing!")
79
+ print("="*70)
80
+
81
+ self.running = True
82
+ use_rag_next = True
83
+
84
+ while self.running:
85
+ try:
86
+ # Get player input
87
+ player_input = input(f"\n🎲 {self.character.name}: ").strip()
88
+
89
+ if not player_input:
90
+ continue
91
+
92
+ # Handle commands
93
+ if player_input.startswith('/'):
94
+ # Check for character-specific commands first
95
+ if player_input.lower() == '/character':
96
+ self._show_character_sheet()
97
+ continue
98
+ elif player_input.lower() == '/stats':
99
+ self._show_quick_stats()
100
+ continue
101
+ else:
102
+ # Use parent's command handler
103
+ self._handle_command(player_input)
104
+ continue
105
+
106
+ # Generate GM response
107
+ print("\n🎭 GM: ", end="", flush=True)
108
+ response = self.gm.generate_response(player_input, use_rag=use_rag_next)
109
+ print(response)
110
+
111
+ # Reset RAG flag
112
+ use_rag_next = True
113
+
114
+ except KeyboardInterrupt:
115
+ print("\n\n👋 Game interrupted. Type /quit to exit or continue playing.")
116
+ except Exception as e:
117
+ print(f"\n❌ Error: {e}")
118
+
119
+ def _show_character_sheet(self):
120
+ """Display full character sheet."""
121
+ char = self.character
122
+ mods = char.get_modifiers()
123
+
124
+ print("\n" + "="*70)
125
+ print(f"CHARACTER SHEET - {char.name}")
126
+ print("="*70)
127
+ print(f"{char.race} {char.character_class}, Level {char.level}")
128
+ print(f"{char.background} | {char.alignment}")
129
+ print("\n" + "─"*70)
130
+
131
+ print("\nABILITY SCORES:")
132
+ print(f" STR: {char.strength:2d} ({mods['strength']:+d}) | INT: {char.intelligence:2d} ({mods['intelligence']:+d})")
133
+ print(f" DEX: {char.dexterity:2d} ({mods['dexterity']:+d}) | WIS: {char.wisdom:2d} ({mods['wisdom']:+d})")
134
+ print(f" CON: {char.constitution:2d} ({mods['constitution']:+d}) | CHA: {char.charisma:2d} ({mods['charisma']:+d})")
135
+
136
+ print(f"\nCOMBAT STATS:")
137
+ print(f" HP: {char.hit_points} | AC: {char.armor_class} | Proficiency: +{char.proficiency_bonus}")
138
+
139
+ print(f"\nEQUIPMENT:")
140
+ for item in char.equipment:
141
+ print(f" - {item}")
142
+
143
+ if char.spells:
144
+ print(f"\nSPELLS:")
145
+ for spell in char.spells:
146
+ print(f" - {spell}")
147
+
148
+ print("="*70)
149
+
150
+ def _show_quick_stats(self):
151
+ """Show condensed stats."""
152
+ char = self.character
153
+ mods = char.get_modifiers()
154
+
155
+ print(f"\n📊 {char.name} | HP: {char.hit_points} | AC: {char.armor_class} | Prof: +{char.proficiency_bonus}")
156
+ print(f" STR {mods['strength']:+d} | DEX {mods['dexterity']:+d} | CON {mods['constitution']:+d} | INT {mods['intelligence']:+d} | WIS {mods['wisdom']:+d} | CHA {mods['charisma']:+d}")
157
+
158
+
159
+ def load_character_from_json(filepath: str) -> Character:
160
+ """Load character from JSON file."""
161
+ with open(filepath, 'r') as f:
162
+ data = json.load(f)
163
+
164
+ # Create Character object from dict
165
+ char = Character()
166
+ for key, value in data.items():
167
+ if hasattr(char, key):
168
+ setattr(char, key, value)
169
+
170
+ return char
171
+
172
+
173
+ def main():
174
+ """Main entry point for character-aware game session."""
175
+ print("\n" + "="*70)
176
+ print("🎲 D&D CHARACTER-AWARE GAME SESSION")
177
+ print("="*70)
178
+
179
+ try:
180
+ # Initialize ChromaDB
181
+ print("\n📚 Connecting to D&D knowledge base...")
182
+ db = ChromaDBManager()
183
+
184
+ # Character selection
185
+ print("\n" + "─"*70)
186
+ print("CHARACTER SELECTION")
187
+ print("─"*70)
188
+ print("\n1. Create new character")
189
+ print("2. Load existing character")
190
+ print("3. Use test character (quick start)")
191
+
192
+ choice = input("\nSelect option (1-3): ").strip()
193
+
194
+ if choice == "1":
195
+ # Create new character
196
+ print("\n🎭 Starting character creator...")
197
+ creator = CharacterCreator(db)
198
+ character = creator.create_character_interactive()
199
+
200
+ # Save option
201
+ save = input("\nSave character? (y/n): ").strip().lower()
202
+ if save == 'y':
203
+ filename = input("Filename (e.g., aragorn.json): ").strip()
204
+ if not filename.endswith('.json'):
205
+ filename += '.json'
206
+ character.to_json(filename)
207
+
208
+ elif choice == "2":
209
+ # Load existing character
210
+ filename = input("\nCharacter filename: ").strip()
211
+ if not filename.endswith('.json'):
212
+ filename += '.json'
213
+
214
+ character = load_character_from_json(filename)
215
+ print(f"\n✅ Loaded: {character.name} ({character.race} {character.character_class})")
216
+
217
+ else:
218
+ # Quick test character
219
+ print("\n⚡ Creating test character...")
220
+ character = Character(
221
+ name="Thorin Stormshield",
222
+ race="Dwarf",
223
+ character_class="Fighter",
224
+ level=3,
225
+ strength=16,
226
+ dexterity=12,
227
+ constitution=16,
228
+ intelligence=10,
229
+ wisdom=13,
230
+ charisma=8,
231
+ hit_points=28,
232
+ armor_class=18,
233
+ proficiency_bonus=2,
234
+ background="Soldier",
235
+ alignment="Lawful Good",
236
+ race_traits=["Dwarven Resilience", "Darkvision"],
237
+ class_features=["Fighting Style: Defense", "Second Wind", "Action Surge"],
238
+ proficiencies=["All armor", "All weapons", "Shields"],
239
+ equipment=["Longsword", "Shield", "Plate Armor", "Backpack", "50 GP"],
240
+ spells=[]
241
+ )
242
+ print(f"✅ Created: {character.name} - a battle-ready dwarf fighter!")
243
+
244
+ # Initialize Game Master with character
245
+ print("\n🎭 Initializing AI Game Master...")
246
+ gm = GameMaster(db)
247
+
248
+ print("✅ Game Master ready!")
249
+
250
+ # Start character-aware session
251
+ interactive = CharacterAwareGM(gm, character)
252
+ interactive.run()
253
+
254
+ except FileNotFoundError as e:
255
+ print(f"\n❌ Character file not found: {e}")
256
+ return 1
257
+ except Exception as e:
258
+ print(f"\n❌ Failed to initialize: {e}")
259
+ print("\nTroubleshooting:")
260
+ print(" 1. Run: python initialize_rag.py")
261
+ print(" 2. Install Ollama: https://ollama.ai")
262
+ print(" 3. Download model: ollama pull hf.co/Chun121/Qwen3-4B-RPG-Roleplay-V2:Q4_K_M")
263
+ return 1
264
+
265
+ return 0
266
+
267
+
268
+ if __name__ == '__main__':
269
+ sys.exit(main())
requirements.txt CHANGED
@@ -9,6 +9,9 @@ pdfplumber>=0.10.0
9
  # Ollama Python client
10
  ollama>=0.1.0
11
 
 
 
 
12
  # Rich console output
13
  rich>=13.0.0
14
 
 
9
  # Ollama Python client
10
  ollama>=0.1.0
11
 
12
+ # Web UI
13
+ gradio>=4.0.0
14
+
15
  # Rich console output
16
  rich>=13.0.0
17