Skip to content

Commit bbabd9a

Browse files
authored
feat: add system prompt processor (#186)
1 parent 847ff84 commit bbabd9a

File tree

6 files changed

+127
-4
lines changed

6 files changed

+127
-4
lines changed

examples/chat-system-prompt.php

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
use PhpLlm\LlmChain\Bridge\OpenAI\GPT;
4+
use PhpLlm\LlmChain\Bridge\OpenAI\PlatformFactory;
5+
use PhpLlm\LlmChain\Chain;
6+
use PhpLlm\LlmChain\Chain\InputProcessor\SystemPromptInputProcessor;
7+
use PhpLlm\LlmChain\Model\Message\Message;
8+
use PhpLlm\LlmChain\Model\Message\MessageBag;
9+
use Symfony\Component\Dotenv\Dotenv;
10+
11+
require_once dirname(__DIR__).'/vendor/autoload.php';
12+
(new Dotenv())->loadEnv(dirname(__DIR__).'/.env');
13+
14+
if (empty($_ENV['OPENAI_API_KEY'])) {
15+
echo 'Please set the OPENAI_API_KEY environment variable.'.PHP_EOL;
16+
exit(1);
17+
}
18+
19+
$platform = PlatformFactory::create($_ENV['OPENAI_API_KEY']);
20+
$llm = new GPT(GPT::GPT_4O_MINI);
21+
22+
$processor = new SystemPromptInputProcessor('You are Yoda and write like he speaks. But short.');
23+
24+
$chain = new Chain($platform, $llm, [$processor]);
25+
$messages = new MessageBag(Message::ofUser('What is the meaning of life?'));
26+
$response = $chain->call($messages);
27+
28+
echo $response->getContent().PHP_EOL;

src/Chain/LlmOverrideInputProcessor.php renamed to src/Chain/InputProcessor/LlmOverrideInputProcessor.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
declare(strict_types=1);
44

5-
namespace PhpLlm\LlmChain\Chain;
5+
namespace PhpLlm\LlmChain\Chain\InputProcessor;
66

7+
use PhpLlm\LlmChain\Chain\Input;
8+
use PhpLlm\LlmChain\Chain\InputProcessor;
79
use PhpLlm\LlmChain\Exception\InvalidArgumentException;
810
use PhpLlm\LlmChain\Model\LanguageModel;
911

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpLlm\LlmChain\Chain\InputProcessor;
6+
7+
use PhpLlm\LlmChain\Chain\Input;
8+
use PhpLlm\LlmChain\Chain\InputProcessor;
9+
use PhpLlm\LlmChain\Model\Message\Message;
10+
use Psr\Log\LoggerInterface;
11+
use Psr\Log\NullLogger;
12+
13+
final readonly class SystemPromptInputProcessor implements InputProcessor
14+
{
15+
public function __construct(
16+
private string $systemPrompt,
17+
private LoggerInterface $logger = new NullLogger(),
18+
) {
19+
}
20+
21+
public function processInput(Input $input): void
22+
{
23+
$messages = $input->messages;
24+
25+
if (null !== $messages->getSystemMessage()) {
26+
$this->logger->debug('Skipping system prompt injection since MessageBag already contains a system message.');
27+
28+
return;
29+
}
30+
31+
$input->messages = $messages->prepend(Message::forSystem($this->systemPrompt));
32+
}
33+
}

src/Exception/ToolBoxException.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
final class ToolBoxException extends RuntimeException
1010
{
11-
public ?ToolCall $toolCall;
11+
public ?ToolCall $toolCall = null;
1212

1313
public static function notFoundForToolCall(ToolCall $toolCall): self
1414
{

tests/Chain/LlmOverrideInputProcessorTest.php renamed to tests/Chain/InputProcessor/LlmOverrideInputProcessorTest.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22

33
declare(strict_types=1);
44

5-
namespace PhpLlm\LlmChain\Tests\Chain;
5+
namespace PhpLlm\LlmChain\Tests\Chain\InputProcessor;
66

77
use PhpLlm\LlmChain\Bridge\Anthropic\Claude;
88
use PhpLlm\LlmChain\Bridge\OpenAI\Embeddings;
99
use PhpLlm\LlmChain\Bridge\OpenAI\GPT;
1010
use PhpLlm\LlmChain\Chain\Input;
11-
use PhpLlm\LlmChain\Chain\LlmOverrideInputProcessor;
11+
use PhpLlm\LlmChain\Chain\InputProcessor\LlmOverrideInputProcessor;
1212
use PhpLlm\LlmChain\Exception\InvalidArgumentException;
1313
use PhpLlm\LlmChain\Model\Message\MessageBag;
1414
use PHPUnit\Framework\Attributes\CoversClass;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpLlm\LlmChain\Tests\Chain\InputProcessor;
6+
7+
use PhpLlm\LlmChain\Bridge\OpenAI\GPT;
8+
use PhpLlm\LlmChain\Chain\Input;
9+
use PhpLlm\LlmChain\Chain\InputProcessor\SystemPromptInputProcessor;
10+
use PhpLlm\LlmChain\Model\Message\Message;
11+
use PhpLlm\LlmChain\Model\Message\MessageBag;
12+
use PhpLlm\LlmChain\Model\Message\SystemMessage;
13+
use PhpLlm\LlmChain\Model\Message\UserMessage;
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(SystemPromptInputProcessor::class)]
21+
#[UsesClass(GPT::class)]
22+
#[UsesClass(Message::class)]
23+
#[UsesClass(MessageBag::class)]
24+
#[Small]
25+
final class SystemPromptInputProcessorTest extends TestCase
26+
{
27+
#[Test]
28+
public function processInputAddsSystemMessageWhenNoneExists(): void
29+
{
30+
$processor = new SystemPromptInputProcessor('This is a system prompt');
31+
32+
$input = new Input(new GPT(), new MessageBag(Message::ofUser('This is a user message')), []);
33+
$processor->processInput($input);
34+
35+
$messages = $input->messages->getMessages();
36+
self::assertCount(2, $messages);
37+
self::assertInstanceOf(SystemMessage::class, $messages[0]);
38+
self::assertInstanceOf(UserMessage::class, $messages[1]);
39+
self::assertSame('This is a system prompt', $messages[0]->content);
40+
}
41+
42+
#[Test]
43+
public function processInputDoesNotAddSystemMessageWhenOneExists(): void
44+
{
45+
$processor = new SystemPromptInputProcessor('This is a system prompt');
46+
47+
$messages = new MessageBag(
48+
Message::forSystem('This is already a system prompt'),
49+
Message::ofUser('This is a user message'),
50+
);
51+
$input = new Input(new GPT(), $messages, []);
52+
$processor->processInput($input);
53+
54+
$messages = $input->messages->getMessages();
55+
self::assertCount(2, $messages);
56+
self::assertInstanceOf(SystemMessage::class, $messages[0]);
57+
self::assertInstanceOf(UserMessage::class, $messages[1]);
58+
self::assertSame('This is already a system prompt', $messages[0]->content);
59+
}
60+
}

0 commit comments

Comments
 (0)