all(), [ 'provider' => 'sometimes|string|in:openai,anthropic,gemini,deepseek,mistral', 'sort' => 'sometimes|string|in:price,name,provider', ]); if ($validator->fails()) { return response()->json([ 'error' => [ 'code' => 'validation_error', 'message' => 'Invalid query parameters', 'status' => 422, 'details' => $validator->errors(), ], ], 422); } $query = ModelPricing::where('is_active', true) ->whereNotNull('model'); // Apply filters if ($request->has('provider')) { $query->where('provider', $request->input('provider')); } // Apply sorting $sort = $request->input('sort', 'name'); switch ($sort) { case 'price': $query->orderBy('output_price_per_million'); break; case 'provider': $query->orderBy('provider')->orderBy('model'); break; default: $query->orderBy('model'); } $models = $query->get(); $data = $models->map(function ($model) { return [ 'provider' => $model->provider, 'provider_name' => $this->getProviderName($model->provider), 'model' => $model->model, 'model_name' => $this->getModelDisplayName($model->model), 'pricing' => [ 'input_per_1k_tokens' => round($model->input_price_per_million / 1000, 6), 'output_per_1k_tokens' => round($model->output_price_per_million / 1000, 6), 'currency' => 'USD', ], 'last_updated' => $model->updated_at->toIso8601String(), ]; }); $providersCount = $models->pluck('provider')->unique()->count(); $lastSync = $models->max('updated_at'); return response()->json([ 'data' => $data, 'meta' => [ 'total_models' => $data->count(), 'providers_count' => $providersCount, 'last_sync' => $lastSync?->toIso8601String(), ], ]); } /** * Calculate costs for hypothetical requests * * Cost calculator that shows estimated costs for a given number of tokens * with a specific model. * * ## Query Parameters * * - `model` (required) - Model ID (e.g., gpt-4-turbo, claude-3-5-sonnet-20241022) * - `input_tokens` (required) - Number of input tokens * - `output_tokens` (required) - Number of output tokens * * ## Example Request * * ``` * GET /api/pricing/calculator?model=gpt-4-turbo&input_tokens=1000&output_tokens=500 * ``` * * ## Example Response * * ```json * { * "data": { * "model": "gpt-4-turbo", * "provider": "openai", * "input_tokens": 1000, * "output_tokens": 500, * "pricing": { * "input_per_1k": 0.01, * "output_per_1k": 0.03, * "currency": "USD" * }, * "calculation": { * "input_cost": 0.01, * "output_cost": 0.015, * "total_cost": 0.025, * "currency": "USD" * }, * "examples": { * "10_requests": 0.25, * "100_requests": 2.50, * "1000_requests": 25.00 * } * } * } * ``` * * @tags Pricing * * @param Request $request * @return JsonResponse */ public function calculator(Request $request): JsonResponse { $validator = Validator::make($request->all(), [ 'model' => 'required|string', 'input_tokens' => 'required|integer|min:0', 'output_tokens' => 'required|integer|min:0', ]); if ($validator->fails()) { return response()->json([ 'error' => [ 'code' => 'validation_error', 'message' => 'Invalid query parameters', 'status' => 422, 'details' => $validator->errors(), ], ], 422); } $modelId = $request->input('model'); $inputTokens = $request->input('input_tokens'); $outputTokens = $request->input('output_tokens'); // Find the model $model = ModelPricing::where('model', $modelId) ->where('is_active', true) ->first(); if (!$model) { return response()->json([ 'error' => [ 'code' => 'not_found', 'message' => "Model '{$modelId}' not found", 'status' => 404, ], ], 404); } // Calculate costs (convert from per-million to per-1k) $inputPricePer1k = $model->input_price_per_million / 1000; $outputPricePer1k = $model->output_price_per_million / 1000; $inputCost = ($inputTokens / 1000) * $inputPricePer1k; $outputCost = ($outputTokens / 1000) * $outputPricePer1k; $totalCost = $inputCost + $outputCost; // Calculate examples for different request volumes $examples = [ '1_request' => round($totalCost, 4), '10_requests' => round($totalCost * 10, 4), '100_requests' => round($totalCost * 100, 2), '1000_requests' => round($totalCost * 1000, 2), ]; return response()->json([ 'data' => [ 'model' => $model->model, 'provider' => $model->provider, 'input_tokens' => $inputTokens, 'output_tokens' => $outputTokens, 'pricing' => [ 'input_per_1k' => round($inputPricePer1k, 6), 'output_per_1k' => round($outputPricePer1k, 6), 'currency' => 'USD', ], 'calculation' => [ 'input_cost' => round($inputCost, 6), 'output_cost' => round($outputCost, 6), 'total_cost' => round($totalCost, 6), 'currency' => 'USD', ], 'examples' => $examples, 'context' => [ 'tokens_per_page' => 750, // Approximate 'estimated_pages_input' => round($inputTokens / 750, 1), 'estimated_pages_output' => round($outputTokens / 750, 1), ], ], ]); } /** * Compare pricing across multiple models * * Compare costs for the same token counts across different models. * * ## Query Parameters * * - `models` (required) - Comma-separated list of model IDs * - `input_tokens` (required) - Number of input tokens * - `output_tokens` (required) - Number of output tokens * * ## Example Request * * ``` * GET /api/pricing/compare?models=gpt-4-turbo,claude-3-5-sonnet-20241022&input_tokens=1000&output_tokens=500 * ``` * * @tags Pricing * * @param Request $request * @return JsonResponse */ public function compare(Request $request): JsonResponse { $validator = Validator::make($request->all(), [ 'models' => 'required|string', 'input_tokens' => 'required|integer|min:0', 'output_tokens' => 'required|integer|min:0', ]); if ($validator->fails()) { return response()->json([ 'error' => [ 'code' => 'validation_error', 'message' => 'Invalid query parameters', 'status' => 422, 'details' => $validator->errors(), ], ], 422); } $modelIds = explode(',', $request->input('models')); $inputTokens = $request->input('input_tokens'); $outputTokens = $request->input('output_tokens'); // Get all models $models = ModelPricing::whereIn('model', $modelIds) ->where('is_active', true) ->get(); if ($models->isEmpty()) { return response()->json([ 'error' => [ 'code' => 'not_found', 'message' => 'No valid models found', 'status' => 404, ], ], 404); } // Calculate costs for each model $comparisons = $models->map(function ($model) use ($inputTokens, $outputTokens) { $inputPricePer1k = $model->input_price_per_million / 1000; $outputPricePer1k = $model->output_price_per_million / 1000; $inputCost = ($inputTokens / 1000) * $inputPricePer1k; $outputCost = ($outputTokens / 1000) * $outputPricePer1k; $totalCost = $inputCost + $outputCost; return [ 'model' => $model->model, 'model_name' => $this->getModelDisplayName($model->model), 'provider' => $model->provider, 'provider_name' => $this->getProviderName($model->provider), 'costs' => [ 'input_cost' => round($inputCost, 6), 'output_cost' => round($outputCost, 6), 'total_cost' => round($totalCost, 6), ], 'pricing' => [ 'input_per_1k' => round($inputPricePer1k, 6), 'output_per_1k' => round($outputPricePer1k, 6), ], ]; })->sortBy('costs.total_cost')->values(); // Find cheapest and most expensive $cheapest = $comparisons->first(); $mostExpensive = $comparisons->last(); $savings = $mostExpensive['costs']['total_cost'] - $cheapest['costs']['total_cost']; $savingsPercent = $mostExpensive['costs']['total_cost'] > 0 ? ($savings / $mostExpensive['costs']['total_cost']) * 100 : 0; return response()->json([ 'data' => [ 'input_tokens' => $inputTokens, 'output_tokens' => $outputTokens, 'comparisons' => $comparisons, 'summary' => [ 'cheapest' => $cheapest['model'], 'cheapest_cost' => $cheapest['costs']['total_cost'], 'most_expensive' => $mostExpensive['model'], 'most_expensive_cost' => $mostExpensive['costs']['total_cost'], 'max_savings' => round($savings, 6), 'max_savings_percent' => round($savingsPercent, 1), ], ], ]); } /** * Get human-readable provider name */ private function getProviderName(string $provider): string { return match ($provider) { 'openai' => 'OpenAI', 'anthropic' => 'Anthropic', 'gemini' => 'Google Gemini', 'deepseek' => 'DeepSeek', 'mistral' => 'Mistral AI', default => ucfirst($provider), }; } /** * Get model display name from model ID */ private function getModelDisplayName(string $modelId): string { // Convert model ID to a readable display name // e.g., "gpt-4-turbo" -> "GPT-4 Turbo" return ucwords(str_replace(['-', '_'], ' ', $modelId)); } }