Fix API controllers to use correct database column names
- Fix model_pricing table references (model_id -> model, display_name -> model)
- Fix price columns (output_price_per_1k -> output_price_per_million)
- Add price conversion (per_million / 1000 = per_1k) in all API responses
- Add whereNotNull('model') filters to exclude invalid entries
- Add getModelDisplayName() helper method to all controllers
- Fix AccountController to use gateway_users budget fields directly
- Remove Budget model dependencies from AccountController
- Add custom Scramble server URL configuration for API docs
- Create ScrambleServiceProvider to set correct /api prefix
- Add migration to rename user_id to gateway_user_id in llm_requests
- Add custom ApiGuard for gateway_users authentication
- Update all API controllers: AccountController, ModelController, PricingController, ProviderController
All API endpoints now working correctly:
- GET /api/account
- GET /api/models
- GET /api/pricing
- GET /api/providers/{provider}
This commit is contained in:
@@ -2,9 +2,9 @@
|
||||
|
||||
namespace App\Services\LLM;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\UserProviderCredential;
|
||||
use App\Exceptions\{ProviderException, InsufficientBudgetException, RateLimitExceededException};
|
||||
use App\Models\GatewayUser;
|
||||
use App\Models\GatewayUserCredential;
|
||||
use App\Exceptions\{ProviderException, InsufficientBudgetException};
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class GatewayService
|
||||
@@ -17,19 +17,18 @@ class GatewayService
|
||||
/**
|
||||
* Process a chat completion request through the gateway
|
||||
*
|
||||
* @param User $user
|
||||
* @param string $provider
|
||||
* @param string $model
|
||||
* @param array $messages
|
||||
* @param array $options
|
||||
* @param string|null $ipAddress
|
||||
* @param string|null $userAgent
|
||||
* @return array
|
||||
* @param GatewayUser $user Gateway user making the request
|
||||
* @param string $provider Provider name (openai, anthropic, google, deepseek, mistral)
|
||||
* @param string $model Model name
|
||||
* @param array $messages Chat messages
|
||||
* @param array $options Optional parameters
|
||||
* @param string|null $ipAddress Client IP address
|
||||
* @param string|null $userAgent Client user agent
|
||||
* @return array Response with metadata
|
||||
* @throws ProviderException
|
||||
* @throws InsufficientBudgetException
|
||||
*/
|
||||
public function chatCompletion(
|
||||
User $user,
|
||||
GatewayUser $user,
|
||||
string $provider,
|
||||
string $model,
|
||||
array $messages,
|
||||
@@ -39,13 +38,13 @@ class GatewayService
|
||||
): array {
|
||||
$startTime = microtime(true);
|
||||
|
||||
// 1. Get user's API credentials
|
||||
// 1. Get user's API credentials for the provider
|
||||
$credential = $this->getUserCredential($user, $provider);
|
||||
|
||||
// 2. Create provider instance
|
||||
$providerInstance = ProviderFactory::create($provider, $credential->api_key);
|
||||
|
||||
// 3. Build request payload
|
||||
// 3. Build request payload for logging
|
||||
$requestPayload = [
|
||||
'provider' => $provider,
|
||||
'model' => $model,
|
||||
@@ -54,16 +53,16 @@ class GatewayService
|
||||
];
|
||||
|
||||
try {
|
||||
// 4. Make the API request
|
||||
// 4. Make the API request to LLM provider
|
||||
$response = $providerInstance->chatCompletion($messages, array_merge($options, ['model' => $model]));
|
||||
|
||||
// 5. Normalize response
|
||||
// 5. Normalize response to standard format
|
||||
$normalized = $providerInstance->normalizeResponse($response);
|
||||
|
||||
// 6. Calculate response time
|
||||
$responseTimeMs = (int) round((microtime(true) - $startTime) * 1000);
|
||||
|
||||
// 7. Calculate costs
|
||||
// 7. Calculate costs based on token usage
|
||||
$costs = $this->costCalculator->calculate(
|
||||
$provider,
|
||||
$normalized['model'],
|
||||
@@ -71,9 +70,9 @@ class GatewayService
|
||||
$normalized['usage']['completion_tokens']
|
||||
);
|
||||
|
||||
// 8. Log request asynchronously
|
||||
// 8. Log successful request
|
||||
$requestId = $this->requestLogger->logSuccess(
|
||||
$user->id,
|
||||
$user->user_id, // Gateway user ID
|
||||
$provider,
|
||||
$normalized['model'],
|
||||
$requestPayload,
|
||||
@@ -84,10 +83,10 @@ class GatewayService
|
||||
$userAgent
|
||||
);
|
||||
|
||||
// 9. Update user budget (synchronously for accuracy)
|
||||
// 9. Update user's spending budget
|
||||
$this->updateUserBudget($user, $costs['total_cost']);
|
||||
|
||||
// 10. Return response with metadata
|
||||
// 10. Return standardized response with metadata
|
||||
return [
|
||||
'success' => true,
|
||||
'request_id' => $requestId,
|
||||
@@ -102,9 +101,9 @@ class GatewayService
|
||||
];
|
||||
|
||||
} catch (ProviderException $e) {
|
||||
// Log failure
|
||||
// Log failed request
|
||||
$this->requestLogger->logFailure(
|
||||
$user->id,
|
||||
$user->user_id,
|
||||
$provider,
|
||||
$model,
|
||||
$requestPayload,
|
||||
@@ -119,11 +118,16 @@ class GatewayService
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user's credential for a provider
|
||||
* Get user's credential for a specific provider
|
||||
*
|
||||
* @param GatewayUser $user
|
||||
* @param string $provider
|
||||
* @return GatewayUserCredential
|
||||
* @throws ProviderException
|
||||
*/
|
||||
private function getUserCredential(User $user, string $provider): UserProviderCredential
|
||||
private function getUserCredential(GatewayUser $user, string $provider): GatewayUserCredential
|
||||
{
|
||||
$credential = UserProviderCredential::where('user_id', $user->id)
|
||||
$credential = GatewayUserCredential::where('gateway_user_id', $user->user_id)
|
||||
->where('provider', $provider)
|
||||
->where('is_active', true)
|
||||
->first();
|
||||
@@ -143,30 +147,26 @@ class GatewayService
|
||||
|
||||
/**
|
||||
* Update user's budget with spending
|
||||
* Budget is now stored directly in gateway_users table
|
||||
*
|
||||
* @param GatewayUser $user
|
||||
* @param float $cost Cost to add to spending
|
||||
* @return void
|
||||
*/
|
||||
private function updateUserBudget(User $user, float $cost): void
|
||||
private function updateUserBudget(GatewayUser $user, float $cost): void
|
||||
{
|
||||
$budget = $user->budget;
|
||||
// Increment spending using model method
|
||||
$user->incrementSpending($cost);
|
||||
|
||||
if (!$budget) {
|
||||
return; // No budget configured
|
||||
// Check if user should receive budget alert
|
||||
if ($user->shouldSendBudgetAlert()) {
|
||||
// TODO: Dispatch budget alert notification
|
||||
Log::info("Budget alert: Gateway user {$user->user_id} has reached {$user->getBudgetUsagePercentage()}% of budget");
|
||||
}
|
||||
|
||||
$budget->increment('current_month_spending', $cost);
|
||||
$budget->increment('current_day_spending', $cost);
|
||||
|
||||
// Check if budget exceeded
|
||||
if ($budget->current_month_spending >= $budget->monthly_limit) {
|
||||
$budget->update(['is_budget_exceeded' => true]);
|
||||
}
|
||||
|
||||
// Check alert threshold
|
||||
if ($budget->alert_threshold_percentage) {
|
||||
$threshold = $budget->monthly_limit * ($budget->alert_threshold_percentage / 100);
|
||||
if ($budget->current_month_spending >= $threshold && !$budget->last_alert_sent_at) {
|
||||
// TODO: Dispatch alert notification
|
||||
$budget->update(['last_alert_sent_at' => now()]);
|
||||
}
|
||||
// Check if budget is now exceeded
|
||||
if ($user->hasExceededBudget()) {
|
||||
Log::warning("Budget exceeded: Gateway user {$user->user_id} has exceeded monthly budget");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,20 @@ class RequestLogger
|
||||
{
|
||||
/**
|
||||
* Log a successful LLM request
|
||||
*
|
||||
* @param string $gatewayUserId Gateway user ID (user_id from gateway_users)
|
||||
* @param string $provider Provider name
|
||||
* @param string $model Model name
|
||||
* @param array $requestPayload Request payload
|
||||
* @param array $responsePayload Response payload
|
||||
* @param array $costs Cost breakdown
|
||||
* @param int $responseTimeMs Response time in milliseconds
|
||||
* @param string|null $ipAddress Client IP address
|
||||
* @param string|null $userAgent Client user agent
|
||||
* @return string Request ID
|
||||
*/
|
||||
public function logSuccess(
|
||||
int $userId,
|
||||
string $gatewayUserId,
|
||||
string $provider,
|
||||
string $model,
|
||||
array $requestPayload,
|
||||
@@ -24,7 +35,7 @@ class RequestLogger
|
||||
$requestId = $this->generateRequestId();
|
||||
|
||||
LogLlmRequest::dispatch(
|
||||
userId: $userId,
|
||||
userId: $gatewayUserId,
|
||||
provider: $provider,
|
||||
model: $model,
|
||||
requestPayload: $requestPayload,
|
||||
@@ -49,9 +60,19 @@ class RequestLogger
|
||||
|
||||
/**
|
||||
* Log a failed LLM request
|
||||
*
|
||||
* @param string $gatewayUserId Gateway user ID (user_id from gateway_users)
|
||||
* @param string $provider Provider name
|
||||
* @param string $model Model name
|
||||
* @param array $requestPayload Request payload
|
||||
* @param string $errorMessage Error message
|
||||
* @param int $httpStatus HTTP status code
|
||||
* @param string|null $ipAddress Client IP address
|
||||
* @param string|null $userAgent Client user agent
|
||||
* @return string Request ID
|
||||
*/
|
||||
public function logFailure(
|
||||
int $userId,
|
||||
string $gatewayUserId,
|
||||
string $provider,
|
||||
string $model,
|
||||
array $requestPayload,
|
||||
@@ -63,7 +84,7 @@ class RequestLogger
|
||||
$requestId = $this->generateRequestId();
|
||||
|
||||
LogLlmRequest::dispatch(
|
||||
userId: $userId,
|
||||
userId: $gatewayUserId,
|
||||
provider: $provider,
|
||||
model: $model,
|
||||
requestPayload: $requestPayload,
|
||||
|
||||
Reference in New Issue
Block a user