docker compose passes an empty .env line as OPENAI_BASE_URL="", and with
base_url=None the openai SDK fell back to that empty value, so every
call failed with "Connection error". Always pass the base URL
explicitly. Also disable gunicorn's unused control socket, which
logged a permission error because /app is read-only for the app user.
Found while deploying to the homelab server.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Real calls produced "2 Stück Eier" and "1 Zehen Knoblauchzehe". The
prompt now asks for no unit on countable items and no unit repeated in
the name; verified with a real gpt-5-mini call.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A real API call returned 429 "You have no credits remaining" without the
word "quota", so the UI wrongly said the AI was busy. Match quota,
credit and billing wording; test uses the real message.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Optional OPENAI_BASE_URL for OpenAI-compatible endpoints or proxies
- Tests run the real openai SDK against a local stub server: request
shape (model, auth, strict json_schema), text cleanup, photo import
and an authentication error mapped to the UI message
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- 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]>