import os import gradio as gr from controller import generate_questions # returns question_list (or a dict containing it) from controller import generate_answer # returns a dict with question_content, answer_content, reasoning_content from db import save_in_db # saves parsed dict into mongo MAX_QUESTIONS = 20 # ---------------- Helpers ---------------- # 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 # ---------------- Handlers ---------------- # 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([ "", "", "", # Q, A, R gr.update(visible=False, interactive=False), # accept gr.update(visible=False, interactive=False), # reject gr.update(visible=False) # group hidden ]) 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), # accept_btn gr.update(visible=False), # reject_btn gr.update(visible=False) # group hidden ) 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) # ---------------- UI Layout ---------------- # 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("

🌙 Odia Q&A — Generate → Answer (streaming)

") 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") # Bind buttons accept_btn.click( fn=accept_question, inputs=[q_text, a_text, r_text], # ✅ only inputs outputs=[accept_btn, reject_btn, output_group] # ✅ update group visibility ) 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)