comgen42 commited on
Commit
02bdd2d
·
verified ·
1 Parent(s): 23a91e0

Game tab: the Enchanted Returns Desk, you vs. the Bear

Browse files
Files changed (2) hide show
  1. app.py +86 -0
  2. returns_desk_cases.json +0 -0
app.py CHANGED
@@ -224,6 +224,76 @@ def categorize(rows_text: str, file, cats_text: str, question: str, review_below
224
  return summary, table, path
225
 
226
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
  with gr.Blocks(title="Kodiak") as demo:
228
  gr.Markdown("# Kodiak 🐻\nTyped questions in, calibrated answers out, in one forward pass. "
229
  "Answers are always one of your labels or inside your range, or an honest abstention.")
@@ -279,6 +349,22 @@ with gr.Blocks(title="Kodiak") as demo:
279
  preset.change(load_cat_preset, preset, [cats, cat_q, rows_in])
280
  demo.load(load_cat_preset, preset, [cats, cat_q, rows_in])
281
  cat_go.click(categorize, [rows_in, file_in, cats, cat_q, review_below, model], [cat_summary, cat_table, cat_file])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
282
 
283
  if __name__ == "__main__":
284
  demo.launch()
 
224
  return summary, table, path
225
 
226
 
227
+ # ---- Game: the Enchanted Returns Desk ---------------------------------------------------------------------------------
228
+ # Cases come from the Returns Desk simulator (seed 9, never used for training): the shop's rules are code, so every answer is known exactly.
229
+ GAME_CASES = json.load(open(os.path.join(os.path.dirname(__file__), "returns_desk_cases.json"), encoding="utf-8"))
230
+ ROUNDS = 5
231
+ GAME_QS = ("want", "next")
232
+
233
+
234
+ def _labels(case: dict, qid: str) -> dict:
235
+ q = next(q for q in case["questions"] if q["id"] == qid)
236
+ return {lab["id"]: lab["text"] for lab in q["labels"]}
237
+
238
+
239
+ def _case_md(case: dict, n: int) -> str:
240
+ s = case["state"]
241
+ rules = "\n".join(f"- {r}" for r in s["returns_rules"])
242
+ order = "\n".join(f"- **{k.replace('_', ' ')}:** {v}" for k, v in s["order"].items())
243
+ return (f"### Customer {n} of {ROUNDS} · {s['shop']} · today is {s['today']}\n\n> {s['customer_message'].replace(chr(10), chr(10) + '> ')}\n\n"
244
+ f"**The shop's rules**\n{rules}\n\n**The order record**\n{order}")
245
+
246
+
247
+ def game_new(model: str):
248
+ import random
249
+
250
+ picks = random.sample(range(len(GAME_CASES)), ROUNDS)
251
+ g = {"picks": picks, "i": 0, "you": 0, "bear": 0, "model": model, "log": []}
252
+ return (g, *_game_show(g), "**You 0 · Bear 0.** Read the customer's message and the rules, then decide.", gr.update(interactive=True))
253
+
254
+
255
+ def _game_show(g: dict):
256
+ case = GAME_CASES[g["picks"][g["i"]]]
257
+ w, n = _labels(case, "want"), _labels(case, "next")
258
+ return (_case_md(case, g["i"] + 1), gr.update(choices=list(w.values()), value=None), gr.update(choices=list(n.values()), value=None), "")
259
+
260
+
261
+ def game_answer(g: dict | None, want_txt: str, next_txt: str):
262
+ if not g or g["i"] >= ROUNDS:
263
+ return g, gr.update(), gr.update(), gr.update(), "Press **New game** to start.", "", gr.update()
264
+ if not want_txt or not next_txt:
265
+ return g, gr.update(), gr.update(), gr.update(), "Pick an answer for both questions.", "", gr.update()
266
+ case = GAME_CASES[g["picks"][g["i"]]]
267
+ qs = [q for q in case["questions"] if q["id"] in GAME_QS]
268
+ k = LOADED.get(g["model"], kodiak).decide(case["state"], qs, null_threshold=1.0)
269
+ rows, you_pts, bear_pts = [], 0, 0
270
+ for qid, mine in (("want", want_txt), ("next", next_txt)):
271
+ labels = _labels(case, qid)
272
+ right = labels[case["answers"][qid]["label"]]
273
+ a = k[qid]
274
+ bear = labels.get(max(a["probs"], key=a["probs"].get))
275
+ conf = max(a["probs"].values())
276
+ you_ok, bear_ok = mine == right, bear == right
277
+ you_pts += 10 * you_ok
278
+ bear_pts += 10 * bear_ok
279
+ unsure = " *(unsure: in a real deployment it would pass this one to a human)*" if conf < 0.6 else ""
280
+ rows.append(f"| {'What they want' if qid == 'want' else 'Next step'} | {'✅' if you_ok else '❌'} {mine} | "
281
+ f"{'✅' if bear_ok else '❌'} {bear} ({conf:.0%}){unsure} | **{right}** |")
282
+ g["you"] += you_pts
283
+ g["bear"] += bear_pts
284
+ g["i"] += 1
285
+ table = "| | You | 🐻 Kodiak (confidence) | Right answer |\n|---|---|---|---|\n" + "\n".join(rows)
286
+ offer = next(lab["text"] for q in case["questions"] if q["id"] == "allowed" for lab in q["labels"] if lab["id"] == case["answers"]["allowed"]["label"])
287
+ table += f"\n\n*Under the rules, the shop can offer: {offer}.*"
288
+ score = f"**You {g['you']} · Bear {g['bear']}**"
289
+ if g["i"] >= ROUNDS:
290
+ verdict = ("You beat the Bear! 🏆" if g["you"] > g["bear"] else "The Bear wins this time. 🐻" if g["bear"] > g["you"] else "A tie!")
291
+ share = f"I scored {g['you']} vs Kodiak's {g['bear']} at the Enchanted Returns Desk 🧙🐻"
292
+ return (g, gr.update(), gr.update(), gr.update(), f"{score}. **{verdict}** Share it: *{share}* Press **New game** to play again.",
293
+ table, gr.update(interactive=False))
294
+ return (g, *_game_show(g)[:3], f"{score}. Next customer!", table, gr.update())
295
+
296
+
297
  with gr.Blocks(title="Kodiak") as demo:
298
  gr.Markdown("# Kodiak 🐻\nTyped questions in, calibrated answers out, in one forward pass. "
299
  "Answers are always one of your labels or inside your range, or an honest abstention.")
 
349
  preset.change(load_cat_preset, preset, [cats, cat_q, rows_in])
350
  demo.load(load_cat_preset, preset, [cats, cat_q, rows_in])
351
  cat_go.click(categorize, [rows_in, file_in, cats, cat_q, review_below, model], [cat_summary, cat_table, cat_file])
352
+ with gr.Tab("🧙 Returns Desk: you vs. the Bear"):
353
+ gr.Markdown("You work the returns desk of a wizard shop. Each customer writes in; you decide **what they want most** and "
354
+ "**what to do next**, following the shop's rules. Kodiak plays the same customers. The rules are code, so the right "
355
+ f"answer is exact. {ROUNDS} customers per game, 10 points per right answer.")
356
+ game = gr.State(None)
357
+ game_go = gr.Button("New game", variant="primary")
358
+ game_score = gr.Markdown()
359
+ game_case = gr.Markdown()
360
+ with gr.Row():
361
+ game_want = gr.Radio([], label="What does the customer want most?")
362
+ game_next = gr.Radio([], label="What should the clerk do next?")
363
+ game_submit = gr.Button("Decide!", interactive=False)
364
+ game_result = gr.Markdown()
365
+ game_go.click(game_new, model, [game, game_case, game_want, game_next, game_result, game_score, game_submit])
366
+ game_submit.click(game_answer, [game, game_want, game_next], [game, game_case, game_want, game_next, game_score, game_result, game_submit])
367
+
368
 
369
  if __name__ == "__main__":
370
  demo.launch()
returns_desk_cases.json ADDED
The diff for this file is too large to render. See raw diff