sidechat / README.md
lsb's picture
Port steganacrostics to a Gradio app; retarget to MiniCPM5-1B
08b2dd2 verified
|
Raw
History Blame
2.56 kB
metadata
title: Sidechat
emoji: πŸ’¬
colorFrom: yellow
colorTo: purple
sdk: gradio
sdk_version: 6.18.0
app_file: app.py
pinned: false
license: apache-2.0
short_description: Completely normal text assistant, with talking on the side
tags:
  - track:wood
  - sponsor:openbmb
  - achievement:offgrid
  - achievement:sharing
  - achievement:fieldnotes

Side chat

A Gradio port of the browser steganacrostics app. A completely normal text assistant β€” except every line of the answer secretly starts with the next letter of a hidden secret word (an acrostic). It does this with grammar-constrained decoding over a small local model (openbmb/MiniCPM5-1B by default; set SIDECHAT_MODEL=LiquidAI/LFM2.5-350M for the smaller, faster original), running on CPU via PyTorch transformers.

What's ported from the JavaScript original (../../src/):

  • Grammar engine (grammar.py) β€” a tiny NFA that pins each line to its forced first letter, with optional * bullets and a max line length.
  • Constrained generation (logits.py + masking.py) β€” a LogitsProcessor that masks every token that would break the acrostic; EOS only at an accept state. A state-keyed cache makes the per-step vocab scan cheap.
  • List-vs-prose classifier (classifier.py) β€” an optimized prompt, grammar-constrained to list. / story., that auto-picks the render mode. The prompt is tuned per model: failure modes are model-specific, so eval_classifier.py (50 list + 50 prose prompts) and sweep_minicpm.py re-optimize it for whatever model is in use.
  • Local-crossing search (crossing_search.py) β€” the "extra attention at the constraint": generate each prose line greedily, then choose where to break it so a short window straddling the crossing (last k tokens + forced letter + next j tokens) reads best. Plus stealth lowercase casing and a minimum line length.

Run locally:

pip install -r requirements.txt
python app.py

Then open the printed URL, type a prompt, set a secret in βš™οΈ Settings, and click Generate. The list-vs-prose classifier runs automatically on each Generate (turn it off in βš™οΈ Settings to set the render mode by hand, or use πŸ”Ž Detect to preview it). Because everything runs on CPU, generation takes seconds (more for the larger model); the crossing search trades extra time for smoother prose.

The model is downloaded from the Hugging Face Hub on first run. Custom logits processing requires the model to run in-process, so this app does not use the remote Inference API.