diff --git a/README.md b/README.md index 944bc3b..d2f2393 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,22 @@

- Gemini API PHP Client - Example + Gemini API PHP Client - Example

- Total Downloads - Latest Version - License + Total Downloads + Latest Version + License

# Gemini API PHP Client +This library is a fork of [gemini-api-php/client](https://github.com/gemini-api-php/client) adding system instructions and updating api to v1beta. + Gemini API PHP Client allows you to use the Google's generative AI models, like Gemini Pro and Gemini Pro Vision. _This library is not developed or endorsed by Google._ - Erdem Köse - **[github.com/erdemkose](https://github.com/erdemkose)** +- Alexandre Vega - **[github.com/alexandrevega](https://github.com/alexandrevega)** ## Table of Contents - [Installation](#installation) @@ -24,6 +27,7 @@ _This library is not developed or endorsed by Google._ - [Chat Session with history](#chat-session-with-history) - [Streaming responses](#streaming-responses) - [Streaming Chat Session](#streaming-chat-session) + - [System Instruction](#system-instruction) - [Tokens counting](#tokens-counting) - [Listing models](#listing-models) - [Advanced Usages](#advanced-usages) @@ -39,7 +43,7 @@ _This library is not developed or endorsed by Google._ First step is to install the Gemini API PHP client with Composer. ```shell -composer require gemini-api-php/client +composer require alexandrevega/gemini-api-client ``` Gemini API PHP client does not come with an HTTP client. @@ -257,6 +261,58 @@ Response #1 This code will print "Hello World!" to the standard output. ``` +### System Instruction +You can add [System Instructions](https://ai.google.dev/gemini-api/docs/system-instructions?hl=en&lang=web) to steer the behaviour of Gemini. + +```php +use GeminiAPI\Client; +use GeminiAPI\Enums\Role; +use GeminiAPI\Resources\Content; +use GeminiAPI\Resources\Parts\TextPart; + +$history = [ + Content::text('Hello World in PHP', Role::User), + Content::text( + << + + This code will print "Hello World!" to the standard output. + TEXT, + Role::Model, + ), +]; + +$client = new Client('GEMINI_API_KEY'); +$chat = $client->geminiPro() + ->withSystemInstruction("You're an expert developer") + ->startChat() + ->withHistory($history); + +$response = $chat->sendMessage(new TextPart('in Go')); +print $response->text(); +``` + +```text +Response #0 +package main + +import "fmt" + +func main() { + +Response #1 + fmt.Println("Hello World!") +} + +This code will print "Hello World!" to the standard output. +``` + + + + + ### Embed Content ```php diff --git a/composer.json b/composer.json index 7a94437..6ff0169 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "gemini-api-php/client", + "name": "alexandrevega/gemini-api-client", "description": "API client for Google's Gemini API", "keywords": [ "php", @@ -17,6 +17,10 @@ { "name": "Erdem Köse", "email": "erdemkose@gmail.com" + }, + { + "name": "Carlos Ramos", + "email": "contact@cramos.dev" } ], "require": { diff --git a/src/Client.php b/src/Client.php index c4a3544..8cb32da 100644 --- a/src/Client.php +++ b/src/Client.php @@ -103,7 +103,7 @@ public function embeddingModel(ModelName $modelName): EmbeddingModel ); } - /** + /**x * @throws ClientExceptionInterface */ public function generateContent(GenerateContentRequest $request): GenerateContentResponse @@ -163,7 +163,7 @@ public function generateContentStream( } } - curl_setopt($ch, CURLOPT_URL, "{$this->baseUrl}/v1/{$request->getOperation()}"); + curl_setopt($ch, CURLOPT_URL, "{$this->baseUrl}/v1beta/{$request->getOperation()}"); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($request)); curl_setopt($ch, CURLOPT_HTTPHEADER, $headerLines); @@ -250,7 +250,7 @@ private function doRequest(RequestInterface $request): string throw new RuntimeException('Missing client or factory for Gemini API operation'); } - $uri = "{$this->baseUrl}/v1/{$request->getOperation()}"; + $uri = "{$this->baseUrl}/v1beta/{$request->getOperation()}"; $httpRequest = $this->requestFactory ->createRequest($request->getHttpMethod(), $uri); diff --git a/src/GenerativeModel.php b/src/GenerativeModel.php index 0108f35..b56d752 100644 --- a/src/GenerativeModel.php +++ b/src/GenerativeModel.php @@ -11,6 +11,7 @@ use GeminiAPI\Requests\CountTokensRequest; use GeminiAPI\Requests\GenerateContentRequest; use GeminiAPI\Requests\GenerateContentStreamRequest; +use GeminiAPI\Resources\Parts\TextPart; use GeminiAPI\Responses\CountTokensResponse; use GeminiAPI\Responses\GenerateContentResponse; use GeminiAPI\Resources\Content; @@ -25,6 +26,8 @@ class GenerativeModel /** @var SafetySetting[] */ private array $safetySettings = []; + private ?array $systemInstruction = null; + private ?GenerationConfig $generationConfig = null; public function __construct( @@ -56,6 +59,7 @@ public function generateContentWithContents(array $contents): GenerateContentRes $contents, $this->safetySettings, $this->generationConfig, + $this->systemInstruction ); return $this->client->generateContent($request); @@ -97,6 +101,7 @@ public function generateContentStreamWithContents( $contents, $this->safetySettings, $this->generationConfig, + $this->systemInstruction ); $this->client->generateContentStream($request, $callback, $ch); @@ -136,4 +141,13 @@ public function withGenerationConfig(GenerationConfig $generationConfig): self return $clone; } + + public function withSystemInstruction(?string $instruction = null): self + { + + $clone = clone $this; + $clone->systemInstruction['parts'] = [new TextPart($instruction)]; + + return $clone; + } } diff --git a/src/Requests/GenerateContentRequest.php b/src/Requests/GenerateContentRequest.php index a421e3e..a557e76 100644 --- a/src/Requests/GenerateContentRequest.php +++ b/src/Requests/GenerateContentRequest.php @@ -22,12 +22,14 @@ class GenerateContentRequest implements JsonSerializable, RequestInterface * @param Content[] $contents * @param SafetySetting[] $safetySettings * @param GenerationConfig|null $generationConfig + * @param string|null $systemInstructions */ public function __construct( public readonly ModelName $modelName, public readonly array $contents, public readonly array $safetySettings = [], public readonly ?GenerationConfig $generationConfig = null, + public ?array $systemInstructions = null ) { $this->ensureArrayOfType($this->contents, Content::class); $this->ensureArrayOfType($this->safetySettings, SafetySetting::class); @@ -71,6 +73,10 @@ public function jsonSerialize(): array $arr['generationConfig'] = $this->generationConfig; } + if ($this->systemInstructions) { + $arr['systemInstruction'] = $this->systemInstructions; + } + return $arr; } diff --git a/src/Requests/GenerateContentStreamRequest.php b/src/Requests/GenerateContentStreamRequest.php index 74aac7b..df7f16d 100644 --- a/src/Requests/GenerateContentStreamRequest.php +++ b/src/Requests/GenerateContentStreamRequest.php @@ -22,12 +22,15 @@ class GenerateContentStreamRequest implements JsonSerializable, RequestInterface * @param Content[] $contents * @param SafetySetting[] $safetySettings * @param GenerationConfig|null $generationConfig + * @param string|null $systemInstruction */ public function __construct( public readonly ModelName $modelName, public readonly array $contents, public readonly array $safetySettings = [], public readonly ?GenerationConfig $generationConfig = null, + public ?array $systemInstructions = null + ) { $this->ensureArrayOfType($this->contents, Content::class); $this->ensureArrayOfType($this->safetySettings, SafetySetting::class); @@ -71,6 +74,10 @@ public function jsonSerialize(): array $arr['generationConfig'] = $this->generationConfig; } + if ($this->systemInstructions) { + $arr['systemInstruction'] = $this->systemInstructions; + } + return $arr; } diff --git a/tests/Unit/ClientTest.php b/tests/Unit/ClientTest.php index 1e13609..f5ec68d 100644 --- a/tests/Unit/ClientTest.php +++ b/tests/Unit/ClientTest.php @@ -78,7 +78,7 @@ public function testGenerateContent() { $httpRequest = new Request( 'POST', - 'https://generativelanguage.googleapis.com/v1/models/gemini-pro:generateContent', + 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent', ); $httpResponse = new Response( body: <<createMock(RequestFactoryInterface::class); $requestFactory->expects(self::once()) ->method('createRequest') - ->with('POST', 'https://generativelanguage.googleapis.com/v1/models/gemini-pro:generateContent') + ->with('POST', 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent') ->willReturn($httpRequest); $httpRequest = $httpRequest @@ -155,7 +155,7 @@ public function testEmbedContent() { $httpRequest = new Request( 'POST', - 'https://generativelanguage.googleapis.com/v1/models/embedding-001:embedContent', + 'https://generativelanguage.googleapis.com/v1beta/models/embedding-001:embedContent', ); $httpResponse = new Response( body: <<createMock(RequestFactoryInterface::class); $requestFactory->expects(self::once()) ->method('createRequest') - ->with('POST', 'https://generativelanguage.googleapis.com/v1/models/embedding-001:embedContent') + ->with('POST', 'https://generativelanguage.googleapis.com/v1beta/models/embedding-001:embedContent') ->willReturn($httpRequest); $httpRequest = $httpRequest @@ -210,7 +210,7 @@ public function testCountTokens() { $httpRequest = new Request( 'POST', - 'https://generativelanguage.googleapis.com/v1/models/gemini-pro:countTokens', + 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:countTokens', ); $httpResponse = new Response( body: <<createMock(RequestFactoryInterface::class); $requestFactory->expects(self::once()) ->method('createRequest') - ->with('POST', 'https://generativelanguage.googleapis.com/v1/models/gemini-pro:countTokens') + ->with('POST', 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:countTokens') ->willReturn($httpRequest); $httpRequest = $httpRequest @@ -260,7 +260,7 @@ public function testListModels() { $httpRequest = new Request( 'GET', - 'https://generativelanguage.googleapis.com/v1/models', + 'https://generativelanguage.googleapis.com/v1beta/models', ); $httpResponse = new Response( body: <<createMock(RequestFactoryInterface::class); $requestFactory->expects(self::once()) ->method('createRequest') - ->with('GET', 'https://generativelanguage.googleapis.com/v1/models') + ->with('GET', 'https://generativelanguage.googleapis.com/v1beta/models') ->willReturn($httpRequest); $httpRequest = $httpRequest