Anvil Mini-App
Because sometimes the smartest thing you can do is say it like you’re talking to a very good dog.
What you’ll ship: a tiny Anvil app that rewrites any paragraph in three modes — Expert, Manager, and Dog — using an LLM via OpenRouter.
Why it’s good: zero GPUs, five minutes to wire up, infinite snickers.
TL;DR
- Stack: Anvil (client + server Python), OpenRouter (LLM gateway).
- Modes: Expert (dense), Manager (PowerPoint-safe), Dog (happy brain cell).
- Security: API key in App Secrets, never client code.
- Bonus: graceful local fallback when the API is rate-limited.
App Flow (What Happens)
flowchart TD
A[User pastes text] --> B[Selects mode: Expert / Manager / Dog]
B --> C[Click Submit]
C --> D[Client validation]
D -- ok --> E[anvil.server.call('explain_text')]
E --> F[Server builds prompt]
F --> G[POST to OpenRouter /chat/completions]
G -- success --> H[Return rewritten text]
G -- rate limit / error --> I[Local fallback rewrite]
H --> J[Show output]
I --> J[Show output + note about fallback]
UI Layout (Anvil Designer)
Add these components to your form (e.g., ExplainItForm
):
input_text
→ TextArea (user input)mode_dropdown
→ DropDown (Expert / Manager / Dog)submit_button
→ Button (event:click
)helper_label
→ Label (optional hints)output_text
→ TextArea (read-only recommended)
Server Module (OpenRouter version)
Add this to a Server Module (e.g.,
ServerModule.py
).
Set an App Secret calledOPENROUTER_API_KEY
with your key.
import re
import time
import anvil.server
import anvil.http
from anvil.secrets import get_secret
PROMPT_TEMPLATES = {
"Expert": "Rewrite the following text in precise technical language for a subject-matter expert:\n\n{text}",
"Manager": "Rewrite the following text as a high-level executive summary, avoiding jargon. Highlight purpose, outcomes, timelines, and risks:\n\n{text}",
"Dog": "Rewrite the following text for a very enthusiastic Golden Retriever. Use short, simple sentences and a friendly tone:\n\n{text}",
}
OPENROUTER_API_KEY = get_secret("OPENROUTER_API_KEY")
OPENROUTER_URL = "https://openrouter.ai/api/v1/chat/completions"
DEFAULT_MODEL = "mistralai/mistral-7b-instruct" # Free-tier friendly at time of writing
def _rewrite_locally(text: str, mode: str) -> str:
"""Ultra-simple fallback so the demo never 404s your dignity."""
t = (text or "").strip()
if mode == "Expert":
subs = {r"\bcan't\b":"cannot", r"\bwon't\b":"will not", r"\bn't\b":" not",
r"\bI'm\b":"I am", r"\bwe're\b":"we are", r"\bit's\b":"it is"}
for pat, rep in subs.items():
t = re.sub(pat, rep, t, flags=re.I)
return ("Technical restatement:\n"
f"- Scope: {t}\n- Method: deterministic processes; minimize ambiguity.\n"
"- Note: heuristic fallback (no model call).")
if mode == "Manager":
bullets = re.split(r"(?<=[.!?])\s+", t)[:4] or [t]
bullets = [b for b in bullets if b]
return "Executive summary:\n" + "\n".join(f"- {b.strip()}" for b in bullets)
if mode == "Dog":
s = re.sub(r"\b(anomal(y|ies))\b", "weird stuff", t, flags=re.I)
s = re.sub(r"\bdetect(ion)?\b", "spot", s, flags=re.I)
parts = [p.strip() for p in re.split(r"(?<=[.!?])\s+", s) if p.strip()]
wag = " 🐶"
return "Tail-wagging edition:\n" + " ".join(p + wag for p in parts[:6])
return t
def _call_openrouter(prompt: str, model: str = DEFAULT_MODEL) -> str:
headers = {
"Authorization": f"Bearer {OPENROUTER_API_KEY}",
"Content-Type": "application/json",
"HTTP-Referer": "https://hirabarton.io",
"X-Title": "Explain It Like I'm a Golden Retriever",
}
payload = {
"model": model,
"messages": [
{"role": "system", "content": "You are a helpful rewriting assistant."},
{"role": "user", "content": prompt},
],
"temperature": 0.7,
}
resp = anvil.http.request(OPENROUTER_URL, method="POST", headers=headers, data=payload, json=True)
return resp["choices"][0]["message"]["content"].strip()
@anvil.server.callable
def explain_text(text: str, mode: str, allow_fallback: bool = True) -> str:
if mode not in PROMPT_TEMPLATES:
raise ValueError("Invalid mode")
text = (text or "").strip()
if not text:
raise ValueError("Empty input")
prompt = PROMPT_TEMPLATES[mode].format(text=text)
try:
return _call_openrouter(prompt)
except anvil.http.HttpError as e:
# If rate-limited or model hiccups, keep the app useful
if allow_fallback:
local = _rewrite_locally(text, mode)
detail = getattr(e, "content", "") or str(e)
return f"{local}\n\n[Note: Used local fallback due to API error: {detail}]"
raise



Client Form (event handlers)
from ._anvil_designer import ExplainItFormTemplate
from anvil import *
import anvil.server
class ExplainItForm(ExplainItFormTemplate):
def __init__(self, **properties):
self.init_components(**properties)
self.mode_dropdown.items = [
("— Select mode —", None),
("Expert — precise & technical", "Expert"),
("Manager — executive summary", "Manager"),
("🐕 Dog — tail-wagging edition", "Dog"),
]
self.mode_dropdown.selected_value = None
if hasattr(self, "submit_button"):
self.submit_button.enabled = False
self.mode_dropdown.set_event_handler("change", self.mode_dropdown_change)
def mode_dropdown_change(self, **event_args):
helper = {
"Expert": "Dense, accurate, and possibly migraine-inducing.",
"Manager": "High-level, digestible, suspiciously PowerPoint-ready.",
"Dog": "Simple words. Big wags.",
None: ""
}
self.helper_label.text = helper.get(self.mode_dropdown.selected_value, "")
self.submit_button.enabled = bool(self.mode_dropdown.selected_value)
def submit_button_click(self, **event_args):
text = (self.input_text.text or "").strip()
mode = self.mode_dropdown.selected_value
if not text or not mode:
self.output_text.text = "Please enter text and select a mode."
return
self.output_text.text = "Processing..."
try:
out = anvil.server.call("explain_text", text, mode)
self.output_text.text = out
except Exception as e:
self.output_text.text = f"Error: {e}"
