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

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


The following commit(s) were added to refs/heads/main by this push:
     new 9b5d223f [api][java][python] Reject non-positive max_results in 
ContextRetrievalRequestEvent (#1157)
9b5d223f is described below

commit 9b5d223f29d438bf079b17e892cef561a7bfddb9
Author: william <[email protected]>
AuthorDate: Tue Sep 29 15:30:45 2026 +0800

    [api][java][python] Reject non-positive max_results in 
ContextRetrievalRequestEvent (#1157)
---
 .../api/event/ContextRetrievalRequestEvent.java    |  31 ++++++
 .../event/ContextRetrievalRequestEventTest.java    | 112 +++++++++++++++++++++
 .../api/events/context_retrieval_event.py          |  16 ++-
 .../api/tests/test_context_retrieval_event.py      |  52 ++++++++++
 4 files changed, 210 insertions(+), 1 deletion(-)

diff --git 
a/api/src/main/java/org/apache/flink/agents/api/event/ContextRetrievalRequestEvent.java
 
b/api/src/main/java/org/apache/flink/agents/api/event/ContextRetrievalRequestEvent.java
index 25580308..fb336137 100644
--- 
a/api/src/main/java/org/apache/flink/agents/api/event/ContextRetrievalRequestEvent.java
+++ 
b/api/src/main/java/org/apache/flink/agents/api/event/ContextRetrievalRequestEvent.java
@@ -37,18 +37,49 @@ public class ContextRetrievalRequestEvent extends Event {
         this(query, vectorStore, DEFAULT_MAX_RESULTS);
     }
 
+    /**
+     * Creates a new context retrieval request event.
+     *
+     * @param query the search query text
+     * @param vectorStore the name of the vector store to query
+     * @param maxResults the maximum number of results to return; must be 
positive
+     * @throws IllegalArgumentException if {@code maxResults} is non-positive
+     */
     public ContextRetrievalRequestEvent(String query, String vectorStore, int 
maxResults) {
         super(EVENT_TYPE);
+        validateMaxResults(maxResults);
         setAttr("query", query);
         setAttr("vector_store", vectorStore);
         setAttr("max_results", maxResults);
     }
 
+    /**
+     * Reconstructs a context retrieval request event from its serialized form.
+     *
+     * <p>The {@code max_results} attribute is validated here as well, so that 
reconstructed events
+     * are rejected the same way as directly constructed ones and stay 
consistent with the Python
+     * implementation.
+     *
+     * @param id the event id
+     * @param attributes the event attributes
+     * @throws IllegalArgumentException if {@code max_results} is present and 
non-positive
+     */
     @JsonCreator
     public ContextRetrievalRequestEvent(
             @JsonProperty("id") UUID id,
             @JsonProperty("attributes") Map<String, Object> attributes) {
         super(id, EVENT_TYPE, attributes);
+        Object maxResults = attributes == null ? null : 
attributes.get("max_results");
+        if (maxResults instanceof Number) {
+            validateMaxResults(((Number) maxResults).intValue());
+        }
+    }
+
+    private static void validateMaxResults(int maxResults) {
+        if (maxResults <= 0) {
+            throw new IllegalArgumentException(
+                    String.format("`max_results` must be positive, but was 
%d.", maxResults));
+        }
     }
 
     /**
diff --git 
a/api/src/test/java/org/apache/flink/agents/api/event/ContextRetrievalRequestEventTest.java
 
b/api/src/test/java/org/apache/flink/agents/api/event/ContextRetrievalRequestEventTest.java
new file mode 100644
index 00000000..79dc8d85
--- /dev/null
+++ 
b/api/src/test/java/org/apache/flink/agents/api/event/ContextRetrievalRequestEventTest.java
@@ -0,0 +1,112 @@
+/*
+ * 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.event;
+
+import org.apache.flink.agents.api.Event;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/** Tests for the max results validation of {@link 
ContextRetrievalRequestEvent}. */
+class ContextRetrievalRequestEventTest {
+
+    @Test
+    @DisplayName("Zero max results is rejected with a message naming the 
parameter")
+    void testZeroMaxResultsRejected() {
+        assertThatThrownBy(() -> new ContextRetrievalRequestEvent("query", 
"store", 0))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("max_results");
+    }
+
+    @Test
+    @DisplayName(
+            "Negative max results is rejected with a message naming the 
parameter and its value")
+    void testNegativeMaxResultsRejected() {
+        assertThatThrownBy(() -> new ContextRetrievalRequestEvent("query", 
"store", -1))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("max_results")
+                .hasMessageContaining("-1");
+    }
+
+    @Test
+    @DisplayName("Positive max results is accepted")
+    void testPositiveMaxResultsAccepted() {
+        assertThat(new ContextRetrievalRequestEvent("query", "store", 
5).getMaxResults())
+                .isEqualTo(5);
+    }
+
+    @Test
+    @DisplayName("Default max results is unchanged")
+    void testDefaultMaxResultsUnchanged() {
+        assertThat(new ContextRetrievalRequestEvent("query", 
"store").getMaxResults()).isEqualTo(3);
+    }
+
+    @Test
+    @DisplayName("Reconstruction from attributes rejects non-positive max 
results")
+    void testReconstructedNonPositiveMaxResultsRejected() {
+        assertThatThrownBy(() -> new 
ContextRetrievalRequestEvent(UUID.randomUUID(), attributes(0)))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("max_results");
+    }
+
+    @Test
+    @DisplayName("Reconstruction from a base event rejects non-positive max 
results")
+    void testFromEventNonPositiveMaxResultsRejected() {
+        assertThatThrownBy(() -> 
ContextRetrievalRequestEvent.fromEvent(baseEvent(-2)))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("max_results")
+                .hasMessageContaining("-2");
+    }
+
+    @Test
+    @DisplayName("Reconstruction of a valid event keeps max results")
+    void testFromEventValidMaxResultsAccepted() {
+        
assertThat(ContextRetrievalRequestEvent.fromEvent(baseEvent(5)).getMaxResults())
+                .isEqualTo(5);
+    }
+
+    @Test
+    @DisplayName("Reconstruction tolerates a missing max results attribute")
+    void testReconstructionWithoutMaxResultsAttribute() {
+        Map<String, Object> attrs = attributes(5);
+        attrs.remove("max_results");
+        assertThatCode(() -> new 
ContextRetrievalRequestEvent(UUID.randomUUID(), attrs))
+                .doesNotThrowAnyException();
+    }
+
+    private static Map<String, Object> attributes(int maxResults) {
+        Map<String, Object> attrs = new HashMap<>();
+        attrs.put("query", "query");
+        attrs.put("vector_store", "store");
+        attrs.put("max_results", maxResults);
+        return attrs;
+    }
+
+    private static Event baseEvent(int maxResults) {
+        return new Event(
+                UUID.randomUUID(), ContextRetrievalRequestEvent.EVENT_TYPE, 
attributes(maxResults));
+    }
+}
diff --git a/python/flink_agents/api/events/context_retrieval_event.py 
b/python/flink_agents/api/events/context_retrieval_event.py
index d014e185..7dde0ded 100644
--- a/python/flink_agents/api/events/context_retrieval_event.py
+++ b/python/flink_agents/api/events/context_retrieval_event.py
@@ -43,7 +43,21 @@ class ContextRetrievalRequestEvent(Event):
     EVENT_TYPE: ClassVar[str] = "_context_retrieval_request_event"
 
     def __init__(self, query: str, vector_store: str, max_results: int = 3) -> 
None:
-        """Create a ContextRetrievalRequestEvent."""
+        """Create a ContextRetrievalRequestEvent.
+
+        Args:
+        ----
+        query: The search query text to find relevant context for.
+        vector_store: Name of the vector store setup resource to use.
+        max_results: Maximum number of results to return (default: 3). Must be 
positive.
+
+        Raises:
+        ------
+        ValueError: If `max_results` is not positive.
+        """
+        if max_results <= 0:
+            msg = f"`max_results` must be positive, but was {max_results}."
+            raise ValueError(msg)
         super().__init__(
             type=ContextRetrievalRequestEvent.EVENT_TYPE,
             attributes={
diff --git a/python/flink_agents/api/tests/test_context_retrieval_event.py 
b/python/flink_agents/api/tests/test_context_retrieval_event.py
new file mode 100644
index 00000000..19db8417
--- /dev/null
+++ b/python/flink_agents/api/tests/test_context_retrieval_event.py
@@ -0,0 +1,52 @@
+################################################################################
+#  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.
+#################################################################################
+"""Tests for the max_results validation of 
:class:`ContextRetrievalRequestEvent`."""
+
+from __future__ import annotations
+
+import pytest
+
+from flink_agents.api.events.context_retrieval_event import 
ContextRetrievalRequestEvent
+
+
+def test_zero_max_results_rejected() -> None:
+    """A zero max_results should be rejected."""
+    with pytest.raises(ValueError, match="max_results"):
+        ContextRetrievalRequestEvent(query="flink", vector_store="store", 
max_results=0)
+
+
+def test_negative_max_results_rejected() -> None:
+    """A negative max_results should be rejected."""
+    with pytest.raises(ValueError, match="max_results"):
+        ContextRetrievalRequestEvent(
+            query="flink", vector_store="store", max_results=-1
+        )
+
+
+def test_positive_max_results_accepted() -> None:
+    """A positive max_results should be accepted."""
+    event = ContextRetrievalRequestEvent(
+        query="flink", vector_store="store", max_results=5
+    )
+    assert event.max_results == 5
+
+
+def test_default_max_results_unchanged() -> None:
+    """The default max_results should stay unchanged."""
+    event = ContextRetrievalRequestEvent(query="flink", vector_store="store")
+    assert event.max_results == 3

Reply via email to