Add complete Laravel LLM Gateway implementation
Core Features: - Multi-provider support (OpenAI, Anthropic, DeepSeek, Gemini, Mistral) - Provider service architecture with abstract base class - Dynamic model discovery from provider APIs - Encrypted per-user provider credentials storage Admin Interface: - Complete admin panel with Livewire components - User management with CRUD operations - API key management with testing capabilities - Budget system with limits and reset schedules - Usage logs with filtering and CSV export - Model pricing management with cost calculator - Dashboard with Chart.js visualizations Database Schema: - MariaDB migrations for all tables - User provider credentials (encrypted) - LLM request logging - Budget tracking and rate limiting - Model pricing configuration API Implementation: - OpenAI-compatible endpoints - Budget checking middleware - Rate limit enforcement - Request logging jobs - Cost calculation service Testing: - Unit tests for all provider services - Provider factory tests - Cost calculator tests Documentation: - Admin user seeder - Model pricing seeder - Configuration files
This commit is contained in:
103
laravel-app/app/Services/LLM/Providers/AbstractProvider.php
Normal file
103
laravel-app/app/Services/LLM/Providers/AbstractProvider.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\LLM\Providers;
|
||||
|
||||
use App\Services\LLM\Contracts\ProviderInterface;
|
||||
use App\Exceptions\ProviderException;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
abstract class AbstractProvider implements ProviderInterface
|
||||
{
|
||||
protected string $apiKey;
|
||||
protected string $baseUrl;
|
||||
protected int $timeout = 60;
|
||||
protected int $retryAttempts = 3;
|
||||
protected int $retryDelay = 1000; // milliseconds
|
||||
|
||||
public function __construct(string $apiKey)
|
||||
{
|
||||
$this->apiKey = $apiKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build request payload for provider
|
||||
*/
|
||||
abstract protected function buildRequest(array $messages, array $options): array;
|
||||
|
||||
/**
|
||||
* Get authorization headers for provider
|
||||
*/
|
||||
abstract protected function getAuthHeaders(): array;
|
||||
|
||||
/**
|
||||
* Make HTTP request with retry logic
|
||||
*/
|
||||
protected function makeRequest(string $endpoint, array $data): array
|
||||
{
|
||||
$attempt = 0;
|
||||
$lastException = null;
|
||||
|
||||
while ($attempt < $this->retryAttempts) {
|
||||
try {
|
||||
$response = Http::withHeaders($this->getAuthHeaders())
|
||||
->timeout($this->timeout)
|
||||
->post($this->baseUrl . $endpoint, $data);
|
||||
|
||||
if ($response->successful()) {
|
||||
return $response->json();
|
||||
}
|
||||
|
||||
// Handle specific HTTP errors
|
||||
if ($response->status() === 401) {
|
||||
throw new ProviderException('Invalid API key', 401);
|
||||
}
|
||||
|
||||
if ($response->status() === 429) {
|
||||
throw new ProviderException('Rate limit exceeded', 429);
|
||||
}
|
||||
|
||||
if ($response->status() >= 500) {
|
||||
throw new ProviderException('Provider server error', $response->status());
|
||||
}
|
||||
|
||||
throw new ProviderException(
|
||||
'Request failed: ' . $response->body(),
|
||||
$response->status()
|
||||
);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$lastException = $e;
|
||||
$attempt++;
|
||||
|
||||
if ($attempt < $this->retryAttempts) {
|
||||
Log::warning("Provider request failed, retrying ({$attempt}/{$this->retryAttempts})", [
|
||||
'provider' => static::class,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
usleep($this->retryDelay * 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new ProviderException(
|
||||
'All retry attempts failed: ' . ($lastException ? $lastException->getMessage() : 'Unknown error'),
|
||||
$lastException ? $lastException->getCode() : 500
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate API key by making a test request
|
||||
*/
|
||||
public function validateApiKey(): bool
|
||||
{
|
||||
try {
|
||||
$this->chatCompletion([
|
||||
['role' => 'user', 'content' => 'test']
|
||||
], ['max_tokens' => 5]);
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user