Fixes age validation errors caused by the LLM not knowing the current date.
Changes:
- prompts.py: inject date.today() at the top of both system prompts so the
LLM can accurately calculate a child's age from their date of birth
- llm.py: add optional thinking_budget parameter to complete(); when set,
passes thinking={"type": "enabled", "budget_tokens": N} to litellm and
raises max_tokens to thinking_budget + 4096 (Anthropic models only)
- config.py: add thinking_budget field, read from THINKING_BUDGET env var
- .env.example: document the THINKING_BUDGET option
- core.py: pass thinking_budget through to llm.complete()
- main.py: pass thinking_budget when constructing EmailAgent
- chat_app.py: switch from stream_complete to asyncio.to_thread(complete)
so extended thinking works and so only the reply field is shown to
the parent (not the raw JSON wrapper)
To enable extended thinking set THINKING_BUDGET=8000 in .env.
https://claude.ai/code/session_01SUWzMzFvSfWiHXA2p6rPg9
Meister-Eder
AI-powered conversational registration agent for Spielgruppe Pumuckl (Familienverein Fällanden, Switzerland). Parents register their child and ask questions via email — the agent handles the conversation, validates all required fields, and notifies the playgroup admin on completion.
Replaces a static Google Forms workflow with an AI agent that guides parents through child registration via natural conversation — over email or a web chat interface.
What it does
- Guides parents through registration one question at a time, adapting to their responses
- Answers questions about fees, schedule, and policies from a curated knowledge base
- Validates and stores completed registrations as structured data
- Notifies playgroup administrators on completion, routed by playgroup type
- Responds in any language the parent uses; defaults to German
Channels
| Channel | Description |
|---|---|
| Web chat | Real-time, session-based |
| Async, thread-tracked; reminders on days 3, 10, 25 |
Prerequisites
- Python 3.13+
- uv (dependency manager)
Installation
git clone https://github.com/gurix/Meister-Eder.git
cd Meister-Eder
uv sync
Configuration
Copy the example env file and fill in your values:
cp .env.example .env
Required variables
| Variable | Description |
|---|---|
AI_MODEL |
litellm model string, e.g. anthropic/claude-opus-4-6 or openai/gpt-4o |
IMAP_HOST |
IMAP server hostname for receiving parent emails |
IMAP_USERNAME |
Email account username |
IMAP_PASSWORD |
Email account password |
SMTP_HOST |
SMTP server hostname for sending replies |
REGISTRATION_EMAIL |
Sender address shown to parents |
The API key variable depends on your chosen provider — see Switching AI providers below.
Optional variables
| Variable | Default | Description |
|---|---|---|
IMAP_PORT |
993 |
IMAP port |
IMAP_USE_SSL |
true |
Use SSL for IMAP |
SMTP_PORT |
587 |
SMTP port |
SMTP_USE_TLS |
true |
Use STARTTLS for SMTP |
DATA_DIR |
data/ |
Directory for conversation state and completed registrations |
KNOWLEDGE_BASE_DIR |
openspec/…/knowledge-base |
Path to admin-editable knowledge base markdown files |
POLL_INTERVAL |
60 |
Seconds between inbox polls (only used when running as a daemon) |
Switching AI providers
AI_MODEL uses litellm model strings — any supported provider works without code changes:
# Anthropic (default)
AI_MODEL=anthropic/claude-opus-4-6
ANTHROPIC_API_KEY=sk-ant-...
# OpenAI
AI_MODEL=openai/gpt-4o
OPENAI_API_KEY=sk-...
# Google Gemini
AI_MODEL=gemini/gemini-2.0-flash
GEMINI_API_KEY=...
Running
Web chat
Start the web chat interface:
uv run chainlit run chat_app.py
The chat opens at http://localhost:8000 by default.
To listen on a different port or host:
uv run chainlit run chat_app.py --port 8080 --host 0.0.0.0
Minimum required env vars for the web chat:
| Variable | Description |
|---|---|
AI_MODEL |
litellm model string, e.g. anthropic/claude-opus-4-6 |
ANTHROPIC_API_KEY |
(or the key for your chosen provider) |
SMTP_HOST / SMTP_PORT |
For admin notification emails on registration completion |
IMAP_USERNAME / IMAP_PASSWORD |
Used as SMTP credentials |
ADMIN_EMAIL_INDOOR |
Andrea Sigrist — notified when indoor group is booked |
ADMIN_EMAIL_OUTDOOR |
Barbara Gross — notified when outdoor group is booked |
ADMIN_EMAIL_CC |
Markus Graf — always CC'd on notifications |
IMAP variables (IMAP_HOST, etc.) are not required for the web chat — only for the email channel.
Email channel
The email agent polls an IMAP inbox and replies via SMTP. No web server required.
As a cron job (recommended)
Schedule with cron and use flock to prevent overlapping runs:
*/5 * * * * flock -n /tmp/meister-eder-email.lock uv run python main.py
flock -n exits immediately if a previous run is still in progress, so the script is always safe to schedule aggressively.
Manually
uv run python main.py
Running both channels together
The web chat and email agent are independent processes — run them side by side:
# Terminal 1 — web chat
uv run chainlit run chat_app.py
# Terminal 2 — email polling
uv run python main.py
Completed registrations from both channels are stored in the same DATA_DIR (default: data/) and share the same admin notification configuration.
Development
Running tests
uv run pytest
All tests are unit tests — no network access or API keys required.
Knowledge base
The agent answers parent questions from markdown files in the knowledge base directory. These files are designed to be edited directly by playgroup admins — no code changes needed to update fees, schedules, or policies.
Adding a new AI provider
Set AI_MODEL to any litellm-supported model string and set the corresponding API key environment variable. No code changes required.