From d99461951674b58d7c1bf56790b3f0fe5c07a376 Mon Sep 17 00:00:00 2001 From: kary zheng Date: Wed, 7 Oct 2026 20:07:59 -0700 Subject: [PATCH 1/2] feat(workflow-operator): name the missing ground-truth column in Sklearn Prediction's error A ground-truth name that is not in the data input failed compilation with None.get. Say which column is missing instead, in the wording MachineLearningScorer already uses. Closes #8864 Co-Authored-By: Claude Opus 5.5 --- .../amber/operator/sklearn/SklearnPredictionOpDesc.scala | 7 +++++-- .../operator/sklearn/SklearnPredictionOpDescSpec.scala | 5 +++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/sklearn/SklearnPredictionOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/sklearn/SklearnPredictionOpDesc.scala index c1d022974ce..5994a691c05 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/sklearn/SklearnPredictionOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/sklearn/SklearnPredictionOpDesc.scala @@ -92,8 +92,11 @@ class SklearnPredictionOpDesc extends PythonOperatorDescriptor with StandaloneCo var resultType = AttributeType.STRING val inputSchema = inputSchemas(operatorInfo.inputPorts(1).id) if (groundTruthAttribute != "") { - resultType = - inputSchema.attributes.find(attr => attr.getName == groundTruthAttribute).get.getType + if (!inputSchema.containsAttribute(groundTruthAttribute)) + throw new RuntimeException( + s"Ground Truth column '$groundTruthAttribute' is not in the input table" + ) + resultType = inputSchema.getAttribute(groundTruthAttribute).getType } Map( operatorInfo.outputPorts.head.id -> inputSchema diff --git a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/sklearn/SklearnPredictionOpDescSpec.scala b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/sklearn/SklearnPredictionOpDescSpec.scala index b640e4888df..d8c2ea5498d 100644 --- a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/sklearn/SklearnPredictionOpDescSpec.scala +++ b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/sklearn/SklearnPredictionOpDescSpec.scala @@ -71,14 +71,15 @@ class SklearnPredictionOpDescSpec extends AnyFlatSpec with Matchers { .getType shouldBe AttributeType.INTEGER } - it should "throw when the configured ground-truth attribute is absent from the input schema" in { + it should "name the configured ground-truth attribute when it is absent from the input schema" in { val d = new SklearnPredictionOpDesc d.resultAttribute = "prediction" d.groundTruthAttribute = "missing" val data = Schema().add("feature", AttributeType.STRING) - intercept[NoSuchElementException] { + val e = intercept[RuntimeException] { d.getOutputSchemas(Map(PortIdentity(1) -> data)) } + e.getMessage shouldBe "Ground Truth column 'missing' is not in the input table" } "SklearnPredictionOpDesc.generatePythonCode" should "emit the model-applying tuple operator" in { From 0cf15b14fb0a49c20b869d76741d7721b17d0536 Mon Sep 17 00:00:00 2001 From: kary zheng Date: Fri, 9 Oct 2026 09:21:59 -0700 Subject: [PATCH 2/2] feat(workflow-operator): keep the exact-case ground-truth lookup in Sklearn Prediction Schema lookups ignore case, but the generated drop() does not, so a case-only match passed validation and then raised KeyError at runtime. Co-Authored-By: Claude Opus 5.5 --- .../operator/sklearn/SklearnPredictionOpDesc.scala | 12 ++++++++---- .../sklearn/SklearnPredictionOpDescSpec.scala | 13 +++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/sklearn/SklearnPredictionOpDesc.scala b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/sklearn/SklearnPredictionOpDesc.scala index 5994a691c05..206bed80792 100644 --- a/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/sklearn/SklearnPredictionOpDesc.scala +++ b/common/workflow-operator/src/main/scala/org/apache/texera/amber/operator/sklearn/SklearnPredictionOpDesc.scala @@ -92,11 +92,15 @@ class SklearnPredictionOpDesc extends PythonOperatorDescriptor with StandaloneCo var resultType = AttributeType.STRING val inputSchema = inputSchemas(operatorInfo.inputPorts(1).id) if (groundTruthAttribute != "") { - if (!inputSchema.containsAttribute(groundTruthAttribute)) - throw new RuntimeException( - s"Ground Truth column '$groundTruthAttribute' is not in the input table" + // Exact case: Schema lookups ignore case, but the generated drop() does not. + resultType = inputSchema.attributes + .find(attr => attr.getName == groundTruthAttribute) + .getOrElse( + throw new RuntimeException( + s"Ground Truth column '$groundTruthAttribute' is not in the input table" + ) ) - resultType = inputSchema.getAttribute(groundTruthAttribute).getType + .getType } Map( operatorInfo.outputPorts.head.id -> inputSchema diff --git a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/sklearn/SklearnPredictionOpDescSpec.scala b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/sklearn/SklearnPredictionOpDescSpec.scala index d8c2ea5498d..a376901c436 100644 --- a/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/sklearn/SklearnPredictionOpDescSpec.scala +++ b/common/workflow-operator/src/test/scala/org/apache/texera/amber/operator/sklearn/SklearnPredictionOpDescSpec.scala @@ -82,6 +82,19 @@ class SklearnPredictionOpDescSpec extends AnyFlatSpec with Matchers { e.getMessage shouldBe "Ground Truth column 'missing' is not in the input table" } + it should "reject a ground-truth attribute that matches an input column only by case" in { + val d = new SklearnPredictionOpDesc + d.resultAttribute = "prediction" + d.groundTruthAttribute = "Label" + val data = Schema() + .add("feature", AttributeType.STRING) + .add("label", AttributeType.INTEGER) + val e = intercept[RuntimeException] { + d.getOutputSchemas(Map(PortIdentity(1) -> data)) + } + e.getMessage shouldBe "Ground Truth column 'Label' is not in the input table" + } + "SklearnPredictionOpDesc.generatePythonCode" should "emit the model-applying tuple operator" in { val d = new SklearnPredictionOpDesc d.model = "model"