Provide proper support for "unconfigured" AI provider state
>>> [!note] Migrated issue <!-- Drupal.org comment --> <!-- Migrated from issue #3572645. --> Reported by: [mxr576](https://www.drupal.org/user/315522) >>> <p>[Tracker]<br> <strong>Update Summary: </strong>[One-line status update for stakeholders]<br> <strong>Short Description: </strong>[One-line issue summary for stakeholders]<br> <strong>Check-in Date: </strong>MM/DD/YYYY<br> <em>Metadata is used by the <a href="https://www.drupalstarforge.ai/" title="AI Tracker">AI Tracker.</a> Docs and additional fields <a href="https://www.drupalstarforge.ai/ai-dashboard/docs" title="AI Issue Tracker Documentation">here</a>.</em><br> [/Tracker]</p> <h3 id="summary-problem-motivation">Problem/Motivation</h3> <p>This is the second time we have hit a WSOD with the Gemini provider where simply enabling the module can break pages that instantiate AI provider clients. See <span class="drupalorg-gitlab-issue-link project-issue-status-info project-issue-status-7"><a href="https://www.drupal.org/project/gemini_provider/issues/3572638" title="Status: Closed (fixed)">#3572638: WSOD after module enabled</a></span>.</p> <p>At the moment, this does not look like something that can be solved only in the Gemini provider. It appears to be an architectural issue that affects any provider built on the shared client base, because providers can be installed (enabled) while still unconfigured, and the current client-loading code path does not reliably tolerate that state.</p> <p>The failing path is <code>\Drupal\ai\Base\OpenAiBasedProviderClientBase::loadClient()</code> &rarr; <code>\\Drupal\ai\Base\AiProviderClientBase::loadApiKey()</code>. Specifically, <code>loadApiKey()</code> throws when the configured Key entity cannot be loaded or when the loaded Key has an empty stored value.</p> <p>For most providers (Amazee AI appears to be an exception), the default installed configuration has no Key entity ID configured, for example:</p> <p>acme_provider.settings.yml</p> <pre>api_key: ''</pre><p>That means the installed-but-unconfigured state is effectively guaranteed to hit an exception if code calls <code>loadClient()</code> without first verifying usability.</p> <p>Possible fixes seem to be one of the following:</p> <ul> <li>Change the behavior of <code>\Drupal\ai\Base\AiProviderClientBase::loadApiKey()</code> so it does not throw for an unconfigured provider and instead returns <code>null</code> (or otherwise signals "not configured"), which would likely be an API change suited for 2.x.</li> <li>Update provider implementations and calling code to consistently call <code>\Drupal\\ai\AiProviderInterface::isUsable()</code> before calling <code>loadClient()</code>, since <code>isUsable()</code> already accounts for the unconfigured state.<br> <pre>&nbsp; /**<br>&nbsp;&nbsp; * {@inheritdoc}<br>&nbsp;&nbsp; */<br>&nbsp; public function isUsable(?string $operation_type = NULL, array $capabilities = []): bool {<br>&nbsp;&nbsp;&nbsp; if (!$this-&gt;hasAuthentication() &amp;&amp; !$this-&gt;getConfig()-&gt;get('api_key')) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return FALSE;<br>&nbsp;&nbsp;&nbsp; }<br>....<br>&nbsp; }</pre></li> </ul> <p>I generally prefer APIs that guide developers toward the safe path and reduce the chance of simple mistakes. If there is a third option, it likely needs to ensure that an installed but unconfigured provider cannot trigger a WSOD just because a page or service tries to instantiate a client.</p> <h4 id="summary-steps-reproduce">Steps to reproduce (required for bugs, but not feature requests)</h4> <p>Please provide information like AI modules enabled, which AI provider, browser, etc.</p> <h3 id="summary-proposed-resolution">Proposed resolution</h3> <h3 id="summary-remaining-tasks">Remaining tasks</h3> <h3>Optional: Other details as applicable (e.g., User interface changes, API changes, Data model changes)</h3> <h3 id="summary-ai-usage">AI usage (if applicable)</h3> <p>[ ] AI Assisted Issue<br> This issue was generated with AI assistance, but was reviewed and refined by the creator.</p> <p>[ ] AI Assisted Code<br> This code was mainly generated by a human, with AI autocompleting or parts AI generated, but under full human supervision.</p> <p>[ ] AI Generated Code<br> This code was mainly generated by an AI with human guidance, and reviewed, tested, and refined by a human.</p> <p>[ ] Vibe Coded<br> This code was generated by an AI and has only been functionally tested.</p>
issue