Fix: korrekte Credit-Verrechnung + Duplikat-Schutz
- Credits nur bei status='success': Konflikt/Fehler/Failed → 0 Credits - Multi-Action: Credits proportional zu erfolgreichen Aktionen - AgentActionService: Duplikat-Check vor Event/Task/Notiz-Anlage - Multi-Action-Status: 'success'/'partial'/'failed' statt immer 'success' - Gilt für Web (Livewire/Agent/Index) und API (AgentChatController) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>main
parent
24f43be627
commit
68aa62db6f
|
|
@ -137,9 +137,13 @@ class AgentChatController extends Controller
|
||||||
foreach ($parsed['_multi'] as $action) {
|
foreach ($parsed['_multi'] as $action) {
|
||||||
$results[] = $actionService->handle($user, $action);
|
$results[] = $actionService->handle($user, $action);
|
||||||
}
|
}
|
||||||
|
$successCount = collect($results)->where('status', 'success')->count();
|
||||||
|
$totalCount = max(1, count($results));
|
||||||
$actionResult = [
|
$actionResult = [
|
||||||
'status' => collect($results)->every(fn ($r) => $r['status'] === 'success') ? 'success' : 'partial',
|
'status' => $successCount === count($results) ? 'success' : ($successCount > 0 ? 'partial' : 'failed'),
|
||||||
'results' => $results,
|
'results' => $results,
|
||||||
|
'success_count' => $successCount,
|
||||||
|
'total_count' => $totalCount,
|
||||||
];
|
];
|
||||||
} elseif (isset($parsed['type']) && $parsed['type'] !== 'chat') {
|
} elseif (isset($parsed['type']) && $parsed['type'] !== 'chat') {
|
||||||
$actionService = new AgentActionService();
|
$actionService = new AgentActionService();
|
||||||
|
|
@ -180,9 +184,16 @@ class AgentChatController extends Controller
|
||||||
$shouldLog = true;
|
$shouldLog = true;
|
||||||
|
|
||||||
if ($isAction) {
|
if ($isAction) {
|
||||||
$credits = (($actionResult['status'] ?? '') === 'error')
|
$status = $actionResult['status'] ?? 'error';
|
||||||
? 0
|
if ($status !== 'success' && $status !== 'partial') {
|
||||||
: $this->calculateCredits($usage, $aiConfig, $type);
|
$credits = 0;
|
||||||
|
} elseif (isset($actionResult['success_count'], $actionResult['total_count'])) {
|
||||||
|
// Multi-Action: proportional zu erfolgreichen Aktionen
|
||||||
|
$full = $this->calculateCredits($usage, $aiConfig, $type);
|
||||||
|
$credits = (int) ceil($full * $actionResult['success_count'] / $actionResult['total_count']);
|
||||||
|
} else {
|
||||||
|
$credits = $this->calculateCredits($usage, $aiConfig, $type);
|
||||||
|
}
|
||||||
} elseif ($historyCount === 0) {
|
} elseif ($historyCount === 0) {
|
||||||
$credits = 5;
|
$credits = 5;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -129,10 +129,15 @@ class Index extends Component
|
||||||
}
|
}
|
||||||
|
|
||||||
$duration = round((microtime(true) - $startTime) * 1000);
|
$duration = round((microtime(true) - $startTime) * 1000);
|
||||||
$credits = $this->calculateCredits(['type' => 'multi'], $duration, $usage);
|
$successCount = collect($results)->where('status', 'success')->count();
|
||||||
|
$totalCount = max(1, count($results));
|
||||||
|
$fullCredits = $this->calculateCredits(['type' => 'multi'], $duration, $usage);
|
||||||
|
$credits = $successCount > 0
|
||||||
|
? (int) ceil($fullCredits * $successCount / $totalCount)
|
||||||
|
: 0;
|
||||||
|
|
||||||
$combinedResult = [
|
$combinedResult = [
|
||||||
'status' => 'success',
|
'status' => $successCount === count($results) ? 'success' : ($successCount > 0 ? 'partial' : 'failed'),
|
||||||
'message' => implode(' | ', $messages) ?: 'Erledigt!',
|
'message' => implode(' | ', $messages) ?: 'Erledigt!',
|
||||||
'meta' => ['actions' => count($actions)],
|
'meta' => ['actions' => count($actions)],
|
||||||
];
|
];
|
||||||
|
|
@ -209,7 +214,9 @@ class Index extends Component
|
||||||
$this->dispatch('agent:sent');
|
$this->dispatch('agent:sent');
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$credits = $this->calculateCredits($parsed, $duration, $usage);
|
$credits = ($result['status'] === 'success')
|
||||||
|
? $this->calculateCredits($parsed, $duration, $usage)
|
||||||
|
: 0;
|
||||||
$this->logConversationAction($userMessage, $parsed, $result, $model, $duration, $credits);
|
$this->logConversationAction($userMessage, $parsed, $result, $model, $duration, $credits);
|
||||||
|
|
||||||
if ($result['status'] === 'success' && ($parsed['type'] ?? '') === 'event') {
|
if ($result['status'] === 'success' && ($parsed['type'] ?? '') === 'event') {
|
||||||
|
|
|
||||||
|
|
@ -48,9 +48,14 @@ class AgentActionService
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($data['start']) && !empty($data['end'])) {
|
if (!empty($data['start']) && !empty($data['end'])) {
|
||||||
|
$title = $data['title'] ?? 'Termin';
|
||||||
|
$dateStr = Carbon::parse($data['start'])->toDateString();
|
||||||
|
if (Event::where('user_id', $user->id)->where('title', $title)->whereDate('starts_at', $dateStr)->exists()) {
|
||||||
|
return ['status' => 'failed', 'message' => "Termin \"{$title}\" existiert an diesem Tag bereits.", 'meta' => []];
|
||||||
|
}
|
||||||
|
|
||||||
return $planner->plan($user, array_merge($data, [
|
return $planner->plan($user, array_merge($data, [
|
||||||
'title' => $data['title'] ?? 'Termin',
|
'title' => $title,
|
||||||
'start' => $data['start'],
|
'start' => $data['start'],
|
||||||
'end' => $data['end'],
|
'end' => $data['end'],
|
||||||
'duration_minutes' => Carbon::parse($data['start'])
|
'duration_minutes' => Carbon::parse($data['start'])
|
||||||
|
|
@ -67,9 +72,14 @@ class AgentActionService
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (!empty($data['datetime'])) {
|
if (!empty($data['datetime'])) {
|
||||||
|
$title = $data['title'] ?? 'Termin';
|
||||||
|
$dateStr = Carbon::parse($data['datetime'])->toDateString();
|
||||||
|
if (Event::where('user_id', $user->id)->where('title', $title)->whereDate('starts_at', $dateStr)->exists()) {
|
||||||
|
return ['status' => 'failed', 'message' => "Termin \"{$title}\" existiert an diesem Tag bereits.", 'meta' => []];
|
||||||
|
}
|
||||||
|
|
||||||
return $planner->plan($user, array_merge($data, [
|
return $planner->plan($user, array_merge($data, [
|
||||||
'title' => $data['title'] ?? 'Termin',
|
'title' => $title,
|
||||||
'start' => $data['datetime'],
|
'start' => $data['datetime'],
|
||||||
'duration_minutes' => $data['duration_minutes'] ?? null,
|
'duration_minutes' => $data['duration_minutes'] ?? null,
|
||||||
'ai_duration' => $data['ai_duration'] ?? null,
|
'ai_duration' => $data['ai_duration'] ?? null,
|
||||||
|
|
@ -92,6 +102,11 @@ class AgentActionService
|
||||||
|
|
||||||
protected static function handleNote(User $user, array $data): array
|
protected static function handleNote(User $user, array $data): array
|
||||||
{
|
{
|
||||||
|
$content = $data['content'] ?? $data['text'] ?? '';
|
||||||
|
if ($content && Note::where('user_id', $user->id)->where('content', $content)->whereDate('created_at', today())->exists()) {
|
||||||
|
return ['status' => 'failed', 'message' => 'Diese Notiz wurde heute bereits angelegt.', 'meta' => []];
|
||||||
|
}
|
||||||
|
|
||||||
$note = Note::create([
|
$note = Note::create([
|
||||||
'user_id' => $user->id,
|
'user_id' => $user->id,
|
||||||
'title' => $data['title'] ?? null,
|
'title' => $data['title'] ?? null,
|
||||||
|
|
@ -127,6 +142,10 @@ class AgentActionService
|
||||||
{
|
{
|
||||||
$title = $data['title'] ?? $data['description'] ?? 'Aufgabe';
|
$title = $data['title'] ?? $data['description'] ?? 'Aufgabe';
|
||||||
|
|
||||||
|
if (Task::where('user_id', $user->id)->where('title', $title)->whereDate('created_at', today())->exists()) {
|
||||||
|
return ['status' => 'failed', 'message' => "Aufgabe \"{$title}\" wurde heute bereits angelegt.", 'meta' => []];
|
||||||
|
}
|
||||||
|
|
||||||
// due_at: AI schickt due_at, due_date oder datetime
|
// due_at: AI schickt due_at, due_date oder datetime
|
||||||
$dueAt = null;
|
$dueAt = null;
|
||||||
if (!empty($data['due_at'])) {
|
if (!empty($data['due_at'])) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue