Skip to main content

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

  1. Navigate to: /admin/structure/webform
  2. Edit your webform
  3. Go to Handlers tab
  4. Add handler: AI Processor
  5. 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

See Also

Next Steps

  1. Create Custom Handler: Implement AiProcessorHandler for your form use case
  2. Setup Provider: Configure AI provider credentials in Drupal settings
  3. Test Integration: Use AI Explorer to test prompts before deployment
  4. Monitor Usage: Track AI requests and token consumption
  5. Optimize Caching: Implement caching for frequently-generated content