2026-04-04 20:37:35 +03:00
<? php
namespace App\Console\Commands ;
use App\Models\Job ;
use App\Models\Tag ;
use Illuminate\Console\Attributes\Description ;
use Illuminate\Console\Attributes\Signature ;
use Illuminate\Console\Command ;
use Illuminate\Support\Facades\Http ;
use Illuminate\Support\Facades\Log ;
#[ Signature ( 'jobs:curate {--batch=50}' )]
#[ Description ( 'Curate jobs by fetching tags from an external API' )]
class CurateJobs extends Command
{
2026-05-01 23:33:15 +03:00
protected $signature = 'jobs:curate {--batch=50}' ;
protected $description = 'Curate jobs by fetching tags from an external API' ;
2026-04-04 20:37:35 +03:00
/**
* Execute the console command.
*/
public function handle () : int
{
$batchSize = ( int ) $this -> option ( 'batch' );
//$apiUrl = $this->option('api-url');
2026-05-01 02:36:25 +03:00
$apiUrl = 'https://olm.c0rrup7.net/api/generate' ;
2026-04-04 20:37:35 +03:00
if ( empty ( $apiUrl )) {
$this -> error ( 'API URL is required. Use --api-url option to provide the endpoint.' );
return self :: FAILURE ;
}
$this -> info ( 'Starting job curation...' );
$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 {
2026-05-01 02:36:25 +03:00
$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.' ;
2026-04-04 20:37:35 +03:00
$body .= 'Title: ' . htmlentities ( $job -> title );
$body .= 'Description: ' . htmlentities ( $job -> description );
2026-05-01 02:36:25 +03:00
$body .= 'RETURN ONLY VALID JSON, NO TEXT BEFORE OR AFTER THE JSON!' ;
2026-04-04 20:37:35 +03:00
// Make POST request to API with job name and description
$response = Http :: timeout ( 130 )
-> withHeaders ([
'Accept' => 'application/json' ,
2026-05-01 02:36:25 +03:00
'CF-Access-Client-Id' => '8c4cc272c7f08f8d7911a28eb5d7db44.access' ,
'CF-Access-Client-Secret' => 'adeeed2f75236053953cc9dd898c7756bab5e00f7d0901bf7d488b6db8aba336' ,
2026-04-04 20:37:35 +03:00
])
-> post ( $apiUrl , [
2026-05-01 02:36:25 +03:00
'model' => 'mixtral' ,
2026-04-04 20:37:35 +03:00
'stream' => false ,
'prompt' => $body ,
]);
if ( $response -> successful ()) {
2026-05-01 02:36:25 +03:00
$responseArray = $response -> json ();
$responseTagsJson = $responseArray [ 'response' ];
$tags = json_decode ( $responseTagsJson , true );
$tags = array_flip ( $tags );
2026-04-04 20:37:35 +03:00
if ( is_array ( $tags ) && ! empty ( $tags )) {
$tagIds = [];
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 () } " );
}
} catch ( \Exception $e ) {
$failedCount ++ ;
Log :: error ( "Error curating job # { $job -> id } : { $e -> getMessage () } " );
}
$progressBar -> advance ();
}
$progressBar -> finish ();
$this -> newLine ( 2 );
$this -> info ( "Successfully curated { $curatedCount } jobs." );
if ( $failedCount > 0 ) {
$this -> warn ( "Failed to curate { $failedCount } jobs. Check logs for details." );
}
return self :: SUCCESS ;
}
}