Spaces:
Running on Zero
Running on Zero
Game tab: the Enchanted Returns Desk, you vs. the Bear
Browse files- app.py +86 -0
- 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
|
|
|