Skip to content

Commit

Permalink
feat: empower input processor to change LLM and MessageBag
Browse files Browse the repository at this point in the history
  • Loading branch information
chr-hertel committed Jan 3, 2025
1 parent bec9272 commit aea0b24
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 15 deletions.
19 changes: 6 additions & 13 deletions src/Chain.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,25 +47,18 @@ public function __construct(
*/
public function call(MessageBagInterface $messages, array $options = []): ResponseInterface
{
$llm = $this->llm;

if (array_key_exists('llm', $options)) {
if (!$options['llm'] instanceof LanguageModel) {
throw new InvalidArgumentException(sprintf('Option "llm" must be an instance of %s.', LanguageModel::class));
}

$llm = $options['llm'];
unset($options['llm']);
}

$input = new Input($llm, $messages, $options);
$input = new Input($this->llm, $messages, $options);
array_map(fn (InputProcessor $processor) => $processor->processInput($input), $this->inputProcessor);

$llm = $input->llm;
$messages = $input->messages;
$options = $input->getOptions();

if ($messages->containsImage() && !$llm->supportsImageInput()) {
throw MissingModelSupport::forImageInput($llm::class);
}

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

if ($response instanceof AsyncResponse) {
$response = $response->unwrap();
Expand Down
4 changes: 2 additions & 2 deletions src/Chain/Input.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ final class Input
* @param array<string, mixed> $options
*/
public function __construct(
public readonly LanguageModel $llm,
public readonly MessageBagInterface $messages,
public LanguageModel $llm,
public MessageBagInterface $messages,
private array $options,
) {
}
Expand Down
26 changes: 26 additions & 0 deletions src/Chain/LlmOverrideInputProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace PhpLlm\LlmChain\Chain;

use PhpLlm\LlmChain\Exception\InvalidArgumentException;
use PhpLlm\LlmChain\Model\LanguageModel;

final class LlmOverrideInputProcessor implements InputProcessor
{
public function processInput(Input $input): void
{
$options = $input->getOptions();

if (!array_key_exists('llm', $options)) {
return;
}

if (!$options['llm'] instanceof LanguageModel) {
throw new InvalidArgumentException(sprintf('Option "llm" must be an instance of %s.', LanguageModel::class));
}

$input->llm = $options['llm'];
}
}
66 changes: 66 additions & 0 deletions tests/Chain/LlmOverrideInputProcessorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

declare(strict_types=1);

namespace PhpLlm\LlmChain\Tests\Chain;

use PhpLlm\LlmChain\Bridge\Anthropic\Claude;
use PhpLlm\LlmChain\Bridge\OpenAI\Embeddings;
use PhpLlm\LlmChain\Bridge\OpenAI\GPT;
use PhpLlm\LlmChain\Chain\Input;
use PhpLlm\LlmChain\Chain\LlmOverrideInputProcessor;
use PhpLlm\LlmChain\Exception\InvalidArgumentException;
use PhpLlm\LlmChain\Model\Message\MessageBag;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Small;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\TestCase;

#[CoversClass(LlmOverrideInputProcessor::class)]
#[UsesClass(GPT::class)]
#[UsesClass(Claude::class)]
#[UsesClass(Input::class)]
#[UsesClass(MessageBag::class)]
#[Small]
final class LlmOverrideInputProcessorTest extends TestCase
{
#[Test]
public function processInputWithValidLlmOption(): void
{
$gpt = new GPT();
$claude = new Claude();
$input = new Input($gpt, new MessageBag(), ['llm' => $claude]);

$processor = new LlmOverrideInputProcessor();
$processor->processInput($input);

self::assertSame($claude, $input->llm);
}

#[Test]
public function processInputWithoutLlmOption(): void
{
$gpt = new GPT();
$input = new Input($gpt, new MessageBag(), []);

$processor = new LlmOverrideInputProcessor();
$processor->processInput($input);

self::assertSame($gpt, $input->llm);
}

#[Test]
public function processInputWithInvalidLlmOption(): void
{
self::expectException(InvalidArgumentException::class);
self::expectExceptionMessage('Option "llm" must be an instance of PhpLlm\LlmChain\Model\LanguageModel.');

$gpt = new GPT();
$model = new Embeddings();
$input = new Input($gpt, new MessageBag(), ['llm' => $model]);

$processor = new LlmOverrideInputProcessor();
$processor->processInput($input);
}
}

0 comments on commit aea0b24

Please sign in to comment.