- 默认切换 AgentProvider 为 HttpAgentProvider,增强网络请求的容错和重试机制 - 优化 Run 逻辑,支持多场景去重与并发保护 - 添加 Redis 发布失败的日志记录以提升问题排查效率 - 扩展 OpenAPI 规范,新增 Error 和 Run 状态相关模型 - 增强测试覆盖,验证调度策略和重复请求的幂等性 - 增加数据库索引以优化查询性能 - 更新所有相关文档和配置文件
107 lines
3.4 KiB
PHP
107 lines
3.4 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Agent;
|
|
|
|
use Illuminate\Http\Client\ConnectionException;
|
|
use Illuminate\Support\Facades\Http;
|
|
|
|
class HttpAgentProvider implements AgentProviderInterface
|
|
{
|
|
protected string $endpoint;
|
|
protected int $timeoutSeconds;
|
|
protected int $connectTimeoutSeconds;
|
|
protected int $retryTimes;
|
|
protected int $retryBackoffMs;
|
|
|
|
public function __construct(?string $endpoint = null)
|
|
{
|
|
$this->endpoint = $endpoint ?? config('agent.provider.endpoint', '');
|
|
$this->timeoutSeconds = (int) config('agent.provider.timeout_seconds', 30);
|
|
$this->connectTimeoutSeconds = (int) config('agent.provider.connect_timeout_seconds', 5);
|
|
$this->retryTimes = (int) config('agent.provider.retry_times', 1);
|
|
$this->retryBackoffMs = (int) config('agent.provider.retry_backoff_ms', 500);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $context
|
|
* @param array<string, mixed> $options
|
|
*/
|
|
public function generate(array $context, array $options = []): string
|
|
{
|
|
if (empty($this->endpoint)) {
|
|
// placeholder to avoid accidental outbound calls when未配置
|
|
return (new DummyAgentProvider())->generate($context, $options);
|
|
}
|
|
|
|
$payload = [
|
|
'context' => $context,
|
|
'options' => $options,
|
|
];
|
|
|
|
$attempts = $this->retryTimes + 1;
|
|
$lastException = null;
|
|
$lastResponseBody = null;
|
|
$lastStatus = null;
|
|
|
|
for ($attempt = 1; $attempt <= $attempts; $attempt++) {
|
|
try {
|
|
$response = Http::connectTimeout($this->connectTimeoutSeconds)
|
|
->timeout($this->timeoutSeconds)
|
|
->post($this->endpoint, $payload);
|
|
|
|
$lastStatus = $response->status();
|
|
$lastResponseBody = $response->body();
|
|
|
|
if ($response->successful()) {
|
|
$data = $response->json();
|
|
|
|
return is_string($data) ? $data : ($data['content'] ?? '');
|
|
}
|
|
|
|
$retryable = $lastStatus === 429 || $lastStatus >= 500;
|
|
if ($retryable && $attempt < $attempts) {
|
|
usleep($this->retryBackoffMs * 1000);
|
|
continue;
|
|
}
|
|
|
|
throw new ProviderException(
|
|
'HTTP_ERROR',
|
|
'Agent provider failed',
|
|
$retryable,
|
|
$lastStatus,
|
|
$lastResponseBody
|
|
);
|
|
} catch (ConnectionException $exception) {
|
|
$lastException = $exception;
|
|
if ($attempt < $attempts) {
|
|
usleep($this->retryBackoffMs * 1000);
|
|
continue;
|
|
}
|
|
} catch (\Throwable $exception) {
|
|
$lastException = $exception;
|
|
break;
|
|
}
|
|
}
|
|
|
|
$rawMessage = $lastException ? $lastException->getMessage() : $lastResponseBody;
|
|
|
|
if ($lastException instanceof ConnectionException) {
|
|
throw new ProviderException(
|
|
'CONNECTION_FAILED',
|
|
'Agent provider connection failed',
|
|
true,
|
|
$lastStatus,
|
|
$rawMessage
|
|
);
|
|
}
|
|
|
|
throw new ProviderException(
|
|
'UNKNOWN_ERROR',
|
|
'Agent provider error',
|
|
false,
|
|
$lastStatus,
|
|
$rawMessage
|
|
);
|
|
}
|
|
}
|