Improve ai_agents module API to simplify tool result handling

Created on 10 June 2025, about 2 months ago

Problem/Motivation

Current usage of the `ai_agents` module requires manually looping over tool results to find tools by plugin ID or by class, and parsing their outputs repeatedly. See https://git.drupalcode.org/project/experience_builder/-/merge_requests/1...

Steps to reproduce

Proposed resolution

Create helper methods such as getToolResultsByPluginId(string $pluginId), getToolResultsByClass(string $className),
getParsedOutput() to avoid code complexity

Remaining tasks

User interface changes

API changes

Data model changes

📌 Task
Status

Active

Version

1.1

Component

Code

Created by

🇮🇳India narendraR Jaipur, India

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @narendraR
  • 🇩🇪Germany marcus_johansson

    I would suggest the following

    getToolResultsByPluginId(string $plugin_id, $recursive = FALSE);
    getToolResultsByClass(string $class_name, $recursive = FALSE);

    and also add recursive to getToolsResults.

    Recursive will go through any potential child agents, grand child agents etc. to get the tool results from there as well.

    Since the same results can be used multiple times, it should return arrays of tool results.

  • @marcus_johansson opened merge request.
  • 🇩🇪Germany marcus_johansson

    This turned out to be more work the expected, because when I wrote tests for it, it was obvious that a bug in how the agent is loaded from array didn't seed sub-agents correctly. So this is part of the fix.

    There now exists:

    getToolResults($recursive = FALSE) - that gets all the tool results, with recursive to true, it gets the results for every sub agent as well.
    getToolResultsByClassName($class_name, $recursive = false) - that gets all the tool results that fits a specific class.
    getToolResultsByPluginId($plugin_id, $recursive = false) - that gets all the tool results that fits a sepcific plugin id.

    Testing is added - documentation will be added in Drupal AI cores documentation.

  • 🇮🇳India narendraR Jaipur, India

    Tested it manually by making below changes in existing xb_ai code to make it work. Changes looks good to me and we have tests to support this.

         elseif ($solvability == AiAgentInterface::JOB_SOLVABLE) {
           $response['status'] = TRUE;
    -      $tools = $agent->getToolResults();
    -      $map = [
    -        EditComponentJs::class => ['js_structure', 'props_metadata'],
    -        CreateComponent::class => ['component_structure'],
    -      ];
    -      if (!empty($tools)) {
    -        foreach ($tools as $tool) {
    -          // @todo Refactor this after https://www.drupal.org/i/3529310 is fixed.
    -          if (
    -            $tool->getPluginId() === 'ai_agents::ai_agent::experience_builder_component_agent'
    -          ) {
    -            $response['message'] = $tool->getReadableOutput();
    -            foreach ($tool->getAgent()->getToolResults() as $sub_agent_tool) {
    -              foreach ($map as $class => $keys) {
    -                if ($sub_agent_tool instanceof $class) {
    -                  // @todo Refactor this after https://www.drupal.org/i/3529313 is fixed.
    -                  $output = $sub_agent_tool->getReadableOutput();
    -                  $data = Yaml::parse($output);
    -                  foreach ($keys as $key) {
    -                    if (!empty($data[$key])) {
    -                      $response[$key] = $data[$key];
    -                    }
    -                  }
    -                }
    +      $component_agent_tool_results = $agent->getToolResultsByPluginId(
    +        'ai_agents::ai_agent::experience_builder_component_agent',
    +        TRUE
    +      );
    +      if (!empty($component_agent_tool_results)) {
    +        $tool = reset($component_agent_tool_results);
    +        $response['message'] = $tool->getReadableOutput();
    +        $map = [
    +          EditComponentJs::class => ['js_structure', 'props_metadata'],
    +          CreateComponent::class => ['component_structure'],
    +        ];
    +        foreach ($map as $class => $keys) {
    +          $class_tools = $tool->getAgent()->getToolResultsByClassName($class, TRUE);
    +          foreach ($class_tools as $sub_agent_tool) {
    +            $output = $sub_agent_tool->getReadableOutput();
    +            $data = Yaml::parse($output);
    +            foreach ($keys as $key) {
    +              if (!empty($data[$key])) {
    +                $response[$key] = $data[$key];
                   }
                 }
               }
    
  • 🇩🇪Germany marcus_johansson

    Thank you for the review - getting merged.

  • Automatically closed - issue fixed for 2 weeks with no activity.

  • 🇺🇸United States Kristen Pol Santa Cruz, CA, USA

    unassigning

Production build 0.71.5 2024