You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by zh...@apache.org on 2026/08/03 14:48:02 UTC

(shardingsphere) branch master updated: Fix sharding workflow alternative input clarification (#39324)

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

terrymanu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git


The following commit(s) were added to refs/heads/master by this push:
     new 2ccb352f8a6 Fix sharding workflow alternative input clarification (#39324)
2ccb352f8a6 is described below

commit 2ccb352f8a634535aaeb905aaa868b7e4e6ecdf5
Author: Liang Zhang <zh...@apache.org>
AuthorDate: Mon Aug 3 22:47:56 2026 +0800

    Fix sharding workflow alternative input clarification (#39324)
    
    * Fix sharding workflow alternative input clarification
    
    * Fix sharding workflow alternative input clarification
---
 .../service/ShardingWorkflowInputValidator.java    | 31 +++----
 .../ShardingWorkflowPlanningServiceTest.java       | 99 ++++++++++++++++++++++
 2 files changed, 115 insertions(+), 15 deletions(-)

diff --git a/mcp/features/sharding/src/main/java/org/apache/shardingsphere/mcp/feature/sharding/tool/service/ShardingWorkflowInputValidator.java b/mcp/features/sharding/src/main/java/org/apache/shardingsphere/mcp/feature/sharding/tool/service/ShardingWorkflowInputValidator.java
index 2455c4cea20..8ec7ab73b88 100644
--- a/mcp/features/sharding/src/main/java/org/apache/shardingsphere/mcp/feature/sharding/tool/service/ShardingWorkflowInputValidator.java
+++ b/mcp/features/sharding/src/main/java/org/apache/shardingsphere/mcp/feature/sharding/tool/service/ShardingWorkflowInputValidator.java
@@ -180,7 +180,7 @@ final class ShardingWorkflowInputValidator {
             return false;
         }
         if (request.getDataNodes().isEmpty() && request.getStorageUnits().isEmpty()) {
-            return clarify(snapshot, List.of("data_nodes", "storage_units"), "Please provide data nodes or storage units.");
+            return clarify(snapshot, "Please provide data nodes or storage units.");
         }
         return (request.getStorageUnits().isEmpty() ? ensureRequiredStrategyInputs(request, snapshot) : ensureRequiredAutoTableRuleInputs(request, snapshot))
                 && ensureRequiredTableRuleKeyGenerateInputs(request, snapshot) && ensureRequiredAuditInputs(request, snapshot);
@@ -199,10 +199,16 @@ final class ShardingWorkflowInputValidator {
             }
             return require(snapshot, "", "key_generate_column", "Please provide key generate column when configuring a key generator.");
         }
-        if (!request.getKeyGeneratorName().isEmpty()) {
+        return ensureRequiredKeyGeneratorSelection(request, snapshot);
+    }
+    
+    private boolean ensureRequiredKeyGeneratorSelection(final ShardingWorkflowRequest request, final WorkflowContextSnapshot snapshot) {
+        if (!request.getKeyGeneratorName().isEmpty() || !request.getKeyGeneratorType().isEmpty()) {
             return true;
         }
-        return require(snapshot, request.getKeyGeneratorType(), "key_generator_type", "Please provide key generator type or key generator name for key generate strategy.");
+        return request.getKeyGeneratorProperties().isEmpty()
+                ? clarify(snapshot, "Please provide key_generator or key_generator_type.")
+                : require(snapshot, "", "key_generator_type", "Please provide key generator type.");
     }
     
     private boolean ensureRequiredAuditInputs(final ShardingWorkflowRequest request, final WorkflowContextSnapshot snapshot) {
@@ -290,16 +296,13 @@ final class ShardingWorkflowInputValidator {
             return false;
         }
         if (request.getSequenceName().isEmpty() && request.getTable().isEmpty() && request.getColumn().isEmpty()) {
-            return clarify(snapshot, List.of("table", "column", "sequence"), "Please provide table and column, or sequence.");
+            return clarify(snapshot, "Please provide table and column, or sequence.");
         }
         if (request.getSequenceName().isEmpty() && (!require(snapshot, request.getTable(), "table", "Please provide target logical table.")
                 || !require(snapshot, request.getColumn(), "column", "Please provide key generate column."))) {
             return false;
         }
-        if (request.getKeyGeneratorName().isEmpty() && request.getKeyGeneratorType().isEmpty()) {
-            return clarify(snapshot, List.of("key_generator", "key_generator_type"), "Please provide key_generator or key_generator_type.");
-        }
-        return true;
+        return ensureRequiredKeyGeneratorSelection(request, snapshot);
     }
     
     boolean ensureRequiredCleanupInputs(final ShardingWorkflowRequest request, final WorkflowContextSnapshot snapshot) {
@@ -333,15 +336,13 @@ final class ShardingWorkflowInputValidator {
     }
     
     private boolean clarify(final WorkflowContextSnapshot snapshot, final String fieldName, final String message) {
-        return clarify(snapshot, List.of(fieldName), message);
+        if (!snapshot.getClarifiedIntent().getUnresolvedFields().contains(fieldName)) {
+            snapshot.getClarifiedIntent().getUnresolvedFields().add(fieldName);
+        }
+        return clarify(snapshot, message);
     }
     
-    private boolean clarify(final WorkflowContextSnapshot snapshot, final Collection<String> fieldNames, final String message) {
-        for (String each : fieldNames) {
-            if (!snapshot.getClarifiedIntent().getUnresolvedFields().contains(each)) {
-                snapshot.getClarifiedIntent().getUnresolvedFields().add(each);
-            }
-        }
+    private boolean clarify(final WorkflowContextSnapshot snapshot, final String message) {
         if (!snapshot.getClarifiedIntent().getClarificationMessages().contains(message)) {
             snapshot.getClarifiedIntent().getClarificationMessages().add(message);
         }
diff --git a/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/tool/service/ShardingWorkflowPlanningServiceTest.java b/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/tool/service/ShardingWorkflowPlanningServiceTest.java
index c1292859eea..cbe3df79557 100644
--- a/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/tool/service/ShardingWorkflowPlanningServiceTest.java
+++ b/mcp/features/sharding/src/test/java/org/apache/shardingsphere/mcp/feature/sharding/tool/service/ShardingWorkflowPlanningServiceTest.java
@@ -98,6 +98,35 @@ class ShardingWorkflowPlanningServiceTest {
         assertThat(actual.getRuleArtifacts().getFirst().getSql(), is("CREATE SHARDING TABLE RULE `t_order`(DATANODES('ds_0.t_order'))"));
     }
     
+    @Test
+    void assertPlanTableRuleContinuesAfterDataNodeModeClarification() {
+        TestWorkflowSessionContext sessionContext = new TestWorkflowSessionContext();
+        ShardingWorkflowRequest initialRequest = createDatabaseRequest();
+        initialRequest.setTable("t_order");
+        WorkflowContextSnapshot initial = planningService.planTableRule(sessionContext, mock(MCPFeatureQueryFacade.class), initialRequest);
+        assertChoiceClarification(initial, "Please provide data nodes or storage units.");
+        ShardingWorkflowRequest continuation = new ShardingWorkflowRequest();
+        continuation.setPlanId(initial.getPlanId());
+        continuation.setDataNodes("ds_0.t_order");
+        continuation.setStrategyType("none");
+        WorkflowContextSnapshot actual = planningService.planTableRule(sessionContext, createQueryFacade(List.of()), continuation);
+        assertThat(actual.getStatus(), is(WorkflowLifecycle.STATUS_PLANNED));
+    }
+    
+    @Test
+    void assertPlanTableRuleContinuesAfterKeyGeneratorModeClarification() {
+        TestWorkflowSessionContext sessionContext = new TestWorkflowSessionContext();
+        ShardingWorkflowRequest initialRequest = createTableRuleRequest();
+        initialRequest.setKeyGenerateColumn("order_id");
+        WorkflowContextSnapshot initial = planningService.planTableRule(sessionContext, mock(MCPFeatureQueryFacade.class), initialRequest);
+        assertChoiceClarification(initial, "Please provide key_generator or key_generator_type.");
+        ShardingWorkflowRequest continuation = new ShardingWorkflowRequest();
+        continuation.setPlanId(initial.getPlanId());
+        continuation.setKeyGeneratorName("snowflake_generator");
+        WorkflowContextSnapshot actual = planningService.planTableRule(sessionContext, createQueryFacade(List.of()), continuation);
+        assertThat(actual.getStatus(), is(WorkflowLifecycle.STATUS_PLANNED));
+    }
+    
     @Test
     void assertPlanTableReferenceRuleClarifiesMissingInputs() {
         ShardingWorkflowRequest request = createDatabaseRequest();
@@ -245,6 +274,62 @@ class ShardingWorkflowPlanningServiceTest {
         assertThat(WorkflowPlanPayloadBuilder.build(actual).get("missing_required_inputs"), is(List.of("key_generate_strategy")));
     }
     
+    @Test
+    void assertPlanKeyGenerateStrategyContinuesAfterTargetModeClarification() {
+        TestWorkflowSessionContext sessionContext = new TestWorkflowSessionContext();
+        ShardingWorkflowRequest initialRequest = createDatabaseRequest();
+        initialRequest.setKeyGenerateStrategyName("order_sequence_strategy");
+        initialRequest.setKeyGeneratorName("snowflake_generator");
+        WorkflowContextSnapshot initial = planningService.planKeyGenerateStrategy(sessionContext, mock(MCPFeatureQueryFacade.class), initialRequest);
+        assertChoiceClarification(initial, "Please provide table and column, or sequence.");
+        ShardingWorkflowRequest continuation = new ShardingWorkflowRequest();
+        continuation.setPlanId(initial.getPlanId());
+        continuation.setSequenceName("order_seq");
+        WorkflowContextSnapshot actual = planningService.planKeyGenerateStrategy(sessionContext, createQueryFacade(List.of()), continuation);
+        assertThat(actual.getStatus(), is(WorkflowLifecycle.STATUS_PLANNED));
+    }
+    
+    @Test
+    void assertPlanKeyGenerateStrategyContinuesAfterGeneratorModeClarification() {
+        TestWorkflowSessionContext sessionContext = new TestWorkflowSessionContext();
+        ShardingWorkflowRequest initialRequest = createDatabaseRequest();
+        initialRequest.setKeyGenerateStrategyName("order_sequence_strategy");
+        initialRequest.setSequenceName("order_seq");
+        WorkflowContextSnapshot initial = planningService.planKeyGenerateStrategy(sessionContext, mock(MCPFeatureQueryFacade.class), initialRequest);
+        assertChoiceClarification(initial, "Please provide key_generator or key_generator_type.");
+        ShardingWorkflowRequest continuation = new ShardingWorkflowRequest();
+        continuation.setPlanId(initial.getPlanId());
+        continuation.setKeyGeneratorName("snowflake_generator");
+        WorkflowContextSnapshot actual = planningService.planKeyGenerateStrategy(sessionContext, createQueryFacade(List.of()), continuation);
+        assertThat(actual.getStatus(), is(WorkflowLifecycle.STATUS_PLANNED));
+    }
+    
+    @Test
+    void assertPlanKeyGenerateStrategyContinuesAfterMissingKeyGeneratorTypeClarification() {
+        TestWorkflowSessionContext sessionContext = new TestWorkflowSessionContext();
+        ShardingWorkflowRequest initialRequest = createDatabaseRequest();
+        initialRequest.setKeyGenerateStrategyName("order_sequence_strategy");
+        initialRequest.setSequenceName("order_seq");
+        initialRequest.putKeyGeneratorProperties(Map.of("worker-id", "1"));
+        WorkflowContextSnapshot initial = planningService.planKeyGenerateStrategy(sessionContext, mock(MCPFeatureQueryFacade.class), initialRequest);
+        assertThat(initial.getStatus(), is(WorkflowLifecycle.STATUS_CLARIFYING));
+        assertThat(initial.getClarifiedIntent().getUnresolvedFields(), is(List.of("key_generator_type")));
+        assertThat(initial.getClarifiedIntent().getClarificationMessages(), is(List.of("Please provide key generator type.")));
+        Map<String, Object> initialPayload = WorkflowPlanPayloadBuilder.build(initial);
+        assertThat(initialPayload.get("missing_required_inputs"), is(List.of("key_generator_type")));
+        Map<?, ?> actualQuestion = (Map<?, ?>) ((List<?>) initialPayload.get("clarification_questions")).getFirst();
+        assertThat(actualQuestion.get("field"), is("key_generator_type"));
+        assertThat(actualQuestion.get("display_message"), is("Please provide key generator type."));
+        Map<?, ?> actualNextAction = (Map<?, ?>) ((List<?>) initialPayload.get("next_actions")).getFirst();
+        assertThat(actualNextAction.get("required_inputs"), is(List.of("key_generator_type")));
+        ShardingWorkflowRequest continuation = new ShardingWorkflowRequest();
+        continuation.setPlanId(initial.getPlanId());
+        continuation.setKeyGeneratorType("SNOWFLAKE");
+        WorkflowContextSnapshot actual = planningService.planKeyGenerateStrategy(sessionContext, createQueryFacade(List.of()), continuation);
+        assertThat(actual.getStatus(), is(WorkflowLifecycle.STATUS_PLANNED));
+        assertThat(((ShardingWorkflowRequest) actual.getRequest()).getKeyGeneratorProperties(), is(Map.of("worker-id", "1")));
+    }
+    
     @Test
     void assertPlanTableRuleRejectsDataNodeAndStorageUnitInputs() {
         ShardingWorkflowRequest request = createTableRuleRequest();
@@ -546,6 +631,20 @@ class ShardingWorkflowPlanningServiceTest {
         return result;
     }
     
+    private void assertChoiceClarification(final WorkflowContextSnapshot actual, final String expectedMessage) {
+        assertThat(actual.getStatus(), is(WorkflowLifecycle.STATUS_CLARIFYING));
+        assertThat(actual.getClarifiedIntent().getUnresolvedFields(), is(List.of()));
+        assertThat(actual.getClarifiedIntent().getClarificationMessages(), is(List.of(expectedMessage)));
+        assertThat(actual.getIssues().getFirst().getCode(), is(WorkflowIssueCode.RULE_INPUT_REQUIRED));
+        Map<String, Object> payload = WorkflowPlanPayloadBuilder.build(actual);
+        assertThat(payload.get("missing_required_inputs"), is(List.of("user_clarification")));
+        Map<?, ?> actualQuestion = (Map<?, ?>) ((List<?>) payload.get("clarification_questions")).getFirst();
+        assertThat(actualQuestion.get("field"), is("user_clarification"));
+        assertThat(actualQuestion.get("display_message"), is(expectedMessage));
+        Map<?, ?> actualNextAction = (Map<?, ?>) ((List<?>) payload.get("next_actions")).getFirst();
+        assertThat(actualNextAction.get("required_inputs"), is(List.of("user_clarification")));
+    }
+    
     private void assertTableRuleInputConflict(final ShardingWorkflowRequest request, final List<String> expectedConflictingInputs) {
         WorkflowContextSnapshot actual = planningService.planTableRule(new TestWorkflowSessionContext(), mock(MCPFeatureQueryFacade.class), request);
         assertThat(actual.getStatus(), is(WorkflowLifecycle.STATUS_FAILED));