Spaces:
Running
Running
import base64 | |
import gradio as gr | |
import json | |
import mimetypes | |
import os | |
import requests | |
import time | |
import modelscope_studio.components.antd as antd | |
import modelscope_studio.components.antdx as antdx | |
import modelscope_studio.components.base as ms | |
import modelscope_studio.components.pro as pro | |
from modelscope_studio.components.pro.chatbot import ( | |
ChatbotActionConfig, ChatbotBotConfig, ChatbotMarkdownConfig, | |
ChatbotPromptsConfig, ChatbotUserConfig, ChatbotWelcomeConfig) | |
from config import DEFAULT_PROMPTS, EXAMPLES, SystemPrompt | |
import re | |
MODEL_VERSION = os.environ['MODEL_VERSION'] | |
API_URL = os.environ['API_URL'] | |
API_KEY = os.environ['API_KEY'] | |
SYSTEM_PROMPT = os.environ.get('SYSTEM_PROMPT') | |
MULTIMODAL_FLAG = os.environ.get('MULTIMODAL') | |
MODEL_CONTROL_DEFAULTS = json.loads(os.environ['MODEL_CONTROL_DEFAULTS']) | |
NAME_MAP = { | |
'system': os.environ.get('SYSTEM_NAME'), | |
'user': os.environ.get('USER_NAME'), | |
} | |
MODEL_NAME = 'MiniMax-M1' | |
def prompt_select(e: gr.EventData): | |
return gr.update(value=e._data["payload"][0]["value"]["description"]) | |
def clear(): | |
return gr.update(value=None) | |
def retry(chatbot_value, e: gr.EventData): | |
index = e._data["payload"][0]["index"] | |
chatbot_value = chatbot_value[:index] | |
yield gr.update(loading=True), gr.update(value=chatbot_value), gr.update( | |
disabled=True) | |
for chunk in submit(None, chatbot_value): | |
yield chunk | |
def cancel(chatbot_value): | |
chatbot_value[-1]["loading"] = False | |
chatbot_value[-1]["status"] = "done" | |
chatbot_value[-1]["footer"] = "Chat completion paused" | |
return gr.update(value=chatbot_value), gr.update(loading=False), gr.update( | |
disabled=False) | |
def add_name_for_message(message): | |
name = NAME_MAP.get(message['role']) | |
if name is not None: | |
message['name'] = name | |
def convert_content(content): | |
if isinstance(content, str): | |
return content | |
if isinstance(content, tuple): | |
return [{ | |
'type': 'image_url', | |
'image_url': { | |
'url': encode_base64(content[0]), | |
}, | |
}] | |
content_list = [] | |
for key, val in content.items(): | |
if key == 'text': | |
content_list.append({ | |
'type': 'text', | |
'text': val, | |
}) | |
elif key == 'files': | |
for f in val: | |
content_list.append({ | |
'type': 'image_url', | |
'image_url': { | |
'url': encode_base64(f), | |
}, | |
}) | |
return content_list | |
def encode_base64(path): | |
guess_type = mimetypes.guess_type(path)[0] | |
if not guess_type.startswith('image/'): | |
raise gr.Error('not an image ({}): {}'.format(guess_type, path)) | |
with open(path, 'rb') as handle: | |
data = handle.read() | |
return 'data:{};base64,{}'.format( | |
guess_type, | |
base64.b64encode(data).decode(), | |
) | |
def format_history(history): | |
"""Convert chatbot history format to API call format""" | |
messages = [] | |
if SYSTEM_PROMPT is not None: | |
messages.append({ | |
'role': 'system', | |
'content': SYSTEM_PROMPT, | |
}) | |
for item in history: | |
if item["role"] == "user": | |
messages.append({ | |
'role': 'user', | |
'content': convert_content(item["content"]), | |
}) | |
elif item["role"] == "assistant": | |
# Extract reasoning content and main content | |
reasoning_content = "" | |
main_content = "" | |
if isinstance(item["content"], list): | |
for content_item in item["content"]: | |
if content_item.get("type") == "tool": | |
reasoning_content = content_item.get("content", "") | |
elif content_item.get("type") == "text": | |
main_content = content_item.get("content", "") | |
else: | |
main_content = item["content"] | |
messages.append({ | |
'role': | |
'assistant', | |
'content': | |
convert_content(main_content), | |
'reasoning_content': | |
convert_content(reasoning_content), | |
}) | |
return messages | |
def submit(sender_value, chatbot_value): | |
if sender_value is not None: | |
chatbot_value.append({ | |
"role": "user", | |
"content": sender_value, | |
}) | |
api_messages = format_history(chatbot_value) | |
for message in api_messages: | |
add_name_for_message(message) | |
chatbot_value.append({ | |
"role": "assistant", | |
"content": [], | |
"loading": True, | |
"status": "pending" | |
}) | |
yield { | |
sender: gr.update(value=None, loading=True), | |
clear_btn: gr.update(disabled=True), | |
chatbot: gr.update(value=chatbot_value) | |
} | |
try: | |
data = { | |
'model': MODEL_VERSION, | |
'messages': api_messages, | |
'stream': True, | |
'max_tokens': MODEL_CONTROL_DEFAULTS['tokens_to_generate'], | |
'temperature': MODEL_CONTROL_DEFAULTS['temperature'], | |
'top_p': MODEL_CONTROL_DEFAULTS['top_p'], | |
} | |
r = requests.post( | |
API_URL, | |
headers={ | |
'Content-Type': 'application/json', | |
'Authorization': 'Bearer {}'.format(API_KEY), | |
}, | |
data=json.dumps(data), | |
stream=True, | |
) | |
thought_done = False | |
start_time = time.time() | |
message_content = chatbot_value[-1]["content"] | |
# Reasoning content (tool type) | |
message_content.append({ | |
"type": "tool", | |
"content": "", | |
"options": { | |
"title": "🤔 Thinking..." | |
} | |
}) | |
# Main content (text type) | |
message_content.append({ | |
"type": "text", | |
"content": "", | |
}) | |
reasoning_start_time = None | |
reasoning_duration = None | |
for row in r.iter_lines(): | |
if row.startswith(b'data:'): | |
data = json.loads(row[5:]) | |
if 'choices' not in data: | |
raise gr.Error('request failed') | |
choice = data['choices'][0] | |
if 'delta' in choice: | |
delta = choice['delta'] | |
reasoning_content = delta.get('reasoning_content', '') | |
content = delta.get('content', '') | |
chatbot_value[-1]["loading"] = False | |
# Handle reasoning content | |
if reasoning_content: | |
if reasoning_start_time is None: | |
reasoning_start_time = time.time() | |
message_content[-2]["content"] += reasoning_content | |
# Handle main content | |
if content: | |
message_content[-1]["content"] += content | |
if not thought_done: | |
thought_done = True | |
if reasoning_start_time is not None: | |
reasoning_duration = time.time( | |
) - reasoning_start_time | |
thought_cost_time = "{:.2f}".format( | |
reasoning_duration) | |
else: | |
reasoning_duration = 0.0 | |
thought_cost_time = "0.00" | |
message_content[-2]["options"] = { | |
"title": | |
f"End of Thought ({thought_cost_time}s)", | |
"status": "done" | |
} | |
yield {chatbot: gr.update(value=chatbot_value)} | |
elif 'message' in choice: | |
message_data = choice['message'] | |
reasoning_content = message_data.get( | |
'reasoning_content', '') | |
main_content = message_data.get('content', '') | |
message_content[-2]["content"] = reasoning_content | |
message_content[-1]["content"] = main_content | |
if reasoning_content and main_content: | |
if reasoning_duration is None: | |
if reasoning_start_time is not None: | |
reasoning_duration = time.time( | |
) - reasoning_start_time | |
thought_cost_time = "{:.2f}".format( | |
reasoning_duration) | |
else: | |
reasoning_duration = 0.0 | |
thought_cost_time = "0.00" | |
else: | |
thought_cost_time = "{:.2f}".format( | |
reasoning_duration) | |
message_content[-2]["options"] = { | |
"title": f"End of Thought ({thought_cost_time}s)", | |
"status": "done" | |
} | |
chatbot_value[-1]["loading"] = False | |
yield {chatbot: gr.update(value=chatbot_value)} | |
chatbot_value[-1]["footer"] = "{:.2f}s".format(time.time() - | |
start_time) | |
chatbot_value[-1]["status"] = "done" | |
yield { | |
clear_btn: gr.update(disabled=False), | |
sender: gr.update(loading=False), | |
chatbot: gr.update(value=chatbot_value), | |
} | |
except Exception as e: | |
chatbot_value[-1]["loading"] = False | |
chatbot_value[-1]["status"] = "done" | |
chatbot_value[-1]["content"] = "Request failed, please try again." | |
yield { | |
clear_btn: gr.update(disabled=False), | |
sender: gr.update(loading=False), | |
chatbot: gr.update(value=chatbot_value), | |
} | |
raise e | |
def remove_code_block(text): | |
# Try to match code blocks with language markers | |
patterns = [ | |
r'```(?:html|HTML)\n([\s\S]+?)\n```', # Match ```html or ```HTML | |
r'```\n([\s\S]+?)\n```', # Match code blocks without language markers | |
r'```([\s\S]+?)```' # Match code blocks without line breaks | |
] | |
for pattern in patterns: | |
match = re.search(pattern, text, re.DOTALL) | |
if match: | |
extracted = match.group(1).strip() | |
print("Successfully extracted code block:", extracted) | |
return extracted | |
# If no code block is found, check if the entire text is HTML | |
if text.strip().startswith('<!DOCTYPE html>') or text.strip().startswith( | |
'<html'): | |
print("Text appears to be raw HTML, using as is") | |
return text.strip() | |
print("No code block found in text:", text) | |
return text.strip() | |
def send_to_sandbox(code): | |
# Add a wrapper to inject necessary permissions | |
wrapped_code = f""" | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<script> | |
// Create a safe storage alternative | |
const safeStorage = {{ | |
_data: {{}}, | |
getItem: function(key) {{ | |
return this._data[key] || null; | |
}}, | |
setItem: function(key, value) {{ | |
this._data[key] = value; | |
}}, | |
removeItem: function(key) {{ | |
delete this._data[key]; | |
}}, | |
clear: function() {{ | |
this._data = {{}}; | |
}} | |
}}; | |
// Replace native localStorage | |
Object.defineProperty(window, 'localStorage', {{ | |
value: safeStorage, | |
writable: false | |
}}); | |
// Add error handling without using alert | |
window.onerror = function(message, source, lineno, colno, error) {{ | |
console.error('Error:', message); | |
}}; | |
</script> | |
</head> | |
<body> | |
{code} | |
</body> | |
</html> | |
""" | |
encoded_html = base64.b64encode( | |
wrapped_code.encode('utf-8')).decode('utf-8') | |
data_uri = f"data:text/html;charset=utf-8;base64,{encoded_html}" | |
iframe = f'<iframe src="{data_uri}" width="100%" height="920px" sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-presentation" allow="display-capture"></iframe>' | |
print("Generated iframe:", iframe) | |
return iframe | |
def select_example(example): | |
if isinstance(example, dict): | |
return example.get("description", "") | |
return "" | |
def generate_code(query: str): | |
if not query: | |
return { | |
code_output: gr.update(value=None), | |
reasoning_output: gr.update(value=None), | |
sandbox: gr.update(value=None), | |
state_tab: gr.update(active_key="empty"), | |
output_tabs: gr.update(active_key="reasoning", visible=False), | |
loading: gr.update(tip="Thinking...") | |
} | |
print("Starting code generation with query:", query) | |
messages = [{ | |
'role': 'system', | |
'content': SystemPrompt | |
}, { | |
'role': 'user', | |
'content': query | |
}] | |
max_retries = 3 | |
retry_count = 0 | |
while retry_count < max_retries: | |
try: | |
data = { | |
'model': MODEL_VERSION, | |
'messages': messages, | |
'stream': True, | |
'max_tokens': MODEL_CONTROL_DEFAULTS['tokens_to_generate'], | |
'temperature': MODEL_CONTROL_DEFAULTS['temperature'], | |
'top_p': MODEL_CONTROL_DEFAULTS['top_p'], | |
} | |
print( | |
f"Attempt {retry_count + 1}: Sending request to API with data:", | |
json.dumps(data, indent=2)) | |
r = requests.post( | |
API_URL, | |
headers={ | |
'Content-Type': 'application/json', | |
'Authorization': 'Bearer {}'.format(API_KEY), | |
}, | |
data=json.dumps(data), | |
stream=True, | |
timeout=60 # Set 60 seconds timeout | |
) | |
content = "" | |
reasoning_content = "" | |
loading_text = "Thinking..." | |
for row in r.iter_lines(): | |
if row.startswith(b'data:'): | |
data = json.loads(row[5:]) | |
print("Received data from API:", json.dumps(data, | |
indent=2)) | |
if 'choices' not in data: | |
raise gr.Error('request failed') | |
choice = data['choices'][0] | |
if 'delta' in choice: | |
delta = choice['delta'] | |
content += delta.get('content', '') | |
reasoning_content += delta.get('reasoning_content', '') | |
# Update loading text based on content | |
if content and not loading_text == "Generating code...": | |
loading_text = "Generating code..." | |
yield { | |
code_output: | |
gr.update(value=content), | |
reasoning_output: | |
gr.update(value=reasoning_content + "\n"), | |
sandbox: | |
gr.update(value=None), | |
state_tab: | |
gr.update(active_key="loading"), | |
output_tabs: | |
gr.update(active_key="reasoning", | |
visible=True), | |
loading: | |
gr.update(tip=loading_text) | |
} | |
else: | |
yield { | |
code_output: | |
gr.update(value=content), | |
reasoning_output: | |
gr.update(value=reasoning_content + "\n"), | |
sandbox: | |
gr.update(value=None), | |
state_tab: | |
gr.update(active_key="loading"), | |
output_tabs: | |
gr.update(active_key="reasoning", | |
visible=True), | |
loading: | |
gr.update(tip=loading_text) | |
} | |
elif 'message' in choice: | |
message_data = choice['message'] | |
content = message_data.get('content', '') | |
reasoning_content = message_data.get( | |
'reasoning_content', '') | |
print("Final content:", content) | |
print("Final reasoning:", reasoning_content) | |
html_content = remove_code_block(content) | |
print("Extracted HTML:", html_content) | |
yield { | |
code_output: | |
gr.update(value=content), | |
reasoning_output: | |
gr.update(value=reasoning_content + "\n"), | |
sandbox: | |
gr.update(value=send_to_sandbox(html_content)), | |
state_tab: | |
gr.update(active_key="render"), | |
output_tabs: | |
gr.update(active_key="code", visible=True), | |
loading: | |
gr.update(tip="Done") | |
} | |
# If successful, break out of retry loop | |
break | |
except (requests.exceptions.Timeout, | |
requests.exceptions.ConnectionError) as e: | |
retry_count += 1 | |
if retry_count == max_retries: | |
print(f"Failed after {max_retries} attempts:", str(e)) | |
raise gr.Error( | |
f"Request failed after {max_retries} attempts: {str(e)}") | |
print(f"Attempt {retry_count} failed, retrying...") | |
time.sleep(1) # Wait 1 second before retrying | |
except Exception as e: | |
print("Error occurred:", str(e)) | |
raise gr.Error(str(e)) | |
css = """ | |
/* Add styles for the main container */ | |
.ant-tabs-content { | |
height: calc(100vh - 200px); | |
overflow: hidden; | |
} | |
.ant-tabs-tabpane { | |
height: 100%; | |
overflow-y: auto; | |
} | |
/* Modify existing styles */ | |
.output-empty,.output-loading { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
width: 100%; | |
min-height: 680px; | |
position: relative; | |
} | |
.output-html { | |
display: flex; | |
flex-direction: column; | |
width: 100%; | |
min-height: 680px; | |
} | |
.output-html > iframe { | |
flex: 1; | |
} | |
.right_content { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
width: 100%; | |
height: 100%; | |
min-height: unset; | |
background: #fff; | |
border-radius: 8px; | |
box-shadow: 0 2px 8px rgba(0,0,0,0.1); | |
} | |
/* Add styles for the code playground container */ | |
.code-playground-container { | |
height: 100%; | |
overflow-y: auto; | |
padding-right: 8px; | |
} | |
.code-playground-container::-webkit-scrollbar { | |
width: 6px; | |
} | |
.code-playground-container::-webkit-scrollbar-track { | |
background: #f1f1f1; | |
border-radius: 3px; | |
} | |
.code-playground-container::-webkit-scrollbar-thumb { | |
background: #888; | |
border-radius: 3px; | |
} | |
.code-playground-container::-webkit-scrollbar-thumb:hover { | |
background: #555; | |
} | |
.render_header { | |
display: flex; | |
align-items: center; | |
padding: 8px 16px; | |
background: #f5f5f5; | |
border-bottom: 1px solid #e8e8e8; | |
border-top-left-radius: 8px; | |
border-top-right-radius: 8px; | |
} | |
.header_btn { | |
width: 12px; | |
height: 12px; | |
border-radius: 50%; | |
margin-right: 8px; | |
display: inline-block; | |
} | |
.header_btn:nth-child(1) { | |
background: #ff5f56; | |
} | |
.header_btn:nth-child(2) { | |
background: #ffbd2e; | |
} | |
.header_btn:nth-child(3) { | |
background: #27c93f; | |
} | |
.output-html > iframe { | |
flex: 1; | |
border: none; | |
background: #fff; | |
} | |
.reasoning-box { | |
max-height: 300px; | |
overflow-y: auto; | |
border-radius: 4px; | |
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; | |
font-size: 14px; | |
line-height: 1.6; | |
width: 100%; | |
scroll-behavior: smooth; | |
display: flex; | |
flex-direction: column-reverse; | |
} | |
.reasoning-box .ms-markdown { | |
padding: 0 12px; | |
} | |
.reasoning-box::-webkit-scrollbar { | |
width: 6px; | |
} | |
.reasoning-box::-webkit-scrollbar-track { | |
background: #f1f1f1; | |
border-radius: 3px; | |
} | |
.reasoning-box::-webkit-scrollbar-thumb { | |
background: #888; | |
border-radius: 3px; | |
} | |
.reasoning-box::-webkit-scrollbar-thumb:hover { | |
background: #555; | |
} | |
.markdown-container { | |
max-height: 300px; | |
overflow-y: auto; | |
border-radius: 4px; | |
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; | |
font-size: 14px; | |
line-height: 1.6; | |
width: 100%; | |
scroll-behavior: smooth; | |
display: flex; | |
flex-direction: column-reverse; | |
} | |
/* Example card styles */ | |
.example-card { | |
flex: 1 1 calc(50% - 20px); | |
max-width: calc(50% - 20px); | |
margin: 6px; | |
transition: all 0.3s; | |
cursor: pointer; | |
border: 1px solid #e8e8e8; | |
border-radius: 8px; | |
box-shadow: 0 2px 8px rgba(0,0,0,0.05); | |
} | |
.example-card:hover { | |
transform: translateY(-4px); | |
box-shadow: 0 4px 12px rgba(0,0,0,0.1); | |
border-color: #d9d9d9; | |
} | |
.example-card .ant-card-meta-title { | |
font-size: 16px; | |
font-weight: 500; | |
margin-bottom: 8px; | |
color: #262626; | |
} | |
.example-card .ant-card-meta-description { | |
color: #666; | |
font-size: 14px; | |
line-height: 1.5; | |
} | |
/* Example tabs styles */ | |
.example-tabs .ant-tabs-nav { | |
margin-bottom: 16px; | |
} | |
.example-tabs .ant-tabs-tab { | |
padding: 8px 16px; | |
font-size: 15px; | |
} | |
.example-tabs .ant-tabs-tab-active { | |
font-weight: 500; | |
} | |
/* Empty state styles */ | |
.right_content { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
width: 100%; | |
min-height: 620px; | |
background: #fff; | |
border-radius: 8px; | |
box-shadow: 0 2px 8px rgba(0,0,0,0.1); | |
} | |
/* Add styles for the example cards container */ | |
.example-tabs .ant-tabs-content { | |
padding: 0 8px; | |
} | |
.example-tabs .ant-flex { | |
margin: 0 -8px; | |
width: calc(100% + 16px); | |
} | |
""" | |
def scroll_to_bottom(): | |
return """ | |
function() { | |
setTimeout(() => { | |
const reasoningBox = document.querySelector('.reasoning-box'); | |
if (reasoningBox) { | |
reasoningBox.scrollTop = reasoningBox.scrollHeight; | |
} | |
const markdownContainer = document.querySelector('.markdown-container'); | |
if (markdownContainer) { | |
markdownContainer.scrollTop = markdownContainer.scrollHeight; | |
} | |
}, 100); | |
} | |
""" | |
with gr.Blocks(css=css) as demo, ms.Application(), antdx.XProvider( | |
), ms.AutoLoading(): | |
with antd.Tabs() as tabs: | |
with antd.Tabs.Item(key="chat", label="Chatbot"): | |
with antd.Flex(vertical=True, | |
gap="middle", | |
elem_style=dict(height="calc(100vh - 150px)")): | |
chatbot = pro.Chatbot( | |
elem_style=dict(flex=1, maxHeight=1200, height=0), | |
markdown_config=ChatbotMarkdownConfig( | |
allow_tags=["think"]), | |
welcome_config=ChatbotWelcomeConfig( | |
variant="borderless", | |
icon="./assets/minimax-logo.png", | |
title="Hello, I'm MiniMax-M1", | |
description="You can input text to get started.", | |
prompts=ChatbotPromptsConfig( | |
title="How can I help you today?", | |
styles={ | |
"list": { | |
"width": '100%', | |
}, | |
"item": { | |
"flex": 1, | |
}, | |
}, | |
items=DEFAULT_PROMPTS)), | |
user_config=ChatbotUserConfig(actions=["copy", "edit"]), | |
bot_config=ChatbotBotConfig( | |
header=MODEL_NAME, | |
avatar="./assets/minimax-logo.png", | |
actions=["copy", "retry"])) | |
with antdx.Sender() as sender: | |
with ms.Slot("prefix"): | |
with antd.Button(value=None, | |
color="default", | |
variant="text") as clear_btn: | |
with ms.Slot("icon"): | |
antd.Icon("ClearOutlined") | |
clear_btn.click(fn=clear, outputs=[chatbot]) | |
submit_event = sender.submit( | |
fn=submit, | |
inputs=[sender, chatbot], | |
outputs=[sender, chatbot, clear_btn]) | |
sender.cancel(fn=cancel, | |
inputs=[chatbot], | |
outputs=[chatbot, sender, clear_btn], | |
cancels=[submit_event], | |
queue=False) | |
chatbot.retry(fn=retry, | |
inputs=[chatbot], | |
outputs=[sender, chatbot, clear_btn]) | |
chatbot.welcome_prompt_select(fn=prompt_select, | |
outputs=[sender]) | |
with antd.Tabs.Item(key="code", label="Code Playground (WebDev)"): | |
with antd.Row(gutter=[32, 12], | |
elem_classes="code-playground-container"): | |
with antd.Col(span=24, md=12): | |
with antd.Flex(vertical=True, gap="middle"): | |
code_input = antd.Input.Textarea( | |
size="large", | |
allow_clear=True, | |
auto_size=dict(minRows=2, maxRows=6), | |
placeholder= | |
"Please enter what kind of application you want or choose an example below and click the button" | |
) | |
code_btn = antd.Button("Generate Code", | |
type="primary", | |
size="large") | |
with antd.Tabs(active_key="reasoning", | |
visible=False) as output_tabs: | |
with antd.Tabs.Item(key="reasoning", | |
label="🤔 Thinking Process"): | |
reasoning_output = ms.Markdown( | |
elem_classes="reasoning-box") | |
with antd.Tabs.Item(key="code", | |
label="💻 Generated Code"): | |
code_output = ms.Markdown( | |
elem_classes="markdown-container") | |
antd.Divider("Examples") | |
# Examples with categories | |
with antd.Tabs( | |
elem_classes="example-tabs") as example_tabs: | |
for category, examples in EXAMPLES.items(): | |
with antd.Tabs.Item(key=category, | |
label=category): | |
with antd.Flex(gap="small", wrap=True): | |
for example in examples: | |
with antd.Card( | |
elem_classes="example-card", | |
hoverable=True | |
) as example_card: | |
antd.Card.Meta( | |
title=example['title'], | |
description=example[ | |
'description']) | |
example_card.click( | |
fn=select_example, | |
inputs=[gr.State(example)], | |
outputs=[code_input]) | |
with antd.Col(span=24, md=12): | |
with antd.Card(title="Output", | |
elem_style=dict(height="100%"), | |
styles=dict(body=dict(height="100%")), | |
elem_id="output-container"): | |
with antd.Tabs( | |
active_key="empty", | |
render_tab_bar="() => null") as state_tab: | |
with antd.Tabs.Item(key="empty"): | |
empty = antd.Empty( | |
description= | |
"Enter your request to generate code", | |
elem_classes="output-empty") | |
with antd.Tabs.Item(key="loading"): | |
with antd.Spin(True, | |
tip="Thinking and coding...", | |
size="large", | |
elem_classes="output-loading" | |
) as loading: | |
# placeholder | |
ms.Div() | |
with antd.Tabs.Item(key="render"): | |
sandbox = gr.HTML(elem_classes="output-html") | |
code_btn.click(generate_code, | |
inputs=[code_input], | |
outputs=[ | |
code_output, reasoning_output, sandbox, | |
state_tab, output_tabs, loading | |
]) | |
# Add auto-scroll functionality | |
reasoning_output.change( | |
fn=scroll_to_bottom, | |
inputs=[], | |
outputs=[], | |
) | |
code_output.change( | |
fn=scroll_to_bottom, | |
inputs=[], | |
outputs=[], | |
) | |
def on_tab_change(e: gr.EventData): | |
tab_key = e._data["payload"][0] | |
return gr.update(active_key=tab_key, visible=True) | |
output_tabs.change( | |
fn=on_tab_change, | |
outputs=[output_tabs], | |
) | |
if __name__ == '__main__': | |
demo.queue(default_concurrency_limit=50).launch(ssr_mode=False) |