-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathutils.py
More file actions
164 lines (132 loc) · 5.44 KB
/
utils.py
File metadata and controls
164 lines (132 loc) · 5.44 KB
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
"""
Utility functions for the 20Q game.
Handles OpenAI API interactions and game logic.
"""
import os
from typing import List, Dict, Optional
from dotenv import load_dotenv
from openai import OpenAI
# Load environment variables
load_dotenv()
# Initialize OpenAI client lazily
_client = None
def get_client():
"""Get or create the OpenAI client."""
global _client
if _client is None:
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
raise ValueError(
"OPENAI_API_KEY environment variable is not set. "
"Please set it in Streamlit Cloud secrets or your .env file."
)
_client = OpenAI(api_key=api_key)
return _client
def initialize_game() -> List[Dict[str, str]]:
"""
Initialize the game with a system prompt.
Returns the initial conversation history.
"""
system_prompt = """You are playing a game of 20 Questions. The player is thinking of an English word, and you need to guess it by asking yes/no questions.
Rules:
- Ask one question at a time
- Questions should be yes/no questions
- The player can answer: "Yes", "No", "Sometimes", or "Unknown"
- Try to guess the word within 20 questions (you can use up to 25 if needed)
- When you are confident you know the word, make a guess using the format: "GUESS: [word]" (e.g., "GUESS: elephant")
- If you're not sure but want to test a hypothesis, you can ask "Is it [word]?" as a question
- Be strategic with your questions to narrow down possibilities
- Start with broad questions and get more specific as you learn more
- Use the conversation history to build context and make smarter guesses
Begin by asking your first question."""
return [
{"role": "system", "content": system_prompt},
{"role": "assistant", "content": "I'm ready to play 20 Questions! Think of an English word, and I'll try to guess it. Let me start with my first question:"}
]
def get_ai_question(conversation_history: List[Dict[str, str]]) -> str:
"""
Get the next question or guess from the AI.
Args:
conversation_history: List of message dictionaries with role and content
Returns:
The AI's question or guess as a string
"""
try:
client = get_client()
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=conversation_history,
temperature=0.7,
max_tokens=150
)
ai_message = response.choices[0].message.content.strip()
return ai_message
except ValueError as e:
return f"Configuration Error: {str(e)}"
except Exception as e:
return f"Error: {str(e)}. Please check your API key and try again."
def add_player_answer(conversation_history: List[Dict[str, str]], answer: str) -> None:
"""
Add the player's answer to the conversation history.
Args:
conversation_history: List of message dictionaries
answer: Player's answer ("Yes", "No", "Sometimes", or "Unknown")
"""
conversation_history.append({"role": "user", "content": answer})
def check_for_guess(message: str) -> Optional[str]:
"""
Check if the AI message contains a guess.
Guesses are in the format "GUESS: [word]" or similar patterns.
Args:
message: The AI's message
Returns:
The guessed word if found, None otherwise
"""
import re
message_lower = message.lower()
# Check for explicit "GUESS:" format (case insensitive)
guess_match = re.search(r'guess\s*:\s*([^\s\.\?!]+)', message_lower)
if guess_match:
guess = guess_match.group(1).strip()
guess = guess.replace('"', '').replace("'", "").strip()
if guess and len(guess) > 1:
return guess
# Check for "Is it [word]?" pattern (common guess format)
# Handle both "Is it word?" and "Is it a/an/the word?" patterns
is_it_match = re.search(r'is\s+it\s+(?:a|an|the\s+)?([^\s\.\?!]+)', message_lower)
if is_it_match:
guess = is_it_match.group(1).strip()
guess = guess.replace('"', '').replace("'", "").strip()
# Filter out common question words and articles
if guess and len(guess) > 1 and guess not in ['a', 'an', 'the']:
return guess
# Check for other common guess patterns
guess_patterns = [
(r"i\s+think\s+it'?s\s+([^\s\.\?!]+)", 1),
(r"i\s+believe\s+it'?s\s+([^\s\.\?!]+)", 1),
(r"it\s+must\s+be\s+([^\s\.\?!]+)", 1),
(r"the\s+word\s+is\s+([^\s\.\?!]+)", 1),
(r"my\s+guess\s+is\s+([^\s\.\?!]+)", 1),
]
for pattern, group_num in guess_patterns:
match = re.search(pattern, message_lower)
if match:
guess = match.group(group_num).strip()
guess = guess.replace('"', '').replace("'", "").strip()
# Remove common articles and filter short words
if guess and len(guess) > 1 and guess not in ['a', 'an', 'the']:
return guess
return None
def format_conversation_history(conversation_history: List[Dict[str, str]]) -> List[Dict[str, str]]:
"""
Format conversation history for display, excluding system messages.
Args:
conversation_history: Full conversation history
Returns:
Formatted history without system messages
"""
formatted = []
for msg in conversation_history:
if msg["role"] != "system":
formatted.append(msg)
return formatted