diff --git a/core/src/main/java/com/google/adk/agents/BaseAgent.java b/core/src/main/java/com/google/adk/agents/BaseAgent.java index fc1f0f31e..0a3c550a4 100644 --- a/core/src/main/java/com/google/adk/agents/BaseAgent.java +++ b/core/src/main/java/com/google/adk/agents/BaseAgent.java @@ -135,7 +135,7 @@ private static void validateAgentName(String name) { throw new IllegalArgumentException( format("Agent name '%s' does not match regex '%s'.", name, IDENTIFIER_REGEX)); } - if (name.equals("user")) { + if (name.equals(Role.USER)) { throw new IllegalArgumentException( "Agent name cannot be 'user'; reserved for end-user input."); } diff --git a/core/src/main/java/com/google/adk/agents/Role.java b/core/src/main/java/com/google/adk/agents/Role.java new file mode 100644 index 000000000..27f95e89c --- /dev/null +++ b/core/src/main/java/com/google/adk/agents/Role.java @@ -0,0 +1,25 @@ +/* + * Copyright 2026 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; + +/** Standard role names for {@code Content} and event authors. */ +public final class Role { + /** The user interacting with the agent or model: a {@code Content} role and an event author. */ + public static final String USER = "user"; + + private Role() {} +} diff --git a/core/src/main/java/com/google/adk/apps/App.java b/core/src/main/java/com/google/adk/apps/App.java index 3133357bd..9120954b5 100644 --- a/core/src/main/java/com/google/adk/apps/App.java +++ b/core/src/main/java/com/google/adk/apps/App.java @@ -18,6 +18,7 @@ import com.google.adk.agents.BaseAgent; import com.google.adk.agents.ContextCacheConfig; +import com.google.adk.agents.Role; import com.google.adk.plugins.Plugin; import com.google.adk.summarizer.EventsCompactionConfig; import com.google.common.collect.ImmutableList; @@ -168,7 +169,7 @@ private static void validateAppName(String name) { + name + "': must be a valid identifier consisting of letters, digits, and underscores."); } - if (name.equals("user")) { + if (name.equals(Role.USER)) { throw new IllegalArgumentException("App name cannot be 'user'; reserved for end-user input."); } } diff --git a/core/src/main/java/com/google/adk/codeexecutors/CodeExecutionUtils.java b/core/src/main/java/com/google/adk/codeexecutors/CodeExecutionUtils.java index a8322b228..a3ae78e86 100644 --- a/core/src/main/java/com/google/adk/codeexecutors/CodeExecutionUtils.java +++ b/core/src/main/java/com/google/adk/codeexecutors/CodeExecutionUtils.java @@ -22,6 +22,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.google.adk.JsonBaseModel; +import com.google.adk.agents.Role; import com.google.auto.value.AutoValue; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; @@ -114,7 +115,7 @@ public static Content convertCodeExecutionParts( + lastPart.codeExecutionResult().get().output() + executionResultDelimiters.get(1)); newParts.set(newParts.size() - 1, newPart); - return Content.builder().parts(newParts).role("user").build(); + return Content.builder().parts(newParts).role(Role.USER).build(); } return content; diff --git a/core/src/main/java/com/google/adk/flows/llmflows/CodeExecution.java b/core/src/main/java/com/google/adk/flows/llmflows/CodeExecution.java index d76cd1a04..e681e48e9 100644 --- a/core/src/main/java/com/google/adk/flows/llmflows/CodeExecution.java +++ b/core/src/main/java/com/google/adk/flows/llmflows/CodeExecution.java @@ -22,6 +22,7 @@ import com.google.adk.agents.InvocationContext; import com.google.adk.agents.LlmAgent; +import com.google.adk.agents.Role; import com.google.adk.codeexecutors.BuiltInCodeExecutor; import com.google.adk.codeexecutors.CodeExecutionUtils; import com.google.adk.codeexecutors.CodeExecutionUtils.CodeExecutionInput; @@ -347,7 +348,7 @@ private static List extractAndReplaceInlineFiles( for (int i = 0; i < llmRequest.contents().size(); i++) { Content content = llmRequest.contents().get(i); if (content.role().isEmpty() - || !Objects.equals(content.role().get(), "user") + || !Objects.equals(content.role().get(), Role.USER) || content.parts().isEmpty()) { continue; } diff --git a/core/src/main/java/com/google/adk/flows/llmflows/Contents.java b/core/src/main/java/com/google/adk/flows/llmflows/Contents.java index a772accb4..a80dfa09d 100644 --- a/core/src/main/java/com/google/adk/flows/llmflows/Contents.java +++ b/core/src/main/java/com/google/adk/flows/llmflows/Contents.java @@ -23,6 +23,7 @@ import com.google.adk.JsonBaseModel; import com.google.adk.agents.InvocationContext; import com.google.adk.agents.LlmAgent; +import com.google.adk.agents.Role; import com.google.adk.events.Event; import com.google.adk.events.EventCompaction; import com.google.adk.models.LlmRequest; @@ -112,7 +113,7 @@ private ImmutableList getCurrentTurnContents( // Find the latest event that starts the current turn and process from there. for (int i = events.size() - 1; i >= 0; i--) { Event event = events.get(i); - if (event.author().equals("user") || isOtherAgentReply(agentName, event)) { + if (event.author().equals(Role.USER) || isOtherAgentReply(agentName, event)) { return getContents( currentBranch, events.subList(i, events.size()), agentName, groupFunctionResponses); } @@ -404,7 +405,7 @@ private static Event createCompactionEvent(Event event) { private static boolean isOtherAgentReply(String agentName, Event event) { return !agentName.isEmpty() && !event.author().equals(agentName) - && !event.author().equals("user"); + && !event.author().equals(Role.USER); } /** @@ -482,8 +483,8 @@ private static boolean isOtherAgentReply(String agentName, Event event) { return null; } - Content content = Content.builder().role("user").parts(parts).build(); - return event.toBuilder().author("user").content(content).build(); + Content content = Content.builder().role(Role.USER).parts(parts).build(); + return event.toBuilder().author(Role.USER).content(content).build(); } private static String convertMapToJson(Map struct) { diff --git a/core/src/main/java/com/google/adk/flows/llmflows/Functions.java b/core/src/main/java/com/google/adk/flows/llmflows/Functions.java index 3f3b8ef86..2b5c07435 100644 --- a/core/src/main/java/com/google/adk/flows/llmflows/Functions.java +++ b/core/src/main/java/com/google/adk/flows/llmflows/Functions.java @@ -25,6 +25,7 @@ import com.google.adk.agents.Callbacks.OnToolErrorCallback; import com.google.adk.agents.InvocationContext; import com.google.adk.agents.LlmAgent; +import com.google.adk.agents.Role; import com.google.adk.agents.RunConfig.ToolExecutionMode; import com.google.adk.events.Event; import com.google.adk.events.EventActions; @@ -379,7 +380,7 @@ private static Maybe> processFunctionLive( result -> { String resultText = "Function " + tool.name() + " returned: " + result; Content updateContent = - Content.builder().role("user").parts(Part.fromText(resultText)).build(); + Content.builder().role(Role.USER).parts(Part.fromText(resultText)).build(); invocationContext.liveRequestQueue().get().content(updateContent); }, error -> logger.error("Error in streaming tool " + tool.name(), error.getCause()), @@ -588,7 +589,7 @@ private static Optional mergeParallelFunctionResponseEvents( .invocationId(baseEvent.invocationId()) .author(baseEvent.author()) .branch(baseEvent.branch().orElse(null)) - .content(Content.builder().role("user").parts(mergedParts).build()) + .content(Content.builder().role(Role.USER).parts(mergedParts).build()) .actions(mergedActionsBuilder.build()) .timestamp(baseEvent.timestamp()) .build()); @@ -739,7 +740,7 @@ private static Event buildResponseEvent( .invocationId(invocationContext.invocationId()) .author(invocationContext.agent().name()) .branch(invocationContext.branch().orElse(null)) - .content(Content.builder().role("user").parts(partFunctionResponse).build()) + .content(Content.builder().role(Role.USER).parts(partFunctionResponse).build()) .actions(toolContext.eventActions()) .build(); } diff --git a/core/src/main/java/com/google/adk/flows/llmflows/RequestConfirmationLlmRequestProcessor.java b/core/src/main/java/com/google/adk/flows/llmflows/RequestConfirmationLlmRequestProcessor.java index 6f73a0a8d..06e50e76d 100644 --- a/core/src/main/java/com/google/adk/flows/llmflows/RequestConfirmationLlmRequestProcessor.java +++ b/core/src/main/java/com/google/adk/flows/llmflows/RequestConfirmationLlmRequestProcessor.java @@ -26,6 +26,7 @@ import com.google.adk.JsonBaseModel; import com.google.adk.agents.InvocationContext; import com.google.adk.agents.LlmAgent; +import com.google.adk.agents.Role; import com.google.adk.events.Event; import com.google.adk.events.ToolConfirmation; import com.google.adk.models.LlmRequest; @@ -169,7 +170,7 @@ private static Optional findMostRecentConfirmations( // function responses. for (int i = events.size() - 1; i >= 0; i--) { Event event = events.get(i); - if (!Objects.equals(event.author(), "user") || event.functionResponses().isEmpty()) { + if (!Objects.equals(event.author(), Role.USER) || event.functionResponses().isEmpty()) { continue; } diff --git a/core/src/main/java/com/google/adk/models/GeminiUtil.java b/core/src/main/java/com/google/adk/models/GeminiUtil.java index ab508be92..5986c3b5e 100644 --- a/core/src/main/java/com/google/adk/models/GeminiUtil.java +++ b/core/src/main/java/com/google/adk/models/GeminiUtil.java @@ -17,6 +17,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList; +import com.google.adk.agents.Role; import com.google.common.base.Ascii; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -202,11 +203,11 @@ private static Part removeClientFunctionCallIdFromPart(Part part) { static List ensureModelResponse(List contents) { // Last content must be from the user, otherwise the model won't respond. if (contents.isEmpty() - || !Ascii.equalsIgnoreCase(Iterables.getLast(contents).role().orElse(""), "user")) { + || !Ascii.equalsIgnoreCase(Iterables.getLast(contents).role().orElse(""), Role.USER)) { Content userContent = Content.builder() .parts(ImmutableList.of(Part.fromText(CONTINUE_OUTPUT_MESSAGE))) - .role("user") + .role(Role.USER) .build(); return Stream.concat(contents.stream(), Stream.of(userContent)).collect(toImmutableList()); } 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 760a7c1c6..d6b56acdb 100644 --- a/core/src/main/java/com/google/adk/models/LlmRequest.java +++ b/core/src/main/java/com/google/adk/models/LlmRequest.java @@ -25,6 +25,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.google.adk.JsonBaseModel; +import com.google.adk.agents.Role; import com.google.adk.tools.BaseTool; import com.google.auto.value.AutoValue; import com.google.common.collect.ImmutableList; @@ -195,7 +196,7 @@ private Content addInstructions( .map(text -> text + "\n\n" + instructions) .orElse(instructions)); - String role = currentSystemInstruction.flatMap(Content::role).orElse("user"); + String role = currentSystemInstruction.flatMap(Content::role).orElse(Role.USER); return Content.builder().parts(part).role(role).build(); } diff --git a/core/src/main/java/com/google/adk/models/chat/ChatCompletionsRequest.java b/core/src/main/java/com/google/adk/models/chat/ChatCompletionsRequest.java index e8eb159d8..78605d9d8 100644 --- a/core/src/main/java/com/google/adk/models/chat/ChatCompletionsRequest.java +++ b/core/src/main/java/com/google/adk/models/chat/ChatCompletionsRequest.java @@ -28,6 +28,7 @@ import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.module.SimpleModule; import com.google.adk.JsonBaseModel; +import com.google.adk.agents.Role; import com.google.adk.models.LlmRequest; import com.google.common.base.Ascii; import com.google.common.collect.ImmutableList; @@ -377,7 +378,7 @@ private static Optional processSystemInstruction( */ private static List processContent(Content content) { Message msg = new Message(); - String role = content.role().orElse("user"); + String role = content.role().orElse(Role.USER); msg.role = role.equals("model") ? "assistant" : role; List contentParts = new ArrayList<>(); diff --git a/core/src/main/java/com/google/adk/plugins/ContextFilterPlugin.java b/core/src/main/java/com/google/adk/plugins/ContextFilterPlugin.java index fb712dd18..05a53be55 100644 --- a/core/src/main/java/com/google/adk/plugins/ContextFilterPlugin.java +++ b/core/src/main/java/com/google/adk/plugins/ContextFilterPlugin.java @@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument; import com.google.adk.agents.CallbackContext; +import com.google.adk.agents.Role; import com.google.adk.models.LlmRequest; import com.google.adk.models.LlmResponse; import com.google.errorprone.annotations.CanIgnoreReturnValue; @@ -63,7 +64,6 @@ public class ContextFilterPlugin extends BasePlugin { private static final Logger logger = LoggerFactory.getLogger(ContextFilterPlugin.class); private static final String MODEL_ROLE = "model"; - private static final String USER_ROLE = "user"; private final Optional numInvocationsToKeep; private final Optional>> customFilter; @@ -159,7 +159,7 @@ private List trimContentsByInvocations(int numInvocations, List 0 - && hasRole(contents.get(finalSplitIndex - 1), USER_ROLE) + && hasRole(contents.get(finalSplitIndex - 1), Role.USER) && !isFunctionResponse(contents.get(finalSplitIndex - 1))) { finalSplitIndex--; } @@ -174,7 +174,7 @@ private int findNthModelTurnStartIndex(int numInvocations, List content if (modelTurnsToFind == 0) { int startIndex = i; // Include all preceding user messages in the same turn. - while (startIndex > 0 && hasRole(contents.get(startIndex - 1), USER_ROLE)) { + while (startIndex > 0 && hasRole(contents.get(startIndex - 1), Role.USER)) { startIndex--; } return startIndex; diff --git a/core/src/main/java/com/google/adk/runner/Runner.java b/core/src/main/java/com/google/adk/runner/Runner.java index 48eb9fad6..5e23eb78c 100644 --- a/core/src/main/java/com/google/adk/runner/Runner.java +++ b/core/src/main/java/com/google/adk/runner/Runner.java @@ -24,6 +24,7 @@ import com.google.adk.agents.InvocationContext; import com.google.adk.agents.LiveRequestQueue; import com.google.adk.agents.LlmAgent; +import com.google.adk.agents.Role; import com.google.adk.agents.RunConfig; import com.google.adk.agents.SequentialAgent; import com.google.adk.apps.App; @@ -391,7 +392,7 @@ private Single appendNewMessageToSession( Event.builder() .id(Event.generateEventId()) .invocationId(invocationContext.invocationId()) - .author("user") + .author(Role.USER) .content(messageToAppend); // Add state delta if provided @@ -867,7 +868,7 @@ private BaseAgent findAgentToRun(Session session, BaseAgent rootAgent) { if (author == null) { continue; } - if (author.equals("user")) { + if (author.equals(Role.USER)) { continue; } diff --git a/core/src/main/java/com/google/adk/summarizer/LlmEventSummarizer.java b/core/src/main/java/com/google/adk/summarizer/LlmEventSummarizer.java index 48808ebdf..7d1c8a821 100644 --- a/core/src/main/java/com/google/adk/summarizer/LlmEventSummarizer.java +++ b/core/src/main/java/com/google/adk/summarizer/LlmEventSummarizer.java @@ -20,6 +20,7 @@ import static java.util.stream.Collectors.joining; import com.google.adk.JsonBaseModel; +import com.google.adk.agents.Role; import com.google.adk.events.Event; import com.google.adk.events.EventActions; import com.google.adk.events.EventCompaction; @@ -76,7 +77,7 @@ public Maybe summarizeEvents(List events) { .contents( ImmutableList.of( Content.builder() - .role("user") + .role(Role.USER) .parts(ImmutableList.of(Part.fromText(prompt))) .build())) .build(); @@ -101,7 +102,7 @@ public Maybe summarizeEvents(List events) { compaction -> Event.builder() .id(Event.generateEventId()) - .author("user") + .author(Role.USER) .actions(EventActions.builder().compaction(compaction).build()) .invocationId(Event.generateEventId()) .build())));