symfony/ai-albert-platform
Symfony AI bridge for the French government’s Albert Platform (OpenGateLLM). Connect Symfony apps to Albert’s OpenAI-compatible chat and embeddings endpoints, with links to the API reference, supported models, and upstream sources.
composer require symfony/ai-albert-platform
config/ai.php:
'providers' => [
'albert' => [
'api_key' => env('ALBERT_API_KEY'),
'base_uri' => 'https://albert.api.etalab.gouv.fr',
],
],
use Symfony\Component\Ai\Provider\AlbertProvider;
public function __construct(private AlbertProvider $albert) {}
public function generateResponse(string $prompt): string
{
$response = $this->albert->complete([
'model' => 'mistral-7b',
'prompt' => $prompt,
]);
return $response->choices[0]->text;
}
src/Provider/AlbertProvider.php to inspect request/response transformations.Leverage Symfony AI’s provider abstraction to route requests dynamically:
// config/ai.php
'providers' => [
'primary' => 'albert',
'fallback' => 'openai',
],
// In service
public function __construct(private AiClient $aiClient) {}
public function generateWithFallback(string $prompt): string
{
try {
return $this->aiClient->complete([
'provider' => 'albert',
'model' => 'mistral-7b',
'prompt' => $prompt,
]);
} catch (Exception $e) {
return $this->aiClient->complete([
'provider' => 'openai',
'model' => 'gpt-3.5-turbo',
'prompt' => $prompt,
]);
}
}
Use Albert’s embeddings with Laravel Scout or custom vector DBs:
public function generateEmbedding(string $text): array
{
$response = $this->albert->embed([
'model' => 'all-minilm',
'input' => $text,
]);
return $response->data[0]->embedding;
}
Store embeddings in a vector database (e.g., Meilisearch):
$client = new MeilisearchClient(env('MEILI_URL'), env('MEILI_API_KEY'));
$client->index('documents')->addDocuments([
'id' => 1,
'embedding' => $embedding,
'content' => $text,
]);
Offload AI calls to queues to avoid blocking requests:
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
class GenerateAiResponse implements ShouldQueue
{
use Queueable;
public function __construct(
public string $prompt,
public string $model = 'mistral-7b'
) {}
public function handle(AiClient $aiClient)
{
$response = $aiClient->complete([
'provider' => 'albert',
'model' => $this->model,
'prompt' => $this->prompt,
]);
// Store or broadcast response
}
}
Dispatch from a controller:
GenerateAiResponse::dispatch($userInput)->onQueue('ai');
Route prompts to optimal models based on context:
public function selectModel(string $prompt): string
{
if (str_contains(strtolower($prompt), 'france') || str_contains(strtolower($prompt), 'français')) {
return 'mistral-7b'; // French-optimized
}
return 'llama-2'; // General-purpose
}
public function generateResponse(string $prompt): string
{
$model = $this->selectModel($prompt);
$response = $this->albert->complete([
'model' => $model,
'prompt' => $prompt,
]);
return $response->choices[0]->text;
}
$this->app->singleton(AiClient::class, function ($app) {
return new AiClient([
'providers' => [
'albert' => [
'api_key' => env('ALBERT_API_KEY'),
'base_uri' => 'https://albert.api.etalab.gouv.fr',
],
],
]);
});
.env for API keys and endpoints:
ALBERT_API_KEY=your_api_key_here
ALBERT_BASE_URI=https://albert.api.etalab.gouv.fr
$this->app->instance(AiClient::class, Mockery::mock(AiClient::class));
API Rate Limits
use Symfony\Component\Ai\Exception\AiException;
use Symfony\Component\Ai\Retry\RetryStrategy;
try {
return $this->albert->complete($request);
} catch (AiException $e) {
return (new RetryStrategy())->retry(
fn() => $this->albert->complete($request),
3, // Max retries
1000 // Delay ms
);
}
Model Availability
text-embedding-ada-002 may not work; use all-minilm instead.Response Format Quirks
finish_reason values). Normalize responses:
$response = $this->albert->complete($request);
$text = $response->choices[0]->text ?? '';
$finishReason = $response->choices[0]->finish_reason ?? 'stop';
class AlbertResponse {
public function __construct(
public string $text,
public string $finishReason,
public int $tokenUsage
) {}
}
Embedding Dimensions
$embedding = $this->albert->embed($request);
if (count($embedding->data[0]->embedding) !== 384) { // Example: all-minilm
throw new \RuntimeException('Unexpected embedding dimension');
}
CORS/Proxy Issues
$client = new Client([
'base_uri' => 'https://albert.api.etalab.gouv.fr',
'headers' => [
'Authorization' => 'Bearer ' . env('ALBERT_API_KEY'),
'Accept' => 'application/json',
],
'timeout' => 30,
]);
$response = $this->albert->complete($request, [
'debug' => true,
]);
\Log::debug('Albert API Response', [
'status' => $response->getStatusCode(),
'body' => $response->getContent(),
]);
429 Too Many Requests: Implement rate limiting (e.g., symfony/rate-limiter).401 Unauthorized: Verify ALBERT_API_KEY in .env.400 Bad Request: Check model names and input formats against ReDoc.AlbertProvider to add Albert-specific features:
namespace App\Providers;
use Symfony\Component\Ai\Provider\AlbertProvider as BaseAlbertProvider;
class CustomAlbertProvider extends BaseAlbertProvider {
public function generateWithCustomParams(array $params): mixed {
How can I help you explore Laravel packages today?