diff --git a/core/src/main/java/com/google/adk/flows/llmflows/Identity.java b/core/src/main/java/com/google/adk/flows/llmflows/Identity.java index 54aeb3b3..86069356 100644 --- a/core/src/main/java/com/google/adk/flows/llmflows/Identity.java +++ b/core/src/main/java/com/google/adk/flows/llmflows/Identity.java @@ -35,12 +35,10 @@ public Single processRequest( StringBuilder builder = new StringBuilder() .append("You are an agent. Your internal name is ") - .append("\"") .append(agent.name()) - .append("\"") .append("."); if (!Strings.isNullOrEmpty(agent.description())) { - builder.append(" The description about you is \"").append(agent.description()).append("\"."); + builder.append(" The description about you is ").append(agent.description()); } return Single.just( RequestProcessor.RequestProcessingResult.create( diff --git a/core/src/main/java/com/google/adk/models/LlmRequest.java b/core/src/main/java/com/google/adk/models/LlmRequest.java index 6d1f8dd3..9985e585 100644 --- a/core/src/main/java/com/google/adk/models/LlmRequest.java +++ b/core/src/main/java/com/google/adk/models/LlmRequest.java @@ -16,8 +16,6 @@ package com.google.adk.models; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableMap.toImmutableMap; @@ -172,29 +170,14 @@ public final Builder appendInstructions(List instructions) { return liveConnectConfig(liveCfg.toBuilder().systemInstruction(newLiveSi).build()); } - private Content addInstructions( - Optional currentSystemInstruction, List additionalInstructions) { - checkArgument( - currentSystemInstruction.isEmpty() - || currentSystemInstruction.get().parts().map(parts -> parts.size()).orElse(0) <= 1, - "At most one instruction is supported."); - - // Either append to the existing instruction, or create a new one. - String instructions = String.join("\n\n", additionalInstructions); - - Optional part = - currentSystemInstruction - .flatMap(Content::parts) - .flatMap(parts -> parts.stream().findFirst()); - if (part.isEmpty() || part.get().text().isEmpty()) { - part = Optional.of(Part.fromText(instructions)); - } else { - part = Optional.of(Part.fromText(part.get().text().get() + "\n\n" + instructions)); - } - checkState(part.isPresent(), "Failed to create instruction."); + private Content addInstructions(Optional currentSi, List newInst) { + ImmutableList.Builder parts = ImmutableList.builder(); + currentSi.flatMap(Content::parts).ifPresent(parts::addAll); + + newInst.stream().map(Part::fromText).forEach(parts::add); - String role = currentSystemInstruction.flatMap(Content::role).orElse("user"); - return Content.builder().parts(part.get()).role(role).build(); + String role = currentSi.flatMap(Content::role).orElse("user"); + return Content.builder().parts(parts.build()).role(role).build(); } @CanIgnoreReturnValue diff --git a/core/src/test/java/com/google/adk/agents/CallbacksTest.java b/core/src/test/java/com/google/adk/agents/CallbacksTest.java index c743e5c0..85c64106 100644 --- a/core/src/test/java/com/google/adk/agents/CallbacksTest.java +++ b/core/src/test/java/com/google/adk/agents/CallbacksTest.java @@ -1,3 +1,19 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.google.adk.agents; import static com.google.adk.testing.TestUtils.createEvent; diff --git a/core/src/test/java/com/google/adk/agents/InstructionTest.java b/core/src/test/java/com/google/adk/agents/InstructionTest.java index b93343a9..ba590af5 100644 --- a/core/src/test/java/com/google/adk/agents/InstructionTest.java +++ b/core/src/test/java/com/google/adk/agents/InstructionTest.java @@ -1,3 +1,19 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.google.adk.agents; import static com.google.adk.testing.TestUtils.createInvocationContext; diff --git a/core/src/test/java/com/google/adk/agents/ParallelAgentTest.java b/core/src/test/java/com/google/adk/agents/ParallelAgentTest.java index b4049e0c..fe897326 100644 --- a/core/src/test/java/com/google/adk/agents/ParallelAgentTest.java +++ b/core/src/test/java/com/google/adk/agents/ParallelAgentTest.java @@ -1,3 +1,19 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.google.adk.agents; import static com.google.adk.testing.TestUtils.createInvocationContext; diff --git a/core/src/test/java/com/google/adk/agents/RunConfigTest.java b/core/src/test/java/com/google/adk/agents/RunConfigTest.java index 83bc82c9..7b6e7558 100644 --- a/core/src/test/java/com/google/adk/agents/RunConfigTest.java +++ b/core/src/test/java/com/google/adk/agents/RunConfigTest.java @@ -1,3 +1,19 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.google.adk.agents; import static com.google.common.truth.Truth.assertThat; diff --git a/core/src/test/java/com/google/adk/events/EventActionsTest.java b/core/src/test/java/com/google/adk/events/EventActionsTest.java new file mode 100644 index 00000000..12684371 --- /dev/null +++ b/core/src/test/java/com/google/adk/events/EventActionsTest.java @@ -0,0 +1,90 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.adk.events; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.adk.tools.ToolConfirmation; +import com.google.common.collect.ImmutableMap; +import com.google.genai.types.Part; +import java.util.concurrent.ConcurrentHashMap; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public final class EventActionsTest { + + private static final Part PART = Part.builder().text("text").build(); + private static final ToolConfirmation TOOL_CONFIRMATION = + ToolConfirmation.builder().hint("hint").confirmed(true).build(); + + @Test + public void toBuilder_createsBuilderWithSameValues() { + EventActions eventActionsWithSkipSummarization = + EventActions.builder().skipSummarization(true).build(); + + EventActions eventActionsAfterRebuild = eventActionsWithSkipSummarization.toBuilder().build(); + + assertThat(eventActionsAfterRebuild).isEqualTo(eventActionsWithSkipSummarization); + } + + @Test + public void merge_mergesAllFields() { + EventActions eventActions1 = + EventActions.builder() + .skipSummarization(true) + .stateDelta(new ConcurrentHashMap<>(ImmutableMap.of("key1", "value1"))) + .artifactDelta(new ConcurrentHashMap<>(ImmutableMap.of("artifact1", PART))) + .requestedAuthConfigs( + new ConcurrentHashMap<>( + ImmutableMap.of("config1", new ConcurrentHashMap<>(ImmutableMap.of("k", "v"))))) + .requestedToolConfirmations( + new ConcurrentHashMap<>(ImmutableMap.of("tool1", TOOL_CONFIRMATION))) + .build(); + EventActions eventActions2 = + EventActions.builder() + .stateDelta(new ConcurrentHashMap<>(ImmutableMap.of("key2", "value2"))) + .artifactDelta(new ConcurrentHashMap<>(ImmutableMap.of("artifact2", PART))) + .transferToAgent("agentId") + .escalate(true) + .requestedAuthConfigs( + new ConcurrentHashMap<>( + ImmutableMap.of("config2", new ConcurrentHashMap<>(ImmutableMap.of("k", "v"))))) + .requestedToolConfirmations( + new ConcurrentHashMap<>(ImmutableMap.of("tool2", TOOL_CONFIRMATION))) + .endInvocation(true) + .build(); + + EventActions merged = eventActions1.toBuilder().merge(eventActions2).build(); + + assertThat(merged.skipSummarization()).hasValue(true); + assertThat(merged.stateDelta()).containsExactly("key1", "value1", "key2", "value2"); + assertThat(merged.artifactDelta()).containsExactly("artifact1", PART, "artifact2", PART); + assertThat(merged.transferToAgent()).hasValue("agentId"); + assertThat(merged.escalate()).hasValue(true); + assertThat(merged.requestedAuthConfigs()) + .containsExactly( + "config1", + new ConcurrentHashMap<>(ImmutableMap.of("k", "v")), + "config2", + new ConcurrentHashMap<>(ImmutableMap.of("k", "v"))); + assertThat(merged.requestedToolConfirmations()) + .containsExactly("tool1", TOOL_CONFIRMATION, "tool2", TOOL_CONFIRMATION); + assertThat(merged.endInvocation()).hasValue(true); + } +} diff --git a/core/src/test/java/com/google/adk/events/EventTest.java b/core/src/test/java/com/google/adk/events/EventTest.java index a0e82c66..358ac8bd 100644 --- a/core/src/test/java/com/google/adk/events/EventTest.java +++ b/core/src/test/java/com/google/adk/events/EventTest.java @@ -1,3 +1,19 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.google.adk.events; import static com.google.common.truth.Truth.assertThat; diff --git a/core/src/test/java/com/google/adk/flows/llmflows/InstructionsTest.java b/core/src/test/java/com/google/adk/flows/llmflows/InstructionsTest.java index 623caabb..877c9f4a 100644 --- a/core/src/test/java/com/google/adk/flows/llmflows/InstructionsTest.java +++ b/core/src/test/java/com/google/adk/flows/llmflows/InstructionsTest.java @@ -257,6 +257,7 @@ public void processRequest_agentInstructionAndGlobalInstruction_bothAreAppendedT instructionsProcessor.processRequest(context, initialRequest).blockingGet(); assertThat(result.updatedRequest().getSystemInstructions()) - .containsExactly("Global instruction.\n\nAgent instruction."); + .containsExactly("Global instruction.", "Agent instruction.") + .inOrder(); } } diff --git a/core/src/test/java/com/google/adk/models/LlmRequestTest.java b/core/src/test/java/com/google/adk/models/LlmRequestTest.java index 56c2debd..646e2c4d 100644 --- a/core/src/test/java/com/google/adk/models/LlmRequestTest.java +++ b/core/src/test/java/com/google/adk/models/LlmRequestTest.java @@ -120,9 +120,9 @@ public void appendInstructions_existingInstruction_appendsNewInstructionCorrectl assertThat(request.config()).isPresent(); Content systemInstruction = request.config().get().systemInstruction().get(); assertThat(systemInstruction.role()).hasValue("system"); - assertThat(systemInstruction.parts().get()).hasSize(1); - assertThat(systemInstruction.parts().get().get(0).text()) - .hasValue(initialInstructionText + "\n\n" + newInstructionText); + assertThat(systemInstruction.parts().get()).hasSize(2); + assertThat(systemInstruction.parts().get().get(0).text()).hasValue(initialInstructionText); + assertThat(systemInstruction.parts().get().get(1).text()).hasValue(newInstructionText); assertThat(request.liveConnectConfig().systemInstruction()).isPresent(); Content liveSystemInstruction = request.liveConnectConfig().systemInstruction().get(); @@ -154,9 +154,10 @@ public void appendInstructions_existingInstruction_appendsNewInstructionCorrectl assertThat(request.liveConnectConfig().systemInstruction()).isPresent(); Content liveSystemInstruction = request.liveConnectConfig().systemInstruction().get(); assertThat(liveSystemInstruction.role()).hasValue("system"); // Role preserved - assertThat(liveSystemInstruction.parts().get()).hasSize(1); + assertThat(liveSystemInstruction.parts().get()).hasSize(2); assertThat(liveSystemInstruction.parts().get().get(0).text()) - .hasValue(initialLiveInstructionText + "\n\n" + newInstructionText); + .hasValue(initialLiveInstructionText); + assertThat(liveSystemInstruction.parts().get().get(1).text()).hasValue(newInstructionText); // Assertions for main config (should get the new instruction with default role) assertThat(request.config()).isPresent(); @@ -195,16 +196,16 @@ public void appendInstructions_multipleInstructions_appendsAllInOrder() { assertThat(request.config()).isPresent(); Content systemInstruction = request.config().get().systemInstruction().get(); - assertThat(systemInstruction.parts().get()).hasSize(1); - assertThat(systemInstruction.parts().get().get(0).text()) - .hasValue(instruction1 + "\n\n" + instruction2); + assertThat(systemInstruction.parts().get()).hasSize(2); + assertThat(systemInstruction.parts().get().get(0).text()).hasValue(instruction1); + assertThat(systemInstruction.parts().get().get(1).text()).hasValue(instruction2); assertThat(request.liveConnectConfig().systemInstruction()).isPresent(); Content liveSystemInstruction = request.liveConnectConfig().systemInstruction().get(); assertThat(liveSystemInstruction.role()).hasValue("user"); - assertThat(liveSystemInstruction.parts().get()).hasSize(1); - assertThat(liveSystemInstruction.parts().get().get(0).text()) - .hasValue(instruction1 + "\n\n" + instruction2); + assertThat(liveSystemInstruction.parts().get()).hasSize(2); + assertThat(liveSystemInstruction.parts().get().get(0).text()).hasValue(instruction1); + assertThat(liveSystemInstruction.parts().get().get(1).text()).hasValue(instruction2); } @Test @@ -305,7 +306,7 @@ public void getSystemInstructions_whenPresent_returnsList() { .appendInstructions(ImmutableList.of(instruction1, instruction2)) .build(); assertThat(request.getSystemInstructions()) - .containsExactly(instruction1 + "\n\n" + instruction2) + .containsExactly(instruction1, instruction2) .inOrder(); } } diff --git a/core/src/test/java/com/google/adk/plugins/BasePluginTest.java b/core/src/test/java/com/google/adk/plugins/BasePluginTest.java index 7f8ce16c..9a4a243c 100644 --- a/core/src/test/java/com/google/adk/plugins/BasePluginTest.java +++ b/core/src/test/java/com/google/adk/plugins/BasePluginTest.java @@ -1,3 +1,19 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.google.adk.plugins; import com.google.adk.agents.CallbackContext; diff --git a/core/src/test/java/com/google/adk/plugins/LoggingPluginTest.java b/core/src/test/java/com/google/adk/plugins/LoggingPluginTest.java index abd7003a..4c90c11b 100644 --- a/core/src/test/java/com/google/adk/plugins/LoggingPluginTest.java +++ b/core/src/test/java/com/google/adk/plugins/LoggingPluginTest.java @@ -1,3 +1,19 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.google.adk.plugins; import static org.mockito.Mockito.when; diff --git a/core/src/test/java/com/google/adk/plugins/PluginManagerTest.java b/core/src/test/java/com/google/adk/plugins/PluginManagerTest.java index b6070925..4737d6cd 100644 --- a/core/src/test/java/com/google/adk/plugins/PluginManagerTest.java +++ b/core/src/test/java/com/google/adk/plugins/PluginManagerTest.java @@ -1,3 +1,19 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.google.adk.plugins; import static com.google.common.truth.Truth.assertThat; diff --git a/core/src/test/java/com/google/adk/tools/ToolConfirmationTest.java b/core/src/test/java/com/google/adk/tools/ToolConfirmationTest.java new file mode 100644 index 00000000..9410f306 --- /dev/null +++ b/core/src/test/java/com/google/adk/tools/ToolConfirmationTest.java @@ -0,0 +1,55 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.adk.tools; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public final class ToolConfirmationTest { + + @Test + public void builder_setsDefaultValues() { + ToolConfirmation toolConfirmation = ToolConfirmation.builder().build(); + + assertThat(toolConfirmation.hint()).isEmpty(); + assertThat(toolConfirmation.confirmed()).isFalse(); + assertThat(toolConfirmation.payload()).isNull(); + } + + @Test + public void builder_setsValues() { + ToolConfirmation toolConfirmation = + ToolConfirmation.builder().hint("hint").confirmed(true).payload("payload").build(); + + assertThat(toolConfirmation.hint()).isEqualTo("hint"); + assertThat(toolConfirmation.confirmed()).isTrue(); + assertThat(toolConfirmation.payload()).isEqualTo("payload"); + } + + @Test + public void toBuilder_createsBuilderWithSameValues() { + ToolConfirmation toolConfirmation = + ToolConfirmation.builder().hint("hint").confirmed(true).payload("payload").build(); + ToolConfirmation copiedToolConfirmation = toolConfirmation.toBuilder().build(); + + assertThat(copiedToolConfirmation).isEqualTo(toolConfirmation); + } +}