Jiawei Dong commited on
Commit
9a40f1b
·
1 Parent(s): 067d7b2

eng updated

Browse files
Files changed (4) hide show
  1. README.md +1 -16
  2. __pycache__/api_client.cpython-310.pyc +0 -0
  3. api_client.py +3 -3
  4. app.py +76 -72
README.md CHANGED
@@ -18,24 +18,9 @@ Patent translation demo for the public HIRO Translation API.
18
 
19
  - **stream** — SSE streaming (`textOriginal` / `textTranslated` per segment)
20
  - **fast** — single JSON with full `textTranslated`
21
- - Default gateway: `https://connect.zhihuiya.com/hiro_translation`
22
  - Auth: Bearer token via **Advanced → API Key** or env `HIRO_API_KEY`
23
 
24
- Request body (aligned with terminal curl):
25
-
26
- ```json
27
- {
28
- "content": "本发明公开了一种档案管理文件储存用分类标识装置。",
29
- "sourceLanguageCode": "zh",
30
- "targetLanguageCode": "en",
31
- "mode": "stream"
32
- }
33
- ```
34
-
35
- Internal stage URL (optional in Advanced):
36
-
37
- `http://stage-s-patsnaprd-hiro-translation-api-internet.patsnap.info/compute/hiro_translation_api`
38
-
39
  ## Files
40
 
41
  | File | Purpose |
 
18
 
19
  - **stream** — SSE streaming (`textOriginal` / `textTranslated` per segment)
20
  - **fast** — single JSON with full `textTranslated`
21
+ - Gateway (fixed): `https://connect.zhihuiya.com/hiro_translation`
22
  - Auth: Bearer token via **Advanced → API Key** or env `HIRO_API_KEY`
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  ## Files
25
 
26
  | File | Purpose |
__pycache__/api_client.cpython-310.pyc CHANGED
Binary files a/__pycache__/api_client.cpython-310.pyc and b/__pycache__/api_client.cpython-310.pyc differ
 
api_client.py CHANGED
@@ -73,11 +73,11 @@ def health_ok(
73
  r.raise_for_status()
74
  data = r.json()
75
  if data.get("status") != "OK":
76
- return False, f"异常响应: {r.text[:200]}"
77
  upstream = data.get("upstream", "UNKNOWN")
78
  if upstream == "OK":
79
- return True, "服务正常 · 上游 OK"
80
- return False, f"网关在线,上游不可用 ({upstream})"
81
  except requests.RequestException as exc:
82
  return False, str(exc)
83
 
 
73
  r.raise_for_status()
74
  data = r.json()
75
  if data.get("status") != "OK":
76
+ return False, f"Unexpected response: {r.text[:200]}"
77
  upstream = data.get("upstream", "UNKNOWN")
78
  if upstream == "OK":
79
+ return True, "Healthy · upstream OK"
80
+ return False, f"Gateway up, upstream unavailable ({upstream})"
81
  except requests.RequestException as exc:
82
  return False, str(exc)
83
 
app.py CHANGED
@@ -193,7 +193,7 @@ RESULTS_SHELL_HTML = """
193
  <div class="results-stack">
194
  <textarea class="hiro-full-translation" readonly aria-hidden="true" tabindex="-1"></textarea>
195
  <div class="progress-section">
196
- <div class="progress-meta" id="hiro-progress-meta">就绪</div>
197
  <div class="progress-track">
198
  <div class="progress-fill" id="hiro-progress-fill" style="width:0%"></div>
199
  </div>
@@ -202,31 +202,31 @@ RESULTS_SHELL_HTML = """
202
  <div class="pairs-wrap" id="hiro-pairs-scroll">
203
  <div id="hiro-pairs-header"></div>
204
  <div id="hiro-pairs-body">
205
- <div class="empty-hint">翻译结果将显示在此处</div>
206
  </div>
207
  </div>
208
  </div>
209
  """
210
 
211
  LANG_OPTIONS = [
212
- ("中文", "zh"),
213
- ("英语", "en"),
214
- ("日语", "ja"),
215
- ("韩语", "ko"),
216
- ("德语", "de"),
217
- ("法语", "fr"),
218
- ("俄语", "ru"),
219
- ("西班牙语", "es"),
220
- ("葡萄牙语", "pt"),
221
- ("意大利语", "it"),
222
- ("荷兰语", "nl"),
223
- ("阿拉伯语", "ar"),
224
- ("印地语", "hi"),
225
- ("泰语", "th"),
226
- ("越南语", "vi"),
227
- ("印尼语", "id"),
228
- ("土耳其语", "tr"),
229
- ("波兰语", "pl"),
230
  ]
231
 
232
  EXAMPLES = [
@@ -266,24 +266,24 @@ def _has_health_endpoint(base: str) -> bool:
266
  return "/compute/hiro_translation_api" in base
267
 
268
 
269
- def check_service(api_base: str, api_key: str = "") -> str:
270
- base = _resolve_base(api_base)
271
 
272
  if not _has_health_endpoint(base):
273
  return (
274
- f'<span class="status-pill ok">● 公开网关<br>'
275
- f'<span style="opacity:0.85">调用时以 Bearer 鉴权 · {html.escape(base)}</span>'
276
  f"</span>"
277
  )
278
 
279
  ok, msg = health_ok(base, api_key=api_key or None)
280
  if ok:
281
  return (
282
- f'<span class="status-pill ok">● 在线<br>'
283
  f'<span style="opacity:0.85">{html.escape(base)}</span></span>'
284
  )
285
  return (
286
- f'<span class="status-pill err">● 不可用<br>'
287
  f'<span style="opacity:0.85">{html.escape(msg)}</span></span>'
288
  )
289
 
@@ -352,7 +352,7 @@ def _empty_outputs():
352
  full="",
353
  lang="zh2en",
354
  percent=0,
355
- status="等待输入…",
356
  reset=True,
357
  )
358
  return _pack_outputs(stream_payload=payload, panel_visible=False, copy_enabled=False)
@@ -389,7 +389,7 @@ COPY_TRANSLATION_JS = """
389
  const ta = document.querySelector(".hiro-full-translation");
390
  const text = ta ? ta.value : "";
391
  if (!String(text).trim()) {
392
- alert("暂无译文可复制");
393
  return [];
394
  }
395
  const copyFallback = (value) => {
@@ -407,7 +407,7 @@ COPY_TRANSLATION_JS = """
407
  if (!btn) return;
408
  const label = btn.querySelector("button") || btn;
409
  const orig = label.textContent;
410
- label.textContent = "已复制";
411
  setTimeout(() => { label.textContent = orig; }, 1500);
412
  };
413
  if (navigator.clipboard && window.isSecureContext) {
@@ -451,7 +451,7 @@ UPDATE_RESULTS_JS = """
451
  const tgtCode = (parts[1] || "en").toLowerCase();
452
  const src = codeMap[srcCode] || srcCode.toUpperCase();
453
  const tgt = codeMap[tgtCode] || tgtCode.toUpperCase();
454
- return { src: `原文 (${src})`, tgt: `译文 (${tgt})` };
455
  };
456
 
457
  let state;
@@ -502,7 +502,7 @@ UPDATE_RESULTS_JS = """
502
 
503
  if (totalVisible === 0 && store.count === 0) {
504
  if (!body.querySelector(".empty-hint")) {
505
- body.innerHTML = '<div class="empty-hint">翻译结果将显示在此处</div>';
506
  }
507
  } else {
508
  const hint = body.querySelector(".empty-hint");
@@ -519,7 +519,7 @@ UPDATE_RESULTS_JS = """
519
  body.insertAdjacentHTML(
520
  "beforeend",
521
  `<div class="pair-block" data-idx="${displayIdx}">
522
- <div class="pair-idx"> ${displayIdx}</div>
523
  <div class="pair-row">
524
  <div class="pair-cell src">${esc(orig)}</div>
525
  <div class="pair-cell tgt">${esc(trans)}</div>
@@ -539,8 +539,8 @@ UPDATE_RESULTS_JS = """
539
  """
540
 
541
  INIT_STREAM_WATCH_JS = """
542
- (api_base) => {
543
- if (window.__hiroStreamWatch) return [api_base];
544
  window.__hiroStreamWatch = true;
545
 
546
  const bind = () => {
@@ -564,18 +564,18 @@ INIT_STREAM_WATCH_JS = """
564
  });
565
  boot.observe(document.body, { childList: true, subtree: true });
566
  }
567
- return [api_base];
568
  }
569
  """
570
 
571
  RESET_STREAM_JS = """
572
- (text, lang, mode, api_base) => {
573
  if (window.__hiroResults) {
574
  window.__hiroResults.count = 0;
575
  window.__hiroResults.layout = null;
576
  window.__hiroResults.lastJson = "";
577
  }
578
- return [text, lang, mode, api_base];
579
  }
580
  """
581
 
@@ -591,13 +591,13 @@ CLEAR_STREAM_JS = """
591
  """
592
 
593
 
594
- def run_translation(text: str, lang: str, mode: str, api_base: str, api_key: str = ""):
595
  text = (text or "").strip()
596
  if not text:
597
  yield _empty_outputs()
598
  return
599
 
600
- base = _resolve_base(api_base)
601
  auth = api_key or None
602
  pairs: list[tuple[str, str]] = []
603
  t0 = time.perf_counter()
@@ -621,7 +621,7 @@ def run_translation(text: str, lang: str, mode: str, api_base: str, api_key: str
621
  reset=reset,
622
  )
623
 
624
- yield emit(percent=0, status="正在连接网关…", reset=True)
625
 
626
  try:
627
  if mode == "fast":
@@ -630,7 +630,7 @@ def run_translation(text: str, lang: str, mode: str, api_base: str, api_key: str
630
  trans = data.get("text_translated", "")
631
  elapsed = time.perf_counter() - t0
632
  pairs = [(orig, trans)]
633
- status = f"完成 · fast 模式 · {elapsed:.1f}s"
634
  out_chars = data.get("translated_character_count")
635
  if out_chars is not None:
636
  status += f" · {out_chars} chars"
@@ -646,7 +646,11 @@ def run_translation(text: str, lang: str, mode: str, api_base: str, api_key: str
646
  last_progress = chunk.get("progress") or last_progress
647
  pct = _progress_percent(last_progress, len(pairs))
648
  total = _parse_progress(last_progress)[1]
649
- desc = f"翻译中 {last_progress}" if total else f"已收到 {len(pairs)} 段"
 
 
 
 
650
  elapsed = time.perf_counter() - t0
651
 
652
  visible = _pairs_for_display(pairs)
@@ -658,7 +662,7 @@ def run_translation(text: str, lang: str, mode: str, api_base: str, api_key: str
658
  ):
659
  out = emit(
660
  percent=pct,
661
- status=f"流式翻译中 · {desc} · {elapsed:.1f}s",
662
  )
663
  if out is not None:
664
  yield out
@@ -666,7 +670,9 @@ def run_translation(text: str, lang: str, mode: str, api_base: str, api_key: str
666
  elapsed = time.perf_counter() - t0
667
  yield emit(
668
  percent=100,
669
- status=f"完成 · 共 {len(pairs)} 段 · {elapsed:.1f}s · {last_progress}",
 
 
670
  force=True,
671
  )
672
 
@@ -678,7 +684,7 @@ def run_translation(text: str, lang: str, mode: str, api_base: str, api_key: str
678
  full="",
679
  lang=lang,
680
  percent=0,
681
- status=f"失败 · {elapsed:.1f}s · {err}",
682
  reset=True,
683
  )
684
  yield _pack_outputs(stream_payload=payload, panel_visible=True, copy_enabled=False)
@@ -703,44 +709,40 @@ def build_ui() -> gr.Blocks:
703
  ) as demo:
704
  with gr.Sidebar():
705
  gr.HTML('<p class="main-title">HIRO Translation</p>')
706
- gr.Markdown("专利翻译 · `connect.zhihuiya.com` / `hiro-translation-api`")
 
 
707
 
708
  gateway_status = gr.HTML(value=check_service(DEFAULT_BASE))
709
- refresh_btn = gr.Button("检查网关", size="sm", variant="secondary")
710
 
711
  with gr.Row():
712
  source_lang = gr.Dropdown(
713
  choices=LANG_OPTIONS,
714
  value="zh",
715
- label="源语言",
716
  )
717
  target_lang = gr.Dropdown(
718
  choices=LANG_OPTIONS,
719
  value="en",
720
- label="目标语言",
721
  )
722
  lang = gr.Textbox(value="zh2en", visible=False)
723
  mode = gr.Radio(
724
  choices=["stream", "fast"],
725
  value="stream",
726
- label="调用模式",
727
- info="stream:流式分段;fast:一次返回完整译文",
728
  )
729
 
730
- with gr.Accordion("高级选项", open=False):
731
- api_base = gr.Textbox(
732
- label="API Base URL",
733
- value=DEFAULT_BASE,
734
- placeholder=DEFAULT_BASE,
735
- info="公开网关默认 https://connect.zhihuiya.com/hiro_translation;内网可填 stage 地址",
736
- )
737
  api_key = gr.Textbox(
738
  label="API Key (Bearer)",
739
  type="password",
740
- placeholder="sk-... 或设置环境变量 HIRO_API_KEY",
741
  )
742
 
743
- gr.Markdown("**快速示例**")
744
  example_btns: list[tuple[gr.Button, str, str, str]] = []
745
  for i, (sample, src, tgt) in enumerate(EXAMPLES, start=1):
746
  preview = sample.replace("\n", " ")[:36]
@@ -751,16 +753,19 @@ def build_ui() -> gr.Blocks:
751
 
752
  with gr.Column():
753
  input_text = gr.Textbox(
754
- label="待翻译文本",
755
- placeholder="输入专利相关文本,支持中英日韩及多语种互译",
 
 
 
756
  lines=8,
757
  elem_id="input-box",
758
  )
759
  with gr.Row(elem_classes=["toolbar-row"]):
760
- submit_btn = gr.Button("开始翻译", variant="primary", scale=0)
761
- clear_btn = gr.Button("清空", scale=0)
762
  copy_btn = gr.Button(
763
- "复制完整译文",
764
  variant="secondary",
765
  interactive=False,
766
  scale=0,
@@ -775,15 +780,14 @@ def build_ui() -> gr.Blocks:
775
  full="",
776
  lang="zh2en",
777
  percent=0,
778
- status="就绪",
779
  ),
780
  visible=False,
781
  elem_id="hiro-stream-state",
782
  )
783
 
784
- refresh_btn.click(check_service, inputs=[api_base, api_key], outputs=[gateway_status])
785
- api_base.change(check_service, inputs=[api_base, api_key], outputs=[gateway_status])
786
- api_key.change(check_service, inputs=[api_base, api_key], outputs=[gateway_status])
787
 
788
  def _combine_lang(src: str, tgt: str) -> str:
789
  return f"{src}2{tgt}"
@@ -803,7 +807,7 @@ def build_ui() -> gr.Blocks:
803
 
804
  submit_btn.click(
805
  fn=run_translation,
806
- inputs=[input_text, lang, mode, api_base, api_key],
807
  outputs=[stream_state, results_panel, copy_btn],
808
  js=RESET_STREAM_JS,
809
  )
@@ -830,7 +834,7 @@ def build_ui() -> gr.Blocks:
830
  full="",
831
  lang="zh2en",
832
  percent=0,
833
- status="等待输入…",
834
  reset=True,
835
  )
836
  return "", *_pack_outputs(
@@ -845,7 +849,7 @@ def build_ui() -> gr.Blocks:
845
 
846
  demo.load(
847
  check_service,
848
- inputs=[api_base, api_key],
849
  outputs=[gateway_status],
850
  js=INIT_STREAM_WATCH_JS,
851
  )
 
193
  <div class="results-stack">
194
  <textarea class="hiro-full-translation" readonly aria-hidden="true" tabindex="-1"></textarea>
195
  <div class="progress-section">
196
+ <div class="progress-meta" id="hiro-progress-meta">Ready</div>
197
  <div class="progress-track">
198
  <div class="progress-fill" id="hiro-progress-fill" style="width:0%"></div>
199
  </div>
 
202
  <div class="pairs-wrap" id="hiro-pairs-scroll">
203
  <div id="hiro-pairs-header"></div>
204
  <div id="hiro-pairs-body">
205
+ <div class="empty-hint">Translation results will appear here</div>
206
  </div>
207
  </div>
208
  </div>
209
  """
210
 
211
  LANG_OPTIONS = [
212
+ ("Chinese", "zh"),
213
+ ("English", "en"),
214
+ ("Japanese", "ja"),
215
+ ("Korean", "ko"),
216
+ ("German", "de"),
217
+ ("French", "fr"),
218
+ ("Russian", "ru"),
219
+ ("Spanish", "es"),
220
+ ("Portuguese", "pt"),
221
+ ("Italian", "it"),
222
+ ("Dutch", "nl"),
223
+ ("Arabic", "ar"),
224
+ ("Hindi", "hi"),
225
+ ("Thai", "th"),
226
+ ("Vietnamese", "vi"),
227
+ ("Indonesian", "id"),
228
+ ("Turkish", "tr"),
229
+ ("Polish", "pl"),
230
  ]
231
 
232
  EXAMPLES = [
 
266
  return "/compute/hiro_translation_api" in base
267
 
268
 
269
+ def check_service(api_key: str = "") -> str:
270
+ base = _resolve_base(DEFAULT_BASE)
271
 
272
  if not _has_health_endpoint(base):
273
  return (
274
+ f'<span class="status-pill ok">● Public gateway<br>'
275
+ f'<span style="opacity:0.85">Bearer auth on request · {html.escape(base)}</span>'
276
  f"</span>"
277
  )
278
 
279
  ok, msg = health_ok(base, api_key=api_key or None)
280
  if ok:
281
  return (
282
+ f'<span class="status-pill ok">● Online<br>'
283
  f'<span style="opacity:0.85">{html.escape(base)}</span></span>'
284
  )
285
  return (
286
+ f'<span class="status-pill err">● Unavailable<br>'
287
  f'<span style="opacity:0.85">{html.escape(msg)}</span></span>'
288
  )
289
 
 
352
  full="",
353
  lang="zh2en",
354
  percent=0,
355
+ status="Waiting for input…",
356
  reset=True,
357
  )
358
  return _pack_outputs(stream_payload=payload, panel_visible=False, copy_enabled=False)
 
389
  const ta = document.querySelector(".hiro-full-translation");
390
  const text = ta ? ta.value : "";
391
  if (!String(text).trim()) {
392
+ alert("Nothing to copy yet");
393
  return [];
394
  }
395
  const copyFallback = (value) => {
 
407
  if (!btn) return;
408
  const label = btn.querySelector("button") || btn;
409
  const orig = label.textContent;
410
+ label.textContent = "Copied";
411
  setTimeout(() => { label.textContent = orig; }, 1500);
412
  };
413
  if (navigator.clipboard && window.isSecureContext) {
 
451
  const tgtCode = (parts[1] || "en").toLowerCase();
452
  const src = codeMap[srcCode] || srcCode.toUpperCase();
453
  const tgt = codeMap[tgtCode] || tgtCode.toUpperCase();
454
+ return { src: `Source (${src})`, tgt: `Target (${tgt})` };
455
  };
456
 
457
  let state;
 
502
 
503
  if (totalVisible === 0 && store.count === 0) {
504
  if (!body.querySelector(".empty-hint")) {
505
+ body.innerHTML = '<div class="empty-hint">Translation results will appear here</div>';
506
  }
507
  } else {
508
  const hint = body.querySelector(".empty-hint");
 
519
  body.insertAdjacentHTML(
520
  "beforeend",
521
  `<div class="pair-block" data-idx="${displayIdx}">
522
+ <div class="pair-idx">Segment ${displayIdx}</div>
523
  <div class="pair-row">
524
  <div class="pair-cell src">${esc(orig)}</div>
525
  <div class="pair-cell tgt">${esc(trans)}</div>
 
539
  """
540
 
541
  INIT_STREAM_WATCH_JS = """
542
+ () => {
543
+ if (window.__hiroStreamWatch) return [];
544
  window.__hiroStreamWatch = true;
545
 
546
  const bind = () => {
 
564
  });
565
  boot.observe(document.body, { childList: true, subtree: true });
566
  }
567
+ return [];
568
  }
569
  """
570
 
571
  RESET_STREAM_JS = """
572
+ (text, lang, mode, api_key) => {
573
  if (window.__hiroResults) {
574
  window.__hiroResults.count = 0;
575
  window.__hiroResults.layout = null;
576
  window.__hiroResults.lastJson = "";
577
  }
578
+ return [text, lang, mode, api_key];
579
  }
580
  """
581
 
 
591
  """
592
 
593
 
594
+ def run_translation(text: str, lang: str, mode: str, api_key: str = ""):
595
  text = (text or "").strip()
596
  if not text:
597
  yield _empty_outputs()
598
  return
599
 
600
+ base = _resolve_base(DEFAULT_BASE)
601
  auth = api_key or None
602
  pairs: list[tuple[str, str]] = []
603
  t0 = time.perf_counter()
 
621
  reset=reset,
622
  )
623
 
624
+ yield emit(percent=0, status="Connecting to gateway…", reset=True)
625
 
626
  try:
627
  if mode == "fast":
 
630
  trans = data.get("text_translated", "")
631
  elapsed = time.perf_counter() - t0
632
  pairs = [(orig, trans)]
633
+ status = f"Done · fast mode · {elapsed:.1f}s"
634
  out_chars = data.get("translated_character_count")
635
  if out_chars is not None:
636
  status += f" · {out_chars} chars"
 
646
  last_progress = chunk.get("progress") or last_progress
647
  pct = _progress_percent(last_progress, len(pairs))
648
  total = _parse_progress(last_progress)[1]
649
+ desc = (
650
+ f"Translating {last_progress}"
651
+ if total
652
+ else f"Received {len(pairs)} segment(s)"
653
+ )
654
  elapsed = time.perf_counter() - t0
655
 
656
  visible = _pairs_for_display(pairs)
 
662
  ):
663
  out = emit(
664
  percent=pct,
665
+ status=f"Streaming · {desc} · {elapsed:.1f}s",
666
  )
667
  if out is not None:
668
  yield out
 
670
  elapsed = time.perf_counter() - t0
671
  yield emit(
672
  percent=100,
673
+ status=(
674
+ f"Done · {len(pairs)} segment(s) · {elapsed:.1f}s · {last_progress}"
675
+ ),
676
  force=True,
677
  )
678
 
 
684
  full="",
685
  lang=lang,
686
  percent=0,
687
+ status=f"Failed · {elapsed:.1f}s · {err}",
688
  reset=True,
689
  )
690
  yield _pack_outputs(stream_payload=payload, panel_visible=True, copy_enabled=False)
 
709
  ) as demo:
710
  with gr.Sidebar():
711
  gr.HTML('<p class="main-title">HIRO Translation</p>')
712
+ gr.Markdown(
713
+ "Patent translation · `connect.zhihuiya.com` / `hiro-translation-api`"
714
+ )
715
 
716
  gateway_status = gr.HTML(value=check_service(DEFAULT_BASE))
717
+ refresh_btn = gr.Button("Check gateway", size="sm", variant="secondary")
718
 
719
  with gr.Row():
720
  source_lang = gr.Dropdown(
721
  choices=LANG_OPTIONS,
722
  value="zh",
723
+ label="Source language",
724
  )
725
  target_lang = gr.Dropdown(
726
  choices=LANG_OPTIONS,
727
  value="en",
728
+ label="Target language",
729
  )
730
  lang = gr.Textbox(value="zh2en", visible=False)
731
  mode = gr.Radio(
732
  choices=["stream", "fast"],
733
  value="stream",
734
+ label="Mode",
735
+ info="stream: segment streaming; fast: single full response",
736
  )
737
 
738
+ with gr.Accordion("Advanced", open=False):
 
 
 
 
 
 
739
  api_key = gr.Textbox(
740
  label="API Key (Bearer)",
741
  type="password",
742
+ placeholder="sk-... or set HIRO_API_KEY",
743
  )
744
 
745
+ gr.Markdown("**Examples**")
746
  example_btns: list[tuple[gr.Button, str, str, str]] = []
747
  for i, (sample, src, tgt) in enumerate(EXAMPLES, start=1):
748
  preview = sample.replace("\n", " ")[:36]
 
753
 
754
  with gr.Column():
755
  input_text = gr.Textbox(
756
+ label="Source text",
757
+ placeholder=(
758
+ "Enter patent-related text. Supports Chinese, English, "
759
+ "Japanese, Korean, and more."
760
+ ),
761
  lines=8,
762
  elem_id="input-box",
763
  )
764
  with gr.Row(elem_classes=["toolbar-row"]):
765
+ submit_btn = gr.Button("Translate", variant="primary", scale=0)
766
+ clear_btn = gr.Button("Clear", scale=0)
767
  copy_btn = gr.Button(
768
+ "Copy full translation",
769
  variant="secondary",
770
  interactive=False,
771
  scale=0,
 
780
  full="",
781
  lang="zh2en",
782
  percent=0,
783
+ status="Ready",
784
  ),
785
  visible=False,
786
  elem_id="hiro-stream-state",
787
  )
788
 
789
+ refresh_btn.click(check_service, inputs=[api_key], outputs=[gateway_status])
790
+ api_key.change(check_service, inputs=[api_key], outputs=[gateway_status])
 
791
 
792
  def _combine_lang(src: str, tgt: str) -> str:
793
  return f"{src}2{tgt}"
 
807
 
808
  submit_btn.click(
809
  fn=run_translation,
810
+ inputs=[input_text, lang, mode, api_key],
811
  outputs=[stream_state, results_panel, copy_btn],
812
  js=RESET_STREAM_JS,
813
  )
 
834
  full="",
835
  lang="zh2en",
836
  percent=0,
837
+ status="Waiting for input…",
838
  reset=True,
839
  )
840
  return "", *_pack_outputs(
 
849
 
850
  demo.load(
851
  check_service,
852
+ inputs=[api_key],
853
  outputs=[gateway_status],
854
  js=INIT_STREAM_WATCH_JS,
855
  )