diff --git a/src/Schemas/Converse/ConverseStructuredHandler.php b/src/Schemas/Converse/ConverseStructuredHandler.php index 9ca7c37..694e613 100644 --- a/src/Schemas/Converse/ConverseStructuredHandler.php +++ b/src/Schemas/Converse/ConverseStructuredHandler.php @@ -36,7 +36,9 @@ public function __construct(mixed ...$args) #[\Override] public function handle(Request $request): StructuredResponse { - $this->appendMessageForJsonMode($request); + if (! $request->providerOptions('validated_schema')) { + $this->appendMessageForJsonMode($request); + } $this->sendRequest($request); @@ -82,6 +84,21 @@ public static function buildPayload(Request $request): array 'promptVariables' => $request->providerOptions('promptVariables'), 'requestMetadata' => $request->providerOptions('requestMetadata'), 'system' => MessageMap::mapSystemMessages($request->systemPrompts()), + ...($request->providerOptions('validated_schema') + ? [ + 'outputConfig' => [ + 'textFormat' => [ + 'type' => 'json_schema', + 'structure' => [ + 'jsonSchema' => [ + 'schema' => json_encode($request->schema()->toArray()), + 'name' => $request->schema()->name(), + 'description' => 'The output schema', + ], + ], + ], + ], + ] : []), ]); } diff --git a/tests/Fixtures/converse/structured-validated-schema-1.json b/tests/Fixtures/converse/structured-validated-schema-1.json new file mode 100644 index 0000000..7f9f8cc --- /dev/null +++ b/tests/Fixtures/converse/structured-validated-schema-1.json @@ -0,0 +1 @@ +{"metrics":{"latencyMs":2548},"output":{"message":{"content":[{"text":"{\"weather\":\"70º\",\"game_time\":\"3pm\",\"coat_required\":false}"}],"role":"assistant"}},"stopReason":"end_turn","usage":{"cacheReadInputTokenCount":0,"cacheReadInputTokens":0,"cacheWriteInputTokenCount":0,"cacheWriteInputTokens":0,"inputTokens":273,"outputTokens":22,"serverToolUsage":{},"totalTokens":295}} \ No newline at end of file diff --git a/tests/Schemas/Converse/ConverseStructuredHandlerTest.php b/tests/Schemas/Converse/ConverseStructuredHandlerTest.php index ae3e9d5..5f5b5c5 100644 --- a/tests/Schemas/Converse/ConverseStructuredHandlerTest.php +++ b/tests/Schemas/Converse/ConverseStructuredHandlerTest.php @@ -200,3 +200,48 @@ ], ])->not()->toHaveKey('guardRailConfig')); }); + +it('uses validated results when flag is set in provider options', function () { + FixtureResponse::fakeResponseSequence('converse', 'converse/structured-validated-schema'); + + $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'] + ); + + $response = Prism::structured() + ->withSchema($schema) + ->using('bedrock', 'us.anthropic.claude-haiku-4-5-20251001-v1:0') + ->withProviderOptions(['apiSchema' => BedrockSchema::Converse, 'validated_schema' => true]) + ->withSystemPrompt('The tigers game is at 3pm and the temperature will be 70º') + ->withPrompt('What time is the tigers game today and should I wear a coat?') + ->asStructured(); + + expect($response->structured)->toBeArray(); + expect($response->structured)->toHaveKeys([ + 'weather', + 'game_time', + 'coat_required', + ]); + + Http::assertSent(fn (Request $request): \Pest\Mixins\Expectation|\Pest\Expectation => expect($request->data())->toMatchArray([ + 'outputConfig' => [ + 'textFormat' => [ + 'type' => 'json_schema', + 'structure' => [ + 'jsonSchema' => [ + 'schema' => json_encode($schema->toArray()), + 'name' => $schema->name(), + 'description' => 'The output schema', + ], + ], + ], + ], + ])); +});