Add phase 4: AI cleanup and photo/text import via OpenAI

- recipes/ai.py: Chat Completions with strict JSON schema (Structured
  Outputs), Pydantic re-validation, plausibility clamping, prompt treats
  input as data only
- "Mit KI aufbereiten" on create/edit form returns an editable draft
- Import page: photo/screenshot (vision, downscaled JPEG, not stored) and
  pasted text; drafts pre-fill the recipe form with a preview
- Clear UI messages for timeout, rate limit, quota, auth, model and
  connection errors; without OPENAI_API_KEY AI features show a notice
- Optional per-user and global daily AI limits (default unlimited)
- Tests for response parsing, schema strictness, error mapping, views

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Kyle Dammann
2026-10-30 21:43:30 +01:00
co-authored by Claude Opus 5.5
parent 9256e94286
commit 1bf8ef8b65
17 changed files with 856 additions and 13 deletions
+5 -1
View File
@@ -8,7 +8,7 @@ port; TLS, domain and proxy are out of scope.
- [x] Phase 1: Docker setup, DB, auth with invitations, roles
- [x] Phase 2: Recipes CRUD, visibility (private/public), photos, notes, search, favourites
- [x] Phase 3: Comments, serving calculator (server-side `?portionen=`), cooking mode (wake lock)
- [ ] Phase 4: AI (OpenAI): clean up manual input + photo/screenshot import
- [x] Phase 4: AI (OpenAI): "Mit KI aufbereiten" on the form + photo/screenshot and text import
- [ ] Phase 5: Link import (schema.org JSON-LD, Instagram with paste fallback, Android PWA share target)
- [ ] Phase 6: Backup script, full README
@@ -32,6 +32,10 @@ no JS build step: plain forms with small progressive enhancements in `static/js/
Photos are served only via `recipes:photo` (`/bilder/<uuid>/<gross|klein>.webp`) after a visibility check.
Comments: deletable by their author and admins only (`CommentQuerySet.deletable_by`), rate-limited.
`tests/test_privacy.py` lists every recipe URL – add new recipe routes there.
- `recipes/ai.py` – OpenAI Chat Completions with strict `json_schema` (`RECIPE_JSON_SCHEMA`), answers
re-validated by Pydantic (`AIRecipe`), `map_api_error` → German user messages, per-user/global daily
limits (0 = off). `_create_completion` is the only network call (stubbed in tests). Drafts go into
`RecipeForm(initial=…)` and are never saved directly. `import_views.py`: `/rezepte/import/` (photo, text).
- `templates/`, `static/` – global templates, `app.css` (mobile first, dark mode), `app.js`.
- `docker/entrypoint.sh` – migrate → ensure_admin → purge_ratelimits → gunicorn.
- `scripts/` – `init-env.sh` (creates .env with secrets), `dev_server.py` (local run without Docker).