From ae5e0288a2a9e7f2b435fbe9ecc186263cf79227 Mon Sep 17 00:00:00 2001 From: Christopher Hertel Date: Sat, 5 Oct 2024 10:58:57 +0200 Subject: [PATCH] refactor: `ResponseInterface` as `Chain` return value (#117) * refactor: ResponseInterface as Chain return value * tests: Add Response Tests (#120) * Add tests * - * - * - * - * - * - * rebase --------- Co-authored-by: Oskar Stark --- examples/chat-claude-anthropic.php | 2 +- examples/chat-gpt-azure.php | 2 +- examples/chat-gpt-openai.php | 2 +- examples/image-describer.php | 2 +- examples/reasoning-openai.php | 2 +- examples/store-mongodb-similarity-search.php | 2 +- examples/store-pinecone-similarity-search.php | 2 +- examples/stream-claude-anthropic.php | 4 +- examples/stream-gpt-openai.php | 4 +- examples/structured-output-math.php | 2 +- examples/toolbox-clock.php | 2 +- examples/toolbox-serpapi.php | 2 +- examples/toolbox-weather.php | 2 +- examples/toolbox-wikipedia.php | 2 +- examples/toolbox-youtube.php | 4 +- src/Anthropic/Model/Claude.php | 5 +- src/Chain.php | 5 +- src/OpenAI/Model/Gpt.php | 23 +++- src/Response/ChoiceResponse.php | 32 +++++ src/Response/Response.php | 59 ---------- src/Response/ResponseInterface.php | 16 +-- src/Response/StreamResponse.php | 15 --- src/Response/StructuredResponse.php | 24 ++++ src/Response/TextResponse.php | 18 +++ src/Response/ToolCallResponse.php | 32 +++++ src/StructuredOutput/ChainProcessor.php | 7 +- src/ToolBox/ChainProcessor.php | 13 ++- tests/Response/ChoiceResponseTest.php | 43 +++++++ tests/Response/ResponseTest.php | 109 ------------------ tests/Response/StreamResponseTest.php | 34 ++++++ tests/Response/StructuredResponseTest.php | 30 +++++ tests/Response/TextResponseTest.php | 23 ++++ tests/Response/TollCallResponseTest.php | 36 ++++++ tests/ToolBox/ChainProcessorTest.php | 11 +- 34 files changed, 331 insertions(+), 240 deletions(-) create mode 100644 src/Response/ChoiceResponse.php delete mode 100644 src/Response/Response.php create mode 100644 src/Response/StructuredResponse.php create mode 100644 src/Response/TextResponse.php create mode 100644 src/Response/ToolCallResponse.php create mode 100644 tests/Response/ChoiceResponseTest.php delete mode 100644 tests/Response/ResponseTest.php create mode 100644 tests/Response/StreamResponseTest.php create mode 100644 tests/Response/StructuredResponseTest.php create mode 100644 tests/Response/TextResponseTest.php create mode 100644 tests/Response/TollCallResponseTest.php diff --git a/examples/chat-claude-anthropic.php b/examples/chat-claude-anthropic.php index 1c11a366..84a449a3 100755 --- a/examples/chat-claude-anthropic.php +++ b/examples/chat-claude-anthropic.php @@ -26,4 +26,4 @@ ); $response = $chain->call($messages); -echo $response.PHP_EOL; +echo $response->getContent().PHP_EOL; diff --git a/examples/chat-gpt-azure.php b/examples/chat-gpt-azure.php index 4c8c5f8a..15628fab 100755 --- a/examples/chat-gpt-azure.php +++ b/examples/chat-gpt-azure.php @@ -33,4 +33,4 @@ ); $response = $chain->call($messages); -echo $response.PHP_EOL; +echo $response->getContent().PHP_EOL; diff --git a/examples/chat-gpt-openai.php b/examples/chat-gpt-openai.php index 99da35c4..e241a348 100755 --- a/examples/chat-gpt-openai.php +++ b/examples/chat-gpt-openai.php @@ -31,4 +31,4 @@ 'max_tokens' => 500, // specific options just for this call ]); -echo $response.PHP_EOL; +echo $response->getContent().PHP_EOL; diff --git a/examples/image-describer.php b/examples/image-describer.php index 138e864d..e80c7179 100755 --- a/examples/image-describer.php +++ b/examples/image-describer.php @@ -32,4 +32,4 @@ ); $response = $chain->call($messages); -echo $response.PHP_EOL; +echo $response->getContent().PHP_EOL; diff --git a/examples/reasoning-openai.php b/examples/reasoning-openai.php index e2092b0d..3e7aefc9 100755 --- a/examples/reasoning-openai.php +++ b/examples/reasoning-openai.php @@ -36,4 +36,4 @@ $response = (new Chain($llm))->call(new MessageBag(Message::ofUser($prompt))); -echo $response.PHP_EOL; +echo $response->getContent().PHP_EOL; diff --git a/examples/store-mongodb-similarity-search.php b/examples/store-mongodb-similarity-search.php index f396991d..0a2101db 100755 --- a/examples/store-mongodb-similarity-search.php +++ b/examples/store-mongodb-similarity-search.php @@ -74,4 +74,4 @@ ); $response = $chain->call($messages); -echo $response.PHP_EOL; +echo $response->getContent().PHP_EOL; diff --git a/examples/store-pinecone-similarity-search.php b/examples/store-pinecone-similarity-search.php index 4922fa73..547beb7b 100755 --- a/examples/store-pinecone-similarity-search.php +++ b/examples/store-pinecone-similarity-search.php @@ -65,4 +65,4 @@ ); $response = $chain->call($messages); -echo $response.PHP_EOL; +echo $response->getContent().PHP_EOL; diff --git a/examples/stream-claude-anthropic.php b/examples/stream-claude-anthropic.php index 39a9dcc1..08f12fcf 100644 --- a/examples/stream-claude-anthropic.php +++ b/examples/stream-claude-anthropic.php @@ -28,9 +28,7 @@ 'stream' => true, // enable streaming of response text ]); -assert($response instanceof Generator); - -foreach ($response as $word) { +foreach ($response->getContent() as $word) { echo $word; } echo PHP_EOL; diff --git a/examples/stream-gpt-openai.php b/examples/stream-gpt-openai.php index 35b8e8ba..a668b0ab 100644 --- a/examples/stream-gpt-openai.php +++ b/examples/stream-gpt-openai.php @@ -29,9 +29,7 @@ 'stream' => true, // enable streaming of response text ]); -assert($response instanceof Generator); - -foreach ($response as $word) { +foreach ($response->getContent() as $word) { echo $word; } echo PHP_EOL; diff --git a/examples/structured-output-math.php b/examples/structured-output-math.php index c021e718..7dd48d25 100644 --- a/examples/structured-output-math.php +++ b/examples/structured-output-math.php @@ -35,4 +35,4 @@ ); $response = $chain->call($messages, ['output_structure' => MathReasoning::class]); -dump($response); +dump($response->getContent()); diff --git a/examples/toolbox-clock.php b/examples/toolbox-clock.php index 385fde87..99f1a8a7 100755 --- a/examples/toolbox-clock.php +++ b/examples/toolbox-clock.php @@ -33,4 +33,4 @@ $messages = new MessageBag(Message::ofUser('What date and time is it?')); $response = $chain->call($messages); -echo $response.PHP_EOL; +echo $response->getContent().PHP_EOL; diff --git a/examples/toolbox-serpapi.php b/examples/toolbox-serpapi.php index 09508cd8..197ee809 100755 --- a/examples/toolbox-serpapi.php +++ b/examples/toolbox-serpapi.php @@ -32,4 +32,4 @@ $messages = new MessageBag(Message::ofUser('Who is the current chancellor of Germany?')); $response = $chain->call($messages); -echo $response.PHP_EOL; +echo $response->getContent().PHP_EOL; diff --git a/examples/toolbox-weather.php b/examples/toolbox-weather.php index 2599a291..8dba9b0a 100755 --- a/examples/toolbox-weather.php +++ b/examples/toolbox-weather.php @@ -33,4 +33,4 @@ $messages = new MessageBag(Message::ofUser('How is the weather currently in Berlin?')); $response = $chain->call($messages); -echo $response.PHP_EOL; +echo $response->getContent().PHP_EOL; diff --git a/examples/toolbox-wikipedia.php b/examples/toolbox-wikipedia.php index ec71cfb0..06e3362f 100755 --- a/examples/toolbox-wikipedia.php +++ b/examples/toolbox-wikipedia.php @@ -33,4 +33,4 @@ $messages = new MessageBag(Message::ofUser('Who is the current chancellor of Germany?')); $response = $chain->call($messages); -echo $response.PHP_EOL; +echo $response->getContent().PHP_EOL; diff --git a/examples/toolbox-youtube.php b/examples/toolbox-youtube.php index 130fd3e5..f3a63c8d 100755 --- a/examples/toolbox-youtube.php +++ b/examples/toolbox-youtube.php @@ -35,9 +35,7 @@ 'stream' => true, // enable streaming of response text ]); -assert($response instanceof Generator); - -foreach ($response as $word) { +foreach ($response->getContent() as $word) { echo $word; } echo PHP_EOL; diff --git a/src/Anthropic/Model/Claude.php b/src/Anthropic/Model/Claude.php index 3be4cfba..fda96696 100644 --- a/src/Anthropic/Model/Claude.php +++ b/src/Anthropic/Model/Claude.php @@ -8,10 +8,9 @@ use PhpLlm\LlmChain\Anthropic\Platform; use PhpLlm\LlmChain\LanguageModel; use PhpLlm\LlmChain\Message\MessageBag; -use PhpLlm\LlmChain\Response\Choice; -use PhpLlm\LlmChain\Response\Response; use PhpLlm\LlmChain\Response\ResponseInterface; use PhpLlm\LlmChain\Response\StreamResponse; +use PhpLlm\LlmChain\Response\TextResponse; final class Claude implements LanguageModel { @@ -45,7 +44,7 @@ public function call(MessageBag $messages, array $options = []): ResponseInterfa return new StreamResponse($this->convertStream($response)); } - return new Response(new Choice($response['content'][0]['text'])); + return new TextResponse($response['content'][0]['text']); } public function supportsToolCalling(): bool diff --git a/src/Chain.php b/src/Chain.php index d4113c22..dbe3e216 100644 --- a/src/Chain.php +++ b/src/Chain.php @@ -10,6 +10,7 @@ use PhpLlm\LlmChain\Chain\OutputProcessor; use PhpLlm\LlmChain\Exception\MissingModelSupport; use PhpLlm\LlmChain\Message\MessageBag; +use PhpLlm\LlmChain\Response\ResponseInterface; final readonly class Chain { @@ -39,7 +40,7 @@ public function __construct( /** * @param array $options */ - public function call(MessageBag $messages, array $options = []): string|object + public function call(MessageBag $messages, array $options = []): ResponseInterface { $input = new Input($this->llm, $messages, $options); array_map(fn (InputProcessor $processor) => $processor->processInput($input), $this->inputProcessor); @@ -59,6 +60,6 @@ public function call(MessageBag $messages, array $options = []): string|object } } - return $response->getContent(); + return $response; } } diff --git a/src/OpenAI/Model/Gpt.php b/src/OpenAI/Model/Gpt.php index 9b35e145..cb4ec476 100644 --- a/src/OpenAI/Model/Gpt.php +++ b/src/OpenAI/Model/Gpt.php @@ -10,10 +10,12 @@ use PhpLlm\LlmChain\OpenAI\Model\Gpt\Version; use PhpLlm\LlmChain\OpenAI\Platform; use PhpLlm\LlmChain\Response\Choice; -use PhpLlm\LlmChain\Response\Response; +use PhpLlm\LlmChain\Response\ChoiceResponse; use PhpLlm\LlmChain\Response\ResponseInterface; use PhpLlm\LlmChain\Response\StreamResponse; +use PhpLlm\LlmChain\Response\TextResponse; use PhpLlm\LlmChain\Response\ToolCall; +use PhpLlm\LlmChain\Response\ToolCallResponse; final class Gpt implements LanguageModel { @@ -45,7 +47,22 @@ public function call(MessageBag $messages, array $options = []): ResponseInterfa return new StreamResponse($this->convertStream($response)); } - return new Response(...array_map([$this, 'convertChoice'], $response['choices'])); + if (!isset($response['choices'])) { + throw new RuntimeException('Response does not contain choices'); + } + + /** @var Choice[] $choices */ + $choices = array_map([$this, 'convertChoice'], $response['choices']); + + if (1 !== count($choices)) { + return new ChoiceResponse(...$choices); + } + + if ($choices[0]->hasToolCall()) { + return new ToolCallResponse(...$choices[0]->getToolCalls()); + } + + return new TextResponse($choices[0]->getContent()); } public function supportsToolCalling(): bool @@ -104,7 +121,7 @@ private function convertChoice(array $choice): Choice return new Choice($choice['message']['content']); } - throw new RuntimeException('Unsupported finish reason'); + throw new RuntimeException(sprintf('Unsupported finish reason "%s".', $choice['finish_reason'])); } /** diff --git a/src/Response/ChoiceResponse.php b/src/Response/ChoiceResponse.php new file mode 100644 index 00000000..0ae05e01 --- /dev/null +++ b/src/Response/ChoiceResponse.php @@ -0,0 +1,32 @@ +choices = $choices; + } + + /** + * @return Choice[] + */ + public function getContent(): array + { + return $this->choices; + } +} diff --git a/src/Response/Response.php b/src/Response/Response.php deleted file mode 100644 index 6290ed96..00000000 --- a/src/Response/Response.php +++ /dev/null @@ -1,59 +0,0 @@ -choices = $choice; - } - - public function getChoices(): array - { - return $this->choices; - } - - public function getContent(): ?string - { - if (1 < count($this->choices)) { - throw new LogicException('Response has more than one choice'); - } - - return $this->choices[0]->getContent(); - } - - public function getToolCalls(): array - { - if (1 < count($this->choices)) { - throw new LogicException('Response has more than one choice'); - } - - return $this->choices[0]->getToolCalls(); - } - - public function hasToolCalls(): bool - { - foreach ($this->choices as $choice) { - if ($choice->hasToolCall()) { - return true; - } - } - - return false; - } -} diff --git a/src/Response/ResponseInterface.php b/src/Response/ResponseInterface.php index c9859714..fa1fbaa4 100644 --- a/src/Response/ResponseInterface.php +++ b/src/Response/ResponseInterface.php @@ -7,19 +7,7 @@ interface ResponseInterface { /** - * @return Choice[] + * @return string|iterable|object|null */ - public function getChoices(): array; - - /** - * @return string|iterable|null - */ - public function getContent(): string|iterable|null; - - /** - * @return ToolCall[] - */ - public function getToolCalls(): array; - - public function hasToolCalls(): bool; + public function getContent(): string|iterable|object|null; } diff --git a/src/Response/StreamResponse.php b/src/Response/StreamResponse.php index 96b1aec3..499bca3e 100644 --- a/src/Response/StreamResponse.php +++ b/src/Response/StreamResponse.php @@ -11,23 +11,8 @@ public function __construct( ) { } - public function getChoices(): array - { - throw new \LogicException('Stream response does not have choices'); - } - public function getContent(): \Generator { yield from $this->generator; } - - public function getToolCalls(): array - { - return []; - } - - public function hasToolCalls(): bool - { - return false; - } } diff --git a/src/Response/StructuredResponse.php b/src/Response/StructuredResponse.php new file mode 100644 index 00000000..4e77fbce --- /dev/null +++ b/src/Response/StructuredResponse.php @@ -0,0 +1,24 @@ + $structuredOutput + */ + public function __construct( + private object|array $structuredOutput, + ) { + } + + /** + * @return object|array + */ + public function getContent(): object|array + { + return $this->structuredOutput; + } +} diff --git a/src/Response/TextResponse.php b/src/Response/TextResponse.php new file mode 100644 index 00000000..86c90d02 --- /dev/null +++ b/src/Response/TextResponse.php @@ -0,0 +1,18 @@ +content; + } +} diff --git a/src/Response/ToolCallResponse.php b/src/Response/ToolCallResponse.php new file mode 100644 index 00000000..23caa2e9 --- /dev/null +++ b/src/Response/ToolCallResponse.php @@ -0,0 +1,32 @@ +toolCalls = $toolCalls; + } + + /** + * @return ToolCall[] + */ + public function getContent(): array + { + return $this->toolCalls; + } +} diff --git a/src/StructuredOutput/ChainProcessor.php b/src/StructuredOutput/ChainProcessor.php index af4b9433..f7d7cf9a 100644 --- a/src/StructuredOutput/ChainProcessor.php +++ b/src/StructuredOutput/ChainProcessor.php @@ -10,6 +10,7 @@ use PhpLlm\LlmChain\Chain\OutputProcessor; use PhpLlm\LlmChain\Exception\InvalidArgumentException; use PhpLlm\LlmChain\Exception\MissingModelSupport; +use PhpLlm\LlmChain\Response\StructuredResponse; use Symfony\Component\Serializer\SerializerInterface; final class ChainProcessor implements InputProcessor, OutputProcessor @@ -46,7 +47,7 @@ public function processInput(Input $input): void $input->setOptions($options); } - public function processOutput(Output $output): ?object + public function processOutput(Output $output): ?StructuredResponse { $options = $output->options; @@ -54,6 +55,8 @@ public function processOutput(Output $output): ?object return null; } - return $this->serializer->deserialize($output->response->getContent(), $this->outputStructure, 'json'); + return new StructuredResponse( + $this->serializer->deserialize($output->response->getContent(), $this->outputStructure, 'json') + ); } } diff --git a/src/ToolBox/ChainProcessor.php b/src/ToolBox/ChainProcessor.php index 4d56b808..12ffe3a0 100644 --- a/src/ToolBox/ChainProcessor.php +++ b/src/ToolBox/ChainProcessor.php @@ -10,6 +10,8 @@ use PhpLlm\LlmChain\Chain\OutputProcessor; use PhpLlm\LlmChain\Exception\MissingModelSupport; use PhpLlm\LlmChain\Message\Message; +use PhpLlm\LlmChain\Response\ResponseInterface; +use PhpLlm\LlmChain\Response\ToolCallResponse; final readonly class ChainProcessor implements InputProcessor, OutputProcessor { @@ -28,15 +30,16 @@ public function processInput(Input $input): void $input->setOptions($options); } - public function processOutput(Output $output): mixed + public function processOutput(Output $output): ResponseInterface { $response = $output->response; $messages = clone $output->messages; - while ($response->hasToolCalls()) { - $messages[] = Message::ofAssistant(toolCalls: $response->getToolCalls()); + while ($response instanceof ToolCallResponse) { + $toolCalls = $response->getContent(); + $messages[] = Message::ofAssistant(toolCalls: $toolCalls); - foreach ($response->getToolCalls() as $toolCall) { + foreach ($toolCalls as $toolCall) { $result = $this->toolBox->execute($toolCall); $messages[] = Message::ofToolCall($toolCall, $result); } @@ -44,6 +47,6 @@ public function processOutput(Output $output): mixed $response = $output->llm->call($messages, $output->options); } - return $response->getContent(); + return $response; } } diff --git a/tests/Response/ChoiceResponseTest.php b/tests/Response/ChoiceResponseTest.php new file mode 100644 index 00000000..036bca42 --- /dev/null +++ b/tests/Response/ChoiceResponseTest.php @@ -0,0 +1,43 @@ +getContent()); + self::assertSame('choice1', $response->getContent()[0]->getContent()); + self::assertNull($response->getContent()[1]->getContent()); + self::assertSame('choice3', $response->getContent()[2]->getContent()); + } + + #[Test] + public function choiceResponseWithNoChoices(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Response must have at least one choice.'); + + new ChoiceResponse(); + } +} diff --git a/tests/Response/ResponseTest.php b/tests/Response/ResponseTest.php deleted file mode 100644 index b802a6cb..00000000 --- a/tests/Response/ResponseTest.php +++ /dev/null @@ -1,109 +0,0 @@ - 'bar'])]), - new Choice('content', [new ToolCall('call_234567', 'name', ['foo' => 'bar'])]), - ); - - self::assertCount(2, $response->getChoices()); - } - - #[Test] - public function constructorThrowsException(): void - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Response must have at least one choice'); - - new Response(); - } - - #[Test] - public function getContent(): void - { - $response = new Response( - new Choice('content', [new ToolCall('call_123456', 'name', ['foo' => 'bar'])]), - ); - - self::assertSame('content', $response->getContent()); - } - - #[Test] - public function getContentThrowsException(): void - { - $response = new Response( - new Choice('content', [new ToolCall('call_123456', 'name', ['foo' => 'bar'])]), - new Choice('content', [new ToolCall('call_123456', 'name', ['foo' => 'bar'])]), - ); - - $this->expectException(LogicException::class); - $this->expectExceptionMessage('Response has more than one choice'); - - $response->getContent(); - } - - #[Test] - public function getToolCalls(): void - { - $response = new Response( - new Choice('content', [new ToolCall('call_123456', 'name', ['foo' => 'bar'])]), - ); - - self::assertCount(1, $response->getToolCalls()); - } - - #[Test] - public function getToolCallsThrowsException(): void - { - $response = new Response( - new Choice('content', [new ToolCall('call_123456', 'name', ['foo' => 'bar'])]), - new Choice('content', [new ToolCall('call_123456', 'name', ['foo' => 'bar'])]), - ); - - $this->expectException(LogicException::class); - $this->expectExceptionMessage('Response has more than one choice'); - - $response->getToolCalls(); - } - - #[Test] - public function hasToolCalls(): void - { - $response = new Response( - new Choice('content', [new ToolCall('call_123456', 'name', ['foo' => 'bar'])]), - ); - - self::assertTrue($response->hasToolCalls()); - } - - #[Test] - public function hasToolCallsReturnsFalse(): void - { - $response = new Response(new Choice('content')); - - self::assertFalse($response->hasToolCalls()); - } -} diff --git a/tests/Response/StreamResponseTest.php b/tests/Response/StreamResponseTest.php new file mode 100644 index 00000000..40153b0c --- /dev/null +++ b/tests/Response/StreamResponseTest.php @@ -0,0 +1,34 @@ +getContent()); + + $content = iterator_to_array($response->getContent()); + + self::assertCount(2, $content); + self::assertSame('data1', $content[0]); + self::assertSame('data2', $content[1]); + } +} diff --git a/tests/Response/StructuredResponseTest.php b/tests/Response/StructuredResponseTest.php new file mode 100644 index 00000000..0f944811 --- /dev/null +++ b/tests/Response/StructuredResponseTest.php @@ -0,0 +1,30 @@ + 'bar', 'baz' => ['qux']]); + self::assertSame($expected, $response->getContent()); + } + + #[Test] + public function getContentWithObject(): void + { + $response = new StructuredResponse($expected = (object) ['foo' => 'bar', 'baz' => ['qux']]); + self::assertSame($expected, $response->getContent()); + } +} diff --git a/tests/Response/TextResponseTest.php b/tests/Response/TextResponseTest.php new file mode 100644 index 00000000..ffacb0e8 --- /dev/null +++ b/tests/Response/TextResponseTest.php @@ -0,0 +1,23 @@ +getContent()); + } +} diff --git a/tests/Response/TollCallResponseTest.php b/tests/Response/TollCallResponseTest.php new file mode 100644 index 00000000..c8ae9295 --- /dev/null +++ b/tests/Response/TollCallResponseTest.php @@ -0,0 +1,36 @@ +expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Response must have at least one tool call.'); + + new ToolCallResponse(); + } + + #[Test] + public function getContent(): void + { + $response = new ToolCallResponse($toolCall = new ToolCall('ID', 'name', ['foo' => 'bar'])); + self::assertSame([$toolCall], $response->getContent()); + } +} diff --git a/tests/ToolBox/ChainProcessorTest.php b/tests/ToolBox/ChainProcessorTest.php index 5dd5ae06..3866d418 100644 --- a/tests/ToolBox/ChainProcessorTest.php +++ b/tests/ToolBox/ChainProcessorTest.php @@ -10,7 +10,7 @@ use PhpLlm\LlmChain\LanguageModel; use PhpLlm\LlmChain\Message\MessageBag; use PhpLlm\LlmChain\Response\Choice; -use PhpLlm\LlmChain\Response\Response; +use PhpLlm\LlmChain\Response\TextResponse; use PhpLlm\LlmChain\StructuredOutput\ChainProcessor; use PhpLlm\LlmChain\Tests\Double\ConfigurableResponseFormatFactory; use PhpLlm\LlmChain\Tests\Fixture\SomeStructure; @@ -28,7 +28,6 @@ #[UsesClass(Output::class)] #[UsesClass(MessageBag::class)] #[UsesClass(Choice::class)] -#[UsesClass(Response::class)] #[UsesClass(MissingModelSupport::class)] final class ChainProcessorTest extends TestCase { @@ -95,12 +94,11 @@ public function processOutputWithResponseFormat(): void $input = new Input($llm, new MessageBag(), $options); $chainProcessor->processInput($input); - $choice = new Choice('{"some": "data"}'); - $response = new Response($choice); + $response = new TextResponse('{"some": "data"}'); $output = new Output($llm, $response, new MessageBag(), $options); - $result = $chainProcessor->processOutput($output); + $result = $chainProcessor->processOutput($output)->getContent(); self::assertInstanceOf(SomeStructure::class, $result); self::assertSame('data', $result->some); @@ -114,8 +112,7 @@ public function processOutputWithoutResponseFormat(): void $chainProcessor = new ChainProcessor($responseFormatFactory, $serializer); $llm = $this->createMock(LanguageModel::class); - $choice = new Choice(''); - $response = new Response($choice); + $response = new TextResponse(''); $output = new Output($llm, $response, new MessageBag(), []);