This commit is contained in:
2026-05-11 15:23:34 +08:00
parent ed70a140a2
commit 547cbbb4a5
25 changed files with 721 additions and 332 deletions
@@ -22,24 +22,29 @@ class ChunkEmbeddingHandler
$this->retryQueue = $retryQueue ?? new LLMRetryQueue();
}
public function handle(array $task): void
public function handle(array $task): int
{
if (($task['target_type'] ?? null) !== 'archive') {
return;
return 0;
}
$archiveUid = trim((string) ($task['target_uid'] ?? ''));
if ($archiveUid === '') {
return;
return 0;
}
$batchSize = (int) config('LLMapi.embedding.batch_size', 32);
$chunks = $this->chunks->findQueuedChunks($archiveUid, $batchSize);
if ($chunks === []) {
return;
return 0;
}
$chunkUids = array_column($chunks, 'chunk_uid');
if (!$this->client->isConfigured()) {
$this->chunks->markFailed($chunkUids, 'BigModel embedding API is not configured.', false);
return count($chunkUids);
}
$this->chunks->markProcessing($chunkUids);
try {
@@ -52,6 +57,7 @@ class ChunkEmbeddingHandler
);
$this->persistEmbeddings($chunks, $payload);
return count($chunkUids);
} catch (Throwable $exception) {
$this->chunks->markFailed($chunkUids, $exception->getMessage(), $this->isRetryable($exception));
throw $exception;
@@ -6,11 +6,70 @@ use support\Db;
class ChunkEmbeddingRepository
{
public function countChunks(?string $archiveUid = null): int
{
$query = Db::table('chunks');
if ($archiveUid !== null && trim($archiveUid) !== '') {
$query->where('archive_uid', trim($archiveUid));
}
return (int) $query->count();
}
public function countChunksByStatuses(array $statuses, ?string $archiveUid = null): int
{
$query = Db::table('chunks')->whereIn('embedding_status', $statuses);
if ($archiveUid !== null && trim($archiveUid) !== '') {
$query->where('archive_uid', trim($archiveUid));
}
return (int) $query->count();
}
public function resetAllChunksToPending(?string $archiveUid = null): int
{
$query = Db::table('chunks');
if ($archiveUid !== null && trim($archiveUid) !== '') {
$query->where('archive_uid', trim($archiveUid));
}
return $query->update($this->pendingResetPayload());
}
public function resetRecoverableChunksToPending(?string $archiveUid = null): int
{
$query = Db::table('chunks')
->whereIn('embedding_status', [
EmbeddingStatus::QUEUED,
EmbeddingStatus::PROCESSING,
EmbeddingStatus::FAILED_RETRYABLE,
]);
if ($archiveUid !== null && trim($archiveUid) !== '') {
$query->where('archive_uid', trim($archiveUid));
}
return $query->update($this->pendingResetPayload());
}
public function queuePendingArchiveTasks(int $limit): array
{
$statuses = [EmbeddingStatus::PENDING, EmbeddingStatus::QUEUED, EmbeddingStatus::FAILED_RETRYABLE];
$staleBefore = date('Y-m-d H:i:s', time() - max(60, (int) config('queue.tasks.stale_after_seconds', 900)));
$archiveUids = Db::table('chunks')
->whereIn('embedding_status', $statuses)
->where(function ($query) use ($staleBefore): void {
$query
->whereIn('embedding_status', [EmbeddingStatus::PENDING, EmbeddingStatus::FAILED_RETRYABLE])
->orWhere(function ($stale) use ($staleBefore): void {
$stale
->whereIn('embedding_status', [EmbeddingStatus::QUEUED, EmbeddingStatus::PROCESSING])
->where(function ($time) use ($staleBefore): void {
$time->whereNull('embedding_updated_at')->orWhere('embedding_updated_at', '<', $staleBefore);
});
});
})
->select('archive_uid')
->groupBy('archive_uid')
->orderByRaw('MIN(id)')
@@ -22,7 +81,17 @@ class ChunkEmbeddingRepository
foreach ($archiveUids as $archiveUid) {
Db::table('chunks')
->where('archive_uid', $archiveUid)
->whereIn('embedding_status', $statuses)
->where(function ($query) use ($staleBefore): void {
$query
->whereIn('embedding_status', [EmbeddingStatus::PENDING, EmbeddingStatus::FAILED_RETRYABLE])
->orWhere(function ($stale) use ($staleBefore): void {
$stale
->whereIn('embedding_status', [EmbeddingStatus::QUEUED, EmbeddingStatus::PROCESSING])
->where(function ($time) use ($staleBefore): void {
$time->whereNull('embedding_updated_at')->orWhere('embedding_updated_at', '<', $staleBefore);
});
});
})
->update([
'embedding_status' => EmbeddingStatus::QUEUED,
'embedding_error' => null,
@@ -94,4 +163,18 @@ class ChunkEmbeddingRepository
'embedding_updated_at' => Db::raw('CURRENT_TIMESTAMP'),
]);
}
private function pendingResetPayload(): array
{
return [
'embedding_status' => EmbeddingStatus::PENDING,
'embedding_ref' => null,
'embedding_model' => null,
'embedding_error' => null,
'embedding_updated_at' => null,
'search_index_status' => 0,
'search_index_error' => null,
'search_index_updated_at' => null,
];
}
}