This is an automated email from the ASF dual-hosted git repository.

wenjin272 pushed a commit to branch release-0.2
in repository https://gitbox.apache.org/repos/asf/flink-agents.git


The following commit(s) were added to refs/heads/release-0.2 by this push:
     new dddb1e2b [fix] Fix ReActAgent failure when output schema is null. 
(#838)
dddb1e2b is described below

commit dddb1e2b2a990a78d858440245080176380534a8
Author: Wenjin Xie <[email protected]>
AuthorDate: Sat Jul 25 12:28:49 2026 +0800

    [fix] Fix ReActAgent failure when output schema is null. (#838)
---
 .../apache/flink/agents/api/agents/ReActAgent.java |   5 +-
 .../agents/integration/test/ReActAgentTest.java    | 111 +++++++++++++++++----
 python/flink_agents/api/agents/react_agent.py      |   4 +-
 .../e2e_tests_integration/react_agent_test.py      |  53 ++++++++++
 4 files changed, 152 insertions(+), 21 deletions(-)

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 278e356b..33eedbec 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
@@ -59,6 +59,7 @@ public class ReActAgent extends Agent {
     public ReActAgent(
             ResourceDescriptor descriptor, @Nullable Prompt prompt, @Nullable 
Object outputSchema) {
         this.addResource(DEFAULT_CHAT_MODEL, ResourceType.CHAT_MODEL, 
descriptor);
+        Map<String, Object> actionConfig = new HashMap<>();
 
         if (outputSchema != null) {
             String jsonSchema;
@@ -81,15 +82,13 @@ public class ReActAgent extends Agent {
                                     "The final response should be json format, 
and match the schema %s",
                                     jsonSchema));
             this.addResource(DEFAULT_SCHEMA_PROMPT, ResourceType.PROMPT, 
schemaPrompt);
+            actionConfig.put("output_schema", outputSchema);
         }
 
         if (prompt != null) {
             this.addResource(DEFAULT_USER_PROMPT, ResourceType.PROMPT, prompt);
         }
 
-        Map<String, Object> actionConfig = new HashMap<>();
-        actionConfig.put("output_schema", outputSchema);
-
         try {
             Method method =
                     this.getClass().getMethod("startAction", InputEvent.class, 
RunnerContext.class);
diff --git 
a/e2e-test/flink-agents-end-to-end-tests-integration/src/test/java/org/apache/flink/agents/integration/test/ReActAgentTest.java
 
b/e2e-test/flink-agents-end-to-end-tests-integration/src/test/java/org/apache/flink/agents/integration/test/ReActAgentTest.java
index 1baa40d1..92119f40 100644
--- 
a/e2e-test/flink-agents-end-to-end-tests-integration/src/test/java/org/apache/flink/agents/integration/test/ReActAgentTest.java
+++ 
b/e2e-test/flink-agents-end-to-end-tests-integration/src/test/java/org/apache/flink/agents/integration/test/ReActAgentTest.java
@@ -34,6 +34,7 @@ import org.apache.flink.api.common.typeinfo.BasicTypeInfo;
 import org.apache.flink.api.common.typeinfo.TypeInformation;
 import org.apache.flink.api.java.functions.KeySelector;
 import org.apache.flink.api.java.typeutils.RowTypeInfo;
+import org.apache.flink.streaming.api.datastream.DataStream;
 import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
 import org.apache.flink.table.api.DataTypes;
 import org.apache.flink.table.api.Schema;
@@ -46,6 +47,7 @@ import org.junit.jupiter.api.Assumptions;
 import org.junit.jupiter.api.Test;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.List;
 
 import static 
org.apache.flink.agents.api.agents.AgentExecutionOptions.ERROR_HANDLING_STRATEGY;
@@ -114,7 +116,7 @@ public class ReActAgentTest {
         agentsEnv.getConfig().set(MAX_RETRIES, 3);
 
         // Declare the ReAct agent.
-        Agent agent = getAgent();
+        Agent agent = getAgent(true);
 
         // Create input table from sample data
         Table inputTable =
@@ -152,8 +154,80 @@ public class ReActAgentTest {
         checkResult(results);
     }
 
-    // create ReAct agent.
-    private static Agent getAgent() {
+    @Test
+    public void testReActAgentNoOutputSchema() throws Exception {
+        Assumptions.assumeTrue(ollamaReady, String.format("%s is not ready", 
OLLAMA_MODEL));
+        StreamExecutionEnvironment env = 
StreamExecutionEnvironment.getExecutionEnvironment();
+        env.setParallelism(1);
+
+        // Create the table environment
+        StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);
+        tableEnv.getConfig().set("table.exec.result.display.max-column-width", 
"100");
+
+        // Create agents execution environment
+        AgentsExecutionEnvironment agentsEnv =
+                AgentsExecutionEnvironment.getExecutionEnvironment(env, 
tableEnv);
+
+        // register resource to agents execution environment.
+        agentsEnv
+                .addResource(
+                        "ollama",
+                        ResourceType.CHAT_MODEL_CONNECTION,
+                        ResourceDescriptor.Builder.newBuilder(
+                                        
ResourceName.ChatModel.OLLAMA_CONNECTION)
+                                .addInitialArgument("endpoint", 
"http://localhost:11434";)
+                                .addInitialArgument("requestTimeout", 240)
+                                .build())
+                .addResource(
+                        "add",
+                        ResourceType.TOOL,
+                        Tool.fromMethod(
+                                ReActAgentTest.class.getMethod("add", 
Double.class, Double.class)))
+                .addResource(
+                        "multiply",
+                        ResourceType.TOOL,
+                        Tool.fromMethod(
+                                ReActAgentTest.class.getMethod(
+                                        "multiply", Double.class, 
Double.class)));
+
+        agentsEnv.getConfig().set(ERROR_HANDLING_STRATEGY, 
ReActAgent.ErrorHandlingStrategy.RETRY);
+        agentsEnv.getConfig().set(MAX_RETRIES, 3);
+
+        // Declare the ReAct agent without an output schema.
+        Agent agent = getAgent(false);
+
+        // Create input table from sample data
+        Table inputTable =
+                tableEnv.fromValues(
+                        DataTypes.ROW(
+                                DataTypes.FIELD("a", DataTypes.DOUBLE()),
+                                DataTypes.FIELD("b", DataTypes.DOUBLE()),
+                                DataTypes.FIELD("c", DataTypes.DOUBLE())),
+                        Row.of(2131, 29847, 3));
+
+        // Apply agent to the Table; without an output schema the result is a 
string.
+        DataStream<Object> out =
+                agentsEnv
+                        .fromTable(
+                                inputTable,
+                                (KeySelector<Object, Double>)
+                                        value -> (Double) ((Row) 
value).getField("a"))
+                        .apply(agent)
+                        .toDataStream();
+
+        CloseableIterator<Object> results = out.collectAsync();
+
+        env.execute();
+
+        Assertions.assertTrue(results.hasNext(), "The agent should emit a 
result.");
+        Assertions.assertInstanceOf(
+                String.class,
+                results.next(),
+                "The result should be a String when no output schema is 
provided.");
+    }
+
+    // create ReAct agent; pass false to skip the output schema.
+    private static Agent getAgent(boolean withSchema) {
         ResourceDescriptor chatModelDescriptor =
                 
ResourceDescriptor.Builder.newBuilder(ResourceName.ChatModel.OLLAMA_SETUP)
                         .addInitialArgument("connection", "ollama")
@@ -162,21 +236,24 @@ public class ReActAgentTest {
                         .addInitialArgument("extract_reasoning", true)
                         .build();
 
-        Prompt prompt =
-                Prompt.fromMessages(
-                        List.of(
-                                new ChatMessage(
-                                        MessageRole.SYSTEM,
-                                        "Must call function tool to do the 
calculate."),
-                                new ChatMessage(
-                                        MessageRole.SYSTEM,
-                                        "An example of output is {\"result\": 
30.32}"),
-                                new ChatMessage(MessageRole.USER, "What is 
({a} + {b}) * {c}.")));
+        List<ChatMessage> messages = new ArrayList<>();
+        messages.add(
+                new ChatMessage(
+                        MessageRole.SYSTEM, "Must call function tool to do the 
calculate."));
+        if (withSchema) {
+            messages.add(
+                    new ChatMessage(
+                            MessageRole.SYSTEM, "An example of output is 
{\"result\": 30.32}"));
+        }
+        messages.add(new ChatMessage(MessageRole.USER, "What is ({a} + {b}) * 
{c}."));
+
         RowTypeInfo outputTypeInfo =
-                new RowTypeInfo(
-                        new TypeInformation[] {BasicTypeInfo.DOUBLE_TYPE_INFO},
-                        new String[] {"result"});
-        return new ReActAgent(chatModelDescriptor, prompt, outputTypeInfo);
+                withSchema
+                        ? new RowTypeInfo(
+                                new TypeInformation[] 
{BasicTypeInfo.DOUBLE_TYPE_INFO},
+                                new String[] {"result"})
+                        : null;
+        return new ReActAgent(chatModelDescriptor, 
Prompt.fromMessages(messages), outputTypeInfo);
     }
 
     private void checkResult(CloseableIterator<?> results) {
diff --git a/python/flink_agents/api/agents/react_agent.py 
b/python/flink_agents/api/agents/react_agent.py
index fbb4b93f..bdd7414d 100644
--- a/python/flink_agents/api/agents/react_agent.py
+++ b/python/flink_agents/api/agents/react_agent.py
@@ -138,7 +138,9 @@ class ReActAgent(Agent):
             name="start_action",
             events=[InputEvent],
             func=self.start_action,
-            output_schema=OutputSchema(output_schema=output_schema),
+            output_schema=OutputSchema(output_schema=output_schema)
+            if output_schema
+            else None,
         )
 
     @staticmethod
diff --git 
a/python/flink_agents/e2e_tests/e2e_tests_integration/react_agent_test.py 
b/python/flink_agents/e2e_tests/e2e_tests_integration/react_agent_test.py
index 5e4880df..31d4b447 100644
--- a/python/flink_agents/e2e_tests/e2e_tests_integration/react_agent_test.py
+++ b/python/flink_agents/e2e_tests/e2e_tests_integration/react_agent_test.py
@@ -133,6 +133,59 @@ def test_react_agent_on_local_runner() -> None:  # noqa: 
D103
     assert output_list[0]["0001"].result == 1386528
 
 
[email protected](
+    client is None, reason="Ollama client is not available or test model is 
missing"
+)
+def test_react_agent_no_output_schema_on_local_runner() -> None:  # noqa: D103
+    env = AgentsExecutionEnvironment.get_execution_environment()
+    env.get_config().set(
+        AgentExecutionOptions.ERROR_HANDLING_STRATEGY, 
ErrorHandlingStrategy.RETRY
+    )
+    env.get_config().set(AgentExecutionOptions.MAX_RETRIES, 3)
+
+    # register resource to execution environment
+    (
+        env.add_resource(
+            "ollama",
+            ResourceType.CHAT_MODEL_CONNECTION,
+            ResourceDescriptor(clazz=ResourceName.ChatModel.OLLAMA_CONNECTION, 
request_timeout=240.0),
+        )
+        .add_resource("add", ResourceType.TOOL, Tool.from_callable(add))
+        .add_resource("multiply", ResourceType.TOOL, 
Tool.from_callable(multiply))
+    )
+
+    # prepare prompt
+    prompt = Prompt.from_messages(
+        messages=[
+            ChatMessage(role=MessageRole.USER, content="What is ({a} + {b}) * 
{c}"),
+        ],
+    )
+
+    # create ReAct agent without an output schema; result is emitted as a 
string.
+    agent = ReActAgent(
+        chat_model=ResourceDescriptor(
+            clazz=ResourceName.ChatModel.OLLAMA_SETUP,
+            connection="ollama",
+            model=OLLAMA_MODEL,
+            tools=["add", "multiply"],
+        ),
+        prompt=prompt,
+    )
+
+    # execute agent
+    input_list = []
+
+    output_list = env.from_list(input_list).apply(agent).to_list()
+    input_list.append({"key": "0001", "value": InputData(a=2123, b=2321, 
c=312)})
+
+    env.execute()
+
+    assert len(output_list) == 1, (
+        "This may be caused by the LLM response failing to produce a result, 
you can rerun this case."
+    )
+    assert isinstance(output_list[0]["0001"], str)
+
+
 @pytest.mark.skipif(
     client is None, reason="Ollama client is not available or test model is 
missing"
 )

Reply via email to