Drupal is a registered trademark of Dries Buytaert
cms 2.1.2 Update released for Drupal core (2.1.2)! drupal 11.1.10 Update released for Drupal core (11.1.10)! drupal 10.5.10 Update released for Drupal core (10.5.10)! drupal 10.4.10 Update released for Drupal core (10.4.10)! drupal 11.2.12 Update released for Drupal core (11.2.12)! drupal 11.3.10 Update released for Drupal core (11.3.10)! drupal 10.6.9 Update released for Drupal core (10.6.9)! drupal 10.6.8 Update released for Drupal core (10.6.8)! drupal 11.3.9 Update released for Drupal core (11.3.9)! drupal 11.3.8 Update released for Drupal core (11.3.8)! drupal 11.3.7 Update released for Drupal core (11.3.7)! drupal 11.2.11 Update released for Drupal core (11.2.11)! drupal 10.6.7 Update released for Drupal core (10.6.7)! drupal 10.5.9 Update released for Drupal core (10.5.9)! cms 2.1.1 Update released for Drupal core (2.1.1)! drupal 11.3.6 Update released for Drupal core (11.3.6)! drupal 10.6.6 Update released for Drupal core (10.6.6)! cms 2.1.0 Update released for Drupal core (2.1.0)! linkit 7.0.15 Minor update available for module linkit (7.0.15). views_data_export 8.x-1.10 Minor update available for module views_data_export (8.x-1.10).

thunder_ai

No security coverage
View on drupal.org

A Drupal recipe for Thunder that installs and configures AI providers.

What this recipe installs

Module Description drupal/ai Core AI integration layer drupal/ai_provider_azure Azure OpenAI provider integration drupal/ai_provider_openai OpenAI provider integration drupal/gemini_provider Google Gemini provider integration drupal/ai_provider_anthropic Anthropic provider integration

Requirements

  • Thunder distribution
  • Composer

Installation

Apply the recipe using Drupal's recipe system:

drush recipe ../recipes/thunder_ai

The recipe asks the following questions:

  1. Default AI providerazure, openai, gemini, or anthropic
  2. Key storage methodconfig (value stored in Drupal config) or env (value read from an environment variable)
  3. Azure API key value or env var name — press Enter to skip if not using Azure
  4. Azure endpoint URL — the full deployment endpoint from Azure AI Studio
  5. OpenAI: API key value or env var name — press Enter to skip if not using OpenAI
  6. Google Gemini: API key value or env var name — press Enter to skip if not using Gemini
  7. Anthropic: API key value or env var name — press Enter to skip if not using Anthropic
  8. Default model ID — model to use for chat; defaults to thunder_ai_azure (the Azure model registered by this recipe). Override when using a different provider (e.g. gpt-4o, claude-opus-4-5, gemini-2.0-flash)

For questions 3, 5, 6, and 7: enter the raw API key when using config storage, or the environment variable name (e.g. OPENAI_API_KEY) when using env storage.

Note: Drupal recipe inputs cannot be made conditional, so all questions are always shown. Only fill in credentials and a model for the provider you selected; press Enter to skip the rest.

To skip the prompts entirely, pass all values as flags:


drush recipe ../recipes/thunder_ai \ --input="thunder_ai.default_provider=azure" \ --input="thunder_ai.default_model=thunder_ai_azure" \ --input="thunder_ai.key_provider=env" \ --input="thunder_ai.azure_api_key=AZURE_API_KEY" \ --input="thunder_ai.azure_endpoint=https://my-resource.openai.azure.com/openai/deployments/my-deployment/chat" \ --input="thunder_ai.openai_api_key=" \ --input="thunder_ai.gemini_api_key=" \ --input="thunder_ai.anthropic_api_key="

All four provider modules are installed regardless of the selection. The chosen provider is set as the site default for chat operations.

What the recipe configures

For each provider whose credentials are supplied, the recipe:

  1. Creates a Key module entity (thunder_ai_{provider}_key) using the chosen key provider (config or env)
  2. Wires the Key entity to the provider's settings
  3. For Azure: registers a chat model in ai.settings.models with the given endpoint
  4. Sets the chosen provider and its model as the site default for chat in ai.settings

Post-installation

Non-Azure modelai.settings.default_providers.chat.model_id is set from the default_model input. If you selected OpenAI, Gemini, or Anthropic, visit Administration > Configuration > AI to update the default model.

Production key storage — credentials are stored in Drupal config via the Key module's config provider. For production environments, switch to an environment-variable-based key provider at Administration > Configuration > System > Keys.

FAQ

Why are all four AI providers installed when I only selected one?

Drupal recipes have no conditional logic — it is not possible to install a module only when a specific input value is chosen. All four provider modules are always installed regardless of the selection. The chosen provider is set as the site default; the others are installed but unconfigured. See Removing unneeded providers below for how to clean them up.

Why does the default model default to thunder_ai_azure when I selected a different provider?

Azure OpenAI requires an explicit model registration in ai.settings.models that includes the deployment endpoint URL. The recipe registers one such model (thunder_ai_azure) using the endpoint supplied as input. Other providers discover available models dynamically and need no registration entry. Because the recipe always creates the Azure model entry, the default_model input defaults to thunder_ai_azure. When using a different provider, override the default_model input during recipe application or update the default model afterwards at Administration > Configuration > AI.

Why is setupAiProvider not used to configure the providers?

The drupal/ai module provides a setupAiProvider config action that creates a Key entity and wires it to a provider in one step. It was not used for two reasons:

  1. The Azure provider does not implement getSetupData() — it returns an empty array — which causes the action to fail for Azure.
  2. setupAiProvider hardcodes the Key module's config storage backend. There is no way to have it create a Key entity that reads from an environment variable instead.

The recipe uses createIfNotExists and simpleConfigUpdate directly, which avoids both limitations and works uniformly for all four providers.

Removing unneeded providers

The recipe installs all four AI provider modules. Once you have decided which provider to use, you can unpack the recipe and remove the providers you do not need.

Unpacking inlines the recipe's Composer dependencies into your project's composer.json and removes the recipe package itself, giving you full control over each dependency.

1. Install the unpack plugin

composer require drupal/core-recipe-unpack

2. Unpack the recipe

composer drupal:recipe-unpack drupal/thunder_ai

This moves all packages from drupal/thunder_ai's require into your root composer.json and removes the recipe package.

3. Uninstall the unused Drupal modules

Drupal modules must be uninstalled before their packages are removed from Composer. Removing the package first leaves Drupal with a reference to a missing module, which causes pm:uninstall to fail.


drush pm:uninstall ai_provider_azure # not using Azure OpenAI drush pm:uninstall ai_provider_openai # not using OpenAI drush pm:uninstall gemini_provider # not using Google Gemini drush pm:uninstall ai_provider_anthropic # not using Anthropic

4. Delete the unused Key config entries

drush config:delete key.key.thunder_ai_azure_key drush config:delete key.key.thunder_ai_openai_key drush config:delete key.key.thunder_ai_gemini_key drush config:delete key.key.thunder_ai_anthropic_key

5. Export the updated configuration

drush config:export

6. Remove the unused provider packages

Now that the modules are uninstalled, remove their Composer packages:


composer remove drupal/ai_provider_azure composer remove drupal/ai_provider_openai composer remove drupal/gemini_provider composer remove drupal/ai_provider_anthropic

Activity

Total releases
3
First release
May 2026
Latest release
4 days ago
Release cadence
0 days
Stability
0% stable

Release Timeline

Releases

Version Type Release date
1.0.x-dev Dev May 17, 2026
1.0.0-beta1 Pre-release May 17, 2026
1.x-dev Dev May 17, 2026