Skip to content
Snippets Groups Projects
Commit 529c617a authored by mindaugasd's avatar mindaugasd
Browse files

Issue #3439038 by mindaugasd: Provide default backend replacing example backend

parent d30fd379
No related branches found
No related tags found
No related merge requests found
Pipeline #139827 passed with warnings
<?php
namespace Drupal\aichat\Exception;
class AIChatBackendException extends \Exception {
}
<?php
namespace Drupal\aichat;
/**
* Defines the AI chat message for default backend.
*
* @see \Drupal\aichat\Plugin\AIChatBackend\DefaultBackend
*/
class Message extends MessageBase {
/**
* {@inheritdoc}
*/
public function setResponseData(array $data):void {
parent::setResponseData($data);
// fill this message object with data got from API response
if (empty($this->getId())) {
$this->setId( $data['id'] );
}
$this->setCreated( $data['created'] );
$this->setText( $data['choices'][0]['message']['content'] );
$this->setRole( $data['choices'][0]['message']['role'] );
$this->removeFlag('error');
}
}
<?php
namespace Drupal\aichat\Plugin\AIChatBackend;
use Drupal\aichat\Annotation\AIChatBackend;
use Drupal\aichat\Exception\AIChatBackendException;
use Drupal\aichat\Message;
use Drupal\aichat\MessageInterface;
use Drupal\aichat\Plugin\AIChatBackendBase;
use Drupal\Component\Uuid\UuidInterface;
use Drupal\Core\Annotation\Translation;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\key\KeyRepositoryInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* @AIChatBackend(
* id = "default_backend",
* label = @Translation("AI chat default backend"),
* description = @Translation("Default backend for AI Chat.")
* )
*/
class DefaultBackend extends AIChatBackendBase {
/**
* The UUID service.
*
* @var \Drupal\Component\Uuid\UuidInterface
*/
protected $uuid;
/**
* Keys repository service
*
* @var \Drupal\key\KeyRepositoryInterface
*/
protected $keys;
/**
* {@inheritdoc}
*/
protected $messages;
public function __construct(array $configuration, $plugin_id, $plugin_definition, UuidInterface $uuid, KeyRepositoryInterface $keys) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->uuid = $uuid;
$this->keys = $keys;
$this->messages = [];
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('uuid'),
$container->get('key.repository')
);
}
/**
* {@inheritdoc}
*/
public function createNewMessageObject(): MessageInterface {
return new Message($this);
}
/**
* {@inheritdoc}
*/
public function defineBackendConfig(): array {
return [
'api_key' => ['default' => ''],
'model' => ['default' => 'gpt-4-turbo-preview'],
'system_message' => ['default' => '']
];
}
/**
* {@inheritdoc}
*/
public function buildBackendConfigForm(array $form, FormStateInterface $form_state): array {
$library_installed = class_exists('OpenAI');
$instructions = '';
$instructions .= $this->t('This backend requires openai-php/client library to be installed on the system to be able to connect with OpenAI API. </br>'.
'You can install openai library by writing composer command: <em>@command</em>',
['@command' => 'composer require openai-php/client']) . '</br>';
$library_info = $library_installed ? $this->t('OK, library looks installed') : $this->t('Not installed');
$instructions .= $this->t('Current library status: <strong>@status</strong>',
['@status' => $library_info]);
$form['library_status'] = [
'#type' => 'markup',
'#markup' => '<p>'.$instructions.'</p>'
];
$form['api_key'] = [
'#type' => 'key_select',
'#title' => $this->t('OpenAI API key'),
'#default_value' => $this->getBackendConfigurationValue('api_key')
];
$form['model'] = [
'#type' => 'select',
'#title' => $this->t('Model'),
'#options' => [
'gpt-4-turbo-preview' => 'gpt-4-turbo-preview',
'gpt-3.5-turbo' => 'gpt-3.5-turbo'
],
'#required' => TRUE,
'#default_value' => $this->getBackendConfigurationValue('model')
];
$form['system_message'] = [
'#type' => 'textarea',
'#title' => $this->t('OpenAI system message'),
'#default_value' => $this->getBackendConfigurationValue('system_message'),
'#description' => $this->t('Here you can provide some context information for AI')
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateBackendConfigForm(array $form, FormStateInterface $form_state): void {
$library_installed = class_exists('OpenAI');
if (! $library_installed) {
$form_state->setError($form['library_status'], $this->t('openai-php/client library is not installed.'));
}
}
/**
* {@inheritdoc}
*/
public function loadMessages(): void {
$messages_array = $this->aichat->getDataValue('messages');
if (empty($messages_array)) return;
foreach ($messages_array as $uuid => $values) {
$message = $this->createNewMessageObject();
$message->setValuesFromArray($values);
$this->messages[$uuid] = $message;
}
}
/**
* {@inheritdoc}
*/
public function saveMessage(MessageInterface $message): void {
$uuid = $message->getId();
if (empty($uuid)) {
$uuid = $this->uuid->generate();
$message->setId($uuid);
}
$this->aichat->setDataSubValue('messages', $uuid, $message->toArray());
$this->aichat->save();
}
/**
* {@inheritdoc}
*/
public function sendMessage(MessageInterface $message): array {
// save user message
$message->save();
// create response message
$response_message = $this->createNewMessageObject();
$response_message->setRole('assistant');
return $this->queryOpenAI($response_message);
}
/**
* {@inheritdoc}
*/
public function repeatMessage(MessageInterface $response_message): array {
return $this->queryOpenAI($response_message);
}
/**
* Helper method to query OpenAI.
*/
public function queryOpenAI(MessageInterface $response_message): array {
$messages_array = $this->getMessageHistoryArray();
try {
$client = \OpenAI::factory()
->withApiKey($this->getApiKey())
->withHttpClient(\Drupal::httpClient())
->make();
$response = $client->chat()->create([
'model' => $this->getBackendConfigurationValue('model'),
'messages' => $messages_array
]);
$response_message->setResponseData($response->toArray());
$response_message->save();
return [];
}
catch (\Exception $exception) {
$response_message->setException($exception);
$response_message->save();
return ['warning' => $this->t('There was a problem sending or receiving answer from artificial intelligence.')];
}
}
/**
* Helper method to get API key.
*/
public function getApiKey(): string {
$key_id = $this->getBackendConfigurationValue('api_key');
$key = $this->keys->getKey($key_id);
if (empty($key)) {
throw new AIChatBackendException("OpenAI key is not setup.");
}
$api_key = $key->getKeyValue();
return trim($api_key);
}
/**
* Helper method to get messages history.
*/
public function getMessageHistoryArray(): array {
$history = $this->getMessages(TRUE);
$history_array = [];
$system_message = $this->getBackendConfigurationValue('system_message');
if (!empty($system_message)) {
$history_array[] = [
'role' => 'system',
'content' => $system_message
];
}
foreach ($history as $history_message) {
$content = $history_message->getText();
if (empty($content)) continue;
$history_array[] = [
'role' => $history_message->getRole(),
'content' => $content
];
}
return $history_array;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment