add tagging
This commit is contained in:
103
src/Command/TagRebuildRunJobCommand.php
Normal file
103
src/Command/TagRebuildRunJobCommand.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use App\Entity\TagRebuildJob;
|
||||
use App\Tag\TagNdjsonExporter;
|
||||
use App\Tag\TagVectorIndexBuilder;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'mto:agent:tags:job:run',
|
||||
description: 'Run a single tag rebuild job (export tags.ndjson + build vector_tags.index) with lock'
|
||||
)]
|
||||
final class TagRebuildRunJobCommand extends Command
|
||||
{
|
||||
public function __construct(
|
||||
private readonly EntityManagerInterface $em,
|
||||
private readonly TagNdjsonExporter $exporter,
|
||||
private readonly TagVectorIndexBuilder $builder,
|
||||
private readonly string $lockFilePath,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addArgument('jobId', InputArgument::REQUIRED, 'TagRebuildJob UUID');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$jobId = (string)$input->getArgument('jobId');
|
||||
|
||||
/** @var TagRebuildJob|null $job */
|
||||
$job = $this->em->getRepository(TagRebuildJob::class)->find($jobId);
|
||||
if (!$job instanceof TagRebuildJob) {
|
||||
$output->writeln('<error>Job not found.</error>');
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Global lock to avoid parallel rebuilds
|
||||
// ---------------------------------------------------------
|
||||
$lockDir = \dirname($this->lockFilePath);
|
||||
if (!\is_dir($lockDir)) {
|
||||
@\mkdir($lockDir, 0775, true);
|
||||
}
|
||||
|
||||
$fh = @\fopen($this->lockFilePath, 'c+');
|
||||
if (!$fh) {
|
||||
$job->markFailed('Cannot open lock file: ' . $this->lockFilePath);
|
||||
$this->em->flush();
|
||||
$output->writeln('<error>Cannot open lock file.</error>');
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
// If another rebuild runs, we fail fast (simple & safe).
|
||||
if (!@\flock($fh, LOCK_EX | LOCK_NB)) {
|
||||
\fclose($fh);
|
||||
$job->markFailed('Another tag rebuild is currently running (lock busy).');
|
||||
$this->em->flush();
|
||||
$output->writeln('<error>Lock busy. Another rebuild is running.</error>');
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
// mark running
|
||||
$job->markRunning();
|
||||
$this->em->flush();
|
||||
|
||||
try {
|
||||
$export = $this->exporter->export();
|
||||
$this->builder->build();
|
||||
|
||||
$job->markCompleted();
|
||||
$this->em->flush();
|
||||
|
||||
$output->writeln('<info>OK</info>');
|
||||
$output->writeln('tags.ndjson: ' . $export['path']);
|
||||
} catch (\Throwable $e) {
|
||||
$job->markFailed($e->getMessage());
|
||||
$this->em->flush();
|
||||
|
||||
$output->writeln('<error>FAILED: ' . $e->getMessage() . '</error>');
|
||||
|
||||
@\flock($fh, LOCK_UN);
|
||||
@\fclose($fh);
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
@\flock($fh, LOCK_UN);
|
||||
@\fclose($fh);
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
42
src/Command/TagsExportCommand.php
Normal file
42
src/Command/TagsExportCommand.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use App\Tag\TagNdjsonExporter;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'mto:agent:tags:export',
|
||||
description: 'Export tags to NDJSON for tag vector routing'
|
||||
)]
|
||||
final class TagsExportCommand extends Command
|
||||
{
|
||||
public function __construct(
|
||||
private TagNdjsonExporter $exporter,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
try {
|
||||
$result = $this->exporter->export();
|
||||
} catch (\Throwable $e) {
|
||||
$output->writeln('<error>ERROR: ' . $e->getMessage() . '</error>');
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
$output->writeln('<info>Tags NDJSON exported</info>');
|
||||
$output->writeln('Path: ' . $result['path']);
|
||||
$output->writeln('Tags: ' . $result['tags']);
|
||||
$output->writeln('Lines: ' . $result['lines']);
|
||||
$output->writeln('Bytes: ' . $result['bytes']);
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
46
src/Command/TagsRebuildCommand.php
Normal file
46
src/Command/TagsRebuildCommand.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use App\Tag\TagNdjsonExporter;
|
||||
use App\Tag\TagVectorIndexBuilder;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'mto:agent:tags:rebuild',
|
||||
description: 'Export tags.ndjson and rebuild tag vector index (vector_tags.index)'
|
||||
)]
|
||||
final class TagsRebuildCommand extends Command
|
||||
{
|
||||
public function __construct(
|
||||
private readonly TagNdjsonExporter $exporter,
|
||||
private readonly TagVectorIndexBuilder $builder,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
try {
|
||||
$export = $this->exporter->export();
|
||||
$output->writeln('<info>1/2 Exported tags.ndjson</info>');
|
||||
$output->writeln('Path: ' . $export['path']);
|
||||
$output->writeln('Tags: ' . $export['tags']);
|
||||
$output->writeln('Lines: ' . $export['lines']);
|
||||
$output->writeln('Bytes: ' . $export['bytes']);
|
||||
|
||||
$this->builder->build();
|
||||
$output->writeln('<info>2/2 Built vector_tags.index</info>');
|
||||
} catch (\Throwable $e) {
|
||||
$output->writeln('<error>ERROR: ' . $e->getMessage() . '</error>');
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user