app.py is a Gradio-based web application that provides AI-powered hints for Hitman game walkthroughs. It uses OpenAI's GPT models to process game transcripts and generate contextual, spoiler-free hints for players. The system currently supports 1,034 walkthrough transcripts loaded at startup, covering a comprehensive library of Hitman missions and challenges.
- Framework: Gradio 3.50.2 (web UI framework)
- LLM Provider: OpenAI API
- Language: Python 3.9+
- State Management: Gradio State with custom ConversationState class
- Two-stage conversation flow: Mission selection → Hint generation
- Stateful chatbot: Maintains conversation context across interactions
- Lazy loading: Transcripts loaded once at startup, processed on-demand
- Purpose: Recursively loads all game walkthrough transcripts from the
transcripts/directory - Implementation:
- Uses
Path.rglob("*.txt")for recursive file discovery - Filters out files starting with underscore (
_) - Stores as dict:
{filename_stem: file_content} - Executes at module load time (line 51)
- Uses
- Error Handling: Catches and logs file read errors, continues loading other files
- Returns list of available transcript keys for UI display
- Purpose: Transforms verbose filenames into user-friendly display names
- Transformations:
- Removes "HITMAN 2" prefix
- Strips suffixes: "ChallengeFeat", "Walkthrough", "Feat"
- Fixes spacing: "AssassinSuit" → "Assassin Suit"
- Normalizes whitespace
- Example:
"HITMAN 2 Mumbai Memoirs ChallengeFeat Walkthrough"→"Mumbai Memoirs Challenge"
- Purpose: Matches user input to available transcripts without requiring exact names
- Algorithm:
- Exact substring match: Returns immediately if found (lines 108-109)
- Word overlap scoring: Counts common words between input and transcript names
- Best match selection: Returns transcript with highest overlap score
- Input normalization: Case-insensitive, tokenizes on whitespace
- Threshold: Returns
Noneif no common words found
Purpose: Converts raw walkthrough transcripts into structured, actionable steps
Configuration (Optimized from extensive testing):
- Model:
gpt-4.1-nano-2025-04-14 - Temperature:
0.3(deterministic, consistent output) - System Prompt: Gameplay extraction expert persona
- Filters commentary/filler
- Recognizes Hitman mechanics (disguises, weapons, objectives)
- Maintains logical progression
User Prompt Strategy:
- Explicit instructions for segmentation (timestamps, area changes, objective completion)
- Requests numbered step format: "Step 1: ...", "Step 2: ..."
- Emphasizes removal of fluff, commentary, jokes
Output: Numbered list of concise gameplay actions
Purpose: Generates clear, actionable hints that reveal ONE step at a time
Configuration (Optimized from extensive testing):
- Model:
gpt-4.1-mini-2025-04-14 - Temperature:
0.8(creative, varied hints) - System Prompt: Expert hint system persona
- Provides clear, actionable hints one step at a time
- Reveals ONE step from solution without spoiling what comes next
- Balances concrete guidance with maintaining discovery satisfaction
User Prompt Features:
- Core Principle: Reveal ONE step at a time, but reveal it clearly
- Step Analysis: Determines where player is in the solution based on their question
- Single-Step Revelation:
- ✅ DO reveal: Specific locations, items, actions for current step
- ✅ DO use: Concrete nouns, room names, specific disguise types
- ❌ DON'T reveal: What to do AFTER completing this step
- ❌ DON'T reveal: Multiple sequential steps
- Hint Structure: [Direct instruction for ONE step] + [Optional: Why this helps] + [Encouragement]
- Few-shot examples: Good vs. bad hint demonstrations with multi-step warnings
- Output Format: 1-3 sentences that reveal exactly ONE step with enough detail to be actionable
Input: Chunked steps, user's question (transcript parameter removed in latest version) Output: Single actionable hint revealing one step (1-3 sentences)
Tracks conversation progress through mission selection and hint generation.
Attributes:
stage: Current conversation phase ("select_mission"|"ready_for_hints")selected_mission: Transcript key (filename stem)transcript: Full transcript textchunked_steps: LLM-processed step list
Lifecycle:
- Initialize →
stage="select_mission" - User provides keywords → Fuzzy match → Load transcript
- LLM chunks transcript →
stage="ready_for_hints" - User asks questions → Generate hints from chunked steps
- User types
/new→ Reset to step 1
Purpose: Bridge between Gradio's dict-based state and ConversationState object
Flow:
- Convert incoming
state_dicttoConversationStateobject - Call
chatbot_response()with object state - Convert updated state back to dict for Gradio
- Return response text and updated state dict
Purpose: Core chatbot logic handling the two-stage conversation flow
Stage 1: Mission Selection (Lines 261-289)
- Calls
find_best_match()with user input - If match found:
- Load transcript and update state
- Call
chunk_transcript_with_llm()to process - Display cleaned mission name with
clean_mission_name() - Transition to
"ready_for_hints"stage
- If no match:
- Display error message
- List available missions (cleaned names)
Stage 2: Hint Generation (Lines 291-304)
- Special command:
/newresets to mission selection - Normal flow:
- Pass user question + chunked steps to
generate_hint()(transcript no longer passed) - Return hint to user
- Maintain
"ready_for_hints"stage for follow-up questions
- Pass user question + chunked steps to
Theme: Red primary hue (gr.themes.Base(primary_hue="red"))
Components:
- Header Markdown (Lines 332-340): Title and instructions
- State Container (Lines 342-347): Persists conversation state across interactions
- Chatbot Display (Lines 369-372): Chat history with welcome message
- Input Row (Lines 374-381):
- Text input with placeholder (scale=4)
- Submit button (primary variant, scale=1)
- Clear button (scale=1) - clears both message box and chatbot history
- Examples Component (Lines 383-393): Clickable example messages
- Mission examples: "Mumbai Memoirs", "Dubai Silent Assassin Suit Only"
- Question examples: "How do I start?", "Where can I find a disguise?", "I'm at the beach, what should I do?"
- Tips Markdown (Lines 395-405): Usage instructions
user_send() (Lines 407-409):
- Clears input box
- Appends user message to chat history with placeholder
Nonefor bot response - Returns updated history
bot_respond() (Lines 411-416):
- Extracts latest user message
- Calls
respond()to get bot response - Updates placeholder with actual response
- Returns updated history and state
reset_chat() (Lines 418-424):
- Resets chatbot to welcome message
- Clears conversation state (stage, mission, transcript, chunked steps)
- Returns user to initial "select_mission" stage
- Triggered by Clear button click
Event Chaining:
msg.submit→user_send→bot_respondsubmit.click→user_send→bot_respondclear.click→reset_chat- Uses Gradio's
.then()for sequential execution
1. User opens app
↓
2. Gradio loads → load_transcripts() → TRANSCRIPTS dict populated
↓
3. UI displays welcome message with available missions
↓
4. User types mission keywords (e.g., "Mumbai Memoirs")
↓
5. user_send() → Adds message to chat history
↓
6. bot_respond() → respond() → chatbot_response()
↓
7. find_best_match() → Returns "HITMAN 2 Mumbai Memoirs ChallengeFeat Walkthrough"
↓
8. Load transcript from TRANSCRIPTS dict
↓
9. chunk_transcript_with_llm() → OpenAI API call
↓
10. Display cleaned name: "Mumbai Memoirs Challenge"
↓
11. State transitions to "ready_for_hints"
↓
12. User asks: "How do I start?"
↓
13. generate_hint() → OpenAI API call with chunked steps + question (no transcript)
↓
14. Display clear, actionable hint revealing ONE step
↓
15. Loop: User asks more questions → More hints generated
↓
16. User types "/new" → Reset to step 4
- Initialized with environment variable:
OPENAI_API_KEY - Reused across all LLM calls (chunking + hint generation)
response = client.chat.completions.create(
model="<model-name>",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
],
temperature=<temp-value>
)
return response.choices[0].message.content- Try-except blocks around all API calls
- Returns error messages to user
- Suggests checking
OPENAI_API_KEYfor chunking errors
-
Chunking:
gpt-4.1-nano@ temp 0.3- Cost-effective for structured extraction
- Low temperature ensures consistency
-
Hints:
gpt-4.1-mini@ temp 0.8- Better reasoning for contextual hints
- Higher temperature adds variety
- Capable of analyzing player position in solution
- Previous approach: Vague, directional hints that avoided specifics
- Current approach: Clear, actionable hints that reveal ONE step at a time
- Key insight: Players need specific guidance for the current step, but not future steps
- Result: Hints are helpful enough to make progress, but don't spoil the solution path
- One-time transcript loading: All 1,034 files loaded at startup, not per-request
- Average startup time: ~2-3 seconds for full transcript library
- Stored in memory for instant fuzzy matching
- Chunking once per mission: Results cached in state, reused for all hints
- Fuzzy matching: O(n) string operations over 1,034 transcripts, no external dependencies
- Fast execution due to simple word overlap algorithm
- Minimal API calls: Only on mission selection (chunking) and hint requests
- UI Examples: Pre-populated examples reduce user typing and improve UX
OPENAI_API_KEY: Required for LLM functionality- Read via
os.environ.get()with empty string fallback
- Read-only access to
transcripts/directory - No user file uploads or writes
- UTF-8 encoding for international character support
if __name__ == "__main__":
demo.launch(share=True)share=True: Creates public Gradio link (tunneling)- Enables external access without deployment
- Alternative:
share=Falsefor localhost-only access
- Function-level documentation using
#comments (not docstrings) - Inline comments for complex logic
- Configuration details documented at function level
- Multi-line strings (
""") reserved for:- LLM prompts (actual data sent to API)
- Gradio Markdown content (UI text)
- Single-line comments (
#) for all documentation - Important: Triple quotes inside f-strings must be escaped (
\"\"\") to avoid premature string closure- Example:
f"""Prompt with \"\"\" {variable} \"\"\" inside"""
- Example:
gradio==3.50.2
openai (latest compatible with Python 3.9)
pathlib (built-in)
re (built-in)
os (built-in)
- Target: Python 3.9+
- Compatible with macOS/Linux/Windows
- Purpose: Allows users to reset conversation and start fresh
- Functionality: Clears both chat history and conversation state
- Implementation:
gr.ClearButtoncomponent + customreset_chat()handler - Benefits:
- No page refresh needed
- Instant reset to welcome screen
- Preserves transcript library in memory
- Purpose: Guide users with clickable example inputs
- Implementation:
gr.Examplescomponent with 5 pre-populated examples - Benefits:
- Reduces friction for first-time users
- Shows expected input formats
- Demonstrates both mission selection and hint-asking patterns
- Examples Provided:
- Mission selection: "Mumbai Memoirs", "Dubai Silent Assassin Suit Only"
- Hint questions: "How do I start?", "Where can I find a disguise?", "I'm at the beach, what should I do?"
- Add new transcripts: Drop
.txtfiles intranscripts/folder (system handles 1,000+ files efficiently) - Modify prompts: Edit system/user prompts in chunking/hint functions
- Adjust hint style: Change temperature or model configuration
- UI customization: Modify Gradio theme or add components
- Update examples: Edit the examples list in the
gr.Examplescomponent
- Different LLM provider: Replace OpenAI client with alternative API
- Multi-language support: Add translation layer
- User accounts: Integrate authentication system
- Persistent history: Add database for conversation storage
- Advanced filtering: Add transcript categorization by game version or difficulty
- Extensive testing documented in lines 3-9
- Compared multiple models: gpt-4.1-nano, gpt-4.1-mini, gpt-5-nano
- Tested system prompts (3 variations)
- Tested user prompts (3 variations)
- Temperature tuning (6 values: 0.3, 0.5, 0.7, 0.8, 1.0, 1.2)
- Iterative refinement of hint generation approach (vague → one-step-at-a-time)
model_comparison.py: Model/prompt testingtemperature_comparison.py: Temperature optimization
- Total Lines: 438
- Functions: 9 (including
reset_chat()) - Classes: 1 (ConversationState)
- API Calls: 2 types (chunking + hints)
- Conversation Stages: 2 (select_mission, ready_for_hints)
- UI Components: 7
- Markdown header
- Chatbot display
- Textbox (message input)
- Submit button
- Clear button (new!)
- Examples component (new!)
- Tips markdown
- Special Commands: 1 (
/newto reset mission selection) - Available Transcripts: 1,034 Hitman walkthrough files loaded at startup