Skip to content
Merged
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
18 changes: 13 additions & 5 deletions src/Results/DTO/GenerativeAiResult.php
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ public function hasMultipleCandidates(): bool
/**
* Converts the first candidate to text.
*
* Only text from the content channel is considered. Text within model thought or reasoning is ignored.
*
* @since 0.1.0
*
* @return string The text content.
Expand All @@ -210,8 +212,9 @@ public function toText(): string
{
$message = $this->candidates[0]->getMessage();
foreach ($message->getParts() as $part) {
$channel = $part->getChannel();
$text = $part->getText();
if ($text !== null) {
if ($channel->isContent() && $text !== null) {
return $text;
}
}
Expand All @@ -222,6 +225,8 @@ public function toText(): string
/**
* Converts the first candidate to a file.
*
* Only files from the content channel are considered. Files within model thought or reasoning are ignored.
*
* @since 0.1.0
*
* @return File The file.
Expand All @@ -231,8 +236,9 @@ public function toFile(): File
{
$message = $this->candidates[0]->getMessage();
foreach ($message->getParts() as $part) {
$channel = $part->getChannel();
$file = $part->getFile();
if ($file !== null) {
if ($channel->isContent() && $file !== null) {
return $file;
}
}
Expand Down Expand Up @@ -316,7 +322,7 @@ public function toMessage(): Message
}

/**
* Converts all candidates to text array.
* Converts all candidates to text.
*
* @since 0.1.0
*
Expand All @@ -328,8 +334,9 @@ public function toTexts(): array
foreach ($this->candidates as $candidate) {
$message = $candidate->getMessage();
foreach ($message->getParts() as $part) {
$channel = $part->getChannel();
$text = $part->getText();
if ($text !== null) {
if ($channel->isContent() && $text !== null) {
$texts[] = $text;
break;
}
Expand All @@ -351,8 +358,9 @@ public function toFiles(): array
foreach ($this->candidates as $candidate) {
$message = $candidate->getMessage();
foreach ($message->getParts() as $part) {
$channel = $part->getChannel();
$file = $part->getFile();
if ($file !== null) {
if ($channel->isContent() && $file !== null) {
$files[] = $file;
break;
}
Expand Down
75 changes: 75 additions & 0 deletions tests/unit/Results/DTO/GenerativeAiResultTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use WordPress\AiClient\Messages\DTO\Message;
use WordPress\AiClient\Messages\DTO\MessagePart;
use WordPress\AiClient\Messages\DTO\ModelMessage;
use WordPress\AiClient\Messages\Enums\MessagePartChannelEnum;
use WordPress\AiClient\Messages\Enums\MessagePartTypeEnum;
use WordPress\AiClient\Messages\Enums\MessageRoleEnum;
use WordPress\AiClient\Providers\DTO\ProviderMetadata;
Expand Down Expand Up @@ -189,6 +190,36 @@ public function testToText(): void
$this->assertEquals($text, $result->toText());
}

/**
* Tests toText method with both reasoning_content and content channels.
*
* @return void
*/
public function testToTextWithReasoningContent(): void
{
$reasoningContent = 'Let me think about this step by step...';
$actualContent = 'This is the final answer.';

$message = new ModelMessage([
new MessagePart($reasoningContent, MessagePartChannelEnum::thought()),
new MessagePart($actualContent, MessagePartChannelEnum::content())
]);
$candidate = new Candidate($message, FinishReasonEnum::stop());
$tokenUsage = new TokenUsage(10, 8, 18);

$result = new GenerativeAiResult(
'result_with_reasoning',
[$candidate],
$tokenUsage,
$this->createTestProviderMetadata(),
$this->createTestModelMetadata()
);

// toText() should only return content from the 'content' channel, not 'thought' channel
$this->assertEquals($actualContent, $result->toText());
$this->assertNotEquals($reasoningContent, $result->toText());
}

/**
* Tests toText throws exception when no text content.
*
Expand Down Expand Up @@ -430,6 +461,50 @@ public function testToTextsWithMultipleCandidates(): void
$this->assertEquals($texts, $result->toTexts());
}

/**
* Tests toTexts method with both reasoning_content and content channels.
*
* @return void
*/
public function testToTextsWithReasoningContent(): void
{
$reasoningContent1 = 'Let me think about the first question...';
$actualContent1 = 'This is the first answer.';
$reasoningContent2 = 'Now for the second question...';
$actualContent2 = 'This is the second answer.';

$message1 = new ModelMessage([
new MessagePart($reasoningContent1, MessagePartChannelEnum::thought()),
new MessagePart($actualContent1, MessagePartChannelEnum::content())
]);
$message2 = new ModelMessage([
new MessagePart($reasoningContent2, MessagePartChannelEnum::thought()),
new MessagePart($actualContent2, MessagePartChannelEnum::content())
]);

$candidates = [
new Candidate($message1, FinishReasonEnum::stop()),
new Candidate($message2, FinishReasonEnum::stop())
];
$tokenUsage = new TokenUsage(20, 15, 35);

$result = new GenerativeAiResult(
'result_texts_with_reasoning',
$candidates,
$tokenUsage,
$this->createTestProviderMetadata(),
$this->createTestModelMetadata()
);

// toTexts() should only return content from the 'content' channel, not 'thought' channel
$expectedTexts = [$actualContent1, $actualContent2];
$this->assertEquals($expectedTexts, $result->toTexts());

// Verify reasoning content is not included
$this->assertNotContains($reasoningContent1, $result->toTexts());
$this->assertNotContains($reasoningContent2, $result->toTexts());
}

/**
* Tests toFiles method with multiple candidates.
*
Expand Down