refs #739: normalize MCP query plan envelopes
This commit is contained in:
@@ -2,6 +2,7 @@ package com.cloudhandson.vpdbackoffice.service;
|
||||
|
||||
import com.cloudhandson.vpdbackoffice.config.BackofficeProperties;
|
||||
import com.cloudhandson.vpdbackoffice.domain.token.BearerTokenRecord;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
@@ -185,14 +186,16 @@ public class SelectAiService {
|
||||
}
|
||||
|
||||
private QueryPlanContext requiredQueryPlan(JsonNode plan) {
|
||||
String targetType = plan.path("targetType").asText("").trim().toUpperCase(Locale.ROOT);
|
||||
JsonNode normalizedPlan = unwrapMcpToolResult(plan);
|
||||
String targetType = normalizedPlan.path("targetType")
|
||||
.asText("").trim().toUpperCase(Locale.ROOT);
|
||||
if (!Set.of("NONE", "SINGLE", "MULTI", "ALL").contains(targetType)
|
||||
|| !plan.path("targets").isArray()) {
|
||||
|| !normalizedPlan.path("targets").isArray()) {
|
||||
throw new AppException(
|
||||
"queryPlan은 targetType(NONE/SINGLE/MULTI/ALL)과 targets 배열이 필요합니다.");
|
||||
}
|
||||
Set<String> allowedPrefixes = new LinkedHashSet<>();
|
||||
for (JsonNode target : plan.path("targets")) {
|
||||
for (JsonNode target : normalizedPlan.path("targets")) {
|
||||
String prefix = target.path("gamePrefix").asText("").trim();
|
||||
if (!prefix.isEmpty()) {
|
||||
allowedPrefixes.add(prefix.toUpperCase(Locale.ROOT));
|
||||
@@ -200,13 +203,40 @@ public class SelectAiService {
|
||||
}
|
||||
return new QueryPlanContext(
|
||||
targetType,
|
||||
plan.path("status").asText(""),
|
||||
plan.path("targets").size(),
|
||||
normalizedPlan.path("status").asText(""),
|
||||
normalizedPlan.path("targets").size(),
|
||||
Set.copyOf(allowedPrefixes),
|
||||
plan.deepCopy()
|
||||
normalizedPlan.deepCopy()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept the direct game-query-plan contract as well as the standard MCP
|
||||
* tools/call envelope the ReAct model can copy from a preceding observation.
|
||||
* This is transport normalization only; the target contract itself remains
|
||||
* the authoritative data-driven policy.
|
||||
*/
|
||||
private JsonNode unwrapMcpToolResult(JsonNode candidate) {
|
||||
JsonNode current = candidate;
|
||||
for (int depth = 0; depth < 3 && current != null; depth++) {
|
||||
if (current.isTextual()) {
|
||||
try {
|
||||
current = objectMapper.readTree(current.asText());
|
||||
continue;
|
||||
} catch (JsonProcessingException ignored) {
|
||||
return current;
|
||||
}
|
||||
}
|
||||
JsonNode response = current.path("response");
|
||||
if (response.isObject() || response.isTextual()) {
|
||||
current = response;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return current == null ? objectMapper.createObjectNode() : current;
|
||||
}
|
||||
|
||||
private GameContext resolveGameContext(String bearerToken, String question) {
|
||||
try {
|
||||
List<GameCatalogVectorService.GameCandidate> candidates =
|
||||
|
||||
Reference in New Issue
Block a user