1818use Symfony \AI \Agent \Output ;
1919use Symfony \AI \Agent \OutputProcessorInterface ;
2020use Symfony \AI \Agent \Toolbox \Event \ToolCallsExecuted ;
21+ use Symfony \AI \Agent \Toolbox \Source \Source ;
2122use Symfony \AI \Agent \Toolbox \StreamResult as ToolboxStreamResponse ;
2223use Symfony \AI \Platform \Message \AssistantMessage ;
2324use Symfony \AI \Platform \Message \Message ;
@@ -34,11 +35,25 @@ final class AgentProcessor implements InputProcessorInterface, OutputProcessorIn
3435{
3536 use AgentAwareTrait;
3637
38+ /**
39+ * Sources get collected during tool calls on class level to be able to handle consecutive tool calls.
40+ * They get added to the result metadata and reset when the outermost agent call is finished via nesting level.
41+ *
42+ * @var Source[]
43+ */
44+ private array $ sources = [];
45+
46+ /**
47+ * Tracks the nesting level of agent calls.
48+ */
49+ private int $ nestingLevel = 0 ;
50+
3751 public function __construct (
3852 private readonly ToolboxInterface $ toolbox ,
3953 private readonly ToolResultConverter $ resultConverter = new ToolResultConverter (),
4054 private readonly ?EventDispatcherInterface $ eventDispatcher = null ,
4155 private readonly bool $ keepToolMessages = false ,
56+ private readonly bool $ keepToolSources = false ,
4257 ) {
4358 }
4459
@@ -87,6 +102,7 @@ private function isFlatStringArray(array $tools): bool
87102 private function handleToolCallsCallback (Output $ output ): \Closure
88103 {
89104 return function (ToolCallResult $ result , ?AssistantMessage $ streamedAssistantResponse = null ) use ($ output ): ResultInterface {
105+ ++$ this ->nestingLevel ;
90106 $ messages = $ this ->keepToolMessages ? $ output ->getMessageBag () : clone $ output ->getMessageBag ();
91107
92108 if (null !== $ streamedAssistantResponse && '' !== $ streamedAssistantResponse ->getContent ()) {
@@ -101,6 +117,7 @@ private function handleToolCallsCallback(Output $output): \Closure
101117 foreach ($ toolCalls as $ toolCall ) {
102118 $ results [] = $ toolResult = $ this ->toolbox ->execute ($ toolCall );
103119 $ messages ->add (Message::ofToolCall ($ toolCall , $ this ->resultConverter ->convert ($ toolResult )));
120+ array_push ($ this ->sources , ...$ toolResult ->getSources ());
104121 }
105122
106123 $ event = new ToolCallsExecuted (...$ results );
@@ -109,6 +126,12 @@ private function handleToolCallsCallback(Output $output): \Closure
109126 $ result = $ event ->hasResult () ? $ event ->getResult () : $ this ->agent ->call ($ messages , $ output ->getOptions ());
110127 } while ($ result instanceof ToolCallResult);
111128
129+ --$ this ->nestingLevel ;
130+ if ($ this ->keepToolSources && 0 === $ this ->nestingLevel ) {
131+ $ result ->getMetadata ()->add ('sources ' , $ this ->sources );
132+ $ this ->sources = [];
133+ }
134+
112135 return $ result ;
113136 };
114137 }
0 commit comments