diff --git a/core/src/main/java/com/google/adk/tools/SetModelResponseTool.java b/core/src/main/java/com/google/adk/tools/SetModelResponseTool.java index dad84a73b..4f7022914 100644 --- a/core/src/main/java/com/google/adk/tools/SetModelResponseTool.java +++ b/core/src/main/java/com/google/adk/tools/SetModelResponseTool.java @@ -37,10 +37,7 @@ public class SetModelResponseTool extends BaseTool { public static final String NAME = "set_model_response"; - // Prefix of the SchemaUtils validation message after which the full schema is appended. Used to - // strip the schema dump from feedback on a best-effort basis; if SchemaUtils changes its wording - // the feedback simply stays unstripped. runAsync_unknownArg_feedbackOmitsSchemaDump pins the - // current format. + // Marker after which SchemaUtils appends the schema dump; stripped from feedback best-effort. private static final String OUTPUT_SCHEMA_DUMP_MARKER = " does not match agent output schema: "; private final Schema outputSchema; @@ -92,21 +89,22 @@ private static Map excludeNullFields(Map values) for (Map.Entry entry : values.entrySet()) { Object value = entry.getValue(); if (value != null) { - result.put(entry.getKey(), excludeNullFields(value)); + result.put(entry.getKey(), excludeNullsFromValue(value)); } } return result; } - @SuppressWarnings("unchecked") - private static Object excludeNullFields(Object value) { + private static Object excludeNullsFromValue(Object value) { if (value instanceof Map) { - return excludeNullFields((Map) value); + @SuppressWarnings("unchecked") // Function-call args always have String keys. + Map map = (Map) value; + return excludeNullFields(map); } if (value instanceof List) { List result = new ArrayList<>(); for (Object item : (List) value) { - result.add(excludeNullFields(item)); + result.add(excludeNullsFromValue(item)); } return result; } diff --git a/core/src/test/java/com/google/adk/flows/llmflows/OutputSchemaTest.java b/core/src/test/java/com/google/adk/flows/llmflows/OutputSchemaTest.java index ad0e97df6..d9dbfed0a 100644 --- a/core/src/test/java/com/google/adk/flows/llmflows/OutputSchemaTest.java +++ b/core/src/test/java/com/google/adk/flows/llmflows/OutputSchemaTest.java @@ -183,7 +183,8 @@ public void getStructuredModelResponse_withValidationFeedback_returnsEmpty() { .response( ImmutableMap.of( "error", - "Validation Error found: field1 is required. Fix the errors and call it again.")) + "Validation Error found: field1 is required. Fix the errors and call it" + + " again.")) .build(); Event event = Event.builder() @@ -365,19 +366,19 @@ public BaseLlmConnection connect(LlmRequest llmRequest) { // The retry request must carry the validation feedback from the first call back to the model: // the feedback names the missing required field, tying it to the first call's failure. assertThat(scriptedLlm.getRequests()).hasSize(2); - boolean feedbackSentBack = + Optional feedbackError = scriptedLlm.getRequests().get(1).contents().stream() .flatMap(content -> content.parts().orElse(ImmutableList.of()).stream()) .map(Part::functionResponse) .flatMap(Optional::stream) - .anyMatch( - fr -> - Objects.equals(fr.name().orElse(""), SetModelResponseTool.NAME) - && fr.response().orElse(ImmutableMap.of()).get("error") - instanceof String error - && error.contains("Validation Error found") - && error.contains("field1")); - assertThat(feedbackSentBack).isTrue(); + .filter(fr -> Objects.equals(fr.name().orElse(""), SetModelResponseTool.NAME)) + .map(fr -> fr.response().orElse(ImmutableMap.of()).get("error")) + .filter(String.class::isInstance) + .map(String.class::cast) + .findFirst(); + assertThat(feedbackError).isPresent(); + assertThat(feedbackError.get()).contains("Validation Error found"); + assertThat(feedbackError.get()).contains("field1"); // Only the corrected, validated response becomes the final structured output. Event finalEvent = events.get(events.size() - 1);