diff --git a/dev/src/main/java/com/google/adk/web/AdkWebServer.java b/dev/src/main/java/com/google/adk/web/AdkWebServer.java index 0d7c7d0e1..b321cef27 100644 --- a/dev/src/main/java/com/google/adk/web/AdkWebServer.java +++ b/dev/src/main/java/com/google/adk/web/AdkWebServer.java @@ -35,6 +35,8 @@ import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -87,10 +89,25 @@ public BaseMemoryService memoryService() { * @return Configured ObjectMapper instance */ @Bean + @Primary public ObjectMapper objectMapper() { return JsonBaseModel.getMapper(); } + /** + * Configures the message converter to use the custom ADK ObjectMapper. This ensures that Spring + * Web uses the correct JSON serialization settings (like omitting absent optional fields) and + * prevents double-serialization issues, particularly for Server-Sent Events (SSE). + * + * @param objectMapper The primary ObjectMapper configured for the ADK. + * @return A configured MappingJackson2HttpMessageConverter. + */ + @Bean + public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter( + ObjectMapper objectMapper) { + return new MappingJackson2HttpMessageConverter(objectMapper); + } + /** * Configures resource handlers for serving static content (like the Dev UI). Maps requests * starting with "/dev-ui/" to the directory specified by the 'adk.web.ui.dir' system property. diff --git a/dev/src/main/java/com/google/adk/web/controller/ExecutionController.java b/dev/src/main/java/com/google/adk/web/controller/ExecutionController.java index 6d5a2764c..e88a83cef 100644 --- a/dev/src/main/java/com/google/adk/web/controller/ExecutionController.java +++ b/dev/src/main/java/com/google/adk/web/controller/ExecutionController.java @@ -81,7 +81,11 @@ public List agentRun(@RequestBody AgentRunRequest request) { RunConfig runConfig = RunConfig.builder().setStreamingMode(StreamingMode.NONE).build(); Flowable eventStream = runner.runAsync( - request.userId, request.sessionId, request.newMessage, runConfig, request.stateDelta); + request.userId, + request.sessionId, + request.getNewMessage(), + runConfig, + request.stateDelta); List events = Lists.newArrayList(eventStream.blockingIterable()); log.info("Agent run for session {} generated {} events.", request.sessionId, events.size()); @@ -155,7 +159,7 @@ public SseEmitter agentRunSse(@RequestBody AgentRunRequest request) { runner.runAsync( request.userId, request.sessionId, - request.newMessage, + request.getNewMessage(), runConfig, request.stateDelta); @@ -167,7 +171,7 @@ public SseEmitter agentRunSse(@RequestBody AgentRunRequest request) { try { log.debug( "SseEmitter: Sending event {} for session {}", event.id(), sessionId); - emitter.send(SseEmitter.event().data(event.toJson())); + emitter.send(SseEmitter.event().data(event)); } catch (IOException e) { log.error( "SseEmitter: IOException sending event for session {}: {}", diff --git a/dev/src/main/java/com/google/adk/web/dto/AgentRunRequest.java b/dev/src/main/java/com/google/adk/web/dto/AgentRunRequest.java index 652de5aac..025aa64eb 100644 --- a/dev/src/main/java/com/google/adk/web/dto/AgentRunRequest.java +++ b/dev/src/main/java/com/google/adk/web/dto/AgentRunRequest.java @@ -17,6 +17,7 @@ package com.google.adk.web.dto; import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.adk.JsonBaseModel; import com.google.genai.types.Content; import java.util.Map; import javax.annotation.Nullable; @@ -36,7 +37,7 @@ public class AgentRunRequest { public String sessionId; @JsonProperty("newMessage") - public Content newMessage; + public Object newMessage; @JsonProperty("streaming") public boolean streaming = false; @@ -65,7 +66,17 @@ public String getSessionId() { } public Content getNewMessage() { - return newMessage; + if (newMessage instanceof Content) { + return (Content) newMessage; + } + if (newMessage != null) { + try { + return JsonBaseModel.getMapper().convertValue(newMessage, Content.class); + } catch (IllegalArgumentException e) { + throw new IllegalStateException("Failed to parse newMessage into Content", e); + } + } + return null; } public boolean getStreaming() {