xbnz / google-gemini-api
用于访问谷歌AI模型和OAuth2服务的HTTP客户端
Requires
- php: ^8.3
- azjezz/psl: ^2.9
- firebase/php-jwt: ^6.10
- illuminate/collections: ^11.7
- nesbot/carbon: ^3.3
- saloonphp/saloon: ^3.8
Requires (Dev)
- caseyamcl/guzzle_retry_middleware: ^2.9
- colinodell/psr-testlogger: ^1.3
- fakerphp/faker: ^1.23
- infection/infection: ^0.28
- laravel/pint: ^1.1
- nunomaduro/collision: ^8.1
- pestphp/pest: ^2.3
- pestphp/pest-plugin-faker: ^2.0
- php-standard-library/phpstan-extension: ^1.0
- phpstan/extension-installer: ^1.3
- phpstan/phpstan: ^1.1
- symfony/dotenv: ^7.0
- symfony/var-dumper: ^7.0
README
带有OAuth2认证的Google Gemini API客户端
动机
其他库依赖于generativelanguage.googleapis.com
API,该API缺少一些模型。例如,新引入的“Gemini Experimental”模型,它是免费的(!)无法通过该API访问。这个库解决了这个问题。
注意
Gemini Experimental API可能不会永远存在。如果您正在使用在generativelanguage.googleapis.com
API上可用的模型,您也可以使用这个库来访问它。
安装
composer require xbnz/gemini
先决条件
在Google Cloud中创建服务帐户
- 前往 Google Cloud Console
- 前往 IAM & Admin
- 前往 服务帐户
- 点击您的项目
- 如果您还没有,点击“创建服务帐户”
- 给它一个名字并点击“创建”
- 点击“完成”
- 回到您刚刚创建的服务帐户
- 点击“添加密钥”并创建一个JSON密钥。文件将下载到您的计算机
- 从JSON文件中获取必要的信息
client_email
private_key
入门
示例应用程序
这里是一个使用此库的示例Laravel命令。 服务提供者 是将接口绑定到具体实现的地方。
注册到依赖注入容器(可选)
// Laravel example namespace App\Providers; clsss AppServiceProvider extends ServiceProvider { public function register(): void { $this->app->bind(GoogleOAuth2Interface::class, function (Application $app) { return new GoogleOAuth2Service( logger: $app->make(LoggerInterface::class) ); }); $this->app->bind(GoogleAIPlatformInterface::class, function (Application $app) { return new GoogleAIPlatformService( (new GoogleAIPlatformConnector( $app->make('config')->get('services.google_ai_platform.project_id'), $app->make('config')->get('services.google_ai_platform.region'), ))->authenticate( new TokenAuthenticator( $app->make(GoogleOAuth2Interface::class)->token( new TokenRequestDTO( googleServiceAccount: new GoogleServiceAccount( $app->make('config')->get('services.google_ai_platform.client_email'), $app->make('config')->get('services.google_ai_platform.private_key'), ), scope: 'https://www.googleapis.com/auth/cloud-platform', issuedAt: CarbonImmutable::now(), expiration: CarbonImmutable::now()->addHour() ) )->accessToken ) ), $app->make('log') ); }); } }
使用AIPlatform服务进行单轮或多轮对话
namespace App\Console\Commands; class SomeCommand { public function __construct( private readonly AIPlatformInterface $aiPlatformService ) {} public function handle(): void { $response = $this->aiPlatformService->generateContent( new GenerateContentRequestDTO( 'publishers/google/models/gemini-experimental', Collection::make([ new ContentDTO( Role::User, Collection::make([ new TextPart('Explain what is happening in the image'), new BlobPart( 'image/jpeg', 'base64 image...', ) ]) ), new ContentDTO( Role::Model, Collection::make([ new TextPart('Sure! This is an image of a cat.'), ]), ), new ContentDTO( Role::User, Collection::make([ new TextPart('What color is the cat?'), ]), ), // and so on... ]) Collection::make([ new SafetySettings(HarmCategory::HarmCategoryHarassment, SafetyThreshold::BlockOnlyHigh), new SafetySettings(HarmCategory::HarmCategoryHateSpeech, SafetyThreshold::BlockOnlyHigh), new SafetySettings(HarmCategory::HarmCategorySexuallyExplicit, SafetyThreshold::BlockOnlyHigh), new SafetySettings(HarmCategory::HarmCategoryDangerousContent, SafetyThreshold::BlockOnlyHigh), ]), systemInstructions: Collection::make([ new TextPart('Your instructions here...'), ]), generationConfig: new GenerationConfig(...) // Optional ) ); if ($response->finishReason->consideredSuccessful() === false) { // Handle the model not being able to generate content (probably due to unsafe content) } // Do something with the healthy response $response->usage->promptTokenCount; $modelResponse = $response ->content ->parts ->sole(fn(PartContract $part) => $part instanceof TextPart) // There should only be one TextPart::class in a model response (for now) ->text; } }
注意
如果您不想使用依赖注入容器,可以直接new这些类
注意
您可以将模型从TextPart::class响应序列化并存储在您的数据库中。这将允许您与模型进行持久的聊天会话,类似于ChatGPT界面。
核心概念
可扩展的身份验证
谷歌 aiplatform.googleapis.com
API允许多种形式的身份验证。这个库倾向于使用从您的服务帐户派生的access_token
。然而,这个库的底层HTTP客户端(SaloonPHP)允许在new之前扩展“Connector”类以使用任何认证器。因此,您可以创建自己的Saloon认证策略并将其传递给构造函数
namespace App\Providers; class AppServiceProvider extends ServiceProvider { public function register(): void { $this->app->bind(GoogleAIPlatformInterface::class, function (Application $app) { return new GoogleAIPlatformService( (new GoogleAIPlatformConnector(...))->authenticate(new MyCustomAuthenticator), ); }); } }
Bearer token认证
提供的GoogleOAuthInterface::token()
方法允许您获取用于Bearer认证的新令牌。请注意,如果您使用此工具对AIPlatform进行认证,每次调用该方法都会生成新的令牌,这可能或可能不是所需的。
缓存您的令牌直到过期(Laravel示例)
由于大多数现代框架的缓存集成非常简单,因此此包不提供缓存机制——例如使用PSR缓存接口。以下是一个Laravel示例
namespace App\Providers; class AppServiceProvider extends ServiceProvider { public function register(): void { $this->app->bind(GoogleAIPlatformInterface::class, function (Application $app) { return new GoogleAIPlatformService( (new GoogleAIPlatformConnector(...))->authenticate( new TokenAuthenticator( $app->make('cache')->remember( 'google_api_platform_token', CarbonImmutable::now()->addMinutes(60), function () use ($app) { return $app->make(GoogleOAuth2Interface::class)->token( new TokenRequestDTO( googleServiceAccount: new GoogleServiceAccount( $app->make('config')->get('services.google_ai_platform.client_email'), $app->make('config')->get('services.google_ai_platform.private_key'), ), scope: 'https://www.googleapis.com/auth/cloud-platform', issuedAt: CarbonImmutable::now(), expiration: CarbonImmutable::now()->addHour() ) )->accessToken; } ), ) ), $app->make('log') ); }); } }
日志记录和异常处理
提供您自己的PSR兼容的记录器
这个库使用PSR记录器接口进行日志记录。您可以通过将其传递给服务的构造函数来提供自己的记录器
namespace App\Console\Commands; class SomeCommand { public function handle(): void { $service = new GoogleAIPlatformService( new GoogleAIPlatformConnector(...), new MyCustomLogger ); try { $response = $service->generateContent(...); } catch (GoogleAIPlatformException $e) { // Handle the exception } // At this point, the logger will have logged the exception } }
OAuth2服务上也有相同的功能。
请求和响应的生命周期钩子
这不适合基本用例。然而,API 会变化,维护者可能比较懒惰。如果谷歌在响应中添加了一个我无法处理的字段,你可以自由地挂钩到响应中并创建自己的 DTO,如果你不想提交 PR。
namespace App\Console\Commands; class SomeCommand { public function __construct( private readonly GoogleAIPlatformInterface $aiPlatform ) {} public function handle(): void { $this->aiPlatform->generateContent( requestDto: ..., beforeRequest: function (Request $request) { $request->headers()->merge([ 'X-Custom-Header' => 'Value' ]); return $request; }, afterResponse: function (Response $response) { // Return your own DTO or do something with the response return $response; } ) } }
重试失败的请求
当使用免费的 Gemini 模型时,会实施严格的速率限制。当遇到 429 响应时,你可以决定重试。
先决条件
未提供 Guzzle 重试中间件。你可以实现自己的或者使用你选择的库。例如
composer require caseyamcl/guzzle_retry_middleware
namespace App\Providers; class AppServiceProvider extends ServiceProvider { public function register(): void { $this->app->bind(GoogleAIPlatformInterface::class, function (Application $app) { $connector = new GoogleAIPlatformConnector(...); $connector->authenticate(...) $connector->sender()->getHandlerStack()->push( GuzzleRetryMiddleware::factory([ 'max_retry_attempts' => 5, 'retry_on_status' => [429], ]) ) return new GoogleAIPlatformService( $connector ); }); } }
测试
此库提供用于测试的模拟实现。例如,你可以这样使用 GoogleOAuth2ServiceFake::class
调用代码
namespace App\Console\Commands; use XbNz\Gemini\OAuth2\GoogleOAuth2Interface; class SomeCommand { public function __construct( private readonly GoogleOAuth2Interface $googleOAuth2Service ) {} public function handle(): void { $response = $this->googleOAuth2Service->token(...); } }
测试代码
namespace Tests\Feature; use Tests\TestCase; use XbNz\Gemini\OAuth2\DataTransferObjects\Requests\TokenRequestDTO; use XbNz\Gemini\OAuth2\GoogleOAuth2ServiceFake; class SomeCommandTest extends TestCase { public function test_it_works(): void { // Swapping the real service with the fake one in the Laravel container $this->app->swap(GoogleOAuth2Interface::class, $fake = new GoogleOAuth2ServiceFake); // Basic testing helpers $fake->alwaysReturnToken(...); $fake->assertTokenRequestCount(...); // Asserting against requests $fake->assertTokenRequest(function (TokenRequestDTO) { return $dto->grantType === 'urn:ietf:params:oauth:grant-type:jwt-bearer'; }); } }
注意
相同的理念可以应用于 AIPlatform 服务。模拟实现称为 GoogleAIPlatformServiceFake::class
,并提供类似的测试断言