Skip to content

Commit 847ff84

Browse files
authored
feat: empower input processor to change LLM and MessageBag (#184)
1 parent eb8e36a commit 847ff84

File tree

4 files changed

+100
-15
lines changed

4 files changed

+100
-15
lines changed

src/Chain.php

+6-13
Original file line numberDiff line numberDiff line change
@@ -47,25 +47,18 @@ public function __construct(
4747
*/
4848
public function call(MessageBagInterface $messages, array $options = []): ResponseInterface
4949
{
50-
$llm = $this->llm;
51-
52-
if (array_key_exists('llm', $options)) {
53-
if (!$options['llm'] instanceof LanguageModel) {
54-
throw new InvalidArgumentException(sprintf('Option "llm" must be an instance of %s.', LanguageModel::class));
55-
}
56-
57-
$llm = $options['llm'];
58-
unset($options['llm']);
59-
}
60-
61-
$input = new Input($llm, $messages, $options);
50+
$input = new Input($this->llm, $messages, $options);
6251
array_map(fn (InputProcessor $processor) => $processor->processInput($input), $this->inputProcessor);
6352

53+
$llm = $input->llm;
54+
$messages = $input->messages;
55+
$options = $input->getOptions();
56+
6457
if ($messages->containsImage() && !$llm->supportsImageInput()) {
6558
throw MissingModelSupport::forImageInput($llm::class);
6659
}
6760

68-
$response = $this->platform->request($llm, $messages, $options = $input->getOptions());
61+
$response = $this->platform->request($llm, $messages, $options);
6962

7063
if ($response instanceof AsyncResponse) {
7164
$response = $response->unwrap();

src/Chain/Input.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ final class Input
1313
* @param array<string, mixed> $options
1414
*/
1515
public function __construct(
16-
public readonly LanguageModel $llm,
17-
public readonly MessageBagInterface $messages,
16+
public LanguageModel $llm,
17+
public MessageBagInterface $messages,
1818
private array $options,
1919
) {
2020
}
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpLlm\LlmChain\Chain;
6+
7+
use PhpLlm\LlmChain\Exception\InvalidArgumentException;
8+
use PhpLlm\LlmChain\Model\LanguageModel;
9+
10+
final class LlmOverrideInputProcessor implements InputProcessor
11+
{
12+
public function processInput(Input $input): void
13+
{
14+
$options = $input->getOptions();
15+
16+
if (!array_key_exists('llm', $options)) {
17+
return;
18+
}
19+
20+
if (!$options['llm'] instanceof LanguageModel) {
21+
throw new InvalidArgumentException(sprintf('Option "llm" must be an instance of %s.', LanguageModel::class));
22+
}
23+
24+
$input->llm = $options['llm'];
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpLlm\LlmChain\Tests\Chain;
6+
7+
use PhpLlm\LlmChain\Bridge\Anthropic\Claude;
8+
use PhpLlm\LlmChain\Bridge\OpenAI\Embeddings;
9+
use PhpLlm\LlmChain\Bridge\OpenAI\GPT;
10+
use PhpLlm\LlmChain\Chain\Input;
11+
use PhpLlm\LlmChain\Chain\LlmOverrideInputProcessor;
12+
use PhpLlm\LlmChain\Exception\InvalidArgumentException;
13+
use PhpLlm\LlmChain\Model\Message\MessageBag;
14+
use PHPUnit\Framework\Attributes\CoversClass;
15+
use PHPUnit\Framework\Attributes\Small;
16+
use PHPUnit\Framework\Attributes\Test;
17+
use PHPUnit\Framework\Attributes\UsesClass;
18+
use PHPUnit\Framework\TestCase;
19+
20+
#[CoversClass(LlmOverrideInputProcessor::class)]
21+
#[UsesClass(GPT::class)]
22+
#[UsesClass(Claude::class)]
23+
#[UsesClass(Input::class)]
24+
#[UsesClass(MessageBag::class)]
25+
#[Small]
26+
final class LlmOverrideInputProcessorTest extends TestCase
27+
{
28+
#[Test]
29+
public function processInputWithValidLlmOption(): void
30+
{
31+
$gpt = new GPT();
32+
$claude = new Claude();
33+
$input = new Input($gpt, new MessageBag(), ['llm' => $claude]);
34+
35+
$processor = new LlmOverrideInputProcessor();
36+
$processor->processInput($input);
37+
38+
self::assertSame($claude, $input->llm);
39+
}
40+
41+
#[Test]
42+
public function processInputWithoutLlmOption(): void
43+
{
44+
$gpt = new GPT();
45+
$input = new Input($gpt, new MessageBag(), []);
46+
47+
$processor = new LlmOverrideInputProcessor();
48+
$processor->processInput($input);
49+
50+
self::assertSame($gpt, $input->llm);
51+
}
52+
53+
#[Test]
54+
public function processInputWithInvalidLlmOption(): void
55+
{
56+
self::expectException(InvalidArgumentException::class);
57+
self::expectExceptionMessage('Option "llm" must be an instance of PhpLlm\LlmChain\Model\LanguageModel.');
58+
59+
$gpt = new GPT();
60+
$model = new Embeddings();
61+
$input = new Input($gpt, new MessageBag(), ['llm' => $model]);
62+
63+
$processor = new LlmOverrideInputProcessor();
64+
$processor->processInput($input);
65+
}
66+
}

0 commit comments

Comments
 (0)