From a994468a8ce5402736cfde92014872c6a1f12bea Mon Sep 17 00:00:00 2001 From: Moritz Friedrich Date: Mon, 10 Jun 2024 19:05:12 +0200 Subject: [PATCH] Add OpenAI factory service This commit adds support for adding a custom OpenAI factory implementation. While the Factory class itself is final, binding it to a service still has the benefit of letting users customize client bootstrapping by decorating or downright replacing the factory call with their own. --- src/ServiceProvider.php | 21 ++++++++++++++++++--- tests/Arch.php | 1 + tests/ServiceProvider.php | 17 +++++++++++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index a6e6808..7fece4c 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -4,6 +4,7 @@ namespace OpenAI\Laravel; +use Illuminate\Container\Container; use Illuminate\Contracts\Support\DeferrableProvider; use Illuminate\Support\ServiceProvider as BaseServiceProvider; use OpenAI; @@ -12,6 +13,10 @@ use OpenAI\Laravel\Commands\InstallCommand; use OpenAI\Laravel\Exceptions\ApiKeyIsMissing; +use function assert; +use function config; +use function is_string; + /** * @internal */ @@ -22,7 +27,7 @@ final class ServiceProvider extends BaseServiceProvider implements DeferrablePro */ public function register(): void { - $this->app->singleton(ClientContract::class, static function (): Client { + $this->app->bind(OpenAI\Factory::class, static function (): OpenAI\Factory { $apiKey = config('openai.api_key'); $organization = config('openai.organization'); @@ -34,10 +39,18 @@ public function register(): void ->withApiKey($apiKey) ->withOrganization($organization) ->withHttpHeader('OpenAI-Beta', 'assistants=v2') - ->withHttpClient(new \GuzzleHttp\Client(['timeout' => config('openai.request_timeout', 30)])) - ->make(); + ->withHttpClient(new \GuzzleHttp\Client(['timeout' => config('openai.request_timeout', 30)])); }); + $this->app->singleton(ClientContract::class, static function (Container $app): Client { + $factory = $app->make(OpenAI\Factory::class); + assert($factory instanceof OpenAI\Factory); + + return $factory->make(); + } + ); + + $this->app->alias(OpenAI\Factory::class, 'openai.factory'); $this->app->alias(ClientContract::class, 'openai'); $this->app->alias(ClientContract::class, Client::class); } @@ -68,7 +81,9 @@ public function provides(): array return [ Client::class, ClientContract::class, + OpenAI\Factory::class, 'openai', + 'openai.factory', ]; } } diff --git a/tests/Arch.php b/tests/Arch.php index e1ca9cc..d938c68 100644 --- a/tests/Arch.php +++ b/tests/Arch.php @@ -18,6 +18,7 @@ ->toOnlyUse([ 'GuzzleHttp\Client', 'Illuminate\Support\ServiceProvider', + 'Illuminate\Container\Container', 'OpenAI\Laravel', 'OpenAI', 'Illuminate\Contracts\Support\DeferrableProvider', diff --git a/tests/ServiceProvider.php b/tests/ServiceProvider.php index cae4dd5..bcb5c8d 100644 --- a/tests/ServiceProvider.php +++ b/tests/ServiceProvider.php @@ -3,9 +3,24 @@ use Illuminate\Config\Repository; use OpenAI\Client; use OpenAI\Contracts\ClientContract; +use OpenAI\Factory; use OpenAI\Laravel\Exceptions\ApiKeyIsMissing; use OpenAI\Laravel\ServiceProvider; +it('binds the factory on the container', function () { + $app = app(); + + $app->bind('config', fn () => new Repository([ + 'openai' => [ + 'api_key' => 'test', + ], + ])); + + (new ServiceProvider($app))->register(); + + expect($app->get(Factory::class))->toBeInstanceOf(Factory::class); +}); + it('binds the client on the container', function () { $app = app(); @@ -55,6 +70,8 @@ expect($provides)->toBe([ Client::class, ClientContract::class, + Factory::class, 'openai', + 'openai.factory', ]); });