|
import os |
|
import gradio as gr |
|
import speech_recognition as sr |
|
import numpy as np |
|
import tempfile |
|
import scipy.io.wavfile |
|
import requests |
|
|
|
|
|
os.environ["MISTRAL_API_KEY"] = "R1ISnVkHrj7fSd5Dh6ZSZHqCJhhct0ZR" |
|
os.environ["GROQ_API_KEY"] = "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" |
|
|
|
state = { |
|
"started": False, |
|
"history": [], |
|
"current_q": "", |
|
"question_num": 0, |
|
"hint_mode": False, |
|
"final_answer": "" |
|
} |
|
|
|
def convert_audio_to_text(audio, lang_choices): |
|
if audio is None: |
|
return "" |
|
try: |
|
sr_rate, audio_data = audio |
|
with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as fp: |
|
scipy.io.wavfile.write(fp.name, sr_rate, audio_data) |
|
recog = sr.Recognizer() |
|
with sr.AudioFile(fp.name) as src: |
|
recorded = recog.record(src) |
|
lang = "en-US" if "English" in lang_choices else "ur-PK" if "Urdu" in lang_choices else "en-US" |
|
return recog.recognize_google(recorded, language=lang).lower() |
|
except Exception as e: |
|
return f"[Error] Couldn't transcribe: {str(e)}" |
|
|
|
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"].strip() |
|
else: |
|
return f"[{api} Error] {response.status_code} - {response.text}" |
|
|
|
def begin_game(): |
|
state["started"] = True |
|
state["history"] = [] |
|
state["question_num"] = 1 |
|
state["hint_mode"] = False |
|
state["final_answer"] = "" |
|
state["current_q"] = "Does it belong to the world of living things?" |
|
return ( |
|
f"Question 1: {state['current_q']}", |
|
"", |
|
gr.update(visible=False), |
|
gr.update(visible=True), |
|
gr.update(visible=False), |
|
state["question_num"] |
|
) |
|
|
|
def interpret_answer(user_answer): |
|
if not state["started"]: |
|
return "⛔ Please start a new game.", "", gr.update(visible=False), "", gr.update(visible=False), state["question_num"] |
|
|
|
normalized = user_answer.strip().lower() |
|
if normalized not in ["yes", "no", "ہاں", "نہیں"]: |
|
return "⚠️ Respond with 'yes' or 'no' (or ہاں/نہیں).", user_answer, gr.update(visible=False), "", gr.update(visible=False), state["question_num"] |
|
|
|
state["history"].append((state["current_q"], normalized)) |
|
|
|
if state["question_num"] >= 20: |
|
state["started"] = False |
|
guess = generate_final_guess() |
|
state["final_answer"] = guess |
|
return "Game Over!", user_answer, gr.update(visible=True), "", gr.update(value=guess, visible=True), state["question_num"] |
|
|
|
if state["question_num"] % 5 == 0: |
|
guess = generate_final_guess() |
|
if "i think" in guess.lower() or "maybe" in guess.lower(): |
|
state["current_q"] = f"{guess} Am I right?" |
|
return state["current_q"], user_answer, gr.update(visible=False), "", gr.update(visible=False), state["question_num"] |
|
|
|
state["question_num"] += 1 |
|
state["current_q"] = get_next_question() |
|
return ( |
|
f"Question {state['question_num']}: {state['current_q']}", |
|
user_answer, |
|
gr.update(visible=False), |
|
"", |
|
gr.update(visible=False), |
|
state["question_num"] |
|
) |
|
|
|
def get_next_question(): |
|
history_prompt = "\n".join([f"Q: {q}\nA: {a}" for q, a in state["history"]]) |
|
prompt = ( |
|
"You're playing a guessing game. Only respond with a yes/no question, nothing else.\n" |
|
"Based on the following history, ask the next smart question:\n\n" |
|
f"{history_prompt}\n\n" |
|
"Next question only:" |
|
) |
|
return query_llm("MISTRAL", [{"role": "user", "content": prompt}]) |
|
|
|
def generate_final_guess(): |
|
history = "\n".join([f"Q: {q}\nA: {a}" for q, a in state["history"]]) |
|
prompt = f"""Guess the secret concept based on these Q&A. If you're not sure, say "I need more clues."\n\n{history}\n\nYour guess:""" |
|
return query_llm("MISTRAL", [{"role": "user", "content": prompt}]) |
|
|
|
def hint_response(): |
|
if not state["hint_mode"] or not state["started"]: |
|
return "Hint mode is off or game not active." |
|
question = state["current_q"] |
|
history = "\n".join([f"{q} - {a}" for q, a in state["history"]]) |
|
prompt = f"""The player seems confused by this question: "{question}". Previous Q&A were:\n{history}\n\nGive a helpful, simple hint.""" |
|
return query_llm("MISTRAL", [{"role": "user", "content": prompt}]) |
|
|
|
def toggle_hint_mode(): |
|
state["hint_mode"] = not state["hint_mode"] |
|
return ( |
|
"Hint Mode: ON" if state["hint_mode"] else "Hint Mode: OFF", |
|
gr.update(visible=True) |
|
) |
|
|
|
|
|
with gr.Blocks(title="Kasoti", theme=gr.themes.Soft(primary_hue="pink", secondary_hue="blue")) as demo: |
|
gr.Markdown( |
|
""" |
|
<link href="https://fonts.googleapis.com/css2?family=Comic+Neue&display=swap" rel="stylesheet"> |
|
<style> |
|
* { font-family: 'Comic Neue', cursive; } |
|
body { |
|
background: linear-gradient(120deg, #ffe0f0 0%, #e0f7ff 100%); |
|
} |
|
.gr-button { |
|
border-radius: 12px !important; |
|
font-size: 16px !important; |
|
padding: 10px 18px !important; |
|
} |
|
.title-text { |
|
text-align: center; |
|
font-size: 32px; |
|
color: #ff69b4; |
|
font-weight: bold; |
|
margin-bottom: 0; |
|
} |
|
.subtitle-text { |
|
text-align: center; |
|
font-size: 16px; |
|
color: #555; |
|
margin-top: 0; |
|
} |
|
</style> |
|
<div class='title-text'>🧠 Kasoti - The Mind Reading Game</div> |
|
<p class='subtitle-text'>Think of something and answer my yes/no questions — let's see if I can guess it in 20 tries! 🕵️♀️</p> |
|
""" |
|
) |
|
with gr.Row(): |
|
start = gr.Button("🎲 Start Game") |
|
hint_toggle = gr.Button("💡 Toggle Hint Mode") |
|
hint_status = gr.Textbox(value="Hint Mode: OFF", show_label=False, interactive=False) |
|
|
|
with gr.Row(): |
|
with gr.Column(scale=1): |
|
gr.Markdown("### 🎤 Your Answer") |
|
lang_sel = gr.CheckboxGroup(["English", "Urdu"], label="🌐 Choose Language", value=["English"]) |
|
mic_input = gr.Audio(sources=["microphone"], type="numpy", label="🎙️ Speak Your Answer") |
|
transcribe = gr.Button("📝 Transcribe Voice") |
|
typed_ans = gr.Textbox(label="✍️ Or Type Your Answer") |
|
submit = gr.Button("✅ Yep, Here You Go!") |
|
|
|
with gr.Column(scale=2): |
|
gr.Markdown("### 🎮 Game Progress") |
|
question_progress = gr.Slider(minimum=1, maximum=20, value=1, label="❓ Question Number", interactive=False) |
|
game_q_box = gr.Textbox(label="🧩 Current Question", interactive=False, lines=2) |
|
game_history = gr.Textbox(label="📜 Game Updates", interactive=False, lines=3) |
|
final_answer_box = gr.Textbox(label="🎯 Final Guess", visible=False, lines=2, interactive=False) |
|
|
|
|
|
start.click( |
|
fn=begin_game, |
|
outputs=[game_q_box, game_history, final_answer_box, submit, final_answer_box, question_progress] |
|
) |
|
hint_toggle.click(fn=toggle_hint_mode, outputs=[hint_status, game_history]) |
|
hint_toggle.click(fn=hint_response, outputs=[game_history]) |
|
transcribe.click(fn=convert_audio_to_text, inputs=[mic_input, lang_sel], outputs=[typed_ans]) |
|
submit.click( |
|
fn=interpret_answer, |
|
inputs=[typed_ans], |
|
outputs=[game_q_box, typed_ans, final_answer_box, game_history, final_answer_box, question_progress] |
|
) |
|
|
|
demo.launch() |
|
|