diff --git a/api/src/main/java/org/apache/flink/agents/api/agents/ReActAgent.java b/api/src/main/java/org/apache/flink/agents/api/agents/ReActAgent.java
index 10ed457e0..798b453f3 100644
--- a/api/src/main/java/org/apache/flink/agents/api/agents/ReActAgent.java
+++ b/api/src/main/java/org/apache/flink/agents/api/agents/ReActAgent.java
@@ -178,7 +178,7 @@ public static void stopAction(Event event, RunnerContext ctx) {
if (response.getExtraArgs().containsKey(STRUCTURED_OUTPUT)) {
output = response.getExtraArgs().get(STRUCTURED_OUTPUT);
} else {
- output = String.valueOf(response.getContent());
+ output = response.getText();
}
ctx.sendEvent(new OutputEvent(output));
diff --git a/api/src/main/java/org/apache/flink/agents/api/chat/messages/AudioBlock.java b/api/src/main/java/org/apache/flink/agents/api/chat/messages/AudioBlock.java
new file mode 100644
index 000000000..b9b7c507b
--- /dev/null
+++ b/api/src/main/java/org/apache/flink/agents/api/chat/messages/AudioBlock.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.flink.agents.api.chat.messages;
+
+/** The audio content of a {@link ChatMessage} — see {@link MediaBlock} for the media shape. */
+public final class AudioBlock extends MediaBlock {
+
+ public AudioBlock() {}
+
+ private AudioBlock(String mimeType, String data, String url) {
+ super(mimeType, data, url);
+ }
+
+ /** Creates an audio block carrying an inline base64 payload. */
+ public static AudioBlock fromBase64(String mimeType, String data) {
+ return new AudioBlock(mimeType, data, null);
+ }
+
+ /** Creates an audio block referencing an externally managed URL or provider file URI. */
+ public static AudioBlock fromUrl(String mimeType, String url) {
+ return new AudioBlock(mimeType, null, url);
+ }
+}
diff --git a/api/src/main/java/org/apache/flink/agents/api/chat/messages/ChatMessage.java b/api/src/main/java/org/apache/flink/agents/api/chat/messages/ChatMessage.java
index a15f220ce..5d6b50f98 100644
--- a/api/src/main/java/org/apache/flink/agents/api/chat/messages/ChatMessage.java
+++ b/api/src/main/java/org/apache/flink/agents/api/chat/messages/ChatMessage.java
@@ -20,21 +20,32 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.stream.Collectors;
/**
* Chat message class that represents all message types (user, system, assistant, tool) with
- * different roles
+ * different roles.
+ *
+ *
Message content is an ordered list of typed {@link ContentBlock}s ({@link TextBlock} plus the
+ * media blocks); a text-only message simply carries one {@link TextBlock}. The string convenience
+ * constructors and factories preserve the text-message experience, and {@link #getText()} is the
+ * ordered concatenation of the text blocks.
*/
public class ChatMessage {
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
private MessageRole role;
- private String content;
+ private List blocks;
@JsonProperty("tool_calls")
private List