Files
wtrinkl b1363aeab9 Initial commit: Any-LLM Gateway with Laravel Admin Interface
- Any-LLM Gateway setup with Docker Compose
- Laravel 11 admin interface with Livewire
- Dashboard with usage statistics and charts
- Gateway Users management with budget tracking
- API Keys management with revocation
- Budget templates with assignment
- Usage Logs with filtering and CSV export
- Model Pricing management with calculator
- PostgreSQL database integration
- Complete authentication system for admins
2025-11-16 12:38:05 +01:00

201 lines
13 KiB
PHP

<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
Usage Logs
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
{{-- Filter Form --}}
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6">
<h3 class="text-lg font-semibold mb-4">Filters</h3>
<form method="GET" action="{{ route('usage-logs.index') }}" class="space-y-4">
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
{{-- Date From --}}
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Date From</label>
<input type="date" name="date_from" value="{{ request('date_from') }}"
class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
</div>
{{-- Date To --}}
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Date To</label>
<input type="date" name="date_to" value="{{ request('date_to') }}"
class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
</div>
{{-- User Filter --}}
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">User</label>
<select name="user_id" class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
<option value="">All Users</option>
@foreach($users as $user)
<option value="{{ $user->user_id }}" {{ request('user_id') == $user->user_id ? 'selected' : '' }}>
{{ $user->alias ?? $user->user_id }}
</option>
@endforeach
</select>
</div>
{{-- Provider Filter --}}
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Provider</label>
<select name="provider" class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
<option value="">All Providers</option>
@foreach($providers as $provider)
<option value="{{ $provider }}" {{ request('provider') == $provider ? 'selected' : '' }}>
{{ ucfirst($provider) }}
</option>
@endforeach
</select>
</div>
{{-- Model Filter --}}
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Model</label>
<select name="model" class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
<option value="">All Models</option>
@foreach($models as $model)
<option value="{{ $model }}" {{ request('model') == $model ? 'selected' : '' }}>
{{ $model }}
</option>
@endforeach
</select>
</div>
{{-- Status Filter --}}
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Status</label>
<select name="status" class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
<option value="">All Status</option>
<option value="success" {{ request('status') == 'success' ? 'selected' : '' }}>Success</option>
<option value="failed" {{ request('status') == 'failed' ? 'selected' : '' }}>Failed</option>
</select>
</div>
</div>
<div class="flex justify-between items-center pt-4">
<button type="submit" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Apply Filters
</button>
<a href="{{ route('usage-logs.index') }}" class="text-gray-600 hover:text-gray-900">
Clear Filters
</a>
</div>
</form>
</div>
</div>
{{-- Summary Statistics --}}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-4">
<div class="bg-white rounded-lg shadow p-6">
<div class="text-sm text-gray-600">Total Requests</div>
<div class="text-2xl font-bold text-gray-900">{{ number_format($summary->total_requests ?? 0) }}</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="text-sm text-gray-600">Successful</div>
<div class="text-2xl font-bold text-green-600">{{ number_format($summary->successful_requests ?? 0) }}</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="text-sm text-gray-600">Total Tokens</div>
<div class="text-2xl font-bold text-purple-600">{{ number_format($summary->total_tokens ?? 0) }}</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="text-sm text-gray-600">Total Cost</div>
<div class="text-2xl font-bold text-blue-600">${{ number_format($summary->total_cost ?? 0, 2) }}</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="text-sm text-gray-600">Avg Cost/Request</div>
<div class="text-2xl font-bold text-orange-600">
${{ $summary->total_requests > 0 ? number_format(($summary->total_cost ?? 0) / $summary->total_requests, 4) : '0.0000' }}
</div>
</div>
</div>
{{-- Logs Table --}}
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-semibold">Usage Logs ({{ $logs->total() }} results)</h3>
<a href="{{ route('usage-logs.export', request()->query()) }}"
class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded inline-flex items-center">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
Export CSV
</a>
</div>
@if($logs->count() > 0)
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Timestamp</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">User</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Provider / Model</th>
<th class="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Tokens</th>
<th class="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Cost</th>
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@foreach($logs as $log)
<tr class="hover:bg-gray-50">
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">
{{ $log->timestamp->format('M d, Y H:i:s') }}
</td>
<td class="px-4 py-3 whitespace-nowrap text-sm">
<div class="font-medium text-gray-900">{{ $log->gatewayUser?->alias ?? 'N/A' }}</div>
<div class="text-xs text-gray-500">{{ substr($log->user_id, 0, 16) }}...</div>
</td>
<td class="px-4 py-3 whitespace-nowrap text-sm">
<div class="font-medium text-gray-900">{{ ucfirst($log->provider) }}</div>
<div class="text-xs text-gray-500">{{ $log->model }}</div>
</td>
<td class="px-4 py-3 whitespace-nowrap text-sm text-right">
<div class="text-gray-900">{{ number_format($log->total_tokens) }}</div>
<div class="text-xs text-gray-500">
{{ number_format($log->prompt_tokens) }}p / {{ number_format($log->completion_tokens) }}c
</div>
</td>
<td class="px-4 py-3 whitespace-nowrap text-sm text-right font-semibold text-green-600">
{{ $log->cost_formatted }}
</td>
<td class="px-4 py-3 whitespace-nowrap text-center">
@if($log->status === 'success')
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">Success</span>
@else
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800"
title="{{ $log->error_message }}">Failed</span>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
{{-- Pagination --}}
<div class="mt-4">
{{ $logs->links() }}
</div>
@else
<div class="text-center py-12">
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
<h3 class="mt-2 text-sm font-medium text-gray-900">No logs found</h3>
<p class="mt-1 text-sm text-gray-500">Try adjusting your filters or date range.</p>
</div>
@endif
</div>
</div>
</div>
</div>
</x-app-layout>