Files
crm.clientright.ru/aigptassist.php
Fedor ac7467f0b4 Major CRM updates: AI Assistant, Court Status API, S3 integration improvements, and extensive file storage system
- Added comprehensive AI Assistant system (aiassist/ directory):
  * Vector search and embedding capabilities
  * Typebot proxy integration
  * Elastic search functionality
  * Message classification and chat history
  * MCP proxy for external integrations

- Implemented Court Status API (GetCourtStatus.php):
  * Real-time court document status checking
  * Integration with external court systems
  * Comprehensive error handling and logging

- Enhanced S3 integration:
  * Improved file backup system with metadata
  * Batch processing capabilities
  * Enhanced error logging and recovery
  * Copy operations with URL fixing

- Added Telegram contact creation API
- Improved error logging across all modules
- Enhanced callback system for AI responses
- Extensive backup file storage with timestamps
- Updated documentation and README files

- File storage improvements:
  * Thousands of backup files with proper metadata
  * Fix operations for broken file references
  * Project-specific backup and recovery systems
  * Comprehensive file integrity checking

Total: 26,461+ files added/modified including AWS SDK, vendor dependencies, and extensive backup system.
2025-10-16 11:17:21 +03:00

788 lines
34 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
ini_set('log_errors', 1);
ini_set('error_log', 'logs/php_errors.log');
define('LOG_FILE', 'logs/HybridPDFProcessor.log');
define('OPENAI_API_KEY', 'sk-GS24OxHQYfq8ErW5CRLoN5F1CfJPxNsY');
define('OPENAI_API_URL', 'https://api.proxyapi.ru/openai/v1/chat/completions');
define('OPENAI_ASSISTANT_API', 'https://api.proxyapi.ru/openai/v1/assistants');
define('OPENAI_FILES_API', 'https://api.proxyapi.ru/openai/v1/files');
define('OPENAI_THREADS_API', 'https://api.proxyapi.ru/openai/v1/threads');
define('OPENAI_VECTOR_STORES_API', 'https://api.proxyapi.ru/openai/v1/vector_stores');
// Для Vision используем тот же endpoint через ProxyAPI, как в code4
define('OPENAI_VISION_API', 'https://api.proxyapi.ru/v1/chat/completions');
define('ASSISTANT_ID', 'asst_suGt51aoepXUkJiC0t3vobeG');
define('ASSISTANT_NAME', 'Clientright');
// Подключение к БД
$dsn = 'mysql:host=localhost;port=3306;dbname=ci20465_72new;charset=utf8mb4';
$user = 'ci20465_72new';
$password = 'EcY979Rn';
try {
$pdo = new PDO($dsn, $user, $password, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
} catch (PDOException $e) {
logMessage("Ошибка подключения к БД: " . $e->getMessage());
die(json_encode(['status' => 'error', 'message' => 'Database connection error']));
}
// Основная логика обработки POST запроса
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$input = json_decode(file_get_contents('php://input'), true);
$id = $input['id'] ?? null;
if (!$id || !is_numeric($id)) {
logMessage("Ошибка: Некорректный ID");
die(json_encode(['status' => 'error', 'message' => 'Invalid ID']));
}
try {
// Получаем данные документов
$documents = fetchDocumentData($pdo, $id);
if (!$documents) {
throw new Exception("Documents not found");
}
// Создаем массив для хранения результатов обработки
$processedDocuments = [];
// Обрабатываем каждый документ
foreach ($documents as $document) {
logMessage("Обработка документа: " . $document['title']);
// Создаем тред для документа
$threadId = createThread();
if (!$threadId) {
logMessage("Ошибка создания треда для документа: " . $document['title']);
continue;
}
// Загружаем файл в OpenAI
$fileId = uploadFileToOpenAI($document['filepath']);
if (!$fileId) {
logMessage("Ошибка загрузки файла для документа: " . $document['title']);
continue;
}
// Анализируем документ
$analysis = analyzeDocumentWithAssistant($threadId, ASSISTANT_ID, $fileId, $document['content']);
if ($analysis) {
$processedDocuments[] = [
'title' => $document['title'],
'analysis' => $analysis
];
}
}
// Формируем итоговый ответ
echo json_encode([
'status' => 'success',
'documents' => $processedDocuments
], JSON_UNESCAPED_UNICODE);
} catch (Exception $e) {
logMessage("Ошибка: " . $e->getMessage());
die(json_encode(['status' => 'error', 'message' => $e->getMessage()]));
}
} else {
die(json_encode(['status' => 'error', 'message' => 'Method not allowed']));
}
function logMessage($message) {
if (!is_dir('logs')) {
mkdir('logs', 0777, true);
}
$message = mb_convert_encoding($message, 'UTF-8', 'auto');
file_put_contents(LOG_FILE, date('Y-m-d H:i:s') . " - " . $message . "\n", FILE_APPEND | LOCK_EX);
}
// Функция транслитерации (для переименования файлов)
function transliterate($text) {
$cyr = ['а','б','в','г','д','е','ё','ж','з','и','й','к','л','м','н','о','п','р','с','т','у','ф','х','ц','ч','ш','щ','ъ','ы','ь','э','ю','я'];
$lat = ['a','b','v','g','d','e','yo','zh','z','i','y','k','l','m','n','o','p','r','s','t','u','f','h','ts','ch','sh','shch','','y','','e','yu','ya'];
return str_replace($cyr, $lat, mb_strtolower($text));
}
function renameFileForProcessing($filePath, $targetFolder = "scanpdf") {
$pathInfo = pathinfo($filePath);
$newName = transliterate($pathInfo['filename']) . "_" . uniqid() . "." . $pathInfo['extension'];
$newPath = rtrim($targetFolder, "/") . "/" . $newName;
if (copy($filePath, $newPath)) {
logMessage("Файл переименован: $filePath -> $newPath");
return $newPath;
} else {
logMessage("Ошибка при копировании файла: $filePath");
return null;
}
}
// Извлечение текста из PDF с помощью pdftotext
function extractTextFromPDF($pdfPath) {
$text = shell_exec("pdftotext -layout -enc UTF-8 " . escapeshellarg($pdfPath) . " -");
$text = mb_convert_encoding($text, 'UTF-8', 'auto');
logMessage("DEBUG: Извлеченный текст (начало): " . substr($text, 0, 500));
return trim($text);
}
// Конвертация PDF в изображения (ImageMagick convert)
function convertPdfToImages($pdfPath, $outputDir) {
if (!file_exists($outputDir)) {
mkdir($outputDir, 0777, true);
logMessage("Создана директория для изображений: $outputDir");
}
$imagePattern = $outputDir . '/page-%03d.jpg';
$command = "convert -density 300 " . escapeshellarg($pdfPath) . " -quality 90 " . escapeshellarg($imagePattern);
logMessage("Выполняем команду конвертации: " . $command);
exec($command . " 2>&1", $output, $returnVar);
logMessage("DEBUG: Вывод convert: " . implode("\n", $output));
if ($returnVar !== 0) {
logMessage("Ошибка при конвертации PDF в изображения.");
return [];
}
return glob($outputDir . '/*.jpg');
}
// Удаление рабочей папки и ее содержимого
function deleteFolderAndContents($folderPath) {
if (is_dir($folderPath)) {
$files = array_diff(scandir($folderPath), array('.', '..'));
foreach ($files as $file) {
$filePath = $folderPath . DIRECTORY_SEPARATOR . $file;
if (is_dir($filePath)) {
deleteFolderAndContents($filePath);
} else {
unlink($filePath);
}
}
rmdir($folderPath);
logMessage("Удалена директория: $folderPath");
}
}
// Запуск OCR с Tesseract
function doOCR($filePath) {
logMessage("Запуск OCR для файла: $filePath");
$outputFile = tempnam(sys_get_temp_dir(), 'ocr_') . '.txt';
$command = "tesseract " . escapeshellarg($filePath) . " " . escapeshellarg($outputFile) . " -l rus+eng";
exec($command, $output, $returnVar);
if ($returnVar !== 0 || !file_exists($outputFile . ".txt")) {
logMessage("Ошибка OCR для файла: $filePath");
return '';
}
$text = file_get_contents($outputFile . ".txt");
unlink($outputFile . ".txt");
return trim($text);
}
// Запуск NudeNet для NSFW-проверки изображения
function classifyImage($imagePath) {
$absolutePath = realpath($imagePath);
if (!$absolutePath) {
logMessage("ERROR: Не удалось получить абсолютный путь для " . $imagePath);
return [];
}
logMessage("DEBUG: Абсолютный путь для классификации: " . $absolutePath);
$escapedPath = escapeshellarg($absolutePath);
$command = "python3 -c \"import json; from nudenet import NudeClassifier; classifier = NudeClassifier(); print(json.dumps(classifier.classify($escapedPath)))\"";
logMessage("DEBUG: Выполнение команды NudeNet: " . $command);
$output = shell_exec($command);
logMessage("DEBUG: Вывод NudeNet: " . $output);
if ($output === null) {
logMessage("ERROR: shell_exec вернул null при выполнении NudeClassifier");
return [];
}
return json_decode(trim($output), true);
}
// Локальная NSFW-проверка с использованием NudeNet
function checkNSFWLocally($filePath) {
logMessage("Запуск локальной проверки NSFW для файла: $filePath");
$extension = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
$imageToCheck = $filePath;
if ($extension === 'pdf') {
$outputImage = tempnam(sys_get_temp_dir(), 'pdf_img_') . '.png';
$command = "convert -density 150 " . escapeshellarg($filePath) . "[0] -quality 90 " . escapeshellarg($outputImage);
exec($command, $output, $returnVar);
if ($returnVar !== 0) {
logMessage("Ошибка конвертации PDF в изображение для NSFW проверки.");
return null;
}
$imageToCheck = $outputImage;
}
$classification = classifyImage($imageToCheck);
if (empty($classification)) {
logMessage("DEBUG: Нет данных NSFW для изображения '$imageToCheck'.");
return false;
}
$absImagePath = realpath($imageToCheck);
if (isset($classification[$absImagePath])) {
$unsafeProbability = $classification[$absImagePath]['unsafe'] ?? 0;
logMessage("DEBUG: unsafeProbability для '$absImagePath' = " . $unsafeProbability);
return $unsafeProbability > 0.8;
}
logMessage("DEBUG: Классификатор не вернул данные для '$absImagePath'.");
return false;
}
// Stub для получения контекста из базы знаний
function getKnowledgeBaseContext() {
return "Статическая информация: нормы и законы РФ, судебные прецеденты...";
}
// Функции для работы с OpenAI Assistants (как в code4)
function uploadFileToOpenAI($filePath) {
logMessage("Загрузка файла в OpenAI: $filePath");
$ch = curl_init(OPENAI_FILES_API);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
'file' => new CURLFile($filePath),
'purpose' => 'assistants'
],
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . OPENAI_API_KEY,
'OpenAI-Beta: assistants=v2'
]
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
logMessage("Ответ OpenAI (загрузка файла): HTTP $httpCode - " . $response);
$decoded = json_decode($response, true);
if ($httpCode !== 200 || !isset($decoded['id'])) {
logMessage("Ошибка при загрузке файла: " . json_encode($decoded, JSON_UNESCAPED_UNICODE));
return null;
}
return $decoded['id'];
}
function createVectorStore() {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => OPENAI_VECTOR_STORES_API,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(['name' => 'Vector Store']),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . OPENAI_API_KEY,
'OpenAI-Beta: assistants=v2'
]
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
if ($curlError) {
logMessage("Ошибка cURL при создании Vector Store: " . $curlError);
return null;
}
logMessage("Ответ OpenAI (создание Vector Store): HTTP $httpCode - " . $response);
$decoded = json_decode($response, true);
if ($httpCode !== 200 || !isset($decoded['id'])) {
logMessage("Ошибка при создании Vector Store: " . json_encode($decoded, JSON_UNESCAPED_UNICODE));
return null;
}
return $decoded['id'];
}
function addFileToVectorStore($vectorStoreId, $fileId) {
$ch = curl_init(OPENAI_VECTOR_STORES_API . "/$vectorStoreId/files");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(['file_id' => $fileId]),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . OPENAI_API_KEY,
'OpenAI-Beta: assistants=v2'
]
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
logMessage("Ответ OpenAI (добавление файла в Vector Store): HTTP $httpCode - " . $response);
$decoded = json_decode($response, true);
if ($httpCode !== 200 || !isset($decoded['id'])) {
logMessage("Ошибка добавления файла: " . json_encode($decoded, JSON_UNESCAPED_UNICODE));
return false;
}
return true;
}
function updateAssistantWithVectorStore($vectorStoreId) {
$data = [
'tool_resources' => [
'file_search' => [
'vector_store_ids' => [$vectorStoreId]
]
]
];
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => OPENAI_ASSISTANT_API . "/" . ASSISTANT_ID,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . OPENAI_API_KEY,
'OpenAI-Beta: assistants=v2'
]
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
if ($curlError) {
logMessage("Ошибка обновления ассистента: " . $curlError);
return false;
}
logMessage("Ответ OpenAI (обновление ассистента): HTTP $httpCode - " . $response);
$decoded = json_decode($response, true);
if ($httpCode !== 200 || !isset($decoded['id'])) {
logMessage("Ошибка обновления ассистента: " . json_encode($decoded, JSON_UNESCAPED_UNICODE));
return false;
}
return true;
}
function analyzeDocumentWithAssistant($threadId, $assistantId, $fileId, $content) {
logMessage("Анализ документа: thread_id=$threadId, fileId=$fileId");
$messageContent = "Проанализируй документ";
if (!empty($fileId)) {
$messageContent .= " (file_id: $fileId)";
}
$messageContent .= ". Содержимое для анализа:\n" . $content;
$messageData = [
'role' => 'user',
'content' => $messageContent
];
// Отправляем сообщение в тред
$ch = curl_init(OPENAI_THREADS_API . "/$threadId/messages");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($messageData),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . OPENAI_API_KEY,
'OpenAI-Beta: assistants=v2'
]
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
logMessage("Ответ (сообщение): HTTP $httpCode - " . $response);
$decoded = json_decode($response, true);
if ($httpCode !== 200 || !isset($decoded['id'])) {
logMessage("Ошибка отправки сообщения: " . json_encode($decoded, JSON_UNESCAPED_UNICODE));
return null;
}
// Запуск ассистента
$runData = ['assistant_id' => $assistantId];
$ch = curl_init(OPENAI_THREADS_API . "/$threadId/runs");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($runData),
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . OPENAI_API_KEY,
'OpenAI-Beta: assistants=v2'
]
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
logMessage("Ответ (запуск ассистента): HTTP $httpCode - " . $response);
$decodedRun = json_decode($response, true);
if ($httpCode !== 200 || !isset($decodedRun['id'])) {
logMessage("Ошибка запуска ассистента: " . json_encode($decodedRun, JSON_UNESCAPED_UNICODE));
return null;
}
$runId = $decodedRun['id'];
$maxIterations = 30;
$iterations = 0;
do {
sleep(2);
$iterations++;
$ch = curl_init(OPENAI_THREADS_API . "/$threadId/runs/$runId");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . OPENAI_API_KEY,
'OpenAI-Beta: assistants=v2'
]
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
logMessage("Статус запуска (итерация $iterations): HTTP $httpCode - " . $response);
$decodedStatus = json_decode($response, true);
$status = $decodedStatus['status'] ?? null;
if ($iterations >= $maxIterations) {
logMessage("Превышен максимальный таймаут ожидания работы ассистента.");
return null;
}
} while ($status === 'queued' || $status === 'in_progress');
if ($status !== 'completed') {
logMessage("Запуск ассистента завершился с ошибкой: $status");
return null;
}
// Получаем результат
$ch = curl_init(OPENAI_THREADS_API . "/$threadId/messages");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . OPENAI_API_KEY,
'OpenAI-Beta: assistants=v2'
]
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
logMessage("Ответ (сообщения): HTTP $httpCode - " . $response);
$decodedMessages = json_decode($response, true);
if ($httpCode !== 200 || !isset($decodedMessages['data'])) {
logMessage("Ошибка получения сообщений: " . json_encode($decodedMessages, JSON_UNESCAPED_UNICODE));
return null;
}
logMessage("Результаты анализа: " . json_encode($decodedMessages['data'], JSON_UNESCAPED_UNICODE));
return $decodedMessages['data'];
}
function generateReport($allResults) {
if (empty($allResults)) {
logMessage("Ошибка: Нет данных для отчета");
return "Ошибка: Нет данных для отчета";
}
$report = "### Итоговый отчет по документам\n\n";
foreach ($allResults as $result) {
$report .= "**Документ:** " . $result['document'] . "\n";
$report .= "**Статус:** " . $result['status'] . "\n";
if (isset($result['analysis'])) {
$report .= "**Анализ:** " . json_encode($result['analysis'], JSON_UNESCAPED_UNICODE) . "\n";
} else {
$report .= "**Сообщение:** " . $result['message'] . "\n";
}
$report .= "\n";
}
return $report;
}
// Основной процесс: получение ID из POST-запроса, выбор файлов из БД и обработка
$input = json_decode(file_get_contents('php://input'), true);
$id = $input['id'] ?? null;
if (!$id || !is_numeric($id)) {
logMessage("Ошибка: Некорректный ID.");
die(json_encode(["status" => "error", "message" => "Некорректный ID."]));
}
/* $id = $_POST['id'] ?? null;
if (!$id || !is_numeric($id)) {
logMessage("Ошибка: Некорректный ID.");
die(json_encode(["status" => "error", "message" => "Некорректный ID."]));
} */
$dbconfig = [
'db_server' => 'localhost',
'db_port' => '3306',
'db_username' => 'ci20465_72new',
'db_password' => 'EcY979Rn',
'db_name' => 'ci20465_72new'
];
$conn = new mysqli($dbconfig['db_server'], $dbconfig['db_username'], $dbconfig['db_password'], $dbconfig['db_name'], $dbconfig['db_port']);
if ($conn->connect_error) {
logMessage("Ошибка подключения к БД: " . $conn->connect_error);
die(json_encode(["status" => "error", "message" => "Ошибка подключения к БД."]));
}
$conn->set_charset("utf8mb4");
$stmt = $conn->prepare("
SELECT n.title,
CASE WHEN a.storedname IS NOT NULL
THEN CONCAT(a.path, a.attachmentsid, '_', a.storedname)
ELSE CONCAT(a.path, a.attachmentsid, '_', a.name)
END AS filepath
FROM vtiger_senotesrel r
LEFT JOIN vtiger_notes n ON n.notesid = r.notesid
LEFT JOIN vtiger_crmentity e ON e.crmid = r.notesid
LEFT JOIN vtiger_seattachmentsrel r2 ON r2.crmid = r.notesid
LEFT JOIN vtiger_attachments a ON a.attachmentsid = r2.attachmentsid
WHERE r.crmid = ? AND e.deleted = 0
AND (a.type = 'application/pdf' OR a.type = 'application/octet-stream')
");
$stmt->bind_param("i", $id);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows == 0) {
logMessage("Ошибка: Нет данных в БД.");
die(json_encode(["status" => "error", "message" => "Нет данных."]));
}
$uniqueFolder = "scanpdf/" . uniqid('run_', true);
if (!file_exists($uniqueFolder)) {
mkdir($uniqueFolder, 0777, true);
logMessage("Создана рабочая папка: " . $uniqueFolder);
}
$files_data = [];
while ($row = $result->fetch_assoc()) {
$title = $row["title"];
$filePath = $row["filepath"];
logMessage("Обрабатываем файл: " . $filePath);
$tempFilePath = renameFileForProcessing($filePath, $uniqueFolder);
if (!$tempFilePath) continue;
$data = ["title" => $title];
$text = extractTextFromPDF($tempFilePath);
if (!empty(trim($text)) && mb_strlen(trim($text), 'UTF-8') >= 100) {
logMessage("Текст извлечён из '$title'.");
$data["text"] = $text;
} else {
logMessage("Текст не извлечён из '$title', конвертируем в изображения.");
$outputDir = $uniqueFolder . "/pdf_images_" . uniqid();
$images = convertPdfToImages($tempFilePath, $outputDir);
if (!empty($images)) {
$recognizedText = "";
$data["nsfw_alert"] = false;
foreach ($images as $img) {
if (checkNSFWLocally($img)) {
$data["nsfw_alert"] = true;
logMessage("NSFW обнаружен в изображении: " . $img);
break;
}
$ocrText = doOCR($img);
if (!empty(trim($ocrText))) {
$recognizedText .= $ocrText . "\n";
}
}
if (isset($data["nsfw_alert"]) && $data["nsfw_alert"] === true) {
unset($data["text"]);
$data["images"] = $images;
} else {
if (!empty(trim($recognizedText)) && mb_strlen(trim($recognizedText), 'UTF-8') >= 100) {
$data["text"] = $recognizedText;
} else {
$data["images"] = $images;
}
}
} else {
logMessage("Ошибка: Изображения не созданы для '$title'.");
}
}
$data["moderation_passed"] = isset($data["nsfw_alert"]) && $data["nsfw_alert"] === true ? false : (isset($data["text"]) && !empty(trim($data["text"])) ? true : false);
$files_data[] = $data;
}
$stmt->close();
$conn->close();
logMessage("Данные для анализа: " . json_encode($files_data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
// Загрузка файлов в Vector Store (используем методы из code4)
function createVectorStoreAndUploadFiles($files_data) {
$vectorStoreId = createVectorStore();
if (!$vectorStoreId) return null;
$uploadedFiles = [];
foreach ($files_data as $data) {
if (!isset($data["filepath"])) continue;
$filePath = $data["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];
}
$uploadResult = createVectorStoreAndUploadFiles($files_data);
if (!$uploadResult) {
logMessage("Ошибка создания Vector Store или загрузки файлов");
die(json_encode(["status" => "error", "message" => "Ошибка создания Vector Store или загрузки файлов"]));
}
$vectorStoreId = $uploadResult['vectorStoreId'];
$uploadedFileIds = $uploadResult['fileIds'];
if (!updateAssistantWithVectorStore($vectorStoreId)) {
logMessage("Ошибка обновления ассистента с Vector Store");
die(json_encode(["status" => "error", "message" => "Ошибка обновления ассистента"]));
}
// Формирование итогового запроса для OpenAI
$task = "🔹 Отвечай по шаблону. **Задача**:
Проанализируй загруженные документы, выполнив следующие действия:
1⃣ **Список файлов и проверка соответствия названий**
- Перечисли все загруженные файлы.
- Внимательно анализируй изображения.
- Если изображение содержит текст, сначала извлеки текст и проанализируй его содержание.
- Укажи, если текст плохо читается или частично распознан.
- Если изображение не содержит текста, опиши, что на нем изображено.
- Обрати внимание на пометку nsfw_alert. Если она true, файл требует ручной проверки.
2⃣ **Краткий анализ спора**
- Определи истца (потребителя) и ответчика (компанию, на которую подана жалоба).
- Опиши суть спора и основные аргументы сторон.
3⃣ **Проверка на цензуру**
- Проверь документы на наличие ненормативной лексики и нецензурных изображений.
4⃣ **Выдача итогового вердикта**
- Если всё соответствует, укажи: \"Вердикт: Прошло модерацию.\"
- Если есть проблемы, укажи, что требуется ручная проверка.
5⃣ **Характер спора**
- Дай краткую характеристику дела (например, \"возврат денег\", \"некачественная услуга\" и т.д.).
6⃣ **Вероятность положительного решения спора**
- Укажи вероятность в процентах.
7⃣ **Чего не хватает**
- Запроси дополнительные документы, если необходимо.
📌 **Важно**: Отчет должен быть структурированным, четким и лаконичным, с указанием норм права РФ, регулирующих данный спор, и приоритетных файлов для ручной проверки.";
$finalContent = "";
foreach ($files_data as $file) {
$finalContent .= json_encode($file, JSON_UNESCAPED_UNICODE) . "\n";
}
$payload = [
"model" => "o1",
"messages" => [
["role" => "system", "content" => "Ты юридический аналитик. Проанализируй материалы согласно инструкции."],
["role" => "user", "content" => $task],
["role" => "user", "content" => $finalContent]
],
"max_completion_tokens" => 4000
];
$payload_json = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
logMessage("Отправка запроса в OpenAI: " . $payload_json);
$ch = curl_init(OPENAI_API_URL);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload_json,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer ' . OPENAI_API_KEY
]
]);
$response = curl_exec($ch);
curl_close($ch);
logMessage("Ответ от OpenAI: " . $response);
$gptAnalysis = json_decode($response, true);
if (!is_array($gptAnalysis)) {
logMessage("Ошибка: JSON-декодирование не удалось.");
die(json_encode(["status" => "error", "message" => "Ошибка: JSON-декодирование не удалось."]));
}
if (!isset($gptAnalysis['choices']) || empty($gptAnalysis['choices'])) {
logMessage("Ошибка: в JSON-ответе отсутствует ключ 'choices'.");
die(json_encode(["status" => "error", "message" => "Ошибка: в JSON-ответе отсутствует ключ 'choices'."]));
}
$content = $gptAnalysis['choices'][0]['message']['content'] ?? null;
if (!$content) {
logMessage("Ошибка: контент не найден в ответе от GPT.");
die(json_encode(["status" => "error", "message" => "Ошибка: контент не найден в ответе от GPT."]));
}
if (preg_match('/Вердикт:\s*(Прошло модерацию|Не прошло модерацию)/ui', $content, $matches)) {
$moderationVerdict = trim($matches[1]);
} else {
$moderationVerdict = "";
}
logMessage("Извлеченный вердикт модерации: " . ($moderationVerdict ?: "Не найден"));
$final_output = [
"status" => "complete",
"analysis" => $content,
"moderationVerdict" => $moderationVerdict
// "files_data" => $files_data
];
logMessage("Финальный вывод: " . json_encode($final_output, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
echo json_encode($final_output, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
// Очистка: удаление рабочей папки
deleteFolderAndContents($uniqueFolder);
function fetchDocumentData($pdo, $id) {
try {
// Изменяем SQL-запрос для корректного получения данных
$stmt = $pdo->prepare("
SELECT DISTINCT
n.title,
n.content,
CONCAT(a.path, a.attachmentsid, '_', COALESCE(a.storedname, a.name)) AS filepath
FROM vtiger_notes n
INNER JOIN vtiger_crmentity e ON e.crmid = n.notesid
INNER JOIN vtiger_senotesrel snr ON snr.notesid = n.notesid
LEFT JOIN vtiger_seattachmentsrel sar ON sar.crmid = n.notesid
LEFT JOIN vtiger_attachments a ON a.attachmentsid = sar.attachmentsid
WHERE snr.crmid = ?
AND e.deleted = 0
");
// Логируем SQL запрос
logMessage("SQL запрос: " . $stmt->queryString);
$stmt->execute([$id]);
$documents = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Логируем результат запроса
logMessage("Результат запроса: " . print_r($documents, true));
if (empty($documents)) {
logMessage("Документы не найдены для ID = $id");
return null;
}
// Проверяем наличие файлов
foreach ($documents as $key => $doc) {
if (isset($doc['filepath']) && !file_exists($doc['filepath'])) {
logMessage("Предупреждение: Файл не существует: " . $doc['filepath']);
// Попробуем альтернативный путь
$alternativePath = "storage/" . basename($doc['filepath']);
if (file_exists($alternativePath)) {
$documents[$key]['filepath'] = $alternativePath;
logMessage("Найден альтернативный путь: " . $alternativePath);
}
}
}
// Логируем финальный результат
logMessage("Обработанные документы: " . json_encode($documents, JSON_UNESCAPED_UNICODE));
return $documents;
} catch (PDOException $e) {
logMessage("Ошибка SQL: " . $e->getMessage());
logMessage("SQL State: " . $e->getCode());
return null;
}
}
?>