edu-assistant / webui /coding_problem.py
Arcadia822's picture
feat: :sparkles: Configuration (#22)
1543ec3 unverified
Raw
History Blame
10.2 kB
import json
import gradio as gr
from fastapi.encoders import jsonable_encoder
from langchain.callbacks import get_openai_callback
from edu_assistant.learning_tasks.coding_problem import (
DEFAULT_FIRST_QUESTION,
DEFAULT_INSTRUCTION,
CodingProblem,
CodingProblemAnalysis,
)
from edu_assistant.utils.langchain_utils import load_vectorstore, shrink_docs
CodingProblem.enable_redis_orm()
class CodingProblemUI:
def __init__(
self,
*,
instruction: str = DEFAULT_INSTRUCTION,
first_question: str = DEFAULT_FIRST_QUESTION,
knowledge_name: str = "example",
enable_gpt4: bool = False,
):
self._init_task(instruction, first_question, knowledge_name, enable_gpt4)
self._init_ui()
def ui_render(self):
self.ui.render()
def ui_reload(
self,
*,
instruction: str = DEFAULT_INSTRUCTION,
first_question: str = DEFAULT_FIRST_QUESTION,
knowledge_name: str = "example",
enable_gpt4: bool = False,
refresh: bool = True,
):
self._init_task(instruction, first_question, knowledge_name, enable_gpt4)
if refresh:
self.ui_render()
def get_instruction(self):
return self.instruction
def get_first_question(self):
return self.first_question
def _init_task(self, instruction: str, first_question: str, knowledge_name: str, enable_gpt4: bool):
self.instruction = instruction
self.first_question = first_question
self.knowledge = knowledge_name
self.enable_gpt4 = enable_gpt4
self.task = CodingProblemAnalysis(
instruction=instruction,
first_question=first_question,
knowledge=load_vectorstore(knowledge_name).as_retriever(),
enable_gpt4=enable_gpt4,
)
def _init_ui(self):
self.ui = gr.Blocks()
with self.ui:
with gr.Row():
with gr.Column(scale=6):
problem_selector = gr.Dropdown(choices=self._get_problems(), show_label=False, interactive=True)
with gr.Column(scale=1):
refresh_btn = gr.Button(value="刷新")
with gr.Row():
with gr.Column(scale=6):
with gr.Tab(label="错误代码分析"):
with gr.Row():
with gr.Column(scale=3):
with gr.Row():
problem_view = gr.Markdown(label="题目")
with gr.Row():
code_view = gr.Textbox(label="代码", lines=10, interactive=True)
with gr.Column(scale=3):
with gr.Row():
chat_box = gr.Chatbot(height=500, label="聊天记录")
with gr.Row():
chat_input = gr.Textbox(show_label=False)
with gr.Column():
with gr.Row():
analysis_btn = gr.Button(value="分析")
with gr.Row():
clear = gr.ClearButton()
with gr.Row():
session_id = gr.Textbox(label="Session", interactive=False, value="")
with gr.Row():
status = gr.JSON(value={"tokens": 0}, label="Status")
with gr.Row():
docs = gr.JSON(value=["docs"], label="Docs")
with gr.Tab(label="题库管理"):
with gr.Row():
with gr.Column(scale=6):
with gr.Row():
title_edit = gr.Textbox(label="标题", interactive=True)
with gr.Row():
language_edit = gr.Dropdown(
choices=["python", "cpp", "java"],
label="语言",
interactive=True,
allow_custom_value=True,
)
with gr.Column(scale=1):
manage_update = gr.Button(value="更新")
manage_delete = gr.Button(value="删除", variant="stop")
with gr.Row():
with gr.Column():
problem_edit = gr.Textbox(label="题目", lines=10, max_lines=100, interactive=True)
with gr.Column():
analysis_edit = gr.Textbox(label="解析", lines=10, max_lines=100, interactive=True)
with gr.Row():
answer_edit = gr.Textbox(label="标准答案", lines=10, max_lines=100, interactive=True)
with gr.Row():
extra_edit = gr.Textbox(label="额外信息", lines=10, max_lines=100, interactive=True)
refresh_btn.click(self._update_problems, [], [problem_selector])
problem_selector.select(
self._select_problem,
[
problem_selector,
],
[problem_view, title_edit, language_edit, problem_edit, analysis_edit, answer_edit, extra_edit],
)
analysis_btn.click(
self._analysis_problem,
[problem_selector, code_view],
[chat_box, session_id, status, docs],
)
chat_input.submit(self._chat, [chat_input, chat_box, session_id], [chat_input, chat_box, status, docs])
manage_update.click(
self._update_problem,
[title_edit, language_edit, problem_edit, analysis_edit, answer_edit, extra_edit],
[],
)
manage_delete.click(
self._delete_problem,
[problem_selector],
[
problem_selector,
problem_view,
title_edit,
language_edit,
problem_edit,
analysis_edit,
answer_edit,
extra_edit,
],
)
clear.click(
self._clear,
[],
[
problem_selector,
problem_view,
code_view,
chat_box,
session_id,
status,
docs,
problem_view,
title_edit,
language_edit,
problem_edit,
analysis_edit,
answer_edit,
extra_edit,
],
)
def _get_problems(self) -> list[str]:
data = CodingProblem.select(columns=["title"])
if not data:
return []
titles = [problem_data["title"] for problem_data in data]
return titles
def _update_problems(self):
titles = self._get_problems()
gr.Info("更新题目列表成功")
return gr.Dropdown.update(choices=titles)
def _select_problem(self, title: str):
problem: CodingProblem = CodingProblem.select(ids=[title])[0]
return (
problem.expr(),
problem.title,
problem.language,
problem.question,
problem.analysis,
problem.standard_answer,
json.dumps(problem.extra, ensure_ascii=False, indent=4),
)
def _update_problem(self, title, language, problem, analysis, answer, extra):
# TODO: add language
try:
extra_data = json.loads(extra)
except json.JSONDecodeError:
extra_data = [extra]
CodingProblem.update(
title,
data={
"title": title,
"language": language,
"question": problem,
"analysis": analysis,
"standard_answer": answer,
"extra": extra_data,
},
)
gr.Info("更新题目成功")
def _delete_problem(self, title):
CodingProblem.delete(ids=[title])
gr.Info("删除题目成功")
return "", "", "", "", "", "", "", ""
def _analysis_problem(self, title, code, extra: str = ""):
problem = CodingProblem.select(ids=[title])[0]
answer = CodingProblemAnalysis.build_coding_answer(answer=code, extra=[extra])
with get_openai_callback() as cb:
result = self.task.start_analysis(problem, answer)
status = {"tokens": cb.total_tokens, "cost": f"${cb.total_cost:.4f}"}
answer = result["answer"]
session_id = result["session_id"]
docs = jsonable_encoder(shrink_docs(result.get("source_documents", [])))
return [(self.first_question, answer)], session_id, status, docs
def _chat(self, message, chat_history, session_id):
if not session_id:
return "", "", {"tokens": 0}, []
with get_openai_callback() as cb:
result = self.task.ask(message, session_id=session_id)
if not result:
raise gr.Error("Session expired. Please recreate a new problem analysis session.")
session_id = result["session_id"]
docs = jsonable_encoder(result.get("source_documents", []))
bot_message = result["answer"]
chat_history.append((message, bot_message))
status = {"tokens": cb.total_tokens, "cost": f"${cb.total_cost:.4f}"}
return "", chat_history, status, docs
def _clear(self):
return "", "", "", [], "", {"tokens": 0}, ["docs"], "", "", "", "", "", "", ""