Files
crm.clientright.ru/aiassist/fileHandler.php

277 lines
11 KiB
PHP
Raw Normal View History

<?php
// aiassist/fileHandler.php
require_once 'logger.php';
function normalizeFilename($filename) {
$filename = iconv('UTF-8', 'UTF-8//IGNORE', $filename);
return preg_replace('/[^\w\.]+/u', '_', $filename);
}
function cleanQueryText($text) {
$text = preg_replace('/[\x00-\x1F\x7F]/u', ' ', $text);
$text = preg_replace('/\s+/', ' ', $text);
return trim($text);
}
function extractText($filePath) {
$extension = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
if ($extension !== 'pdf') {
return '';
}
$outputFile = tempnam(sys_get_temp_dir(), 'txt_') . '.txt';
$command = "pdftotext " . escapeshellarg($filePath) . " " . escapeshellarg($outputFile);
logMessage("Выполняем команду для извлечения текста: " . $command);
exec($command, $output, $returnVar);
if ($returnVar !== 0) {
logMessage("Ошибка pdftotext: " . implode("\n", $output));
return '';
}
if (!file_exists($outputFile)) {
logMessage("Файл pdftotext не создан: $filePath");
return '';
}
$text = file_get_contents($outputFile);
unlink($outputFile);
$cleanText = trim(preg_replace('/\pC+/u', '', $text));
if (mb_strlen($cleanText, 'UTF-8') < 200) {
logMessage("Извлечённый текст для $filePath слишком короткий или не содержит полезной информации. Запуск OCR.");
return '';
}
logMessage("Извлечённый текст для $filePath (первые 500 символов): " . mb_substr($cleanText, 0, 500, 'UTF-8'));
return $cleanText;
}
function doOCR($filePath) {
logMessage("🔍 Запуск OCR. Сначала конвертируем PDF в изображения: $filePath");
// Создаем временную папку для изображений
$imageDir = sys_get_temp_dir() . "/ocr_images_" . uniqid();
$images = convertPdfToImages($filePath, $imageDir);
if (empty($images)) {
logMessage("❌ Ошибка: Не удалось преобразовать PDF в изображения");
return '';
}
logMessage("✅ PDF преобразован в " . count($images) . " изображений, запускаем OCR");
$outputText = '';
foreach ($images as $image) {
$text = runOCRonImage($image);
$outputText .= "\n" . $text;
}
logMessage("✅ OCR завершен. Извлеченный текст:\n" . mb_substr($outputText, 0, 500, 'UTF-8'));
return trim($outputText);
}
// Функция OCR для обработки каждого изображения
function runOCRonImage($imagePath) {
$outputFile = tempnam(sys_get_temp_dir(), 'ocr_') . '.txt';
$command = "tesseract " . escapeshellarg($imagePath) . " " . escapeshellarg($outputFile) . " -l rus --psm 6 --oem 1";
logMessage("🔍 OCR на изображении: $command");
exec($command, $output, $returnVar);
if ($returnVar !== 0) {
logMessage("❌ Ошибка Tesseract: " . implode("\n", $output));
return '';
}
if (!file_exists($outputFile . ".txt")) {
logMessage("❌ Ошибка: OCR-файл не создан");
return '';
}
$text = file_get_contents($outputFile . ".txt");
unlink($outputFile . ".txt");
return $text;
}
function convertPdfToImages($pdfPath, $outputDir) {
if (!file_exists($pdfPath)) {
logMessage("Файл не существует: $pdfPath");
return [];
}
if (!file_exists($outputDir)) {
mkdir($outputDir, 0777, true);
logMessage("Создана директория для изображений: $outputDir");
}
$imagePattern = $outputDir . '/page-%03d.jpg';
$command = "LC_ALL=en_US.UTF-8 convert -density 300 " . escapeshellarg($pdfPath) . " -quality 90 " . escapeshellarg($imagePattern);
logMessage("Выполняем команду: " . $command);
exec($command . " 2>&1", $output, $returnVar);
if ($returnVar !== 0) {
logMessage("Ошибка при конвертации PDF в изображения.");
return [];
}
return glob($outputDir . '/*.jpg');
}
/*
function checkNSFWWithVision($filePath) {
logMessage("NSFW-проверка через URL " . NSFW_MODERATION_API . " для файла: $filePath");
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => NSFW_MODERATION_API,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
'file' => new CURLFile($filePath)
],
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
'Authorization: Bearer ' . OPENAI_API_KEY
]
]);
$response = curl_exec($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
$curlError = curl_error($curl);
curl_close($curl);
logMessage("Ответ NSFW: HTTP $httpCode - " . $response);
if ($curlError) {
logMessage("Ошибка cURL при проверке NSFW: " . $curlError);
return null;
}
$decoded = json_decode($response, true);
if ($httpCode !== 200 || isset($decoded['detail'])) {
logMessage("Ошибка анализа NSFW: " . json_encode($decoded, JSON_UNESCAPED_UNICODE));
return null;
}
return $decoded['nsfw'] ?? null;
}
*/
/*function checkNSFWWithVision($filePath) {
logMessage("NSFW-проверка через URL https://api.proxyapi.ru/v1/moderation для файла: $filePath");
$curl = curl_init();
curl_setopt_array($curl, [
// CURLOPT_URL => "https://api.proxyapi.ru/v1/moderation",
CURLOPT_URL => NSFW_MODERATION_API,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
'file' => new CURLFile($filePath)
],
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . OPENAI_API_KEY
]
]);
$response = curl_exec($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
$curlError = curl_error($curl);
curl_close($curl);
logMessage("Ответ NSFW: HTTP $httpCode - " . $response);
if ($curlError) {
logMessage("Ошибка cURL при проверке NSFW: " . $curlError);
return null;
}
$decoded = json_decode($response, true);
// Проверяем статус ответа
if ($httpCode !== 200 || isset($decoded['detail'])) {
logMessage("Ошибка анализа NSFW: " . json_encode($decoded, JSON_UNESCAPED_UNICODE));
return null;
}
return $decoded['data'][0]['nsfw'] ?? null;
}
*/
function checkNSFWWithVision($filePath) {
logMessage("⚠️ NSFW-проверка отключена для файла: $filePath");
return null;
}
/**
* Формирует объединённое сообщение по документам.
* Если текст можно извлечь напрямую, то передаётся только ссылка (ID файла).
* Если не удалось извлечь текст, используется OCR, и включается полученный текст.
* Также производится проверка на цензуру.
*
* @param array $documents Массив документов (с полями 'filepath' и 'title').
* @param array $uploadedFileIds Ассоциативный массив с путями к файлам => их идентификаторами.
* @return string Объединённое сообщение для передачи в GPT.
*/
function analyzeDocuments($documents, $uploadedFileIds) {
$combinedMessages = [];
foreach ($documents as $doc) {
if (empty($doc['filepath'])) {
logMessage("Неверный путь: " . json_encode($doc, JSON_UNESCAPED_UNICODE));
continue;
}
// Получаем fileID, полученный при загрузке
$fileId = $uploadedFileIds[$doc['filepath']] ?? еизвестный_ID';
// Выполняем проверку на NSFW
$nsfw = checkNSFWWithVision($doc['filepath']);
if ($nsfw !== null && $nsfw > 0.8) {
$messageBlock = "Документ: " . $doc['title'] . "\n";
$messageBlock .= "Файл (ID: " . $fileId . ") не прошёл проверку цензуры.\n";
} else {
// Пытаемся извлечь текст напрямую
$text = extractText($doc['filepath']);
if (!empty($text)) {
// Если файл читаемый, передаём только ссылку (ID файла)
$messageBlock = "Документ: " . $doc['title'] . "\n";
$messageBlock .= "Файл передан с ID: " . $fileId . ". Текст не загружен, так как файл читаемый.\n";
} else {
// Если не удалось извлечь текст, пробуем OCR
$ocrText = doOCR($doc['filepath']);
if (!empty($ocrText)) {
$messageBlock = "Документ: " . $doc['title'] . "\n";
$messageBlock .= "Файл передан с ID: " . $fileId . ". OCR текст:\n" . $ocrText . "\n";
} else {
$messageBlock = "Документ: " . $doc['title'] . "\n";
$messageBlock .= "Файл передан с ID: " . $fileId . ". Не удалось извлечь текст.\n";
}
}
}
$combinedMessages[] = $messageBlock;
}
return implode("\n-----------------\n", $combinedMessages);
}
/**
* Создает Vector Store, загружает файлы и возвращает массив с идентификаторами.
*
* @param array $filePaths Массив путей к файлам.
* @return array|null Массив с ключами 'vectorStoreId' и 'fileIds' (ассоциативный массив [путь => fileID]), или null при ошибке.
*/
function createVectorStoreAndUploadFiles($filePaths) {
logMessage("Создание Vector Store и загрузка файлов...");
$vectorStoreId = createVectorStore();
if (!$vectorStoreId) {
logMessage("Ошибка: не удалось создать Vector Store.");
return null;
}
$uploadedFiles = [];
foreach ($filePaths as $filePath) {
logMessage("Загрузка файла: $filePath");
if (!file_exists($filePath)) {
logMessage("Ошибка: Файл не существует: $filePath");
continue;
}
$fileId = uploadFileToOpenAI($filePath);
if (!$fileId) {
logMessage("Ошибка загрузки файла: $filePath");
continue;
}
if (!addFileToVectorStore($vectorStoreId, $fileId)) {
logMessage("Ошибка добавления файла в Vector Store: $filePath");
} else {
logMessage("Файл успешно добавлен в Vector Store: $filePath");
$uploadedFiles[$filePath] = $fileId;
}
}
return ['vectorStoreId' => $vectorStoreId, 'fileIds' => $uploadedFiles];
}
?>