feat(logging): migrate AI tasks to ai channel
This commit is contained in:
@@ -20,13 +20,13 @@ class CallAiServiceForFileSelectionTask
|
||||
*/
|
||||
public function run(array $fragments): ?array
|
||||
{
|
||||
Log::info('[CallAiServiceForFileSelectionTask] Начало LLM запроса', [
|
||||
Log::channel('ai')->info('Начало LLM запроса', [
|
||||
'fragments_count' => count($fragments),
|
||||
]);
|
||||
|
||||
$credential = $this->getCredentialTask->run('qwen');
|
||||
if (!$credential) {
|
||||
Log::error('[CallAiServiceForFileSelectionTask] API ключ не настроен', [
|
||||
Log::channel('ai')->error('API ключ не настроен', [
|
||||
'provider' => 'qwen',
|
||||
]);
|
||||
return null;
|
||||
@@ -35,7 +35,7 @@ class CallAiServiceForFileSelectionTask
|
||||
|
||||
$prompt = $this->buildPrompt($fragments);
|
||||
|
||||
Log::info('[CallAiServiceForFileSelectionTask] Отправка запроса к AI');
|
||||
Log::channel('ai')->info('Отправка запроса к AI');
|
||||
|
||||
try {
|
||||
$response = Http::timeout(30) // Таймаут 30 секунд
|
||||
@@ -52,7 +52,7 @@ class CallAiServiceForFileSelectionTask
|
||||
'max_tokens' => 500,
|
||||
]);
|
||||
|
||||
Log::info('[CallAiServiceForFileSelectionTask] Ответ от AI', [
|
||||
Log::channel('ai')->info('Ответ от AI', [
|
||||
'status' => $response->status(),
|
||||
'successful' => $response->successful(),
|
||||
]);
|
||||
@@ -61,19 +61,19 @@ class CallAiServiceForFileSelectionTask
|
||||
$result = $response->json();
|
||||
$llmResponse = json_decode($result['choices'][0]['message']['content'], true);
|
||||
|
||||
Log::info('[CallAiServiceForFileSelectionTask] Распознан ответ', [
|
||||
Log::channel('ai')->info('Распознан ответ', [
|
||||
'main_file' => $llmResponse['main_file'] ?? null,
|
||||
]);
|
||||
|
||||
return $llmResponse;
|
||||
} else {
|
||||
Log::error('[CallAiServiceForFileSelectionTask] Ошибка AI запроса', [
|
||||
Log::channel('ai')->error('Ошибка AI запроса', [
|
||||
'status' => $response->status(),
|
||||
'body' => $response->body(),
|
||||
]);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::error('[CallAiServiceForFileSelectionTask] Исключение при запросе к AI', [
|
||||
Log::channel('ai')->error('Исключение при запросе к AI', [
|
||||
'message' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
]);
|
||||
|
||||
@@ -23,14 +23,14 @@ class CallAiServiceTask
|
||||
public function run(string $text, Collection $categories): ?array
|
||||
{
|
||||
try {
|
||||
Log::info('[CallAiServiceTask] Начало AI запроса', [
|
||||
Log::channel('ai')->info('Начало AI запроса', [
|
||||
'text_length' => strlen($text),
|
||||
'categories_count' => $categories->count(),
|
||||
]);
|
||||
|
||||
$credential = $this->getCredentialTask->run('qwen');
|
||||
if (!$credential) {
|
||||
Log::error('[CallAiServiceTask] API ключ не настроен', [
|
||||
Log::channel('ai')->error('API ключ не настроен', [
|
||||
'provider' => 'qwen',
|
||||
]);
|
||||
throw new \RuntimeException('AI API ключ не настроен');
|
||||
@@ -43,7 +43,7 @@ class CallAiServiceTask
|
||||
|
||||
$systemPrompt = $this->buildSystemPrompt($categoryList);
|
||||
|
||||
Log::info('[CallAiServiceTask] Отправка запроса к AI', [
|
||||
Log::channel('ai')->info('Отправка запроса к AI', [
|
||||
'url' => 'https://routerai.ru/api/v1/chat/completions',
|
||||
'model' => 'qwen/qwen3.5-flash-02-23',
|
||||
]);
|
||||
@@ -58,7 +58,7 @@ class CallAiServiceTask
|
||||
if ($response && $response->successful()) {
|
||||
return false;
|
||||
}
|
||||
Log::warning('[CallAiServiceTask] Попытка не удалась, повтор...', [
|
||||
Log::channel('ai')->warning('Попытка не удалась, повтор...', [
|
||||
'exception' => $exception->getMessage(),
|
||||
]);
|
||||
return true;
|
||||
@@ -75,7 +75,7 @@ class CallAiServiceTask
|
||||
'max_tokens' => 2000,
|
||||
]);
|
||||
|
||||
Log::info('[CallAiServiceTask] Ответ от AI', [
|
||||
Log::channel('ai')->info('Ответ от AI', [
|
||||
'status' => $response->status(),
|
||||
'successful' => $response->successful(),
|
||||
]);
|
||||
@@ -85,7 +85,7 @@ class CallAiServiceTask
|
||||
$content = $result['choices'][0]['message']['content'] ?? null;
|
||||
|
||||
if (empty($content)) {
|
||||
Log::error('[CallAiServiceTask] Пустой ответ от AI', [
|
||||
Log::channel('ai')->error('Пустой ответ от AI', [
|
||||
'response' => $result,
|
||||
]);
|
||||
return null;
|
||||
@@ -94,14 +94,14 @@ class CallAiServiceTask
|
||||
$data = json_decode($content, true);
|
||||
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
Log::error('[CallAiServiceTask] Ошибка парсинга JSON ответа', [
|
||||
Log::channel('ai')->error('Ошибка парсинга JSON ответа', [
|
||||
'error' => json_last_error_msg(),
|
||||
'content' => substr($content, 0, 500),
|
||||
]);
|
||||
return null;
|
||||
}
|
||||
|
||||
Log::info('[CallAiServiceTask] AI данные успешно распознаны', [
|
||||
Log::channel('ai')->info('AI данные успешно распознаны', [
|
||||
'title' => $data['title'] ?? 'N/A',
|
||||
'has_body' => isset($data['body']),
|
||||
'category_id' => $data['category_id'] ?? 'N/A',
|
||||
@@ -110,7 +110,7 @@ class CallAiServiceTask
|
||||
return $data;
|
||||
}
|
||||
|
||||
Log::error('[CallAiServiceTask] AI запрос не удался', [
|
||||
Log::channel('ai')->error('AI запрос не удался', [
|
||||
'status' => $response->status(),
|
||||
'body' => $response->body(),
|
||||
'headers' => $response->headers(),
|
||||
@@ -118,7 +118,7 @@ class CallAiServiceTask
|
||||
|
||||
return null;
|
||||
} catch (\Exception $e) {
|
||||
Log::error('[CallAiServiceTask] Критическая ошибка AI запроса', [
|
||||
Log::channel('ai')->error('Критическая ошибка AI запроса', [
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
]);
|
||||
|
||||
@@ -17,13 +17,13 @@ class ConvertDocToDocxTask
|
||||
*/
|
||||
public function run(UploadedFile $file): ?string
|
||||
{
|
||||
Log::info('[ConvertDocToDocxTask] Начало конвертации', [
|
||||
Log::channel('ai')->info('Начало конвертации', [
|
||||
'file' => $file->getClientOriginalName(),
|
||||
'extension' => $file->getClientOriginalExtension(),
|
||||
]);
|
||||
|
||||
if (strtolower($file->getClientOriginalExtension()) !== 'doc') {
|
||||
Log::warning('[ConvertDocToDocxTask] Неверное расширение', ['extension' => $file->getClientOriginalExtension()]);
|
||||
Log::channel('ai')->warning('Неверное расширение', ['extension' => $file->getClientOriginalExtension()]);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ class ConvertDocToDocxTask
|
||||
$absoluteOriginalPath = Storage::disk('local')->path($originalPath);
|
||||
|
||||
if (!file_exists($absoluteOriginalPath)) {
|
||||
Log::error('[ConvertDocToDocxTask] Файл не сохранен', ['path' => $absoluteOriginalPath]);
|
||||
Log::channel('ai')->error('Файл не сохранен', ['path' => $absoluteOriginalPath]);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ class ConvertDocToDocxTask
|
||||
escapeshellarg($absoluteOriginalPath)
|
||||
);
|
||||
|
||||
Log::info('[ConvertDocToDocxTask] Выполнение команды', ['command' => $command]);
|
||||
Log::channel('ai')->info('Выполнение команды', ['command' => $command]);
|
||||
|
||||
$output = shell_exec($command);
|
||||
|
||||
@@ -63,7 +63,7 @@ class ConvertDocToDocxTask
|
||||
}
|
||||
|
||||
if (!file_exists($docxPath)) {
|
||||
Log::error('[ConvertDocToDocxTask] Конвертация не удалась', [
|
||||
Log::channel('ai')->error('Конвертация не удалась', [
|
||||
'file' => $file->getClientOriginalName(),
|
||||
'output' => $output,
|
||||
'expected_path' => $docxPath,
|
||||
@@ -78,7 +78,7 @@ class ConvertDocToDocxTask
|
||||
|
||||
$relativeDocxPath = preg_replace('/\.doc$/i', '.docx', $originalPath);
|
||||
|
||||
Log::info('[ConvertDocToDocxTask] Конвертация успешна', ['result_path' => $relativeDocxPath]);
|
||||
Log::channel('ai')->info('Конвертация успешна', ['result_path' => $relativeDocxPath]);
|
||||
|
||||
return $relativeDocxPath;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ class ExtractTextFragmentTask
|
||||
*/
|
||||
public function run(UploadedFile $file, int $maxLength = 300): string
|
||||
{
|
||||
Log::info('[ExtractTextFragmentTask] Начало извлечения фрагмента', [
|
||||
Log::channel('ai')->info('Начало извлечения фрагмента', [
|
||||
'file' => $file->getClientOriginalName(),
|
||||
'extension' => $file->getClientOriginalExtension(),
|
||||
]);
|
||||
@@ -30,7 +30,7 @@ class ExtractTextFragmentTask
|
||||
return $this->extractFromDoc($file, $maxLength);
|
||||
}
|
||||
|
||||
Log::warning('[ExtractTextFragmentTask] Неизвестный формат', [
|
||||
Log::channel('ai')->warning('Неизвестный формат', [
|
||||
'extension' => $extension,
|
||||
]);
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ class ExtractTextFromDocumentTask
|
||||
{
|
||||
$extension = strtolower($file->getClientOriginalExtension());
|
||||
|
||||
Log::info('[ExtractTextFromDocumentTask] Начало извлечения текста', [
|
||||
Log::channel('ai')->info('Начало извлечения текста', [
|
||||
'file' => $file->getClientOriginalName(),
|
||||
'extension' => $extension,
|
||||
'size' => $file->getSize(),
|
||||
@@ -31,17 +31,17 @@ class ExtractTextFromDocumentTask
|
||||
|
||||
// Если DOCX — парсим напрямую
|
||||
if ($extension === 'docx') {
|
||||
Log::info('[ExtractTextFromDocumentTask] Обработка DOCX файла');
|
||||
Log::channel('ai')->info('Обработка DOCX файла');
|
||||
return $this->extractFromDocx($file);
|
||||
}
|
||||
|
||||
// Если DOC — конвертируем в DOCX, затем парсим
|
||||
if ($extension === 'doc') {
|
||||
Log::info('[ExtractTextFromDocumentTask] Обработка DOC файла (требуется конвертация)');
|
||||
Log::channel('ai')->info('Обработка DOC файла (требуется конвертация)');
|
||||
return $this->extractFromDoc($file);
|
||||
}
|
||||
|
||||
Log::warning('[ExtractTextFromDocumentTask] Неизвестный формат файла', [
|
||||
Log::channel('ai')->warning('Неизвестный формат файла', [
|
||||
'extension' => $extension,
|
||||
]);
|
||||
|
||||
@@ -53,13 +53,13 @@ class ExtractTextFromDocumentTask
|
||||
*/
|
||||
private function extractFromDocx(UploadedFile $file): ?string
|
||||
{
|
||||
Log::info('[ExtractTextFromDocumentTask:extractFromDocx] Начало');
|
||||
Log::channel('ai')->info('Начало');
|
||||
|
||||
// Сохраняем во временную директорию
|
||||
$tempPath = $file->storeAs('temp', 'temp_doc_' . time() . '.docx', 'local');
|
||||
$absoluteTempPath = Storage::disk('local')->path($tempPath);
|
||||
|
||||
Log::info('[ExtractTextFromDocumentTask:extractFromDocx] Файл сохранен', [
|
||||
Log::channel('ai')->info('Файл сохранен', [
|
||||
'path' => $tempPath,
|
||||
'absolute_path' => $absoluteTempPath,
|
||||
]);
|
||||
@@ -67,7 +67,7 @@ class ExtractTextFromDocumentTask
|
||||
// Парсим файл
|
||||
$extractedText = $this->parseDocxTask->run($absoluteTempPath);
|
||||
|
||||
Log::info('[ExtractTextFromDocumentTask:extractFromDocx] Результат парсинга', [
|
||||
Log::channel('ai')->info('Результат парсинга', [
|
||||
'text_length' => strlen($extractedText ?? ''),
|
||||
]);
|
||||
|
||||
@@ -82,19 +82,19 @@ class ExtractTextFromDocumentTask
|
||||
*/
|
||||
private function extractFromDoc(UploadedFile $file): ?string
|
||||
{
|
||||
Log::info('[ExtractTextFromDocumentTask:extractFromDoc] Начало конвертации');
|
||||
Log::channel('ai')->info('Начало конвертации');
|
||||
|
||||
// Конвертируем DOC → DOCX
|
||||
$docxPath = $this->convertDocToDocxTask->run($file);
|
||||
|
||||
if (!$docxPath) {
|
||||
Log::error('[ExtractTextFromDocumentTask:extractFromDoc] Не удалось конвертировать DOC файл', [
|
||||
Log::channel('ai')->error('Не удалось конвертировать DOC файл', [
|
||||
'file' => $file->getClientOriginalName(),
|
||||
]);
|
||||
return null;
|
||||
}
|
||||
|
||||
Log::info('[ExtractTextFromDocumentTask:extractFromDoc] Конвертация успешна', [
|
||||
Log::channel('ai')->info('Конвертация успешна', [
|
||||
'docx_path' => $docxPath,
|
||||
]);
|
||||
|
||||
@@ -104,7 +104,7 @@ class ExtractTextFromDocumentTask
|
||||
// Парсим конвертированный файл
|
||||
$extractedText = $this->parseDocxTask->run($absoluteDocxPath);
|
||||
|
||||
Log::info('[ExtractTextFromDocumentTask:extractFromDoc] Результат парсинга', [
|
||||
Log::channel('ai')->info('Результат парсинга', [
|
||||
'text_length' => strlen($extractedText ?? ''),
|
||||
]);
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ class FindMainNewsFileTask
|
||||
*/
|
||||
public function run(Collection $files): ?UploadedFile
|
||||
{
|
||||
Log::info('[FindMainNewsFileTask] Начало поиска основного файла', [
|
||||
Log::channel('ai')->info('Начало поиска основного файла', [
|
||||
'files_count' => $files->count(),
|
||||
'files' => $files->map(function($f) {
|
||||
return $f->getClientOriginalName() . ' (' . $f->getClientOriginalExtension() . ')';
|
||||
@@ -61,19 +61,19 @@ class FindMainNewsFileTask
|
||||
return false;
|
||||
});
|
||||
|
||||
Log::info('[FindMainNewsFileTask] Найдено DOC/DOCX файлов', [
|
||||
Log::channel('ai')->info('Найдено DOC/DOCX файлов', [
|
||||
'count' => $docFiles->count(),
|
||||
'files' => $docFiles->map(fn($f) => $f->getClientOriginalName())->toArray(),
|
||||
]);
|
||||
|
||||
if ($docFiles->isEmpty()) {
|
||||
Log::warning('[FindMainNewsFileTask] Не найдено DOC/DOCX файлов');
|
||||
Log::channel('ai')->warning('Не найдено DOC/DOCX файлов');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Если только один файл — используем его
|
||||
if ($docFiles->count() === 1) {
|
||||
Log::info('[FindMainNewsFileTask] Найден один файл, используем его', [
|
||||
Log::channel('ai')->info('Найден один файл, используем его', [
|
||||
'file' => $docFiles->first()->getClientOriginalName(),
|
||||
]);
|
||||
return $docFiles->first();
|
||||
@@ -84,7 +84,7 @@ class FindMainNewsFileTask
|
||||
$filename = mb_strtolower($file->getClientOriginalName());
|
||||
foreach (self::NEWS_KEYWORDS as $keyword) {
|
||||
if (str_contains($filename, $keyword)) {
|
||||
Log::info('[FindMainNewsFileTask] Найден файл по ключевому слову', [
|
||||
Log::channel('ai')->info('Найден файл по ключевому слову', [
|
||||
'file' => $file->getClientOriginalName(),
|
||||
'keyword' => $keyword,
|
||||
]);
|
||||
@@ -93,7 +93,7 @@ class FindMainNewsFileTask
|
||||
}
|
||||
}
|
||||
|
||||
Log::info('[FindMainNewsFileTask] Не найдено файлов по ключевым словам, используем LLM');
|
||||
Log::channel('ai')->info('Не найдено файлов по ключевым словам, используем LLM');
|
||||
|
||||
// Если не нашли по ключевым словам — используем LLM
|
||||
return $this->findViaLLM($docFiles);
|
||||
@@ -104,7 +104,7 @@ class FindMainNewsFileTask
|
||||
*/
|
||||
private function findViaLLM(Collection $docFiles): ?UploadedFile
|
||||
{
|
||||
Log::info('[FindMainNewsFileTask:findViaLLM] Начало LLM определения', [
|
||||
Log::channel('ai')->info('Начало LLM определения', [
|
||||
'files_count' => $docFiles->count(),
|
||||
]);
|
||||
|
||||
@@ -119,7 +119,7 @@ class FindMainNewsFileTask
|
||||
// Собираем фрагменты текста из каждого файла
|
||||
$fragments = [];
|
||||
foreach ($docFiles as $file) {
|
||||
Log::info('[FindMainNewsFileTask:findViaLLM] Извлечение фрагмента', [
|
||||
Log::channel('ai')->info('Извлечение фрагмента', [
|
||||
'file' => $file->getClientOriginalName(),
|
||||
]);
|
||||
|
||||
@@ -131,11 +131,11 @@ class FindMainNewsFileTask
|
||||
}
|
||||
|
||||
// Отправляем запрос к LLM
|
||||
Log::info('[FindMainNewsFileTask:findViaLLM] Отправка запроса к LLM');
|
||||
Log::channel('ai')->info('Отправка запроса к LLM');
|
||||
$llmResponse = app(CallAiServiceForFileSelectionTask::class)->run($fragments);
|
||||
|
||||
if ($llmResponse && isset($llmResponse['main_file'])) {
|
||||
Log::info('[FindMainNewsFileTask:findViaLLM] LLM вернула результат', [
|
||||
Log::channel('ai')->info('LLM вернула результат', [
|
||||
'main_file' => $llmResponse['main_file'],
|
||||
]);
|
||||
|
||||
@@ -147,7 +147,7 @@ class FindMainNewsFileTask
|
||||
}
|
||||
}
|
||||
|
||||
Log::warning('[FindMainNewsFileTask:findViaLLM] LLM не помогла, используем первый файл');
|
||||
Log::channel('ai')->warning('LLM не помогла, используем первый файл');
|
||||
|
||||
// Fallback — первый файл
|
||||
return $docFiles->first();
|
||||
|
||||
@@ -14,7 +14,7 @@ class ParseDocxTask
|
||||
{
|
||||
// 1. Проверяем, существует ли физически файл по этому пути
|
||||
if (!file_exists($filePath)) {
|
||||
Log::error("[ParseDocxTask] Файл не найден по пути: {$filePath}");
|
||||
Log::channel('ai')->error('Файл не найден по пути: {$filePath}");
|
||||
return "ОШИБКА ДЕБАГА: Файл не найден на сервере.";
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ class ParseDocxTask
|
||||
// Если не true, значит произошла ошибка
|
||||
if ($res !== true) {
|
||||
$errorMsg = $this->getZipError($res);
|
||||
Log::error("[ParseDocxTask] Ошибка ZipArchive: {$errorMsg} | Путь: {$filePath}");
|
||||
Log::channel('ai')->error('Ошибка ZipArchive: {$errorMsg} | Путь: {$filePath}");
|
||||
|
||||
// Возвращаем текст ошибки прямо на фронт, чтобы сразу увидеть причину!
|
||||
return "ОШИБКА ДЕБАГА ZipArchive: {$errorMsg}";
|
||||
@@ -39,7 +39,7 @@ class ParseDocxTask
|
||||
$zip->close();
|
||||
|
||||
if ($xmlString === false) {
|
||||
Log::error("[ParseDocxTask] Файл word/document.xml не найден внутри архива. Возможно, это не настоящий DOCX.");
|
||||
Log::channel('ai')->error('Файл word/document.xml не найден внутри архива. Возможно, это не настоящий DOCX.");
|
||||
return "ОШИБКА ДЕБАГА: Внутри архива нет word/document.xml";
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ class ParseDocxTask
|
||||
$text = strip_tags($text);
|
||||
return trim($text);
|
||||
} catch (\Exception $e) {
|
||||
Log::error("[ParseDocxTask] Ошибка очистки тегов: " . $e->getMessage());
|
||||
Log::channel('ai')->error('Ошибка очистки тегов: " . $e->getMessage());
|
||||
return "ОШИБКА ДЕБАГА при очистке текста.";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user