Migration guide

SDK 3.7.0

Migrating from TextGuardrail

TextGuardrail is deprecated. Implement one of the guardrail interfaces that return a Decision instead. Choose the interface by the use-for value of the configuration entry:

  • For model-request, implement ModelCallGuardrail.

  • For model-response, implement AgentResponseGuardrail.

  • For mcp-tool-request, implement ToolCallGuardrail.

  • For mcp-tool-response, implement ModelCallGuardrail. It sees the MCP tool result as a ToolCallResponse in newMessages() before the model does. A ToolCallResponse does not say where the tool runs, so the guardrail sees function tool results and MCP tool results alike. If the RemoteMcpTools has a ToolInterceptor, the guardrail sees the result that interceptResponse returns.

A ModelCallGuardrail that replaces an mcp-tool-response entry behaves differently:

  • It sees the MCP tool result after the tool-result sanitizers have masked it.

  • It runs before the next model call, after all tool calls of the round. The TextGuardrail ran right after each MCP tool call.

  • When it blocks, the interaction record in the ledger already holds the MCP tool result.

  • In an autonomous agent, some rounds end the iteration without another model call. This happens when the model response also calls a control tool. It also happens when a tool call of the round fails. The runtime stores such a round in session memory and the ledger before a ModelCallGuardrail checks it. If the task continues, the ModelCallGuardrail checks it before the first model call of the next iteration.

  • The failure message starts with Before model call guardrail blocked, not with MCP response guardrail blocked.

A ToolCallGuardrail that replaces an mcp-tool-request entry behaves differently:

  • The runtime records the ToolCallGuardrail decision in the ledger.

  • On the same MCP tool call, the runtime evaluates ToolCallGuardrail entries before TextGuardrail entries with mcp-tool-request. The TextGuardrail entries run only when no blocking ToolCallGuardrail stopped the call.

Remove use-for from the migrated entry. The service fails to start when a ModelCallGuardrail, AgentResponseGuardrail, or ToolCallGuardrail entry defines use-for.

All use-for values are deprecated. At startup, the SDK logs a warning for each enabled TextGuardrail entry that sets use-for.

For the built-in SimilarityGuard, see Migrating from SimilarityGuard.

For an agent that returns StreamEffect, the runtime does not evaluate a TextGuardrail on the reply unless the entry sets report-only = true. The runtime logs warning AK-01208 instead. An AgentResponseGuardrail works differently. The service fails to start when an AgentResponseGuardrail entry without report-only = true binds such an agent. See Kinds of guardrail for the ways to fix the startup failure.

Migrating from SimilarityGuard

SimilarityGuard is deprecated for every use-for value. For model-request and mcp-tool-response, use ModelCallSimilarityGuard. It checks the user message and the tool results before each model call. The SDK has no built-in replacement for model-response or mcp-tool-request. For mcp-tool-request, implement a ToolCallGuardrail. See Similarity guardrail for the ModelCallSimilarityGuard configuration.

To move the built-in guard entry, enable "default model-call jailbreak" for the same agents and agent roles. Copy any settings that you changed on "default jailbreak", such as threshold or report-only. Then remove the agents and agent roles from "default jailbreak".

ModelCallSimilarityGuard behaves differently from SimilarityGuard with use-for = ["model-request"]:

  • It checks the user message after the model-call sanitizers have masked it.

  • The service fails at startup if bad-examples-resource-dir is not on the classpath. It also fails if threshold is not greater than 0 and at most 1.

ModelCallSimilarityGuard behaves differently from SimilarityGuard with use-for = ["mcp-tool-response"]:

  • It checks the user message and the function tool results as well as the MCP tool results.

  • It checks the MCP tool result before the next model call.

  • If the RemoteMcpTools has a ToolInterceptor, ModelCallSimilarityGuard checks the result that interceptResponse returns.

ToolCallGuardrail on MCP tool calls

A ToolCallGuardrail entry runs on MCP tool calls as well as function tool calls. An entry without the tools key runs on every MCP tool call of its agents. To run the entry on some tools only, list their names in tools. The tools key matches by name only. To tell a function tool from an MCP tool, check origin() on the CallContext. See Guardrails for the entry configuration.

ToolInterceptor changes

A RemoteMcpTools.ToolInterceptor behaves differently in these cases:

  • ToolInterceptorContext.toolName() returns the name of the tool on the MCP server. When the model uses another name for the tool, the interceptor no longer gets that name.

  • The ledger records the arguments that interceptRequest returns as the arguments of the tool call. Before, it recorded the arguments from the model. The ToolCall in the interaction record still holds the arguments from the model.

  • A null from interceptResponse fails the tool call with an MCP tool call error. The agent can handle the error in onFailure.

Components from libraries are loaded only when listed

A service no longer loads the components of a library jar automatically. List the library components that the service uses in akka.javasdk.library.components, as described in Using components from libraries. On startup, the service logs a warning that lists the library components it found and the configuration that loads them.

You do not need to rebuild a library built with an earlier SDK version. The service treats its component descriptor as a library descriptor, so you list its components like the components of any other library.

Build a component library with the non-akka-service property set to true. A library built with SDK 3.7.0 without this property marks its component descriptor as a service descriptor, and a service that depends on it fails to start.

A deployable service must not set non-akka-service to true. The property marks the component descriptor as a library descriptor, and the service fails to start in production.

On the first build after the upgrade, the Akka parent pom removes the build output from the earlier SDK version. For a build without the Akka parent pom, run a clean build once after the upgrade.

SDK 3.5.5

Akka SDK 3.5.5 deprecates the @AgentDescription annotation in favor of using @Component for agent metadata (name and description) and the new @AgentRole annotation for specifying agent roles. This change aligns agent configuration with other component types and provides a more consistent API.

Migrating from @AgentDescription to @Component and @AgentRole

The @AgentDescription annotation is now deprecated. To migrate your agent classes:

For agent name and description:

  • Remove the @AgentDescription annotation

  • If using @AgentDescription.role field, add an @AgentRole annotation instead.

  • Add or update the @Component annotation with name and description fields:

Before:
@Component(id = "my-agent")
@AgentDescription(
    name = "My Agent",
    description = "An agent that does something useful",
    role = "worker")
public class MyAgent extends Agent {
    // ...
}
After:
@Component(
    id = "my-agent",
    name = "My Agent",
    description = "An agent that does something useful")
@AgentRole("worker")
public class MyAgent extends Agent {
    // ...
}

Update imports:

  • Add import akka.javasdk.annotations.AgentRole; if you are using agent roles

  • Keep import akka.javasdk.annotations.Component;

Backward compatibility:

  • The framework will continue to support @AgentDescription for backward compatibility

  • However, you cannot use both the old and new annotations for the same fields (e.g., both @AgentDescription.name and @Component.name will cause a validation error)

  • New features and improvements will only be available with the new annotation style

SDK 3.5.4

Akka SDK 3.5.4 introduces a new @Component annotation, replacing the legacy @ComponentId for identifying components. This change streamlines component configuration and enables new features and improvements. The migration is straightforward and ensures backward compatibility for existing codebases.

Migrating from @ComponentId to @Component

The @ComponentId annotation is now deprecated in favor of the new @Component annotation. To migrate your codebase:

Replace usages of @ComponentId:

  • Remove the @ComponentId("your-id") annotation from your component classes.

  • Add the @Component annotation, specifying the required id and optionally name and description fields:

@Component(id = "your-id",
           name = "Your Name",
           description = "Description")
public class YourComponent { ... }

Update imports:

  • Change import akka.javasdk.annotations.ComponentId; to import akka.javasdk.annotations.Component;

Test backward compatibility:

  • The framework will continue to support @ComponentId for backward compatibility, but new features and improvements will only be available with @Component.

SDK 3.3.0

This release introduces support for Java 21 virtual threads across all user component and endpoint logic. As a result, it is now safe to perform blocking I/O operations without occupying underlying OS threads.

You no longer need to use CompletionStage or CompletableFuture composition when making calls using component clients, HTTP clients, or gRPC clients. Where services previously used .invokeAsync() and handled a future, you can now simply use .invoke() and work directly with the returned result.

Breaking Changes

To simplify usage and improve developer experience, this release includes a few breaking changes. If your service relies on any of the following features, you will need to update your implementation:

  • gRPC endpoints for unary and client streaming methods now return the response message directly, instead of wrapping it in a CompletionStage.

  • gRPC clients now return the response value directly for unary and client streaming calls. For asynchronous behavior, the request builder APIs (grpcClient.someMethod().invokeAsync(parameter)) are still available.

Deprecations

Timer scheduling and cancellation via TimerScheduler.startSingleTimer(…​) and TimerScheduler.cancel() are now deprecated. Instead, use TimerScheduler.createSingleTimer(…​) and TimerScheduler.delete(). Note that these two new methods are blocking operations that return void.

If you still prefer non-blocking operations, new asynchronous alternatives have been introduced: createSingleTimerAsync() and deleteAsync().

Blocking and Parallelism

To support parallel execution of blocking logic within completion stages, the Service Setup, Endpoints, Consumers, Timed Actions, and Workflows can now accept a java.util.concurrent.Executor via their constructors.

The Akka SDK provides a virtual-thread-enabled executor that is safe for running blocking operations. When using the standard Java CompletableFuture or CompletionStage APIs, you can supply this executor as an additional parameter to methods accepting lambdas, enabling safe parallelism for blocking tasks.