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:
wtrinkl
2025-11-18 22:18:36 +01:00
parent bef36c7ca2
commit 6573e15ba4
60 changed files with 5991 additions and 0 deletions

View File

@@ -0,0 +1,97 @@
<?php
namespace Tests\Unit\Services;
use Tests\TestCase;
use App\Services\LLM\ProviderFactory;
use App\Services\LLM\Providers\{
OpenAIProvider,
AnthropicProvider,
MistralProvider,
GeminiProvider,
DeepSeekProvider
};
class ProviderFactoryTest extends TestCase
{
public function test_creates_openai_provider(): void
{
$provider = ProviderFactory::create('openai', 'test-key');
$this->assertInstanceOf(OpenAIProvider::class, $provider);
}
public function test_creates_anthropic_provider(): void
{
$provider = ProviderFactory::create('anthropic', 'test-key');
$this->assertInstanceOf(AnthropicProvider::class, $provider);
}
public function test_creates_mistral_provider(): void
{
$provider = ProviderFactory::create('mistral', 'test-key');
$this->assertInstanceOf(MistralProvider::class, $provider);
}
public function test_creates_gemini_provider(): void
{
$provider = ProviderFactory::create('gemini', 'test-key');
$this->assertInstanceOf(GeminiProvider::class, $provider);
}
public function test_creates_deepseek_provider(): void
{
$provider = ProviderFactory::create('deepseek', 'test-key');
$this->assertInstanceOf(DeepSeekProvider::class, $provider);
}
public function test_throws_exception_for_unknown_provider(): void
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Unknown provider: unknown');
ProviderFactory::create('unknown', 'test-key');
}
public function test_is_case_insensitive(): void
{
$provider1 = ProviderFactory::create('OpenAI', 'test-key');
$provider2 = ProviderFactory::create('ANTHROPIC', 'test-key');
$this->assertInstanceOf(OpenAIProvider::class, $provider1);
$this->assertInstanceOf(AnthropicProvider::class, $provider2);
}
public function test_get_supported_providers(): void
{
$providers = ProviderFactory::getSupportedProviders();
$this->assertIsArray($providers);
$this->assertContains('openai', $providers);
$this->assertContains('anthropic', $providers);
$this->assertContains('mistral', $providers);
$this->assertContains('gemini', $providers);
$this->assertContains('deepseek', $providers);
$this->assertCount(5, $providers);
}
public function test_is_supported(): void
{
$this->assertTrue(ProviderFactory::isSupported('openai'));
$this->assertTrue(ProviderFactory::isSupported('anthropic'));
$this->assertTrue(ProviderFactory::isSupported('mistral'));
$this->assertTrue(ProviderFactory::isSupported('gemini'));
$this->assertTrue(ProviderFactory::isSupported('deepseek'));
$this->assertFalse(ProviderFactory::isSupported('unknown'));
}
public function test_is_supported_case_insensitive(): void
{
$this->assertTrue(ProviderFactory::isSupported('OpenAI'));
$this->assertTrue(ProviderFactory::isSupported('ANTHROPIC'));
}
}