sofiamak commited on
Commit
20cb253
·
verified ·
1 Parent(s): d99ae44

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +242 -34
app.py CHANGED
@@ -11,6 +11,7 @@ APP_TITLE = "孙子你好(DeepSeek 多样化·诊断版)"
11
 
12
  MODEL_NAME = os.getenv("DEEPSEEK_MODEL", "deepseek-chat").strip() or "deepseek-chat"
13
 
 
14
  SYS_PROMPT = (
15
  "你是中文军师,擅长将《孙子兵法》的智慧用于现实问题。\n"
16
  "任务:针对用户的问题,从《孙子兵法》十三篇中找出最贴合的一章,引用原文(若能确定),并转化为现代生活策略。\n\n"
@@ -24,9 +25,13 @@ SYS_PROMPT = (
24
  "2. 若问题重复,尽量换不同篇章或换角度回答,保持多样性。\n"
25
  "3. 若无法百分之百确认原文或篇名,不要硬编,改为思想意译并标记 unsure=true。\n"
26
  "4. 严格仅输出 JSON,固定字段:\n"
27
- " {\"chapter\":\"<篇名>\",\"quote\":\"<原文或思想>\",\"unsure\":false/true,"
28
- "\"insight\":\"<一句人话启示≤24字>\",\"action\":\"<一步可执行做法≤100字>\","
29
- "\"speech\":\"<直接可说的话术≤120字>\"}\n"
 
 
 
 
30
  "5. 语气简洁、机智、委婉、有边界;不得人身攻击、威胁或违法建议。\n"
31
  "6. 禁止输出 JSON 以外的任何文字。"
32
  )
@@ -46,7 +51,8 @@ FEWSHOT = [
46
  "unsure": False,
47
  "insight": "避免持久消耗,保整体战力。",
48
  "action": "提出“明早高质量交付”的替代方案,比熬夜更稳更可靠。",
49
- "speech": "今晚赶出来质量可能不稳,我建议明早整理成终版,保证更可靠,您看可以吗?"
 
50
  }, ensure_ascii=False)
51
  },
52
  # 借钱 → 始计篇
@@ -62,7 +68,8 @@ FEWSHOT = [
62
  "unsure": False,
63
  "insight": "绕开硬催,设局顺势收回。",
64
  "action": "以“清账/安排资金”为由先要部分或分期,降低对抗。",
65
- "speech": "我这边在清账需要安排下,能否先转一部分?分期也行,别影响咱们交情。"
 
66
  }, ensure_ascii=False)
67
  },
68
  # 邻居噪音 → 行军篇
@@ -78,7 +85,8 @@ FEWSHOT = [
78
  "unsure": True, # 行军篇原文无此句,标不确定,演示 unsure 机制
79
  "insight": "先辨来源时机,再择机沟通。",
80
  "action": "选非冲突时段,先体谅再提困扰,争取对方自觉收敛。",
81
- "speech": "最近晚上声音有点明显,我怕打扰到楼上楼下。能否帮忙晚间稍微注意下?谢谢啦~"
 
82
  }, ensure_ascii=False)
83
  },
84
  # 职场竞争 → 军争篇
@@ -94,13 +102,15 @@ FEWSHOT = [
94
  "unsure": False,
95
  "insight": "不硬碰,转势为己用。",
96
  "action": "会前先递交要点纪要和分工佐证,令功劳记录化、流程化。",
97
- "speech": "我把这次我的负责点和关键产出提前发一份纪要,方便会里对齐,避免遗漏。"
 
98
  }, ensure_ascii=False)
99
  }
100
  ]
101
 
 
102
  def call_deepseek(user_question: str, alt: bool = False) -> dict:
103
- """带精确诊断:自动尝试两个端点,页面/日志展示状态码与返回体;加入多样化提示。"""
104
  key = os.getenv("DEEPSEEK_API_KEY", "").strip()
105
  if not key:
106
  return {
@@ -109,7 +119,8 @@ def call_deepseek(user_question: str, alt: bool = False) -> dict:
109
  "unsure": True,
110
  "insight": "请先在 Settings → Secrets 配置密钥。",
111
  "action": "键名必须是 DEEPSEEK_API_KEY,值不要带引号或空格;配置后重启。",
112
- "speech": "密钥配好我就能��常回答。"
 
113
  }
114
 
115
  endpoints = [
@@ -169,7 +180,8 @@ def call_deepseek(user_question: str, alt: bool = False) -> dict:
169
  "unsure": bool(result.get("unsure", False)),
170
  "insight": result.get("insight", "")[:40],
171
  "action": result.get("action", "")[:120],
172
- "speech": result.get("speech", "")[:140]
 
173
  }
174
  except Exception as e:
175
  last_err = f"{type(e).__name__}: {e}"
@@ -180,19 +192,82 @@ def call_deepseek(user_question: str, alt: bool = False) -> dict:
180
  "unsure": True,
181
  "insight": "DeepSeek 请求未成功。",
182
  "action": f"诊断信息:{last_err or '未知错误'}",
183
- "speech": "请按诊断检查:①密钥②端点③模型名④配额/限速,然后重启。"
 
184
  }
185
 
186
- def render(result: dict, strict: bool) -> str:
187
- """strict=True:若 unsure 则隐藏原文与篇名,仅展示启示/做法/话术并提示原因。"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  unsure = bool(result.get("unsure"))
189
  chap = html.escape(result.get("chapter", ""))
190
  quote = html.escape(result.get("quote", ""))
191
  insight = html.escape(result.get("insight", ""))
192
  action = html.escape(result.get("action", ""))
193
- speech = html.escape(result.get("speech", ""))
194
 
195
- show_quote = not (strict and unsure)
 
196
 
197
  parts = ["<div style='font-size:22px;line-height:1.7em;'>"]
198
  if show_quote:
@@ -205,19 +280,57 @@ def render(result: dict, strict: bool) -> str:
205
 
206
  parts.append(f"<b>启示:</b>{insight}<br><br>")
207
  parts.append(f"<b>做法:</b>{action}<br><br>")
208
- parts.append(
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  "<div style='margin-top:12px;padding:12px 14px;border:1px dashed #e2d7f2;"
210
- "border-radius:10px;background:#fbf8ff;'>"
211
- "<div style='font-weight:600;'>💬 话术示例:</div>"
212
  f"<div>{speech}</div>"
213
  "</div>"
214
  )
215
- if not show_quote:
216
- parts.append("<div style='color:#9aa;font-size:13px;margin-top:8px;'>提示:取消勾选“严格出处校验”可查看模型引用。</div>")
217
- parts.append("</div>")
218
- return "".join(parts)
219
 
220
- # ---------------- UI ----------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
  theme = gr.themes.Soft(primary_hue="pink", neutral_hue="slate")
222
  CSS = """
223
  body,.gradio-container{background:#f7f6fb!important;}
@@ -228,28 +341,123 @@ body,.gradio-container{background:#f7f6fb!important;}
228
  with gr.Blocks(title=APP_TITLE, theme=theme, css=CSS) as demo:
229
  gr.HTML("<div class='header'>孙子你好</div>")
230
  with gr.Group(elem_classes=["card"]):
231
- q = gr.Textbox(label="你的问题", placeholder="例如:朋友借钱不还;老板让我加班;邻居太吵;甲方拖款…", lines=3)
232
- strict = gr.Checkbox(value=True, label="严格出处校验(不确定出典时隐藏原文)")
 
 
 
 
 
 
 
233
  with gr.Row():
234
  b1 = gr.Button("🎯 生成兵法", variant="primary")
235
  b2 = gr.Button("♻️ 换一种说法")
236
  b3 = gr.Button("🧹 清空")
237
- out = gr.HTML()
238
 
239
- def on_gen(user_q, strict_flag):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
  res = call_deepseek(user_q, alt=False)
241
- return render(res, strict_flag)
 
 
242
 
243
- def on_alt(user_q, strict_flag):
 
 
 
 
 
 
244
  res = call_deepseek(user_q, alt=True)
245
- return render(res, strict_flag)
 
246
 
247
  def on_clear():
248
- return "", True, ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
 
250
- b1.click(on_gen, inputs=[q, strict], outputs=out)
251
- b2.click(on_alt, inputs=[q, strict], outputs=out)
252
- b3.click(on_clear, inputs=None, outputs=[q, strict, out])
 
 
253
 
254
  if __name__ == "__main__":
255
  demo.launch()
 
11
 
12
  MODEL_NAME = os.getenv("DEEPSEEK_MODEL", "deepseek-chat").strip() or "deepseek-chat"
13
 
14
+ # ===================== 系统提示词(增加 explain_cn 字段) =====================
15
  SYS_PROMPT = (
16
  "你是中文军师,擅长将《孙子兵法》的智慧用于现实问题。\n"
17
  "任务:针对用户的问题,从《孙子兵法》十三篇中找出最贴合的一章,引用原文(若能确定),并转化为现代生活策略。\n\n"
 
25
  "2. 若问题重复,尽量换不同篇章或换角度回答,保持多样性。\n"
26
  "3. 若无法百分之百确认原文或篇名,不要硬编,改为思想意译并标记 unsure=true。\n"
27
  "4. 严格仅输出 JSON,固定字段:\n"
28
+ " {\"chapter\":\"<篇名>\","
29
+ " \"quote\":\"<原文或思想>\","
30
+ " \"unsure\":false/true,"
31
+ " \"insight\":\"<一句人话启示≤24字>\","
32
+ " \"action\":\"<一步可执行做法≤100字>\","
33
+ " \"speech\":\"<直接可说的话术≤120字>\","
34
+ " \"explain_cn\":\"<用非常简单的话解释这句名言的大意≤60字>\"}\n"
35
  "5. 语气简洁、机智、委婉、有边界;不得人身攻击、威胁或违法建议。\n"
36
  "6. 禁止输出 JSON 以外的任何文字。"
37
  )
 
51
  "unsure": False,
52
  "insight": "避免持久消耗,保整体战力。",
53
  "action": "提出“明早高质量交付”的替代方案,比熬夜更稳更可靠。",
54
+ "speech": "今晚赶出来质量可能不稳,我建议明早整理成终版,保证更可靠,您看可以吗?",
55
+ "explain_cn": "意思是战争拖太久没有好处,打仗要快准狠,不能一直消耗。"
56
  }, ensure_ascii=False)
57
  },
58
  # 借钱 → 始计篇
 
68
  "unsure": False,
69
  "insight": "绕开硬催,设局顺势收回。",
70
  "action": "以“清账/安排资金”为由先要部分或分期,降低对抗。",
71
+ "speech": "我这边在清账需要安排下,能否先转一部分?分期也行,别影响咱们交情。",
72
+ "explain_cn": "打仗要讲策略,不是直来直去,要懂得绕弯子、用计谋。"
73
  }, ensure_ascii=False)
74
  },
75
  # 邻居噪音 → 行军篇
 
85
  "unsure": True, # 行军篇原文无此句,标不确定,演示 unsure 机制
86
  "insight": "先辨来源时机,再择机沟通。",
87
  "action": "选非冲突时段,先体谅再提困扰,争取对方自觉收敛。",
88
+ "speech": "最近晚上声音有点明显,我怕打扰到楼上楼下。能否帮忙晚间稍微注意下?谢谢啦~",
89
+ "explain_cn": "先搞清楚对方情况和自己底牌,这样做事比较稳,不容易吃亏。"
90
  }, ensure_ascii=False)
91
  },
92
  # 职场竞争 → 军争篇
 
102
  "unsure": False,
103
  "insight": "不硬碰,转势为己用。",
104
  "action": "会前先递交要点纪要和分工佐证,令功劳记录化、流程化。",
105
+ "speech": "我把这次我的负责点和关键产出提前发一份纪要,方便会里对齐,避免遗漏。",
106
+ "explain_cn": "看起来绕远路,其实是走捷径;把麻烦事变成自己有利的机会。"
107
  }, ensure_ascii=False)
108
  }
109
  ]
110
 
111
+ # ===================== 调用 DeepSeek:主分析 =====================
112
  def call_deepseek(user_question: str, alt: bool = False) -> dict:
113
+ """自动尝试两个端点,返回结构化兵法解析结果。"""
114
  key = os.getenv("DEEPSEEK_API_KEY", "").strip()
115
  if not key:
116
  return {
 
119
  "unsure": True,
120
  "insight": "请先在 Settings → Secrets 配置密钥。",
121
  "action": "键名必须是 DEEPSEEK_API_KEY,值不要带引号或空格;配置后重启。",
122
+ "speech": "密钥配好我就能正常回答。",
123
+ "explain_cn": "当前还无法解释名言,因为没有配置密钥。"
124
  }
125
 
126
  endpoints = [
 
180
  "unsure": bool(result.get("unsure", False)),
181
  "insight": result.get("insight", "")[:40],
182
  "action": result.get("action", "")[:120],
183
+ "speech": result.get("speech", "")[:140],
184
+ "explain_cn": result.get("explain_cn", "")[:80],
185
  }
186
  except Exception as e:
187
  last_err = f"{type(e).__name__}: {e}"
 
192
  "unsure": True,
193
  "insight": "DeepSeek 请求未成功。",
194
  "action": f"诊断信息:{last_err or '未知错误'}",
195
+ "speech": "请按诊断检查:①密钥②端点③模型名④配额/限速,然后重启。",
196
+ "explain_cn": "当前无法给出名言解释,请检查配置。"
197
  }
198
 
199
+ # ===================== 调用 DeepSeek:中译英翻译 =====================
200
+ def translate_to_en(text_cn: str) -> str:
201
+ """使用 DeepSeek 将简单中文解释翻译成英文。"""
202
+ key = os.getenv("DEEPSEEK_API_KEY", "").strip()
203
+ if not key:
204
+ return "⚠️ 未配置 DEEPSEEK_API_KEY,暂时无法翻译成英文。"
205
+
206
+ endpoints = [
207
+ (os.getenv("DEEPSEEK_BASE_URL", "").strip()
208
+ or "https://api.deepseek.com/v1/chat/completions"),
209
+ "https://api.deepseek.com/chat/completions",
210
+ ]
211
+
212
+ msgs = [
213
+ {
214
+ "role": "system",
215
+ "content": "你是专业的中译英助手,只把用户提供的中文翻译成自然简洁的英文,不要添加解释或多余内容。"
216
+ },
217
+ {
218
+ "role": "user",
219
+ "content": text_cn
220
+ }
221
+ ]
222
+
223
+ last_err = None
224
+ for ep in endpoints:
225
+ try:
226
+ payload = {
227
+ "model": MODEL_NAME,
228
+ "messages": msgs,
229
+ "temperature": 0.3,
230
+ "top_p": 0.9,
231
+ "max_tokens": 200
232
+ }
233
+ r = requests.post(
234
+ ep,
235
+ headers={"Authorization": f"Bearer {key}", "Content-Type": "application/json"},
236
+ json=payload,
237
+ timeout=60
238
+ )
239
+ print(f"[DeepSeek-Translate] endpoint={ep} status={r.status_code} model={MODEL_NAME}")
240
+
241
+ if r.status_code // 100 != 2:
242
+ try:
243
+ err_detail = r.json()
244
+ except Exception:
245
+ err_detail = {"raw": r.text[:500]}
246
+ last_err = f"HTTP {r.status_code} @ {ep} (model={MODEL_NAME}) → {err_detail}"
247
+ continue
248
+
249
+ data = r.json()
250
+ content = data.get("choices", [{}])[0].get("message", {}).get("content", "").strip()
251
+ return html.escape(content)
252
+ except Exception as e:
253
+ last_err = f"{type(e).__name__}: {e}"
254
+
255
+ return f"⚠️ 翻译失败:{last_err or '未知错误'}"
256
+
257
+ # ===================== 渲染函数 =====================
258
+ def render_main(result: dict) -> str:
259
+ """
260
+ 主体渲染(不包含话术示例、不包含“我是老外”解释)。
261
+ 严格校验:若 unsure=True 则隐藏原文与篇名。
262
+ """
263
  unsure = bool(result.get("unsure"))
264
  chap = html.escape(result.get("chapter", ""))
265
  quote = html.escape(result.get("quote", ""))
266
  insight = html.escape(result.get("insight", ""))
267
  action = html.escape(result.get("action", ""))
 
268
 
269
+ # 现在默认“严格出处校验”:模型不确定时,隐藏原文与篇名
270
+ show_quote = not unsure
271
 
272
  parts = ["<div style='font-size:22px;line-height:1.7em;'>"]
273
  if show_quote:
 
280
 
281
  parts.append(f"<b>启示:</b>{insight}<br><br>")
282
  parts.append(f"<b>做法:</b>{action}<br><br>")
283
+ parts.append("</div>")
284
+ return "".join(parts)
285
+
286
+
287
+ def render_speech(result: dict) -> str:
288
+ """单独渲染话术示例区域。"""
289
+ speech = html.escape(result.get("speech", "").strip())
290
+ if not speech:
291
+ return (
292
+ "<div style='margin-top:8px;font-size:16px;color:#777;'>"
293
+ "当前没有可用的「话术示例」,请先点击「生成兵法」。"
294
+ "</div>"
295
+ )
296
+ return (
297
  "<div style='margin-top:12px;padding:12px 14px;border:1px dashed #e2d7f2;"
298
+ "border-radius:10px;background:#fbf8ff;font-size:18px;line-height:1.7em;'>"
299
+ "<div style='font-weight:600;margin-bottom:6px;'>💬 话术示例:</div>"
300
  f"<div>{speech}</div>"
301
  "</div>"
302
  )
 
 
 
 
303
 
304
+
305
+ def render_explain_cn(result: dict) -> str:
306
+ """为“我是老外”按钮渲染特别简单的中文解释。"""
307
+ explain_cn = html.escape(result.get("explain_cn", "").strip())
308
+ if not explain_cn:
309
+ return (
310
+ "<div style='margin-top:8px;font-size:16px;color:#777;'>"
311
+ "暂时没有找到这句名言的简单解释,请先生成一次兵法再试。"
312
+ "</div>"
313
+ )
314
+ return (
315
+ "<div style='margin-top:12px;padding:12px 14px;border:1px solid #d7e6ff;"
316
+ "border-radius:10px;background:#f5f8ff;font-size:18px;line-height:1.7em;'>"
317
+ "<div style='font-weight:600;margin-bottom:6px;'>🌏 给外国朋友看的超简解释:</div>"
318
+ f"<div>{explain_cn}</div>"
319
+ "</div>"
320
+ )
321
+
322
+
323
+ def render_explain_en(text_en: str) -> str:
324
+ """渲染英文翻译结果。"""
325
+ return (
326
+ "<div style='margin-top:8px;padding:12px 14px;border:1px dashed #cddfea;"
327
+ "border-radius:10px;background:#f6f9ff;font-size:18px;line-height:1.7em;'>"
328
+ "<div style='font-weight:600;margin-bottom:6px;'>🔁 英文解释:</div>"
329
+ f"<div>{text_en}</div>"
330
+ "</div>"
331
+ )
332
+
333
+ # ===================== UI =====================
334
  theme = gr.themes.Soft(primary_hue="pink", neutral_hue="slate")
335
  CSS = """
336
  body,.gradio-container{background:#f7f6fb!important;}
 
341
  with gr.Blocks(title=APP_TITLE, theme=theme, css=CSS) as demo:
342
  gr.HTML("<div class='header'>孙子你好</div>")
343
  with gr.Group(elem_classes=["card"]):
344
+ q = gr.Textbox(
345
+ label="你的问题",
346
+ placeholder="例如:朋友借钱不还;老板让我加班;邻居太吵;甲方拖款…",
347
+ lines=3
348
+ )
349
+
350
+ # 保存最近一次 DeepSeek 返回的结构化结果,用于“话术示例”和“我是老外”
351
+ state_result = gr.State({})
352
+
353
  with gr.Row():
354
  b1 = gr.Button("🎯 生成兵法", variant="primary")
355
  b2 = gr.Button("♻️ 换一种说法")
356
  b3 = gr.Button("🧹 清空")
 
357
 
358
+ with gr.Row():
359
+ btn_speech = gr.Button("💬 话术示例")
360
+ btn_foreign = gr.Button("🌏 我是老外")
361
+ btn_translate = gr.Button("🔁 翻译成英文")
362
+
363
+ out_main = gr.HTML()
364
+ out_speech = gr.HTML()
365
+ out_explain_cn = gr.HTML()
366
+ out_explain_en = gr.HTML()
367
+
368
+ # ---------- 回调函数 ----------
369
+ def on_gen(user_q):
370
+ if not (user_q or "").strip():
371
+ empty_msg = (
372
+ "<div style='font-size:16px;color:#777;'>"
373
+ "请先在上面输入你的问题,比如:朋友借钱不还怎么办?</div>"
374
+ )
375
+ return empty_msg, "", "", "", {}
376
  res = call_deepseek(user_q, alt=False)
377
+ main_html = render_main(res)
378
+ # 生成新结果时,清空话术示例和解释区域
379
+ return main_html, "", "", "", res
380
 
381
+ def on_alt(user_q, cur_state):
382
+ if not (user_q or "").strip():
383
+ empty_msg = (
384
+ "<div style='font-size:16px;color:#777;'>"
385
+ "请先在上面输入你的问题,比如:朋友借钱不还怎么办?</div>"
386
+ )
387
+ return empty_msg, "", "", "", {}
388
  res = call_deepseek(user_q, alt=True)
389
+ main_html = render_main(res)
390
+ return main_html, "", "", "", res
391
 
392
  def on_clear():
393
+ # 清空输入框和所有输出 & 状态
394
+ return "", "", "", "", "", {}
395
+
396
+ def on_show_speech(res):
397
+ if not isinstance(res, dict) or not res:
398
+ return (
399
+ "<div style='margin-top:8px;font-size:16px;color:#777;'>"
400
+ "请先点击「生成兵法」,再查看话术示例。</div>"
401
+ )
402
+ return render_speech(res)
403
+
404
+ def on_show_foreign(res):
405
+ if not isinstance(res, dict) or not res:
406
+ return (
407
+ "<div style='margin-top:8px;font-size:16px;color:#777;'>"
408
+ "请先点击「生成兵法」,再点「我是老外」。</div>"
409
+ )
410
+ return render_explain_cn(res)
411
+
412
+ def on_translate(res):
413
+ if not isinstance(res, dict) or not res:
414
+ return (
415
+ "<div style='margin-top:8px;font-size:16px;color:#777;'>"
416
+ "请先生成兵法并点击「我是老外」后,再翻译成英文。</div>"
417
+ )
418
+ text_cn = res.get("explain_cn", "").strip()
419
+ if not text_cn:
420
+ return (
421
+ "<div style='margin-top:8px;font-size:16px;color:#777;'>"
422
+ "当前没有可以翻译的中文解释,请先点「我是老外」。</div>"
423
+ )
424
+ text_en = translate_to_en(text_cn)
425
+ return render_explain_en(text_en)
426
+
427
+ # ---------- 事件绑定 ----------
428
+ b1.click(
429
+ on_gen,
430
+ inputs=[q],
431
+ outputs=[out_main, out_speech, out_explain_cn, out_explain_en, state_result]
432
+ )
433
+ b2.click(
434
+ on_alt,
435
+ inputs=[q, state_result],
436
+ outputs=[out_main, out_speech, out_explain_cn, out_explain_en, state_result]
437
+ )
438
+ b3.click(
439
+ on_clear,
440
+ inputs=None,
441
+ outputs=[q, out_main, out_speech, out_explain_cn, out_explain_en, state_result]
442
+ )
443
+
444
+ btn_speech.click(
445
+ on_show_speech,
446
+ inputs=[state_result],
447
+ outputs=[out_speech]
448
+ )
449
+
450
+ btn_foreign.click(
451
+ on_show_foreign,
452
+ inputs=[state_result],
453
+ outputs=[out_explain_cn]
454
+ )
455
 
456
+ btn_translate.click(
457
+ on_translate,
458
+ inputs=[state_result],
459
+ outputs=[out_explain_en]
460
+ )
461
 
462
  if __name__ == "__main__":
463
  demo.launch()