Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 11 additions & 12 deletions src/Providers/Gemini/Handlers/Stream.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
use Prism\Prism\Exceptions\PrismStreamDecodeException;
use Prism\Prism\Providers\Gemini\Maps\FinishReasonMap;
use Prism\Prism\Providers\Gemini\Maps\MessageMap;
use Prism\Prism\Providers\Gemini\Maps\ToolChoiceMap;
use Prism\Prism\Providers\Gemini\Maps\ToolConfigMap;
use Prism\Prism\Providers\Gemini\Maps\ToolMap;
use Prism\Prism\Streaming\EventID;
use Prism\Prism\Streaming\Events\StepFinishEvent;
Expand Down Expand Up @@ -447,13 +447,8 @@ protected function sendRequest(Request $request): Response
{
$providerOptions = $request->providerOptions();

if ($request->tools() !== [] && $request->providerTools() !== []) {
throw new PrismException('Use of provider tools with custom tools is not currently supported by Gemini.');
}

if ($request->tools() !== [] && ($providerOptions['searchGrounding'] ?? false)) {
throw new PrismException('Use of search grounding with custom tools is not currently supported by Prism.');
}
$hasSearchGrounding = (bool) ($providerOptions['searchGrounding'] ?? false);
$hasBothToolTypes = $request->tools() !== [] && ($request->providerTools() !== [] || $hasSearchGrounding);

$tools = [];

Expand All @@ -464,14 +459,16 @@ protected function sendRequest(Request $request): Response
],
$request->providerTools()
);
} elseif ($providerOptions['searchGrounding'] ?? false) {
} elseif ($hasSearchGrounding) {
$tools = [
[
'google_search' => (object) [],
],
];
} elseif ($request->tools() !== []) {
$tools = ['function_declarations' => ToolMap::map($request->tools())];
}

if ($request->tools() !== []) {
$tools['function_declarations'] = ToolMap::map($request->tools());
}

$thinkingConfig = $providerOptions['thinkingConfig'] ?? null;
Expand All @@ -490,6 +487,8 @@ protected function sendRequest(Request $request): Response
];
}

$toolConfig = ToolConfigMap::map($request->toolChoice(), $hasBothToolTypes);

/** @var Response $response */
$response = $this->client
->withOptions(['stream' => true])
Expand All @@ -505,7 +504,7 @@ protected function sendRequest(Request $request): Response
'thinkingConfig' => $thinkingConfig,
]) ?: null,
'tools' => $tools !== [] ? $tools : null,
'tool_config' => $request->toolChoice() ? ToolChoiceMap::map($request->toolChoice()) : null,
'tool_config' => $toolConfig,
'safetySettings' => $providerOptions['safetySettings'] ?? null,
])
);
Expand Down
16 changes: 7 additions & 9 deletions src/Providers/Gemini/Handlers/Structured.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
use Prism\Prism\Providers\Gemini\Maps\MessageMap;
use Prism\Prism\Providers\Gemini\Maps\SchemaMap;
use Prism\Prism\Providers\Gemini\Maps\ToolCallMap;
use Prism\Prism\Providers\Gemini\Maps\ToolChoiceMap;
use Prism\Prism\Providers\Gemini\Maps\ToolConfigMap;
use Prism\Prism\Providers\Gemini\Maps\ToolMap;
use Prism\Prism\Structured\Request;
use Prism\Prism\Structured\Response as StructuredResponse;
Expand Down Expand Up @@ -79,9 +79,7 @@ public function sendRequest(Request $request): array
{
$providerOptions = $request->providerOptions();

if ($request->tools() !== [] && $request->providerTools() !== []) {
throw new PrismException('Use of provider tools with custom tools is not currently supported by Gemini.');
}
$hasBothToolTypes = $request->tools() !== [] && $request->providerTools() !== [];

$tools = [];

Expand All @@ -97,10 +95,8 @@ public function sendRequest(Request $request): array
}

if ($request->tools() !== []) {
$tools = [
[
'function_declarations' => ToolMap::map($request->tools()),
],
$tools[] = [
'function_declarations' => ToolMap::map($request->tools()),
];
}

Expand All @@ -120,6 +116,8 @@ public function sendRequest(Request $request): array
]);
}

$toolConfig = ToolConfigMap::map($request->toolChoice(), $hasBothToolTypes);

/** @var Response $response */
$response = $this->client->post(
"{$request->model()}:generateContent",
Expand All @@ -135,7 +133,7 @@ public function sendRequest(Request $request): array
'thinkingConfig' => $thinkingConfig,
]),
'tools' => $tools !== [] ? $tools : null,
'tool_config' => $request->toolChoice() ? ToolChoiceMap::map($request->toolChoice()) : null,
'tool_config' => $toolConfig,
'safetySettings' => $providerOptions['safetySettings'] ?? null,
])
);
Expand Down
10 changes: 5 additions & 5 deletions src/Providers/Gemini/Handlers/Text.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
use Prism\Prism\Providers\Gemini\Maps\FinishReasonMap;
use Prism\Prism\Providers\Gemini\Maps\MessageMap;
use Prism\Prism\Providers\Gemini\Maps\ToolCallMap;
use Prism\Prism\Providers\Gemini\Maps\ToolChoiceMap;
use Prism\Prism\Providers\Gemini\Maps\ToolConfigMap;
use Prism\Prism\Providers\Gemini\Maps\ToolMap;
use Prism\Prism\Text\Request;
use Prism\Prism\Text\Response as TextResponse;
Expand Down Expand Up @@ -90,9 +90,7 @@ protected function sendRequest(Request $request): ClientResponse
'thinkingConfig' => $thinkingConfig,
]);

if ($request->tools() !== [] && $request->providerTools() != []) {
throw new PrismException('Use of provider tools with custom tools is not currently supported by Gemini.');
}
$hasBothToolTypes = $request->tools() !== [] && $request->providerTools() !== [];

$tools = [];

Expand All @@ -109,6 +107,8 @@ protected function sendRequest(Request $request): ClientResponse
$tools['function_declarations'] = ToolMap::map($request->tools());
}

$toolConfig = ToolConfigMap::map($request->toolChoice(), $hasBothToolTypes);

/** @var ClientResponse $response */
$response = $this->client->post(
"{$request->model()}:generateContent",
Expand All @@ -117,7 +117,7 @@ protected function sendRequest(Request $request): ClientResponse
'cachedContent' => $providerOptions['cachedContentName'] ?? null,
'generationConfig' => $generationConfig !== [] ? $generationConfig : null,
'tools' => $tools !== [] ? $tools : null,
'tool_config' => $request->toolChoice() ? ToolChoiceMap::map($request->toolChoice()) : null,
'tool_config' => $toolConfig,
'safetySettings' => $providerOptions['safetySettings'] ?? null,
])
);
Expand Down
27 changes: 27 additions & 0 deletions src/Providers/Gemini/Maps/ToolConfigMap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace Prism\Prism\Providers\Gemini\Maps;

use Prism\Prism\Enums\ToolChoice;

class ToolConfigMap
{
/**
* @return array<string, mixed>|null
*/
public static function map(string|ToolChoice|null $toolChoice, bool $includeServerSideToolInvocations = false): ?array
{
$config = ToolChoiceMap::map($toolChoice);

/** @var array<string, mixed>|null $config */
$config = is_array($config) ? $config : null;

if ($includeServerSideToolInvocations) {
return array_merge($config ?? [], ['includeServerSideToolInvocations' => true]);
}

return $config;
}
}
15 changes: 10 additions & 5 deletions tests/Providers/Gemini/GeminiTextTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
use Prism\Prism\Enums\Citations\CitationSourceType;
use Prism\Prism\Enums\FinishReason;
use Prism\Prism\Enums\Provider;
use Prism\Prism\Exceptions\PrismException;
use Prism\Prism\Facades\Prism;
use Prism\Prism\Schema\ArraySchema;
use Prism\Prism\Schema\BooleanSchema;
Expand Down Expand Up @@ -405,7 +404,7 @@ function (Request $request): bool {
});
});

it('throws an exception if provider tools are enabled with other tools', function (): void {
it('sends includeServerSideToolInvocations when provider tools and custom tools are both present', function (): void {
FixtureResponse::fakeResponseSequence('*', 'gemini/generate-text-with-search-grounding');

$tools = [
Expand All @@ -417,13 +416,19 @@ function (Request $request): bool {
];

Prism::text()
->using(Provider::Gemini, 'gemini-2.0-flash')
->withMaxSteps(3)
->using(Provider::Gemini, 'gemini-3.1-pro-preview')
->withMaxSteps(1)
->withTools($tools)
->withProviderTools([new ProviderTool('google_search')])
->withPrompt('What sport fixtures are on today, and will I need a coat based on today\'s weather forecast?')
->asText();
})->throws(PrismException::class, 'Use of provider tools with custom tools is not currently supported by Gemini.');

Http::assertSent(function (Request $request): bool {
$data = $request->data();

return ($data['tool_config']['includeServerSideToolInvocations'] ?? false) === true;
});
});

it('adds file_search provider tool with options to the request', function (): void {
FixtureResponse::fakeResponseSequence('*', 'gemini/generate-text-with-file-search');
Expand Down