xxixx1028 commited on
Commit
f6192f8
·
verified ·
1 Parent(s): 0e75ffe

Honest-failure scoring (no demo fallback) + radar/resilience fixes

Browse files
Files changed (3) hide show
  1. ui/app.py +32 -31
  2. ui/screens/processing.py +7 -0
  3. ui/theme.py +5 -0
ui/app.py CHANGED
@@ -97,49 +97,51 @@ def run(name, parsed, rm_flag):
97
  # history is thousands of model calls, so run it in a worker thread and stream a REAL progress bar
98
  # (ticked per completed call) rather than freezing the screen. A live failure degrades to the dummy.
99
  scorer = get_scorer()
100
- progress = None
101
- try:
102
- from prompt_card.observable_pipeline import Progress
103
- if type(scorer).__name__ == "ObservableScorer":
104
- progress = Progress()
105
- except Exception:
106
- progress = None
107
 
 
 
108
  box: dict = {}
 
109
  def _work():
110
  try:
111
  box["result"] = scorer.score(parsed, progress=progress)
112
- box["used"] = scorer
113
- except Exception:
114
- from .scoring import DummyScorer
115
- d = DummyScorer()
116
- box["result"] = d.score(parsed)
117
- box["used"] = d
118
 
119
  bar = len(lines)
120
- lines.append(P.progress_bar(0, progress.total if progress else 0))
121
  yield _frame(lines, intake_v=HIDE, proc_v=SHOW, result_v=HIDE)
122
 
123
- if progress is not None:
124
- th = threading.Thread(target=_work, daemon=True)
125
- th.start()
126
- while th.is_alive():
127
- done, total = progress.snapshot()
128
- lines[bar] = P.progress_bar(done, total)
129
- yield _frame(lines, intake_v=HIDE, proc_v=SHOW, result_v=HIDE)
130
- time.sleep(0.4)
131
- th.join()
132
  done, total = progress.snapshot()
133
- lines[bar] = P.progress_bar(total or done, total or done) # snap to 100%
134
  yield _frame(lines, intake_v=HIDE, proc_v=SHOW, result_v=HIDE)
135
- else:
136
- _work() # placeholder scorer is instant — no thread/progress needed
137
- lines[bar] = P.progress_bar(1, 1)
 
 
 
 
 
 
 
 
 
 
138
 
139
  result = box["result"]
140
- used = box["used"]
141
  card = score_to_card(result, name)
142
- card.provenance = _provenance(used)
143
  card.single_conversation = (getattr(parsed, "source", "") == "paste")
144
  body_updates = [gr.update(value=render_accordion_body(a, card.provenance)) for a in card.axes]
145
  yield _frame(lines, intake_v=HIDE, proc_v=HIDE, result_v=SHOW,
@@ -154,8 +156,7 @@ def run_paste(name, pasted, rm_flag):
154
  text = pasted or ""
155
  parsed = fetch_share(text) if is_share_url(text) else parse_paste(text)
156
  except ParseError as e:
157
- msg = P.fact(str(e), muted=True)
158
- yield _frame([msg], intake_v=SHOW, proc_v=HIDE, result_v=HIDE)
159
  return
160
  yield from run(name, parsed, rm_flag)
161
 
 
97
  # history is thousands of model calls, so run it in a worker thread and stream a REAL progress bar
98
  # (ticked per completed call) rather than freezing the screen. A live failure degrades to the dummy.
99
  scorer = get_scorer()
100
+ # HONEST FAILURE: never show fake "demo" scores. If there's no real backend, or scoring errors out,
101
+ # surface a clear message — not a DummyScorer card.
102
+ if type(scorer).__name__ != "ObservableScorer":
103
+ lines.append(P.error("Scoring backend not configured. Set OPENBMB_BASE_URL / OPENBMB_TOKEN "
104
+ "(see DEPLOY.md), then reload and try again."))
105
+ yield _frame(lines, intake_v=HIDE, proc_v=SHOW, result_v=HIDE)
106
+ return
107
 
108
+ from prompt_card.observable_pipeline import Progress
109
+ progress = Progress()
110
  box: dict = {}
111
+
112
  def _work():
113
  try:
114
  box["result"] = scorer.score(parsed, progress=progress)
115
+ except Exception as e: # captured, surfaced as a real error frame below
116
+ box["error"] = e
 
 
 
 
117
 
118
  bar = len(lines)
119
+ lines.append(P.progress_bar(0, progress.total))
120
  yield _frame(lines, intake_v=HIDE, proc_v=SHOW, result_v=HIDE)
121
 
122
+ th = threading.Thread(target=_work, daemon=True)
123
+ th.start()
124
+ while th.is_alive():
 
 
 
 
 
 
125
  done, total = progress.snapshot()
126
+ lines[bar] = P.progress_bar(done, total)
127
  yield _frame(lines, intake_v=HIDE, proc_v=SHOW, result_v=HIDE)
128
+ time.sleep(0.4)
129
+ th.join()
130
+
131
+ if "error" in box:
132
+ e = box["error"]
133
+ lines[bar] = P.error(f"Scoring failed — {type(e).__name__}: {str(e)[:240]}")
134
+ lines.append(P.fact("No demo scores are shown. Check the model endpoint / secrets, then reload "
135
+ "and try again.", muted=True))
136
+ yield _frame(lines, intake_v=HIDE, proc_v=SHOW, result_v=HIDE)
137
+ return
138
+
139
+ done, total = progress.snapshot()
140
+ lines[bar] = P.progress_bar(total or done, total or done) # snap to 100%
141
 
142
  result = box["result"]
 
143
  card = score_to_card(result, name)
144
+ card.provenance = _provenance(scorer)
145
  card.single_conversation = (getattr(parsed, "source", "") == "paste")
146
  body_updates = [gr.update(value=render_accordion_body(a, card.provenance)) for a in card.axes]
147
  yield _frame(lines, intake_v=HIDE, proc_v=HIDE, result_v=SHOW,
 
156
  text = pasted or ""
157
  parsed = fetch_share(text) if is_share_url(text) else parse_paste(text)
158
  except ParseError as e:
159
+ yield _frame([P.error(str(e))], intake_v=HIDE, proc_v=SHOW, result_v=HIDE)
 
160
  return
161
  yield from run(name, parsed, rm_flag)
162
 
ui/screens/processing.py CHANGED
@@ -23,6 +23,13 @@ def fact(text: str, muted: bool = False) -> str:
23
  return f"<div class='{cls}'><span class='dot'></span><span>{text}</span></div>"
24
 
25
 
 
 
 
 
 
 
 
26
  def fact_num(n: int, label: str) -> str:
27
  return (f"<div class='omc-fact'><span class='dot'></span>"
28
  f"<span><span class='num'>{n:,}</span> {label}</span></div>")
 
23
  return f"<div class='{cls}'><span class='dot'></span><span>{text}</span></div>"
24
 
25
 
26
+ def error(text: str) -> str:
27
+ """A prominent error row — shown instead of a fake card when scoring can't produce real results."""
28
+ from html import escape
29
+ return (f"<div class='omc-error'><span class='dot'></span>"
30
+ f"<span><b>⚠ Couldn't score this</b><br>{escape(text)}</span></div>")
31
+
32
+
33
  def fact_num(n: int, label: str) -> str:
34
  return (f"<div class='omc-fact'><span class='dot'></span>"
35
  f"<span><span class='num'>{n:,}</span> {label}</span></div>")
ui/theme.py CHANGED
@@ -160,6 +160,11 @@ footer {{ display: none !important; }}
160
  .omc-fact .dot {{ width: 7px; height: 7px; border-radius: 50%; background: {TEAL}; flex: none; }}
161
  .omc-fact .num {{ font-family: 'Saira', sans-serif; font-weight: 600; color: {TEAL}; }}
162
  .omc-fact.muted {{ color: {TXT2}; }}
 
 
 
 
 
163
  .omc-langbar {{ height: 8px; border-radius: 4px; overflow: hidden; display: flex; width: 100%; margin-top: 6px; }}
164
  .omc-langbar .en {{ background: {TEAL}; }}
165
  .omc-langbar .other {{ background: {BORDER}; }}
 
160
  .omc-fact .dot {{ width: 7px; height: 7px; border-radius: 50%; background: {TEAL}; flex: none; }}
161
  .omc-fact .num {{ font-family: 'Saira', sans-serif; font-weight: 600; color: {TEAL}; }}
162
  .omc-fact.muted {{ color: {TXT2}; }}
163
+ .omc-error {{ display: flex; align-items: flex-start; gap: 10px; color: #F8B4B4; font-size: 14px;
164
+ line-height: 1.5; padding: 12px 14px; margin: 10px 0; background: rgba(220,80,80,.10);
165
+ border: 1px solid rgba(220,80,80,.45); border-radius: 10px; }}
166
+ .omc-error .dot {{ width: 7px; height: 7px; border-radius: 50%; background: #E25858; flex: none; margin-top: 6px; }}
167
+ .omc-error b {{ color: #F3C0C0; font-family: 'Saira', sans-serif; }}
168
  .omc-langbar {{ height: 8px; border-radius: 4px; overflow: hidden; display: flex; width: 100%; margin-top: 6px; }}
169
  .omc-langbar .en {{ background: {TEAL}; }}
170
  .omc-langbar .other {{ background: {BORDER}; }}