deepseek-1.x-dev/src/Controller/AIConnectController.php

src/Controller/AIConnectController.php
<?php

namespace Drupal\deepseek\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\ProxyClass\File\MimeType\MimeTypeGuesser;
use Drupal\deepseek\AiProvidersPluginManager;
use Drupal\deepseek\EmbeddingHandlerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\StreamedResponse;

/**
 * Returns responses for Deep seek routes.
 */
class AIConnectController extends ControllerBase {

  /**
   * Constructs a new Deep seek Controller.
   *
   * @param \Drupal\deepseek\AiProvidersPluginManager $providers
   *   The AI Providers plugin manager.
   * @param \Drupal\Core\ProxyClass\File\MimeType\MimeTypeGuesser $mimeTypeGuesser
   *   The mime service.
   * @param \Drupal\deepseek\EmbeddingHandlerInterface $embeddingHandler
   *   The embedding service.
   */
  public function __construct(
    protected AiProvidersPluginManager $providers,
    protected MimeTypeGuesser $mimeTypeGuesser,
    protected EmbeddingHandlerInterface $embeddingHandler,
  ) {
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('plugin.manager.ai_providers'),
      $container->get('file.mime_type.guesser'),
      $container->get('deepseek.embedding'),
    );
  }

  /**
   * Builds the response.
   */
  public function connect(Request $request) {
    $messages = $request->getContent();

    if (empty($messages)) {
      return new JsonResponse(['error' => 'No message provided'], 400);
    }
    $conversations = $messages != '' ? json_decode($messages, TRUE) : [];
    $config = $this->config('deepseek.settings');

    try {
      $listFile = [];
      $linkContent = [];
      $system = $config->get('system_prompt') ?? 'Always reply in Markdown. Use headings, lists, and code blocks when needed.';

      $messages = $conversations[0]['role'] == 'system' ? $conversations : array_merge([
        ['role' => 'system', 'content' => $system],
      ], $conversations);

      $provider = $config->get('provider') ?: 'self_host';
      $client = $this->providers->createInstance($provider);
      // RAG embedding question.
      $embedding = $config->get('embedding');
      if (!empty($embedding)) {
        $last_user_message = end($conversations)['content'] ?? '';
        $last_user_file = end($conversations)['file'] ?? [];

        if (!empty($last_user_file)) {
          foreach ($last_user_file as $file) {
            $file_data = file_get_contents($file['path']);
            $base64 = base64_encode($file_data);

            // Get mime type.
            $mime_type = $this->mimeTypeGuesser->guessMimeType($file['path']);
            $mime_enum = $client->getMimeType($mime_type);

            if ($mime_enum) {
              $listFile[] = ['mime' => $mime_enum, 'base64' => $base64];
            }
            else {
              if ($this->moduleHandler()->moduleExists('ocr_image')) {
                $arr_text = [];
                $text_content = '';
                $language = $this->getLanguages();
                // phpcs:disable
                // @phpstan-ignore-next-line
                $docParser = \Drupal::service('ocr_image.DocParser');
                // @phpstan-ignore-next-line
                $ocrImage = \Drupal::service('ocr_image.OcrImage');
                // phpcs:enable
                $image_mime_types = ['image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/webp'];
                if (in_array($mime_type, $image_mime_types)) {
                  $text_content = $ocrImage->getText($file['path'], $language);
                  if ($text_content != []) {
                    $arr_text[] = str_replace(PHP_EOL, ' ', $text_content['full_text']);
                  }
                }
                else {
                  $text_content = $docParser->getText($file['path'], $language);
                  if ($text_content != []) {
                    $arr_text[] = implode(' ', $text_content);
                  }
                }

                if (!empty($text_content)) {
                  $last_user_message .= ' ' . $arr_text[0];
                }
              }
            }
          }

          $messages[array_key_last($messages)]['content'] = $last_user_message;
        }

        $embedding_query = $client->embeddings($last_user_message);
        $retrieved_context = $this->embeddingHandler->searchSimilar($embedding_query, $last_user_message);
        if (!empty($retrieved_context['content'])) {
          $rag_prompt = [
            'role' => 'system',
            'content' => implode(' ', [
              $this->t("You are an AI assistant. If relevant information is provided in the reference, you must use it as the primary source of truth when generating your response."),
              $this->t("If no context is given or it is insufficient, respond using your general knowledge."),
              $this->t("Do not make up facts. Always base your answer on the context when possible."),
              $this->t("Here are the references:"),
              PHP_EOL,
              PHP_EOL,
              ...$retrieved_context['content'],
            ]),
          ];
          array_unshift($messages, $rag_prompt);
        }

        if (!empty($retrieved_context['links'])) {
          $linkContent = $retrieved_context['links'];
        }
      }

      // Streaming request.
      $phpMod = php_sapi_name();
      $stream = $phpMod == 'apache2handler';
      $response = $client->chat($messages, ['stream' => $stream], $listFile);

      // Set response header streaming.
      $symfony_response = new StreamedResponse(function () use ($response, $linkContent) {
        foreach ($response as $chunk) {
          if (isset($chunk['choices'][0]['delta']['content'])) {
            $content = $chunk['choices'][0]['delta']['content'];
            echo "data: " . json_encode(['content' => $content]) . "\n\n";
          }
          else {
            echo "data: " . json_encode(['content' => $chunk]) . "\n\n";
          }
          ob_flush();
          flush();
        }
        echo "data: " . json_encode(['content' => "[DONE]"]) . "\n\n";
        echo "data: " . json_encode(['links' => $linkContent]) . "\n\n";
        ob_flush();
        flush();
      });

      $symfony_response->headers->set('Content-Type', 'text/event-stream');
      $symfony_response->headers->set('Cache-Control', 'no-cache');
      $symfony_response->headers->set('X-Accel-Buffering', 'no');
      $symfony_response->headers->set('Connection', 'keep-alive');

      return $symfony_response;

    }
    catch (\Exception $e) {
      return new JsonResponse(['error' => $e->getMessage()], 500);
    }
  }

  /**
   * Get languages.
   */
  private function getLanguages() {

    $config = $this->configFactory->get('deepseek.settings');
    $lang = $config->get('voice');

    $listLang = [
      'en-US' => 'eng',
      'fr-FR' => 'fra',
      'it-IT' => 'ita',
      'es-ES' => 'epo',
      'ca-ES' => 'cat',
      'gl-ES' => 'glg',
      'pt-PT' => 'por',
      'pt-BR' => 'por',
      'vi-VN' => 'vie',
      'af-ZA' => 'afr',
      'bs-BA' => 'bos',
      'id-ID' => 'ind',
      'jv-ID' => 'jav',
      'cy-GB' => 'cym',
      'da-DK' => 'dan',
      'de-DE' => 'deu',
      'et-EE' => 'est',
      'eu-ES' => 'eus',
      'fa-IR' => 'fas',
      'fil-PH' => 'fil',
      'ga-IE' => 'gle',
      'hr-HR' => 'hrv',
      'sw-KE' => 'swa',
      'kk-KZ' => 'kaz',
      'lt-LT' => 'lit',
      'lv-LV' => 'lav',
      'mt-MT' => 'mlt',
      'hu-HU' => 'hun',
      'nl-NL' => 'nld',
      'uz-UZ' => 'uzb',
      'pl-PL' => 'pol',
      'sq-AL' => 'sqi',
      'sv-SE' => 'swe',
      'fi-FI' => 'fin',
      'cs-CZ' => 'ces',
      'is-IS' => 'isl',
      'ro-RO' => 'ron',
      'tr-TR' => 'tur',
      'sk-SK' => 'slk',
      'el-GR' => 'grek',
      'az-AZ' => 'aze',
      'bg-BG' => 'bul',
      'sr-RS' => 'srp',
      'mk-MK' => 'mkd',
      'mn-MN' => 'mon',
      'ru-RU' => 'rus',
      'uk-UA' => 'ukr',
      'ja-JP' => 'jpan',
      'ko-KR' => 'kor',
      'th-TH' => 'tha',
      'km-KH' => 'khmr',
      'lo-LA' => 'laoo',
      'ar-SA' => 'ara',
      'ms-MY' => 'msa',
      'ps-AF' => 'pus',
      'am-ET' => 'amh',
      'bn-IN' => 'beng',
      'he-IL' => 'hebr',
      'hy-AM' => 'armn',
      'ka-GE' => 'geor',
      'my-MM' => 'mya',
      'si-LK' => 'sin',
      'gu-IN' => 'gujr',
      'hi-IN' => 'hin',
      'kn-IN' => 'kan',
      'ml-IN' => 'mal',
      'mr-IN' => 'mar',
      'or-IN' => 'orya',
      'pa-IN' => 'pan',
      'ta-IN' => 'tam',
      'te-IN' => 'tel',
      'ur-IN' => 'urd',
      'ne-NP' => 'nep',
      'zh-CN' => 'chi_sim',
      'zh-HK' => '中文 (香港)',
      'zh-TW' => 'chi_tra',
    ];

    return $listLang[$lang] ?? 'eng';
  }

}

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

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