done
Browse files
app.py
CHANGED
@@ -33,6 +33,16 @@ except Exception as e:
|
|
33 |
logger.error(f"Failed to initialize InferenceClient: {str(e)}")
|
34 |
raise RuntimeError(f"Failed to initialize the model. Please check your configuration: {str(e)}")
|
35 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
# Conversation tracking
|
37 |
def save_conversation(user_id, conversation):
|
38 |
filename = f"conversations/{user_id}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
|
@@ -41,70 +51,75 @@ def save_conversation(user_id, conversation):
|
|
41 |
json.dump(conversation, f)
|
42 |
logger.info(f"Saved conversation for user {user_id}")
|
43 |
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
# Add history to messages
|
48 |
-
for h in history:
|
49 |
-
messages.append({"role": "user", "content": h[0]})
|
50 |
-
messages.append({"role": "assistant", "content": h[1]})
|
51 |
-
|
52 |
-
# Add current message
|
53 |
-
messages.append({"role": "user", "content": message})
|
54 |
-
|
55 |
-
# Generate response
|
56 |
-
try:
|
57 |
-
response = client.text_generation(
|
58 |
-
prompt=message,
|
59 |
-
max_new_tokens=max_tokens,
|
60 |
-
temperature=temperature,
|
61 |
-
top_p=top_p,
|
62 |
-
)
|
63 |
-
return response
|
64 |
-
except Exception as e:
|
65 |
-
logger.error(f"Error generating response: {str(e)}")
|
66 |
-
return f"An error occurred: {str(e)}"
|
67 |
-
|
68 |
-
def chat(message, history, system_message, max_tokens, temperature, top_p, user_id):
|
69 |
if not message.strip():
|
70 |
-
return
|
71 |
|
72 |
logger.info(f"User {user_id} sent message - Length: {len(message)}")
|
73 |
|
74 |
try:
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
|
85 |
-
|
86 |
-
|
87 |
-
new_history.append([message, str(response)])
|
88 |
|
89 |
-
# Save conversation
|
90 |
conversation_data = {
|
91 |
"timestamp": datetime.now().isoformat(),
|
92 |
"user_id": user_id,
|
93 |
-
"
|
94 |
-
"response":
|
95 |
-
"
|
|
|
|
|
|
|
|
|
|
|
96 |
}
|
97 |
save_conversation(user_id, conversation_data)
|
98 |
|
99 |
-
|
|
|
|
|
100 |
|
101 |
except Exception as e:
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
return new_history
|
106 |
|
107 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
def authenticate(username, password):
|
109 |
valid_credentials = {
|
110 |
"admin": {"password": "admin123", "role": "admin"},
|
@@ -127,8 +142,8 @@ def login(username, password):
|
|
127 |
)
|
128 |
|
129 |
time.sleep(0.5)
|
130 |
-
success, user_id, role = authenticate(username, password)
|
131 |
|
|
|
132 |
if success:
|
133 |
return (
|
134 |
gr.update(visible=False),
|
@@ -146,9 +161,16 @@ def login(username, password):
|
|
146 |
gr.update(visible=True, value="Invalid username or password")
|
147 |
)
|
148 |
|
149 |
-
# CSS
|
150 |
css = """
|
151 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
152 |
.setting-panel {
|
153 |
background-color: #f0f4f8;
|
154 |
border-radius: 10px;
|
@@ -160,12 +182,64 @@ css = """
|
|
160 |
box-shadow: 0 2px 6px rgba(0,0,0,0.1);
|
161 |
background-color: white;
|
162 |
}
|
163 |
-
.
|
164 |
-
|
165 |
-
|
166 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
167 |
pointer-events: none;
|
168 |
-
opacity: 0.6;
|
169 |
}
|
170 |
"""
|
171 |
|
@@ -174,116 +248,166 @@ with gr.Blocks(css=css, title=f"{COMPANY_NAME} AI Assistant") as demo:
|
|
174 |
user_id = gr.State(None)
|
175 |
user_role = gr.State(None)
|
176 |
|
177 |
-
|
|
|
|
|
|
|
178 |
with gr.Group(visible=True) as login_group:
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
|
|
|
|
183 |
|
184 |
-
# Chat
|
185 |
with gr.Group(visible=False) as chat_group:
|
186 |
with gr.Row():
|
187 |
# Settings Panel
|
188 |
-
with gr.Column(scale=1):
|
189 |
-
|
190 |
-
system_message = gr.Textbox(
|
191 |
-
label="System Message",
|
192 |
-
value=DEFAULT_SYSTEM_PROMPT,
|
193 |
-
lines=3
|
194 |
-
)
|
195 |
|
196 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
197 |
max_tokens = gr.Slider(
|
198 |
minimum=1,
|
199 |
maximum=2048,
|
200 |
value=512,
|
201 |
step=1,
|
202 |
-
label="Max
|
|
|
203 |
)
|
204 |
temperature = gr.Slider(
|
205 |
minimum=0.1,
|
206 |
maximum=1.0,
|
207 |
value=0.7,
|
208 |
step=0.1,
|
209 |
-
label="Temperature"
|
|
|
210 |
)
|
211 |
top_p = gr.Slider(
|
212 |
minimum=0.1,
|
213 |
maximum=1.0,
|
214 |
value=0.95,
|
215 |
step=0.05,
|
216 |
-
label="Top
|
|
|
217 |
)
|
|
|
|
|
|
|
218 |
|
219 |
-
|
220 |
-
|
|
|
|
|
|
|
|
|
221 |
|
222 |
-
# Chat
|
223 |
-
with gr.Column(scale=2):
|
224 |
-
chatbot = gr.Chatbot()
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
|
|
|
|
230 |
|
231 |
-
# Event
|
232 |
-
def
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
gr.update(elem_classes=settings_class)
|
237 |
-
)
|
238 |
|
239 |
-
def
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
return chat(message, history, system_msg, max_tok, temp, tp, uid)
|
249 |
|
250 |
-
# Login
|
251 |
login_button.click(
|
252 |
login,
|
253 |
inputs=[username, password],
|
254 |
outputs=[login_group, chat_group, user_id, user_role, error_message]
|
255 |
).then(
|
256 |
-
|
|
|
|
|
|
|
|
|
257 |
inputs=[user_role],
|
258 |
-
outputs=[
|
259 |
)
|
260 |
|
261 |
-
#
|
262 |
-
|
263 |
-
|
264 |
-
inputs=[
|
265 |
-
outputs=[
|
266 |
-
)
|
267 |
|
|
|
268 |
msg.submit(
|
269 |
-
|
270 |
-
inputs=[msg, chatbot, user_id
|
271 |
outputs=[chatbot]
|
272 |
-
).then(lambda: "", None, msg)
|
|
|
|
|
|
|
|
|
|
|
|
|
273 |
|
274 |
# Clear chat
|
275 |
-
|
276 |
|
277 |
-
#
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
285 |
outputs=[login_group, chat_group, user_id, user_role]
|
286 |
)
|
287 |
|
288 |
if __name__ == "__main__":
|
289 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
logger.error(f"Failed to initialize InferenceClient: {str(e)}")
|
34 |
raise RuntimeError(f"Failed to initialize the model. Please check your configuration: {str(e)}")
|
35 |
|
36 |
+
# Configuration state management
|
37 |
+
class ConfigState:
|
38 |
+
def __init__(self):
|
39 |
+
self.system_message = DEFAULT_SYSTEM_PROMPT
|
40 |
+
self.max_tokens = 512
|
41 |
+
self.temperature = 0.7
|
42 |
+
self.top_p = 0.95
|
43 |
+
|
44 |
+
config_state = ConfigState()
|
45 |
+
|
46 |
# Conversation tracking
|
47 |
def save_conversation(user_id, conversation):
|
48 |
filename = f"conversations/{user_id}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
|
|
|
51 |
json.dump(conversation, f)
|
52 |
logger.info(f"Saved conversation for user {user_id}")
|
53 |
|
54 |
+
# Main chat function
|
55 |
+
def respond(message, chat_history, user_id):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
if not message.strip():
|
57 |
+
return chat_history + [[message, "I'm sorry, I didn't receive any input. How can I help you today?"]]
|
58 |
|
59 |
logger.info(f"User {user_id} sent message - Length: {len(message)}")
|
60 |
|
61 |
try:
|
62 |
+
messages = [{"role": "system", "content": config_state.system_message}]
|
63 |
+
|
64 |
+
for user_msg, assistant_msg in chat_history:
|
65 |
+
messages.append({"role": "user", "content": user_msg})
|
66 |
+
if assistant_msg: # Check if assistant message exists
|
67 |
+
messages.append({"role": "assistant", "content": assistant_msg})
|
68 |
+
|
69 |
+
messages.append({"role": "user", "content": message})
|
70 |
+
|
71 |
+
start_time = datetime.now()
|
72 |
+
full_response = ""
|
73 |
+
|
74 |
+
for message_chunk in client.chat_completion(
|
75 |
+
messages,
|
76 |
+
max_tokens=config_state.max_tokens,
|
77 |
+
temperature=config_state.temperature,
|
78 |
+
top_p=config_state.top_p,
|
79 |
+
stream=True,
|
80 |
+
):
|
81 |
+
if message_chunk.choices[0].delta.content:
|
82 |
+
full_response += message_chunk.choices[0].delta.content
|
83 |
+
yield chat_history + [[message, full_response]]
|
84 |
|
85 |
+
time_taken = (datetime.now() - start_time).total_seconds()
|
86 |
+
logger.info(f"Response generated for user {user_id} in {time_taken:.2f}s - Length: {len(full_response)}")
|
|
|
87 |
|
|
|
88 |
conversation_data = {
|
89 |
"timestamp": datetime.now().isoformat(),
|
90 |
"user_id": user_id,
|
91 |
+
"messages": messages,
|
92 |
+
"response": full_response,
|
93 |
+
"parameters": {
|
94 |
+
"max_tokens": config_state.max_tokens,
|
95 |
+
"temperature": config_state.temperature,
|
96 |
+
"top_p": config_state.top_p
|
97 |
+
},
|
98 |
+
"time_taken": time_taken
|
99 |
}
|
100 |
save_conversation(user_id, conversation_data)
|
101 |
|
102 |
+
if not full_response:
|
103 |
+
return chat_history
|
104 |
+
return chat_history + [[message, full_response]]
|
105 |
|
106 |
except Exception as e:
|
107 |
+
error_msg = f"An error occurred: {str(e)}"
|
108 |
+
logger.error(f"Error generating response for user {user_id}: {str(e)}")
|
109 |
+
return chat_history + [[message, error_msg]]
|
|
|
110 |
|
111 |
+
# Configuration update functions
|
112 |
+
def update_config(system_msg, max_tok, temp, tp, role):
|
113 |
+
if role == "admin":
|
114 |
+
config_state.system_message = system_msg
|
115 |
+
config_state.max_tokens = max_tok
|
116 |
+
config_state.temperature = temp
|
117 |
+
config_state.top_p = tp
|
118 |
+
logger.info("Configuration updated by admin")
|
119 |
+
return "Configuration updated successfully"
|
120 |
+
return "Only administrators can update configuration"
|
121 |
+
|
122 |
+
# Authentication function
|
123 |
def authenticate(username, password):
|
124 |
valid_credentials = {
|
125 |
"admin": {"password": "admin123", "role": "admin"},
|
|
|
142 |
)
|
143 |
|
144 |
time.sleep(0.5)
|
|
|
145 |
|
146 |
+
success, user_id, role = authenticate(username, password)
|
147 |
if success:
|
148 |
return (
|
149 |
gr.update(visible=False),
|
|
|
161 |
gr.update(visible=True, value="Invalid username or password")
|
162 |
)
|
163 |
|
164 |
+
# CSS styling (same as before)
|
165 |
css = """
|
166 |
+
body {
|
167 |
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
168 |
+
background-color: #f9f9f9;
|
169 |
+
}
|
170 |
+
.container {
|
171 |
+
max-width: 1400px !important;
|
172 |
+
margin: auto;
|
173 |
+
}
|
174 |
.setting-panel {
|
175 |
background-color: #f0f4f8;
|
176 |
border-radius: 10px;
|
|
|
182 |
box-shadow: 0 2px 6px rgba(0,0,0,0.1);
|
183 |
background-color: white;
|
184 |
}
|
185 |
+
.company-header {
|
186 |
+
background-color: #2c3e50;
|
187 |
+
color: white;
|
188 |
+
padding: 15px;
|
189 |
+
border-radius: 10px 10px 0 0;
|
190 |
+
margin-bottom: 15px;
|
191 |
+
}
|
192 |
+
.footer {
|
193 |
+
text-align: center;
|
194 |
+
margin-top: 20px;
|
195 |
+
color: #666;
|
196 |
+
font-size: 0.8em;
|
197 |
+
}
|
198 |
+
.message-user {
|
199 |
+
background-color: #e6f7ff !important;
|
200 |
+
border-radius: 15px 15px 0 15px !important;
|
201 |
+
}
|
202 |
+
.message-bot {
|
203 |
+
background-color: #f0f0f0 !important;
|
204 |
+
border-radius: 15px 15px 15px 0 !important;
|
205 |
+
}
|
206 |
+
.login-container {
|
207 |
+
max-width: 500px;
|
208 |
+
margin: 50px auto;
|
209 |
+
padding: 30px;
|
210 |
+
background-color: white;
|
211 |
+
border-radius: 10px;
|
212 |
+
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
|
213 |
+
}
|
214 |
+
.login-header {
|
215 |
+
text-align: center;
|
216 |
+
margin-bottom: 30px;
|
217 |
+
}
|
218 |
+
.error-message {
|
219 |
+
color: #e74c3c;
|
220 |
+
background-color: #fdedeb;
|
221 |
+
padding: 10px;
|
222 |
+
border-radius: 5px;
|
223 |
+
margin-bottom: 15px;
|
224 |
+
font-size: 14px;
|
225 |
+
}
|
226 |
+
.role-badge {
|
227 |
+
font-size: 12px;
|
228 |
+
padding: 3px 8px;
|
229 |
+
border-radius: 10px;
|
230 |
+
margin-left: 10px;
|
231 |
+
}
|
232 |
+
.admin-badge {
|
233 |
+
background-color: #e74c3c;
|
234 |
+
color: white;
|
235 |
+
}
|
236 |
+
.user-badge {
|
237 |
+
background-color: #3498db;
|
238 |
+
color: white;
|
239 |
+
}
|
240 |
+
.setting-disabled {
|
241 |
+
opacity: 0.5;
|
242 |
pointer-events: none;
|
|
|
243 |
}
|
244 |
"""
|
245 |
|
|
|
248 |
user_id = gr.State(None)
|
249 |
user_role = gr.State(None)
|
250 |
|
251 |
+
with gr.Row():
|
252 |
+
gr.Markdown(f"<div class='company-header'><h1>{COMPANY_NAME} AI Assistant</h1></div>")
|
253 |
+
|
254 |
+
# Login Group
|
255 |
with gr.Group(visible=True) as login_group:
|
256 |
+
with gr.Column(elem_classes=["login-container"]):
|
257 |
+
gr.Markdown(f"<div class='login-header'><h2>Welcome to {COMPANY_NAME}</h2><p>Please log in to continue</p></div>")
|
258 |
+
error_message = gr.Markdown(visible=False, value="", elem_classes=["error-message"])
|
259 |
+
username = gr.Textbox(label="Username", placeholder="Enter your username")
|
260 |
+
password = gr.Textbox(label="Password", type="password", placeholder="Enter your password")
|
261 |
+
login_button = gr.Button("Login", variant="primary", size="lg")
|
262 |
|
263 |
+
# Chat Group
|
264 |
with gr.Group(visible=False) as chat_group:
|
265 |
with gr.Row():
|
266 |
# Settings Panel
|
267 |
+
with gr.Column(scale=1, elem_classes=["setting-panel"]):
|
268 |
+
role_indicator = gr.Markdown("", elem_id="role-indicator")
|
|
|
|
|
|
|
|
|
|
|
269 |
|
270 |
+
gr.Markdown("### Configuration")
|
271 |
+
with gr.Group() as config_group:
|
272 |
+
system_message = gr.Textbox(
|
273 |
+
value=DEFAULT_SYSTEM_PROMPT,
|
274 |
+
label="System Instructions",
|
275 |
+
lines=4,
|
276 |
+
interactive=False # Initially disabled
|
277 |
+
)
|
278 |
max_tokens = gr.Slider(
|
279 |
minimum=1,
|
280 |
maximum=2048,
|
281 |
value=512,
|
282 |
step=1,
|
283 |
+
label="Max Response Length",
|
284 |
+
interactive=False
|
285 |
)
|
286 |
temperature = gr.Slider(
|
287 |
minimum=0.1,
|
288 |
maximum=1.0,
|
289 |
value=0.7,
|
290 |
step=0.1,
|
291 |
+
label="Temperature",
|
292 |
+
interactive=False
|
293 |
)
|
294 |
top_p = gr.Slider(
|
295 |
minimum=0.1,
|
296 |
maximum=1.0,
|
297 |
value=0.95,
|
298 |
step=0.05,
|
299 |
+
label="Top-p",
|
300 |
+
interactive=False
|
301 |
)
|
302 |
+
|
303 |
+
update_config_btn = gr.Button("Update Configuration", visible=False)
|
304 |
+
config_status = gr.Markdown("")
|
305 |
|
306 |
+
gr.Markdown("### Chat Actions")
|
307 |
+
with gr.Row():
|
308 |
+
clear_btn = gr.Button("Clear Chat", variant="secondary")
|
309 |
+
export_btn = gr.Button("Export Conversation", variant="secondary")
|
310 |
+
|
311 |
+
logout_btn = gr.Button("Logout", variant="stop")
|
312 |
|
313 |
+
# Chat Interface
|
314 |
+
with gr.Column(scale=2, elem_classes=["chat-container"]):
|
315 |
+
chatbot = gr.Chatbot(elem_classes=["chatbox"])
|
316 |
+
with gr.Row():
|
317 |
+
msg = gr.Textbox(
|
318 |
+
show_label=False,
|
319 |
+
placeholder="Type your message here...",
|
320 |
+
container=False
|
321 |
+
)
|
322 |
+
submit_btn = gr.Button("Send", variant="primary")
|
323 |
|
324 |
+
# Event handlers
|
325 |
+
def update_role_display(role):
|
326 |
+
badge_class = "admin-badge" if role == "admin" else "user-badge"
|
327 |
+
role_display = "Administrator" if role == "admin" else "Standard User"
|
328 |
+
return f"<h3>Role: <span class='role-badge {badge_class}'>{role_display}</span></h3>"
|
|
|
|
|
329 |
|
330 |
+
def handle_role_permissions(role):
|
331 |
+
is_admin = role == "admin"
|
332 |
+
return [
|
333 |
+
gr.update(interactive=is_admin), # system_message
|
334 |
+
gr.update(interactive=is_admin), # max_tokens
|
335 |
+
gr.update(interactive=is_admin), # temperature
|
336 |
+
gr.update(interactive=is_admin), # top_p
|
337 |
+
gr.update(visible=is_admin), # update_config_btn
|
338 |
+
]
|
|
|
339 |
|
340 |
+
# Login handler
|
341 |
login_button.click(
|
342 |
login,
|
343 |
inputs=[username, password],
|
344 |
outputs=[login_group, chat_group, user_id, user_role, error_message]
|
345 |
).then(
|
346 |
+
update_role_display,
|
347 |
+
inputs=[user_role],
|
348 |
+
outputs=[role_indicator]
|
349 |
+
).then(
|
350 |
+
handle_role_permissions,
|
351 |
inputs=[user_role],
|
352 |
+
outputs=[system_message, max_tokens, temperature, top_p, update_config_btn]
|
353 |
)
|
354 |
|
355 |
+
# Configuration update handler
|
356 |
+
update_config_btn.click(
|
357 |
+
update_config,
|
358 |
+
inputs=[system_message, max_tokens, temperature, top_p, user_role],
|
359 |
+
outputs=[config_status]
|
360 |
+
)
|
361 |
|
362 |
+
# Chat handlers
|
363 |
msg.submit(
|
364 |
+
respond,
|
365 |
+
inputs=[msg, chatbot, user_id],
|
366 |
outputs=[chatbot]
|
367 |
+
).then(lambda: "", None, [msg])
|
368 |
+
|
369 |
+
submit_btn.click(
|
370 |
+
respond,
|
371 |
+
inputs=[msg, chatbot, user_id],
|
372 |
+
outputs=[chatbot]
|
373 |
+
).then(lambda: "", None, [msg])
|
374 |
|
375 |
# Clear chat
|
376 |
+
clear_btn.click(lambda: [], None, chatbot, queue=False)
|
377 |
|
378 |
+
# Export conversation
|
379 |
+
def export_conversation(chat_history, uid):
|
380 |
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
381 |
+
filename = f"conversations/export_{uid}_{timestamp}.json"
|
382 |
+
os.makedirs(os.path.dirname(filename), exist_ok=True)
|
383 |
+
with open(filename, 'w') as f:
|
384 |
+
json.dump(chat_history, f)
|
385 |
+
logger.info(f"Exported conversation for user {uid}")
|
386 |
+
return gr.update(value=f"Conversation exported to {filename}", visible=True)
|
387 |
+
|
388 |
+
export_btn.click(
|
389 |
+
export_conversation,
|
390 |
+
inputs=[chatbot, user_id],
|
391 |
+
outputs=[error_message]
|
392 |
+
)
|
393 |
+
|
394 |
+
# Logout handler
|
395 |
+
def logout():
|
396 |
+
return gr.update(visible=True), gr.update(visible=False), None, None
|
397 |
+
|
398 |
+
logout_btn.click(
|
399 |
+
logout,
|
400 |
outputs=[login_group, chat_group, user_id, user_role]
|
401 |
)
|
402 |
|
403 |
if __name__ == "__main__":
|
404 |
+
if os.environ.get("PRODUCTION", "false").lower() == "true":
|
405 |
+
demo.launch(
|
406 |
+
server_name="0.0.0.0",
|
407 |
+
server_port=int(os.environ.get("PORT", 7860)),
|
408 |
+
share=False,
|
409 |
+
show_error=False,
|
410 |
+
auth=None,
|
411 |
+
)
|
412 |
+
else:
|
413 |
+
demo.launch(show_error=True)
|