Add AI photo import via Ollama Vision

Users can upload a photo of a nail polish bottle; a background queue
job sends it to Ollama (llama3.2-vision at 192.168.30.172:11434),
extracts name, number and manufacturer, then creates the nail polish
and adds it to the user's collection automatically.

- ProcessNailPolishImage job (180s timeout, 2 tries)
- NailPolishImport model + migration (status tracking)
- AiSetting model + migration (Ollama URL + model, admin-configurable)
- NailPolishImportController: upload, status page (auto-refresh every 5s)
- Admin\AiSettingController: configure Ollama connection
- Views: drag-drop upload form, live status page, import history
- Navbar: "Foto importieren" link for all users
- Admin dropdown: "KI-Einstellungen"
- neonail-worker.conf: Supervisor config for the queue worker

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Housemann
2026-06-13 17:37:26 +02:00
co-authored by Claude Sonnet 4.6
parent 7a24e554b0
commit f715bc4552
15 changed files with 738 additions and 0 deletions
+72
View File
@@ -0,0 +1,72 @@
@extends('layouts.app')
@section('title', 'KI-Einstellungen NeoNail DB')
@section('content')
<div class="page-hero mb-4">
<h1><i class="fas fa-robot me-2"></i>KI-Einstellungen</h1>
<p>Ollama-Konfiguration für den automatischen Foto-Import</p>
</div>
<div class="row g-4">
<div class="col-lg-7">
<div class="card">
<div class="card-header"><i class="fas fa-cog me-2"></i>Ollama Verbindung</div>
<div class="card-body p-4">
<form method="POST" action="{{ route('admin.ai.update') }}">
@csrf
@method('PUT')
<div class="mb-3">
<label class="form-label" for="ollama_url">Ollama URL <span class="text-danger">*</span></label>
<input type="url" class="form-control @error('ollama_url') is-invalid @enderror"
id="ollama_url" name="ollama_url"
value="{{ old('ollama_url', $ai->ollama_url) }}"
placeholder="http://192.168.30.172:11434">
@error('ollama_url')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
<div class="form-text">URL des Ollama-Containers/Servers ohne abschließenden Slash.</div>
</div>
<div class="mb-4">
<label class="form-label" for="ollama_model">Modell <span class="text-danger">*</span></label>
<input type="text" class="form-control @error('ollama_model') is-invalid @enderror"
id="ollama_model" name="ollama_model"
value="{{ old('ollama_model', $ai->ollama_model) }}"
placeholder="llama3.2-vision">
@error('ollama_model')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
<div class="form-text">Muss ein Vision-fähiges Modell sein (z.B. <code>llama3.2-vision</code>, <code>llava</code>).</div>
</div>
<button type="submit" class="btn btn-primary">
<i class="fas fa-save me-2"></i>Speichern
</button>
</form>
</div>
</div>
</div>
<div class="col-lg-5">
<div class="card">
<div class="card-header"><i class="fas fa-info-circle me-2"></i>Queue Worker</div>
<div class="card-body p-3">
<p style="font-size:.83rem; color:#374151; margin-bottom:10px;">
Jobs werden über die <strong>Datenbank-Queue</strong> verarbeitet. Auf dem Server muss dauerhaft ein Worker laufen:
</p>
<code style="display:block; background:#f3f4f6; padding:10px 12px; border-radius:8px; font-size:.78rem; white-space:pre-wrap;">php artisan queue:work \
--timeout=180 \
--tries=2 \
--sleep=3</code>
<p style="font-size:.78rem; color:#9ca3af; margin-top:10px; margin-bottom:0;">
Empfohlen: Als Supervisor-Dienst einrichten, damit der Worker nach Crashes neu startet.
</p>
</div>
</div>
</div>
</div>
@endsection