AI Module Core Architecture
When to Use
Use this guide to understand how the AI module works before writing any code. Use the Provider System guide when you need to call or build a provider.
Decision
| Situation | Choose | Why |
|---|---|---|
| Need to call AI | Use ai.provider service |
Gets ProviderProxy with events + guardrails |
| Need default model | getDefaultProviderForOperationType() |
Config-driven, not hardcoded |
| Need to tag a request | Pass tags array to operation | Enables logging, guardrail filtering, cost attribution |
| Need to extend AI | Build a plugin (provider, guardrail, etc.) | Architecture is plugin-based throughout |
Pattern
// All AI calls go through ProviderProxy — never instantiate providers directly.
$providerManager = \Drupal::service('ai.provider');
$defaults = $providerManager->getDefaultProviderForOperationType('chat');
// ['provider_id' => 'anthropic', 'model_id' => 'claude-sonnet-4-20250514']
$provider = $providerManager->createInstance($defaults['provider_id']);
if (!$provider->isUsable('chat')) {
// Provider not configured — handle gracefully.
return;
}
$output = $provider->chat($input, $defaults['model_id'], ['my_module']);
Module Hierarchy
ai (core)
├── ai_assistant_api — Assistant config entities + action plugins
│ └── ai_chatbot — DeepChat frontend + REST API
├── ai_automators — Field-level AI automation (52 plugin types)
├── ai_ckeditor — CKEditor 5 AI toolbar
├── ai_search — Vector DB Search API backend
├── ai_translate — Content + interface translation [DEPRECATED]
├── ai_observability — Production monitoring (logger + OpenTelemetry)
├── ai_logging — Entity-based request logging [DEPRECATED]
├── ai_api_explorer — Developer testing UI
├── ai_validations — AI-powered field validation [DEPRECATED]
├── ai_content_suggestions — Content review panels [DEPRECATED]
├── ai_external_moderation — Migration shim [DEPRECATED]
├── ai_eca — Migration shim [DEPRECATED]
└── field_widget_actions — Framework for field widget action buttons
Core Services
| Service ID | Purpose |
|---|---|
ai.provider |
Main entry point — create providers, get defaults |
ai.vdb_provider |
Vector database providers |
ai.form_helper |
Provider/model selection form elements |
ai.prompt_manager |
Manage prompt config entities |
ai.tokenizer |
Token counting for context management |
ai.text_chunker |
Split text into chunks for embeddings |
plugin.manager.ai.function_calls |
Discover FunctionCall plugins |
plugin.manager.ai_guardrail |
Discover guardrail plugins |
plugin.manager.ai.chat_processor |
Changed in 1.4: Discover ChatProcessor plugins (new plugin type for preprocessing chat inputs) |
ai.exception_event_subscriber |
Changed in 1.4: Logs AI exceptions via AiExceptionEvent; enables graceful failover |
ai.file_manager |
New in 1.4.2: Manages the AI File lifecycle — uploads local files to a provider's Files API, tracks them as ai_file entities, deletes and loads them by purpose |
Content Entities
New in 1.4.2: The ai_file content entity (Drupal\ai\Entity\AiFile) represents a file uploaded to a provider's Files API. Fields: provider ID, remote ID, filename, MIME type, size, purpose, JSON metadata, optional local file reference, owner. Administered at /admin/config/ai/files (permission: administer ai). Managed via ai.file_manager service. See Operation Types for usage and Provider System for AiFileProviderInterface.
Key Concepts
| Concept | Description |
|---|---|
| ProviderProxy | Transparent wrapper that fires events (pre/post) and applies guardrails |
| Guardrail | Pre/post processing plugin (content moderation, PII filtering, etc.) |
| Global Guardrails | Changed in 1.4: Site-wide guardrail sets configured in ai.settings and applied to every AI request before caller-attached guardrails |
| ChatProcessor | Changed in 1.4: New plugin type (#[ChatProcessor]) for preprocessing chat inputs before they reach the provider |
ProviderProxy Request Lifecycle
All provider calls go through ProviderProxy (src/Plugin/ProviderProxy.php):
- Normalize config, set tags, sync streaming/system-role
- Generate UUID
$event_id(ties pre/post events together) - Dispatch
PreGenerateResponseEvent— subscribers can modify input, force early return - Call the real provider method
- Catch and re-throw typed exceptions with logging. Changed in 1.4: Also dispatches
AiExceptionEventbefore re-throwing — allows subscribers to inject a recovery output viasetForcedOutputObject() - Dispatch
PostGenerateResponseEvent— subscribers can modify output - For streamed responses, attach metadata to the iterator
Drupal core requirement: ^10.5 || ^11.2 (raised from ^10.3 || ^11 in earlier releases).
Common Mistakes
- Wrong: Calling providers directly without
ai.provider→ Right: Always useai.providerservice — bypassing it skips events, guardrails, and logging - Wrong: Hardcoding provider/model IDs → Right: Use
getDefaultProviderForOperationType()for config-driven defaults - Wrong: Not passing tags → Right: Tags enable logging filters, guardrails, and event filtering
- Wrong: Not checking
isUsable()before calling → Right: Provider may not be configured; always check first
See Also
- Provider System
- Operation Types
- Events System
- Guardrails System
- Reference:
web/modules/contrib/ai/src/Plugin/ProviderProxy.php