getUserCredential($user, $provider); // 2. Create provider instance $providerInstance = ProviderFactory::create($provider, $credential->api_key); // 3. Build request payload $requestPayload = [ 'provider' => $provider, 'model' => $model, 'messages' => $messages, 'options' => $options, ]; try { // 4. Make the API request $response = $providerInstance->chatCompletion($messages, array_merge($options, ['model' => $model])); // 5. Normalize response $normalized = $providerInstance->normalizeResponse($response); // 6. Calculate response time $responseTimeMs = (int) round((microtime(true) - $startTime) * 1000); // 7. Calculate costs $costs = $this->costCalculator->calculate( $provider, $normalized['model'], $normalized['usage']['prompt_tokens'], $normalized['usage']['completion_tokens'] ); // 8. Log request asynchronously $requestId = $this->requestLogger->logSuccess( $user->id, $provider, $normalized['model'], $requestPayload, $normalized, $costs, $responseTimeMs, $ipAddress, $userAgent ); // 9. Update user budget (synchronously for accuracy) $this->updateUserBudget($user, $costs['total_cost']); // 10. Return 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 failure $this->requestLogger->logFailure( $user->id, $provider, $model, $requestPayload, $e->getMessage(), $e->getCode(), $ipAddress, $userAgent ); throw $e; } } /** * Get user's credential for a provider */ private function getUserCredential(User $user, string $provider): UserProviderCredential { $credential = UserProviderCredential::where('user_id', $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 */ private function updateUserBudget(User $user, float $cost): void { $budget = $user->budget; if (!$budget) { return; // No budget configured } $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()]); } } } }