File size: 9,122 Bytes
a374aef
 
6173475
 
 
 
 
 
07e9b52
1b81753
 
5f987f1
a374aef
 
 
 
 
 
 
1b81753
6173475
a374aef
 
6173475
 
a374aef
5f987f1
a374aef
 
 
6173475
 
 
 
a374aef
 
 
 
6173475
a374aef
5f987f1
1b81753
 
 
 
 
 
 
 
 
 
 
 
 
6173475
 
 
 
 
 
 
 
 
 
a374aef
 
 
 
6173475
a374aef
 
 
 
 
 
6173475
 
a374aef
6173475
 
a6f2dac
a374aef
 
6173475
 
a374aef
6173475
 
a374aef
 
6173475
 
a374aef
6173475
 
a374aef
 
6173475
a374aef
 
 
6173475
a374aef
 
6173475
 
 
 
 
 
f728565
a374aef
6173475
a374aef
 
 
6173475
a374aef
 
 
6173475
db56e14
5f987f1
 
 
 
 
 
 
 
a374aef
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6cbb352
5f987f1
a374aef
 
 
 
 
 
 
1b08205
a374aef
1b81753
 
a374aef
f626a20
 
 
5f987f1
a374aef
 
65b63b6
a374aef
 
1b81753
f626a20
a374aef
1b81753
a374aef
1d90dcf
 
 
5f987f1
1b81753
 
 
 
 
 
 
a374aef
1b81753
a374aef
 
 
1b81753
a374aef
1b81753
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
import gradio as gr
import requests
import speech_recognition as sr
import tempfile
import scipy.io.wavfile
import os
import numpy as np


os.environ["MISTRAL_API_KEY"] = "cNjUx79Hl0A2AeiAMf6yi7o7ah4APoZy"
os.environ["GROQ_API_KEY"] = "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"

game_state = {
    "active": False,
    "questions_asked": 0,
    "answers": [],
    "current_question": None,
    "consult_mode": False
}
consult_history = []

def transcribe_audio(audio, language):
    if audio is None:
        return ""
    try:
        sr_rate, audio_data = audio
        audio_data = np.array(audio_data, dtype=np.int16)

        with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp_file:
            scipy.io.wavfile.write(tmp_file.name, sr_rate, audio_data)
            tmp_file_path = tmp_file.name

        recognizer = sr.Recognizer()
        with sr.AudioFile(tmp_file_path) as source:
            audio_content = recognizer.record(source)
            language_code = "en-US" if language == "English" else "ur-PK"
            text = recognizer.recognize_google(audio_content, language=language_code)
            return text.lower()
    except Exception as e:
        print(f"Transcription error: {e}")
        return "Could not transcribe."

def handle_user_question(user_question):
    if not game_state["consult_mode"]:
        return "❗Consultant mode is off.", gr.update(value=""), gr.update(visible=True)

    messages = [{"role": "user", "content": user_question}]
    answer = query_llm("MISTRAL", messages)
    if answer:
        consult_history.append((user_question, answer))
        formatted_history = "\n".join([f"**Q:** {q}\n**A:** {a}" for q, a in consult_history])
        return answer, gr.update(value=formatted_history), gr.update(visible=True)
    else:
        return "⚠️ Failed to get a response.", gr.update(), gr.update()

def query_llm(api, messages, model=None):
    headers = {
        "Authorization": f"Bearer {os.environ[f'{api}_API_KEY']}",
        "Content-Type": "application/json"
    }
    payload = {
        "messages": messages,
        "model": model or ("llama3-70b-8192" if api == "GROQ" else "mistral-medium")
    }
    endpoint = {
        "MISTRAL": "https://api.mistral.ai/v1/chat/completions",
        "GROQ": "https://api.groq.com/openai/v1/chat/completions"
    }[api]

    response = requests.post(endpoint, headers=headers, json=payload)
    if response.status_code == 200:
        return response.json()["choices"][0]["message"]["content"]
    else:
        print(f"Error from {api} API: {response.text}")
        return None

def generate_question(answers):
    prompt = "You are playing a game called Kasoti (20 Questions)...\n"
    for i, (q, a) in enumerate(answers, 1):
        prompt += f"{i}. Q: {q}\n   A: {a}\n"
    prompt += "\nAsk ONLY the next best yes/no question. When you are sure about what word user is thinking, User will think of any publicly known personality, a common place, living thing or object, end the question with is this correct?"
    response = query_llm("GROQ", [{"role": "user", "content": prompt}])
    return response.strip() if response else "Is it something you can hold?"

def make_guess(answers):
    prompt = "Based on the following yes/no history, make a best guess.\n\nHistory:\n"
    for i, (q, a) in enumerate(answers, 1):
        prompt += f"{i}. Q: {q}\n   A: {a}\n"
    response = query_llm("GROQ", [{"role": "user", "content": prompt}])
    return response.strip() if response else "I need more information."

def get_hint(question, answers):
    prompt = f"The player is unsure about answering: '{question}'\n\nHistory:\n"
    for q, a in answers:
        prompt += f"- Q: {q}\n  A: {a}\n"
    prompt += "\nSuggest a helpful hint to clarify."
    return query_llm("MISTRAL", [{"role": "user", "content": prompt}]) or "Consider the common meaning."

def normalize_answer(ans):
    ans = ans.strip().lower()
    return "yes" if ans in ["yes", "y", "ہاں", "haan"] else "no" if ans in ["no", "n", "نہیں", "nahi"] else None

def start_game():
    game_state.update({
        "active": True,
        "questions_asked": 1,
        "answers": [],
        "current_question": "Is it a living thing?",
        "consult_mode": False
    })
    intro = "🎯 **Kasoti Started!**\nThink of something... I'll guess in 20 questions.\n\n➡️ First Question: Is it a living thing?"
    return intro, gr.update(interactive=True), gr.update(interactive=True), "🔕 Consult Mode: OFF", gr.update(visible=False)

def process_answer(answer_text):
    if not game_state["active"]:
        return "⚠️ Start the game first.", "", "", gr.update(visible=False)

    normalized = normalize_answer(answer_text)
    if normalized is None:
        return "❌ Please reply with 'yes' or 'no' (or 'ہاں/نہیں').", "", answer_text, gr.update(visible=game_state["consult_mode"])

    if game_state["current_question"] and "is this correct" in game_state["current_question"].lower():
        if normalized == "yes":
            game_state["active"] = False
            return "🎉 YAY! I guessed it!", "", answer_text, gr.update(visible=False)
        else:
            next_q = generate_question(game_state["answers"])
            game_state["current_question"] = next_q
            game_state["questions_asked"] += 1
            return next_q, "", answer_text, gr.update(visible=game_state["consult_mode"])

    game_state["answers"].append((game_state["current_question"], normalized))

    if game_state["questions_asked"] >= 20:
        game_state["active"] = False
        guess = make_guess(game_state["answers"])
        return f"🕹️ Game over! My final guess: **{guess}**", "", answer_text, gr.update(visible=False)

    if game_state["questions_asked"] % 5 == 0:
        guess = make_guess(game_state["answers"])
        if guess.startswith("I think it's"):
            game_state["current_question"] = guess + " Is this correct? (yes/no)"
            return game_state["current_question"], "", answer_text, gr.update(visible=game_state["consult_mode"])

    next_q = generate_question(game_state["answers"])
    game_state["current_question"] = next_q
    game_state["questions_asked"] += 1
    return next_q, "", answer_text, gr.update(visible=game_state["consult_mode"])

def toggle_consult_mode():
    game_state["consult_mode"] = not game_state["consult_mode"]
    return ("🔔 Consult Mode: ON" if game_state["consult_mode"] else "🔕 Consult Mode: OFF",
            gr.update(visible=game_state["consult_mode"]),
            gr.update(visible=game_state["consult_mode"]))

def get_consult_hint():
    if not game_state["active"] or not game_state["consult_mode"]:
        return "ℹ️ Consult mode is not active or game not started."
    return get_hint(game_state["current_question"], game_state["answers"])

# Colorful and polished UI
with gr.Blocks(css="body {background-color: #B100CD;}") as demo:
    gr.Markdown("## 🧠 Kasoti: 20 Questions AI Game")
    gr.Markdown("Think of a person, place, or thing. I'll try to guess it in 20 questions or less!")

    with gr.Row():
        with gr.Column():
            start_btn = gr.Button("🚀 Start Game", elem_id="start-btn")
            consult_output = gr.Textbox(label="💡 Consult Hint", visible=False)

    with gr.Row():
        with gr.Column():
            game_output = gr.Textbox(label="🎲 Game Progress", interactive=False)
            language = gr.Dropdown(["English", "Urdu"], label="Audio Language", value="English")
            audio_input = gr.Audio(label="🎤 Answer via Microphone", type="numpy", sources=["microphone"])
            transcribe_btn = gr.Button("📝 Transcribe Audio", elem_id="transcribe-btn")
        with gr.Column():
            transcribed_text = gr.Textbox(label="✍️ Answer Text", interactive=True)
            submit_btn = gr.Button("✅ Submit Answer", elem_id="submit-btn")

    with gr.Row():
        consult_btn = gr.Button("💬 Toggle Consult Mode", elem_id="consult-btn")
        consult_status = gr.Textbox(label="Consult Mode", interactive=False)

    with gr.Row(visible=False) as consult_row:
        user_question = gr.Textbox(label="Ask a question to the LLM")
        ask_btn = gr.Button("📤 Ask")
        llm_answer = gr.Textbox(label="🧠 LLM Answer", interactive=False)
        consult_history_box = gr.Textbox(label="🗂️ Session History", lines=8, interactive=False)

    # Events
    start_btn.click(start_game, outputs=[game_output, transcribed_text, submit_btn, consult_status, consult_output])
    consult_btn.click(toggle_consult_mode, outputs=[consult_status, consult_output, consult_row])
    consult_btn.click(get_consult_hint, outputs=[consult_output])
    transcribe_btn.click(transcribe_audio, inputs=[audio_input, language], outputs=[transcribed_text])
    submit_btn.click(process_answer, inputs=[transcribed_text], outputs=[game_output, transcribed_text, transcribed_text, consult_output])
    ask_btn.click(handle_user_question, inputs=[user_question], outputs=[llm_answer, consult_history_box, consult_row])

demo.launch()