Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/code-quality.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
- name: Configure environment
run: |
echo COLUMNS=120 >> $GITHUB_ENV
echo COMPOSER_UP='composer update --no-progress --no-interaction --ansi --ignore-platform-req=ext-mongodb' >> $GITHUB_ENV
echo COMPOSER_UP='composer update --no-progress --no-interaction --no-scripts --ansi --ignore-platform-req=ext-mongodb' >> $GITHUB_ENV
echo PHPSTAN='vendor/bin/phpstan' >> $GITHUB_ENV

PACKAGES=$(find src/ -mindepth 2 -type f -name composer.json -not -path "*/vendor/*" -printf '%h\n' | sed 's/^src\///' | grep -Ev "examples" | sort | tr '\n' ' ')
Expand Down
5 changes: 3 additions & 2 deletions demo/config/packages/ai.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ ai:
options:
temperature: 0.5
prompt:
text: 'Please answer the users question based on Wikipedia and provide a link to the article.'
text: 'Please answer the users question based on Wikipedia, only use information provided in the messages.'
include_tools: true
tools:
- 'Symfony\AI\Agent\Toolbox\Tool\Wikipedia'
include_sources: true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only thing is, that this should be under the tools key, or not?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, thought the same, but wasn't able to find think of a good target structure. Should also include the fault_toletant_toolbox.
Buuuuut, i like the direct tools array. Maybe an additional tool_settings or just settings node?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets keep it for now, can be optimized later. Ready to merge from my side

audio:
model: 'gpt-4o-mini?temperature=1.0'
prompt: 'You are a friendly chatbot that likes to have a conversation with users and asks them some questions.'
Expand Down Expand Up @@ -89,7 +90,7 @@ services:

Symfony\AI\Store\Document\Loader\RssFeedLoader: ~
Symfony\AI\Store\Document\Transformer\TextTrimTransformer: ~

app.filter.week_of_symfony:
class: 'Symfony\AI\Store\Document\Filter\TextContainsFilter'
arguments:
Expand Down
4 changes: 3 additions & 1 deletion demo/src/Wikipedia/Chat.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ public function submitMessage(string $message): void

\assert($result instanceof TextResult);

$messages->add(Message::ofAssistant($result->getContent()));
$response = Message::ofAssistant($result->getContent());
$response->getMetadata()->add('sources', $result->getMetadata()->get('sources', []));
$messages->add($response);

$this->saveMessages($messages);
}
Expand Down
16 changes: 13 additions & 3 deletions demo/templates/components/wikipedia.html.twig
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{% import "_message.html.twig" as message %}
{% import "_message.html.twig" as msg %}

<div class="card mx-auto shadow-lg" {{ attributes.defaults(stimulus_controller('wikipedia')) }}>
<div class="card-header p-2">
Expand All @@ -9,6 +9,16 @@
<div id="chat-body" class="card-body p-4 overflow-auto">
{% for message in this.messages %}
{% include '_message.html.twig' with { message, latest: loop.last } %}
{% if message.metadata.has('sources') %}
<div class="ms-5">
<h6>Sources:</h6>
<ol>
{% for source in message.metadata.get('sources') %}
<li><a class="text-black-50" href="{{ source.reference }}" target="_blank" rel="noopener">{{ source.name }}</a></li>
{% endfor %}
</ol>
</div>
{% endif %}
{% else %}
<div id="welcome" class="text-center mt-5 py-5 bg-white rounded-5 shadow-sm w-75 mx-auto">
{{ ux_icon('mdi:wikipedia', { height: '200px', width: '200px' }) }}
Expand All @@ -17,8 +27,8 @@
</div>
{% endfor %}
<div id="loading-message" class="d-none">
{{ message.user([{text:''}]) }}
{{ message.bot('The Wikipedia Bot is doing some research ...', true) }}
{{ msg.user([{text:''}]) }}
{{ msg.bot('The Wikipedia Bot is doing some research ...', true) }}
</div>
</div>
<div class="card-footer p-2">
Expand Down
4 changes: 2 additions & 2 deletions docs/components/agent.rst
Original file line number Diff line number Diff line change
Expand Up @@ -318,13 +318,13 @@ Tool Sources
~~~~~~~~~~~~

Some tools bring in data to the agent from external sources, like search engines or APIs. Those sources can be exposed
by enabling `keepToolSources` as argument of the :class:`Symfony\\AI\\Agent\\Toolbox\\AgentProcessor`::
by enabling `includeSources` as argument of the :class:`Symfony\\AI\\Agent\\Toolbox\\AgentProcessor`::

use Symfony\AI\Agent\Toolbox\AgentProcessor;
use Symfony\AI\Agent\Toolbox\Toolbox;

$toolbox = new Toolbox([new MyTool()]);
$toolProcessor = new AgentProcessor($toolbox, keepToolSources: true);
$toolProcessor = new AgentProcessor($toolbox, includeSources: true);

In the tool implementation sources can be added by implementing the
:class:`Symfony\\AI\\Agent\\Toolbox\\Source\\HasSourcesInterface` in combination with the trait
Expand Down
2 changes: 1 addition & 1 deletion examples/anthropic/toolcall.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

$wikipedia = new Wikipedia(http_client());
$toolbox = new Toolbox([$wikipedia], logger: logger());
$processor = new AgentProcessor($toolbox, keepToolSources: true);
$processor = new AgentProcessor($toolbox, includeSources: true);
$agent = new Agent($platform, 'claude-3-5-sonnet-20241022', [$processor], [$processor]);

$messages = new MessageBag(Message::ofUser('Who is the current chancellor of Germany?'));
Expand Down
4 changes: 2 additions & 2 deletions src/agent/src/Toolbox/AgentProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public function __construct(
private readonly ToolResultConverter $resultConverter = new ToolResultConverter(),
private readonly ?EventDispatcherInterface $eventDispatcher = null,
private readonly bool $keepToolMessages = false,
private readonly bool $keepToolSources = false,
private readonly bool $includeSources = false,
) {
}

Expand Down Expand Up @@ -127,7 +127,7 @@ private function handleToolCallsCallback(Output $output): \Closure
} while ($result instanceof ToolCallResult);

--$this->nestingLevel;
if ($this->keepToolSources && 0 === $this->nestingLevel) {
if ($this->includeSources && 0 === $this->nestingLevel) {
$result->getMetadata()->add('sources', $this->sources);
$this->sources = [];
}
Expand Down
6 changes: 3 additions & 3 deletions src/agent/tests/Toolbox/AgentProcessorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ public function testSourcesEndUpInResultMetadataWithSettingOn()
->method('call')
->willReturn(new TextResult('Final response based on the two articles.'));

$processor = new AgentProcessor($toolbox, keepToolSources: true);
$processor = new AgentProcessor($toolbox, includeSources: true);
$processor->setAgent($agent);

$output = new Output('gpt-4', $result, $messageBag);
Expand Down Expand Up @@ -181,7 +181,7 @@ public function testSourcesDoNotEndUpInResultMetadataWithSettingOff()
->method('call')
->willReturn(new TextResult('Final response based on the two articles.'));

$processor = new AgentProcessor($toolbox, keepToolSources: false);
$processor = new AgentProcessor($toolbox, includeSources: false);
$processor->setAgent($agent);

$output = new Output('gpt-4', $result, $messageBag);
Expand Down Expand Up @@ -220,7 +220,7 @@ public function testSourcesGetCollectedAcrossConsecutiveToolCalls()
new DeferredResult(new PlainConverter(new TextResult('Final response based on both articles.')), new InMemoryRawResult())
);

$processor = new AgentProcessor($toolbox, keepToolSources: true);
$processor = new AgentProcessor($toolbox, includeSources: true);
$agent = new Agent($platform, 'foo-bar', [$processor], [$processor]);
$processor->setAgent($agent);

Expand Down
13 changes: 12 additions & 1 deletion src/ai-bundle/config/options.php
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,18 @@
->end()
->end()
->end()
->booleanNode('fault_tolerant_toolbox')->defaultTrue()->end()
->booleanNode('keep_tool_messages')
->info('Keep tool messages in the conversation history')
->defaultFalse()
->end()
->booleanNode('include_sources')
->info('Include sources exposed by tools as part of the tool result metadata')
->defaultFalse()
->end()
->booleanNode('fault_tolerant_toolbox')
->info('Continue the agent run even if a tool call fails')
->defaultTrue()
->end()
->end()
->end()
->end()
Expand Down
1 change: 1 addition & 0 deletions src/ai-bundle/config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@
service('ai.tool_result_converter'),
service('event_dispatcher')->nullOnInvalid(),
false,
false,
])
->set('ai.security.is_granted_attribute_listener', IsGrantedToolAttributeListener::class)
->args([
Expand Down
4 changes: 3 additions & 1 deletion src/ai-bundle/src/AiBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,9 @@ private function processAgentConfig(string $name, array $config, ContainerBuilde
}

$toolProcessorDefinition = (new ChildDefinition('ai.tool.agent_processor.abstract'))
->replaceArgument(0, new Reference('ai.toolbox.'.$name));
->replaceArgument(0, new Reference('ai.toolbox.'.$name))
->replaceArgument(3, $config['keep_tool_messages'])
->replaceArgument(4, $config['include_sources']);

$container->setDefinition('ai.tool.agent_processor.'.$name, $toolProcessorDefinition)
->addTag('ai.agent.input_processor', ['agent' => $agentId, 'priority' => -10])
Expand Down
6 changes: 5 additions & 1 deletion src/ai-bundle/src/Profiler/DataCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\AI\AiBundle\Profiler;

use Symfony\AI\Agent\Toolbox\ToolResult;
use Symfony\AI\Platform\Metadata\Metadata;
use Symfony\AI\Platform\Tool\Tool;
use Symfony\Bundle\FrameworkBundle\DataCollector\AbstractDataCollector;
use Symfony\Component\HttpFoundation\Request;
Expand Down Expand Up @@ -103,7 +104,8 @@ private function getAllTools(): array
* model: string,
* input: array<mixed>|string|object,
* options: array<string, mixed>,
* result: string|iterable<mixed>|object|null
* result: string|iterable<mixed>|object|null,
* metadata: Metadata,
* }[]
*/
private function awaitCallResults(TraceablePlatform $platform): array
Expand All @@ -118,6 +120,8 @@ private function awaitCallResults(TraceablePlatform $platform): array
$call['result'] = $result->getContent();
}

$call['metadata'] = $result->getMetadata();

$calls[$key] = $call;
}

Expand Down
9 changes: 9 additions & 0 deletions src/ai-bundle/templates/data_collector.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,15 @@
{% else %}
{{ call.result }}
{% endif %}
{% if call.metadata|length > 0 %}
<br />
<strong>Metadata</strong>
<ul>
{% for key, value in call.metadata.all %}
<li>{{ key }}:<br />{{ dump(value) }}</li>
{% endfor %}
</ul>
{% endif %}
</td>
</tr>
</tbody>
Expand Down