- 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]>
24 lines
1.2 KiB
HTML
24 lines
1.2 KiB
HTML
<div class="msg msg-warning draft-notice">
|
||
<strong>{{ draft_notice|default:"KI-Entwurf" }} – bitte prüfen.</strong>
|
||
Noch nichts ist gespeichert. Korrigiere die Felder unten bei Bedarf und tippe dann auf „Speichern“.
|
||
</div>
|
||
{% if ai_draft %}
|
||
<details class="card preview" open>
|
||
<summary><strong>Vorschau: {{ ai_draft.title|default:"Ohne Titel" }}</strong></summary>
|
||
{% if ai_draft.ingredients %}
|
||
<h3>Zutaten</h3>
|
||
<ul class="ingredients">
|
||
{% for ing in ai_draft.ingredients %}
|
||
<li><span class="amount">{% if ing.amount is not None %}{{ ing.amount|floatformat:"-2" }}{% if ing.amount_max is not None %}–{{ ing.amount_max|floatformat:"-2" }}{% endif %}{% endif %} {{ ing.unit }}</span>
|
||
<span>{% if ing.group %}<span class="muted">{{ ing.group }}: </span>{% endif %}{{ ing.name }}{% if ing.note %}<span class="muted">, {{ ing.note }}</span>{% endif %}</span></li>
|
||
{% endfor %}
|
||
</ul>
|
||
{% endif %}
|
||
{% if ai_draft.steps %}
|
||
<h3>Zubereitung</h3>
|
||
<ol class="steps">{% for step in ai_draft.steps %}<li>{{ step }}</li>{% endfor %}</ol>
|
||
{% endif %}
|
||
{% if ai_draft.tags %}<p class="chips">{% for tag in ai_draft.tags %}<span class="chip">#{{ tag }}</span>{% endfor %}</p>{% endif %}
|
||
</details>
|
||
{% endif %}
|