Arcadia822 commited on
Commit
532a759
·
unverified ·
1 Parent(s): 29e839a

feat: :sparkles: qa gradio ui and coding problem chain (#11)

Browse files
README.md CHANGED
@@ -18,9 +18,12 @@
18
  | AZURE_OPENAI_API_BASE | No | | azure openai api base |
19
  | AZURE_OPENAI_DEPLOYMENT_ID | No | | azure openai deployment id for gpt 3.5 |
20
  | AZURE_OPENAI_EMBEDDING_DEP_ID | No | | azure openai deployment id for embedding |
 
 
 
21
 
22
  ## setup
23
 
24
  - install python 3.10+
25
  - install poetry 1.5.1+
26
- - run: `poetry install`
 
18
  | AZURE_OPENAI_API_BASE | No | | azure openai api base |
19
  | AZURE_OPENAI_DEPLOYMENT_ID | No | | azure openai deployment id for gpt 3.5 |
20
  | AZURE_OPENAI_EMBEDDING_DEP_ID | No | | azure openai deployment id for embedding |
21
+ | QDRANT_API | No | | use qdrant api if not blank |
22
+ | QDRANT_API_KEY | No | | qdrant api key |
23
+ | QDRANT_API_BASE | No | | qdrant api base |
24
 
25
  ## setup
26
 
27
  - install python 3.10+
28
  - install poetry 1.5.1+
29
+ - run: `poetry install --with dev`
edu_assistant/learning_cases/__init__.py DELETED
File without changes
edu_assistant/learning_cases/base.py DELETED
File without changes
edu_assistant/learning_cases/context.py DELETED
File without changes
edu_assistant/learning_tasks/qa.py CHANGED
@@ -19,11 +19,29 @@ The ai act following below instructions:
19
  {instruction}
20
  ---
21
 
 
22
  Current conversation:
23
- {{history}}
24
  Human: {{input}}
25
  AI:"""
26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  TEMPLATE_ONCE = """The following is a friendly conversation between a human and an ai.
28
  The ai is talkative and provides lots of specific details from its context.
29
  If the ai does not know the answer to a question, it truthfully says it does not know.
@@ -35,12 +53,29 @@ The ai act following below instructions:
35
  {{input}}
36
  """
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
  class QaTask(BaseTask):
40
  _session_store: dict
41
  _knowledge: BaseRetriever | None
42
  _qa_once: Chain
43
 
 
 
44
  def __init__(self, instruction: str = "", knowledge: BaseRetriever = None):
45
  """Create a new QaTask service.
46
 
@@ -53,8 +88,16 @@ class QaTask(BaseTask):
53
  If not set, will use internal memory to store chat history. Which will be lost after restart and might
54
  cost huge memory.
55
  """
56
- self._chat_prompt = PromptTemplate.from_template(TEMPLATE_CHAT.format(instruction=instruction))
57
- self._once_prompt = PromptTemplate.from_template(TEMPLATE_ONCE.format(instruction=instruction))
 
 
 
 
 
 
 
 
58
 
59
  self._session_store = {}
60
  self._knowledge = knowledge
@@ -66,7 +109,6 @@ class QaTask(BaseTask):
66
  return LLMChain(
67
  llm=load_llm(),
68
  prompt=self._once_prompt,
69
- memory=ConversationBufferMemory(),
70
  )
71
  else:
72
  return RetrievalQA.from_llm(
@@ -80,7 +122,7 @@ class QaTask(BaseTask):
80
  if not self._knowledge:
81
  return ConversationChain(
82
  llm=load_llm(),
83
- memory=ConversationBufferMemory(),
84
  prompt=self._chat_prompt,
85
  )
86
  else:
@@ -89,8 +131,8 @@ class QaTask(BaseTask):
89
  retriever=self._knowledge,
90
  condense_question_llm=load_llm(),
91
  return_source_documents=True,
92
- combine_docs_chain_kwargs={"prompt": PromptTemplate.from_template(self._chat_prompt)},
93
- memory=ConversationBufferMemory(),
94
  )
95
 
96
  def ask(
@@ -117,14 +159,14 @@ class QaTask(BaseTask):
117
  """
118
 
119
  if session:
120
- args = {"input": question}
121
  if session_id and session_id in self._session_store:
122
  chain = self._session_store[session_id]
123
  else:
124
  session_id = self._create_session_id()
125
  chain = self._create_session_chain(session_id)
126
  else:
127
- args = {"input": question, "history": ""}
128
  chain = self._qa_once
129
 
130
  if session_mem:
 
19
  {instruction}
20
  ---
21
 
22
+
23
  Current conversation:
24
+ {{chat_history}}
25
  Human: {{input}}
26
  AI:"""
27
 
28
+ TEMPLATE_CHAT_CONTEXT = """The following is a friendly conversation between a human and an ai.
29
+ The ai is talkative and provides lots of specific details from its context.
30
+ If the ai does not know the answer to a question, it truthfully says it does not know.
31
+ The ai act following below instructions:
32
+ ---
33
+ {instruction}
34
+ ---
35
+ Useful context for you to answer the question:
36
+ ---
37
+ {{context}}
38
+ ---
39
+
40
+ Current conversation:
41
+ {{chat_history}}
42
+ Human: {{question}}
43
+ AI:"""
44
+
45
  TEMPLATE_ONCE = """The following is a friendly conversation between a human and an ai.
46
  The ai is talkative and provides lots of specific details from its context.
47
  If the ai does not know the answer to a question, it truthfully says it does not know.
 
53
  {{input}}
54
  """
55
 
56
+ TEMPLATE_ONCE_CONTEXT = """The following is a friendly conversation between a human and an ai.
57
+ The ai is talkative and provides lots of specific details from its context.
58
+ If the ai does not know the answer to a question, it truthfully says it does not know.
59
+ The ai act following below instructions:
60
+ ---
61
+ {instruction}
62
+ ---
63
+ Useful context for you to answer the question:
64
+ ---
65
+ {{context}}
66
+ ---
67
+
68
+ {{input}}
69
+ """
70
+
71
 
72
  class QaTask(BaseTask):
73
  _session_store: dict
74
  _knowledge: BaseRetriever | None
75
  _qa_once: Chain
76
 
77
+ HISTORY_KEY = "chat_history"
78
+
79
  def __init__(self, instruction: str = "", knowledge: BaseRetriever = None):
80
  """Create a new QaTask service.
81
 
 
88
  If not set, will use internal memory to store chat history. Which will be lost after restart and might
89
  cost huge memory.
90
  """
91
+ if knowledge:
92
+ self._chat_prompt = PromptTemplate.from_template(TEMPLATE_CHAT_CONTEXT.format(instruction=instruction))
93
+ self._once_prompt = PromptTemplate.from_template(TEMPLATE_ONCE_CONTEXT.format(instruction=instruction))
94
+ self._input_key = "question"
95
+ self._output_key = "answer"
96
+ else:
97
+ self._chat_prompt = PromptTemplate.from_template(TEMPLATE_CHAT.format(instruction=instruction))
98
+ self._once_prompt = PromptTemplate.from_template(TEMPLATE_ONCE.format(instruction=instruction))
99
+ self._input_key = "input"
100
+ self._output_key = "response"
101
 
102
  self._session_store = {}
103
  self._knowledge = knowledge
 
109
  return LLMChain(
110
  llm=load_llm(),
111
  prompt=self._once_prompt,
 
112
  )
113
  else:
114
  return RetrievalQA.from_llm(
 
122
  if not self._knowledge:
123
  return ConversationChain(
124
  llm=load_llm(),
125
+ memory=ConversationBufferMemory(memory_key=QaTask.HISTORY_KEY, output_key=self._output_key),
126
  prompt=self._chat_prompt,
127
  )
128
  else:
 
131
  retriever=self._knowledge,
132
  condense_question_llm=load_llm(),
133
  return_source_documents=True,
134
+ combine_docs_chain_kwargs={"prompt": self._chat_prompt},
135
+ memory=ConversationBufferMemory(memory_key=QaTask.HISTORY_KEY, output_key=self._output_key),
136
  )
137
 
138
  def ask(
 
159
  """
160
 
161
  if session:
162
+ args = {self._input_key: question}
163
  if session_id and session_id in self._session_store:
164
  chain = self._session_store[session_id]
165
  else:
166
  session_id = self._create_session_id()
167
  chain = self._create_session_chain(session_id)
168
  else:
169
+ args = {self._input_key: question, QaTask.HISTORY_KEY: ""}
170
  chain = self._qa_once
171
 
172
  if session_mem:
edu_assistant/utils/langchain_utils.py CHANGED
@@ -3,7 +3,10 @@ from functools import lru_cache
3
 
4
  from langchain.chat_models import AzureChatOpenAI, ChatOpenAI
5
  from langchain.chat_models.base import BaseChatModel
6
- from langchain.memory.chat_memory import BaseChatMemory
 
 
 
7
 
8
 
9
  @lru_cache(maxsize=1)
@@ -28,6 +31,7 @@ def load_llm() -> BaseChatModel:
28
  return llm
29
 
30
 
 
31
  def load_gpt4_llm() -> BaseChatModel:
32
  llm = ChatOpenAI(
33
  openai_api_key=os.environ.get("OPENAI_API_KEY"),
@@ -38,13 +42,32 @@ def load_gpt4_llm() -> BaseChatModel:
38
  return llm
39
 
40
 
41
- def update_chat_memory(memory: BaseChatMemory, user_text: str, ai_text: str):
42
- """update langchain memory
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
- Args:
45
- memory (BaseChatMemory): chat history memory
46
- question (str): qa question str
47
- result (str): chain answer result
48
- """
49
- memory.chat_memory.add_user_message(user_text)
50
- memory.chat_memory.add_ai_message(ai_text)
 
3
 
4
  from langchain.chat_models import AzureChatOpenAI, ChatOpenAI
5
  from langchain.chat_models.base import BaseChatModel
6
+ from langchain.embeddings import OpenAIEmbeddings
7
+ from langchain.vectorstores import Qdrant, VectorStore
8
+
9
+ from edu_assistant.utils.qdrant_utils import load_qdrant_client
10
 
11
 
12
  @lru_cache(maxsize=1)
 
31
  return llm
32
 
33
 
34
+ @lru_cache(maxsize=1)
35
  def load_gpt4_llm() -> BaseChatModel:
36
  llm = ChatOpenAI(
37
  openai_api_key=os.environ.get("OPENAI_API_KEY"),
 
42
  return llm
43
 
44
 
45
+ @lru_cache(maxsize=1)
46
+ def load_embeddings():
47
+ if os.environ.get("AZURE_OPENAI"):
48
+ embeddings = OpenAIEmbeddings(
49
+ openai_api_type="azure",
50
+ openai_api_key=os.environ.get("AZURE_OPENAI_API_KEY"),
51
+ openai_api_base=os.environ.get("AZURE_OPENAI_API_BASE"),
52
+ openai_api_version="2023-05-15",
53
+ deployment=os.environ.get("AZURE_OPENAI_EMBEDDING_DEP_ID", ""),
54
+ )
55
+ else:
56
+ embeddings = OpenAIEmbeddings(
57
+ openai_api_key=os.environ.get("OPENAI_API_KEY"),
58
+ openai_proxy=os.environ.get("OPENAI_PROXY", ""),
59
+ )
60
+
61
+ return embeddings
62
+
63
+
64
+ @lru_cache(maxsize=10)
65
+ def load_vectorstore(collection_name: str = "default") -> VectorStore:
66
+ if os.environ.get("QDRANT_API"):
67
+ client = load_qdrant_client()
68
+ embeddings = load_embeddings()
69
+ doc_store = Qdrant(client=client, collection_name=collection_name, embeddings=embeddings)
70
+
71
+ return doc_store
72
 
73
+ return None
 
 
 
 
 
 
edu_assistant/utils/qdrant_utils.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from functools import lru_cache
3
+
4
+ from qdrant_client import QdrantClient
5
+
6
+
7
+ @lru_cache(maxsize=1)
8
+ def load_qdrant_client():
9
+ qdrant_url = os.environ.get("QDRANT_API_BASE", "")
10
+ qdrant_apikey = os.environ.get("QDRANT_API_KEY", "")
11
+ client = QdrantClient(url=qdrant_url, api_key=qdrant_apikey)
12
+
13
+ return client
examples/docs/1. cpp_intro.txt ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ What is C++?
2
+ C++ is a cross-platform language that can be used to create high-performance applications.
3
+
4
+ C++ was developed by Bjarne Stroustrup, as an extension to the C language.
5
+
6
+ C++ gives programmers a high level of control over system resources and memory.
7
+
8
+ The language was updated 4 major times in 2011, 2014, 2017, and 2020 to C++11, C++14, C++17, C++20.
9
+
10
+ Why Use C++
11
+ C++ is one of the world's most popular programming languages.
12
+
13
+ C++ can be found in today's operating systems, Graphical User Interfaces, and embedded systems.
14
+
15
+ C++ is an object-oriented programming language which gives a clear structure to programs and allows code to be reused, lowering development costs.
16
+
17
+ C++ is portable and can be used to develop applications that can be adapted to multiple platforms.
18
+
19
+ C++ is fun and easy to learn!
20
+
21
+ As C++ is close to C, C# and Java, it makes it easy for programmers to switch to C++ or vice versa.
22
+
23
+ Difference between C and C++
24
+ C++ was developed as an extension of C, and both languages have almost the same syntax.
25
+
26
+ The main difference between C and C++ is that C++ support classes and objects, while C does not.
examples/docs/2. cpp_getting_started.txt ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ C++ Get Started
2
+ To start using C++, you need two things:
3
+
4
+ A text editor, like Notepad, to write C++ code
5
+ A compiler, like GCC, to translate the C++ code into a language that the computer will understand
6
+ There are many text editors and compilers to choose from. In this tutorial, we will use an IDE (see below).
7
+
8
+ C++ Install IDE
9
+ An IDE (Integrated Development Environment) is used to edit AND compile the code.
10
+
11
+ Popular IDE's include Code::Blocks, Eclipse, and Visual Studio. These are all free, and they can be used to both edit and debug C++ code.
12
+
13
+ Note: Web-based IDE's can work as well, but functionality is limited.
14
+
15
+ We will use Code::Blocks in our tutorial, which we believe is a good place to start.
16
+
17
+ You can find the latest version of Codeblocks at http://www.codeblocks.org/. Download the mingw-setup.exe file, which will install the text editor with a compiler.
18
+
19
+ C++ Quickstart
20
+ Let's create our first C++ file.
21
+
22
+ Open Codeblocks and go to File > New > Empty File.
23
+
24
+ Write the following C++ code and save the file as myfirstprogram.cpp (File > Save File as):
25
+
26
+ myfirstprogram.cpp
27
+ #include <iostream>
28
+ using namespace std;
29
+
30
+ int main() {
31
+ cout << "Hello World!";
32
+ return 0;
33
+ }
34
+ Don't worry if you don't understand the code above - we will discuss it in detail in later chapters. For now, focus on how to run the code.
35
+
36
+ In Codeblocks, it should look like this:
37
+
38
+
39
+ Then, go to Build > Build and Run to run (execute) the program. The result will look something to this:
40
+
41
+ Hello World!
42
+ Process returned 0 (0x0) execution time : 0.011 s
43
+ Press any key to continue.
44
+ Congratulations! You have now written and executed your first C++ program.
45
+
46
+ Learning C++ At W3Schools
47
+ When learning C++ at W3Schools.com, you can use our "Try it Yourself" tool, which shows both the code and the result. This will make it easier for you to understand every part as we move forward:
48
+
49
+ myfirstprogram.cpp
50
+ Code:
51
+
52
+ #include <iostream>
53
+ using namespace std;
54
+
55
+ int main() {
56
+ cout << "Hello World!";
57
+ return 0;
58
+ }
59
+ Result:
60
+
61
+ Hello World!
examples/qa.py CHANGED
@@ -1,9 +1,10 @@
1
  import json
 
2
 
3
  from edu_assistant.learning_tasks import QaTask
4
  from edu_assistant.utils.common_utils import init_local_logging
5
 
6
- init_local_logging()
7
 
8
 
9
  instruction = """
 
1
  import json
2
+ import logging
3
 
4
  from edu_assistant.learning_tasks import QaTask
5
  from edu_assistant.utils.common_utils import init_local_logging
6
 
7
+ init_local_logging(logging.DEBUG)
8
 
9
 
10
  instruction = """
examples/qdrant_vs.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import logging
3
+
4
+ from fastapi.encoders import jsonable_encoder
5
+ from langchain.chains import RetrievalQA
6
+ from langchain.document_loaders import DirectoryLoader, TextLoader
7
+ from langchain.text_splitter import CharacterTextSplitter
8
+ from qdrant_client.http.models import Distance, VectorParams
9
+
10
+ from edu_assistant.utils.common_utils import init_local_logging
11
+ from edu_assistant.utils.langchain_utils import load_llm, load_vectorstore
12
+ from edu_assistant.utils.qdrant_utils import load_qdrant_client
13
+
14
+ init_local_logging(logging.DEBUG)
15
+
16
+
17
+ def create_collection(collection_name: str):
18
+ client = load_qdrant_client()
19
+ client.recreate_collection(
20
+ collection_name=collection_name,
21
+ vectors_config=VectorParams(size=1536, distance=Distance.DOT),
22
+ )
23
+ collection_info = client.get_collection(collection_name=collection_name)
24
+ print(collection_info)
25
+
26
+
27
+ def delete_collection(collection_name: str):
28
+ client = load_qdrant_client()
29
+ client.delete_collection(collection_name=collection_name)
30
+
31
+
32
+ def add_docs(path: str, collection_name: str):
33
+ vs = load_vectorstore(collection_name=collection_name)
34
+ loader = DirectoryLoader(path=path, glob="*.txt", loader_cls=TextLoader)
35
+ documents = loader.load()
36
+ text_splitter = CharacterTextSplitter(chunk_size=500, chunk_overlap=0)
37
+ docs = text_splitter.split_documents(documents)
38
+
39
+ for doc in docs:
40
+ vs.add_documents([doc])
41
+
42
+
43
+ def qa(collection_name: str, question: str):
44
+ chain = RetrievalQA.from_llm(
45
+ llm=load_llm(),
46
+ retriever=load_vectorstore(collection_name=collection_name).as_retriever(),
47
+ return_source_documents=True,
48
+ )
49
+ result = chain(question)
50
+ print(json.dumps(jsonable_encoder(result), ensure_ascii=False, indent=4))
51
+
52
+
53
+ if __name__ == "__main__":
54
+ name = "example"
55
+ path = "examples/docs"
56
+ question = "C和C++的区别是什么?"
57
+
58
+ create_collection(name)
59
+ add_docs(path, name)
60
+ qa(name, question)
61
+ delete_collection(name)
poetry.lock CHANGED
The diff for this file is too large to render. See raw diff
 
pyproject.toml CHANGED
@@ -8,13 +8,16 @@ readme = "README.md"
8
  packages = [{include = "edu_assistant"}]
9
 
10
  [tool.poetry.dependencies]
11
- python = "^3.10"
12
  fastapi = "^0.95.1"
13
  langchain = "^0.0.234"
14
  openai = "^0.27.4"
15
  pydantic = "^1.10.7"
16
  tenacity = "^8.2.2"
17
  uvicorn = "^0.21.1"
 
 
 
18
 
19
  [tool.poetry.group.dev]
20
  optional = true
 
8
  packages = [{include = "edu_assistant"}]
9
 
10
  [tool.poetry.dependencies]
11
+ python = "^3.10 <3.12"
12
  fastapi = "^0.95.1"
13
  langchain = "^0.0.234"
14
  openai = "^0.27.4"
15
  pydantic = "^1.10.7"
16
  tenacity = "^8.2.2"
17
  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
webui/coding_problem.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ with gr.Blocks() as coding_problem_ui:
4
+ pass
webui/qa.py ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from fastapi.encoders import jsonable_encoder
3
+ from langchain.callbacks import get_openai_callback
4
+
5
+ from edu_assistant.learning_tasks.qa import QaTask
6
+ from edu_assistant.utils.langchain_utils import load_vectorstore
7
+
8
+ DEFAULT_INSTRUCTION = """Act as a c++ professional to answer student aged 5-10 questions. Answer properly and politely.
9
+ Don't extend conversation multiple times. Only add one time saying."""
10
+
11
+
12
+ task = QaTask(
13
+ instruction=DEFAULT_INSTRUCTION,
14
+ knowledge=load_vectorstore("example").as_retriever(),
15
+ )
16
+
17
+
18
+ def respond(message, chat_history, session_id):
19
+ with get_openai_callback() as cb:
20
+ if session_id:
21
+ result = task.ask(message, session_id=session_id)
22
+ else:
23
+ result = task.ask(message)
24
+
25
+ session_id = result["session_id"]
26
+ docs = jsonable_encoder(result.get("source_documents", []))
27
+
28
+ bot_message = result["answer"]
29
+ chat_history.append((message, bot_message))
30
+
31
+ status = {"tokens": cb.total_tokens, "cost": f"${cb.total_cost:.4f}"}
32
+ return "", chat_history, session_id, status, docs
33
+
34
+
35
+ def recreate(instruction):
36
+ global task
37
+ task = QaTask(instruction=instruction, knowledge=load_vectorstore("example").as_retriever())
38
+
39
+
40
+ with gr.Blocks() as qa_ui:
41
+ with gr.Row():
42
+ with gr.Column(scale=6):
43
+ with gr.Row():
44
+ chatbot = gr.Chatbot(height=800)
45
+ with gr.Row():
46
+ msg = gr.Textbox()
47
+ with gr.Column(scale=1):
48
+ with gr.Row():
49
+ session_id = gr.Textbox(label="Session", interactive=False, value="")
50
+ with gr.Row():
51
+ telemetry = gr.JSON(value="""{"tokens":0}""", label="Telemetry")
52
+ with gr.Row():
53
+ docs = gr.JSON(value="""["docs"]""", label="Docs")
54
+ with gr.Row():
55
+ clear = gr.ClearButton([msg, chatbot, session_id, telemetry, docs])
56
+ with gr.Row():
57
+ with gr.Column(scale=6):
58
+ instruction = gr.Textbox(label="Instruction", value=DEFAULT_INSTRUCTION, interactive=False)
59
+ with gr.Column(scale=1):
60
+ apply = gr.Button(value="更换Prompt")
61
+
62
+ msg.submit(respond, [msg, chatbot, session_id], [msg, chatbot, session_id, telemetry])
63
+ apply.click(recreate, [instruction], [])
webui/ui.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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():
9
+ gr.Markdown(f" v{version}")
10
+
11
+ with gr.Tab(label="答疑"):
12
+ qa_ui.render()
13
+
14
+ with gr.Tab(label="编程题"):
15
+ coding_problem_ui.render()
16
+
17
+ if __name__ == "__main__":
18
+ ui.launch()