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

277 lines
11 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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];
}
?>