Files
laravel-llm-gateway/laravel-app/app/Http/Controllers/Api/ProviderController.php
wtrinkl b6d75d51e3 feat: Implementiere umfassende RESTful API für LLM Gateway
Fügt 21 neue API-Endpoints in 4 Phasen hinzu:

Phase 1 - Foundation (Provider & Models):
- GET /api/providers - Liste aller Provider
- GET /api/providers/{provider} - Provider-Details
- GET /api/models - Liste aller Models mit Filtering/Sorting
- GET /api/models/{provider}/{model} - Model-Details

Phase 2 - Core Features (Credentials, Budget, Pricing):
- GET/POST/PUT/DELETE /api/credentials - Credential-Management
- POST /api/credentials/{id}/test - Connection Testing
- GET /api/budget - Budget-Status mit Projektionen
- GET /api/budget/history - Budget-Historie
- GET /api/pricing - Model-Pricing-Listen
- GET /api/pricing/calculator - Kosten-Kalkulator
- GET /api/pricing/compare - Preis-Vergleich

Phase 3 - Analytics (Usage Statistics):
- GET /api/usage/summary - Umfassende Statistiken
- GET /api/usage/requests - Request-History mit Pagination
- GET /api/usage/requests/{id} - Request-Details
- GET /api/usage/charts - Chart-Daten (4 Typen)

Phase 4 - Account (Account Info & Activity):
- GET /api/account - User-Informationen
- GET /api/account/activity - Activity-Log

Features:
- Vollständige Scramble/Swagger-Dokumentation
- Consistent Error-Handling
- API-Key Authentication
- Filtering, Sorting, Pagination
- Budget-Tracking mit Alerts
- Provider-Breakdown
- Performance-Metriken
- Chart-Ready-Data

Controller erstellt:
- ProviderController
- ModelController
- CredentialController
- BudgetController
- PricingController
- UsageController
- AccountController

Dokumentation:
- API_KONZEPT.md - Vollständiges API-Konzept
- API_IMPLEMENTATION_STATUS.txt - Implementation-Tracking
- API_IMPLEMENTATION_SUMMARY.md - Zusammenfassung und Workflows
2025-11-19 12:33:11 +01:00

312 lines
11 KiB
PHP

<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Services\LLM\ProviderFactory;
use App\Models\{GatewayUserCredential, LlmRequest, ModelPricing};
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class ProviderController extends Controller
{
/**
* Get list of all supported LLM providers
*
* Returns a list of all LLM providers supported by the gateway, including their
* availability status, credential status for the authenticated user, and basic statistics.
*
* ## Response Fields
*
* - `id` - Provider identifier (openai, anthropic, gemini, deepseek, mistral)
* - `name` - Human-readable provider name
* - `status` - Always "available" for supported providers
* - `has_credentials` - Whether the user has configured credentials for this provider
* - `credentials_status` - Status of the credentials (active, inactive, null if not configured)
* - `last_tested` - When credentials were last tested (ISO 8601)
* - `supported_features` - Array of supported features (chat, streaming, etc.)
* - `models_count` - Number of models available from this provider
*
* ## Example Response
*
* ```json
* {
* "data": [
* {
* "id": "openai",
* "name": "OpenAI",
* "status": "available",
* "has_credentials": true,
* "credentials_status": "active",
* "last_tested": "2025-11-19T10:30:00Z",
* "supported_features": ["chat", "streaming"],
* "models_count": 12
* }
* ]
* }
* ```
*
* @tags Providers
*
* @param Request $request
* @return JsonResponse
*/
public function index(Request $request): JsonResponse
{
$user = $request->user();
$providers = ProviderFactory::getSupportedProviders();
$providerData = [];
foreach ($providers as $providerId) {
// Get credential info for this provider
$credential = GatewayUserCredential::where('gateway_user_id', $user->user_id)
->where('provider', $providerId)
->first();
// Get model count for this provider
$modelsCount = ModelPricing::where('provider', $providerId)
->where('is_active', true)
->count();
$providerData[] = [
'id' => $providerId,
'name' => $this->getProviderName($providerId),
'status' => 'available',
'has_credentials' => $credential !== null,
'credentials_status' => $credential?->is_active ? 'active' : ($credential ? 'inactive' : null),
'last_tested' => $credential?->last_tested_at?->toIso8601String(),
'supported_features' => $this->getProviderFeatures($providerId),
'models_count' => $modelsCount,
];
}
return response()->json([
'data' => $providerData,
]);
}
/**
* Get detailed information about a specific provider
*
* Returns comprehensive information about a specific LLM provider, including:
* - Provider details and capabilities
* - User's credential status
* - Available models with pricing
* - Usage statistics
*
* ## Path Parameters
*
* - `provider` - Provider ID (openai, anthropic, gemini, deepseek, mistral)
*
* ## Example Response
*
* ```json
* {
* "data": {
* "id": "openai",
* "name": "OpenAI",
* "description": "OpenAI GPT Models",
* "status": "available",
* "has_credentials": true,
* "credentials_status": "active",
* "credentials": {
* "api_key_format": "sk-...",
* "organization_id_required": false,
* "last_tested": "2025-11-19T10:30:00Z",
* "test_status": "success"
* },
* "supported_features": ["chat", "streaming", "function_calling"],
* "models": [
* {
* "id": "gpt-4-turbo",
* "name": "GPT-4 Turbo",
* "context_window": 128000,
* "max_output_tokens": 4096,
* "supports_streaming": true,
* "pricing": {
* "input_per_1k": 0.01,
* "output_per_1k": 0.03,
* "currency": "USD"
* }
* }
* ],
* "statistics": {
* "total_requests": 1250,
* "total_cost": 45.67,
* "total_tokens": 2500000,
* "last_used": "2025-11-19T11:45:00Z"
* }
* }
* }
* ```
*
* @tags Providers
*
* @param Request $request
* @param string $provider
* @return JsonResponse
*/
public function show(Request $request, string $provider): JsonResponse
{
// Validate provider exists
$supportedProviders = ProviderFactory::getSupportedProviders();
if (!in_array($provider, $supportedProviders)) {
return response()->json([
'error' => [
'code' => 'not_found',
'message' => "Provider '{$provider}' not found",
'status' => 404,
],
], 404);
}
$user = $request->user();
// Get credential info
$credential = GatewayUserCredential::where('gateway_user_id', $user->user_id)
->where('provider', $provider)
->first();
// Get models for this provider
$models = ModelPricing::where('provider', $provider)
->where('is_active', true)
->orderBy('display_name')
->get()
->map(function ($model) {
return [
'id' => $model->model_id,
'name' => $model->display_name,
'context_window' => $model->context_window,
'max_output_tokens' => $model->max_output_tokens,
'supports_streaming' => true, // Default to true for now
'supports_function_calling' => in_array($model->provider, ['openai', 'anthropic']),
'pricing' => [
'input_per_1k' => $model->input_price_per_1k,
'output_per_1k' => $model->output_price_per_1k,
'currency' => 'USD',
],
];
});
// Get usage statistics for this provider
$statistics = LlmRequest::where('gateway_user_id', $user->user_id)
->where('provider', $provider)
->where('status', 'success')
->selectRaw('
COUNT(*) as total_requests,
SUM(total_cost) as total_cost,
SUM(total_tokens) as total_tokens,
MAX(created_at) as last_used
')
->first();
$response = [
'data' => [
'id' => $provider,
'name' => $this->getProviderName($provider),
'description' => $this->getProviderDescription($provider),
'status' => 'available',
'has_credentials' => $credential !== null,
'credentials_status' => $credential?->is_active ? 'active' : ($credential ? 'inactive' : null),
'credentials' => $credential ? [
'api_key_format' => $this->getApiKeyFormat($provider),
'organization_id_required' => false,
'last_tested' => $credential->last_tested_at?->toIso8601String(),
'test_status' => $credential->test_status,
] : null,
'supported_features' => $this->getProviderFeatures($provider),
'api_documentation' => $this->getProviderDocUrl($provider),
'models' => $models,
'statistics' => [
'total_requests' => $statistics->total_requests ?? 0,
'total_cost' => round($statistics->total_cost ?? 0, 4),
'total_tokens' => $statistics->total_tokens ?? 0,
'last_used' => $statistics->last_used?->toIso8601String(),
],
],
];
return response()->json($response);
}
/**
* 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 provider description
*/
private function getProviderDescription(string $provider): string
{
return match ($provider) {
'openai' => 'OpenAI GPT Models',
'anthropic' => 'Anthropic Claude Models',
'gemini' => 'Google Gemini Models',
'deepseek' => 'DeepSeek AI Models',
'mistral' => 'Mistral AI Models',
default => "{$provider} Models",
};
}
/**
* Get provider features
*/
private function getProviderFeatures(string $provider): array
{
$baseFeatures = ['chat'];
// All providers support streaming
$baseFeatures[] = 'streaming';
// OpenAI and Anthropic support function calling
if (in_array($provider, ['openai', 'anthropic'])) {
$baseFeatures[] = 'function_calling';
}
return $baseFeatures;
}
/**
* Get API key format hint
*/
private function getApiKeyFormat(string $provider): string
{
return match ($provider) {
'openai' => 'sk-...',
'anthropic' => 'sk-ant-...',
'gemini' => 'AI...',
'deepseek' => 'sk-...',
'mistral' => 'sk-...',
default => 'API key',
};
}
/**
* Get provider documentation URL
*/
private function getProviderDocUrl(string $provider): string
{
return match ($provider) {
'openai' => 'https://platform.openai.com/docs/api-reference',
'anthropic' => 'https://docs.anthropic.com/claude/reference',
'gemini' => 'https://ai.google.dev/docs',
'deepseek' => 'https://platform.deepseek.com/api-docs',
'mistral' => 'https://docs.mistral.ai/api',
default => '#',
};
}
}