AI Provider System
When to Use
Use this guide when calling a specific provider, building a custom provider plugin, or working with the provider/model selection form. Use Operation Types for the typed Input/Output classes.
Providers are plugins implementing AiProviderInterface. Each provider wraps one AI service and declares which operation types it supports.
Decision
| Situation | Choose | Why |
|---|---|---|
| Provider with unique API | AiProviderClientBase |
Full flexibility to implement any API |
| Provider with OpenAI-compatible API | OpenAiBasedProviderClientBase |
Gets chat, embeddings, TTS, STT, T2I for free |
| List models for a form | getSimpleProviderModelOptions() |
Returns formatted provider__model => label array |
| Check if operation is available | hasProvidersForOperationType() |
Boolean check before calling |
| Upload file to provider | ai.file_manager service |
Lifecycle management; don't call provider file methods directly |
Using Providers
// Get the provider manager
$providerManager = \Drupal::service('ai.provider');
// Get default provider for an operation type
$defaults = $providerManager->getDefaultProviderForOperationType('chat');
// Returns: ['provider_id' => 'anthropic', 'model_id' => 'claude-sonnet-4-20250514']
// Create a provider instance (returns ProviderProxy)
$provider = $providerManager->createInstance('anthropic');
// Check availability
$providerManager->hasProvidersForOperationType('embeddings'); // bool
// Get all models for operation type
$models = $provider->getConfiguredModels('chat');
// Get simple options for form selects
$options = $providerManager->getSimpleProviderModelOptions('chat');
// Returns: ['anthropic__claude-3-sonnet' => 'Anthropic: Claude 3 Sonnet', ...]
Building a Custom Provider
use Drupal\ai\Attribute\AiProvider;
use Drupal\ai\Base\AiProviderClientBase;
#[AiProvider(
id: 'my_provider',
label: new TranslatableMarkup('My Provider'),
)]
class MyProvider extends AiProviderClientBase implements ChatInterface {
public function isUsable(?string $operation_type = NULL): bool {
return !empty($this->getApiKey());
}
public function getSupportedOperationTypes(): array {
return ['chat', 'embeddings'];
}
public function getConfiguredModels(string $operation_type): array {
return ['my-model-v1' => 'My Model v1'];
}
public function chat(ChatInput $input, string $model_id, array $tags = []): ChatOutput {
$client = $this->loadClient($model_id);
// ... call API, normalize response
return new ChatOutput($input, $normalizedMessages, $rawResponse, []);
}
}
Provider Matrix (Key Providers)
| Provider | Chat | Embeddings | Moderation | TTS | STT | T2I | Translation |
|---|---|---|---|---|---|---|---|
| Anthropic | Yes | ||||||
| OpenAI | Yes | Yes | Yes | Yes | Yes | Yes | |
| Google/Gemini | Yes | ||||||
| Ollama | Yes | Yes | |||||
| AWS Bedrock | Yes | Yes | Yes | ||||
| Azure | Yes | Yes | Yes | Yes | Yes | Yes | |
| LiteLLM | Yes | Yes | Yes | Yes | Yes | Yes | |
| DeepL | Yes | ||||||
| Vertex AI | Yes | Yes | Yes |
Key AiProviderInterface Methods
Beyond getConfiguredModels(), isUsable(), getSupportedOperationTypes():
| Method | Purpose |
|---|---|
getAvailableConfiguration($op, $model) |
Returns configurable parameters (temperature, max_tokens, etc.) for the model config UI |
getDefaultConfigurationValues($op, $model) |
Default values for configuration parameters |
setAuthentication($auth) |
Override authentication at runtime (used by PreGenerateResponseEvent) |
setConfiguration($config) / getConfiguration() |
Runtime config override |
getSupportedCapabilities() |
Returns AiModelCapability[] or AiProviderCapability[] the provider supports |
loadModelsForm($form, $state, $op, $model) |
Builds per-model config form in admin UI |
hasPredefinedModels() |
false = system generates model list dynamically |
getSetupData() |
Returns key_config_name (Key module integration) + default_models for initial setup |
setTag($tag) / getTags() / resetTags() |
Tag management for logging/filtering |
setDebugData($key, $value) / getDebugData() |
Attach debug metadata to requests |
Base Classes
| Class | Use When |
|---|---|
AiProviderClientBase |
Custom provider with unique API |
OpenAiBasedProviderClientBase |
Provider with OpenAI-compatible API (e.g., Ollama, LiteLLM) |
OpenAiBasedProviderClientBase implements ChatInterface, ModerationInterface, EmbeddingsInterface, TextToSpeechInterface, SpeechToTextInterface, and TextToImageInterface out of the box using the openai-php/client library. It handles streaming via OpenAiTypeStreamedChatMessageIterator, token usage extraction into TokenUsageDto, rate limit parsing into ChatProviderLimitsDto, and standard error mapping to AI exceptions. Extend it when your provider's API is OpenAI-compatible — you only need to provide loadClient() with your endpoint/key.
Provider File Handling (New in 1.4.2)
Providers that support a remote Files API (upload a document once, reference it across requests) implement AiFileProviderInterface (Drupal\ai\AiFileProviderInterface):
| Method | Purpose |
|---|---|
uploadFile(AiFileInterface $ai_file, mixed $file): AiFileInterface |
Upload binary/stream to the provider; implementation MUST set the remote id on the entity |
deleteFile(AiFileInterface $ai_file): bool |
Delete the remote file by its stored remote id |
downloadFile(AiFileInterface $ai_file, ?string $destination = NULL): string |
Download remote file to a path, or return raw contents when no destination is given |
supportsMimeType(string $mime_type, string $purpose): bool |
Whether a MIME type is allowed for the declared purpose |
OpenAI-compatible providers get this for free via the FileApiTrait (Drupal\ai\Traits\OpenAi\FileApiTrait), which maps to the OpenAI files() endpoint and enforces per-purpose MIME rules (batch/fine-tune → text/plain, application/jsonl, application/json; vision → images; otherwise unrestricted). Don't call a provider's file methods directly — go through the ai.file_manager service (see Operation Types).
Scaffolding Providers (New in 1.4)
The module ships a Drush code generator for new providers:
drush generate plugin:ai:provider # alias: ai-provider
(Companion generators exist for guardrails — plugin:ai:guardrail — and automator types — plugin:ai:automator-type.)
Common Mistakes
| Mistake | Why it's wrong |
|---|---|
Using createInstance() without checking isUsable() |
Provider may lack API key — check first |
Not implementing getAvailableConfiguration() |
Breaks the form helper's model configuration UI |
Missing loadClient() |
Base class expects this for lazy client initialization |
| Calling a provider's file methods directly | Always use the ai.file_manager service for file lifecycle management |
See Also
- Core Architecture
- Operation Types
- Reference:
web/modules/contrib/ai/src/Base/AiProviderClientBase.php - Reference:
web/modules/contrib/ai/src/Base/OpenAiBasedProviderClientBase.php - Reference:
web/modules/contrib/ai/src/AiFileProviderInterface.php