Spaces:
Running
Running
docs: add copilot demo for chatbot
Browse files
layout_templates/chatbot/README-zh_CN.md
CHANGED
@@ -15,3 +15,5 @@
|
|
15 |
<demo name="basic" position="bottom" collapsible="true"></demo>
|
16 |
|
17 |
<demo name="fine_grained_control" title="细粒度控制" position="bottom" collapsible="true"></demo>
|
|
|
|
|
|
15 |
<demo name="basic" position="bottom" collapsible="true"></demo>
|
16 |
|
17 |
<demo name="fine_grained_control" title="细粒度控制" position="bottom" collapsible="true"></demo>
|
18 |
+
|
19 |
+
<demo name="copilot" title="助手式" position="bottom" collapsible="true"></demo>
|
layout_templates/chatbot/README.md
CHANGED
@@ -15,3 +15,5 @@ This template provides the following features:
|
|
15 |
<demo name="basic" position="bottom" collapsible="true"></demo>
|
16 |
|
17 |
<demo name="fine_grained_control" title="Fine-grained Control" position="bottom" collapsible="true"></demo>
|
|
|
|
|
|
15 |
<demo name="basic" position="bottom" collapsible="true"></demo>
|
16 |
|
17 |
<demo name="fine_grained_control" title="Fine-grained Control" position="bottom" collapsible="true"></demo>
|
18 |
+
|
19 |
+
<demo name="copilot" title="Copilot" position="bottom" collapsible="true"></demo>
|
layout_templates/chatbot/demos/copilot.py
ADDED
@@ -0,0 +1,325 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import base64
|
2 |
+
import os
|
3 |
+
import time
|
4 |
+
|
5 |
+
import gradio as gr
|
6 |
+
import modelscope_studio.components.antd as antd
|
7 |
+
import modelscope_studio.components.antdx as antdx
|
8 |
+
import modelscope_studio.components.base as ms
|
9 |
+
import modelscope_studio.components.pro as pro
|
10 |
+
from modelscope_studio.components.pro.chatbot import (ChatbotBotConfig,
|
11 |
+
ChatbotPromptsConfig,
|
12 |
+
ChatbotWelcomeConfig)
|
13 |
+
from modelscope_studio.components.pro.multimodal_input import \
|
14 |
+
MultimodalInputUploadConfig
|
15 |
+
from openai import OpenAI
|
16 |
+
|
17 |
+
client = OpenAI(
|
18 |
+
base_url='https://api-inference.modelscope.cn/v1/',
|
19 |
+
api_key=os.getenv("MODELSCOPE_ACCESS_TOKEN"), # ModelScope Token
|
20 |
+
)
|
21 |
+
|
22 |
+
model = "Qwen/Qwen2.5-72B-Instruct"
|
23 |
+
|
24 |
+
|
25 |
+
def prompt_select(input_value, e: gr.EventData):
|
26 |
+
input_value["text"] = e._data["payload"][0]["value"]["description"]
|
27 |
+
return gr.update(value=input_value)
|
28 |
+
|
29 |
+
|
30 |
+
def clear():
|
31 |
+
return gr.update(value=None)
|
32 |
+
|
33 |
+
|
34 |
+
def cancel(chatbot_value):
|
35 |
+
chatbot_value[-1]["loading"] = False
|
36 |
+
chatbot_value[-1]["status"] = "done"
|
37 |
+
chatbot_value[-1]["footer"] = "Chat completion paused"
|
38 |
+
|
39 |
+
return gr.update(value=chatbot_value), gr.update(loading=False), gr.update(
|
40 |
+
disabled=False)
|
41 |
+
|
42 |
+
|
43 |
+
def retry(chatbot_value, e: gr.EventData):
|
44 |
+
index = e._data["payload"][0]["index"]
|
45 |
+
chatbot_value = chatbot_value[:index]
|
46 |
+
|
47 |
+
yield gr.update(loading=True), gr.update(value=chatbot_value), gr.update(
|
48 |
+
disabled=True)
|
49 |
+
for chunk in submit(None, chatbot_value):
|
50 |
+
yield chunk
|
51 |
+
|
52 |
+
|
53 |
+
def format_history(history):
|
54 |
+
messages = [{"role": "system", "content": "You are a helpful assistant."}]
|
55 |
+
for item in history:
|
56 |
+
if item["role"] == "user":
|
57 |
+
messages.append({
|
58 |
+
"role":
|
59 |
+
"user",
|
60 |
+
"content": [{
|
61 |
+
"type": "text",
|
62 |
+
"text": item["content"]
|
63 |
+
}]
|
64 |
+
})
|
65 |
+
elif item["role"] == "assistant":
|
66 |
+
# ignore thought message
|
67 |
+
messages.append({"role": "assistant", "content": item["content"]})
|
68 |
+
return messages
|
69 |
+
|
70 |
+
|
71 |
+
def submit(input_value, chatbot_value):
|
72 |
+
if input_value is not None:
|
73 |
+
chatbot_value.append({
|
74 |
+
"role": "user",
|
75 |
+
"content": input_value["text"],
|
76 |
+
})
|
77 |
+
history_messages = format_history(chatbot_value)
|
78 |
+
chatbot_value.append({
|
79 |
+
"role": "assistant",
|
80 |
+
"content": "",
|
81 |
+
"loading": True,
|
82 |
+
"status": "pending"
|
83 |
+
})
|
84 |
+
yield {
|
85 |
+
input: gr.update(value=None, loading=True),
|
86 |
+
clear_btn: gr.update(disabled=True),
|
87 |
+
chatbot: gr.update(value=chatbot_value)
|
88 |
+
}
|
89 |
+
|
90 |
+
try:
|
91 |
+
response = client.chat.completions.create(model=model,
|
92 |
+
messages=history_messages,
|
93 |
+
stream=True)
|
94 |
+
start_time = time.time()
|
95 |
+
|
96 |
+
for chunk in response:
|
97 |
+
chatbot_value[-1]["content"] += chunk.choices[0].delta.content
|
98 |
+
chatbot_value[-1]["loading"] = False
|
99 |
+
|
100 |
+
yield {chatbot: gr.update(value=chatbot_value)}
|
101 |
+
|
102 |
+
chatbot_value[-1]["footer"] = "{:.2f}".format(time.time() -
|
103 |
+
start_time) + 's'
|
104 |
+
chatbot_value[-1]["status"] = "done"
|
105 |
+
yield {
|
106 |
+
clear_btn: gr.update(disabled=False),
|
107 |
+
input: gr.update(loading=False),
|
108 |
+
chatbot: gr.update(value=chatbot_value),
|
109 |
+
}
|
110 |
+
except Exception as e:
|
111 |
+
chatbot_value[-1]["loading"] = False
|
112 |
+
chatbot_value[-1]["status"] = "done"
|
113 |
+
chatbot_value[-1]["content"] = "Failed to respond, please try again."
|
114 |
+
yield {
|
115 |
+
clear_btn: gr.update(disabled=False),
|
116 |
+
input: gr.update(loading=False),
|
117 |
+
chatbot: gr.update(value=chatbot_value),
|
118 |
+
}
|
119 |
+
raise e
|
120 |
+
|
121 |
+
|
122 |
+
def close_copilot():
|
123 |
+
return gr.update(md=24), gr.update(md=0), gr.update(elem_style=dict(
|
124 |
+
display=""))
|
125 |
+
|
126 |
+
|
127 |
+
def open_copilot():
|
128 |
+
return gr.update(md=16), gr.update(md=8), gr.update(elem_style=dict(
|
129 |
+
display="none"))
|
130 |
+
|
131 |
+
|
132 |
+
def resize_window(e: gr.EventData):
|
133 |
+
screen_width = e._data["screen"]["width"]
|
134 |
+
is_mobile = screen_width < 768
|
135 |
+
return gr.update(visible=False if is_mobile else True), gr.update(
|
136 |
+
visible=False if is_mobile else True)
|
137 |
+
|
138 |
+
|
139 |
+
css = """
|
140 |
+
.copilot-container {
|
141 |
+
height: calc(100vh - 32px - 21px - 16px);
|
142 |
+
}
|
143 |
+
|
144 |
+
.copilot-container .copilot {
|
145 |
+
height: 100%;
|
146 |
+
padding: 10px;
|
147 |
+
background: var(--background-fill-secondary);
|
148 |
+
border-top: 1px solid var(--border-color-primary);
|
149 |
+
border-right: 1px solid var(--border-color-primary);
|
150 |
+
border-bottom: 1px solid var(--border-color-primary);
|
151 |
+
}
|
152 |
+
|
153 |
+
|
154 |
+
.copilot-container .content-body {
|
155 |
+
height: 100%;
|
156 |
+
overflow: auto;
|
157 |
+
}
|
158 |
+
|
159 |
+
@media (max-width: 768px) {
|
160 |
+
.copilot-container {
|
161 |
+
height: auto;
|
162 |
+
}
|
163 |
+
.copilot-container .copilot {
|
164 |
+
height: 600px;
|
165 |
+
border-left: 1px solid var(--border-color-primary);
|
166 |
+
}
|
167 |
+
.copilot-container .content-body {
|
168 |
+
height: auto;
|
169 |
+
max-height: 400px;
|
170 |
+
}
|
171 |
+
}
|
172 |
+
"""
|
173 |
+
|
174 |
+
with gr.Blocks(css=css) as demo, ms.Application() as app, antdx.XProvider():
|
175 |
+
with antd.Row(elem_classes="copilot-container", wrap=True):
|
176 |
+
# Content column
|
177 |
+
with antd.Col(md=16, xs=24,
|
178 |
+
elem_style=dict(height="100%")) as content_col:
|
179 |
+
with ms.AutoLoading(elem_style=dict(height="100%")):
|
180 |
+
with antd.Card(elem_style=dict(height="100%",
|
181 |
+
borderRadius=0,
|
182 |
+
display="flex",
|
183 |
+
flexDirection="column"),
|
184 |
+
class_names=dict(body="content-body")):
|
185 |
+
# Title
|
186 |
+
with ms.Slot("title"):
|
187 |
+
with antd.Typography.Title(level=1,
|
188 |
+
elem_style=dict(
|
189 |
+
margin=0,
|
190 |
+
textAlign="center",
|
191 |
+
fontSize=30)):
|
192 |
+
ms.Text("🤖 Copilot Template")
|
193 |
+
|
194 |
+
# Copilot button
|
195 |
+
with ms.Slot("extra"):
|
196 |
+
copilot_open_btn = antd.Button(
|
197 |
+
"✨ AI Copilot",
|
198 |
+
shape="round",
|
199 |
+
variant='filled',
|
200 |
+
color="primary",
|
201 |
+
elem_style=dict(display="none"))
|
202 |
+
|
203 |
+
# Content
|
204 |
+
ms.Markdown("""
|
205 |
+
<img src="https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*48RLR41kwHIAAAAAAAAAAAAADgCCAQ/fmt.webp" style="width: 100%;" />
|
206 |
+
<h4>What is the RICH design paradigm?</h4>
|
207 |
+
<div>
|
208 |
+
RICH is an AI interface design paradigm we propose, similar to how the WIMP paradigm
|
209 |
+
relates to graphical user interfaces.
|
210 |
+
</div>
|
211 |
+
<br />
|
212 |
+
<div>
|
213 |
+
The ACM SIGCHI 2005 (the premier conference on human-computer interaction) defined
|
214 |
+
that the core issues of human-computer interaction can be divided into three levels:
|
215 |
+
</div>
|
216 |
+
<ul>
|
217 |
+
<li>
|
218 |
+
Interface Paradigm Layer: Defines the design elements of human-computer
|
219 |
+
interaction interfaces, guiding designers to focus on core issues.
|
220 |
+
</li>
|
221 |
+
<li>
|
222 |
+
User model layer: Build an interface experience evaluation model to measure the
|
223 |
+
quality of the interface experience.
|
224 |
+
</li>
|
225 |
+
<li>
|
226 |
+
Software framework layer: The underlying support algorithms and data structures
|
227 |
+
for human-computer interfaces, which are the contents hidden behind the front-end
|
228 |
+
interface.
|
229 |
+
</li>
|
230 |
+
</ul>
|
231 |
+
<div>
|
232 |
+
The interface paradigm is the aspect that designers need to focus on and define the
|
233 |
+
most when a new human-computer interaction technology is born. The interface
|
234 |
+
paradigm defines the design elements that designers should pay attention to, and
|
235 |
+
based on this, it is possible to determine what constitutes good design and how to
|
236 |
+
achieve it.
|
237 |
+
</div>
|
238 |
+
""")
|
239 |
+
|
240 |
+
# Copilot column
|
241 |
+
with antd.Col(md=8, xs=24,
|
242 |
+
elem_style=dict(height="100%")) as copilot_col:
|
243 |
+
with ms.AutoLoading(elem_style=dict(height="100%")):
|
244 |
+
with antd.Flex(
|
245 |
+
vertical=True,
|
246 |
+
gap="small",
|
247 |
+
elem_classes="copilot",
|
248 |
+
):
|
249 |
+
with antd.Flex(justify="space-between", align="center"):
|
250 |
+
antd.Typography.Title("✨ AI Copilot",
|
251 |
+
level=4,
|
252 |
+
elem_style=dict(margin=0))
|
253 |
+
with antd.Flex(align="center", gap="small"):
|
254 |
+
with antd.Button(
|
255 |
+
variant="text",
|
256 |
+
color="default") as copilot_close_btn:
|
257 |
+
with ms.Slot("icon"):
|
258 |
+
antd.Icon("CloseOutlined")
|
259 |
+
antd.Divider(size="small")
|
260 |
+
chatbot = pro.Chatbot(
|
261 |
+
# for flex=1 to fill the remaining space
|
262 |
+
height=0,
|
263 |
+
elem_style=dict(flex=1),
|
264 |
+
welcome_config=ChatbotWelcomeConfig(
|
265 |
+
variant="filled",
|
266 |
+
title="👋🏻 Hello, I'm AI Copilot",
|
267 |
+
description="Enter a prompt to get started",
|
268 |
+
prompts=ChatbotPromptsConfig(
|
269 |
+
title="I can help: ",
|
270 |
+
vertical=True,
|
271 |
+
items=[{
|
272 |
+
"description":
|
273 |
+
"Help me with a plan to start a business"
|
274 |
+
}, {
|
275 |
+
"description":
|
276 |
+
"Help me with a plan to achieve my goals"
|
277 |
+
}, {
|
278 |
+
"description":
|
279 |
+
"Help me with a plan for a successful interview"
|
280 |
+
}])),
|
281 |
+
user_config=dict(
|
282 |
+
avatar=
|
283 |
+
"https://api.dicebear.com/7.x/miniavs/svg?seed=3"),
|
284 |
+
bot_config=ChatbotBotConfig(
|
285 |
+
header="Copilot",
|
286 |
+
actions=["copy", "retry"],
|
287 |
+
avatar=
|
288 |
+
"https://api.dicebear.com/7.x/miniavs/svg?seed=2"),
|
289 |
+
)
|
290 |
+
|
291 |
+
with pro.MultimodalInput(
|
292 |
+
upload_config=MultimodalInputUploadConfig(
|
293 |
+
allow_upload=False)) as input:
|
294 |
+
with ms.Slot("prefix"):
|
295 |
+
with antd.Button(value=None,
|
296 |
+
color="default",
|
297 |
+
variant="text") as clear_btn:
|
298 |
+
with ms.Slot("icon"):
|
299 |
+
antd.Icon("ClearOutlined")
|
300 |
+
clear_btn.click(fn=clear, outputs=[chatbot])
|
301 |
+
submit_event = input.submit(fn=submit,
|
302 |
+
inputs=[input, chatbot],
|
303 |
+
outputs=[input, chatbot, clear_btn])
|
304 |
+
input.cancel(fn=cancel,
|
305 |
+
inputs=[chatbot],
|
306 |
+
outputs=[chatbot, input, clear_btn],
|
307 |
+
cancels=[submit_event],
|
308 |
+
queue=False)
|
309 |
+
chatbot.retry(fn=retry,
|
310 |
+
inputs=[chatbot],
|
311 |
+
outputs=[input, chatbot, clear_btn])
|
312 |
+
chatbot.welcome_prompt_select(fn=prompt_select,
|
313 |
+
inputs=[input],
|
314 |
+
outputs=[input])
|
315 |
+
copilot_open_btn.click(
|
316 |
+
fn=open_copilot,
|
317 |
+
outputs=[content_col, copilot_col, copilot_open_btn])
|
318 |
+
copilot_close_btn.click(
|
319 |
+
fn=close_copilot,
|
320 |
+
outputs=[content_col, copilot_col, copilot_open_btn])
|
321 |
+
gr.on([app.mount, app.resize],
|
322 |
+
fn=resize_window,
|
323 |
+
outputs=[copilot_open_btn, copilot_close_btn])
|
324 |
+
if __name__ == "__main__":
|
325 |
+
demo.queue().launch()
|
layout_templates/coder_artifacts/demos/app.py
CHANGED
@@ -22,26 +22,41 @@ DEFAULT_SYSTEM_PROMPT = """You are a web development engineer, writing web pages
|
|
22 |
All code is written in a single code block to form a complete code file for display, without separating HTML and JavaScript code. An artifact refers to a runnable complete code snippet, you prefer to integrate and output such complete runnable code rather than breaking it down into several code blocks. For certain types of code, they can render graphical interfaces in a UI window. After generation, please check the code execution again to ensure there are no errors in the output.
|
23 |
Output only the HTML, without any additional descriptive text."""
|
24 |
|
25 |
-
EXAMPLES = [
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
|
46 |
DEFAULT_LOCALE = 'en_US'
|
47 |
|
@@ -266,20 +281,26 @@ with gr.Blocks(css=css) as demo:
|
|
266 |
antd.Divider("Examples")
|
267 |
|
268 |
# Examples
|
269 |
-
with antd.
|
270 |
-
for
|
271 |
-
with antd.
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
283 |
|
284 |
# Right Column
|
285 |
with antd.Col(span=24, md=16):
|
|
|
22 |
All code is written in a single code block to form a complete code file for display, without separating HTML and JavaScript code. An artifact refers to a runnable complete code snippet, you prefer to integrate and output such complete runnable code rather than breaking it down into several code blocks. For certain types of code, they can render graphical interfaces in a UI window. After generation, please check the code execution again to ensure there are no errors in the output.
|
23 |
Output only the HTML, without any additional descriptive text."""
|
24 |
|
25 |
+
EXAMPLES = [{
|
26 |
+
"tab":
|
27 |
+
"Section 1",
|
28 |
+
"examples": [
|
29 |
+
{
|
30 |
+
"title":
|
31 |
+
"Qwen,Start!",
|
32 |
+
"description":
|
33 |
+
"Help me design an interface with a purple button that says 'Qwen, Start!'. When the button is clicked, display a countdown from 5 in a very large font for 5 seconds.",
|
34 |
+
},
|
35 |
+
{
|
36 |
+
"title":
|
37 |
+
"Spam with emojis!",
|
38 |
+
"description":
|
39 |
+
"Write code in a single HTML file: Capture the click event, place a random number of emojis at the click position, and add gravity and collision effects to each emoji."
|
40 |
+
},
|
41 |
+
]
|
42 |
+
}, {
|
43 |
+
"tab":
|
44 |
+
"Section 2",
|
45 |
+
"examples": [
|
46 |
+
{
|
47 |
+
"title":
|
48 |
+
"Strawberry card",
|
49 |
+
"description":
|
50 |
+
"""How many "r"s are in the word "strawberry"? Make a cute little card!"""
|
51 |
+
},
|
52 |
+
{
|
53 |
+
"title":
|
54 |
+
"TODO list",
|
55 |
+
"description":
|
56 |
+
"I want a TODO list that allows me to add tasks, delete tasks, and I would like the overall color theme to be purple."
|
57 |
+
},
|
58 |
+
]
|
59 |
+
}]
|
60 |
|
61 |
DEFAULT_LOCALE = 'en_US'
|
62 |
|
|
|
281 |
antd.Divider("Examples")
|
282 |
|
283 |
# Examples
|
284 |
+
with antd.Tabs():
|
285 |
+
for item in EXAMPLES:
|
286 |
+
with antd.Tabs.Item(label=item["tab"]):
|
287 |
+
with antd.Flex(gap="small", wrap=True):
|
288 |
+
for example in item["examples"]:
|
289 |
+
with antd.Card(
|
290 |
+
elem_style=dict(
|
291 |
+
flex=
|
292 |
+
"1 1 fit-content"),
|
293 |
+
hoverable=True
|
294 |
+
) as example_card:
|
295 |
+
antd.Card.Meta(
|
296 |
+
title=example['title'],
|
297 |
+
description=example[
|
298 |
+
'description'])
|
299 |
+
|
300 |
+
example_card.click(
|
301 |
+
fn=GradioEvents.
|
302 |
+
select_example(example),
|
303 |
+
outputs=[input])
|
304 |
|
305 |
# Right Column
|
306 |
with antd.Col(span=24, md=16):
|