Save workspace changes
This commit is contained in:
138
app/Console/Commands/ValidateAiBiographyCommand.php
Normal file
138
app/Console/Commands/ValidateAiBiographyCommand.php
Normal file
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\CreatorAiBiography;
|
||||
use App\Models\User;
|
||||
use App\Services\AiBiography\AiBiographyValidator;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
* Validate stored AI biographies against the current validator rules.
|
||||
*
|
||||
* Useful after hardening the validator (e.g. v1.1 upgrade) to identify bios
|
||||
* generated under older, looser rules that no longer pass.
|
||||
*
|
||||
* Flagged biographies have needs_review=true set but are NOT hidden or deleted.
|
||||
*
|
||||
* Usage:
|
||||
* php artisan ai-biography:validate
|
||||
* php artisan ai-biography:validate {user_id}
|
||||
* php artisan ai-biography:validate --dry-run
|
||||
*/
|
||||
final class ValidateAiBiographyCommand extends Command
|
||||
{
|
||||
protected $signature = 'ai-biography:validate
|
||||
{user_id? : Validate biography for a single creator}
|
||||
{--dry-run : Report failures without updating records}
|
||||
{--limit=500 : Maximum number of records to check}';
|
||||
|
||||
protected $description = 'Validate stored AI biographies against current validator rules';
|
||||
|
||||
public function handle(AiBiographyValidator $validator): int
|
||||
{
|
||||
$userId = $this->argument('user_id');
|
||||
$dryRun = (bool) $this->option('dry-run');
|
||||
$limit = max(1, (int) $this->option('limit'));
|
||||
|
||||
if ($userId !== null) {
|
||||
return $this->handleSingle((int) $userId, $validator, $dryRun);
|
||||
}
|
||||
|
||||
return $this->handleBatch($validator, $dryRun, $limit);
|
||||
}
|
||||
|
||||
private function handleSingle(int $userId, AiBiographyValidator $validator, bool $dryRun): int
|
||||
{
|
||||
$user = User::query()->where('id', $userId)->whereNull('deleted_at')->first();
|
||||
|
||||
if ($user === null) {
|
||||
$this->error("User #{$userId} not found.");
|
||||
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
$record = CreatorAiBiography::query()
|
||||
->where('user_id', $userId)
|
||||
->where('is_active', true)
|
||||
->latest()
|
||||
->first();
|
||||
|
||||
if ($record === null) {
|
||||
$this->line("User #{$userId} ({$user->username}): no active biography.");
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
if ($record->is_user_edited) {
|
||||
$this->line("User #{$userId} ({$user->username}): biography is user-edited — skipping.");
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$errors = $validator->validate(
|
||||
(string) $record->text,
|
||||
$record->input_quality_tier ?? 'rich',
|
||||
);
|
||||
|
||||
if ($errors === []) {
|
||||
$this->info("User #{$userId} ({$user->username}): ✓ valid");
|
||||
} else {
|
||||
$this->warn("User #{$userId} ({$user->username}): ✗ " . implode('; ', $errors));
|
||||
|
||||
if (! $dryRun) {
|
||||
$record->update(['needs_review' => true]);
|
||||
}
|
||||
}
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
private function handleBatch(AiBiographyValidator $validator, bool $dryRun, int $limit): int
|
||||
{
|
||||
$this->info('Validating stored AI biographies against current rules...');
|
||||
|
||||
$checked = 0;
|
||||
$passed = 0;
|
||||
$failed = 0;
|
||||
$skipped = 0;
|
||||
|
||||
CreatorAiBiography::query()
|
||||
->where('is_active', true)
|
||||
->whereNotNull('text')
|
||||
->where('is_user_edited', false)
|
||||
->whereIn('status', [
|
||||
CreatorAiBiography::STATUS_GENERATED,
|
||||
CreatorAiBiography::STATUS_APPROVED,
|
||||
])
|
||||
->limit($limit)
|
||||
->chunkById(100, function ($records) use ($validator, $dryRun, &$checked, &$passed, &$failed, &$skipped): void {
|
||||
foreach ($records as $record) {
|
||||
$checked++;
|
||||
|
||||
$errors = $validator->validate(
|
||||
(string) $record->text,
|
||||
$record->input_quality_tier ?? 'rich',
|
||||
);
|
||||
|
||||
if ($errors === []) {
|
||||
$passed++;
|
||||
} else {
|
||||
$failed++;
|
||||
$this->warn(" [user:{$record->user_id}] id:{$record->id} — " . implode('; ', $errors));
|
||||
|
||||
if (! $dryRun) {
|
||||
$record->update(['needs_review' => true]);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$dryTag = $dryRun ? ' [dry-run — no records updated]' : '';
|
||||
$this->info("Done. checked={$checked} passed={$passed} failed/flagged={$failed}{$dryTag}");
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user