|
|
import os |
|
|
import gradio as gr |
|
|
from controller import generate_questions |
|
|
from controller import generate_answer |
|
|
from db import save_in_db |
|
|
|
|
|
MAX_QUESTIONS = 20 |
|
|
|
|
|
|
|
|
def _extract_question_list(result): |
|
|
"""Safely extract question list from a controller response""" |
|
|
if not result: |
|
|
return [] |
|
|
if isinstance(result, dict): |
|
|
if "success" in result and isinstance(result.get("data"), dict): |
|
|
return result["data"].get("question_list", []) |
|
|
if "question_list" in result: |
|
|
return result["question_list"] or [] |
|
|
if "data" in result and isinstance(result["data"], list): |
|
|
return result["data"] |
|
|
if isinstance(result, list): |
|
|
return result |
|
|
return [] |
|
|
|
|
|
def _extract_qa(result, original_q): |
|
|
"""Return a dict with question_content, answer_content, reasoning_content""" |
|
|
default = { |
|
|
"question_content": original_q, |
|
|
"answer_content": "No answer returned", |
|
|
"reasoning_content": "" |
|
|
} |
|
|
if not result: |
|
|
return default |
|
|
if isinstance(result, dict): |
|
|
d = result.get("data") if result.get("success") and isinstance(result.get("data"), dict) else result |
|
|
return { |
|
|
"question_content": d.get("question_content", original_q), |
|
|
"answer_content": d.get("answer_content", d.get("answer", "")) or "", |
|
|
"reasoning_content": d.get("reasoning_content", d.get("reasoning", "")) or "" |
|
|
} |
|
|
return default |
|
|
|
|
|
|
|
|
def generate_questions_ui(topic: str, num_questions: int): |
|
|
"""Stream Q β A β R one by one""" |
|
|
result_values = [] |
|
|
for _ in range(MAX_QUESTIONS): |
|
|
result_values.extend([ |
|
|
"", "", "", |
|
|
gr.update(visible=False, interactive=False), |
|
|
gr.update(visible=False, interactive=False), |
|
|
gr.update(visible=False) |
|
|
]) |
|
|
yield result_values |
|
|
|
|
|
try: |
|
|
qres = generate_questions(topic.strip(), int(num_questions)) |
|
|
except Exception as e: |
|
|
result_values[0] = f"Error generating questions: {e}" |
|
|
yield result_values |
|
|
return |
|
|
|
|
|
question_list = _extract_question_list(qres) |
|
|
if not question_list: |
|
|
result_values[0] = "No questions returned." |
|
|
yield result_values |
|
|
return |
|
|
|
|
|
for i, q in enumerate(question_list[:MAX_QUESTIONS]): |
|
|
base = i * 6 |
|
|
result_values[base + 0] = q |
|
|
result_values[base + 1] = "Generating answer..." |
|
|
result_values[base + 2] = "Generating reasoning..." |
|
|
result_values[base + 5] = gr.update(visible=True) |
|
|
yield result_values |
|
|
|
|
|
try: |
|
|
ans_res = generate_answer(q) |
|
|
qa = _extract_qa(ans_res, q) |
|
|
result_values[base + 1] = qa["answer_content"] |
|
|
result_values[base + 2] = qa["reasoning_content"] |
|
|
result_values[base + 3] = gr.update(visible=True, interactive=True) |
|
|
result_values[base + 4] = gr.update(visible=True, interactive=True) |
|
|
except Exception as e: |
|
|
result_values[base + 1] = f"Error: {e}" |
|
|
result_values[base + 2] = "" |
|
|
result_values[base + 4] = gr.update(visible=True, interactive=True) |
|
|
|
|
|
yield result_values |
|
|
|
|
|
yield result_values |
|
|
|
|
|
def accept_question(question, answer, reasoning): |
|
|
"""Save in DB and hide card""" |
|
|
parsed = { |
|
|
"question_content": question, |
|
|
"answer_language": "Odia", |
|
|
"reasoning_content": reasoning, |
|
|
"answer_content": answer, |
|
|
} |
|
|
try: |
|
|
save_in_db(parsed) |
|
|
return ( |
|
|
gr.update(visible=False), |
|
|
gr.update(visible=False), |
|
|
gr.update(visible=False) |
|
|
) |
|
|
except Exception as e: |
|
|
return ( |
|
|
gr.update(interactive=False, value=f"Error: {e}"), |
|
|
gr.update(visible=True), |
|
|
gr.update(visible=True) |
|
|
) |
|
|
|
|
|
def reject_card(): |
|
|
"""Hide rejected card""" |
|
|
return gr.update(visible=False) |
|
|
|
|
|
|
|
|
custom_css = """ |
|
|
.gradio-container { background-color: #121212 !important; color: #E0E0E0 !important; } |
|
|
.question-card { |
|
|
border: 1px solid #333; |
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.4); |
|
|
border-radius: 12px; |
|
|
padding: 20px !important; |
|
|
margin-bottom: 20px !important; |
|
|
background-color: #1E1E1E; |
|
|
transition: 0.3s ease-in-out; |
|
|
} |
|
|
.question-card:hover { |
|
|
transform: translateY(-3px); |
|
|
box-shadow: 0 6px 16px rgba(0,0,0,0.6); |
|
|
} |
|
|
textarea { background-color: #2A2A2A !important; color: #E0E0E0 !important; border: 1px solid #444 !important; border-radius: 8px !important; } |
|
|
button { border-radius: 8px !important; padding: 8px 12px !important; } |
|
|
""" |
|
|
|
|
|
with gr.Blocks(theme=gr.themes.Base(), css=custom_css) as demo: |
|
|
gr.Markdown("<h2 style='color:#90CAF9;'>π Odia Q&A β Generate β Answer (streaming)</h2>") |
|
|
|
|
|
with gr.Row(): |
|
|
topic_input = gr.Textbox(label="π Topic", placeholder="Enter a topic, e.g., 'Photosynthesis'") |
|
|
num_questions_input = gr.Dropdown(label="π’ Number of Questions", choices=[5, 10, 15, 20], value=5) |
|
|
generate_btn = gr.Button("β‘ Generate", variant="primary") |
|
|
|
|
|
output_components = [] |
|
|
for i in range(MAX_QUESTIONS): |
|
|
with gr.Group(visible=False, elem_classes=["question-card"]) as output_group: |
|
|
with gr.Row(): |
|
|
with gr.Column(scale=4): |
|
|
q_text = gr.Textbox(label="β Question", interactive=False) |
|
|
a_text = gr.Textbox(label="β
Answer", interactive=False) |
|
|
r_text = gr.Textbox(label="π§ Reasoning", interactive=False) |
|
|
with gr.Column(scale=1, min_width=150): |
|
|
accept_btn = gr.Button("Accept", variant="primary") |
|
|
reject_btn = gr.Button("Reject", variant="stop") |
|
|
|
|
|
|
|
|
accept_btn.click( |
|
|
fn=accept_question, |
|
|
inputs=[q_text, a_text, r_text], |
|
|
outputs=[accept_btn, reject_btn, output_group] |
|
|
) |
|
|
reject_btn.click(fn=reject_card, outputs=[output_group]) |
|
|
|
|
|
output_components.extend([q_text, a_text, r_text, accept_btn, reject_btn, output_group]) |
|
|
|
|
|
generate_btn.click( |
|
|
fn=generate_questions_ui, |
|
|
inputs=[topic_input, num_questions_input], |
|
|
outputs=output_components |
|
|
) |
|
|
|
|
|
demo.queue() |
|
|
|
|
|
if __name__ == "__main__": |
|
|
port = int(os.getenv("UI_PORT", "7860")) |
|
|
demo.launch(server_name="0.0.0.0", server_port=int(os.getenv("PORT", "7860")), share=False) |
|
|
|