Canvas AI: Expose Props of Blocks to the Agent
>>> [!note] Migrated issue <!-- Drupal.org comment --> <!-- Migrated from issue #3569120. --> Reported by: [akhil babu](https://www.drupal.org/user/3632866) Related to !498 >>> <h3 id="overview">Overview</h3> <p>Currently CanvasAiPageBuilderHelper::getAllComponentsKeyedBySource() does this</p> <div class="codeblock"> <pre><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #007700">if (</span><span style="color: #0000BB">$source </span><span style="color: #007700">=== </span><span style="color: #0000BB">SingleDirectoryComponent</span><span style="color: #007700">::</span><span style="color: #0000BB">SOURCE_PLUGIN_ID</span><span style="color: #007700">) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">processSdc</span><span style="color: #007700">(</span><span style="color: #0000BB">$component</span><span style="color: #007700">, </span><span style="color: #0000BB">$sdc_definitions</span><span style="color: #007700">, </span><span style="color: #0000BB">$output</span><span style="color: #007700">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; elseif (</span><span style="color: #0000BB">$source </span><span style="color: #007700">=== </span><span style="color: #0000BB">JsComponent</span><span style="color: #007700">::</span><span style="color: #0000BB">SOURCE_PLUGIN_ID</span><span style="color: #007700">) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">processCodeComponents</span><span style="color: #007700">(</span><span style="color: #0000BB">$component</span><span style="color: #007700">, </span><span style="color: #0000BB">$output</span><span style="color: #007700">, </span><span style="color: #0000BB">$available_components</span><span style="color: #007700">[</span><span style="color: #0000BB">$component_id</span><span style="color: #007700">]);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// Other sources: id, name, description (description = name)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$output</span><span style="color: #007700">[</span><span style="color: #0000BB">$source</span><span style="color: #007700">][</span><span style="color: #DD0000">'components'</span><span style="color: #007700">][</span><span style="color: #0000BB">$component_id</span><span style="color: #007700">] = [<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'id' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$component_id</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'name' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$component</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">label</span><span style="color: #007700">(),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'description' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$component</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">label</span><span style="color: #007700">(),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br></span><span style="color: #0000BB">?&gt;</span></span></pre></div> <p>So for blocks, only the ID, name, and description are passed as context to the agent. However, this causes issues when the agent decides to use a block with required props (e.g., the System Branding block), because AiResponseValidator::validateComponentStructure() will throw errors like:</p> <p><code>Component validation errors: components.0.[block.system_branding_block].props.: 'label' is a required key. components.0.[block.system_branding_block].props.: 'label_display' is a required key. components.0.[block.system_branding_block].props.: 'use_site_logo' is a required key. components.0.[block.system_branding_block].props.: 'use_site_name' is a required key. components.0.[block.system_branding_block].props.: 'use_site_slogan' is a required key</code></p> <h3 id="proposed-resolution">Proposed resolution</h3> <p>Expose the props of blocks to the agent.</p> <p>Currently, _addNewComponentToLayout in layoutModelSlice.ts only sets field values for components that have the propSources property . Block components do not have this property.</p> <pre>&nbsp;&nbsp;&nbsp; const buildInitialData = (component: CanvasComponent): ComponentModel =&gt; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (isPropSourceComponent(component)) {&nbsp; // Block components return false for this check.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const initialData: EvaluatedComponentModel = {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; resolved: {},<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; source: {},<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Object.keys(component.propSources).forEach((propName) =&gt; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const prop = component.propSources[propName];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // These will be needed when we support client-side preview updates.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; initialData.resolved[propName] = prop.default_values?.resolved || [];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // These are the values the server needs.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // @todo Reduce the verbosity of this in https://drupal.org/i/3463996<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp; and https://drupal.org/i/3528043 to send less data.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; initialData.source[propName] = {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; expression: prop.expression,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sourceType: prop.sourceType,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value: prop.default_values?.source || [],<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sourceTypeSettings: prop.sourceTypeSettings || undefined,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return initialData;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; resolved: {},<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };</pre><p>This means it is not possible to place a block component with specific configuration values. For example, when the AI agent tries to place a Site Branding block with values like:</p> <pre>&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; "use_site_logo": true,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; "use_site_name": true&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp; }</pre><p> these values are ignored because block components lack propSources. As a result, block components are always placed with their default configuration, regardless of what values the AI specifies.</p> <h3 id="ui-changes">Testing steps</h3> <ul> <li>Install ai_api_explorer module</li> <li>Go to /admin/config/ai/explorers/tools_explorer, Select the 'Get component context' tool and click 'Run function'</li> <li>The props of block components must be visible in the output. Previously it included only block id, name and description</li> </ul> <p>Eg:</p> <pre>block.events_block:<br>&nbsp; id: block.events_block<br>&nbsp; name: 'Events Feed'<br>&nbsp; description: 'Events Feed'<br>&nbsp; props:<br>&nbsp;&nbsp;&nbsp; label:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; name: Description<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; description: Description<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type: label<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; default: 'Events Feed'<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; required: true<br>&nbsp;&nbsp;&nbsp; label_display:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; name: 'Display title'<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; description: 'Display title'<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type: string<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; default: '0'<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; required: true<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; enum: ['0', visible]<br>&nbsp;&nbsp;&nbsp; context_mapping:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; name: 'Context assignments'<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; description: 'Context assignments'<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type: sequence<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; default: null</pre>
issue