Spaces:
Runtime error
Runtime error
Merge pull request #16 from codedog-ai/feat/#2-coding-problem-mgmt
Browse files- README.md +19 -15
- edu_assistant/learning_tasks/coding_problem.py +33 -17
- edu_assistant/utils/redis_utils.py +20 -0
- examples/coding_problem.py +2 -0
- examples/coding_problem_orm.py +21 -0
- examples/coding_problems.json +9 -0
- poetry.lock +0 -0
- pyproject.toml +2 -0
- webui/__init__.py +0 -0
- webui/coding_problem.py +194 -1
- webui/qa.py +4 -3
- webui/ui.py +3 -2
README.md
CHANGED
|
@@ -19,21 +19,25 @@ license: mit
|
|
| 19 |
|
| 20 |
## config
|
| 21 |
|
| 22 |
-
| Environment Variable | Necessary | Default
|
| 23 |
-
| ----------------------------- | --------- | ------- | ---------------------------------------- |
|
| 24 |
-
| CODEDOG_SERVER | No | 0.0.0.0
|
| 25 |
-
| CODEDOG_PORT | No | 32167
|
| 26 |
-
| CODEDOG_WORKER_NUM | No | 1
|
| 27 |
-
| OPENAI_API_KEY | Yes |
|
| 28 |
-
| OPENAI_PROXY | No |
|
| 29 |
-
| AZURE_OPENAI | No |
|
| 30 |
-
| AZURE_OPENAI_API_KEY | No |
|
| 31 |
-
| AZURE_OPENAI_API_BASE | No |
|
| 32 |
-
| AZURE_OPENAI_DEPLOYMENT_ID | No |
|
| 33 |
-
| AZURE_OPENAI_EMBEDDING_DEP_ID | No |
|
| 34 |
-
| QDRANT_API | No |
|
| 35 |
-
| QDRANT_API_KEY | No |
|
| 36 |
-
| QDRANT_API_BASE | No |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
|
| 38 |
## setup
|
| 39 |
|
|
|
|
| 19 |
|
| 20 |
## config
|
| 21 |
|
| 22 |
+
| Environment Variable | Necessary | Default | Description |
|
| 23 |
+
| ----------------------------- | --------- | --------- | ---------------------------------------- |
|
| 24 |
+
| CODEDOG_SERVER | No | 0.0.0.0 | Server address |
|
| 25 |
+
| CODEDOG_PORT | No | 32167 | Server port |
|
| 26 |
+
| CODEDOG_WORKER_NUM | No | 1 | Server worker number |
|
| 27 |
+
| OPENAI_API_KEY | Yes | | Api Key for calling openai api |
|
| 28 |
+
| OPENAI_PROXY | No | | openai proxy |
|
| 29 |
+
| AZURE_OPENAI | No | | use azure openai if not blank |
|
| 30 |
+
| AZURE_OPENAI_API_KEY | No | | azure openai api key |
|
| 31 |
+
| AZURE_OPENAI_API_BASE | No | | azure openai api base |
|
| 32 |
+
| AZURE_OPENAI_DEPLOYMENT_ID | No | | azure openai deployment id for gpt 3.5 |
|
| 33 |
+
| AZURE_OPENAI_EMBEDDING_DEP_ID | No | | azure openai deployment id for embedding |
|
| 34 |
+
| QDRANT_API | No | | use qdrant api if not blank |
|
| 35 |
+
| QDRANT_API_KEY | No | | qdrant api key |
|
| 36 |
+
| QDRANT_API_BASE | No | | qdrant api base |
|
| 37 |
+
| REDIS_HOST | No | 127.0.0.1 | redis host. used for orm |
|
| 38 |
+
| REDIS_PORT | No | 6379 | redis port |
|
| 39 |
+
| REDIS_PASSWORD | No | | redis password |
|
| 40 |
+
| REDIS_SSL | No | | connect use ssl if not blank |
|
| 41 |
|
| 42 |
## setup
|
| 43 |
|
edu_assistant/learning_tasks/coding_problem.py
CHANGED
|
@@ -4,9 +4,11 @@ from langchain.chains.base import Chain
|
|
| 4 |
from langchain.memory import ConversationBufferMemory
|
| 5 |
from langchain.schema import BaseRetriever
|
| 6 |
from pydantic import BaseModel, Field
|
|
|
|
| 7 |
|
| 8 |
from edu_assistant.learning_tasks.base import BaseTask
|
| 9 |
from edu_assistant.utils.langchain_utils import load_llm
|
|
|
|
| 10 |
|
| 11 |
TEMPLATE = """The following is a friendly conversation between a human and an ai.
|
| 12 |
The ai is talkative and provides lots of specific details from its context.
|
|
@@ -38,24 +40,33 @@ The code is written by a student aged 5-10 and mostly like to buggy or bad perfo
|
|
| 38 |
DEFAULT_FIRST_QUESTION = "请问这段代码中有什么问题吗?"
|
| 39 |
|
| 40 |
|
| 41 |
-
class CodingProblem(
|
|
|
|
|
|
|
| 42 |
question: str = Field()
|
| 43 |
standard_answer: str = Field(default="")
|
| 44 |
analysis: str = Field(default="")
|
| 45 |
-
|
|
|
|
| 46 |
|
| 47 |
# TODO: Add cache to expr function with pydantic 2 computed_field decorator.
|
| 48 |
# Wait for langchain to support pydantic2.
|
| 49 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
def expr(self, lang=""):
|
| 51 |
-
expr = f"Question
|
| 52 |
expr += (
|
| 53 |
-
f"
|
| 54 |
if self.standard_answer
|
| 55 |
else ""
|
| 56 |
)
|
| 57 |
-
expr += f"
|
| 58 |
-
expr += "".join(self.extra) + "\n"
|
| 59 |
return expr
|
| 60 |
|
| 61 |
def __str__(self):
|
|
@@ -84,6 +95,16 @@ class CodingProblemAnalysis(BaseTask):
|
|
| 84 |
self._session_store = {}
|
| 85 |
self._knowledge = knowledge
|
| 86 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
def start_analysis(self, problem: CodingProblem, answer: CodingAnswer, first_question: str = None) -> dict:
|
| 88 |
"""start analysis of a coding problem and incorrect answer.
|
| 89 |
|
|
@@ -116,6 +137,9 @@ class CodingProblemAnalysis(BaseTask):
|
|
| 116 |
"""
|
| 117 |
assert question
|
| 118 |
|
|
|
|
|
|
|
|
|
|
| 119 |
chain = self._session_store[session_id]
|
| 120 |
|
| 121 |
result = chain({"input": question})
|
|
@@ -129,7 +153,9 @@ class CodingProblemAnalysis(BaseTask):
|
|
| 129 |
memory = ConversationBufferMemory()
|
| 130 |
prompt = PromptTemplate.from_template(
|
| 131 |
TEMPLATE.format(
|
| 132 |
-
instruction=self.instruction,
|
|
|
|
|
|
|
| 133 |
)
|
| 134 |
)
|
| 135 |
|
|
@@ -148,13 +174,3 @@ class CodingProblemAnalysis(BaseTask):
|
|
| 148 |
return_source_documents=True,
|
| 149 |
combine_docs_chain_kwargs={"prompt": prompt},
|
| 150 |
)
|
| 151 |
-
|
| 152 |
-
@staticmethod
|
| 153 |
-
def build_coding_problem(question: str, standard_answer: str = "", analysis: str = "", extra: list[str] = None):
|
| 154 |
-
extra = [] if extra is None else extra
|
| 155 |
-
return CodingProblem(question=question, standard_answer=standard_answer, analysis=analysis, extra=extra)
|
| 156 |
-
|
| 157 |
-
@staticmethod
|
| 158 |
-
def build_coding_answer(answer: str, extra: list[str] = None):
|
| 159 |
-
extra = [] if extra is None else extra
|
| 160 |
-
return CodingAnswer(answer=answer, extra=extra)
|
|
|
|
| 4 |
from langchain.memory import ConversationBufferMemory
|
| 5 |
from langchain.schema import BaseRetriever
|
| 6 |
from pydantic import BaseModel, Field
|
| 7 |
+
from pydantic_redis import Model, Store
|
| 8 |
|
| 9 |
from edu_assistant.learning_tasks.base import BaseTask
|
| 10 |
from edu_assistant.utils.langchain_utils import load_llm
|
| 11 |
+
from edu_assistant.utils.redis_utils import get_redis_config
|
| 12 |
|
| 13 |
TEMPLATE = """The following is a friendly conversation between a human and an ai.
|
| 14 |
The ai is talkative and provides lots of specific details from its context.
|
|
|
|
| 40 |
DEFAULT_FIRST_QUESTION = "请问这段代码中有什么问题吗?"
|
| 41 |
|
| 42 |
|
| 43 |
+
class CodingProblem(Model):
|
| 44 |
+
_primary_key_field: str = "title"
|
| 45 |
+
title: str = Field()
|
| 46 |
question: str = Field()
|
| 47 |
standard_answer: str = Field(default="")
|
| 48 |
analysis: str = Field(default="")
|
| 49 |
+
language: str = Field(default="")
|
| 50 |
+
extra: str = Field(default_factory=lambda: list())
|
| 51 |
|
| 52 |
# TODO: Add cache to expr function with pydantic 2 computed_field decorator.
|
| 53 |
# Wait for langchain to support pydantic2.
|
| 54 |
|
| 55 |
+
@staticmethod
|
| 56 |
+
def enable_redis_orm():
|
| 57 |
+
store = Store(name="coding_problems", redis_config=get_redis_config(), life_span_in_seconds=3600 * 24 * 30)
|
| 58 |
+
|
| 59 |
+
store.register_model(CodingProblem)
|
| 60 |
+
|
| 61 |
def expr(self, lang=""):
|
| 62 |
+
expr = f"## Question\n\n```\n{self.question}\n```\n\n"
|
| 63 |
expr += (
|
| 64 |
+
f"## Standard Answer (There might be others)\n\n```{lang}\n{self.standard_answer}\n```\n\n"
|
| 65 |
if self.standard_answer
|
| 66 |
else ""
|
| 67 |
)
|
| 68 |
+
expr += f"## Analysis\n\n```\n{self.analysis}\n```\n\n" if self.analysis else ""
|
| 69 |
+
expr += "## Extra\n\n" + "".join(self.extra) + "\n"
|
| 70 |
return expr
|
| 71 |
|
| 72 |
def __str__(self):
|
|
|
|
| 95 |
self._session_store = {}
|
| 96 |
self._knowledge = knowledge
|
| 97 |
|
| 98 |
+
@staticmethod
|
| 99 |
+
def build_coding_problem(question: str, standard_answer: str = "", analysis: str = "", extra: list[str] = None):
|
| 100 |
+
extra = [] if extra is None else extra
|
| 101 |
+
return CodingProblem(question=question, standard_answer=standard_answer, analysis=analysis, extra=extra)
|
| 102 |
+
|
| 103 |
+
@staticmethod
|
| 104 |
+
def build_coding_answer(answer: str, extra: list[str] = None):
|
| 105 |
+
extra = [] if extra is None else extra
|
| 106 |
+
return CodingAnswer(answer=answer, extra=extra)
|
| 107 |
+
|
| 108 |
def start_analysis(self, problem: CodingProblem, answer: CodingAnswer, first_question: str = None) -> dict:
|
| 109 |
"""start analysis of a coding problem and incorrect answer.
|
| 110 |
|
|
|
|
| 137 |
"""
|
| 138 |
assert question
|
| 139 |
|
| 140 |
+
if session_id not in self._session_store:
|
| 141 |
+
return {}
|
| 142 |
+
|
| 143 |
chain = self._session_store[session_id]
|
| 144 |
|
| 145 |
result = chain({"input": question})
|
|
|
|
| 153 |
memory = ConversationBufferMemory()
|
| 154 |
prompt = PromptTemplate.from_template(
|
| 155 |
TEMPLATE.format(
|
| 156 |
+
instruction=self.instruction,
|
| 157 |
+
problem=problem.expr(lang=problem.language or self.lang or ""),
|
| 158 |
+
answer=answer.expr(lang=problem.language or self.lang or ""),
|
| 159 |
)
|
| 160 |
)
|
| 161 |
|
|
|
|
| 174 |
return_source_documents=True,
|
| 175 |
combine_docs_chain_kwargs={"prompt": prompt},
|
| 176 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
edu_assistant/utils/redis_utils.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from functools import lru_cache
|
| 3 |
+
|
| 4 |
+
from pydantic_redis import RedisConfig
|
| 5 |
+
from redis import Redis
|
| 6 |
+
|
| 7 |
+
host = os.environ.get("REDIS_HOST", "localhost")
|
| 8 |
+
port = int(os.environ.get("REDIS_PORT", 6379))
|
| 9 |
+
password = os.environ.get("REDIS_PASSWORD", None)
|
| 10 |
+
ssl = True if os.environ.get("REDIS_SSL") else False
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
@lru_cache(maxsize=1)
|
| 14 |
+
def get_redis_client():
|
| 15 |
+
return Redis(host=host, port=port, password=password, ssl=ssl)
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
@lru_cache(maxsize=1)
|
| 19 |
+
def get_redis_config():
|
| 20 |
+
return RedisConfig(host=host, port=port, password=password, ssl=ssl)
|
examples/coding_problem.py
CHANGED
|
@@ -4,9 +4,11 @@ from edu_assistant.learning_tasks import CodingProblemAnalysis
|
|
| 4 |
|
| 5 |
# 创建一个CodingProblem实例
|
| 6 |
problem = CodingProblemAnalysis.build_coding_problem(
|
|
|
|
| 7 |
question="请编写一个函数,该函数接收一个整数列表,并返回该列表的最大值。",
|
| 8 |
standard_answer="def find_max(lst):\n\treturn max(lst)",
|
| 9 |
analysis="在这个问题中,我们需要使用Python的内置函数max来找到列表的最大值。",
|
|
|
|
| 10 |
)
|
| 11 |
|
| 12 |
# 创建一个CodingAnswer实例
|
|
|
|
| 4 |
|
| 5 |
# 创建一个CodingProblem实例
|
| 6 |
problem = CodingProblemAnalysis.build_coding_problem(
|
| 7 |
+
title="求解最大值",
|
| 8 |
question="请编写一个函数,该函数接收一个整数列表,并返回该列表的最大值。",
|
| 9 |
standard_answer="def find_max(lst):\n\treturn max(lst)",
|
| 10 |
analysis="在这个问题中,我们需要使用Python的内置函数max来找到列表的最大值。",
|
| 11 |
+
language="python",
|
| 12 |
)
|
| 13 |
|
| 14 |
# 创建一个CodingAnswer实例
|
examples/coding_problem_orm.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
|
| 3 |
+
from edu_assistant.learning_tasks.coding_problem import CodingProblem
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def load_problems(file_path: str):
|
| 7 |
+
"""load problems from json file and insert into redis orm.
|
| 8 |
+
|
| 9 |
+
Args:
|
| 10 |
+
file_path (str): file path to json file.
|
| 11 |
+
"""
|
| 12 |
+
with open(file_path, "r", encoding="utf-8") as f:
|
| 13 |
+
problems_data = json.load(f)
|
| 14 |
+
|
| 15 |
+
CodingProblem.enable_redis_orm()
|
| 16 |
+
problems = [CodingProblem.parse_obj(problem_data) for problem_data in problems_data]
|
| 17 |
+
CodingProblem.insert(problems)
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
if __name__ == "__main__":
|
| 21 |
+
load_problems("examples/coding_problems.json")
|
examples/coding_problems.json
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"title": "Find Max Number",
|
| 4 |
+
"question": "请编写一个函数,该函数接收一个整数列表,并返回该列表的最大值",
|
| 5 |
+
"standard_answer": "def find_max(lst):\n\treturn max(lst)",
|
| 6 |
+
"analysis": "在这个问题中,我们需要使用Python的内置函数max来找到列表的最大值。",
|
| 7 |
+
"language": "python"
|
| 8 |
+
}
|
| 9 |
+
]
|
poetry.lock
CHANGED
|
The diff for this file is too large to render.
See raw diff
|
|
|
pyproject.toml
CHANGED
|
@@ -18,6 +18,8 @@ uvicorn = "^0.21.1"
|
|
| 18 |
qdrant-client = "^1.3.1"
|
| 19 |
tiktoken = "^0.4.0"
|
| 20 |
gradio = "^3.37.0"
|
|
|
|
|
|
|
| 21 |
|
| 22 |
[tool.poetry.group.dev]
|
| 23 |
optional = true
|
|
|
|
| 18 |
qdrant-client = "^1.3.1"
|
| 19 |
tiktoken = "^0.4.0"
|
| 20 |
gradio = "^3.37.0"
|
| 21 |
+
redis = "^4.6.0"
|
| 22 |
+
pydantic-redis = "^0.4.3"
|
| 23 |
|
| 24 |
[tool.poetry.group.dev]
|
| 25 |
optional = true
|
webui/__init__.py
ADDED
|
File without changes
|
webui/coding_problem.py
CHANGED
|
@@ -1,4 +1,197 @@
|
|
|
|
|
|
|
|
| 1 |
import gradio as gr
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
with gr.Blocks() as coding_problem_ui:
|
| 4 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
|
| 3 |
import gradio as gr
|
| 4 |
+
from fastapi.encoders import jsonable_encoder
|
| 5 |
+
from langchain.callbacks import get_openai_callback
|
| 6 |
+
|
| 7 |
+
from edu_assistant.learning_tasks.coding_problem import (
|
| 8 |
+
DEFAULT_FIRST_QUESTION,
|
| 9 |
+
CodingProblem,
|
| 10 |
+
CodingProblemAnalysis,
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
CodingProblem.enable_redis_orm()
|
| 14 |
+
task = CodingProblemAnalysis()
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def get_problems() -> list[str]:
|
| 18 |
+
data = CodingProblem.select(columns=["title"])
|
| 19 |
+
if not data:
|
| 20 |
+
return []
|
| 21 |
+
titles = [problem_data["title"] for problem_data in data]
|
| 22 |
+
return titles
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def update_problems():
|
| 26 |
+
titles = get_problems()
|
| 27 |
+
gr.Info("更新题目列表成功")
|
| 28 |
+
return gr.Dropdown.update(choices=titles)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def select_problem(title: str):
|
| 32 |
+
problem: CodingProblem = CodingProblem.select(ids=[title])[0]
|
| 33 |
+
return (
|
| 34 |
+
problem.expr(),
|
| 35 |
+
problem.title,
|
| 36 |
+
problem.language,
|
| 37 |
+
problem.question,
|
| 38 |
+
problem.analysis,
|
| 39 |
+
problem.standard_answer,
|
| 40 |
+
json.dumps(problem.extra, ensure_ascii=False, indent=4),
|
| 41 |
+
)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def update_problem(title, language, problem, analysis, answer, extra):
|
| 45 |
+
# TODO: add language
|
| 46 |
+
try:
|
| 47 |
+
extra_data = json.loads(extra)
|
| 48 |
+
except json.JSONDecodeError:
|
| 49 |
+
extra_data = [extra]
|
| 50 |
+
|
| 51 |
+
CodingProblem.update(
|
| 52 |
+
title,
|
| 53 |
+
data={
|
| 54 |
+
"title": title,
|
| 55 |
+
"language": language,
|
| 56 |
+
"question": problem,
|
| 57 |
+
"analysis": analysis,
|
| 58 |
+
"standard_answer": answer,
|
| 59 |
+
"extra": extra_data,
|
| 60 |
+
},
|
| 61 |
+
)
|
| 62 |
+
gr.Info("更新题目成功")
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def delete_problem(title):
|
| 66 |
+
CodingProblem.delete(ids=[title])
|
| 67 |
+
gr.Info("删除题目成功")
|
| 68 |
+
|
| 69 |
+
return "", "", "", "", "", "", "", ""
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def analysis_problem(title, code, extra: str = ""):
|
| 73 |
+
problem = CodingProblem.select(ids=[title])[0]
|
| 74 |
+
answer = CodingProblemAnalysis.build_coding_answer(answer=code)
|
| 75 |
+
|
| 76 |
+
with get_openai_callback() as cb:
|
| 77 |
+
result = task.start_analysis(problem, answer)
|
| 78 |
+
status = {"tokens": cb.total_tokens, "cost": f"${cb.total_cost:.4f}"}
|
| 79 |
+
|
| 80 |
+
answer = result["response"]
|
| 81 |
+
session_id = result["session_id"]
|
| 82 |
+
docs = jsonable_encoder(result.get("source_documents", []))
|
| 83 |
+
return [(DEFAULT_FIRST_QUESTION, answer)], session_id, status, docs
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def chat(message, chat_history, session_id):
|
| 87 |
+
if not session_id:
|
| 88 |
+
return "", "", {"tokens": 0}, []
|
| 89 |
+
with get_openai_callback() as cb:
|
| 90 |
+
result = task.ask(message, session_id=session_id)
|
| 91 |
+
if not result:
|
| 92 |
+
raise gr.Error("Session expired. Please recreate a new problem analysis session.")
|
| 93 |
+
|
| 94 |
+
session_id = result["session_id"]
|
| 95 |
+
docs = jsonable_encoder(result.get("source_documents", []))
|
| 96 |
+
|
| 97 |
+
bot_message = result["response"]
|
| 98 |
+
chat_history.append((message, bot_message))
|
| 99 |
+
|
| 100 |
+
status = {"tokens": cb.total_tokens, "cost": f"${cb.total_cost:.4f}"}
|
| 101 |
+
|
| 102 |
+
return "", chat_history, status, docs
|
| 103 |
+
|
| 104 |
|
| 105 |
with gr.Blocks() as coding_problem_ui:
|
| 106 |
+
with gr.Row():
|
| 107 |
+
with gr.Column(scale=6):
|
| 108 |
+
problem_selector = gr.Dropdown(choices=get_problems(), show_label=False, interactive=True)
|
| 109 |
+
with gr.Column():
|
| 110 |
+
refresh_btn = gr.Button(value="刷新")
|
| 111 |
+
with gr.Row():
|
| 112 |
+
with gr.Column(scale=6):
|
| 113 |
+
with gr.Tab(label="错误代码分析"):
|
| 114 |
+
with gr.Row():
|
| 115 |
+
with gr.Column(scale=3):
|
| 116 |
+
with gr.Row():
|
| 117 |
+
problem_view = gr.Markdown(label="题目")
|
| 118 |
+
with gr.Row():
|
| 119 |
+
code_view = gr.Textbox(label="代码", lines=10, interactive=True)
|
| 120 |
+
with gr.Column(scale=3):
|
| 121 |
+
with gr.Row():
|
| 122 |
+
chat_box = gr.Chatbot(height=500)
|
| 123 |
+
with gr.Row():
|
| 124 |
+
chat_input = gr.Textbox(interactive=True)
|
| 125 |
+
with gr.Column():
|
| 126 |
+
with gr.Row():
|
| 127 |
+
analysis_btn = gr.Button(value="分析")
|
| 128 |
+
with gr.Row():
|
| 129 |
+
status = gr.JSON(value="""{"tokens":0}""")
|
| 130 |
+
with gr.Row():
|
| 131 |
+
session_id = gr.Textbox(label="Session", interactive=False, value="")
|
| 132 |
+
with gr.Row():
|
| 133 |
+
docs = gr.JSON(value="""["docs"]""", label="Docs")
|
| 134 |
+
with gr.Row():
|
| 135 |
+
clear = gr.ClearButton([problem_view, code_view, problem_selector, session_id, docs])
|
| 136 |
+
|
| 137 |
+
with gr.Tab(label="题库管理"):
|
| 138 |
+
with gr.Row():
|
| 139 |
+
with gr.Column(scale=6):
|
| 140 |
+
with gr.Row():
|
| 141 |
+
title_edit = gr.Textbox(label="标题", interactive=True)
|
| 142 |
+
with gr.Row():
|
| 143 |
+
language_edit = gr.Dropdown(
|
| 144 |
+
choices=["python", "cpp", "java"],
|
| 145 |
+
label="���言",
|
| 146 |
+
interactive=True,
|
| 147 |
+
allow_custom_value=True,
|
| 148 |
+
)
|
| 149 |
+
with gr.Column():
|
| 150 |
+
manage_update = gr.Button(value="更新")
|
| 151 |
+
manage_delete = gr.Button(value="删除", variant="stop")
|
| 152 |
+
with gr.Row():
|
| 153 |
+
with gr.Column():
|
| 154 |
+
problem_edit = gr.Textbox(label="题目", lines=10, max_lines=100, interactive=True)
|
| 155 |
+
with gr.Column():
|
| 156 |
+
analysis_edit = gr.Textbox(label="解析", lines=10, max_lines=100, interactive=True)
|
| 157 |
+
with gr.Row():
|
| 158 |
+
answer_edit = gr.Textbox(label="标准答案", lines=10, max_lines=100, interactive=True)
|
| 159 |
+
with gr.Row():
|
| 160 |
+
extra_edit = gr.Textbox(label="额外信息", lines=10, max_lines=100, interactive=True)
|
| 161 |
+
|
| 162 |
+
refresh_btn.click(update_problems, [], [problem_selector])
|
| 163 |
+
problem_selector.select(
|
| 164 |
+
select_problem,
|
| 165 |
+
[
|
| 166 |
+
problem_selector,
|
| 167 |
+
],
|
| 168 |
+
[problem_view, title_edit, language_edit, problem_edit, analysis_edit, answer_edit, extra_edit],
|
| 169 |
+
)
|
| 170 |
+
analysis_btn.click(
|
| 171 |
+
analysis_problem,
|
| 172 |
+
[problem_selector, code_view],
|
| 173 |
+
[chat_box, session_id, status, docs],
|
| 174 |
+
)
|
| 175 |
+
chat_input.submit(chat, [chat_input, chat_box, session_id], [chat_input, chat_box, status, docs])
|
| 176 |
+
|
| 177 |
+
manage_update.click(
|
| 178 |
+
update_problem, [title_edit, language_edit, problem_edit, analysis_edit, answer_edit, extra_edit], []
|
| 179 |
+
)
|
| 180 |
+
manage_delete.click(
|
| 181 |
+
delete_problem,
|
| 182 |
+
[problem_selector],
|
| 183 |
+
[
|
| 184 |
+
problem_selector,
|
| 185 |
+
problem_view,
|
| 186 |
+
title_edit,
|
| 187 |
+
language_edit,
|
| 188 |
+
problem_edit,
|
| 189 |
+
analysis_edit,
|
| 190 |
+
answer_edit,
|
| 191 |
+
extra_edit,
|
| 192 |
+
],
|
| 193 |
+
)
|
| 194 |
+
|
| 195 |
+
if __name__ == "__main__":
|
| 196 |
+
coding_problem_ui.queue()
|
| 197 |
+
coding_problem_ui.launch(max_threads=2)
|
webui/qa.py
CHANGED
|
@@ -38,7 +38,7 @@ def recreate(instruction):
|
|
| 38 |
|
| 39 |
|
| 40 |
def clear(msg, chatbot, session_id, telemetry, docs):
|
| 41 |
-
return "", "",
|
| 42 |
|
| 43 |
|
| 44 |
with gr.Blocks() as qa_ui:
|
|
@@ -52,7 +52,7 @@ with gr.Blocks() as qa_ui:
|
|
| 52 |
with gr.Row():
|
| 53 |
with gr.Column(scale=6):
|
| 54 |
with gr.Row():
|
| 55 |
-
chatbot = gr.Chatbot(height=
|
| 56 |
with gr.Row():
|
| 57 |
msg = gr.Textbox()
|
| 58 |
with gr.Column(scale=1):
|
|
@@ -62,7 +62,8 @@ with gr.Blocks() as qa_ui:
|
|
| 62 |
telemetry = gr.JSON(value="""{"tokens":0}""", label="Telemetry")
|
| 63 |
with gr.Row():
|
| 64 |
docs = gr.JSON(value="""["docs"]""", label="Docs")
|
|
|
|
|
|
|
| 65 |
|
| 66 |
clear_btn.click(clear, [msg, chatbot, session_id, telemetry, docs], [msg, chatbot, session_id, telemetry, docs])
|
| 67 |
msg.submit(respond, [msg, chatbot, session_id], [msg, chatbot, session_id, telemetry, docs])
|
| 68 |
-
apply.click(recreate, [instruction], [])
|
|
|
|
| 38 |
|
| 39 |
|
| 40 |
def clear(msg, chatbot, session_id, telemetry, docs):
|
| 41 |
+
return "", "", "", '{"tokens":0}', '["docs"]'
|
| 42 |
|
| 43 |
|
| 44 |
with gr.Blocks() as qa_ui:
|
|
|
|
| 52 |
with gr.Row():
|
| 53 |
with gr.Column(scale=6):
|
| 54 |
with gr.Row():
|
| 55 |
+
chatbot = gr.Chatbot(height=500)
|
| 56 |
with gr.Row():
|
| 57 |
msg = gr.Textbox()
|
| 58 |
with gr.Column(scale=1):
|
|
|
|
| 62 |
telemetry = gr.JSON(value="""{"tokens":0}""", label="Telemetry")
|
| 63 |
with gr.Row():
|
| 64 |
docs = gr.JSON(value="""["docs"]""", label="Docs")
|
| 65 |
+
with gr.Row():
|
| 66 |
+
instruction = gr.Textbox(label="Instruction", value=DEFAULT_INSTRUCTION, interactive=False)
|
| 67 |
|
| 68 |
clear_btn.click(clear, [msg, chatbot, session_id, telemetry, docs], [msg, chatbot, session_id, telemetry, docs])
|
| 69 |
msg.submit(respond, [msg, chatbot, session_id], [msg, chatbot, session_id, telemetry, docs])
|
|
|
webui/ui.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
-
from coding_problem import coding_problem_ui
|
| 3 |
-
from qa import qa_ui
|
| 4 |
|
| 5 |
from edu_assistant import version
|
|
|
|
|
|
|
| 6 |
|
| 7 |
with gr.Blocks() as ui:
|
| 8 |
with gr.Row():
|
|
@@ -15,4 +15,5 @@ with gr.Blocks() as ui:
|
|
| 15 |
coding_problem_ui.render()
|
| 16 |
|
| 17 |
if __name__ == "__main__":
|
|
|
|
| 18 |
ui.launch()
|
|
|
|
| 1 |
import gradio as gr
|
|
|
|
|
|
|
| 2 |
|
| 3 |
from edu_assistant import version
|
| 4 |
+
from webui.coding_problem import coding_problem_ui
|
| 5 |
+
from webui.qa import qa_ui
|
| 6 |
|
| 7 |
with gr.Blocks() as ui:
|
| 8 |
with gr.Row():
|
|
|
|
| 15 |
coding_problem_ui.render()
|
| 16 |
|
| 17 |
if __name__ == "__main__":
|
| 18 |
+
ui.queue()
|
| 19 |
ui.launch()
|