From b46086a6d0a9f6c5ed2dfcfc2d4bcf3d821ec903 Mon Sep 17 00:00:00 2001 From: Alexandre <8642690+alexandrevega@users.noreply.github.com> Date: Mon, 16 Sep 2024 12:59:12 +0000 Subject: [PATCH 01/12] Add: initial support for system instruction --- src/Client.php | 2 +- src/GenerativeModel.php | 12 ++++++++++++ src/Requests/GenerateContentRequest.php | 6 ++++++ src/Requests/GenerateContentStreamRequest.php | 7 +++++++ 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/Client.php b/src/Client.php index c4a3544..bee6763 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 diff --git a/src/GenerativeModel.php b/src/GenerativeModel.php index 0108f35..abb1990 100644 --- a/src/GenerativeModel.php +++ b/src/GenerativeModel.php @@ -25,6 +25,8 @@ class GenerativeModel /** @var SafetySetting[] */ private array $safetySettings = []; + private ?string $systemInstruction = null; + private ?GenerationConfig $generationConfig = null; public function __construct( @@ -56,6 +58,7 @@ public function generateContentWithContents(array $contents): GenerateContentRes $contents, $this->safetySettings, $this->generationConfig, + $this->systemInstruction ); return $this->client->generateContent($request); @@ -136,4 +139,13 @@ public function withGenerationConfig(GenerationConfig $generationConfig): self return $clone; } + + public function withSystemInstruction(?string $instruction = null): self + { + + $clone = clone $this; + $this->systemInstruction = $instruction; + + return $clone; + } } diff --git a/src/Requests/GenerateContentRequest.php b/src/Requests/GenerateContentRequest.php index a421e3e..f8eb522 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 $systemInstruction */ public function __construct( public readonly ModelName $modelName, public readonly array $contents, public readonly array $safetySettings = [], public readonly ?GenerationConfig $generationConfig = null, + public ?string $systemInstruction = 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->systemInstruction) { + $arr['system_instruction'] = $this->systemInstruction; + } + return $arr; } diff --git a/src/Requests/GenerateContentStreamRequest.php b/src/Requests/GenerateContentStreamRequest.php index 74aac7b..b790695 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 readonly ?string $systemInstruction = 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->systemInstruction) { + $arr['system_instruction'] = $this->systemInstruction; + } + return $arr; } From 2250e09f8a63fb84807664650af2151822b21292 Mon Sep 17 00:00:00 2001 From: Alexandre <8642690+alexandrevega@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:51:28 +0000 Subject: [PATCH 02/12] Fix: clone system instructions --- src/GenerativeModel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GenerativeModel.php b/src/GenerativeModel.php index abb1990..2fb8a69 100644 --- a/src/GenerativeModel.php +++ b/src/GenerativeModel.php @@ -144,7 +144,7 @@ public function withSystemInstruction(?string $instruction = null): self { $clone = clone $this; - $this->systemInstruction = $instruction; + $clone->systemInstruction = $instruction; return $clone; } From 48b9e0de888943c794afeeacccee00268c0efbc6 Mon Sep 17 00:00:00 2001 From: Alexandre <8642690+alexandrevega@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:55:42 +0000 Subject: [PATCH 03/12] Fix: readonly system instruction --- src/Requests/GenerateContentStreamRequest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Requests/GenerateContentStreamRequest.php b/src/Requests/GenerateContentStreamRequest.php index b790695..5c0a69d 100644 --- a/src/Requests/GenerateContentStreamRequest.php +++ b/src/Requests/GenerateContentStreamRequest.php @@ -29,7 +29,7 @@ public function __construct( public readonly array $contents, public readonly array $safetySettings = [], public readonly ?GenerationConfig $generationConfig = null, - public readonly ?string $systemInstruction = null + public ?string $systemInstruction = null ) { $this->ensureArrayOfType($this->contents, Content::class); From eba270038fc7c30c3a9a113721ff6fa210ca1ad8 Mon Sep 17 00:00:00 2001 From: Alexandre <8642690+alexandrevega@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:56:59 +0000 Subject: [PATCH 04/12] Fix: add system instruction to generate content stream --- src/GenerativeModel.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/GenerativeModel.php b/src/GenerativeModel.php index 2fb8a69..3fd04e8 100644 --- a/src/GenerativeModel.php +++ b/src/GenerativeModel.php @@ -100,6 +100,7 @@ public function generateContentStreamWithContents( $contents, $this->safetySettings, $this->generationConfig, + $this->systemInstruction ); $this->client->generateContentStream($request, $callback, $ch); From 8219a6cf07ff6feadcafdd9051999dc9c5171343 Mon Sep 17 00:00:00 2001 From: Alexandre <8642690+alexandrevega@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:58:07 +0000 Subject: [PATCH 05/12] Add: modify v1 to v1beta api calls --- src/Client.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Client.php b/src/Client.php index bee6763..8cb32da 100644 --- a/src/Client.php +++ b/src/Client.php @@ -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); From 7f6335c2f6996cb81dc72f5ae1d32feb06ad4ade Mon Sep 17 00:00:00 2001 From: Alexandre <8642690+alexandrevega@users.noreply.github.com> Date: Mon, 16 Sep 2024 14:35:30 +0000 Subject: [PATCH 06/12] Fix: system intructions to parts --- src/GenerativeModel.php | 5 +++-- src/Requests/GenerateContentRequest.php | 8 ++++---- src/Requests/GenerateContentStreamRequest.php | 6 +++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/GenerativeModel.php b/src/GenerativeModel.php index 3fd04e8..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,7 +26,7 @@ class GenerativeModel /** @var SafetySetting[] */ private array $safetySettings = []; - private ?string $systemInstruction = null; + private ?array $systemInstruction = null; private ?GenerationConfig $generationConfig = null; @@ -145,7 +146,7 @@ public function withSystemInstruction(?string $instruction = null): self { $clone = clone $this; - $clone->systemInstruction = $instruction; + $clone->systemInstruction['parts'] = [new TextPart($instruction)]; return $clone; } diff --git a/src/Requests/GenerateContentRequest.php b/src/Requests/GenerateContentRequest.php index f8eb522..a557e76 100644 --- a/src/Requests/GenerateContentRequest.php +++ b/src/Requests/GenerateContentRequest.php @@ -22,14 +22,14 @@ class GenerateContentRequest implements JsonSerializable, RequestInterface * @param Content[] $contents * @param SafetySetting[] $safetySettings * @param GenerationConfig|null $generationConfig - * @param string|null $systemInstruction + * @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 ?string $systemInstruction = null + public ?array $systemInstructions = null ) { $this->ensureArrayOfType($this->contents, Content::class); $this->ensureArrayOfType($this->safetySettings, SafetySetting::class); @@ -73,8 +73,8 @@ public function jsonSerialize(): array $arr['generationConfig'] = $this->generationConfig; } - if ($this->systemInstruction) { - $arr['system_instruction'] = $this->systemInstruction; + if ($this->systemInstructions) { + $arr['systemInstruction'] = $this->systemInstructions; } return $arr; diff --git a/src/Requests/GenerateContentStreamRequest.php b/src/Requests/GenerateContentStreamRequest.php index 5c0a69d..df7f16d 100644 --- a/src/Requests/GenerateContentStreamRequest.php +++ b/src/Requests/GenerateContentStreamRequest.php @@ -29,7 +29,7 @@ public function __construct( public readonly array $contents, public readonly array $safetySettings = [], public readonly ?GenerationConfig $generationConfig = null, - public ?string $systemInstruction = null + public ?array $systemInstructions = null ) { $this->ensureArrayOfType($this->contents, Content::class); @@ -74,8 +74,8 @@ public function jsonSerialize(): array $arr['generationConfig'] = $this->generationConfig; } - if ($this->systemInstruction) { - $arr['system_instruction'] = $this->systemInstruction; + if ($this->systemInstructions) { + $arr['systemInstruction'] = $this->systemInstructions; } return $arr; From e6fd486d18ca4387e67c37e6ccd2c9ab70e2c7c5 Mon Sep 17 00:00:00 2001 From: Alexandre <8642690+alexandrevega@users.noreply.github.com> Date: Fri, 27 Sep 2024 09:31:26 +0000 Subject: [PATCH 07/12] Fix: tests urls --- tests/Unit/ClientTest.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) 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 From f83115e675a4cac6b565bc7ac6dcbcca46d25b4c Mon Sep 17 00:00:00 2001 From: Alexandre <8642690+alexandrevega@users.noreply.github.com> Date: Fri, 27 Sep 2024 09:35:44 +0000 Subject: [PATCH 08/12] Change: composer name --- composer.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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": { From 64edece232dd99c4e08f80897acb4e4cc0a8028e Mon Sep 17 00:00:00 2001 From: Alexandre <8642690+alexandrevega@users.noreply.github.com> Date: Fri, 27 Sep 2024 09:41:15 +0000 Subject: [PATCH 09/12] Add: system instruction support to README.md --- README.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/README.md b/README.md index 944bc3b..64961f7 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ Gemini API PHP Client allows you to use the Google's generative AI models, like _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 +25,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 Instructions](#system-instructions) - [Tokens counting](#tokens-counting) - [Listing models](#listing-models) - [Advanced Usages](#advanced-usages) @@ -257,6 +259,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( + <<
-
+