Skip to content

Commit 62ff5df

Browse files
committed
Add a unit test for \LLM\Agents\OpenAI\Client\Parsers\ChatResponseParser::parse
1 parent fec9784 commit 62ff5df

File tree

2 files changed

+75
-1
lines changed

2 files changed

+75
-1
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
},
2121
"autoload-dev": {
2222
"psr-4": {
23-
"LLM\\Tests\\": "tests/src"
23+
"LLM\\Tests\\": "tests/"
2424
}
2525
},
2626
"config": {
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace LLM\Tests\Parsers;
6+
7+
use ArrayIterator;
8+
use LLM\Agents\OpenAI\Client\Parsers\ChatResponseParser;
9+
use OpenAI\Contracts\ResponseStreamContract;
10+
use PHPUnit\Framework\TestCase;
11+
use Psr\EventDispatcher\EventDispatcherInterface;
12+
use ReflectionClass;
13+
use Traversable;
14+
use OpenAI\Responses\Chat\CreateStreamedResponseDelta;
15+
use OpenAI\Responses\Chat\CreateStreamedResponseChoice;
16+
use OpenAI\Responses\Chat\CreateStreamedResponse;
17+
18+
class ChatResponseParserTest extends TestCase
19+
{
20+
private function makeDelta(?string $content): object
21+
{
22+
$toolCalls = [];
23+
$deltaClass = new ReflectionClass(CreateStreamedResponseDelta::class);
24+
$delta = $deltaClass->newInstanceWithoutConstructor();
25+
$deltaClass->getProperty('content')->setValue($delta, $content);
26+
$deltaClass->getProperty('role')->setValue($delta, null);
27+
$deltaClass->getProperty('toolCalls')->setValue($delta, $toolCalls);
28+
return $delta;
29+
}
30+
31+
private function makeChoice(object $delta, $finishReason = null): object
32+
{
33+
$choiceClass = new ReflectionClass(CreateStreamedResponseChoice::class);
34+
$choice = $choiceClass->newInstanceWithoutConstructor();
35+
$choiceClass->getProperty('delta')->setValue($choice, $delta);
36+
$choiceClass->getProperty('finishReason')->setValue($choice, $finishReason);
37+
return $choice;
38+
}
39+
40+
private function makeChunk(array $choices): object
41+
{
42+
$chunkClass = new ReflectionClass(CreateStreamedResponse::class);
43+
$chunk = $chunkClass->newInstanceWithoutConstructor();
44+
$chunkClass->getProperty('choices')->setValue($chunk, $choices);
45+
return $chunk;
46+
}
47+
48+
private function makeStream(array $chunks): object
49+
{
50+
return new class($chunks) implements ResponseStreamContract {
51+
private array $chunks;
52+
public function __construct(array $chunks) { $this->chunks = $chunks; }
53+
public function getIterator(): Traversable { return new ArrayIterator($this->chunks); }
54+
};
55+
}
56+
57+
public function test_parse_with_mock_stream_contract_returns_expected_response(): void
58+
{
59+
$chunk1 = $this->makeChunk([
60+
$this->makeChoice($this->makeDelta('Hello, world!'))
61+
]);
62+
$chunk2 = $this->makeChunk([
63+
$this->makeChoice($this->makeDelta(null), 'stop')
64+
]);
65+
$mockStream = $this->makeStream([$chunk1, $chunk2]);
66+
67+
$mockDispatcher = $this->createMock(EventDispatcherInterface::class);
68+
$parser = new ChatResponseParser($mockDispatcher);
69+
$result = $parser->parse($mockStream);
70+
71+
$this->assertSame('Hello, world!', $result->content);
72+
$this->assertSame('stop', $result->finishReason);
73+
}
74+
}

0 commit comments

Comments
 (0)