Rename project from any-llm to laravel-llm
- Remove old any-llm related files (Dockerfile, config.yml, web/, setup-laravel.sh) - Update README.md with new Laravel LLM Gateway documentation - Keep docker-compose.yml with laravel-llm container names - Clean project structure for Laravel-only implementation
This commit is contained in:
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
|
||||
class Admin extends Authenticatable
|
||||
{
|
||||
use Notifiable;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'email',
|
||||
'password',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be hidden for serialization.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -2,34 +2,73 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ApiKey extends Model
|
||||
{
|
||||
protected $primaryKey = 'id';
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'api_keys';
|
||||
protected $primaryKey = 'token';
|
||||
public $incrementing = false;
|
||||
protected $keyType = 'string';
|
||||
|
||||
protected $fillable = [
|
||||
'id',
|
||||
'key_hash',
|
||||
'key_name',
|
||||
'token',
|
||||
'user_id',
|
||||
'last_used_at',
|
||||
'expires_at',
|
||||
'is_active',
|
||||
'key_alias',
|
||||
'key_name',
|
||||
'permissions',
|
||||
'models',
|
||||
'metadata',
|
||||
'expires',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
protected $casts = [
|
||||
'permissions' => 'array',
|
||||
'models' => 'array',
|
||||
'metadata' => 'array',
|
||||
'expires' => 'datetime',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get masked version of the key
|
||||
*/
|
||||
public function getMaskedKeyAttribute(): string
|
||||
{
|
||||
return [
|
||||
'is_active' => 'boolean',
|
||||
'metadata' => 'array',
|
||||
'created_at' => 'datetime',
|
||||
'last_used_at' => 'datetime',
|
||||
'expires_at' => 'datetime',
|
||||
];
|
||||
return substr($this->token, 0, 8) . '...' . substr($this->token, -4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if key is active (not explicitly marked inactive)
|
||||
*/
|
||||
public function getIsActiveAttribute(): bool
|
||||
{
|
||||
// For now, consider all keys active unless explicitly deleted
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if key is expired
|
||||
*/
|
||||
public function getIsExpiredAttribute(): bool
|
||||
{
|
||||
if (!$this->expires) {
|
||||
return false;
|
||||
}
|
||||
return $this->expires->isPast();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last used at timestamp
|
||||
*/
|
||||
public function getLastUsedAtAttribute()
|
||||
{
|
||||
$latestLog = $this->usageLogs()->latest('timestamp')->first();
|
||||
return $latestLog ? $latestLog->timestamp : null;
|
||||
}
|
||||
|
||||
public function gatewayUser()
|
||||
@@ -37,33 +76,14 @@ class ApiKey extends Model
|
||||
return $this->belongsTo(GatewayUser::class, 'user_id', 'user_id');
|
||||
}
|
||||
|
||||
// Alias for backwards compatibility
|
||||
public function user()
|
||||
{
|
||||
return $this->gatewayUser();
|
||||
}
|
||||
|
||||
public function usageLogs()
|
||||
{
|
||||
return $this->hasMany(UsageLog::class, 'api_key_id', 'id');
|
||||
}
|
||||
|
||||
public function scopeActive($query)
|
||||
{
|
||||
return $query->where('is_active', true)
|
||||
->where(function ($q) {
|
||||
$q->whereNull('expires_at')
|
||||
->orWhere('expires_at', '>', now());
|
||||
});
|
||||
}
|
||||
|
||||
public function scopeExpired($query)
|
||||
{
|
||||
return $query->whereNotNull('expires_at')
|
||||
->where('expires_at', '<=', now());
|
||||
}
|
||||
|
||||
public function getMaskedKeyAttribute()
|
||||
{
|
||||
return 'gw-' . substr($this->id, 0, 8) . '...' . substr($this->id, -8);
|
||||
}
|
||||
|
||||
public function getIsExpiredAttribute()
|
||||
{
|
||||
return $this->expires_at && $this->expires_at->isPast();
|
||||
return $this->hasMany(UsageLog::class, 'api_key', 'token');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,47 +2,61 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Budget extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'budgets';
|
||||
protected $primaryKey = 'budget_id';
|
||||
public $incrementing = false;
|
||||
protected $keyType = 'string';
|
||||
|
||||
protected $fillable = [
|
||||
'budget_id',
|
||||
'max_budget',
|
||||
'budget_duration_sec',
|
||||
'name',
|
||||
'monthly_limit',
|
||||
'daily_limit',
|
||||
'created_by',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
protected $casts = [
|
||||
'monthly_limit' => 'decimal:2',
|
||||
'daily_limit' => 'decimal:2',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get formatted max budget display
|
||||
*/
|
||||
public function getMaxBudgetFormattedAttribute(): string
|
||||
{
|
||||
return [
|
||||
'max_budget' => 'double',
|
||||
'budget_duration_sec' => 'integer',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
if ($this->monthly_limit) {
|
||||
return '$' . number_format($this->monthly_limit, 2);
|
||||
}
|
||||
if ($this->daily_limit) {
|
||||
return '$' . number_format($this->daily_limit, 2) . '/day';
|
||||
}
|
||||
return 'Unlimited';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get human-readable duration
|
||||
*/
|
||||
public function getDurationHumanAttribute(): string
|
||||
{
|
||||
if ($this->monthly_limit && $this->daily_limit) {
|
||||
return 'Monthly';
|
||||
}
|
||||
if ($this->daily_limit && !$this->monthly_limit) {
|
||||
return 'Daily';
|
||||
}
|
||||
return 'Unlimited';
|
||||
}
|
||||
|
||||
public function gatewayUsers()
|
||||
{
|
||||
return $this->hasMany(GatewayUser::class, 'budget_id', 'budget_id');
|
||||
}
|
||||
|
||||
public function getMaxBudgetFormattedAttribute()
|
||||
{
|
||||
return '$' . number_format($this->max_budget, 2);
|
||||
}
|
||||
|
||||
public function getDurationHumanAttribute()
|
||||
{
|
||||
if (!$this->budget_duration_sec) return 'No limit';
|
||||
|
||||
$days = floor($this->budget_duration_sec / 86400);
|
||||
$hours = floor(($this->budget_duration_sec % 86400) / 3600);
|
||||
|
||||
return "{$days}d {$hours}h";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,74 +2,45 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class GatewayUser extends Model
|
||||
{
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'users';
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The primary key for the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'gateway_users';
|
||||
protected $primaryKey = 'user_id';
|
||||
|
||||
/**
|
||||
* Indicates if the IDs are auto-incrementing.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $incrementing = false;
|
||||
|
||||
/**
|
||||
* The data type of the primary key ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $keyType = 'string';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'alias',
|
||||
'spend',
|
||||
'budget_id',
|
||||
'spend',
|
||||
'blocked',
|
||||
'metadata',
|
||||
'budget_started_at',
|
||||
'next_budget_reset_at',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'metadata' => 'array',
|
||||
'blocked' => 'boolean',
|
||||
'spend' => 'decimal:2',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
* @return array<string, string>
|
||||
* Get the budget associated with the user.
|
||||
*/
|
||||
protected function casts(): array
|
||||
public function budget()
|
||||
{
|
||||
return [
|
||||
'spend' => 'double',
|
||||
'blocked' => 'boolean',
|
||||
'metadata' => 'array',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'budget_started_at' => 'datetime',
|
||||
'next_budget_reset_at' => 'datetime',
|
||||
];
|
||||
return $this->belongsTo(Budget::class, 'budget_id', 'budget_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the API keys for this user.
|
||||
* Get the API keys for the user.
|
||||
*/
|
||||
public function apiKeys()
|
||||
{
|
||||
@@ -77,21 +48,13 @@ class GatewayUser extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the usage logs for this user.
|
||||
* Get the usage logs for the user.
|
||||
*/
|
||||
public function usageLogs()
|
||||
{
|
||||
return $this->hasMany(UsageLog::class, 'user_id', 'user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the budget for this user.
|
||||
*/
|
||||
public function budget()
|
||||
{
|
||||
return $this->belongsTo(Budget::class, 'budget_id', 'budget_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope a query to only include active users.
|
||||
*/
|
||||
@@ -107,28 +70,4 @@ class GatewayUser extends Model
|
||||
{
|
||||
return $query->where('blocked', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the formatted spend amount.
|
||||
*/
|
||||
public function getSpendFormattedAttribute()
|
||||
{
|
||||
return '$' . number_format($this->spend, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total number of requests.
|
||||
*/
|
||||
public function getTotalRequestsAttribute()
|
||||
{
|
||||
return $this->usageLogs()->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total number of tokens used.
|
||||
*/
|
||||
public function getTotalTokensAttribute()
|
||||
{
|
||||
return $this->usageLogs()->sum('total_tokens');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,45 +7,53 @@ use Illuminate\Database\Eloquent\Model;
|
||||
class ModelPricing extends Model
|
||||
{
|
||||
protected $table = 'model_pricing';
|
||||
protected $primaryKey = 'model_key';
|
||||
public $incrementing = false;
|
||||
protected $keyType = 'string';
|
||||
|
||||
|
||||
protected $fillable = [
|
||||
'model_key',
|
||||
'provider',
|
||||
'model',
|
||||
'input_price_per_million',
|
||||
'output_price_per_million',
|
||||
'context_window',
|
||||
'max_output_tokens',
|
||||
'is_active',
|
||||
'effective_from',
|
||||
'effective_until',
|
||||
'notes',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'input_price_per_million' => 'double',
|
||||
'output_price_per_million' => 'double',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
protected $casts = [
|
||||
'input_price_per_million' => 'decimal:4',
|
||||
'output_price_per_million' => 'decimal:4',
|
||||
'context_window' => 'integer',
|
||||
'max_output_tokens' => 'integer',
|
||||
'is_active' => 'boolean',
|
||||
'effective_from' => 'date',
|
||||
'effective_until' => 'date',
|
||||
];
|
||||
|
||||
// Accessors
|
||||
public function getInputPriceFormattedAttribute()
|
||||
public function getInputPriceFormattedAttribute(): string
|
||||
{
|
||||
return '$' . number_format($this->input_price_per_million, 2) . '/M';
|
||||
}
|
||||
|
||||
public function getOutputPriceFormattedAttribute()
|
||||
public function getOutputPriceFormattedAttribute(): string
|
||||
{
|
||||
return '$' . number_format($this->output_price_per_million, 2) . '/M';
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate cost for given token counts
|
||||
*/
|
||||
public function calculateCost($inputTokens, $outputTokens)
|
||||
public function calculateCost(int $inputTokens, int $outputTokens): float
|
||||
{
|
||||
$inputCost = ($inputTokens / 1000000) * $this->input_price_per_million;
|
||||
$outputCost = ($outputTokens / 1000000) * $this->output_price_per_million;
|
||||
$inputCost = ($inputTokens / 1_000_000) * $this->input_price_per_million;
|
||||
$outputCost = ($outputTokens / 1_000_000) * $this->output_price_per_million;
|
||||
|
||||
return $inputCost + $outputCost;
|
||||
return round($inputCost + $outputCost, 6);
|
||||
}
|
||||
|
||||
public function isCurrentlyActive(): bool
|
||||
{
|
||||
$now = now()->toDateString();
|
||||
return $this->is_active
|
||||
&& $this->effective_from <= $now
|
||||
&& ($this->effective_until === null || $this->effective_until >= $now);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,20 +2,23 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class UsageLog extends Model
|
||||
{
|
||||
protected $primaryKey = 'id';
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'usage_logs';
|
||||
protected $primaryKey = 'request_id';
|
||||
public $incrementing = false;
|
||||
protected $keyType = 'string';
|
||||
public $timestamps = false;
|
||||
|
||||
protected $fillable = [
|
||||
'id',
|
||||
'api_key_id',
|
||||
'request_id',
|
||||
'user_id',
|
||||
'timestamp',
|
||||
'api_key',
|
||||
'model',
|
||||
'provider',
|
||||
'endpoint',
|
||||
@@ -25,17 +28,22 @@ class UsageLog extends Model
|
||||
'cost',
|
||||
'status',
|
||||
'error_message',
|
||||
'timestamp',
|
||||
'metadata',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
protected $casts = [
|
||||
'prompt_tokens' => 'integer',
|
||||
'completion_tokens' => 'integer',
|
||||
'total_tokens' => 'integer',
|
||||
'cost' => 'decimal:6',
|
||||
'timestamp' => 'datetime',
|
||||
'metadata' => 'array',
|
||||
];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return [
|
||||
'timestamp' => 'datetime',
|
||||
'prompt_tokens' => 'integer',
|
||||
'completion_tokens' => 'integer',
|
||||
'total_tokens' => 'integer',
|
||||
'cost' => 'double',
|
||||
];
|
||||
return $this->belongsTo(GatewayUser::class, 'user_id', 'user_id');
|
||||
}
|
||||
|
||||
public function gatewayUser()
|
||||
@@ -45,9 +53,10 @@ class UsageLog extends Model
|
||||
|
||||
public function apiKey()
|
||||
{
|
||||
return $this->belongsTo(ApiKey::class, 'api_key_id', 'id');
|
||||
return $this->belongsTo(ApiKey::class, 'api_key', 'token');
|
||||
}
|
||||
|
||||
// Scopes
|
||||
public function scopeSuccess($query)
|
||||
{
|
||||
return $query->where('status', 'success');
|
||||
@@ -55,21 +64,6 @@ class UsageLog extends Model
|
||||
|
||||
public function scopeFailed($query)
|
||||
{
|
||||
return $query->where('status', '!=', 'success');
|
||||
}
|
||||
|
||||
public function scopeToday($query)
|
||||
{
|
||||
return $query->whereDate('timestamp', today());
|
||||
}
|
||||
|
||||
public function scopeDateRange($query, $start, $end)
|
||||
{
|
||||
return $query->whereBetween('timestamp', [$start, $end]);
|
||||
}
|
||||
|
||||
public function getCostFormattedAttribute()
|
||||
{
|
||||
return $this->cost ? '$' . number_format($this->cost, 4) : 'N/A';
|
||||
return $query->where('status', 'failed');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,4 +45,36 @@ class User extends Authenticatable
|
||||
'password' => 'hashed',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user's budget
|
||||
*/
|
||||
public function budget()
|
||||
{
|
||||
return $this->hasOne(UserBudget::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user's rate limit
|
||||
*/
|
||||
public function rateLimit()
|
||||
{
|
||||
return $this->hasOne(RateLimit::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user's provider credentials
|
||||
*/
|
||||
public function providerCredentials()
|
||||
{
|
||||
return $this->hasMany(UserProviderCredential::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user's LLM requests
|
||||
*/
|
||||
public function llmRequests()
|
||||
{
|
||||
return $this->hasMany(LlmRequest::class);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user