From 2fd5ac9c22324a4c430498e0f7d94f53301b39d8 Mon Sep 17 00:00:00 2001 From: Google Team Member Date: Tue, 10 Feb 2026 01:41:15 -0800 Subject: [PATCH] ADK changes PiperOrigin-RevId: 868014417 --- .../adk/a2a/A2ASendMessageExecutor.java | 307 ------------------ a2a/webservice/pom.xml | 67 ---- .../adk/webservice/A2ARemoteApplication.java | 20 -- .../webservice/A2ARemoteConfiguration.java | 49 --- .../adk/webservice/A2ARemoteController.java | 40 --- .../adk/webservice/A2ARemoteService.java | 93 ------ contrib/samples/a2a_remote/README.md | 70 ---- contrib/samples/a2a_remote/pom.xml | 139 -------- .../a2a_remote/remote_prime_agent/Agent.java | 101 ------ .../a2a_remote/remote_prime_agent/agent.json | 17 - .../a2a_remote/RemoteA2AApplication.java | 24 -- contrib/samples/pom.xml | 1 - pom.xml | 1 - 13 files changed, 929 deletions(-) delete mode 100644 a2a/src/main/java/com/google/adk/a2a/A2ASendMessageExecutor.java delete mode 100644 a2a/webservice/pom.xml delete mode 100644 a2a/webservice/src/main/java/com/google/adk/webservice/A2ARemoteApplication.java delete mode 100644 a2a/webservice/src/main/java/com/google/adk/webservice/A2ARemoteConfiguration.java delete mode 100644 a2a/webservice/src/main/java/com/google/adk/webservice/A2ARemoteController.java delete mode 100644 a2a/webservice/src/main/java/com/google/adk/webservice/A2ARemoteService.java delete mode 100644 contrib/samples/a2a_remote/README.md delete mode 100644 contrib/samples/a2a_remote/pom.xml delete mode 100644 contrib/samples/a2a_remote/remote_prime_agent/Agent.java delete mode 100644 contrib/samples/a2a_remote/remote_prime_agent/agent.json delete mode 100644 contrib/samples/a2a_remote/src/main/java/com/google/adk/samples/a2a_remote/RemoteA2AApplication.java diff --git a/a2a/src/main/java/com/google/adk/a2a/A2ASendMessageExecutor.java b/a2a/src/main/java/com/google/adk/a2a/A2ASendMessageExecutor.java deleted file mode 100644 index bd345ab22..000000000 --- a/a2a/src/main/java/com/google/adk/a2a/A2ASendMessageExecutor.java +++ /dev/null @@ -1,307 +0,0 @@ -package com.google.adk.a2a; - -import static com.google.common.base.Strings.isNullOrEmpty; -import static java.util.concurrent.TimeUnit.MILLISECONDS; - -import com.google.adk.a2a.converters.ConversationPreprocessor; -import com.google.adk.a2a.converters.RequestConverter; -import com.google.adk.a2a.converters.ResponseConverter; -import com.google.adk.agents.BaseAgent; -import com.google.adk.agents.RunConfig; -import com.google.adk.artifacts.InMemoryArtifactService; -import com.google.adk.events.Event; -import com.google.adk.memory.InMemoryMemoryService; -import com.google.adk.runner.Runner; -import com.google.adk.sessions.InMemorySessionService; -import com.google.adk.sessions.Session; -import com.google.common.collect.ImmutableList; -import com.google.genai.types.Content; -import io.a2a.spec.Message; -import io.a2a.spec.TextPart; -import io.reactivex.rxjava3.core.Completable; -import io.reactivex.rxjava3.core.Single; -import java.time.Duration; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeoutException; -import org.jspecify.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Shared SendMessage execution between HTTP service and other integrations. - * - *

**EXPERIMENTAL:** Subject to change, rename, or removal in any future patch release. Do not - * use in production code. - */ -public final class A2ASendMessageExecutor { - private static final Logger logger = LoggerFactory.getLogger(A2ASendMessageExecutor.class); - - @FunctionalInterface - public interface AgentExecutionStrategy { - Single> execute( - String userId, - String sessionId, - Content userContent, - RunConfig runConfig, - String invocationId); - } - - private final InMemorySessionService sessionService; - private final String appName; - @Nullable private final Runner runner; - @Nullable private final Duration agentTimeout; - private static final RunConfig DEFAULT_RUN_CONFIG = - RunConfig.builder().setStreamingMode(RunConfig.StreamingMode.NONE).setMaxLlmCalls(20).build(); - - public A2ASendMessageExecutor(InMemorySessionService sessionService, String appName) { - this.sessionService = sessionService; - this.appName = appName; - this.runner = null; - this.agentTimeout = null; - } - - public A2ASendMessageExecutor(BaseAgent agent, String appName, Duration agentTimeout) { - InMemorySessionService sessionService = new InMemorySessionService(); - Runner runnerInstance = - new Runner( - agent, - appName, - new InMemoryArtifactService(), - sessionService, - new InMemoryMemoryService()); - this.sessionService = sessionService; - this.appName = appName; - this.runner = runnerInstance; - this.agentTimeout = agentTimeout; - } - - public Single execute( - @Nullable Message request, AgentExecutionStrategy agentExecutionStrategy) { - final String invocationId = UUID.randomUUID().toString(); - final String contextId = resolveContextId(request); - final ImmutableList inputEvents = buildInputEvents(request, invocationId); - - ConversationPreprocessor.PreparedInput prepared = - ConversationPreprocessor.extractHistoryAndUserContent(inputEvents); - - String userId = buildUserId(contextId); - String sessionId = contextId; - - return ensureSessionExistsSingle(userId, sessionId, contextId) - .flatMap( - session -> - processEventsSingle( - session, prepared, userId, sessionId, invocationId, agentExecutionStrategy)) - .map( - resultEvents -> { - final String taskId = resolveTaskId(request); - return ResponseConverter.eventsToMessage(resultEvents, contextId, taskId); - }) - .onErrorReturn( - throwable -> { - logger.error("Error processing A2A request", throwable); - return errorResponse("Internal error: " + throwable.getMessage(), contextId); - }); - } - - public Single execute(@Nullable Message request) { - if (runner == null || agentTimeout == null) { - throw new IllegalStateException( - "Runner-based handle invoked without configured runner or timeout"); - } - return execute(request, this::executeAgentWithTimeout); - } - - private Single ensureSessionExistsSingle( - String userId, String sessionId, String contextId) { - return sessionService - .getSession(appName, userId, sessionId, Optional.empty()) - .switchIfEmpty( - Single.defer( - () -> { - ConcurrentHashMap initialState = new ConcurrentHashMap<>(); - return sessionService.createSession(appName, userId, initialState, sessionId); - })); - } - - private Completable appendHistoryEvents( - Session session, ConversationPreprocessor.PreparedInput prepared, String invocationId) { - ImmutableList eventsToAppend = - filterNewHistoryEvents(session, prepared.historyEvents, invocationId); - return appendEvents(session, eventsToAppend); - } - - private ImmutableList filterNewHistoryEvents( - Session session, List historyEvents, String invocationId) { - Set existingEventIds = new HashSet<>(); - for (Event existing : session.events()) { - if (existing.id() != null) { - existingEventIds.add(existing.id()); - } - } - - ImmutableList.Builder eventsToAppend = ImmutableList.builder(); - for (Event historyEvent : historyEvents) { - ensureIdentifiers(historyEvent, invocationId); - if (existingEventIds.add(historyEvent.id())) { - eventsToAppend.add(historyEvent); - } - } - return eventsToAppend.build(); - } - - private Completable appendEvents(Session session, ImmutableList events) { - Completable chain = Completable.complete(); - for (Event event : events) { - chain = chain.andThen(sessionService.appendEvent(session, event).ignoreElement()); - } - return chain; - } - - private Single> processEventsSingle( - Session session, - ConversationPreprocessor.PreparedInput prepared, - String userId, - String sessionId, - String invocationId, - AgentExecutionStrategy agentExecutionStrategy) { - Content userContent = - prepared.userContent.orElseGet(A2ASendMessageExecutor::defaultUserContent); - return appendHistoryEvents(session, prepared, invocationId) - .andThen( - agentExecutionStrategy.execute( - userId, sessionId, userContent, DEFAULT_RUN_CONFIG, invocationId)); - } - - private static ImmutableList defaultHelloEvent(String invocationId) { - Event e = - Event.builder() - .id(UUID.randomUUID().toString()) - .invocationId(invocationId) - .author("user") - .content(defaultUserContent()) - .build(); - return ImmutableList.of(e); - } - - private static Content defaultUserContent() { - return Content.builder() - .role("user") - .parts(ImmutableList.of(com.google.genai.types.Part.builder().text("Hello").build())) - .build(); - } - - private static Message errorResponse(String msg, String contextId) { - Message error = - new Message.Builder() - .messageId(UUID.randomUUID().toString()) - .role(Message.Role.AGENT) - .parts(ImmutableList.of(new TextPart("Error: " + msg))) - .build(); - if (contextId != null && !contextId.isEmpty()) { - error.setContextId(contextId); - } - return error; - } - - private Single> executeAgentWithTimeout( - String userId, - String sessionId, - Content userContent, - RunConfig runConfig, - String invocationId) { - if (runner == null || agentTimeout == null) { - throw new IllegalStateException("Runner-based execution invoked without configuration"); - } - - Single> agentResultSingle = - runner - .runAsync(userId, sessionId, userContent, runConfig) - .toList() - .map(events -> ImmutableList.copyOf(events)); - - return agentResultSingle - .timeout(agentTimeout.toMillis(), MILLISECONDS) - .onErrorResumeNext( - throwable -> { - if (isTimeout(throwable)) { - logger.warn( - "Agent execution exceeded {}; returning timeout event", - agentTimeout, - throwable); - return Single.just(ImmutableList.of(createTimeoutEvent(invocationId))); - } - return Single.error(throwable); - }); - } - - private static String resolveContextId(@Nullable Message inbound) { - if (inbound == null || inbound.getContextId() == null || inbound.getContextId().isEmpty()) { - return UUID.randomUUID().toString(); - } - return inbound.getContextId(); - } - - private static String resolveTaskId(@Nullable Message inbound) { - if (inbound != null && inbound.getTaskId() != null && !inbound.getTaskId().isEmpty()) { - return inbound.getTaskId(); - } - return UUID.randomUUID().toString(); - } - - private static ImmutableList buildInputEvents( - @Nullable Message inbound, String invocationId) { - if (inbound == null) { - return defaultHelloEvent(invocationId); - } - return RequestConverter.convertAggregatedA2aMessageToAdkEvents(inbound, invocationId); - } - - private static String buildUserId(String contextId) { - return "user-" + contextId; - } - - private static void ensureIdentifiers(Event event, String invocationId) { - if (isNullOrEmpty(event.id())) { - event.setId(Event.generateEventId()); - } - if (isNullOrEmpty(event.invocationId())) { - event.setInvocationId(invocationId); - } - } - - private static Event createTimeoutEvent(String invocationId) { - return Event.builder() - .id(UUID.randomUUID().toString()) - .invocationId(invocationId) - .author("agent") - .content( - Content.builder() - .role("model") - .parts( - ImmutableList.of( - com.google.genai.types.Part.builder() - .text("Agent execution timed out.") - .build())) - .build()) - .build(); - } - - private static boolean isTimeout(@Nullable Throwable throwable) { - while (throwable != null) { - if (throwable instanceof TimeoutException) { - return true; - } - if (throwable.getClass().getName().endsWith("TimeoutException")) { - return true; - } - throwable = throwable.getCause(); - } - return false; - } -} diff --git a/a2a/webservice/pom.xml b/a2a/webservice/pom.xml deleted file mode 100644 index deb03fd27..000000000 --- a/a2a/webservice/pom.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - 4.0.0 - - - com.google.adk - google-adk-parent - 0.5.1-SNAPSHOT - ../../pom.xml - - - google-adk-a2a-webservice - jar - - Google ADK A2A Webservice - - - 17 - ${java.version} - - - - - com.google.adk - google-adk-a2a - ${project.version} - - - org.springframework.boot - spring-boot-starter-web - - - org.slf4j - slf4j-api - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.13.0 - - ${java.version} - - - - org.springframework.boot - spring-boot-maven-plugin - ${spring-boot.version} - - - - repackage - - - exec - - - - - - - \ No newline at end of file diff --git a/a2a/webservice/src/main/java/com/google/adk/webservice/A2ARemoteApplication.java b/a2a/webservice/src/main/java/com/google/adk/webservice/A2ARemoteApplication.java deleted file mode 100644 index 93e321eb1..000000000 --- a/a2a/webservice/src/main/java/com/google/adk/webservice/A2ARemoteApplication.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.google.adk.webservice; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Import; - -/** - * Entry point for the standalone Spring Boot A2A service. - * - *

**EXPERIMENTAL:** Subject to change, rename, or removal in any future patch release. Do not - * use in production code. - */ -@SpringBootApplication -@Import(A2ARemoteConfiguration.class) -public class A2ARemoteApplication { - - public static void main(String[] args) { - SpringApplication.run(A2ARemoteApplication.class, args); - } -} diff --git a/a2a/webservice/src/main/java/com/google/adk/webservice/A2ARemoteConfiguration.java b/a2a/webservice/src/main/java/com/google/adk/webservice/A2ARemoteConfiguration.java deleted file mode 100644 index a3f9b48ac..000000000 --- a/a2a/webservice/src/main/java/com/google/adk/webservice/A2ARemoteConfiguration.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.google.adk.webservice; - -import com.google.adk.a2a.A2ASendMessageExecutor; -import com.google.adk.agents.BaseAgent; -import java.time.Duration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; - -/** - * Registers the transport-only A2A webservice stack. - * - *

Importers must supply a {@link BaseAgent} bean. The agent remains opaque to this module so the - * transport can be reused across applications. - * - *

TODO: - * - *

- * - *

**EXPERIMENTAL:** Subject to change, rename, or removal in any future patch release. Do not - * use in production code. - */ -@Configuration -@ComponentScan(basePackages = "com.google.adk.webservice") -public class A2ARemoteConfiguration { - - private static final Logger logger = LoggerFactory.getLogger(A2ARemoteConfiguration.class); - private static final String DEFAULT_APP_NAME = "a2a-remote-service"; - private static final long DEFAULT_TIMEOUT_SECONDS = 15L; - - @Bean - public A2ASendMessageExecutor a2aSendMessageExecutor( - BaseAgent agent, - @Value("${a2a.remote.appName:" + DEFAULT_APP_NAME + "}") String appName, - @Value("${a2a.remote.timeoutSeconds:" + DEFAULT_TIMEOUT_SECONDS + "}") long timeoutSeconds) { - logger.info( - "Initializing A2A send message executor for appName {} with timeout {}s", - appName, - timeoutSeconds); - return new A2ASendMessageExecutor(agent, appName, Duration.ofSeconds(timeoutSeconds)); - } -} diff --git a/a2a/webservice/src/main/java/com/google/adk/webservice/A2ARemoteController.java b/a2a/webservice/src/main/java/com/google/adk/webservice/A2ARemoteController.java deleted file mode 100644 index a0fe5b0cc..000000000 --- a/a2a/webservice/src/main/java/com/google/adk/webservice/A2ARemoteController.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.google.adk.webservice; - -import io.a2a.spec.SendMessageRequest; -import io.a2a.spec.SendMessageResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -/** - * REST controller exposing an A2A-compliant JSON-RPC endpoint backed by a local ADK runner. - * - *

**EXPERIMENTAL:** Subject to change, rename, or removal in any future patch release. Do not - * use in production code. - */ -@RestController -@RequestMapping("/a2a/remote") -public class A2ARemoteController { - - private static final Logger logger = LoggerFactory.getLogger(A2ARemoteController.class); - - private final A2ARemoteService service; - - public A2ARemoteController(A2ARemoteService service) { - this.service = service; - } - - @PostMapping( - path = "/v1/message:send", - consumes = "application/json", - produces = "application/json") - public SendMessageResponse sendMessage(@RequestBody SendMessageRequest request) { - logger.debug("Received remote A2A request: {}", request); - SendMessageResponse response = service.handle(request); - logger.debug("Responding with remote A2A payload: {}", response); - return response; - } -} diff --git a/a2a/webservice/src/main/java/com/google/adk/webservice/A2ARemoteService.java b/a2a/webservice/src/main/java/com/google/adk/webservice/A2ARemoteService.java deleted file mode 100644 index 803774568..000000000 --- a/a2a/webservice/src/main/java/com/google/adk/webservice/A2ARemoteService.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.google.adk.webservice; - -import com.google.adk.a2a.A2ASendMessageExecutor; -import com.google.adk.a2a.converters.ResponseConverter; -import io.a2a.spec.JSONRPCError; -import io.a2a.spec.Message; -import io.a2a.spec.MessageSendParams; -import io.a2a.spec.SendMessageRequest; -import io.a2a.spec.SendMessageResponse; -import java.util.List; -import java.util.UUID; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Service; - -/** - * Core service that bridges the A2A JSON-RPC sendMessage API to a local ADK runner. - * - *

**EXPERIMENTAL:** Subject to change, rename, or removal in any future patch release. Do not - * use in production code. - */ -@Service -public class A2ARemoteService { - - private static final Logger logger = LoggerFactory.getLogger(A2ARemoteService.class); - private static final int ERROR_CODE_INVALID_PARAMS = -32602; - private static final int ERROR_CODE_INTERNAL_ERROR = -32603; - - private final A2ASendMessageExecutor executor; - - public A2ARemoteService(A2ASendMessageExecutor executor) { - this.executor = executor; - } - - public SendMessageResponse handle(SendMessageRequest request) { - if (request == null) { - logger.warn("Received null SendMessageRequest"); - return invalidParamsResponse(null, "Request body is missing"); - } - - MessageSendParams params = request.getParams(); - if (params == null) { - logger.warn("SendMessageRequest {} missing params", request.getId()); - return invalidParamsResponse(request, "Request params are missing"); - } - - Message inbound = params.message(); - if (inbound == null) { - logger.warn("SendMessageRequest {} missing message payload", request.getId()); - return invalidParamsResponse(request, "Request message payload is missing"); - } - - boolean generatedContext = inbound.getContextId() == null || inbound.getContextId().isEmpty(); - Message normalized = ensureContextId(inbound); - if (generatedContext) { - logger.debug("Incoming request lacked contextId; generated {}", normalized.getContextId()); - } - - try { - Message result = executor.execute(normalized).blockingGet(); - if (result == null) { - result = - ResponseConverter.eventsToMessage( - List.of(), normalized.getContextId(), normalized.getTaskId()); - } - - logger.debug("Returning A2A response for context {}", normalized.getContextId()); - return new SendMessageResponse(request.getId(), result); - } catch (RuntimeException e) { - logger.error("Failed to process remote A2A request", e); - return errorResponse(request, e); - } - } - - private static Message ensureContextId(Message message) { - if (message.getContextId() != null && !message.getContextId().isEmpty()) { - return message; - } - return new Message.Builder(message).contextId(UUID.randomUUID().toString()).build(); - } - - private static SendMessageResponse invalidParamsResponse( - SendMessageRequest request, String reason) { - JSONRPCError error = new JSONRPCError(ERROR_CODE_INVALID_PARAMS, reason, null); - return new SendMessageResponse(request != null ? request.getId() : null, error); - } - - private static SendMessageResponse errorResponse(SendMessageRequest request, Throwable error) { - String message = "Internal error processing sendMessage request"; - JSONRPCError jsonrpcError = new JSONRPCError(ERROR_CODE_INTERNAL_ERROR, message, null); - return new SendMessageResponse(request != null ? request.getId() : null, jsonrpcError); - } -} diff --git a/contrib/samples/a2a_remote/README.md b/contrib/samples/a2a_remote/README.md deleted file mode 100644 index d1d2601ca..000000000 --- a/contrib/samples/a2a_remote/README.md +++ /dev/null @@ -1,70 +0,0 @@ -# A2A Remote Prime Service Sample - -This sample starts a standalone Spring Boot service that exposes the -`remote_prime_agent` via the shared A2A webservice module -(`google-adk-a2a-webservice`). It behaves like a third‑party service that -implements the A2A JSON‑RPC contract and can be used by the ADK client (for -example, the `a2a_basic` demo) as its remote endpoint. - -## Running the service - -```bash -cd google_adk -mvn -f contrib/samples/a2a_remote/pom.xml package - -GOOGLE_GENAI_USE_VERTEXAI=FALSE \ -GOOGLE_API_KEY= \ -mvn -f contrib/samples/a2a_remote/pom.xml exec:java -``` - -`RemoteA2AApplication` imports the reusable controller/service from -`google-adk-a2a-webservice`, so the server listens on -`http://localhost:8080/a2a/remote/v1/message:send` by default. Override the -port with `-Dspring-boot.run.arguments=--server.port=` when running via -`spring-boot:run` if you need to avoid collisions. - -``` -POST /a2a/remote/v1/message:send -Content-Type: application/json -``` - -and accepts standard A2A JSON‑RPC payloads (`SendMessageRequest`). The -response is a `SendMessageResponse` that contains either a `Message` or a -`Task` in the `result` field. Spring Boot logs the request/response lifecycle -to the console; add your preferred logging configuration if you need -persistent logs. - -## Agent implementation - -- `remote_prime_agent/Agent.java` hosts the LLM agent that checks whether - numbers are prime (lifted from the Stubby demo). The model name defaults - to `gemini-2.5-pro`; set `GOOGLE_API_KEY` before running. -- `RemoteA2AApplication` bootstraps the service by importing - `A2ARemoteConfiguration` and publishing the prime `BaseAgent` bean. The shared - configuration consumes that bean to create the `A2ASendMessageExecutor`. - -## Sample request - -```bash -curl -X POST http://localhost:8080/a2a/remote/v1/message:send \ - -H 'Content-Type: application/json' \ - -d '{ - "jsonrpc": "2.0", - "id": "demo-123", - "method": "message/send", - "params": { - "message": { - "role": "user", - "messageId": "msg-1", - "contextId": "ctx-1", - "parts": [ - {"kind": "text", "text": "Check if 17 is prime"} - ] - }, - "metadata": {} - } - }' -``` - -The response contains the prime check result, and the interaction is logged in -the application console. diff --git a/contrib/samples/a2a_remote/pom.xml b/contrib/samples/a2a_remote/pom.xml deleted file mode 100644 index 59d9cf01e..000000000 --- a/contrib/samples/a2a_remote/pom.xml +++ /dev/null @@ -1,139 +0,0 @@ - - - 4.0.0 - - - com.google.adk - google-adk-parent - 0.5.1-SNAPSHOT - ../../../pom.xml - - - google-adk-sample-a2a-remote - Google ADK - Sample - A2A Remote Prime Service - Spring Boot service that exposes the remote prime-check agent over the A2A REST interface. - jar - - - 3.3.4 - 17 - 0.8 - com.google.adk.samples.a2a_remote.RemoteA2AApplication - - - - - - org.springframework.boot - spring-boot-dependencies - ${spring-boot.version} - pom - import - - - - - - - org.springframework.boot - spring-boot-starter-web - - - - com.google.adk - google-adk - ${project.version} - - - - com.google.adk - google-adk-a2a - ${project.version} - - - - com.google.adk - google-adk-a2a-webservice - ${project.version} - - - - com.google.flogger - flogger - ${flogger.version} - - - com.google.flogger - google-extensions - ${flogger.version} - - - com.google.flogger - flogger-system-backend - ${flogger.version} - - - - org.springframework.boot - spring-boot-starter-test - test - - - - com.google.truth - truth - ${truth.version} - test - - - - - - - org.springframework.boot - spring-boot-maven-plugin - ${spring-boot.version} - - - org.codehaus.mojo - build-helper-maven-plugin - 3.6.0 - - - add-source - generate-sources - - add-source - - - - . - - - - - - - org.apache.maven.plugins - maven-source-plugin - - - **/*.jar - target/** - - - - - org.codehaus.mojo - exec-maven-plugin - 3.2.0 - - ${exec.mainClass} - runtime - - - - - \ No newline at end of file diff --git a/contrib/samples/a2a_remote/remote_prime_agent/Agent.java b/contrib/samples/a2a_remote/remote_prime_agent/Agent.java deleted file mode 100644 index a0072e8e3..000000000 --- a/contrib/samples/a2a_remote/remote_prime_agent/Agent.java +++ /dev/null @@ -1,101 +0,0 @@ -package com.google.adk.samples.a2a_remote.remote_prime_agent; - -import static java.util.stream.Collectors.joining; - -import com.google.adk.agents.LlmAgent; -import com.google.adk.tools.FunctionTool; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.flogger.GoogleLogger; -import io.reactivex.rxjava3.core.Maybe; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** Agent that can check whether numbers are prime. */ -public final class Agent { - - private static final GoogleLogger logger = GoogleLogger.forEnclosingClass(); - - public static ImmutableMap checkPrime(List nums) { - logger.atInfo().log("checkPrime called with nums=%s", nums); - Set primes = new HashSet<>(); - for (int num : nums) { - if (num <= 1) { - continue; - } - boolean isPrime = true; - for (int i = 2; i <= Math.sqrt(num); i++) { - if (num % i == 0) { - isPrime = false; - break; - } - } - if (isPrime) { - primes.add(num); - } - } - String result; - if (primes.isEmpty()) { - result = "No prime numbers found."; - } else if (primes.size() == 1) { - int only = primes.iterator().next(); - // Per request: singular phrasing without article - result = only + " is prime number."; - } else { - result = primes.stream().map(String::valueOf).collect(joining(", ")) + " are prime numbers."; - } - logger.atInfo().log("checkPrime result=%s", result); - return ImmutableMap.of("result", result); - } - - public static final LlmAgent ROOT_AGENT = - LlmAgent.builder() - .model("gemini-2.5-pro") - .name("check_prime_agent") - .description("check prime agent that can check whether numbers are prime.") - .instruction( - """ - You check whether numbers are prime. - - If the last user message contains numbers, call checkPrime exactly once with exactly - those integers as a list (e.g., [2]). Never add other numbers. Do not ask for - clarification. Return only the tool's result. - - Always pass a list of integers to the tool (use a single-element list for one - number). Never pass strings. - """) - // Log the exact contents passed to the LLM request for verification - .beforeModelCallback( - (callbackContext, llmRequest) -> { - try { - logger.atInfo().log( - "Invocation events (count=%d): %s", - callbackContext.events().size(), callbackContext.events()); - } catch (Throwable t) { - logger.atWarning().withCause(t).log("BeforeModel logging error"); - } - return Maybe.empty(); - }) - .afterModelCallback( - (callbackContext, llmResponse) -> { - try { - String content = - llmResponse.content().map(Object::toString).orElse(""); - logger.atInfo().log("AfterModel content=%s", content); - llmResponse - .errorMessage() - .ifPresent( - error -> - logger.atInfo().log( - "AfterModel errorMessage=%s", error.replace("\n", "\\n"))); - } catch (Throwable t) { - logger.atWarning().withCause(t).log("AfterModel logging error"); - } - return Maybe.empty(); - }) - .tools(ImmutableList.of(FunctionTool.create(Agent.class, "checkPrime"))) - .build(); - - private Agent() {} -} diff --git a/contrib/samples/a2a_remote/remote_prime_agent/agent.json b/contrib/samples/a2a_remote/remote_prime_agent/agent.json deleted file mode 100644 index 87f2d9ecc..000000000 --- a/contrib/samples/a2a_remote/remote_prime_agent/agent.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "capabilities": {}, - "defaultInputModes": ["text/plain"], - "defaultOutputModes": ["application/json"], - "description": "An agent specialized in checking whether numbers are prime. It can efficiently determine the primality of individual numbers or lists of numbers.", - "name": "check_prime_agent", - "skills": [ - { - "id": "prime_checking", - "name": "Prime Number Checking", - "description": "Check if numbers in a list are prime using efficient mathematical algorithms", - "tags": ["mathematical", "computation", "prime", "numbers"] - } - ], - "url": "http://localhost:8080/a2a/prime_agent", - "version": "1.0.0" -} diff --git a/contrib/samples/a2a_remote/src/main/java/com/google/adk/samples/a2a_remote/RemoteA2AApplication.java b/contrib/samples/a2a_remote/src/main/java/com/google/adk/samples/a2a_remote/RemoteA2AApplication.java deleted file mode 100644 index 53be8d1d0..000000000 --- a/contrib/samples/a2a_remote/src/main/java/com/google/adk/samples/a2a_remote/RemoteA2AApplication.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.google.adk.samples.a2a_remote; - -import com.google.adk.agents.BaseAgent; -import com.google.adk.samples.a2a_remote.remote_prime_agent.Agent; -import com.google.adk.webservice.A2ARemoteConfiguration; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Import; - -/** Spring Boot entry point that wires the shared A2A webservice with the prime demo agent. */ -@SpringBootApplication -@Import(A2ARemoteConfiguration.class) -public class RemoteA2AApplication { - - public static void main(String[] args) { - SpringApplication.run(RemoteA2AApplication.class, args); - } - - @Bean - public BaseAgent primeAgent() { - return Agent.ROOT_AGENT; - } -} diff --git a/contrib/samples/pom.xml b/contrib/samples/pom.xml index fa5d6dfae..580b10de9 100644 --- a/contrib/samples/pom.xml +++ b/contrib/samples/pom.xml @@ -17,7 +17,6 @@ a2a_basic - a2a_remote configagent helloworld mcpfilesystem diff --git a/pom.xml b/pom.xml index 6a1aa5af5..89f0d2c0f 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,6 @@ tutorials/city-time-weather tutorials/live-audio-single-agent a2a - a2a/webservice