Skip to content
This repository was archived by the owner on Jul 16, 2025. It is now read-only.

Commit 0fd4677

Browse files
committed
Ability to add the tool definitions to the system prompt
1 parent 56673df commit 0fd4677

File tree

2 files changed

+123
-1
lines changed

2 files changed

+123
-1
lines changed

src/Chain/InputProcessor/SystemPromptInputProcessor.php

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
use PhpLlm\LlmChain\Chain\Input;
88
use PhpLlm\LlmChain\Chain\InputProcessor;
9+
use PhpLlm\LlmChain\Chain\ToolBox\Metadata;
10+
use PhpLlm\LlmChain\Chain\ToolBox\ToolBoxInterface;
911
use PhpLlm\LlmChain\Model\Message\Message;
1012
use Psr\Log\LoggerInterface;
1113
use Psr\Log\NullLogger;
@@ -14,8 +16,13 @@
1416
{
1517
public function __construct(
1618
private string $systemPrompt,
19+
private ?ToolBoxInterface $toolBox = null,
20+
private bool $includeToolDefinitions = false,
1721
private LoggerInterface $logger = new NullLogger(),
1822
) {
23+
if ($includeToolDefinitions && null === $toolBox) {
24+
throw new \InvalidArgumentException('Tool definitions cannot be included without a ToolBox.');
25+
}
1926
}
2027

2128
public function processInput(Input $input): void
@@ -28,6 +35,26 @@ public function processInput(Input $input): void
2835
return;
2936
}
3037

31-
$input->messages = $messages->prepend(Message::forSystem($this->systemPrompt));
38+
$message = $this->systemPrompt;
39+
40+
if ($this->includeToolDefinitions) {
41+
$tools = implode(PHP_EOL.PHP_EOL, array_map(
42+
fn (Metadata $tool) => <<<TOOL
43+
## {$tool->name}
44+
{$tool->description}
45+
TOOL,
46+
$this->toolBox->getMap()
47+
));
48+
49+
$message = <<<PROMPT
50+
{$this->systemPrompt}
51+
52+
# Available tools
53+
54+
{$tools}
55+
PROMPT;
56+
}
57+
58+
$input->messages = $messages->prepend(Message::forSystem($message));
3259
}
3360
}

tests/Chain/InputProcessor/SystemPromptInputProcessorTest.php

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,16 @@
77
use PhpLlm\LlmChain\Bridge\OpenAI\GPT;
88
use PhpLlm\LlmChain\Chain\Input;
99
use PhpLlm\LlmChain\Chain\InputProcessor\SystemPromptInputProcessor;
10+
use PhpLlm\LlmChain\Chain\ToolBox\Metadata;
11+
use PhpLlm\LlmChain\Chain\ToolBox\ToolBoxInterface;
1012
use PhpLlm\LlmChain\Model\Message\Content\Text;
1113
use PhpLlm\LlmChain\Model\Message\Message;
1214
use PhpLlm\LlmChain\Model\Message\MessageBag;
1315
use PhpLlm\LlmChain\Model\Message\SystemMessage;
1416
use PhpLlm\LlmChain\Model\Message\UserMessage;
17+
use PhpLlm\LlmChain\Model\Response\ToolCall;
18+
use PhpLlm\LlmChain\Tests\Fixture\Tool\ToolNoParams;
19+
use PhpLlm\LlmChain\Tests\Fixture\Tool\ToolRequiredParams;
1520
use PHPUnit\Framework\Attributes\CoversClass;
1621
use PHPUnit\Framework\Attributes\Small;
1722
use PHPUnit\Framework\Attributes\Test;
@@ -62,4 +67,94 @@ public function processInputDoesNotAddSystemMessageWhenOneExists(): void
6267
self::assertInstanceOf(UserMessage::class, $messages[1]);
6368
self::assertSame('This is already a system prompt', $messages[0]->content);
6469
}
70+
71+
#[Test]
72+
public function needsToolboxToBeAbleToIncludeToolDefinitions(): void
73+
{
74+
$this->expectException(\InvalidArgumentException::class);
75+
$this->expectExceptionMessage('Tool definitions cannot be included without a ToolBox.');
76+
77+
new SystemPromptInputProcessor(
78+
'This is a system prompt',
79+
toolBox: null,
80+
includeToolDefinitions: true,
81+
);
82+
}
83+
84+
#[Test]
85+
public function includeToolDefinitions(): void
86+
{
87+
$processor = new SystemPromptInputProcessor(
88+
'This is a system prompt',
89+
new class implements ToolBoxInterface {
90+
public function getMap(): array
91+
{
92+
return [
93+
new Metadata(ToolNoParams::class, 'tool_no_params', 'A tool without parameters', '__invoke', null),
94+
new Metadata(
95+
ToolRequiredParams::class,
96+
'tool_required_params',
97+
<<<DESCRIPTION
98+
A tool with required parameters
99+
or not
100+
DESCRIPTION,
101+
'bar',
102+
null
103+
),
104+
];
105+
}
106+
107+
public function execute(ToolCall $toolCall): mixed
108+
{
109+
return null;
110+
}
111+
},
112+
includeToolDefinitions: true,
113+
);
114+
115+
$input = new Input(new GPT(), new MessageBag(Message::ofUser('This is a user message')), []);
116+
$processor->processInput($input);
117+
118+
$messages = $input->messages->getMessages();
119+
self::assertCount(2, $messages);
120+
self::assertInstanceOf(SystemMessage::class, $messages[0]);
121+
self::assertInstanceOf(UserMessage::class, $messages[1]);
122+
self::assertSame(<<<PROMPT
123+
This is a system prompt
124+
125+
# Available tools
126+
127+
## tool_no_params
128+
A tool without parameters
129+
130+
## tool_required_params
131+
A tool with required parameters
132+
or not
133+
PROMPT, $messages[0]->content);
134+
}
135+
136+
private function createFaultyToolBox(\Closure $exceptionFactory): ToolBoxInterface
137+
{
138+
return new class($exceptionFactory) implements ToolBoxInterface {
139+
public function __construct(private readonly \Closure $exceptionFactory)
140+
{
141+
}
142+
143+
/**
144+
* @return Metadata[]
145+
*/
146+
public function getMap(): array
147+
{
148+
return [
149+
new Metadata(ToolNoParams::class, 'tool_no_params', 'A tool without parameters', '__invoke', null),
150+
new Metadata(ToolRequiredParams::class, 'tool_required_params', 'A tool with required parameters', 'bar', null),
151+
];
152+
}
153+
154+
public function execute(ToolCall $toolCall): mixed
155+
{
156+
throw ($this->exceptionFactory)($toolCall);
157+
}
158+
};
159+
}
65160
}

0 commit comments

Comments
 (0)