refs #737: preserve unmatched game mentions in query plan

This commit is contained in:
devmrko
2026-07-27 16:25:33 +09:00
parent 3cc033e4e6
commit 8dffa2f515
5 changed files with 360 additions and 48 deletions

View File

@@ -261,17 +261,47 @@ public class McpSseService {
private ObjectNode gameCatalogVectorResponse(GameCatalogVectorService.Resolution resolution) {
ObjectNode response = objectMapper.createObjectNode();
response.put("scopeType", resolution.scopeType());
response.put("status", resolution.candidates().isEmpty() ? "NO_MATCH" : "CANDIDATES");
boolean hasUnmatched = resolution.mentions().stream()
.anyMatch(mention -> "UNMATCHED".equals(mention.status()));
response.put("status", resolution.candidates().isEmpty()
? "NO_MATCH" : hasUnmatched ? "PARTIAL" : "CANDIDATES");
ArrayNode items = response.putArray("matchedGames");
for (GameCatalogVectorService.GameCandidate c : resolution.candidates()) {
ObjectNode item = items.addObject();
item.put("gameKey", c.gameKey()); item.put("gameId", c.gameId());
item.put("gamePrefix", c.gamePrefix()); item.put("gameName", c.gameName());
item.put("aliases", c.aliases()); item.put("cosineDistance", c.similarity());
addGameCandidate(items.addObject(), c, true);
}
ArrayNode mentionResults = response.putArray("mentionResults");
for (GameCatalogVectorService.MentionResolution mention
: resolution.mentions()) {
ObjectNode item = mentionResults.addObject();
item.put("mention", mention.mention());
item.put("status", mention.status());
if (mention.matchedGame() != null) {
item.put("matchedGameKey", mention.matchedGame().gameKey());
}
ArrayNode candidates = item.putArray("candidateGames");
for (GameCatalogVectorService.GameCandidate candidate
: mention.candidates()) {
addGameCandidate(candidates.addObject(), candidate, false);
}
}
return response;
}
private void addGameCandidate(
ObjectNode item,
GameCatalogVectorService.GameCandidate candidate,
boolean includeAliases
) {
item.put("gameKey", candidate.gameKey());
item.put("gameId", candidate.gameId());
item.put("gamePrefix", candidate.gamePrefix());
item.put("gameName", candidate.gameName());
if (includeAliases) {
item.put("aliases", candidate.aliases());
}
item.put("cosineDistance", candidate.similarity());
}
private ObjectNode qaVectorStoreResponse(QaVectorService.VectorStoreResult stored) {
ObjectNode response = objectMapper.createObjectNode();
response.put("status", "QA_VECTOR_STORED");
@@ -381,7 +411,12 @@ public class McpSseService {
private ObjectNode gameQueryPlanResponse(GameCatalogVectorService.Resolution resolution) {
ObjectNode response = gameCatalogVectorResponse(resolution);
response.put("status", resolution.candidates().isEmpty() ? "NO_MATCH" : "SUPPORTED");
List<GameCatalogVectorService.MentionResolution> unmatchedMentions =
resolution.mentions().stream()
.filter(mention -> "UNMATCHED".equals(mention.status()))
.toList();
response.put("status", resolution.candidates().isEmpty()
? "NO_MATCH" : unmatchedMentions.isEmpty() ? "SUPPORTED" : "PARTIAL");
ArrayNode supported = response.putArray("supportedGames");
for (GameCatalogVectorService.GameCandidate c : resolution.candidates()) {
ObjectNode item = supported.addObject();
@@ -391,7 +426,13 @@ public class McpSseService {
item.put("gamePrefix", c.gamePrefix());
item.put("cosineDistance", c.similarity());
}
response.putArray("unmatchedGames");
ArrayNode unmatched = response.putArray("unmatchedGames");
for (GameCatalogVectorService.MentionResolution mention
: unmatchedMentions) {
ObjectNode item = unmatched.addObject();
item.put("mention", mention.mention());
item.put("reasonCode", "NO_CATALOG_IDENTITY_MATCH");
}
response.put("nextAction", resolution.candidates().isEmpty()
? "STOP_NO_MATCH"
: ("MULTI_GAME".equals(resolution.scopeType())