diff --git a/core/src/main/java/com/google/adk/examples/ExampleUtils.java b/core/src/main/java/com/google/adk/examples/ExampleUtils.java index dfd435f3d..17f12dfb2 100644 --- a/core/src/main/java/com/google/adk/examples/ExampleUtils.java +++ b/core/src/main/java/com/google/adk/examples/ExampleUtils.java @@ -16,16 +16,17 @@ package com.google.adk.examples; -import static com.google.common.collect.ImmutableList.toImmutableList; +import static java.util.stream.Collectors.joining; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.genai.types.Content; +import com.google.genai.types.FunctionCall; +import com.google.genai.types.FunctionResponse; import com.google.genai.types.Part; import java.util.List; -import java.util.Map; /** Utility class for examples. */ public final class ExampleUtils { @@ -59,67 +60,91 @@ public final class ExampleUtils { */ private static String convertExamplesToText(List examples) { StringBuilder examplesStr = new StringBuilder(); - for (int exampleNum = 0; exampleNum < examples.size(); exampleNum++) { - Example example = examples.get(exampleNum); - StringBuilder output = new StringBuilder(); - if (example.input().parts().isPresent() - && !example.input().parts().get().isEmpty() - && example.input().parts().get().get(0).text().isPresent()) { - output - .append(String.format(EXAMPLE_START, exampleNum + 1)) - .append(USER_PREFIX) - .append(example.input().parts().get().get(0).text().get()) - .append("\n\n"); - } + // super header + examplesStr.append(EXAMPLES_INTRO); + + for (int i = 0; i < examples.size(); i++) { + Example example = examples.get(i); + + // header + examplesStr.append(String.format(EXAMPLE_START, i + 1)); + + // user content + appendInput(example, examplesStr); + + // model content for (Content content : example.output()) { - String rolePrefix = content.role().orElse("").equals("model") ? MODEL_PREFIX : USER_PREFIX; - for (Part part : content.parts().orElse(ImmutableList.of())) { - if (part.functionCall().isPresent()) { - Map argsMap = - part.functionCall().get().args().orElse(ImmutableMap.of()); - ImmutableList args = - argsMap.entrySet().stream() - .map( - entry -> { - String key = entry.getKey(); - Object value = entry.getValue(); - if (value instanceof String) { - return String.format("%s='%s'", key, value); - } else { - return String.format("%s=%s", key, value); - } - }) - .collect(toImmutableList()); - output - .append(rolePrefix) - .append(FUNCTION_CALL_PREFIX) - .append(part.functionCall().get().name().orElse("")) - .append("(") - .append(String.join(", ", args)) - .append(")") - .append(FUNCTION_CALL_SUFFIX); - } else if (part.functionResponse().isPresent()) { - try { - output - .append(FUNCTION_RESPONSE_PREFIX) - .append( - OBJECT_MAPPER.writeValueAsString( - part.functionResponse().get().response().orElse(ImmutableMap.of()))) - .append(FUNCTION_RESPONSE_SUFFIX); - } catch (JsonProcessingException e) { - output.append(FUNCTION_RESPONSE_PREFIX).append(FUNCTION_RESPONSE_SUFFIX); - } - } else if (part.text().isPresent()) { - output.append(rolePrefix).append(part.text().orElse("")).append("\n"); - } - } + appendOutput(content, examplesStr); + } + + // footer + examplesStr.append(EXAMPLE_END); + } + + // super footer + examplesStr.append(EXAMPLES_END); + + return examplesStr.toString(); + } + + private static void appendInput(Example example, StringBuilder builder) { + example + .input() + .parts() + .flatMap(parts -> parts.stream().findFirst().flatMap(Part::text)) + .ifPresent(text -> builder.append(USER_PREFIX).append(text).append("\n\n")); + } + + private static void appendOutput(Content output, StringBuilder builder) { + String rolePrefix = output.role().orElse("").equals("model") ? MODEL_PREFIX : USER_PREFIX; + for (Part part : output.parts().orElse(ImmutableList.of())) { + if (part.functionCall().isPresent()) { + appendFunctionCall(part.functionCall().get(), rolePrefix, builder); + } else if (part.functionResponse().isPresent()) { + appendFunctionResponse(part.functionResponse().get(), builder); + } else if (part.text().isPresent()) { + builder.append(rolePrefix).append(part.text().get()).append("\n"); } - output.append(EXAMPLE_END); - examplesStr.append(output); } + } + + private static void appendFunctionCall( + FunctionCall functionCall, String rolePrefix, StringBuilder builder) { + String argsString = + functionCall.args().stream() + .flatMap(argsMap -> argsMap.entrySet().stream()) + .map( + entry -> { + String key = entry.getKey(); + Object value = entry.getValue(); + if (value instanceof String) { + return String.format("%s='%s'", key, value); + } else { + return String.format("%s=%s", key, value); + } + }) + .collect(joining(", ")); + builder + .append(rolePrefix) + .append(FUNCTION_CALL_PREFIX) + .append(functionCall.name().orElse("")) + .append("(") + .append(argsString) + .append(")") + .append(FUNCTION_CALL_SUFFIX); + } - return EXAMPLES_INTRO + examplesStr + EXAMPLES_END; + private static void appendFunctionResponse(FunctionResponse response, StringBuilder builder) { + try { + Object responseMap = response.response().orElse(ImmutableMap.of()); + builder + .append(FUNCTION_RESPONSE_PREFIX) + .append(OBJECT_MAPPER.writeValueAsString(responseMap)) + .append(FUNCTION_RESPONSE_SUFFIX); + } catch (JsonProcessingException e) { + builder.append(FUNCTION_RESPONSE_PREFIX).append(FUNCTION_RESPONSE_SUFFIX); + } } /** diff --git a/core/src/test/java/com/google/adk/examples/ExampleUtilsTest.java b/core/src/test/java/com/google/adk/examples/ExampleUtilsTest.java index c50a9671e..4a1dcf8e3 100644 --- a/core/src/test/java/com/google/adk/examples/ExampleUtilsTest.java +++ b/core/src/test/java/com/google/adk/examples/ExampleUtilsTest.java @@ -296,7 +296,6 @@ public void buildFewShotFewShot_multipleExamples() { assertThat(ExampleUtils.buildExampleSi(exampleProvider, "test query")).isEqualTo(expected); } - // TODO: sduskis - Fix this test case. It should show "EXAMPLE 1:" @Test public void buildFewShotFewShot_onlyOutputExample() { Example example = @@ -313,6 +312,8 @@ public void buildFewShotFewShot_onlyOutputExample() { Begin few-shot The following are examples of user queries and model responses using the available tools. + EXAMPLE 1: + Begin example [model] Model response End example