maestro-3.0.1-rc2/modules/maestro_ai_task/maestro_ai_task.module

modules/maestro_ai_task/maestro_ai_task.module
<?php

/**
 * @file
 * maestro_ai_task.module
 */

use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\maestro\Engine\MaestroEngine;
use Drupal\maestro_ai_task\MaestroAiTaskAPI\MaestroAiTaskAPI;
use Drupal\maestro_ai_task\MaestroAiTaskCapabilitiesPluginBase;
use Drupal\webform\Entity\WebformSubmission;



/**
 * Implements hook_entity_delete().
 */
function maestro_ai_task_entity_delete(EntityInterface $entity) {
  // If this is our maestro process entity.
  if ($entity->getEntityTypeId() == 'maestro_process') {
    // Using the maestro AI Task API, delete all of the entities associated with this process.
    $process_id = $entity->process_id->value ?? NULL;
    if($process_id) {
      // For this process, do an entity query for any maestro_ai_storage entities that have a
      // process_id that matches this one and delete it
      $query = \Drupal::entityQuery('maestro_ai_storage')
        ->accessCheck(FALSE)
        ->condition('process_id', $process_id);
      $entity_ids = $query->execute();
      if(count($entity_ids)) {
        $storage_entities = \Drupal::entityTypeManager()->getStorage('maestro_ai_storage')->loadMultiple($entity_ids);
        foreach($storage_entities as $entity) {
          $entity->delete();
        }
      }
    }
  }
}

/**
 * Implements hook_entity_presave().
 */
function maestro_ai_task_entity_presave(EntityInterface $entity) {
  // If this is our maestro template entity.
  if ($entity->getEntityTypeId() == 'maestro_template') {
    $config = \Drupal::config('maestro_ai_task.settings');
    // Check each task in the template for AI tasks and force set some of the core
    // values if they've not been set.
    $tasks = $entity->get('tasks') ?? [];
    foreach ($tasks as $task_id => $task) {
      if ($task['tasktype'] == 'MaestroAITask') {
        // Check if 'ai_prompt' is set.  If not, set it to the base prompt.
        if (!isset($task['data']['ai']['ai_prompt'])) {
          $config = \Drupal::config('maestro_ai_task.settings');
          $base_prompt = $config->get('base_prompt');
          if (empty($base_prompt)) {
            $base_prompt = 'You are a knowledgeable and helpful office administrator who reviews documents.';
          }
          $tasks[$task_id]['data']['ai']['ai_prompt'] = $base_prompt;
          $entity->tasks = $tasks;
        }
      }
    }
  }
}

/**
 * Implements hook_token_info_alter().
 */
function maestro_ai_task_token_info_alter(&$data) {
  // Add the rendering token for the AI task entity.
  $existing_description = $data['tokens']['maestro']['render-entity-identifier']['description'];
  $data['tokens']['maestro']['render-entity-identifier']['description'] = 
    $existing_description . ' ' .
    t('Use the Render Maestro AI Task Entity for Maestro AI Task entities.');

  $data['tokens']['maestro']['render_ai_task_entity'] = [
    'name' => t('Render Maestro AI Task Entity'),
    'description' => 
      t('Renders a Maestro AI task entity. Example: maestro:ai_task_entity:unique_id_ai_task_entity:render_as:optional_process_id. ') . 
      t('"render_as" can be "text", "base_64_image_url" or have a custom written mechanism. "text" should be used for default. "optional_process_id" is only required if you need to explicitly pull from an existing process.') .
      t('If you render an entity and send it to a chatbot or image processor as Base64, you will most likely exceed your AI provider\'s allowable token size.'),
  ];
}

/**
 * Implements hook_tokens().
 */
function maestro_ai_task_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
  $url_options = ['absolute' => TRUE];
  if (isset($options['langcode'])) {
    $url_options['language'] = \Drupal::languageManager()->getLanguage($options['langcode']);
    $langcode = $options['langcode'];
  }
  else {
    $langcode = NULL;
  }
  $replacements = [];

  if ($type == 'maestro' && !empty($data['maestro'])) {
    $queueID = $data['maestro']['queueID'];
    $processID = MaestroEngine::getProcessIdFromQueueId($queueID);
    $replace = '';
    foreach ($tokens as $name => $original) {
      $token_parts = explode(':', $name);
      if (count($token_parts) > 1 && $token_parts[0] == 'render_ai_task_entity') {
        $unique_id = $token_parts[1];
        $render_as = $token_parts[2];
        $processID = $token_parts[3] ?? $processID; // Optional, if not provided, use the process ID from the queue ID.
        $storage_entity = MaestroAiTaskAPI::getAiStorageEntityByUniqueId($unique_id, $processID);
        if ($storage_entity) {
          $entity = current($storage_entity);
          $entity_value = MaestroAiTaskAPI::getAiStorageValueByUniqueId($unique_id, $processID);
          if($entity_value) {
            if($render_as == 'text') {
              $replace = $entity_value;
              $replacements[$original] = $replace;
            }
            elseif($render_as == 'base_64_image_url') {
              $file_info = MaestroAiTaskAPI::convertBase64AiStorageImageToFile($unique_id, $processID);
              if(count($file_info)) {
                $replace = $file_info['file'];
              }
              $replacements[$original] = $replace;
            }
            else {
              // Offload to a maestro ai task hook to render the entity.
              $replace = \Drupal::moduleHandler()->invokeAll('maestro_ai_task_render_ai_entity', [$entity, $render_as, $queueID]);
              $replacements[$original] = $replace;
            }
          }
        }
      }
    }
    
  }
  return $replacements;
}


function maestro_ai_task_form_alter(&$form, &$form_state, $form_id) {
  if($form_id == 'template_builder') {
    $markup = $form['task_legend']['#markup'];
    $markup .= '<div class="legend-task legend-ai-task"></div>' .
        '<div class="legend-information clearfix">' .
        '<div class="legend-information-title">' . t('Maestro AI Task') . '</div>' .
        '<div class="legend-information-explanation">' . t('AI-enabled task allowing Maestro to use AI to execute configured prompts.') . '</div>' .
        '</div>';
    $form['task_legend']['#markup'] = $markup;
    $form['#attached']['library'][] = 'maestro_ai_task/maestro_ai_task_css';
  }
}

/**
 * Implements hook_form_FORM_ID_alter().  
 * We are targetting the template edit task form and managing the AI task capabilities.
 */
function maestro_ai_task_form_template_edit_task_alter(&$form, &$form_state) {
  // Make sure this is a Maestro AI Task.
  $taskID = $form['task_id']['#default_value'] ?? NULL;
  $templateMachineName = $form['template_machine_name']['#default_value'] ?? NULL;
  // getTemplateTaskByID returns FALSE if the templateMachineName is NULL/blank.
  $template_task = MaestroEngine::getTemplateTaskByID($templateMachineName, $taskID);
  if($template_task) {
    $taskType = $template_task['tasktype'] ?? NULL;
    if($taskType == 'MaestroAITask') {
      // See if this is an ajax operation for the ai provider field
      $triggering_element = $form_state->getTriggeringElement();
      if($triggering_element && $triggering_element['#name'] == 'ai_provider') {
        $selected_provider = $triggering_element['#value'];
        $form['ai_provider']['#value'] = $selected_provider;
        $form['ai_provider']['#default_value'] = $selected_provider;
        
        // For potential use in our Maestro AI Task Capabilites plugins, we can 
        // store the template task and template machine name in the form state storage.
        /** @var FormStateInterface $form_state */
        $storage = $form_state->getStorage();
        $storage['templateTask'] = $template_task;
        $storage['templateMachineName'] = $templateMachineName;
        $form_state->setStorage($storage);

        // Blank out the capabilities wrapper
        $form['capabilities_wrapper'] = [
          '#type' => 'fieldset',
          '#prefix' => '<div id="ai-provider-ajax-refresh-wrapper"',
          '#suffix' => '</div>',
        ];
    
        $selected_provider = $form['ai_provider']['#value'];
        $form_state_storage = $form_state->getStorage();
        $task = $form_state_storage['templateTask'] ?? NULL;
        $templateMachineName = $form_state_storage['templateMachineName'] ?? NULL;

        /** @var MaestroAiTaskCapabilitiesPluginBase $maestro_capability */
        $maestro_capability = MaestroAiTaskAPI::createMaestroAiTaskCapabilityPlugin(
          $selected_provider,
          [
            'task' => $task,
            'templateMachineName' => $templateMachineName,
            'form_state' => $form_state, 
            'form' => $form,
          ] 
        );
        if($maestro_capability) {
          // We have a valid Maestro AI Task capability.  Let's execute and add to our capabilities wrapper.
          $ai_capability_form_elements = $maestro_capability->getMaestroAiTaskConfigFormElements();
          
          // Let the chosen Maestro AI Task Capability determine if we can have a response set.
          $allow_return_format = $maestro_capability->allowConfigurableReturnFormat();
          if($allow_return_format) {
            $options = _maestroAiTaskReturnFormatOptions();
            $ai_capability_form_elements['ai_return_format'] = [
              '#type' => 'select',
              '#title' => t('Return format from the AI Call'),
              '#description' => t('Default is JSON as a Yes/No string. See documentation for more examples'),
              '#options' => $options,
              '#required' => TRUE,
              '#default_value' => $task['data']['ai']['ai_return_format'] ?? 'json_yes_no',
            ];

            $ai_capability_form_elements['ai_return_custom_format'] = [
              '#type' => 'textarea',
              '#title' => t('Custom return format'),
              '#default_value' => $task['data']['ai']['ai_return_custom_format'] ?? '',
              '#required' => FALSE,
              '#description' => t('The custom format that you wish to return the information as. <strong>Please note that the return format you specify may not be supported by the AI Configuration you\'ve chosen.</strong>'),
              '#states' => [
                // Show this textfield only if the select box above has 'custom' chosen.
                'visible' => [
                  ':input[name="ai_return_format"]' => ['value' => 'custom'],
                ],
              ],
            ];
          }
          else {
            $ai_capability_form_elements['ai_return_format'] = [
              '#type' => 'hidden',
              '#value' => '',
              '#default_value' => '',
            ];

            $ai_capability_form_elements['ai_return_custom_format'] = [
              '#type' => 'hidden',
              '#value' => '',
              '#default_value' => '',
            ];
          }

          $form['capabilities_wrapper'] += $ai_capability_form_elements;
        }
        
      } // End of ajax operation for the ai provider field
    }
  }
}

/**
 * Internal method that returns our options.
 */
function _maestroAiTaskReturnFormatOptions() {
  return [
    'json_yes_no' => t('Response will be a single JSON encoded string specifying a YES or NO response'),
    'json_true_false' => t('Response will be a single JSON encoded string specifying a TRUE or FALSE response'),
    'custom' => t('A custom response format specified'),
  ];
}

/**
 * Set Process Variable function. Allows you to set a process variable to the 
 * number of items in a field
 *
 * This function requires that the maestro_entity_identifiers entity actually be set
 * with an appropriate entity ID inside of it for a webform submission.
 *
 * @param string $uniqueIdentifier
 *   The unique identifier set by the task for the entity identifiers entity.
 * @param string $field
 *   The field name of the node in question.
 * @param int $queueID
 *   The queue ID of the task calling this function.
 * @param int $processID
 *   The process ID of the tatsk calling this function.
 *
 * @return string
 *   The resulting value that the set process variable custom function requires
 */
function maestro_ai_task_spv_webform_field_count($uniqueIdentifier, $field, $queueID, $processID) {
  $returnValue = 0;

  $entityID = intval(MaestroEngine::getEntityIdentiferByUniqueID($processID, $uniqueIdentifier));
  $webform_submission = WebformSubmission::load($entityID);

  if ($webform_submission) {
    // Submission found.  Get the number of entries on this field.
    $submission_data = $webform_submission->getData();
    if(array_key_exists($field, $submission_data)) {
      $returnValue = count($submission_data[$field]);
    }
  }

  return $returnValue;
}


/**
 * Set Process Variable function. Allows you to set a process variable to the 
 * number of items in a field
 *
 * This function requires that the maestro_entity_identifiers entity actually be set
 * with an appropriate entity ID inside of it for a webform submission.
 *
 * @param string $uniqueIdentifier
 *   The unique identifier set by the task for the entity identifiers entity.
 * @param string $field
 *   The field name of the node in question.
 * @param string $field_property
 *   The field property. This is used for composite fields.
 *  @param string $field_delta
 *   The field delta value to retrieve.
 * @param int $queueID
 *   The queue ID of the task calling this function.
 * @param int $processID
 *   The process ID of the tatsk calling this function.
 *
 * @return string
 *   The resulting value that the set process variable custom function requires
 */
function maestro_ai_task_spv_webform_field_delta_retrieve($uniqueIdentifier, $field, $field_property, $field_delta, $queueID, $processID) {
  $returnValue = NULL;

  $entityID = intval(MaestroEngine::getEntityIdentiferByUniqueID($processID, $uniqueIdentifier));
  $webform_submission = WebformSubmission::load($entityID);

  // Test if the $field_delta is an integer or not.  If not, it's likely the name of a PV.
  if($field_delta != intval($field_delta)) {
    $field_delta = MaestroEngine::getProcessVariable($field_delta, $processID);
  }
  

  if ($webform_submission && $field_delta !== FALSE) {
    // Submission found.  Get the entriy of this field.
    $submission_data = $webform_submission->getData();
    if(array_key_exists($field, $submission_data)) {
      // Now fetch off the field delta value.
      if(is_array($submission_data[$field])) {
        if($field_property != '') {
          $returnValue = $submission_data[$field][$field_delta][$field_property] ?? NULL;
        }
        else {
          $returnValue = $submission_data[$field][$field_delta] ?? NULL;
        }
        
      }
    }
  }
  else {
    $returnValue = 'error';
  }

  return $returnValue;
}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc