Fix: event vs task distinction, event reminder support, correct credit calculation

- AgentChatController: _multi hatte kein type-Key → wurde als 'chat' gewertet
  → nur 5 Credits statt Action-Credits. Fix: isset(_multi) erkennt Multi-Actions
- AgentAIService: EVENT vs TASK Entscheidungsregel + reminder_at für Events
  Event mit Erinnerung → immer event+reminder_at, niemals als Task anlegen
- EventPlannerService: reminder_at beim Event-Erstellen speichern (UTC)
- Event Model + Migration: reminder_at + reminder_sent Felder ergänzt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main
boban 2026-04-19 07:49:39 +02:00
parent e205186465
commit bcc1c0aac2
5 changed files with 57 additions and 7 deletions

View File

@ -175,10 +175,12 @@ class AgentChatController extends Controller
} }
// Credits berechnen — Flat-Rate-Logik // Credits berechnen — Flat-Rate-Logik
// - Aktionen: tokenbasiert, nur bei Erfolg // - Aktionen (inkl. _multi): tokenbasiert, nur bei Erfolg
// - Chat (type = 'chat'): immer pauschal 5 Credits // - Chat (type = 'chat'): immer pauschal 5 Credits
// _multi hat kein 'type'-Key → würde sonst fälschlich als 'chat' gewertet
$type = $parsed['type'] ?? 'chat'; $type = $parsed['type'] ?? 'chat';
$isAction = $type !== 'chat'; $isAction = ($type !== 'chat') || isset($parsed['_multi']);
$logType = isset($parsed['_multi']) ? 'multi' : $type;
$shouldLog = true; $shouldLog = true;
if ($isAction) { if ($isAction) {
@ -199,7 +201,7 @@ class AgentChatController extends Controller
if ($shouldLog) { if ($shouldLog) {
// Input für Log bestimmen (wie im Web) // Input für Log bestimmen (wie im Web)
$logInput = match ($type) { $logInput = match ($logType) {
'chat' => 'Konversation', 'chat' => 'Konversation',
'event', 'event_update' => $parsed['data']['title'] ?? $request->message, 'event', 'event_update' => $parsed['data']['title'] ?? $request->message,
'note', 'note_update' => mb_substr($parsed['data']['content'] ?? $request->message, 0, 100), 'note', 'note_update' => mb_substr($parsed['data']['content'] ?? $request->message, 0, 100),
@ -214,7 +216,7 @@ class AgentChatController extends Controller
AgentLog::create([ AgentLog::create([
'user_id' => $user->id, 'user_id' => $user->id,
'type' => $type, 'type' => $logType,
'input' => $logInput, 'input' => $logInput,
'status' => $logStatus, 'status' => $logStatus,
'output' => $actionResult['meta'] ?? $actionResult ?? null, 'output' => $actionResult['meta'] ?? $actionResult ?? null,
@ -245,7 +247,7 @@ class AgentChatController extends Controller
$responseData = [ $responseData = [
'message' => $assistantMessage, 'message' => $assistantMessage,
'action' => $actionResult, 'action' => $actionResult,
'type' => $parsed['type'] ?? 'chat', 'type' => $logType,
'credits_used' => $credits, 'credits_used' => $credits,
'usage' => [ 'usage' => [
'credits_used' => $user->monthly_usage, 'credits_used' => $user->monthly_usage,

View File

@ -26,6 +26,8 @@ class Event extends Model
'google_event_id', 'google_event_id',
'recurrence', 'recurrence',
'recurrence_end_date', 'recurrence_end_date',
'reminder_at',
'reminder_sent',
]; ];
protected $casts = [ protected $casts = [
@ -38,6 +40,8 @@ class Event extends Model
'exceptions' => 'array', 'exceptions' => 'array',
'participants' => 'array', 'participants' => 'array',
'recurrence_end_date' => 'date:Y-m-d', 'recurrence_end_date' => 'date:Y-m-d',
'reminder_at' => 'datetime',
'reminder_sent' => 'boolean',
]; ];
protected static function boot() protected static function boot()

View File

@ -552,9 +552,16 @@ WICHTIG bei Terminabfragen:
JSON-FORMATE: JSON-FORMATE:
EVENT vs TASK ENTSCHEIDUNGSREGEL (SEHR WICHTIG):
EVENT: Termin, Meeting, Arzt, Zahnarzt, Friseur, Reifenwechsel, Sport, Treffen, "um X Uhr", externe Aktivität
TASK: "ich muss", "erledigen", "kaufen", "nicht vergessen", "To-Do", interne Aufgaben ohne konkreten externen Termin
Event MIT Erinnerung: IMMER event + reminder_at NIEMALS als Task anlegen!
Beispiel: "Reifenwechsel 17 Uhr, erinnere mich morgen früh" event mit datetime + reminder_at, kein task
EVENT: EVENT:
{"type": "event", "data": {"title": "str", "datetime": "YYYY-MM-DD HH:mm"}} {"type": "event", "data": {"title": "str", "datetime": "YYYY-MM-DD HH:mm"}}
{"type": "event", "data": {"title": "str", "datetime": "YYYY-MM-DD HH:mm", "notes": "str"}} {"type": "event", "data": {"title": "str", "datetime": "YYYY-MM-DD HH:mm", "notes": "str"}}
{"type": "event", "data": {"title": "str", "datetime": "YYYY-MM-DD HH:mm", "reminder_at": "YYYY-MM-DD HH:mm:ss"}}
{"type": "event", "data": {"title": "str", "start": "YYYY-MM-DD HH:mm", "end": "YYYY-MM-DD HH:mm", "is_all_day": bool}} {"type": "event", "data": {"title": "str", "start": "YYYY-MM-DD HH:mm", "end": "YYYY-MM-DD HH:mm", "is_all_day": bool}}
NOTE: NOTE:
@ -583,8 +590,12 @@ Beispiele:
Setze priority NUR auf "medium" wenn keine Dringlichkeit erkennbar ist. Setze priority NUR auf "medium" wenn keine Dringlichkeit erkennbar ist.
TASK REMINDER PFLICHT wenn User Zeitangabe macht: EVENT REMINDER für Termine mit Erinnerungswunsch:
Wenn User "erinnere mich in X Min/Std" oder "um HH:MM" oder "morgen früh" sagt: Wenn User einen Termin + Erinnerung möchte event mit reminder_at (UTC):
{"type": "event", "data": {"title": "Reifenwechsel", "datetime": "{{ now('Europe/Vienna')->setTime(17,0)->utc()->format('Y-m-d H:i') }}", "reminder_at": "{{ now('Europe/Vienna')->addDay()->setTime(7,0)->utc()->format('Y-m-d H:i:s') }}"}}
TASK REMINDER NUR für reine Aufgaben (kein externer Termin):
Wenn User "erinnere mich in X Min/Std" oder "um HH:MM" oder "morgen früh" sagt UND es ein internes To-Do ist:
- reminder_at UND due_at MÜSSEN gesetzt werden - reminder_at UND due_at MÜSSEN gesetzt werden
- Zeiten IMMER in UTC (Format: YYYY-MM-DD HH:mm:ss) - Zeiten IMMER in UTC (Format: YYYY-MM-DD HH:mm:ss)
- due_at = reminder_at wenn kein anderes Datum - due_at = reminder_at wenn kein anderes Datum

View File

@ -134,6 +134,13 @@ class EventPlannerService
$eventData['notes'] = $data['notes']; $eventData['notes'] = $data['notes'];
} }
if (!empty($data['reminder_at'])) {
try {
$eventData['reminder_at'] = Carbon::parse($data['reminder_at'])->utc();
$eventData['reminder_sent'] = false;
} catch (\Throwable) {}
}
$event = Event::create($eventData); $event = Event::create($eventData);
#TODO Das mich später freischalten #TODO Das mich später freischalten

View File

@ -0,0 +1,26 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('events', function (Blueprint $table) {
$table->timestamp('reminder_at')->nullable()->after('reminders');
$table->boolean('reminder_sent')->default(false)->after('reminder_at');
});
}
public function down(): void
{
Schema::table('events', function (Blueprint $table) {
$table->dropColumn(['reminder_at', 'reminder_sent']);
});
}
};