修复SuperAgent自动分发恢复逻辑
This commit is contained in:
@@ -47,7 +47,7 @@ public class MybatisSuperAgentDispatchRunRepository implements SuperAgentDispatc
|
||||
}
|
||||
|
||||
/**
|
||||
* 抢占 PENDING/RETRYABLE_FAILED 记录。先查候选再逐条条件更新,降低多 worker 重复执行概率。
|
||||
* 抢占 PENDING/RETRYABLE_FAILED 或锁已过期的 RUNNING 记录。先查候选再逐条条件更新,降低多 worker 重复执行概率。
|
||||
*/
|
||||
@Override
|
||||
public List<SuperAgentDispatchRunSnapshot> claimDue(
|
||||
|
||||
@@ -80,7 +80,6 @@ public class SuperAgentDispatchServiceImpl implements SuperAgentDispatchService
|
||||
return;
|
||||
}
|
||||
if (captureResult.inboxId() == null
|
||||
|| !captureResult.created()
|
||||
|| !CAPTURE_STATUS_RECEIVED.equals(captureResult.captureStatus())) {
|
||||
return;
|
||||
}
|
||||
@@ -116,7 +115,10 @@ public class SuperAgentDispatchServiceImpl implements SuperAgentDispatchService
|
||||
LocalDateTime now = nowUtc();
|
||||
List<SuperAgentDispatchRunSnapshot> runs = runRepository.claimDue(
|
||||
"local-worker",
|
||||
List.of(SuperAgentDispatchStatus.PENDING.code(), SuperAgentDispatchStatus.RETRYABLE_FAILED.code()),
|
||||
List.of(
|
||||
SuperAgentDispatchStatus.PENDING.code(),
|
||||
SuperAgentDispatchStatus.RETRYABLE_FAILED.code(),
|
||||
SuperAgentDispatchStatus.RUNNING.code()),
|
||||
now,
|
||||
now.plus(lockTtl()),
|
||||
safeBatchSize());
|
||||
|
||||
@@ -7,17 +7,18 @@ import cn.nianxx.thhotel.integrations.ai.superagent.common.result.SuperAgentOpen
|
||||
import cn.nianxx.thhotel.integrations.ai.superagent.service.SuperAgentOpenApiClient;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
@@ -29,17 +30,31 @@ public class SuperAgentOpenApiClientImpl implements SuperAgentOpenApiClient {
|
||||
private final SuperAgentOpenApiProperties properties;
|
||||
private final SuperAgentOpenApiSseParser sseParser;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final HttpClient injectedHttpClient;
|
||||
|
||||
/**
|
||||
* 注入 SuperAgent 配置、SSE 解析器和 JSON 工具。
|
||||
*/
|
||||
@Autowired
|
||||
public SuperAgentOpenApiClientImpl(
|
||||
SuperAgentOpenApiProperties properties,
|
||||
SuperAgentOpenApiSseParser sseParser,
|
||||
ObjectMapper objectMapper) {
|
||||
this(properties, sseParser, objectMapper, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入可替换的 HTTP 客户端,供单元测试验证低层 HTTP 边界行为。
|
||||
*/
|
||||
SuperAgentOpenApiClientImpl(
|
||||
SuperAgentOpenApiProperties properties,
|
||||
SuperAgentOpenApiSseParser sseParser,
|
||||
ObjectMapper objectMapper,
|
||||
HttpClient injectedHttpClient) {
|
||||
this.properties = properties;
|
||||
this.sseParser = sseParser;
|
||||
this.objectMapper = objectMapper;
|
||||
this.injectedHttpClient = injectedHttpClient;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,7 +144,7 @@ public class SuperAgentOpenApiClientImpl implements SuperAgentOpenApiClient {
|
||||
HttpResponse<InputStream> httpResponse = httpClient.send(
|
||||
httpRequest,
|
||||
HttpResponse.BodyHandlers.ofInputStream());
|
||||
require2xx(httpResponse.statusCode(), null);
|
||||
require2xxOrClose(httpResponse.statusCode(), httpResponse.body());
|
||||
SuperAgentOpenApiSseParser.ParsedState state = sseParser.newState(sessionId);
|
||||
applyRunLocation(baseUri, httpResponse, state);
|
||||
try {
|
||||
@@ -160,6 +175,9 @@ public class SuperAgentOpenApiClientImpl implements SuperAgentOpenApiClient {
|
||||
* 构造 JDK HttpClient;SSE 请求不设置整体 read timeout,避免长任务被固定超时截断。
|
||||
*/
|
||||
private HttpClient httpClient() {
|
||||
if (injectedHttpClient != null) {
|
||||
return injectedHttpClient;
|
||||
}
|
||||
return HttpClient.newBuilder()
|
||||
.connectTimeout(properties.getConnectTimeout())
|
||||
.version(HttpClient.Version.HTTP_1_1)
|
||||
@@ -202,6 +220,31 @@ public class SuperAgentOpenApiClientImpl implements SuperAgentOpenApiClient {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验流式响应状态码;失败时必须关闭响应流,避免连接资源泄漏。
|
||||
*/
|
||||
private void require2xxOrClose(int statusCode, InputStream responseBody) {
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
return;
|
||||
}
|
||||
closeQuietly(responseBody);
|
||||
require2xx(statusCode, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 静默关闭错误响应流;关闭异常不覆盖原始 HTTP 状态错误。
|
||||
*/
|
||||
private void closeQuietly(InputStream responseBody) {
|
||||
if (responseBody == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
responseBody.close();
|
||||
} catch (IOException exception) {
|
||||
// 错误响应流关闭失败不覆盖原始 HTTP 状态错误。
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Content-Location 保存 run URI 和 runId,供 EOF 后恢复使用。
|
||||
*/
|
||||
@@ -297,7 +340,7 @@ public class SuperAgentOpenApiClientImpl implements SuperAgentOpenApiClient {
|
||||
HttpResponse<InputStream> response = httpClient.send(
|
||||
builder.build(),
|
||||
HttpResponse.BodyHandlers.ofInputStream());
|
||||
require2xx(response.statusCode(), null);
|
||||
require2xxOrClose(response.statusCode(), response.body());
|
||||
consumeResponse(response.body(), state, traceConsumer);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user