- 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]>
42 lines
1.9 KiB
HTML
42 lines
1.9 KiB
HTML
{% extends "base.html" %}
|
||
{% block title %}{% if is_new %}Neues Rezept{% else %}{{ recipe.title }} bearbeiten{% endif %}{% endblock %}
|
||
{% block content %}
|
||
<h1>{% if is_new %}Neues Rezept{% else %}Rezept bearbeiten{% endif %}</h1>
|
||
|
||
{% if draft_notice or ai_draft %}{% include "recipes/_draft_preview.html" %}{% endif %}
|
||
|
||
<form method="post"{% if form_action %} action="{{ form_action }}"{% endif %} enctype="multipart/form-data" class="card recipe-form" novalidate>
|
||
{% csrf_token %}
|
||
{% include "form_fields.html" %}
|
||
<div class="form-buttons">
|
||
<button type="submit" class="button">Speichern</button>
|
||
{% if ai_enabled %}
|
||
<button type="submit" name="ai" value="1" class="button secondary" data-busy-text="KI arbeitet …">✨ Mit KI aufbereiten</button>
|
||
{% endif %}
|
||
<a class="button secondary" href="{% if recipe %}{% url 'recipes:detail' recipe.pk %}{% else %}{% url 'recipes:list' %}{% endif %}">Abbrechen</a>
|
||
</div>
|
||
{% if ai_enabled %}<p class="muted small">„Mit KI aufbereiten“ strukturiert deine Eingaben (Zutaten, Schritte, Tags) als Entwurf. Fotos danach auswählen – sie werden dabei nicht übernommen.</p>{% endif %}
|
||
</form>
|
||
|
||
{% if recipe and images %}
|
||
<section class="card">
|
||
<h2>Fotos</h2>
|
||
<ul class="photo-admin">
|
||
{% for img in images %}
|
||
<li>
|
||
<img src="{% url 'recipes:photo' img.uuid 'klein' %}" alt="Foto {{ forloop.counter }}" loading="lazy">
|
||
<div class="action-buttons">
|
||
{% if img.is_cover %}
|
||
<span class="badge">Titelbild</span>
|
||
{% else %}
|
||
<form method="post" action="{% url 'recipes:photo_cover' recipe.pk img.pk %}">{% csrf_token %}<button class="button secondary small">Als Titelbild</button></form>
|
||
{% endif %}
|
||
<form method="post" action="{% url 'recipes:photo_delete' recipe.pk img.pk %}">{% csrf_token %}<button class="button danger small">Entfernen</button></form>
|
||
</div>
|
||
</li>
|
||
{% endfor %}
|
||
</ul>
|
||
</section>
|
||
{% endif %}
|
||
{% endblock %}
|