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
This commit is contained in:
200
laravel-app/resources/views/usage-logs/index.blade.php
Normal file
200
laravel-app/resources/views/usage-logs/index.blade.php
Normal file
@@ -0,0 +1,200 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user