bookId = $bookId; $this->onQueue('summary-generation'); } /** * Execute the job. */ public function handle(GutendexClient $gutendexClient, OllamaService $ollamaService): void { Log::info('GenerateSummary: Starting summary generation', [ 'book_id' => $this->bookId, ]); try { // Load the book $book = Book::find($this->bookId); if (!$book) { Log::error('GenerateSummary: Book not found', [ 'book_id' => $this->bookId, ]); return; } // Check if summary already exists and is completed $existingSummary = $book->summary; if ($existingSummary && $existingSummary->processing_status === 'completed') { Log::info('GenerateSummary: Summary already exists and is completed', [ 'book_id' => $this->bookId, 'summary_id' => $existingSummary->id, ]); return; } // Create or get summary record $summary = $existingSummary ?? Summary::create([ 'book_id' => $book->id, 'content' => '', 'processing_status' => 'pending', ]); // Mark as processing $summary->markAsProcessing(); Log::info('GenerateSummary: Fetching full text', [ 'book_id' => $this->bookId, 'url' => $book->full_text_url, ]); // Fetch full text from URL $fullText = $gutendexClient->fetchFullText($book->full_text_url); if (!$fullText) { $errorMessage = 'Failed to fetch full text from URL'; Log::error('GenerateSummary: ' . $errorMessage, [ 'book_id' => $this->bookId, 'url' => $book->full_text_url, ]); $summary->markAsFailed($errorMessage); return; } // Clean and extract excerpt from the text $cleanedText = TextProcessor::cleanText($fullText); $maxLength = config('ollama.text_extraction.max_length', 50000); $strategy = config('ollama.text_extraction.strategy', 'beginning'); $excerpt = TextProcessor::extractExcerpt($cleanedText, $maxLength, $strategy); Log::info('GenerateSummary: Text processed', [ 'book_id' => $this->bookId, 'original_length' => strlen($fullText), 'cleaned_length' => strlen($cleanedText), 'excerpt_length' => strlen($excerpt), ]); // Generate summary using Ollama Log::info('GenerateSummary: Calling Ollama API for summary', [ 'book_id' => $this->bookId, 'title' => $book->title, 'author' => $book->author, ]); $summaryContent = $ollamaService->generateSummary($excerpt, $book->title, $book->author); if (!$summaryContent) { $errorMessage = 'Failed to generate summary from Ollama API'; Log::error('GenerateSummary: ' . $errorMessage, [ 'book_id' => $this->bookId, ]); $summary->markAsFailed($errorMessage); return; } // Generate key insights using Ollama Log::info('GenerateSummary: Calling Ollama API for key insights', [ 'book_id' => $this->bookId, ]); $keyInsights = $ollamaService->generateKeyInsights($summaryContent, $book->title, $book->author); if (!$keyInsights) { Log::warning('GenerateSummary: Failed to generate key insights, proceeding without them', [ 'book_id' => $this->bookId, ]); $keyInsights = null; } // Store the summary $summary->update([ 'content' => $summaryContent, 'key_insights' => $keyInsights, ]); // Mark as completed $summary->markAsCompleted($ollamaService->getModel()); Log::info('GenerateSummary: Summary generated successfully', [ 'book_id' => $this->bookId, 'summary_id' => $summary->id, 'summary_length' => strlen($summaryContent), 'has_insights' => !empty($keyInsights), ]); } catch (\Exception $e) { Log::error('GenerateSummary: Exception occurred', [ 'book_id' => $this->bookId, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), ]); // Try to mark summary as failed if it exists $book = Book::find($this->bookId); if ($book && $book->summary) { $book->summary->markAsFailed($e->getMessage()); } throw $e; } } /** * Handle a job failure. */ public function failed(\Throwable $exception): void { Log::error('GenerateSummary: Job failed permanently', [ 'book_id' => $this->bookId, 'error' => $exception->getMessage(), ]); // Mark summary as failed $book = Book::find($this->bookId); if ($book && $book->summary) { $book->summary->markAsFailed( 'Job failed after ' . $this->tries . ' attempts: ' . $exception->getMessage() ); } } }