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));