Problem/Motivation

It would be nice if this module be compatible with RAG/Vector search AiFunctionCall provided by ai_search module. This function is used by ai assistants / ai chatbot. So, implementing this will allow that chatbot queries be looked up by
solr indexes using this module.

Trying to configure RAG/Vector search using an index that uses this vector, results in nothing because the RAG/Vector search plugin expects there exists data at 'content' extraData

Proposed resolution

Adding the Dense vector fields to the 'content' extraData at the solr_densevector processor.

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

omarlopesino created an issue. See original summary.

omarlopesino’s picture

Title: Compatibility with RAG/Vector Search » Compatibility with AI search "RAG/Vector Search"
kevinquillen’s picture

I spent yesterday looking into this for the first time (RAG tool with AI Chatbot) and in the cases where you want to use a View or different Solr options (parse mode, dismax/edismax, etc) its lost in the tool because its called and executed directly. This was causing me to never get any results.

That caused me to write my own plugin like:

  /**
   * {@inheritdoc}
   */
  public function execute() {
    // Collect the context values.
    $this->searchString = $this->getContextValue('search_string');

    $end_results = [];

    try {
      $view = Views::getView('search');
      $view->setDisplay('sitesearch');
      $view->setExposedInput(['keyword' => $this->searchString]);
      $view->execute();

      $i = 1;

      /** @var \Drupal\views\ResultRow $result */
      foreach ($view->result as $result) {
        /** @var \Drupal\node\NodeInterface $node */
        $node = $result->_entity;
        $url = $node->toUrl()->toString();
        $end_results[] = "#$i: " . $node->getTitle() . " (<a href=\"$url\">visit page</a>)<br>";
        $i++;
        if ($i >= 6) {
          break;
        }
      }
    }
    catch (\Exception $e) {
      $this->setOutput("We're sorry, but I could not search the site. Please give me a few moments and try again.");
      return;
    }
    if (count($end_results)) {
      $output = "I was able to find some relevant content for you! Here are the top results based on what you asked:<br><br>";
      $output .= implode("<br/><br/>", $end_results);
      $output .= "<br/><br/>-----<br/>Didn't find what you were looking for? Try using our more robust <a href='/search'>site search</a>!";
      $this->setOutput($output);
    }
    else {
      $this->setOutput("No results were found when searching in the rag index " . $this->index . " for the following prompt: " . $this->searchString . ".\n");
    }
  }

This produced results. I could also confirm that the event subscriber in this module was also fired, so it performed a RAG search against Solr. These are probably items to report back to the main ai_search module because it can be a little confusing when the RAG tool returns nothing. I see that ai_search module provides its own search backend that creates the content property.

Anyway, if I add that event to the current query event subscriber:

  public function postExtractResults(PostExtractResultsEvent $event): void {
    $results = $event->getSearchApiQuery()->getResults();

    foreach ($results as $result) {
      $result->setExtraData('content', 'Content string here');
    }
  }

I can see the extra data in the result on the FunctionCall plugin I made. Perhaps the shortest solution here is adding on the processor setting to ask the user "which" field should be returned as 'content' in this context.

kevinquillen’s picture

StatusFileSize
new49.76 KB

Something like this perhaps?

screen

kevinquillen’s picture

A more complete example may look something like this:


  /**
   * {@inheritdoc}
   */
  public function postExtractResults(PostExtractResultsEvent $event): void {
    $query = $event->getSearchApiQuery();

    if ($query->getIndex()->isValidProcessor('solr_densevector')) {
      try {
        $processor = $query->getIndex()->getProcessor('solr_densevector');
        $settings = $processor->getConfiguration();

        if (!empty($settings['content_field'])) {
          $results = $event->getSearchApiQuery()->getResults();

          foreach ($results as $result) {
            $field = $result->getField($settings['content_field']);
            $field_values = [];
            
            $values = $field->getValues();

            foreach ($values as $value) {
              $text = ($value instanceof TextValue) ? $value->getText() : $value;
              $text = strip_tags($text);
              $field_values[] = $text;
            }

            $result->setExtraData('content', implode(' ', $field_values));
          }
        }
      }
      catch (\Exception $e) {
         // log here
      }
    }
  }

I tested this using the node title field only.

omarlopesino’s picture

That looks good and easy to configure. I can check this approach in my site, and even creating an MR from #5 so it can be reviewed.

omarlopesino’s picture

Status: Active » Needs review

I have tested the MR based on #5 in a project with search api solr dense vector and an AI chatbot that uses RAG/Vector search and it works fine. Thanks!

Moving to Needs review.

kevinquillen’s picture

I've added some additional touches.

omarlopesino’s picture

Status: Needs review » Reviewed & tested by the community

I have checked it in my project and the data is correctly stored and used by the chatbot. So it works fine.

kevinquillen’s picture

Status: Reviewed & tested by the community » Fixed

Now that this issue is closed, please review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, please credit people who helped resolve this issue.

omarlopesino’s picture

Thank you!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.