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:
105
laravel-app/tests/Unit/Services/CostCalculatorTest.php
Normal file
105
laravel-app/tests/Unit/Services/CostCalculatorTest.php
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Services;
|
||||
|
||||
use Tests\TestCase;
|
||||
use App\Services\LLM\CostCalculator;
|
||||
use App\Models\ModelPricing;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
class CostCalculatorTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
private CostCalculator $calculator;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->calculator = new CostCalculator();
|
||||
}
|
||||
|
||||
public function test_calculates_cost_correctly(): void
|
||||
{
|
||||
ModelPricing::create([
|
||||
'provider' => 'openai',
|
||||
'model' => 'gpt-4o-mini',
|
||||
'input_price_per_million' => 0.15,
|
||||
'output_price_per_million' => 0.60,
|
||||
'is_active' => true,
|
||||
'effective_from' => now(),
|
||||
]);
|
||||
|
||||
Cache::flush();
|
||||
|
||||
$costs = $this->calculator->calculate('openai', 'gpt-4o-mini', 1000, 500);
|
||||
|
||||
// (1000/1M * 0.15) + (500/1M * 0.60) = 0.00015 + 0.0003 = 0.00045
|
||||
$this->assertEquals(0.00015, $costs['prompt_cost']);
|
||||
$this->assertEquals(0.0003, $costs['completion_cost']);
|
||||
$this->assertEquals(0.00045, $costs['total_cost']);
|
||||
}
|
||||
|
||||
public function test_returns_zero_cost_for_unknown_model(): void
|
||||
{
|
||||
Cache::flush();
|
||||
|
||||
$costs = $this->calculator->calculate('unknown', 'unknown-model', 1000, 500);
|
||||
|
||||
$this->assertEquals(0.0, $costs['prompt_cost']);
|
||||
$this->assertEquals(0.0, $costs['completion_cost']);
|
||||
$this->assertEquals(0.0, $costs['total_cost']);
|
||||
}
|
||||
|
||||
public function test_uses_cache_for_pricing(): void
|
||||
{
|
||||
ModelPricing::create([
|
||||
'provider' => 'anthropic',
|
||||
'model' => 'claude-sonnet-4',
|
||||
'input_price_per_million' => 3.00,
|
||||
'output_price_per_million' => 15.00,
|
||||
'is_active' => true,
|
||||
'effective_from' => now(),
|
||||
]);
|
||||
|
||||
Cache::flush();
|
||||
|
||||
// First call - should query database
|
||||
$costs1 = $this->calculator->calculate('anthropic', 'claude-sonnet-4', 1000, 500);
|
||||
|
||||
// Second call - should use cache
|
||||
$costs2 = $this->calculator->calculate('anthropic', 'claude-sonnet-4', 1000, 500);
|
||||
|
||||
$this->assertEquals($costs1, $costs2);
|
||||
$this->assertTrue(Cache::has('pricing:anthropic:claude-sonnet-4'));
|
||||
}
|
||||
|
||||
public function test_estimate_cost(): void
|
||||
{
|
||||
ModelPricing::create([
|
||||
'provider' => 'openai',
|
||||
'model' => 'gpt-4o',
|
||||
'input_price_per_million' => 2.50,
|
||||
'output_price_per_million' => 10.00,
|
||||
'is_active' => true,
|
||||
'effective_from' => now(),
|
||||
]);
|
||||
|
||||
Cache::flush();
|
||||
|
||||
$estimatedCost = $this->calculator->estimateCost('openai', 'gpt-4o', 2000, 1000);
|
||||
|
||||
// (2000/1M * 2.50) + (1000/1M * 10.00) = 0.005 + 0.01 = 0.015
|
||||
$this->assertEquals(0.015, $estimatedCost);
|
||||
}
|
||||
|
||||
public function test_clear_cache(): void
|
||||
{
|
||||
Cache::put('pricing:test:model', 'test_data', 3600);
|
||||
|
||||
$this->calculator->clearCache('test', 'model');
|
||||
|
||||
$this->assertFalse(Cache::has('pricing:test:model'));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user