option('batch'); //$apiUrl = $this->option('api-url'); $apiUrl = 'https://olm.c0rrup7.net/api/generate'; if (empty($apiUrl)) { $this->error('API URL is required. Use --api-url option to provide the endpoint.'); return self::FAILURE; } $log = Log::channel('jobs_curate'); $this->info('Starting job curation...'); $log->info('Starting job curation batch', ['batch_size' => $batchSize]); $this->newLine(); // Fetch uncurated jobs $uncuratedJobs = Job::uncurated() ->limit($batchSize) ->get(); if ($uncuratedJobs->isEmpty()) { $this->info('No uncurated jobs found.'); return self::SUCCESS; } $this->info("Found {$uncuratedJobs->count()} uncurated jobs."); $this->newLine(); $curatedCount = 0; $failedCount = 0; $progressBar = $this->output->createProgressBar($uncuratedJobs->count()); $progressBar->start(); foreach ($uncuratedJobs as $job) { try { $body = 'This is the content of a job ad, you need to read it and extract the relevant tags from it. Respond only with the tags in JSON format, NO OTHER TEXT BEFORE OR AFTER THE JSON.'; $body .= 'Title: '. htmlentities($job->title); $body .= 'Description: '. htmlentities($job->description); $body .= 'RETURN ONLY VALID JSON, NO TEXT BEFORE OR AFTER THE JSON!'; // Make POST request to API with job name and description $response = Http::timeout(300) ->withHeaders([ 'Accept' => 'application/json', 'CF-Access-Client-Id' => '8c4cc272c7f08f8d7911a28eb5d7db44.access', 'CF-Access-Client-Secret' => 'adeeed2f75236053953cc9dd898c7756bab5e00f7d0901bf7d488b6db8aba336', ]) ->post($apiUrl, [ 'model' => 'granite4.1:3b', 'stream' => false, 'prompt' => $body, ]); if ($response->successful()) { $responseArray = $response->json(); $responseTagsJson = $responseArray['response']; $log->info("Curated job id: {$job->id} Result: {$responseTagsJson}"); $tags = json_decode($responseTagsJson, true); if (empty($tags) || $tags === null) { continue; } //$tags = array_flip($tags); if (is_array($tags) && !empty($tags)) { $tagIds = []; if (isset($tags['tags'])) { $tags = $tags['tags']; } foreach ($tags as $tagName) { // Find or create tag $tag = Tag::firstOrCreate( ['name' => $tagName] ); $tagIds[] = $tag->id; } // Sync tags with the job $job->tags()->sync($tagIds); // Mark job as curated $job->update(['is_curated' => true]); $curatedCount++; } else { // Mark as curated even if no tags returned $job->update(['is_curated' => true]); $curatedCount++; } } else { $failedCount++; $log->warning("Failed to curate job #{$job->id}: API returned {$response->status()} -> {$response->body()}"); } } catch (\Exception $e) { $failedCount++; $log->error("Error curating job #{$job->id}: {$e->getMessage()} {$e->getTraceAsString()}"); } $progressBar->advance(); } $progressBar->finish(); $this->newLine(2); $this->info("Successfully curated {$curatedCount} jobs."); $log->info("Curation complete", ['curated' => $curatedCount, 'failed' => $failedCount]); if ($failedCount > 0) { $this->warn("Failed to curate {$failedCount} jobs. Check logs for details."); } return self::SUCCESS; } }