- Fix model_pricing table references (model_id -> model, display_name -> model)
- Fix price columns (output_price_per_1k -> output_price_per_million)
- Add price conversion (per_million / 1000 = per_1k) in all API responses
- Add whereNotNull('model') filters to exclude invalid entries
- Add getModelDisplayName() helper method to all controllers
- Fix AccountController to use gateway_users budget fields directly
- Remove Budget model dependencies from AccountController
- Add custom Scramble server URL configuration for API docs
- Create ScrambleServiceProvider to set correct /api prefix
- Add migration to rename user_id to gateway_user_id in llm_requests
- Add custom ApiGuard for gateway_users authentication
- Update all API controllers: AccountController, ModelController, PricingController, ProviderController
All API endpoints now working correctly:
- GET /api/account
- GET /api/models
- GET /api/pricing
- GET /api/providers/{provider}
209 lines
6.7 KiB
PHP
209 lines
6.7 KiB
PHP
<?php
|
||
|
||
use Dedoc\Scramble\Http\Middleware\RestrictedDocsAccess;
|
||
|
||
return [
|
||
/*
|
||
* Your API path. By default, all routes starting with this path will be added to the docs.
|
||
* If you need to change this behavior, you can add your custom routes resolver using `Scramble::routes()`.
|
||
*/
|
||
'api_path' => 'api',
|
||
|
||
/*
|
||
* Your API domain. By default, app domain is used. This is also a part of the default API routes
|
||
* matcher, so when implementing your own, make sure you use this config if needed.
|
||
*/
|
||
'api_domain' => null,
|
||
|
||
/*
|
||
* The path where your OpenAPI specification will be exported.
|
||
*/
|
||
'export_path' => 'api.json',
|
||
|
||
'info' => [
|
||
/*
|
||
* API version.
|
||
*/
|
||
'version' => env('API_VERSION', '1.0.0'),
|
||
|
||
/*
|
||
* Description rendered on the home page of the API documentation (`/docs/api`).
|
||
*/
|
||
'description' => '
|
||
# Laravel LLM Gateway API
|
||
|
||
Multi-provider LLM Gateway supporting OpenAI, Anthropic, Google Gemini, DeepSeek, and Mistral AI.
|
||
|
||
## Authentication
|
||
|
||
All API requests require authentication via API key in the **Authorization** header:
|
||
|
||
```
|
||
Authorization: Bearer {your_api_key_here}
|
||
```
|
||
|
||
Gateway users receive API keys from the admin interface. Each key is linked to a specific gateway user with their budget limits, rate limits, and provider credentials.
|
||
|
||
## Providers
|
||
|
||
The gateway supports the following LLM providers:
|
||
|
||
* **openai** - OpenAI models (GPT-4, GPT-3.5-turbo, etc.)
|
||
* **anthropic** - Anthropic Claude models (Claude 3, Claude Sonnet, etc.)
|
||
* **gemini** - Google Gemini models (Gemini Pro, etc.)
|
||
* **deepseek** - DeepSeek models (DeepSeek Chat, DeepSeek Coder)
|
||
* **mistral** - Mistral AI models (Mistral Large, Mistral Medium, etc.)
|
||
|
||
## Rate Limits
|
||
|
||
Each gateway user has configurable rate limits (default: 60 requests/hour). Rate limit information is returned in error responses when exceeded:
|
||
|
||
```json
|
||
{
|
||
"error": "Rate limit exceeded",
|
||
"limit": 60,
|
||
"reset_at": "2024-01-15T14:30:00Z"
|
||
}
|
||
```
|
||
|
||
## Budgets
|
||
|
||
Monthly budget limits are enforced per gateway user. Costs are calculated based on token usage and provider-specific pricing. When the budget is exceeded, requests return:
|
||
|
||
```json
|
||
{
|
||
"error": "Budget exceeded",
|
||
"current": 150.50,
|
||
"limit": 100.00
|
||
}
|
||
```
|
||
|
||
## Error Handling
|
||
|
||
The API returns structured error responses:
|
||
|
||
* **400** Bad Request - Invalid parameters
|
||
* **401** Unauthorized - Invalid or missing API key
|
||
* **403** Forbidden - Budget exceeded
|
||
* **404** Not Found - User blocked
|
||
* **429** Too Many Requests - Rate limit exceeded
|
||
* **500** Internal Server Error - Unexpected error
|
||
|
||
## Cost Tracking
|
||
|
||
All requests are logged with:
|
||
* Model used
|
||
* Input/output tokens
|
||
* Calculated cost
|
||
* Provider response time
|
||
* Error details (if any)
|
||
|
||
Administrators can view detailed usage analytics in the admin interface.
|
||
',
|
||
],
|
||
|
||
/*
|
||
* Customize Stoplight Elements UI
|
||
*/
|
||
'ui' => [
|
||
/*
|
||
* Define the title of the documentation's website. App name is used when this config is `null`.
|
||
*/
|
||
'title' => null,
|
||
|
||
/*
|
||
* Define the theme of the documentation. Available options are `light`, `dark`, and `system`.
|
||
*/
|
||
'theme' => 'light',
|
||
|
||
/*
|
||
* Hide the `Try It` feature. Enabled by default.
|
||
*/
|
||
'hide_try_it' => false,
|
||
|
||
/*
|
||
* Hide the schemas in the Table of Contents. Enabled by default.
|
||
*/
|
||
'hide_schemas' => false,
|
||
|
||
/*
|
||
* URL to an image that displays as a small square logo next to the title, above the table of contents.
|
||
*/
|
||
'logo' => '',
|
||
|
||
/*
|
||
* Use to fetch the credential policy for the Try It feature. Options are: omit, include (default), and same-origin
|
||
*/
|
||
'try_it_credentials_policy' => 'include',
|
||
|
||
/*
|
||
* There are three layouts for Elements:
|
||
* - sidebar - (Elements default) Three-column design with a sidebar that can be resized.
|
||
* - responsive - Like sidebar, except at small screen sizes it collapses the sidebar into a drawer that can be toggled open.
|
||
* - stacked - Everything in a single column, making integrations with existing websites that have their own sidebar or other columns already.
|
||
*/
|
||
'layout' => 'responsive',
|
||
],
|
||
|
||
/*
|
||
* The list of servers of the API. By default, when `null`, server URL will be created from
|
||
* `scramble.api_path` and `scramble.api_domain` config variables. When providing an array, you
|
||
* will need to specify the local server URL manually (if needed).
|
||
*
|
||
* Example of non-default config (final URLs are generated using Laravel `url` helper):
|
||
*
|
||
* ```php
|
||
* 'servers' => [
|
||
* 'Live' => 'api',
|
||
* 'Prod' => 'https://scramble.dedoc.co/api',
|
||
* ],
|
||
* ```
|
||
*/
|
||
'servers' => [
|
||
'Local' => 'http://localhost/api',
|
||
],
|
||
|
||
/**
|
||
* Determines how Scramble stores the descriptions of enum cases.
|
||
* Available options:
|
||
* - 'description' – Case descriptions are stored as the enum schema's description using table formatting.
|
||
* - 'extension' – Case descriptions are stored in the `x-enumDescriptions` enum schema extension.
|
||
*
|
||
* @see https://redocly.com/docs-legacy/api-reference-docs/specification-extensions/x-enum-descriptions
|
||
* - false - Case descriptions are ignored.
|
||
*/
|
||
'enum_cases_description_strategy' => 'description',
|
||
|
||
/**
|
||
* Determines how Scramble stores the names of enum cases.
|
||
* Available options:
|
||
* - 'names' – Case names are stored in the `x-enumNames` enum schema extension.
|
||
* - 'varnames' - Case names are stored in the `x-enum-varnames` enum schema extension.
|
||
* - false - Case names are not stored.
|
||
*/
|
||
'enum_cases_names_strategy' => false,
|
||
|
||
/**
|
||
* When Scramble encounters deep objects in query parameters, it flattens the parameters so the generated
|
||
* OpenAPI document correctly describes the API. Flattening deep query parameters is relevant until
|
||
* OpenAPI 3.2 is released and query string structure can be described properly.
|
||
*
|
||
* For example, this nested validation rule describes the object with `bar` property:
|
||
* `['foo.bar' => ['required', 'int']]`.
|
||
*
|
||
* When `flatten_deep_query_parameters` is `true`, Scramble will document the parameter like so:
|
||
* `{"name":"foo[bar]", "schema":{"type":"int"}, "required":true}`.
|
||
*
|
||
* When `flatten_deep_query_parameters` is `false`, Scramble will document the parameter like so:
|
||
* `{"name":"foo", "schema": {"type":"object", "properties":{"bar":{"type": "int"}}, "required": ["bar"]}, "required":true}`.
|
||
*/
|
||
'flatten_deep_query_parameters' => true,
|
||
|
||
'middleware' => [
|
||
'web',
|
||
RestrictedDocsAccess::class,
|
||
],
|
||
|
||
'extensions' => [],
|
||
];
|