diff --git a/src/Schemas/Anthropic/AnthropicStructuredHandler.php b/src/Schemas/Anthropic/AnthropicStructuredHandler.php index 8398f00..6b8edf1 100644 --- a/src/Schemas/Anthropic/AnthropicStructuredHandler.php +++ b/src/Schemas/Anthropic/AnthropicStructuredHandler.php @@ -79,7 +79,7 @@ public static function buildPayload(Request $request, ?string $apiVersion): arra 'system' => MessageMap::mapSystemMessages($request->systemPrompts()), 'temperature' => $request->temperature(), 'top_p' => $request->topP(), - ]); + ], fn ($value): bool => $value !== null); } protected function sendRequest(Request $request): void diff --git a/src/Schemas/Anthropic/AnthropicTextHandler.php b/src/Schemas/Anthropic/AnthropicTextHandler.php index 000a475..03a2d33 100644 --- a/src/Schemas/Anthropic/AnthropicTextHandler.php +++ b/src/Schemas/Anthropic/AnthropicTextHandler.php @@ -80,7 +80,7 @@ public static function buildPayload(Request $request, ?string $apiVersion): arra 'top_p' => $request->topP(), 'tools' => ToolMap::map($request->tools()), 'tool_choice' => ToolChoiceMap::map($request->toolChoice()), - ]); + ], fn ($value): bool => $value !== null); } protected function sendRequest(Request $request): void diff --git a/src/Schemas/Converse/ConverseStructuredHandler.php b/src/Schemas/Converse/ConverseStructuredHandler.php index a6800e8..aa92e40 100644 --- a/src/Schemas/Converse/ConverseStructuredHandler.php +++ b/src/Schemas/Converse/ConverseStructuredHandler.php @@ -77,7 +77,7 @@ public static function buildPayload(Request $request): array 'maxTokens' => $request->maxTokens(), 'temperature' => $request->temperature(), 'topP' => $request->topP(), - ]), + ], fn ($value): bool => $value !== null), 'messages' => MessageMap::map($request->messages()), 'performanceConfig' => $request->providerOptions('performanceConfig'), 'promptVariables' => $request->providerOptions('promptVariables'), diff --git a/src/Schemas/Converse/ConverseTextHandler.php b/src/Schemas/Converse/ConverseTextHandler.php index ef2b1d5..c84da55 100644 --- a/src/Schemas/Converse/ConverseTextHandler.php +++ b/src/Schemas/Converse/ConverseTextHandler.php @@ -75,7 +75,7 @@ public static function buildPayload(Request $request, int $stepCount = 0): array 'maxTokens' => $request->maxTokens(), 'temperature' => $request->temperature(), 'topP' => $request->topP(), - ]), + ], fn ($value): bool => $value !== null), 'messages' => MessageMap::map($request->messages()), 'system' => MessageMap::mapSystemMessages($request->systemPrompts()), 'toolConfig' => $request->tools() === [] diff --git a/tests/Schemas/Anthropic/AnthropicStructuredHandlerTest.php b/tests/Schemas/Anthropic/AnthropicStructuredHandlerTest.php index 101a3bd..a84fb68 100644 --- a/tests/Schemas/Anthropic/AnthropicStructuredHandlerTest.php +++ b/tests/Schemas/Anthropic/AnthropicStructuredHandlerTest.php @@ -4,6 +4,8 @@ namespace Tests\Schemas\Anthropic; +use Illuminate\Http\Client\Request; +use Illuminate\Support\Facades\Http; use Prism\Prism\Prism; use Prism\Prism\Schema\BooleanSchema; use Prism\Prism\Schema\ObjectSchema; @@ -41,3 +43,32 @@ expect($response->structured['game_time'])->toBeString(); expect($response->structured['coat_required'])->toBeBool(); }); + +it('does not remove 0 values from payloads', function (): void { + FixtureResponse::fakeResponseSequence('invoke', 'anthropic/structured'); + + $schema = new ObjectSchema( + 'output', + 'the output object', + [ + new StringSchema('weather', 'The weather forecast'), + new StringSchema('game_time', 'The tigers game time'), + new BooleanSchema('coat_required', 'whether a coat is required'), + ], + ['weather', 'game_time', 'coat_required'] + ); + + Prism::structured() + ->withSchema($schema) + ->using('bedrock', 'anthropic.claude-3-5-haiku-20241022-v1:0') + ->withProviderOptions([ + 'guardRailConfig' => null, + ]) + ->withMaxTokens(2048) + ->usingTemperature(0) + ->asStructured(); + + Http::assertSent(fn (Request $request): \Pest\Mixins\Expectation|\Pest\Expectation => expect($request->data())->toMatchArray([ + 'temperature' => 0, + ])); +}); diff --git a/tests/Schemas/Anthropic/AnthropicTextHandlerTest.php b/tests/Schemas/Anthropic/AnthropicTextHandlerTest.php index f629c6f..202f1fc 100644 --- a/tests/Schemas/Anthropic/AnthropicTextHandlerTest.php +++ b/tests/Schemas/Anthropic/AnthropicTextHandlerTest.php @@ -193,3 +193,17 @@ Http::assertSent(fn (Request $request): bool => $request->header('explicitPromptCaching')[0] === 'enabled'); }); + +it('does not remove 0 values from payloads', function (): void { + FixtureResponse::fakeResponseSequence('invoke', 'anthropic/generate-text-with-a-prompt'); + + Prism::text() + ->using('bedrock', 'anthropic.claude-3-5-haiku-20241022-v1:0') + ->withPrompt('Who are you?') + ->usingTemperature(0) + ->asText(); + + Http::assertSent(fn (Request $request): \Pest\Mixins\Expectation|\Pest\Expectation => expect($request->data())->toMatchArray([ + 'temperature' => 0, + ])); +}); diff --git a/tests/Schemas/Converse/ConverseStructuredHandlerTest.php b/tests/Schemas/Converse/ConverseStructuredHandlerTest.php index fcb3404..60c632a 100644 --- a/tests/Schemas/Converse/ConverseStructuredHandlerTest.php +++ b/tests/Schemas/Converse/ConverseStructuredHandlerTest.php @@ -4,6 +4,8 @@ namespace Tests\Schemas\Converse; +use Illuminate\Http\Client\Request; +use Illuminate\Support\Facades\Http; use Prism\Bedrock\Enums\BedrockSchema; use Prism\Prism\Prism; use Prism\Prism\Schema\BooleanSchema; @@ -94,3 +96,36 @@ $fake->assertRequest(fn (array $requests): mixed => expect($requests[0]->providerOptions())->toBe($providerOptions)); }); + +it('does not remove 0 values from payloads', function (): void { + FixtureResponse::fakeResponseSequence('converse', 'converse/structured'); + + $schema = new ObjectSchema( + 'output', + 'the output object', + [ + new StringSchema('weather', 'The weather forecast'), + new StringSchema('game_time', 'The tigers game time'), + new BooleanSchema('coat_required', 'whether a coat is required'), + ], + ['weather', 'game_time', 'coat_required'] + ); + + Prism::structured() + ->withSchema($schema) + ->using('bedrock', 'anthropic.claude-3-5-haiku-20241022-v1:0') + ->withProviderOptions([ + 'apiSchema' => BedrockSchema::Converse, + 'guardRailConfig' => null, + ]) + ->withMaxTokens(2048) + ->usingTemperature(0) + ->asStructured(); + + Http::assertSent(fn (Request $request): \Pest\Mixins\Expectation|\Pest\Expectation => expect($request->data())->toMatchArray([ + 'inferenceConfig' => [ + 'maxTokens' => 2048, + 'temperature' => 0, + ], + ])->not()->toHaveKey('guardRailConfig')); +}); diff --git a/tests/Schemas/Converse/ConverseTextHandlerTest.php b/tests/Schemas/Converse/ConverseTextHandlerTest.php index 8fd2aaf..3e7a4d0 100644 --- a/tests/Schemas/Converse/ConverseTextHandlerTest.php +++ b/tests/Schemas/Converse/ConverseTextHandlerTest.php @@ -273,3 +273,20 @@ $fake->assertRequest(fn (array $requests): mixed => expect($requests[0]->providerOptions())->toBe($providerOptions)); }); + +it('does not remove zero values from payload', function (): void { + FixtureResponse::fakeResponseSequence('converse', 'converse/generate-text-with-a-prompt'); + + Prism::text() + ->using('bedrock', 'amazon.nova-micro-v1:0') + ->withPrompt('Who are you?') + ->usingTemperature(0) + ->asText(); + + Http::assertSent(fn (Request $request): \Pest\Mixins\Expectation|\Pest\Expectation => expect($request->data())->toMatchArray([ + 'inferenceConfig' => [ + 'temperature' => 0, + 'maxTokens' => 2048, + ], + ])); +});