|
|
@@ -1,13 +1,96 @@
|
|
|
package es.uv.saic.service;
|
|
|
|
|
|
+import com.fasterxml.jackson.core.JsonProcessingException;
|
|
|
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
|
|
+import com.fasterxml.jackson.annotation.JsonProperty;
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
import org.springframework.stereotype.Component;
|
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Locale;
|
|
|
+import java.util.regex.Pattern;
|
|
|
+
|
|
|
@Component
|
|
|
public class ExtractionResponseMapper {
|
|
|
-
|
|
|
+ private static final Pattern JSON_BLOCK_PATTERN = Pattern.compile("(?s)```(?:json)?\\s*(\\{.*})\\s*```");
|
|
|
+
|
|
|
+ private final ObjectMapper objectMapper = new ObjectMapper();
|
|
|
+
|
|
|
+ public ExtractionResponse mapResponse(String llmResponse) {
|
|
|
+ if (StringUtils.isBlank(llmResponse)) {
|
|
|
+ return new ExtractionResponse(
|
|
|
+ "",
|
|
|
+ "",
|
|
|
+ new Items(0, "Warning", List.of()),
|
|
|
+ new Items(0, "Critical", List.of())
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ String payload = extractJsonPayload(llmResponse);
|
|
|
+ try {
|
|
|
+ LlmExtractionResponse llmParsed = objectMapper.readValue(payload, LlmExtractionResponse.class);
|
|
|
+ return mapToExtractionResponse(llmResponse, llmParsed);
|
|
|
+ } catch (JsonProcessingException e) {
|
|
|
+ throw new IllegalArgumentException("LLM response is not valid JSON for expected schema", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- public String mapResponse(String llmResponse) {
|
|
|
- return null;
|
|
|
+ private ExtractionResponse mapToExtractionResponse(String rawAnswer, LlmExtractionResponse llmParsed) {
|
|
|
+ List<Item> warnings = new ArrayList<>();
|
|
|
+ List<Item> criticals = new ArrayList<>();
|
|
|
+ List<LlmItem> items = llmParsed.items() == null ? List.of() : llmParsed.items();
|
|
|
+
|
|
|
+ for (LlmItem llmItem : items) {
|
|
|
+ if (llmItem == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ Item item = new Item(
|
|
|
+ llmItem.code(),
|
|
|
+ llmItem.group(),
|
|
|
+ llmItem.score(),
|
|
|
+ llmItem.level(),
|
|
|
+ llmItem.comment()
|
|
|
+ );
|
|
|
+ String level = StringUtils.defaultString(llmItem.level()).toLowerCase(Locale.ROOT);
|
|
|
+ if (level.contains("critical")) {
|
|
|
+ criticals.add(item);
|
|
|
+ } else if (level.contains("warning")) {
|
|
|
+ warnings.add(item);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return new ExtractionResponse(
|
|
|
+ rawAnswer,
|
|
|
+ llmParsed.okSummary(),
|
|
|
+ new Items(warnings.size(), "Warning", warnings),
|
|
|
+ new Items(criticals.size(), "Critical", criticals)
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
+ private String extractJsonPayload(String llmResponse) {
|
|
|
+ String trimmed = llmResponse.trim();
|
|
|
+ var matcher = JSON_BLOCK_PATTERN.matcher(trimmed);
|
|
|
+ if (matcher.matches()) {
|
|
|
+ return matcher.group(1).trim();
|
|
|
+ }
|
|
|
+ return trimmed;
|
|
|
+ }
|
|
|
+
|
|
|
+ @JsonIgnoreProperties(ignoreUnknown = true)
|
|
|
+ private record LlmExtractionResponse(
|
|
|
+ List<LlmItem> items,
|
|
|
+ @JsonProperty("ok_summary")
|
|
|
+ String okSummary
|
|
|
+ ) {}
|
|
|
+
|
|
|
+ @JsonIgnoreProperties(ignoreUnknown = true)
|
|
|
+ private record LlmItem(
|
|
|
+ String code,
|
|
|
+ String group,
|
|
|
+ Double score,
|
|
|
+ String level,
|
|
|
+ String comment
|
|
|
+ ) {}
|
|
|
}
|