Drupal Webform AI Integration Guide
Drupal Webform AI Integration Guide
Overview: Comprehensive guide for integrating artificial intelligence with Drupal's webform module to create intelligent, adaptive forms with AI-powered validation, field suggestions, and content generation.
Overview
The Drupal webform module is a powerful form builder supporting 305,856+ active installations. Combined with Drupal's AI module (supporting 48+ AI providers including Anthropic, OpenAI, Google, and AWS), it enables creation of intelligent forms that leverage AI for:
- Smart Field Suggestions: AI-powered autocomplete and field population
- Intelligent Validation: AI-based form validation beyond traditional rules
- Content Generation: Dynamic field content using AI models
- Form Completion Assistance: AI-powered form filling suggestions
- Conditional Logic: Smart branching based on AI analysis
Core Webform Architecture
Form Builder Capabilities
The webform module provides:
- Multi-step Wizard Forms: Conditional logic with page progression
- Drag-and-drop Interface: Visual form building without coding
- Computed Elements: Dynamic field calculations and transformations
- Composite Elements: Grouped multi-field inputs
- Custom Elements: Extensible element type system
- Entity References: Link forms to Drupal content
- Submission Management: Email, webhook, database storage
- Draft & Preview: Save-and-continue functionality
- Conditional Display: Show/hide fields based on user input
Element Types
Webform supports diverse input mechanisms:
Standard Inputs:
- Text, textarea, email, URL, password
- Number, range, spinner
- Date, time, datetime
- Checkbox, radios, select, checkboxes
- File upload with limits
- Entity references
Advanced Elements:
- Computed elements (calculations/transformations)
- Composite elements (grouped fields)
- Scale & Likert questions
- Signature fields
- Custom elements (plugin-based)
Submission Flow
User Fills Form
†
Validation (Standard + Custom)
†
Pre-submission Handlers
†
Computed Elements Process
†
Submission Stored/Sent
†
Post-submission Handlers
AI Form Handlers
Webform handlers are the primary extension point for AI integration. Handlers process submissions at different stages and can trigger AI operations.
Handler Architecture
Handlers implement \Drupal\webform\Plugin\WebformHandlerInterface:
<?php namespace Drupal\my_module\Plugin\WebformHandler; use Drupal\webform\Plugin\WebformHandlerBase; use Drupal\webform\WebformSubmissionInterface; /** * Webform submission handler for AI processing. * * @WebformHandler( * id = "ai_processor", * label = @Translation("AI Processor"), * category = @Translation("AI"), * description = @Translation("Process webform submissions with AI"), * cardinality = Drupal\webform\Plugin\WebformHandlerInterface::CARDINALITY_UNLIMITED, * results = Drupal\webform\Plugin\WebformHandlerInterface::RESULTS_IGNORED, * submission = Drupal\webform\Plugin\WebformHandlerInterface::SUBMISSION_OPTIONAL, * ) */ class AiProcessorHandler extends WebformHandlerBase { /** * {@inheritdoc} */ public function submitForm( array &$form, array &$form_state, WebformSubmissionInterface $webform_submission ) { $submission_data = $webform_submission->getData(); // Call AI service $ai_service = \Drupal::service('ai.provider'); $result = $ai_service->sendMessage( 'text_generation', json_encode($submission_data), ['model' => 'gpt-4'] ); // Store AI response in submission $webform_submission->setData(array_merge($submission_data, [ 'ai_analysis' => $result['response'] ?? null, ])); } }
Handler Hooks
Process submissions at different lifecycle stages:
// Execute during submission processing public function submitForm(&$form, &$form_state, WebformSubmissionInterface $submission) // Execute before sending emails public function preEmailSend(WebformSubmissionInterface $submission, &$message) // Execute after submission is saved public function postSave(WebformSubmissionInterface $submission, $update) // Execute on submission delete public function postDelete(WebformSubmissionInterface $submission)
AI Handler Implementation Pattern
<?php namespace Drupal\ai_webform\Plugin\WebformHandler; use Drupal\Core\Form\FormStateInterface; use Drupal\webform\Plugin\WebformHandlerBase; use Drupal\webform\WebformSubmissionInterface; class AiValidationHandler extends WebformHandlerBase { /** * {@inheritdoc} */ public function buildConfigurationForm( array $form, FormStateInterface $form_state ) { $form = parent::buildConfigurationForm($form, $form_state); $form['ai_provider'] = [ '#type' => 'select', '#title' => $this->t('AI Provider'), '#options' => [ 'anthropic' => 'Anthropic Claude', 'openai' => 'OpenAI GPT', 'google' => 'Google Gemini', ], '#default_value' => $this->configuration['ai_provider'] ?? 'anthropic', ]; $form['ai_model'] = [ '#type' => 'textfield', '#title' => $this->t('Model'), '#default_value' => $this->configuration['ai_model'] ?? 'claude-3-sonnet', ]; $form['validation_prompt'] = [ '#type' => 'textarea', '#title' => $this->t('Validation Prompt'), '#description' => $this->t('Prompt sent to AI for validation'), '#default_value' => $this->configuration['validation_prompt'] ?? '', ]; return $form; } /** * {@inheritdoc} */ public function submitForm( array &$form, array &$form_state, WebformSubmissionInterface $submission ) { $config = $this->configuration; $data = $submission->getData(); // Build validation prompt with submission data $prompt = str_replace( '[submission_data]', json_encode($data, JSON_PRETTY_PRINT), $config['validation_prompt'] ); // Call AI service $ai_client = \Drupal::service('ai.client'); try { $response = $ai_client->sendMessage([ 'model' => $config['ai_model'], 'messages' => [ ['role' => 'user', 'content' => $prompt], ], 'provider' => $config['ai_provider'], ]); // Store validation result $submission->setData(array_merge($data, [ 'ai_validation_result' => $response['content'] ?? '', ])); // Log interaction \Drupal::logger('ai_webform')->info( 'AI validation completed for submission @id', ['@id' => $submission->id()] ); } catch (\Exception $e) { \Drupal::logger('ai_webform')->error( 'AI validation failed: @message', ['@message' => $e->getMessage()] ); } } }
Field Suggestions with AI
Computed Element Integration
Computed elements can use AI to generate field values dynamically:
<?php namespace Drupal\ai_webform\Element; use Drupal\Core\Render\Element\FormElement; class AiSuggestedElement extends FormElement { /** * {@inheritdoc} */ public function getInfo() { return [ '#type' => 'ai_suggested', '#ai_prompt' => '', '#ai_model' => 'claude-3-sonnet', '#cache' => ['max-age' => 3600], ]; } /** * {@inheritdoc} */ public static function valueCallback( &$element, $input, FormStateInterface $form_state ) { if ($input !== FALSE && $input !== NULL) { return $input; } if (empty($element['#ai_prompt'])) { return NULL; } // Get form values to build context $form_values = $form_state->getValues(); // Call AI to generate suggestion $ai_client = \Drupal::service('ai.client'); try { $prompt = str_replace( '[form_context]', json_encode($form_values), $element['#ai_prompt'] ); $response = $ai_client->sendMessage([ 'model' => $element['#ai_model'], 'messages' => [ ['role' => 'user', 'content' => $prompt], ], ]); return $response['content'] ?? NULL; } catch (\Exception $e) { \Drupal::logger('ai_webform')->error( 'AI suggestion failed: @message', ['@message' => $e->getMessage()] ); return NULL; } } }
YAML Configuration Example
# webform.webform.service_request.yml elements: customer_name: '#type': textfield '#title': 'Customer Name' '#required': true company: '#type': textfield '#title': 'Company' '#required': true suggested_service: '#type': ai_suggested '#title': 'Suggested Service' '#ai_prompt': | Based on the customer details: [form_context] Suggest the most appropriate service. Return only the service name. '#ai_model': claude-3-sonnet '#disabled': true
Intelligent Validation
Custom AI Validation Element
<?php namespace Drupal\ai_webform\Plugin\WebformElement; use Drupal\webform\Plugin\WebformElementBase; class AiValidatedTextElement extends WebformElementBase { /** * {@inheritdoc} */ public function form( array $form, array &$form_state, array $element, $web_form = NULL ) { $form = parent::form($form, $form_state, $element, $web_form); $form['validation']['#title'] = 'Validation Settings'; $form['validation']['ai_validation_enabled'] = [ '#type' => 'checkbox', '#title' => 'Enable AI Validation', '#default_value' => $element['#ai_validation_enabled'] ?? FALSE, ]; $form['validation']['ai_validation_rules'] = [ '#type' => 'textarea', '#title' => 'AI Validation Rules', '#description' => 'Define what constitutes valid input', '#default_value' => $element['#ai_validation_rules'] ?? '', '#states' => [ 'visible' => [ ':input[name="properties[ai_validation_enabled]"]' => ['checked' => TRUE], ], ], ]; return $form; } /** * {@inheritdoc} */ public static function validateElement( &$element, FormStateInterface $form_state ) { $value = $element['#value']; if (empty($element['#ai_validation_enabled'])) { return; } // Call AI validation $ai_client = \Drupal::service('ai.client'); $validation_rules = $element['#ai_validation_rules'] ?? ''; $prompt = <<<EOT Validate the following input against these rules: RULES: {$validation_rules} INPUT: {$value} Respond with JSON: { "valid": true/false, "message": "explanation" } EOT; try { $response = $ai_client->sendMessage([ 'model' => 'claude-3-sonnet', 'messages' => [ ['role' => 'user', 'content' => $prompt], ], ]); $result = json_decode($response['content'], TRUE); if (!$result['valid']) { $form_state->setError( $element, $result['message'] ?? 'Validation failed' ); } } catch (\Exception $e) { \Drupal::logger('ai_webform')->error( 'AI validation error: @message', ['@message' => $e->getMessage()] ); } } }
Real-time Validation via AJAX
<?php public function ajaxValidate(Request $request) { $field_name = $request->query->get('field'); $field_value = $request->query->get('value'); $validation_rules = $request->query->get('rules'); $ai_client = \Drupal::service('ai.client'); $prompt = "Validate: {$field_value}\nAgainst rules: {$validation_rules}"; try { $response = $ai_client->sendMessage([ 'model' => 'claude-3-sonnet', 'messages' => [ ['role' => 'user', 'content' => $prompt], ], ]); return new JsonResponse([ 'valid' => strpos($response['content'], 'valid') !== FALSE, 'message' => $response['content'], ]); } catch (\Exception $e) { return new JsonResponse( ['error' => $e->getMessage()], 500 ); } }
Dynamic Form Content Generation
AI-Powered Field Options
<?php namespace Drupal\ai_webform\Plugin\WebformElement; use Drupal\webform\Plugin\WebformElementBase; class AiGeneratedSelectElement extends WebformElementBase { /** * {@inheritdoc} */ public static function getCompositeElements(array $element) { // Generate options using AI $ai_client = \Drupal::service('ai.client'); $prompt = $element['#ai_options_prompt'] ?? ''; $context = json_encode($element['#ai_context'] ?? []); try { $response = $ai_client->sendMessage([ 'model' => 'claude-3-sonnet', 'messages' => [ [ 'role' => 'user', 'content' => str_replace( '[context]', $context, $prompt ), ], ], ]); $options = json_decode($response['content'], TRUE); return [ '#type' => 'select', '#options' => $options['items'] ?? [], '#empty_option' => $options['empty_label'] ?? '- Select -', ]; } catch (\Exception $e) { return ['#type' => 'select', '#options' => []]; } } }
Configuration Example
# YAML configuration for AI-generated select field service_type: '#type': ai_generated_select '#title': 'Service Type' '#required': true '#ai_options_prompt': | Based on the organization type "[form_context]", generate 5 relevant service categories. Return as JSON: {"items": {"key": "Label"}} '#ai_context': previous_field: organization_type
Integration with Drupal AI Module
Using AI Automators
<?php namespace Drupal\ai_webform\EventSubscriber; use Drupal\ai\Event\AiEventInterface; use Drupal\webform\WebformSubmissionInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; class WebformAiSubscriber implements EventSubscriberInterface { /** * {@inheritdoc} */ public static function getSubscribedEvents() { return [ 'ai.form_submission' => 'onFormSubmission', ]; } /** * Trigger AI automators on webform submission. */ public function onFormSubmission(AiEventInterface $event) { $submission = $event->getData()['submission']; // Trigger automator to populate related fields $automator = \Drupal::service('ai.automator'); $automator->execute('webform_submit', [ 'submission' => $submission->id(), 'data' => $submission->getData(), ]); } }
AI-Powered Email Generation
<?php namespace Drupal\ai_webform\Plugin\WebformHandler; class AiEmailHandler extends WebformHandlerBase { public function preEmailSend( WebformSubmissionInterface $submission, &$message ) { // Generate email body using AI $ai_client = \Drupal::service('ai.client'); $prompt = <<<EOT Generate a professional email response to this form submission: [submission_data] The email should be personalized and acknowledge all key information provided. EOT; try { $response = $ai_client->sendMessage([ 'model' => 'claude-3-sonnet', 'messages' => [ [ 'role' => 'user', 'content' => str_replace( '[submission_data]', json_encode($submission->getData(), JSON_PRETTY_PRINT), $prompt ), ], ], ]); $message['body'][0] = $response['content'] ?? $message['body'][0]; } catch (\Exception $e) { \Drupal::logger('ai_webform')->error( 'AI email generation failed: @message', ['@message' => $e->getMessage()] ); } } }
Advanced: Multi-Step Wizard with AI
<?php namespace Drupal\ai_webform\Plugin\WebformHandler; class AiWizardHandler extends WebformHandlerBase { /** * Determine next wizard step based on AI analysis. */ public function preSave(WebformSubmissionInterface $submission) { $ai_client = \Drupal::service('ai.client'); $data = $submission->getData(); // Analyze current responses $analysis_prompt = <<<EOT Based on these form responses: [form_data] Determine the best next step in the process. Return a JSON object with: { "next_step": "step_name", "reasoning": "explanation", "skip_steps": ["optional", "step", "names"] } EOT; try { $response = $ai_client->sendMessage([ 'model' => 'claude-3-sonnet', 'messages' => [ [ 'role' => 'user', 'content' => str_replace( '[form_data]', json_encode($data, JSON_PRETTY_PRINT), $analysis_prompt ), ], ], ]); $result = json_decode($response['content'], TRUE); // Store wizard navigation in submission $data['ai_next_step'] = $result['next_step'] ?? null; $data['ai_skip_steps'] = $result['skip_steps'] ?? []; $submission->setData($data); } catch (\Exception $e) { \Drupal::logger('ai_webform')->error( 'AI wizard analysis failed: @message', ['@message' => $e->getMessage()] ); } } }
Performance & Caching
Cache AI Responses
<?php $cache_key = 'ai_webform:field_suggestion:' . md5( $prompt . $model . $context ); $cached = \Drupal::cache('ai_webform')->get($cache_key); if ($cached) { return $cached->data; } $response = $ai_client->sendMessage($request); \Drupal::cache('ai_webform')->set( $cache_key, $response, time() + 3600, // 1 hour TTL ); return $response;
Security Considerations
Input Sanitization
<?php // Always sanitize user input before sending to AI $sanitized_input = Xss::filter($user_input); $sanitized_input = strip_tags($sanitized_input); // Validate field presence before inclusion $allowed_fields = array_keys($form['#fields']); $safe_data = array_intersect_key( $submission_data, array_flip($allowed_fields) ); // Send only necessary data to AI service $ai_request = [ 'model' => 'claude-3-sonnet', 'messages' => [ ['role' => 'user', 'content' => $prompt], ], ];
Token Management
<?php // Use OIDC tokens instead of long-lived API keys $oidc_token = \Drupal::service('ai.oidc_provider')->getToken([ 'aud' => 'https://api.anthropic.com', 'scope' => ['text_generation'], ]); $ai_client->authenticate($oidc_token);
Installation & Configuration
Install Required Modules
# Install webform module composer require drupal/webform drush en webform webform_ui -y # Install AI module composer require drupal/ai drush en ai ai_automators -y # Install webform AI integration (custom module) composer require drupal/ai_webform drush en ai_webform -y
Enable AI Handler
- Navigate to:
/admin/structure/webform - Edit your webform
- Go to Handlers tab
- Add handler: AI Processor
- Configure:
- AI Provider (Anthropic, OpenAI, etc.)
- Model (claude-3-sonnet, gpt-4, etc.)
- Enable validation, suggestions, or generation as needed
Configuration File
# config/ai_webform.settings.yml ai_webform: default_provider: 'anthropic' default_model: 'claude-3-sonnet' # Cache settings cache_suggestions: true cache_ttl: 3600 # Request limits max_tokens: 1000 temperature: 0.7 # Logging log_requests: true log_level: 'info'
Troubleshooting
Handler Not Triggering
- Check webform handlers tab:
/admin/structure/webform/manage/[webform]/handlers - Verify handler is enabled (checkbox checked)
- Check logs:
/admin/reports/dblog
AI Service Timeouts
- Increase timeout in
settings.php:$config['ai.settings']['request_timeout'] = 30; - Use streaming for long-running operations
- Implement retry logic with exponential backoff
Memory Issues
- Cache AI responses aggressively
- Batch process large submissions
- Use queues for async operations:
$queue = \Drupal::queue('ai_webform_processing'); $queue->createItem($submission);
Resources
- Webform Module: https://www.drupal.org/project/webform
- AI Module: https://www.drupal.org/project/ai
- Webform Cookbook: https://www.drupal.org/docs/contributed-modules/webform/webform-cookbook
- Drupal Form API: https://api.drupal.org/api/drupal/elements
See Also
Next Steps
- Create Custom Handler: Implement AiProcessorHandler for your form use case
- Setup Provider: Configure AI provider credentials in Drupal settings
- Test Integration: Use AI Explorer to test prompts before deployment
- Monitor Usage: Track AI requests and token consumption
- Optimize Caching: Implement caching for frequently-generated content