getUserCredential($user, $provider); // 2. Create provider instance $providerInstance = ProviderFactory::create($provider, $credential->api_key); // 3. Build request payload for logging $requestPayload = [ 'provider' => $provider, 'model' => $model, 'messages' => $messages, 'options' => $options, ]; try { // 4. Make the API request to LLM provider $response = $providerInstance->chatCompletion($messages, array_merge($options, ['model' => $model])); // 5. Normalize response to standard format $normalized = $providerInstance->normalizeResponse($response); // 6. Calculate response time $responseTimeMs = (int) round((microtime(true) - $startTime) * 1000); // 7. Calculate costs based on token usage $costs = $this->costCalculator->calculate( $provider, $normalized['model'], $normalized['usage']['prompt_tokens'], $normalized['usage']['completion_tokens'] ); // 8. Log successful request $requestId = $this->requestLogger->logSuccess( $user->user_id, // Gateway user ID $provider, $normalized['model'], $requestPayload, $normalized, $costs, $responseTimeMs, $ipAddress, $userAgent ); // 9. Update user's spending budget $this->updateUserBudget($user, $costs['total_cost']); // 10. Return standardized response with metadata return [ 'success' => true, 'request_id' => $requestId, 'provider' => $provider, 'model' => $normalized['model'], 'content' => $normalized['content'], 'role' => $normalized['role'], 'finish_reason' => $normalized['finish_reason'], 'usage' => $normalized['usage'], 'cost' => $costs, 'response_time_ms' => $responseTimeMs, ]; } catch (ProviderException $e) { // Log failed request $this->requestLogger->logFailure( $user->user_id, $provider, $model, $requestPayload, $e->getMessage(), $e->getCode(), $ipAddress, $userAgent ); throw $e; } } /** * Get user's credential for a specific provider * * @param GatewayUser $user * @param string $provider * @return GatewayUserCredential * @throws ProviderException */ private function getUserCredential(GatewayUser $user, string $provider): GatewayUserCredential { $credential = GatewayUserCredential::where('gateway_user_id', $user->user_id) ->where('provider', $provider) ->where('is_active', true) ->first(); if (!$credential) { throw new ProviderException( "No active API credentials found for provider: {$provider}", 400 ); } // Update last used timestamp $credential->update(['last_used_at' => now()]); return $credential; } /** * 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(GatewayUser $user, float $cost): void { // Increment spending using model method $user->incrementSpending($cost); // 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"); } // Check if budget is now exceeded if ($user->hasExceededBudget()) { Log::warning("Budget exceeded: Gateway user {$user->user_id} has exceeded monthly budget"); } } }