Skip to content

Commit 115bf8c

Browse files
committed
Simplify choice handling
1 parent 2b6f483 commit 115bf8c

File tree

9 files changed

+29
-172
lines changed

9 files changed

+29
-172
lines changed

src/agent/tests/StructuredOutput/AgentProcessorTest.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
use Symfony\AI\Platform\Capability;
2525
use Symfony\AI\Platform\Message\MessageBag;
2626
use Symfony\AI\Platform\Model;
27-
use Symfony\AI\Platform\Result\Choice;
2827
use Symfony\AI\Platform\Result\Metadata\Metadata;
2928
use Symfony\AI\Platform\Result\ObjectResult;
3029
use Symfony\AI\Platform\Result\TextResult;
@@ -34,7 +33,6 @@
3433
#[UsesClass(Input::class)]
3534
#[UsesClass(Output::class)]
3635
#[UsesClass(MessageBag::class)]
37-
#[UsesClass(Choice::class)]
3836
#[UsesClass(MissingModelSupportException::class)]
3937
#[UsesClass(TextResult::class)]
4038
#[UsesClass(ObjectResult::class)]

src/platform/src/Bridge/Gemini/Gemini/ResultConverter.php

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313

1414
use Symfony\AI\Platform\Bridge\Gemini\Gemini;
1515
use Symfony\AI\Platform\Exception\RuntimeException;
16+
use Symfony\AI\Platform\Message\Content\Text;
1617
use Symfony\AI\Platform\Model;
17-
use Symfony\AI\Platform\Result\Choice;
1818
use Symfony\AI\Platform\Result\ChoiceResult;
1919
use Symfony\AI\Platform\Result\RawHttpResult;
2020
use Symfony\AI\Platform\Result\RawResultInterface;
@@ -49,18 +49,13 @@ public function convert(RawResultInterface|RawHttpResult $result, array $options
4949
throw new RuntimeException('Response does not contain any content');
5050
}
5151

52-
/** @var Choice[] $choices */
5352
$choices = array_map($this->convertChoice(...), $data['candidates']);
5453

5554
if (1 !== \count($choices)) {
5655
return new ChoiceResult(...$choices);
5756
}
5857

59-
if ($choices[0]->hasToolCall()) {
60-
return new ToolCallResult(...$choices[0]->getToolCalls());
61-
}
62-
63-
return new TextResult($choices[0]->getContent());
58+
return $choices[0];
6459
}
6560

6661
private function convertStream(HttpResponse $result): \Generator
@@ -94,7 +89,6 @@ private function convertStream(HttpResponse $result): \Generator
9489
throw new RuntimeException('Failed to decode JSON response', 0, $e);
9590
}
9691

97-
/** @var Choice[] $choices */
9892
$choices = array_map($this->convertChoice(...), $data['candidates'] ?? []);
9993

10094
if (!$choices) {
@@ -106,13 +100,7 @@ private function convertStream(HttpResponse $result): \Generator
106100
continue;
107101
}
108102

109-
if ($choices[0]->hasToolCall()) {
110-
yield new ToolCallResult(...$choices[0]->getToolCalls());
111-
}
112-
113-
if ($choices[0]->hasContent()) {
114-
yield $choices[0]->getContent();
115-
}
103+
yield $choices[0]->getContent();
116104
}
117105
}
118106
}
@@ -132,16 +120,16 @@ private function convertStream(HttpResponse $result): \Generator
132120
* }
133121
* } $choice
134122
*/
135-
private function convertChoice(array $choice): Choice
123+
private function convertChoice(array $choice): ToolCallResult|TextResult
136124
{
137125
$contentPart = $choice['content']['parts'][0] ?? [];
138126

139127
if (isset($contentPart['functionCall'])) {
140-
return new Choice(toolCalls: [$this->convertToolCall($contentPart['functionCall'])]);
128+
return new ToolCallResult($this->convertToolCall($contentPart['functionCall']));
141129
}
142130

143131
if (isset($contentPart['text'])) {
144-
return new Choice($contentPart['text']);
132+
return new TextResult($contentPart['text']);
145133
}
146134

147135
throw new RuntimeException(\sprintf('Unsupported finish reason "%s".', $choice['finishReason']));

src/platform/src/Bridge/Mistral/Llm/ResultConverter.php

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
use Symfony\AI\Platform\Bridge\Mistral\Mistral;
1515
use Symfony\AI\Platform\Exception\RuntimeException;
1616
use Symfony\AI\Platform\Model;
17-
use Symfony\AI\Platform\Result\Choice;
1817
use Symfony\AI\Platform\Result\ChoiceResult;
1918
use Symfony\AI\Platform\Result\RawHttpResult;
2019
use Symfony\AI\Platform\Result\RawResultInterface;
@@ -60,18 +59,13 @@ public function convert(RawResultInterface|RawHttpResult $result, array $options
6059
throw new RuntimeException('Response does not contain choices');
6160
}
6261

63-
/** @var Choice[] $choices */
6462
$choices = array_map($this->convertChoice(...), $data['choices']);
6563

6664
if (1 !== \count($choices)) {
6765
return new ChoiceResult(...$choices);
6866
}
6967

70-
if ($choices[0]->hasToolCall()) {
71-
return new ToolCallResult(...$choices[0]->getToolCalls());
72-
}
73-
74-
return new TextResult($choices[0]->getContent());
68+
return $choices[0];
7569
}
7670

7771
private function convertStream(HttpResponse $result): \Generator
@@ -170,14 +164,14 @@ private function isToolCallsStreamFinished(array $data): bool
170164
* finish_reason: 'stop'|'length'|'tool_calls'|'content_filter',
171165
* } $choice
172166
*/
173-
private function convertChoice(array $choice): Choice
167+
private function convertChoice(array $choice): ToolCallResult|TextResult
174168
{
175169
if ('tool_calls' === $choice['finish_reason']) {
176-
return new Choice(toolCalls: array_map([$this, 'convertToolCall'], $choice['message']['tool_calls']));
170+
return new ToolCallResult(...array_map([$this, 'convertToolCall'], $choice['message']['tool_calls']));
177171
}
178172

179173
if ('stop' === $choice['finish_reason']) {
180-
return new Choice($choice['message']['content']);
174+
return new TextResult($choice['message']['content']);
181175
}
182176

183177
throw new RuntimeException(\sprintf('Unsupported finish reason "%s".', $choice['finish_reason']));

src/platform/src/Bridge/OpenAI/GPT/ResultConverter.php

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
use Symfony\AI\Platform\Exception\ContentFilterException;
1616
use Symfony\AI\Platform\Exception\RuntimeException;
1717
use Symfony\AI\Platform\Model;
18-
use Symfony\AI\Platform\Result\Choice;
1918
use Symfony\AI\Platform\Result\ChoiceResult;
2019
use Symfony\AI\Platform\Result\RawHttpResult;
2120
use Symfony\AI\Platform\Result\RawResultInterface;
@@ -57,18 +56,13 @@ public function convert(RawResultInterface|RawHttpResult $result, array $options
5756
throw new RuntimeException('Response does not contain choices');
5857
}
5958

60-
/** @var Choice[] $choices */
6159
$choices = array_map($this->convertChoice(...), $data['choices']);
6260

6361
if (1 !== \count($choices)) {
6462
return new ChoiceResult(...$choices);
6563
}
6664

67-
if ($choices[0]->hasToolCall()) {
68-
return new ToolCallResult(...$choices[0]->getToolCalls());
69-
}
70-
71-
return new TextResult($choices[0]->getContent());
65+
return $choices[0];
7266
}
7367

7468
private function convertStream(HttpResponse $result): \Generator
@@ -167,14 +161,14 @@ private function isToolCallsStreamFinished(array $data): bool
167161
* finish_reason: 'stop'|'length'|'tool_calls'|'content_filter',
168162
* } $choice
169163
*/
170-
private function convertChoice(array $choice): Choice
164+
private function convertChoice(array $choice): ToolCallResult|TextResult
171165
{
172166
if ('tool_calls' === $choice['finish_reason']) {
173-
return new Choice(toolCalls: array_map([$this, 'convertToolCall'], $choice['message']['tool_calls']));
167+
return new ToolCallResult(...array_map([$this, 'convertToolCall'], $choice['message']['tool_calls']));
174168
}
175169

176170
if (\in_array($choice['finish_reason'], ['stop', 'length'], true)) {
177-
return new Choice($choice['message']['content']);
171+
return new TextResult($choice['message']['content']);
178172
}
179173

180174
throw new RuntimeException(\sprintf('Unsupported finish reason "%s".', $choice['finish_reason']));

src/platform/src/Result/Choice.php

Lines changed: 0 additions & 50 deletions
This file was deleted.

src/platform/src/Result/ChoiceResult.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,24 @@
1919
final class ChoiceResult extends BaseResult
2020
{
2121
/**
22-
* @var Choice[]
22+
* @var ResultInterface[]
2323
*/
24-
private readonly array $choices;
24+
private readonly array $results;
2525

26-
public function __construct(Choice ...$choices)
26+
public function __construct(ResultInterface ...$results)
2727
{
28-
if ([] === $choices) {
29-
throw new InvalidArgumentException('Result must have at least one choice.');
28+
if (1 >= \count($results)) {
29+
throw new InvalidArgumentException('A choice result must contain at least two results.');
3030
}
3131

32-
$this->choices = $choices;
32+
$this->results = $results;
3333
}
3434

3535
/**
36-
* @return Choice[]
36+
* @return ResultInterface[]
3737
*/
3838
public function getContent(): array
3939
{
40-
return $this->choices;
40+
return $this->results;
4141
}
4242
}

src/platform/tests/Bridge/OpenAI/GPT/ResultConverterTest.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
use Symfony\AI\Platform\Bridge\OpenAI\GPT\ResultConverter;
1919
use Symfony\AI\Platform\Exception\ContentFilterException;
2020
use Symfony\AI\Platform\Exception\RuntimeException;
21-
use Symfony\AI\Platform\Result\Choice;
2221
use Symfony\AI\Platform\Result\ChoiceResult;
2322
use Symfony\AI\Platform\Result\RawHttpResult;
2423
use Symfony\AI\Platform\Result\TextResult;
@@ -29,7 +28,6 @@
2928

3029
#[CoversClass(ResultConverter::class)]
3130
#[Small]
32-
#[UsesClass(Choice::class)]
3331
#[UsesClass(ChoiceResult::class)]
3432
#[UsesClass(TextResult::class)]
3533
#[UsesClass(ToolCall::class)]

src/platform/tests/Result/ChoiceResultTest.php

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,34 +13,30 @@
1313

1414
use PHPUnit\Framework\Attributes\CoversClass;
1515
use PHPUnit\Framework\Attributes\Small;
16-
use PHPUnit\Framework\Attributes\UsesClass;
1716
use PHPUnit\Framework\TestCase;
1817
use Symfony\AI\Platform\Exception\InvalidArgumentException;
19-
use Symfony\AI\Platform\Result\Choice;
2018
use Symfony\AI\Platform\Result\ChoiceResult;
19+
use Symfony\AI\Platform\Result\TextResult;
2120

2221
#[CoversClass(ChoiceResult::class)]
23-
#[UsesClass(Choice::class)]
2422
#[Small]
2523
final class ChoiceResultTest extends TestCase
2624
{
2725
public function testChoiceResultCreation(): void
2826
{
29-
$choice1 = new Choice('choice1');
30-
$choice2 = new Choice(null);
31-
$choice3 = new Choice('choice3');
32-
$result = new ChoiceResult($choice1, $choice2, $choice3);
27+
$choice1 = new TextResult('choice1');
28+
$choice3 = new TextResult('choice2');
29+
$result = new ChoiceResult($choice1, $choice3);
3330

34-
$this->assertCount(3, $result->getContent());
31+
$this->assertCount(2, $result->getContent());
3532
$this->assertSame('choice1', $result->getContent()[0]->getContent());
36-
$this->assertNull($result->getContent()[1]->getContent());
37-
$this->assertSame('choice3', $result->getContent()[2]->getContent());
33+
$this->assertSame('choice2', $result->getContent()[1]->getContent());
3834
}
3935

4036
public function testChoiceResultWithNoChoices(): void
4137
{
4238
self::expectException(InvalidArgumentException::class);
43-
self::expectExceptionMessage('Result must have at least one choice.');
39+
self::expectExceptionMessage('A choice result must contain at least two results.');
4440

4541
new ChoiceResult();
4642
}

src/platform/tests/Result/ChoiceTest.php

Lines changed: 0 additions & 61 deletions
This file was deleted.

0 commit comments

Comments
 (0)