Make Ollama prompt editable in admin AI settings
- Add prompt column to ai_settings (migration) - AiSetting: DEFAULT_PROMPT constant + getActivePrompt() (falls back to default if empty) - ProcessNailPolishImage: reads prompt from DB instead of hardcoded - Admin view: textarea to edit prompt, shows active prompt, reset-to-default link Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
2677ee6f39
commit
f60221d0ef
@@ -19,11 +19,13 @@ class AiSettingController extends Controller
|
|||||||
$request->validate([
|
$request->validate([
|
||||||
'ollama_url' => 'required|url|max:255',
|
'ollama_url' => 'required|url|max:255',
|
||||||
'ollama_model' => 'required|string|max:100',
|
'ollama_model' => 'required|string|max:100',
|
||||||
|
'prompt' => 'nullable|string',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$ai = AiSetting::get();
|
$ai = AiSetting::get();
|
||||||
$ai->ollama_url = rtrim($request->ollama_url, '/');
|
$ai->ollama_url = rtrim($request->ollama_url, '/');
|
||||||
$ai->ollama_model = $request->ollama_model;
|
$ai->ollama_model = $request->ollama_model;
|
||||||
|
$ai->prompt = $request->filled('prompt') ? $request->prompt : null;
|
||||||
$ai->save();
|
$ai->save();
|
||||||
|
|
||||||
return redirect()->route('admin.ai.show')->with('success', 'KI-Einstellungen gespeichert.');
|
return redirect()->route('admin.ai.show')->with('success', 'KI-Einstellungen gespeichert.');
|
||||||
|
|||||||
@@ -33,20 +33,7 @@ class ProcessNailPolishImage implements ShouldQueue
|
|||||||
$ai = AiSetting::get();
|
$ai = AiSetting::get();
|
||||||
$imageB64 = base64_encode(Storage::disk('public')->get($import->image_path));
|
$imageB64 = base64_encode(Storage::disk('public')->get($import->image_path));
|
||||||
|
|
||||||
$prompt = <<<PROMPT
|
$prompt = $ai->getActivePrompt();
|
||||||
You are analyzing a nail polish or nail product bottle/container image.
|
|
||||||
Extract exactly these three fields and respond ONLY with a valid JSON object, no explanation, no markdown:
|
|
||||||
{
|
|
||||||
"name": "the full product name or color description (e.g. 'Modeling Base Calcium Neutral Pink', 'Ruby Red', 'French White')",
|
|
||||||
"number": "the product code or article number printed on the label or cap (digits, letters, hyphens only — e.g. '12392-7', '123', 'NP-05')",
|
|
||||||
"manufacturer": "the brand or company name (the logo/brand printed on the product — e.g. 'NEONAIL', 'OPI', 'essie', 'Catrice')"
|
|
||||||
}
|
|
||||||
Rules:
|
|
||||||
- manufacturer = the brand/logo name (usually prominent on the label)
|
|
||||||
- name = the descriptive product/color name (NOT the brand)
|
|
||||||
- number = numeric or alphanumeric code (NOT the color name)
|
|
||||||
- If a value is genuinely not visible, use null
|
|
||||||
PROMPT;
|
|
||||||
|
|
||||||
$response = Http::timeout(160)->post("{$ai->ollama_url}/api/generate", [
|
$response = Http::timeout(160)->post("{$ai->ollama_url}/api/generate", [
|
||||||
'model' => $ai->ollama_model,
|
'model' => $ai->ollama_model,
|
||||||
|
|||||||
@@ -8,7 +8,27 @@ class AiSetting extends Model
|
|||||||
{
|
{
|
||||||
protected $table = 'ai_settings';
|
protected $table = 'ai_settings';
|
||||||
|
|
||||||
protected $fillable = ['ollama_url', 'ollama_model'];
|
protected $fillable = ['ollama_url', 'ollama_model', 'prompt'];
|
||||||
|
|
||||||
|
public const DEFAULT_PROMPT = <<<PROMPT
|
||||||
|
You are analyzing a nail polish or nail product bottle/container image.
|
||||||
|
Extract exactly these three fields and respond ONLY with a valid JSON object, no explanation, no markdown:
|
||||||
|
{
|
||||||
|
"name": "the full product name or color description (e.g. 'Modeling Base Calcium Neutral Pink', 'Ruby Red', 'French White')",
|
||||||
|
"number": "the product code or article number printed on the label or cap (digits, letters, hyphens only — e.g. '12392-7', '123', 'NP-05')",
|
||||||
|
"manufacturer": "the brand or company name (the logo/brand printed on the product — e.g. 'NEONAIL', 'OPI', 'essie', 'Catrice')"
|
||||||
|
}
|
||||||
|
Rules:
|
||||||
|
- manufacturer = the brand/logo name (usually prominent on the label)
|
||||||
|
- name = the descriptive product/color name (NOT the brand)
|
||||||
|
- number = numeric or alphanumeric code (NOT the color name)
|
||||||
|
- If a value is genuinely not visible, use null
|
||||||
|
PROMPT;
|
||||||
|
|
||||||
|
public function getActivePrompt(): string
|
||||||
|
{
|
||||||
|
return !empty($this->prompt) ? $this->prompt : self::DEFAULT_PROMPT;
|
||||||
|
}
|
||||||
|
|
||||||
public static function get(): self
|
public static function get(): self
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('ai_settings', function (Blueprint $table) {
|
||||||
|
$table->text('prompt')->nullable()->after('ollama_model');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('ai_settings', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('prompt');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
<div class="form-text">URL des Ollama-Containers/Servers ohne abschließenden Slash.</div>
|
<div class="form-text">URL des Ollama-Containers/Servers ohne abschließenden Slash.</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-4">
|
<div class="mb-3">
|
||||||
<label class="form-label" for="ollama_model">Modell <span class="text-danger">*</span></label>
|
<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"
|
<input type="text" class="form-control @error('ollama_model') is-invalid @enderror"
|
||||||
id="ollama_model" name="ollama_model"
|
id="ollama_model" name="ollama_model"
|
||||||
@@ -42,6 +42,28 @@
|
|||||||
<div class="form-text">Muss ein Vision-fähiges Modell sein (z.B. <code>llama3.2-vision</code>, <code>llava</code>).</div>
|
<div class="form-text">Muss ein Vision-fähiges Modell sein (z.B. <code>llama3.2-vision</code>, <code>llava</code>).</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="form-label" for="prompt">
|
||||||
|
Erkennungs-Prompt
|
||||||
|
<span class="badge bg-secondary ms-1" style="font-size:.65rem;">optional</span>
|
||||||
|
</label>
|
||||||
|
<textarea class="form-control @error('prompt') is-invalid @enderror"
|
||||||
|
id="prompt" name="prompt" rows="12"
|
||||||
|
placeholder="Leer lassen für Standard-Prompt"
|
||||||
|
style="font-family: monospace; font-size: .82rem;">{{ old('prompt', $ai->prompt) }}</textarea>
|
||||||
|
@error('prompt')
|
||||||
|
<div class="invalid-feedback">{{ $message }}</div>
|
||||||
|
@enderror
|
||||||
|
<div class="form-text">
|
||||||
|
Leer lassen um den Standard-Prompt zu verwenden.
|
||||||
|
<a href="#" onclick="resetPrompt(); return false;">Standard-Prompt einfügen</a>
|
||||||
|
</div>
|
||||||
|
<div class="mt-2 p-3" style="background:#f9fafb; border-radius:10px; border:1px solid #e5e7eb;">
|
||||||
|
<p style="font-size:.75rem; color:#6b7280; margin:0 0 6px; font-weight:600;">AKTUELL AKTIVER PROMPT:</p>
|
||||||
|
<pre style="font-size:.75rem; color:#374151; margin:0; white-space:pre-wrap; word-break:break-word;">{{ $ai->getActivePrompt() }}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary">
|
<button type="submit" class="btn btn-primary">
|
||||||
<i class="fas fa-save me-2"></i>Speichern
|
<i class="fas fa-save me-2"></i>Speichern
|
||||||
</button>
|
</button>
|
||||||
@@ -69,4 +91,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@section('scripts')
|
||||||
|
<script>
|
||||||
|
function resetPrompt() {
|
||||||
|
document.getElementById('prompt').value = @json(\App\Models\AiSetting::DEFAULT_PROMPT);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
Reference in New Issue
Block a user