
Drupal AI: Custom AI Helper module
AI Helper: Streamline Your Drupal AI Chat Integrations
Struggling with repetitive code when using Drupal's AI module for chat operations? Discover how the AI Helper module simplifies your development by streamlining common tasks, letting you focus on unique solutions instead of boilerplate.
Having written a couple of Drush scripts that utilise the Chat operation of the Drupal AI module (e.g. see part 1 in this series of Drupal AI articles) it was clear that certain operations were repeated in each task. I wrote a module, AI Helper, to provide a service that would avoid the need for repeated code.
Code Stages
It made sense to me to separate the code into three stages, each of which will be explained below:
- Pre-processing
- Processing
- Post-processing
1) Pre-processing
The first stage consists of converting the input into the format required for the Chat request and obtaining the Provider and Model.
1a) Prepare input
Working back from the parameters of the Chat request to the simplest input to the service, the logic was as follows:
- The chat() call requires a ChatInput object
- the ChatInput object is composed of an array of ChatMessage objects
- each ChatMessage object requires a role (hard-coded to 'user') and a text string
From that I wrote the following code:
$messages = [];
foreach ($message_texts as $message_text) {
$messages[] = new ChatMessage('user', $message_text);
}
$input = new ChatInput($messages);
1b) Obtain Provider and Model
For this first pass at the code it was assumed that the Chat request would use the default provider/model. Adapting code from the Making simple provider/model selection available section of the "Develop a third-party module" page of the Drupal AI module documentation, I wrote the following code:
$operation_type = 'chat';
$default = $this->aiProvider->getDefaultProviderForOperationType($operation_type);
if (empty($default['provider_id'])) {
throw new AiSetupFailureException('No provider set, please check the AI Default Settings');
}
$provider = $this->aiProvider->createInstance($default['provider_id']);
$model = $default['model_id'];
2) Processing
The processing stage simply consists of the call to chat() based of the variables generated in the pre-processing stage:
$output = $provider->chat($input, $model);
3) Post-processing
The return value of the chat() call is a ChatOutput object. That needs to be processed so that the service can return a simple text string. Again referring to the Drupal AI module documentation, specifically the section Example normalized Chat call, I wrote the following code:
return trim($output->getNormalized()->getText());
Service code structure
I intend for this module to eventually handle other AI calls (e.g. textToImage() ), not just chat(), therefore I extracted common code into a class called AiBase. The only (sub-)stage that could be detached from the specific operation is 1b) Obtain Provider and Model as long as the $operation_type can be provided. This is achieved with the following Interface:
interface AiInterface {
/**
* Get the operation type of the AI interaction.
*
* @return string
* The operation type.
*/
function getOperationType(): string;
}
Rather than write a simple function that would return the $provider and $model variables, I decided a more elegant solution would be to write get() functions for each that would return a property if it is set, otherwise it would call a function that would set both properties. Rather than include a huge code snippet here, you can review the AiBase class on GitHub.
I opted to combine the remaining stages into a single function makeAiRequest():
public function makeAiRequest(array $message_texts, array $tags = []): string {
$messages = [];
foreach ($message_texts as $message_text) {
$messages[] = new ChatMessage('user', $message_text);
}
$input = new ChatInput($messages);
/** @var \Drupal\ai\OperationType\Chat\ChatInterface $provider */
$provider = $this->getProvider();
$output = $provider->chat($input, $this->getModel(), $tags);
return trim($output->getNormalized()->getText());
}
Final words
The source code for this AI helper module is publicly available on GitHub. Anyone is welcome to use it, fork it etc.
I will be writing more articles about my use of the AI module so watch this space.