55 lines
1.8 KiB
PHP
55 lines
1.8 KiB
PHP
<?php
|
|
|
|
namespace app\service\LLM;
|
|
|
|
use Throwable;
|
|
|
|
class LLMRetryQueue
|
|
{
|
|
public function run(callable $job, array $config = []): mixed
|
|
{
|
|
$enabled = (bool) ($config['enabled'] ?? true);
|
|
$maxAttempts = max(1, (int) ($config['max_attempts'] ?? 3));
|
|
$baseDelayMs = max(0, (int) ($config['base_delay_ms'] ?? 1500));
|
|
$maxDelayMs = max($baseDelayMs, (int) ($config['max_delay_ms'] ?? 10000));
|
|
$retryStatuses = array_map('intval', $config['retry_statuses'] ?? [429]);
|
|
$retryErrorCodes = array_map('strval', $config['retry_error_codes'] ?? ['1302', '1303', '1304', '1305', '1306', '1307', '1308']);
|
|
|
|
$attempt = 0;
|
|
while (true) {
|
|
$attempt++;
|
|
|
|
try {
|
|
return $job($attempt);
|
|
} catch (Throwable $exception) {
|
|
if (!$enabled || $attempt >= $maxAttempts || !$this->shouldRetry($exception, $retryStatuses, $retryErrorCodes)) {
|
|
throw $exception;
|
|
}
|
|
|
|
usleep($this->delayMs($attempt, $baseDelayMs, $maxDelayMs) * 1000);
|
|
}
|
|
}
|
|
}
|
|
|
|
private function shouldRetry(Throwable $exception, array $retryStatuses, array $retryErrorCodes): bool
|
|
{
|
|
if (!$exception instanceof LLMRequestException) {
|
|
return false;
|
|
}
|
|
|
|
if ($exception->statusCode() !== null && in_array($exception->statusCode(), $retryStatuses, true)) {
|
|
return true;
|
|
}
|
|
|
|
return $exception->providerCode() !== null && in_array($exception->providerCode(), $retryErrorCodes, true);
|
|
}
|
|
|
|
private function delayMs(int $attempt, int $baseDelayMs, int $maxDelayMs): int
|
|
{
|
|
$delay = min($maxDelayMs, $baseDelayMs * (2 ** max(0, $attempt - 1)));
|
|
$jitter = $delay > 0 ? random_int(0, min(500, $delay)) : 0;
|
|
|
|
return $delay + $jitter;
|
|
}
|
|
}
|