Skip to main content

Overview

The AiUsageService manages AI diagnostic usage tracking, quota validation, and usage recording for companies. It enforces plan-based limits on queries and token consumption. Namespace: App\Services\AiUsageService Dependencies:
  • AiPlanPolicyService - Provides plan limit policies
  • AiTokenEstimator - Estimates token usage from character counts

Methods

validateBeforeUsage

Validates that a company can use AI diagnostics before making the request. Checks plan support, query limits, and token limits.
public function validateBeforeUsage(
    Company $company,
    string $plan,
    int $projectedPromptTokens,
    ?string $yearMonth = null
): void

Parameters

company
Company
required
The company model instance requesting AI diagnostics
plan
string
required
The subscription plan identifier (e.g., “starter”, “professional”, “enterprise”)
projectedPromptTokens
int
required
Estimated number of tokens for the prompt
yearMonth
string|null
Year-month string in “Y-m” format. Defaults to current month if null

Throws

AiUsageException with specific status codes:
  • blocked_plan - Plan does not support AI features
  • blocked_quota - Monthly query limit exceeded
  • blocked_tokens - Monthly token limit would be exceeded

Example Usage

use App\Services\AiUsageService;
use App\Services\Exceptions\AiUsageException;

$aiUsage = app(AiUsageService::class);

try {
    $aiUsage->validateBeforeUsage(
        company: $company,
        plan: 'professional',
        projectedPromptTokens: 1500
    );
    
    // Proceed with AI diagnostic
    $analysis = $aiDiagnostic->analyze(...);
    
} catch (AiUsageException $e) {
    // Handle quota exceeded
    return response()->json([
        'error' => $e->getMessage(),
        'status' => $e->status()
    ], 429);
}

validateAfterUsage

Validates token usage after receiving the AI response. Ensures actual token consumption doesn’t exceed limits.
public function validateAfterUsage(
    Company $company,
    string $plan,
    int $realTotalTokens,
    ?string $yearMonth = null
): void

Parameters

company
Company
required
The company model instance
plan
string
required
The subscription plan identifier
realTotalTokens
int
required
Actual total tokens consumed (prompt + response)
yearMonth
string|null
Year-month string in “Y-m” format. Defaults to current month if null

Throws

AiUsageException with status blocked_tokens if limit exceeded

monthlyUsage

Retrieves the current month’s AI usage statistics for a company.
public function monthlyUsage(
    Company $company,
    ?string $yearMonth = null
): array

Parameters

company
Company
required
The company model instance
yearMonth
string|null
Year-month string in “Y-m” format. Defaults to current month if null

Returns

Returns an associative array:
[
    'queries_used' => int,  // Number of successful AI queries this month
    'tokens_used' => int,   // Total tokens consumed this month
]

Example Usage

$usage = $aiUsage->monthlyUsage($company);

echo "Queries used: {$usage['queries_used']}";
echo "Tokens used: {$usage['tokens_used']}";

// Check specific month
$lastMonth = $aiUsage->monthlyUsage($company, '2025-12');

registerSuccess

Records a successful AI diagnostic usage in the database.
public function registerSuccess(
    Company $company,
    Order $order,
    string $plan,
    int $promptChars,
    int $responseChars,
    ?string $yearMonth = null
): CompanyAiUsage

Parameters

company
Company
required
The company model instance
order
Order
required
The order that received the AI diagnostic
plan
string
required
The subscription plan identifier at time of usage
promptChars
int
required
Character count of the prompt sent to AI
responseChars
int
required
Character count of the AI response received
yearMonth
string|null
Year-month string in “Y-m” format. Defaults to current month if null

Returns

Returns the created CompanyAiUsage model instance with status 'success'

Example Usage

$promptChars = mb_strlen($promptText);
$responseChars = mb_strlen(json_encode($analysis, JSON_UNESCAPED_UNICODE));

$usage = $aiUsage->registerSuccess(
    company: $company,
    order: $order,
    plan: 'professional',
    promptChars: $promptChars,
    responseChars: $responseChars
);

echo "Tokens consumed: {$usage->total_tokens_estimated}";

registerBlocked

Records a blocked or failed AI diagnostic attempt.
public function registerBlocked(
    Company $company,
    ?Order $order,
    string $plan,
    string $status,
    string $errorMessage,
    int $promptChars = 0,
    int $responseChars = 0,
    ?string $yearMonth = null
): CompanyAiUsage

Parameters

company
Company
required
The company model instance
order
Order|null
required
The order that attempted AI diagnostic (can be null for pre-order validation)
plan
string
required
The subscription plan identifier
status
string
required
The blocking status (e.g., “blocked_plan”, “blocked_quota”, “blocked_tokens”)
errorMessage
string
required
Human-readable error message describing why the request was blocked
promptChars
int
Character count of the prompt (defaults to 0)
responseChars
int
Character count of the response (defaults to 0)
yearMonth
string|null
Year-month string in “Y-m” format. Defaults to current month if null

Returns

Returns the created CompanyAiUsage model instance with the specified error status

Example Usage

try {
    $aiUsage->validateBeforeUsage($company, $plan, $tokens);
} catch (AiUsageException $e) {
    // Log the blocked attempt
    $aiUsage->registerBlocked(
        company: $company,
        order: $order,
        plan: $plan,
        status: $e->status(),
        errorMessage: $e->getMessage(),
        promptChars: mb_strlen($promptText)
    );
    
    throw $e;
}

Token Estimation

The service uses AiTokenEstimator to convert character counts to estimated tokens:
$promptTokens = $tokenEstimator->estimateFromChars($promptChars);
$responseTokens = $tokenEstimator->estimateFromChars($responseChars);
$totalTokens = $promptTokens + $responseTokens;

Usage Tracking

All usage records are stored in the company_ai_usage table with:
  • year_month - Month identifier for quota tracking
  • status - 'success', 'blocked_plan', 'blocked_quota', or 'blocked_tokens'
  • plan_snapshot - The plan at time of usage
  • Token estimates for prompt, response, and total
  • Error messages for blocked attempts
Only records with status = 'success' count toward monthly limits.

Build docs developers (and LLMs) love