diff --git a/.env b/.env index 7dbea184..1e85fc77 100644 --- a/.env +++ b/.env @@ -65,5 +65,9 @@ RUN_EXPENSIVE_EXAMPLES=false # For using Gemini GOOGLE_API_KEY= +# For using Albert API (French Sovereign AI) +ALBERT_API_KEY= +ALBERT_API_URL= + # For MariaDB store. Server defined in compose.yaml MARIADB_URI=pdo-mysql://root@127.0.0.1:3309/my_database diff --git a/README.md b/README.md index 7a1fb99e..0923ad81 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ $embeddings = new Embeddings(); * [DeepSeek's R1](https://www.deepseek.com/) with [OpenRouter](https://www.openrouter.com/) as Platform * [Amazon's Nova](https://nova.amazon.com) with [AWS](https://aws.amazon.com/bedrock/) as Platform * [Mistral's Mistral](https://www.mistral.ai/) with [Mistral](https://www.mistral.ai/) as Platform + * [Albert API](https://github.com/etalab-ia/albert-api) models with [Albert](https://github.com/etalab-ia/albert-api) as Platform (French government's sovereign AI gateway) * Embeddings Models * [OpenAI's Text Embeddings](https://platform.openai.com/docs/guides/embeddings/embedding-models) with [OpenAI](https://platform.openai.com/docs/overview) and [Azure](https://learn.microsoft.com/azure/ai-services/openai/concepts/models) as Platform * [Voyage's Embeddings](https://docs.voyageai.com/docs/embeddings) with [Voyage](https://www.voyageai.com/) as Platform @@ -166,6 +167,7 @@ $response = $chain->call($messages, [ 1. [Google's Gemini with Google](examples/google/chat.php) 1. [Google's Gemini with OpenRouter](examples/openrouter/chat-gemini.php) 1. [Mistral's Mistral with Mistral](examples/mistral/chat-mistral.php) +1. [Albert API (French Sovereign AI)](examples/albert/chat.php) ### Tools diff --git a/examples/albert/chat.php b/examples/albert/chat.php new file mode 100644 index 00000000..e2306078 --- /dev/null +++ b/examples/albert/chat.php @@ -0,0 +1,55 @@ +call($messages); + +echo $response->getContent().\PHP_EOL; diff --git a/src/Platform/Bridge/Albert/EmbeddingsModelClient.php b/src/Platform/Bridge/Albert/EmbeddingsModelClient.php new file mode 100644 index 00000000..edcbd359 --- /dev/null +++ b/src/Platform/Bridge/Albert/EmbeddingsModelClient.php @@ -0,0 +1,38 @@ +httpClient->request('POST', $this->baseUrl.'embeddings', [ + 'auth_bearer' => $this->apiKey, + 'json' => array_merge($options, $payload), + ]); + } +} diff --git a/src/Platform/Bridge/Albert/GPTModelClient.php b/src/Platform/Bridge/Albert/GPTModelClient.php new file mode 100644 index 00000000..d3f7a976 --- /dev/null +++ b/src/Platform/Bridge/Albert/GPTModelClient.php @@ -0,0 +1,42 @@ +httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient); + Assert::stringNotEmpty($apiKey, 'The API key must not be empty.'); + Assert::stringNotEmpty($baseUrl, 'The base URL must not be empty.'); + } + + public function supports(Model $model): bool + { + return $model instanceof GPT; + } + + public function request(Model $model, array|string $payload, array $options = []): ResponseInterface + { + return $this->httpClient->request('POST', $this->baseUrl.'chat/completions', [ + 'auth_bearer' => $this->apiKey, + 'json' => array_merge($options, $payload), + ]); + } +} diff --git a/src/Platform/Bridge/Albert/PlatformFactory.php b/src/Platform/Bridge/Albert/PlatformFactory.php new file mode 100644 index 00000000..108bccf1 --- /dev/null +++ b/src/Platform/Bridge/Albert/PlatformFactory.php @@ -0,0 +1,50 @@ + ['https://albert.example.com/'], + 'without trailing slash' => ['https://albert.example.com'], + 'with v1 path' => ['https://albert.example.com/v1'], + 'with v1 path and trailing slash' => ['https://albert.example.com/v1/'], + ]; + } + + #[Test] + public function throwsExceptionForNonHttpsUrl(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The Albert URL must start with "https://".'); + + PlatformFactory::create('test-key', 'http://albert.example.com'); + } +}