Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions core/src/main/java/com/google/adk/agents/BaseAgentState.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* 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;

import com.google.adk.JsonBaseModel;

/** Base class for all agent states. */
public class BaseAgentState extends JsonBaseModel {

protected BaseAgentState() {}

/** Returns a new {@link Builder} for creating {@link BaseAgentState} instances. */
public static Builder builder() {
return new Builder();
}

/** Builder for {@link BaseAgentState}. */
public static class Builder {
private Builder() {}

public BaseAgentState build() {
return new BaseAgentState();
}
}
}
123 changes: 121 additions & 2 deletions core/src/main/java/com/google/adk/agents/InvocationContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,22 @@

import static com.google.common.base.Strings.isNullOrEmpty;

import com.google.adk.apps.ResumabilityConfig;
import com.google.adk.artifacts.BaseArtifactService;
import com.google.adk.events.Event;
import com.google.adk.memory.BaseMemoryService;
import com.google.adk.models.LlmCallsLimitExceededException;
import com.google.adk.plugins.Plugin;
import com.google.adk.plugins.PluginManager;
import com.google.adk.sessions.BaseSessionService;
import com.google.adk.sessions.Session;
import com.google.adk.summarizer.EventsCompactionConfig;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.InlineMe;
import com.google.genai.types.Content;
import com.google.genai.types.FunctionCall;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
Expand All @@ -49,6 +54,9 @@ public class InvocationContext {
private final Session session;
private final Optional<Content> userContent;
private final RunConfig runConfig;
private final Map<String, BaseAgentState> agentStates;
private final Map<String, Boolean> endOfAgents;
private final ResumabilityConfig resumabilityConfig;
@Nullable private final EventsCompactionConfig eventsCompactionConfig;
@Nullable private final ContextCacheConfig contextCacheConfig;
private final InvocationCostManager invocationCostManager;
Expand All @@ -72,10 +80,13 @@ protected InvocationContext(Builder builder) {
this.userContent = builder.userContent;
this.runConfig = builder.runConfig;
this.endInvocation = builder.endInvocation;
this.agentStates = builder.agentStates;
this.endOfAgents = builder.endOfAgents;
this.resumabilityConfig = builder.resumabilityConfig;
this.eventsCompactionConfig = builder.eventsCompactionConfig;
this.contextCacheConfig = builder.contextCacheConfig;
this.invocationCostManager = builder.invocationCostManager;
this.callbackContextData = new ConcurrentHashMap<>(builder.callbackContextData);
this.callbackContextData = builder.callbackContextData;
}

/**
Expand Down Expand Up @@ -256,7 +267,10 @@ public String invocationId() {
/**
* Sets the [branch] ID for the current invocation. A branch represents a fork in the conversation
* history.
*
* @deprecated Use {@link #toBuilder()} and {@link Builder#branch(String)} instead.
*/
@Deprecated(forRemoval = true)
public void branch(@Nullable String branch) {
this.branch = Optional.ofNullable(branch);
}
Expand Down Expand Up @@ -307,6 +321,16 @@ public Map<String, Object> callbackContextData() {
return callbackContextData;
}

/** Returns agent-specific state saved within this invocation. */
public Map<String, BaseAgentState> agentStates() {
return agentStates;
}

/** Returns map of agents that ended during this invocation. */
public Map<String, Boolean> endOfAgents() {
return endOfAgents;
}

/**
* Returns whether this invocation should be ended, e.g., due to reaching a terminal state or
* error.
Expand Down Expand Up @@ -345,6 +369,36 @@ public void incrementLlmCallsCount() throws LlmCallsLimitExceededException {
this.invocationCostManager.incrementAndEnforceLlmCallsLimit(this.runConfig);
}

/** Returns whether the current invocation is resumable. */
public boolean isResumable() {
return resumabilityConfig.isResumable();
}

/** Returns ResumabilityConfig for this invocation. */
public ResumabilityConfig resumabilityConfig() {
return resumabilityConfig;
}

/**
* Populates agentStates and endOfAgents maps by reading session events for this invocation id.
*/
public void populateAgentStates(List<Event> events) {
events.stream()
.filter(event -> invocationId().equals(event.invocationId()))
.forEach(
event -> {
if (event.actions() != null) {
if (event.actions().agentState() != null
&& !event.actions().agentState().isEmpty()) {
agentStates.putAll(event.actions().agentState());
}
if (event.actions().endOfAgent()) {
endOfAgents.put(event.author(), true);
}
}
});
}

/** Returns the events compaction configuration for the current agent run. */
public Optional<EventsCompactionConfig> eventsCompactionConfig() {
return Optional.ofNullable(eventsCompactionConfig);
Expand All @@ -355,6 +409,23 @@ public Optional<ContextCacheConfig> contextCacheConfig() {
return Optional.ofNullable(contextCacheConfig);
}

/** Returns whether to pause the invocation right after this [event]. */
public boolean shouldPauseInvocation(Event event) {
if (!isResumable()) {
return false;
}

var longRunningToolIds = event.longRunningToolIds().orElse(ImmutableSet.of());
if (longRunningToolIds.isEmpty()) {
return false;
}

return event.functionCalls().stream()
.map(FunctionCall::id)
.flatMap(Optional::stream)
.anyMatch(functionCallId -> longRunningToolIds.contains(functionCallId));
}

private static class InvocationCostManager {
private int numberOfLlmCalls = 0;

Expand Down Expand Up @@ -406,10 +477,13 @@ private Builder(InvocationContext context) {
this.userContent = context.userContent;
this.runConfig = context.runConfig;
this.endInvocation = context.endInvocation;
this.agentStates = new ConcurrentHashMap<>(context.agentStates);
this.endOfAgents = new ConcurrentHashMap<>(context.endOfAgents);
this.resumabilityConfig = context.resumabilityConfig;
this.eventsCompactionConfig = context.eventsCompactionConfig;
this.contextCacheConfig = context.contextCacheConfig;
this.invocationCostManager = context.invocationCostManager;
this.callbackContextData = new ConcurrentHashMap<>(context.callbackContextData);
this.callbackContextData = context.callbackContextData;
}

private BaseSessionService sessionService;
Expand All @@ -425,6 +499,9 @@ private Builder(InvocationContext context) {
private Optional<Content> userContent = Optional.empty();
private RunConfig runConfig = RunConfig.builder().build();
private boolean endInvocation = false;
private Map<String, BaseAgentState> agentStates = new ConcurrentHashMap<>();
private Map<String, Boolean> endOfAgents = new ConcurrentHashMap<>();
private ResumabilityConfig resumabilityConfig = new ResumabilityConfig();
@Nullable private EventsCompactionConfig eventsCompactionConfig;
@Nullable private ContextCacheConfig contextCacheConfig;
private InvocationCostManager invocationCostManager = new InvocationCostManager();
Expand Down Expand Up @@ -616,6 +693,42 @@ public Builder endInvocation(boolean endInvocation) {
return this;
}

/**
* Sets agent-specific state saved within this invocation.
*
* @param agentStates agent-specific state saved within this invocation.
* @return this builder instance for chaining.
*/
@CanIgnoreReturnValue
public Builder agentStates(Map<String, BaseAgentState> agentStates) {
this.agentStates = agentStates;
return this;
}

/**
* Sets agent end-of-invocation status.
*
* @param endOfAgents agent end-of-invocation status.
* @return this builder instance for chaining.
*/
@CanIgnoreReturnValue
public Builder endOfAgents(Map<String, Boolean> endOfAgents) {
this.endOfAgents = endOfAgents;
return this;
}

/**
* Sets the resumability configuration for the current agent run.
*
* @param resumabilityConfig the resumability configuration.
* @return this builder instance for chaining.
*/
@CanIgnoreReturnValue
public Builder resumabilityConfig(ResumabilityConfig resumabilityConfig) {
this.resumabilityConfig = resumabilityConfig;
return this;
}

/**
* Sets the events compaction configuration for the current agent run.
*
Expand Down Expand Up @@ -705,6 +818,9 @@ public boolean equals(Object o) {
&& Objects.equals(session, that.session)
&& Objects.equals(userContent, that.userContent)
&& Objects.equals(runConfig, that.runConfig)
&& Objects.equals(agentStates, that.agentStates)
&& Objects.equals(endOfAgents, that.endOfAgents)
&& Objects.equals(resumabilityConfig, that.resumabilityConfig)
&& Objects.equals(eventsCompactionConfig, that.eventsCompactionConfig)
&& Objects.equals(contextCacheConfig, that.contextCacheConfig)
&& Objects.equals(invocationCostManager, that.invocationCostManager)
Expand All @@ -727,6 +843,9 @@ public int hashCode() {
userContent,
runConfig,
endInvocation,
agentStates,
endOfAgents,
resumabilityConfig,
eventsCompactionConfig,
contextCacheConfig,
invocationCostManager,
Expand Down
18 changes: 17 additions & 1 deletion core/src/main/java/com/google/adk/apps/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,21 @@ public class App {
private final BaseAgent rootAgent;
private final ImmutableList<? extends Plugin> plugins;
@Nullable private final EventsCompactionConfig eventsCompactionConfig;
@Nullable private final ResumabilityConfig resumabilityConfig;
@Nullable private final ContextCacheConfig contextCacheConfig;

private App(
String name,
BaseAgent rootAgent,
List<? extends Plugin> plugins,
@Nullable EventsCompactionConfig eventsCompactionConfig,
@Nullable ResumabilityConfig resumabilityConfig,
@Nullable ContextCacheConfig contextCacheConfig) {
this.name = name;
this.rootAgent = rootAgent;
this.plugins = ImmutableList.copyOf(plugins);
this.eventsCompactionConfig = eventsCompactionConfig;
this.resumabilityConfig = resumabilityConfig;
this.contextCacheConfig = contextCacheConfig;
}

Expand All @@ -73,6 +76,11 @@ public EventsCompactionConfig eventsCompactionConfig() {
return eventsCompactionConfig;
}

@Nullable
public ResumabilityConfig resumabilityConfig() {
return resumabilityConfig;
}

@Nullable
public ContextCacheConfig contextCacheConfig() {
return contextCacheConfig;
Expand All @@ -84,6 +92,7 @@ public static class Builder {
private BaseAgent rootAgent;
private List<? extends Plugin> plugins = ImmutableList.of();
@Nullable private EventsCompactionConfig eventsCompactionConfig;
@Nullable private ResumabilityConfig resumabilityConfig;
@Nullable private ContextCacheConfig contextCacheConfig;

@CanIgnoreReturnValue
Expand All @@ -110,6 +119,12 @@ public Builder eventsCompactionConfig(EventsCompactionConfig eventsCompactionCon
return this;
}

@CanIgnoreReturnValue
public Builder resumabilityConfig(ResumabilityConfig resumabilityConfig) {
this.resumabilityConfig = resumabilityConfig;
return this;
}

@CanIgnoreReturnValue
public Builder contextCacheConfig(ContextCacheConfig contextCacheConfig) {
this.contextCacheConfig = contextCacheConfig;
Expand All @@ -124,7 +139,8 @@ public App build() {
throw new IllegalStateException("Root agent must be provided.");
}
validateAppName(name);
return new App(name, rootAgent, plugins, eventsCompactionConfig, contextCacheConfig);
return new App(
name, rootAgent, plugins, eventsCompactionConfig, resumabilityConfig, contextCacheConfig);
}
}

Expand Down
28 changes: 28 additions & 0 deletions core/src/main/java/com/google/adk/apps/ResumabilityConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* 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 language governing permissions and
* limitations under the License.
*/
package com.google.adk.apps;

/**
* An app contains Resumability configuration for the agents.
*
* @param isResumable Whether the app is resumable.
*/
public record ResumabilityConfig(boolean isResumable) {

/** Creates a new {@code ResumabilityConfig} with resumability disabled. */
public ResumabilityConfig() {
this(false);
}
}
Loading