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
3 changes: 2 additions & 1 deletion src/Providers/AWS/MessageMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use function array_map;
use function array_merge;
use function array_filter;
use function array_values;

class MessageMapper implements MessageMapperInterface
{
Expand Down Expand Up @@ -90,7 +91,7 @@ protected function mapMessage(Message $message): array
*/
protected function mapBlocks(array $blocks): array
{
return array_filter(array_map($this->mapContentBlock(...), $blocks));
return array_values(array_filter(array_map($this->mapContentBlock(...), $blocks)));
}

protected function mapContentBlock(ContentBlockInterface $block): ?array
Expand Down
2 changes: 1 addition & 1 deletion src/Providers/Anthropic/MessageMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ protected function mapMessage(Message $message): array

protected function mapBlocks(array $blocks): array
{
return array_filter(array_map($this->mapSingleBlock(...), $blocks));
return array_values(array_filter(array_map($this->mapSingleBlock(...), $blocks)));
}

protected function mapSingleBlock(ContentBlockInterface $block): ?array
Expand Down
3 changes: 2 additions & 1 deletion src/Providers/Gemini/MessageMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

use function array_filter;
use function array_map;
use function array_values;

class MessageMapper implements MessageMapperInterface
{
Expand Down Expand Up @@ -59,7 +60,7 @@ protected function mapMessage(Message $message): array

protected function mapBlocks(array $blocks): array
{
return array_filter(array_map($this->mapContentBlock(...), $blocks));
return array_values(array_filter(array_map($this->mapContentBlock(...), $blocks)));
}

protected function mapContentBlock(ContentBlock $block): ?array
Expand Down
3 changes: 2 additions & 1 deletion src/Providers/Mistral/MessageMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

use function array_filter;
use function array_map;
use function array_values;
use function json_encode;

class MessageMapper implements MessageMapperInterface
Expand Down Expand Up @@ -58,7 +59,7 @@ protected function mapMessage(Message $message): void

protected function mapBlocks(array $blocks): array
{
return array_filter(array_map($this->mapContentBlock(...), $blocks));
return array_values(array_filter(array_map($this->mapContentBlock(...), $blocks)));
}

protected function mapContentBlock(ContentBlockInterface $block): ?array
Expand Down
3 changes: 2 additions & 1 deletion src/Providers/OpenAI/MessageMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use function array_is_list;
use function array_map;
use function array_merge;
use function array_values;
use function json_encode;

class MessageMapper implements MessageMapperInterface
Expand Down Expand Up @@ -64,7 +65,7 @@ protected function mapMessage(Message $message): array

protected function mapBlocks(array $blocks): array
{
return array_filter(array_map($this->mapContentBlock(...), $blocks));
return array_values(array_filter(array_map($this->mapContentBlock(...), $blocks)));
}

protected function mapContentBlock(ContentBlockInterface $block): ?array
Expand Down
5 changes: 3 additions & 2 deletions src/Providers/OpenAI/Responses/MessageMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use function array_filter;
use function array_map;
use function array_merge;
use function array_values;
use function json_encode;

class MessageMapper implements MessageMapperInterface
Expand Down Expand Up @@ -62,10 +63,10 @@ protected function mapMessage(Message $message): void
*/
protected function mapBlocks(array $blocks, bool $isUser): array
{
return array_filter(array_map(
return array_values(array_filter(array_map(
fn (ContentBlockInterface $item): ?array => $this->mapContentBlock($item, $isUser),
$blocks
));
)));
}

protected function mapContentBlock(ContentBlockInterface $block, bool $isUser): ?array
Expand Down
123 changes: 123 additions & 0 deletions tests/Providers/MessageMapperReindexingTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<?php

declare(strict_types=1);

namespace NeuronAI\Tests\Providers;

use NeuronAI\Chat\Enums\SourceType;
use NeuronAI\Chat\Messages\AssistantMessage;
use NeuronAI\Chat\Messages\ContentBlocks\FileContent;
use NeuronAI\Chat\Messages\ContentBlocks\ImageContent;
use NeuronAI\Chat\Messages\ContentBlocks\ReasoningContent;
use NeuronAI\Chat\Messages\ContentBlocks\TextContent;
use NeuronAI\Providers\AWS\MessageMapper as AWSMessageMapper;
use NeuronAI\Providers\Anthropic\MessageMapper as AnthropicMessageMapper;
use NeuronAI\Providers\Gemini\MessageMapper as GeminiMessageMapper;
use NeuronAI\Providers\MessageMapperInterface;
use NeuronAI\Providers\Mistral\MessageMapper as MistralMessageMapper;
use NeuronAI\Providers\OpenAI\MessageMapper as OpenAIMessageMapper;
use NeuronAI\Providers\OpenAI\Responses\MessageMapper as OpenAIResponsesMessageMapper;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

use function array_is_list;
use function json_decode;
use function json_encode;

class MessageMapperReindexingTest extends TestCase
{
/**
* @param array<int, array<string, mixed>> $expectedBlocks
*/
#[DataProvider('mapperProvider')]
public function test_filtered_blocks_serialize_as_lists(
MessageMapperInterface $mapper,
AssistantMessage $message,
string $blocksKey,
array $expectedBlocks,
): void {
$payload = json_decode(json_encode($mapper->map([$message]), JSON_THROW_ON_ERROR), true, 512, JSON_THROW_ON_ERROR);

$this->assertIsArray($payload[0][$blocksKey]);
$this->assertTrue(array_is_list($payload[0][$blocksKey]));
$this->assertSame($expectedBlocks, $payload[0][$blocksKey]);
}

/**
* @return iterable<string, array{0: MessageMapperInterface, 1: AssistantMessage, 2: string, 3: array<int, array<string, mixed>>}>
*/
public static function mapperProvider(): iterable
{
yield 'openai responses reasoning then text' => [
new OpenAIResponsesMessageMapper(),
new AssistantMessage([
new ReasoningContent('Thinking'),
new TextContent('Hello'),
]),
'content',
[
['type' => 'output_text', 'text' => 'Hello'],
],
];

yield 'openai reasoning then text' => [
new OpenAIMessageMapper(),
new AssistantMessage([
new ReasoningContent('Thinking'),
new TextContent('Hello'),
]),
'content',
[
['type' => 'text', 'text' => 'Hello'],
],
];

yield 'anthropic unsupported image id then text' => [
new AnthropicMessageMapper(),
new AssistantMessage([
new ImageContent('file_123', SourceType::ID),
new TextContent('Hello'),
]),
'content',
[
['type' => 'text', 'text' => 'Hello'],
],
];

yield 'mistral unsupported base64 file then text' => [
new MistralMessageMapper(),
new AssistantMessage([
new FileContent('ZmFrZQ==', SourceType::BASE64, 'application/pdf', 'test.pdf'),
new TextContent('Hello'),
]),
'content',
[
['type' => 'text', 'text' => 'Hello'],
],
];

yield 'gemini unsupported image id then text' => [
new GeminiMessageMapper(),
new AssistantMessage([
new ImageContent('file_123', SourceType::ID, 'image/png'),
new TextContent('Hello'),
]),
'parts',
[
['text' => 'Hello'],
],
];

yield 'aws unsupported image then text' => [
new AWSMessageMapper(),
new AssistantMessage([
new ImageContent('https://example.com/image.png', SourceType::URL),
new TextContent('Hello'),
]),
'content',
[
['text' => 'Hello'],
],
];
}
}
Loading