Commit Graph
6 Commits
Author SHA1 Message Date
Claude b2e04fa07b implement registration-confirmation-email
- Add qrbill and pillow dependencies for Swiss QR-bill PNG generation
- Add _STRINGS_DE / _STRINGS_EN bilingual string tables to AdminNotifier
- Add _generate_qr_bill_png(): Swiss QR code with cross overlay (PNG bytes)
- Add _build_parent_html(): HTML confirmation with inline cid:qrbill image
- Add _build_parent_text(): plain-text fallback with IBAN in full
- Add notify_parent(): multipart/mixed MIME email to parent on completion
- Persist metadata.language in _build_record() in json_store.py
- Wire notify_parent() into src/agent/core.py and chat_app.py completion events
- Add 9 tests for notify_parent, _generate_qr_bill_png, language fallback
- Update tasks.md: tasks 1–5 complete; task 6 (smoke test) remains manual

112 tests passing.

https://claude.ai/code/session_01LjjK7RjKVnC8bETtccfgna
2026-02-22 21:23:32 +00:00
Claude 1bd11e6acd feat: inject today's date into system prompt and add extended thinking support
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
2026-02-22 19:57:32 +00:00
Claude 9fdbe341be feat(chat): implement web chat interface with accessibility
Core implementation:
- chat_app.py: Chainlit entry point with @cl.on_chat_start,
  @cl.on_message (streaming via llm.stream_complete), @cl.on_chat_end
  Reuses Config, KnowledgeBase, ConversationStore, AdminNotifier from src/
  Handles registration completion, post-completion updates, new-child flow

- src/llm.py: add stream_complete() generator (litellm stream=True)
  alongside existing complete(); tests added in tests/test_llm.py

- src/agent/response_parser.py: extract parse_llm_response(),
  apply_updates(), fallback_message() from EmailAgent into shared module
  EmailAgent now delegates to these functions (no logic change)

Chainlit configuration:
- chainlit.toml: telemetry off, German default, custom CSS + JS paths
- chainlit.md: German welcome page with playgroup info

Accessibility (WCAG 2.1 AA):
- public/custom.css: contrast overrides (≥4.5:1), prefers-reduced-motion
  (static "…" replaces animated dots), skip link styles, 100dvh fix
- public/accessibility.js: MutationObserver injects aria-live="polite"
  on message list, focus management after agent replies, skip link element

Other:
- .gitignore: add .chainlit/ (Chainlit runtime, auto-generated)
- openspec/config.yaml: populate context field with tech stack
- openspec/changes/implement-web-chat/tasks.md: mark completed tasks

95 tests pass.

https://claude.ai/code/session_01SUWzMzFvSfWiHXA2p6rPg9
2026-02-22 07:42:29 +00:00
Claude 431847a8b7 Replace custom provider abstraction with litellm
Drops the src/providers/ package (base class, AnthropicProvider,
OpenAIProvider, factory) in favour of a single src/llm.py that calls
litellm.completion() directly. litellm handles provider routing,
authentication, and SDK differences for 100+ providers without any
code we need to maintain.

Changes:
- Delete src/providers/ entirely
- Add src/llm.py — one complete() function wrapping litellm
- src/agent/core.py: EmailAgent takes model: str instead of LLMProvider
- src/config.py: ai_provider + api key fields → single ai_model string
  in litellm format (e.g. "anthropic/claude-opus-4-6")
- main.py: remove provider factory wiring; pass config.ai_model to agent
- .env.example: simplify AI section, show litellm model string examples
- pyproject.toml: replace anthropic + openai deps with litellm>=1.0.0
- uv.lock: regenerated

https://claude.ai/code/session_01HaUFs7SaLD5SoiuGCY27Tw
2026-02-21 07:35:37 +00:00
Claude 0c3b5a9033 Implement email-address-based conversation matching
Closes the gap where parents sending a new email (instead of replying)
would lose their registration progress. All changes follow the
email-based-conversation-matching OpenSpec change.

Key changes
-----------
storage/json_store.py
  - normalize_email() helper (lowercase + trim)
  - Conversations now keyed by sender email address, not thread ID
  - Versioned registration storage: data/registrations/<email>/v<N>_<ts>.json
  - current.json always reflects the latest version
  - save_registration() returns (email_key, version) tuple
  - save_registration_version() for updates with change_summary
  - get_registration_history() returns all versions in order

models/conversation.py
  - Added last_inbound_message_id field for reply threading (not matching)

channels/email_channel.py
  - fetch_unread_messages() no longer exposes thread_id
  - Conversation matching removed from channel layer (now in agent)
  - Removed _resolve_thread_id() — threading headers kept for SMTP only

agent/core.py
  - process_message() takes parent_email + inbound_message_id (no thread ID)
  - Looks up conversation by normalized email address
  - Post-completion handler: detects intent (question / update / new_child)
  - Registration updates: diffs old vs new, versions storage, notifies admin

agent/prompts.py
  - build_system_prompt() dispatches to registration or post-completion prompt
  - Post-completion prompt guides LLM to return intent field
  - Reminder language updated: no expiration threats

notifications/notifier.py
  - notify_admin() accepts version parameter
  - notify_registration_update() sends "Registration Updated" emails with diff
  - _build_update_body() includes field-level old→new change summary

main.py
  - Poll loop passes parent_email + inbound_message_id to agent (no thread_id)

https://claude.ai/code/session_01HaUFs7SaLD5SoiuGCY27Tw
2026-02-20 22:15:49 +00:00
Claude b82ff27efd Implement Python email agent with multi-model AI support
Adds a complete email-based registration agent for Spielgruppe Pumuckl
based on the OpenSpec define-project-scope specifications.

Architecture
- Channel-agnostic EmailAgent core — no email-specific code in business logic
- Pluggable AI provider layer: Anthropic (Claude) and OpenAI (GPT) supported
  via a shared LLMProvider interface; switch with AI_PROVIDER env var
- IMAP polling for inbound emails with thread-tracking via email headers
  (Message-ID / In-Reply-To / References)
- SMTP for outbound replies and admin notifications
- File-based JSON storage for conversation state and completed registrations
- Admin-editable knowledge-base loaded from markdown files at startup

Key files
  src/config.py                  — env-var configuration
  src/providers/base.py          — abstract LLMProvider
  src/providers/anthropic_provider.py — Claude backend
  src/providers/openai_provider.py    — OpenAI backend
  src/agent/core.py              — EmailAgent orchestrator
  src/agent/prompts.py           — system prompt builder (KB + registration state)
  src/models/registration.py     — RegistrationData matching the JSON schema
  src/models/conversation.py     — ConversationState persisted per thread
  src/channels/email_channel.py  — IMAP/SMTP I/O + quoted-text stripping
  src/storage/json_store.py      — conversation & registration persistence
  src/notifications/notifier.py  — admin notification routing by playgroup type
  main.py                        — polling entry point
  requirements.txt               — anthropic, openai, python-dotenv, jsonschema
  .env.example                   — configuration template

https://claude.ai/code/session_01HaUFs7SaLD5SoiuGCY27Tw
2026-02-20 20:17:00 +00:00