Daily prompt
Scheduled multilingual generation, robust JSON parsing and a stable pointer for the client.
What it is
An introspective question pinned at the top of the global feed every morning, identical for all users, produced by Gemini in all six supported languages. The idea is to offer a writing cue without forcing it: those who want to answer do, others scroll past.
Scheduling
exports.generateDailyPrompt = onSchedule({
schedule: "0 0 * * *",
timeZone: "Europe/Rome",
secrets: [GEMINI_API_KEY],
timeoutSeconds: 120,
retryCount: 2,
}, async () => { ... });
Europe/Rome is a deliberate choice: the initial audience is Italian, and “new day” should match local perception, not UTC. Other timezones see the prompt roll at an hour that depends on their own — a tradeoff accepted in exchange for a single source of truth.
One call, six languages
Instead of six Gemini calls (one per language), we make one call that returns a JSON with every translation:
{
"it": "...",
"en": "...",
"de": "...",
"es": "...",
"fr": "...",
"pt-BR": "..."
}
Upside: one-sixth of the cost and time, and — more importantly — semantic coherence. The model doesn’t “re-paraphrase” the question, losing nuance between languages.
Parsing robustness
A JSON answer from an LLM isn’t as reliable as one from a typed endpoint. Two defenses:
- Truncated-JSON recovery — if the response was cut off (e.g.
MAX_TOKENS), a tolerant parser tries to extract the keys that are already complete. Five languages out of six is better than an error. - Per-language fallback — if any key is still missing after recovery, it’s filled with a neutral default and logged as a warning. The UI will never show an empty card.
If no language survives, the function throws and dailyPrompts/current stays on the previous day — see below.
Stable pointer
The UI doesn’t read dailyPrompts/2026-04-22, which would require the client to know the current Europe/Rome date. It reads dailyPrompts/current, updated by the function at the end of generation:
dailyPrompts/
2026-04-21 ← history
2026-04-22 ← history (today)
current ← pointer, carrying today's payload
If generation fails, current is not overwritten. Users see yesterday’s prompt instead of an empty card — exactly the behavior we want during an outage.
Manual trigger
runDailyPromptNow is an HTTP endpoint authenticated via a Bearer token verified server-side. Useful to regenerate a prompt if the first one doesn’t land, or to test in staging without waiting for midnight. Not exposed to the client, and it doesn’t replace the scheduled trigger — it sits alongside it.