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

github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/texera.git


The following commit(s) were added to refs/heads/main by this push:
     new 2faa5c5074 refactor(frontend): extract HuggingFace field bindings into 
a helper (#8946)
2faa5c5074 is described below

commit 2faa5c507461a1dfb589cac0699602b4a82479c8
Author: Matthew B. <[email protected]>
AuthorDate: Mon Oct 12 00:41:15 2026 +0000

    refactor(frontend): extract HuggingFace field bindings into a helper (#8946)
    
    ### What changes were proposed in this PR?
    - Extracted the ~185-line HuggingFace-specific field block out of
    `operator-property-edit-frame.component.ts` into a pure helper
    `applyHuggingFaceFieldBindings(field)` in
    `workspace/util/hugging-face-field-bindings.ts`, beside the existing
    `custom-formly-type.ts` that owns the sibling "which widget a field
    renders as" concern.
    - The component now calls the helper under a single `operatorType ===
    "HuggingFace"` guard; the helper reproduces both original guards (`key
    === "task"` hide, then the string-key gate), so the task lists, `hide`
    expressions, required-validators, and `validation.show` flags are
    byte-identical to before.
    - Added `hugging-face-field-bindings.spec.ts` with direct unit tests for
    the hide/validator logic per task (image/audio inputs and their columns,
    prompt column, text-generation-only controls, context, candidate labels,
    sentences), coverage that was impossible while the logic was inline.
    ### Any related issues, documentation, discussions?
    Closes: #8944
    ### How was this PR tested?
    - Run `cd frontend && npx ng test
    --include='**/hugging-face-field-bindings.spec.ts'`, expect 15 tests to
    pass.
    - Run `cd frontend && npx ng test
    --include='**/operator-property-edit-frame.component.spec.ts'`, expect
    the existing suite to pass unchanged (262 pass, 1 pre-existing skip).
    - In the workflow editor, add a HuggingFace operator and change its
    Task: confirm the image upload / input image column appear only for
    image tasks, the audio upload / input audio column only for audio tasks,
    the prompt column hides for image-only and audio tasks,
    systemPrompt/maxNewTokens/temperature show only for text-generation, and
    the required-field validators still block running when a needed input is
    empty.
    ### Was this PR authored or co-authored using generative AI tooling?
    Co-authored with Claude Opus 4.8 in compliance with ASF
---
 .../operator-property-edit-frame.component.ts      | 191 +-----------------
 .../util/hugging-face-field-bindings.spec.ts       | 178 ++++++++++++++++
 .../workspace/util/hugging-face-field-bindings.ts  | 223 +++++++++++++++++++++
 3 files changed, 404 insertions(+), 188 deletions(-)

diff --git 
a/frontend/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.ts
 
b/frontend/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.ts
index 6825eac1a0..f9c2f6f6c5 100644
--- 
a/frontend/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.ts
+++ 
b/frontend/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.ts
@@ -39,6 +39,7 @@ import {
 } from "../../../types/custom-json-schema.interface";
 import { isDefined } from "../../../../common/util/predicate";
 import { customFormlyFieldType, NON_FORM_FIELD_TYPES } from 
"../../../util/custom-formly-type";
+import { applyHuggingFaceFieldBindings } from 
"../../../util/hugging-face-field-bindings";
 import { ExecutionState, OperatorState } from 
"src/app/workspace/types/execute-workflow.interface";
 import { DynamicSchemaService } from 
"../../../service/dynamic-schema/dynamic-schema.service";
 import { WorkflowCompilingService } from 
"../../../service/compile-workflow/workflow-compiling.service";
@@ -960,194 +961,8 @@ export class OperatorPropertyEditFrameComponent 
implements OnInit, OnChanges, On
         mappedField.type = customType;
       }
 
-      if (mappedField.key === "task" && 
this.currentOperatorSchema?.operatorType === "HuggingFace") {
-        mappedField.hide = true;
-      }
-
-      // ── Dynamic field visibility for HuggingFace based on selected task ──
-      if (this.currentOperatorSchema?.operatorType === "HuggingFace" && typeof 
mappedField.key === "string") {
-        const hfKey = mappedField.key;
-        const imageOnlyTasks = ["image-classification", "object-detection", 
"image-segmentation", "image-to-text"];
-        const imageInputTasks = [
-          ...imageOnlyTasks,
-          "visual-question-answering",
-          "document-question-answering",
-          "zero-shot-image-classification",
-          "image-text-to-text",
-          "image-to-image",
-          "image-to-video",
-        ];
-        const audioInputTasks = ["automatic-speech-recognition", 
"audio-classification"];
-        const promptRequiredTasks = [
-          "text-generation",
-          "text-classification",
-          "token-classification",
-          "question-answering",
-          "table-question-answering",
-          "zero-shot-classification",
-          "translation",
-          "summarization",
-          "feature-extraction",
-          "fill-mask",
-          "sentence-similarity",
-          "text-ranking",
-          "visual-question-answering",
-          "document-question-answering",
-          "zero-shot-image-classification",
-        ];
-        const getSelectedTask = (field: FormlyFieldConfig): string | undefined 
=> {
-          const fromForm = field.form?.get("task")?.value ?? 
field.formControl?.parent?.get("task")?.value;
-          if (typeof fromForm === "string" && fromForm.trim().length > 0) {
-            return fromForm;
-          }
-          const fromModel = field.model?.task;
-          if (typeof fromModel === "string" && fromModel.trim().length > 0) {
-            return fromModel;
-          }
-          return undefined;
-        };
-        if (hfKey === "imageInput") {
-          // type ("huggingface-image-upload") is set by customFormlyFieldType 
above
-          mappedField.expressions = {
-            ...mappedField.expressions,
-            hide: (field: FormlyFieldConfig) => {
-              const t = getSelectedTask(field);
-              return t === undefined || !imageInputTasks.includes(t);
-            },
-          };
-          mappedField.validators = {
-            ...mappedField.validators,
-            requiredImageInput: {
-              expression: (_control: AbstractControl, field: 
FormlyFieldConfig) => {
-                const t = getSelectedTask(field);
-                if (t === undefined || !imageInputTasks.includes(t)) {
-                  return true;
-                }
-                const inputImageCol = field.model?.inputImageColumn;
-                if (typeof inputImageCol === "string" && 
inputImageCol.trim().length > 0) {
-                  return true;
-                }
-                const value = field.formControl?.value ?? 
field.model?.imageInput;
-                return typeof value === "string" && value.trim().length > 0;
-              },
-              message: () => "Upload an image or select an Input Image Column 
for this task.",
-            },
-          };
-          mappedField.validation = {
-            ...mappedField.validation,
-            show: true,
-          };
-        }
-        if (hfKey === "audioInput") {
-          // type ("huggingface-audio-upload") is set by customFormlyFieldType 
above
-          mappedField.expressions = {
-            ...mappedField.expressions,
-            hide: (field: FormlyFieldConfig) => {
-              const t = getSelectedTask(field);
-              return t === undefined || !audioInputTasks.includes(t);
-            },
-          };
-          mappedField.validators = {
-            ...mappedField.validators,
-            requiredAudioInput: {
-              expression: (_control: AbstractControl, field: 
FormlyFieldConfig) => {
-                const t = getSelectedTask(field);
-                if (t === undefined || !audioInputTasks.includes(t)) {
-                  return true;
-                }
-                const inputAudioCol = field.model?.inputAudioColumn;
-                if (typeof inputAudioCol === "string" && 
inputAudioCol.trim().length > 0) {
-                  return true;
-                }
-                const value = field.formControl?.value ?? 
field.model?.audioInput;
-                return typeof value === "string" && value.trim().length > 0;
-              },
-              message: () => "Upload audio or select an Input Audio Column for 
this task.",
-            },
-          };
-          mappedField.validation = {
-            ...mappedField.validation,
-            show: true,
-          };
-        }
-        if (hfKey === "inputImageColumn") {
-          mappedField.expressions = {
-            ...mappedField.expressions,
-            hide: (field: FormlyFieldConfig) => {
-              const t = getSelectedTask(field);
-              return t === undefined || !imageInputTasks.includes(t);
-            },
-          };
-        }
-        if (hfKey === "inputAudioColumn") {
-          mappedField.expressions = {
-            ...mappedField.expressions,
-            hide: (field: FormlyFieldConfig) => {
-              const t = getSelectedTask(field);
-              return t === undefined || !audioInputTasks.includes(t);
-            },
-          };
-        }
-        if (hfKey === "promptColumn") {
-          mappedField.expressions = {
-            ...mappedField.expressions,
-            hide: (field: FormlyFieldConfig) => {
-              const t = getSelectedTask(field);
-              return t !== undefined && (imageOnlyTasks.includes(t) || 
audioInputTasks.includes(t));
-            },
-          };
-          mappedField.validators = {
-            ...mappedField.validators,
-            requiredPromptColumn: {
-              expression: (_control: AbstractControl, field: 
FormlyFieldConfig) => {
-                const t = getSelectedTask(field);
-                if (t === undefined || !promptRequiredTasks.includes(t)) {
-                  return true;
-                }
-                const value = field.formControl?.value ?? 
field.model?.promptColumn;
-                return typeof value === "string" && value.trim().length > 0;
-              },
-              message: () => "Select a prompt column for this task.",
-            },
-          };
-          mappedField.validation = {
-            ...mappedField.validation,
-            show: true,
-          };
-        }
-        if (["systemPrompt", "maxNewTokens", "temperature"].includes(hfKey)) {
-          mappedField.expressions = {
-            ...mappedField.expressions,
-            hide: (field: FormlyFieldConfig) => {
-              const t = getSelectedTask(field);
-              return t !== "text-generation";
-            },
-          };
-        }
-        if (hfKey === "contextColumn") {
-          mappedField.expressions = {
-            ...mappedField.expressions,
-            hide: (field: FormlyFieldConfig) => getSelectedTask(field) !== 
"question-answering",
-          };
-        }
-        if (hfKey === "candidateLabels") {
-          mappedField.expressions = {
-            ...mappedField.expressions,
-            hide: (field: FormlyFieldConfig) => {
-              const t = getSelectedTask(field);
-              return t !== "zero-shot-classification" && t !== 
"zero-shot-image-classification";
-            },
-          };
-        }
-        if (hfKey === "sentencesColumn") {
-          mappedField.expressions = {
-            ...mappedField.expressions,
-            hide: (field: FormlyFieldConfig) => {
-              const t = getSelectedTask(field);
-              return t !== "sentence-similarity" && t !== "text-ranking";
-            },
-          };
-        }
+      if (this.currentOperatorSchema?.operatorType === "HuggingFace") {
+        applyHuggingFaceFieldBindings(mappedField);
       }
 
       // Show the required marker for a field the schema requires 
conditionally,
diff --git 
a/frontend/src/app/workspace/util/hugging-face-field-bindings.spec.ts 
b/frontend/src/app/workspace/util/hugging-face-field-bindings.spec.ts
new file mode 100644
index 0000000000..05de5e0266
--- /dev/null
+++ b/frontend/src/app/workspace/util/hugging-face-field-bindings.spec.ts
@@ -0,0 +1,178 @@
+/**
+ * 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.
+ */
+
+import { AbstractControl } from "@angular/forms";
+import { FormlyFieldConfig } from "@ngx-formly/core";
+import { applyHuggingFaceFieldBindings } from "./hugging-face-field-bindings";
+
+// Builds a field with the given key, runs the bindings on it, and returns it 
ready to probe.
+function bind(key: unknown): FormlyFieldConfig {
+  const field: FormlyFieldConfig = { key: key as FormlyFieldConfig["key"] };
+  applyHuggingFaceFieldBindings(field);
+  return field;
+}
+
+// Invokes the field's compiled hide expression against a model carrying the 
selected task.
+function hiddenForTask(field: FormlyFieldConfig, task?: string): boolean {
+  const hide = field.expressions?.hide as (f: FormlyFieldConfig) => boolean;
+  return hide({ model: task === undefined ? {} : { task } } as 
FormlyFieldConfig);
+}
+
+// Invokes a named validator's expression (true = valid) against a given field 
state.
+function validatorPasses(
+  field: FormlyFieldConfig,
+  name: string,
+  state: { task?: string; value?: string; model?: Record<string, unknown> }
+): boolean {
+  const validator = (field.validators as Record<string, { expression: Function 
}>)[name];
+  const probe = {
+    model: { task: state.task, ...(state.model ?? {}) },
+    formControl: { value: state.value },
+  } as unknown as FormlyFieldConfig;
+  return validator.expression({} as AbstractControl, probe);
+}
+
+describe("applyHuggingFaceFieldBindings", () => {
+  it("hides the task field itself", () => {
+    expect(bind("task").hide).toBe(true);
+  });
+
+  it("does nothing for a non-string key", () => {
+    const field = bind(undefined);
+    expect(field.expressions).toBeUndefined();
+    expect(field.validators).toBeUndefined();
+  });
+
+  it("does nothing for a key the operator does not drive", () => {
+    const field = bind("modelId");
+    expect(field.expressions).toBeUndefined();
+    expect(field.validators).toBeUndefined();
+  });
+
+  describe("imageInput", () => {
+    it("shows for an image task and hides otherwise", () => {
+      const field = bind("imageInput");
+      expect(hiddenForTask(field, "image-classification")).toBe(false);
+      expect(hiddenForTask(field, "visual-question-answering")).toBe(false);
+      expect(hiddenForTask(field, "text-generation")).toBe(true);
+      expect(hiddenForTask(field, undefined)).toBe(true);
+    });
+
+    it("requires an upload or an Input Image Column only for an image task", 
() => {
+      const field = bind("imageInput");
+      // non-image task: always valid
+      expect(validatorPasses(field, "requiredImageInput", { task: 
"text-generation" })).toBe(true);
+      // image task, nothing provided: invalid
+      expect(validatorPasses(field, "requiredImageInput", { task: 
"image-classification" })).toBe(false);
+      // satisfied by an uploaded value
+      expect(validatorPasses(field, "requiredImageInput", { task: 
"image-classification", value: "pic.png" })).toBe(
+        true
+      );
+      // satisfied by an Input Image Column instead
+      expect(
+        validatorPasses(field, "requiredImageInput", {
+          task: "image-classification",
+          model: { inputImageColumn: "col" },
+        })
+      ).toBe(true);
+    });
+  });
+
+  describe("audioInput", () => {
+    it("shows for an audio task and hides otherwise", () => {
+      const field = bind("audioInput");
+      expect(hiddenForTask(field, "automatic-speech-recognition")).toBe(false);
+      expect(hiddenForTask(field, "image-classification")).toBe(true);
+    });
+
+    it("requires an upload or an Input Audio Column only for an audio task", 
() => {
+      const field = bind("audioInput");
+      expect(validatorPasses(field, "requiredAudioInput", { task: 
"text-generation" })).toBe(true);
+      expect(validatorPasses(field, "requiredAudioInput", { task: 
"audio-classification" })).toBe(false);
+      expect(validatorPasses(field, "requiredAudioInput", { task: 
"audio-classification", value: "a.wav" })).toBe(true);
+      expect(
+        validatorPasses(field, "requiredAudioInput", {
+          task: "audio-classification",
+          model: { inputAudioColumn: "col" },
+        })
+      ).toBe(true);
+    });
+  });
+
+  it("hides inputImageColumn / inputAudioColumn by their task families", () => 
{
+    expect(hiddenForTask(bind("inputImageColumn"), 
"object-detection")).toBe(false);
+    expect(hiddenForTask(bind("inputImageColumn"), 
"audio-classification")).toBe(true);
+    expect(hiddenForTask(bind("inputAudioColumn"), 
"audio-classification")).toBe(false);
+    expect(hiddenForTask(bind("inputAudioColumn"), 
"object-detection")).toBe(true);
+  });
+
+  describe("promptColumn", () => {
+    it("hides only for image-only and audio tasks", () => {
+      const field = bind("promptColumn");
+      expect(hiddenForTask(field, "image-classification")).toBe(true);
+      expect(hiddenForTask(field, "audio-classification")).toBe(true);
+      expect(hiddenForTask(field, "text-generation")).toBe(false);
+      expect(hiddenForTask(field, "visual-question-answering")).toBe(false);
+      expect(hiddenForTask(field, undefined)).toBe(false);
+    });
+
+    it("requires a value only for a prompt-required task", () => {
+      const field = bind("promptColumn");
+      expect(validatorPasses(field, "requiredPromptColumn", { task: 
"image-classification" })).toBe(true);
+      expect(validatorPasses(field, "requiredPromptColumn", { task: 
"summarization" })).toBe(false);
+      expect(validatorPasses(field, "requiredPromptColumn", { task: 
"summarization", value: "col" })).toBe(true);
+    });
+  });
+
+  it("shows text-generation-only controls solely for text-generation", () => {
+    for (const key of ["systemPrompt", "maxNewTokens", "temperature"]) {
+      const field = bind(key);
+      expect(hiddenForTask(field, "text-generation")).toBe(false);
+      expect(hiddenForTask(field, "summarization")).toBe(true);
+    }
+  });
+
+  it("shows contextColumn only for question-answering", () => {
+    const field = bind("contextColumn");
+    expect(hiddenForTask(field, "question-answering")).toBe(false);
+    expect(hiddenForTask(field, "text-generation")).toBe(true);
+  });
+
+  it("shows candidateLabels only for the zero-shot tasks", () => {
+    const field = bind("candidateLabels");
+    expect(hiddenForTask(field, "zero-shot-classification")).toBe(false);
+    expect(hiddenForTask(field, "zero-shot-image-classification")).toBe(false);
+    expect(hiddenForTask(field, "text-classification")).toBe(true);
+  });
+
+  it("shows sentencesColumn only for similarity and ranking tasks", () => {
+    const field = bind("sentencesColumn");
+    expect(hiddenForTask(field, "sentence-similarity")).toBe(false);
+    expect(hiddenForTask(field, "text-ranking")).toBe(false);
+    expect(hiddenForTask(field, "translation")).toBe(true);
+  });
+
+  it("preserves expressions already present on the field", () => {
+    const existing = () => false;
+    const field: FormlyFieldConfig = { key: "contextColumn", expressions: { 
"props.required": existing } };
+    applyHuggingFaceFieldBindings(field);
+    expect(field.expressions?.["props.required"]).toBe(existing);
+    expect(typeof field.expressions?.hide).toBe("function");
+  });
+});
diff --git a/frontend/src/app/workspace/util/hugging-face-field-bindings.ts 
b/frontend/src/app/workspace/util/hugging-face-field-bindings.ts
new file mode 100644
index 0000000000..a3f31e5629
--- /dev/null
+++ b/frontend/src/app/workspace/util/hugging-face-field-bindings.ts
@@ -0,0 +1,223 @@
+/**
+ * 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.
+ */
+
+import { AbstractControl } from "@angular/forms";
+import { FormlyFieldConfig } from "@ngx-formly/core";
+
+const imageOnlyTasks = ["image-classification", "object-detection", 
"image-segmentation", "image-to-text"];
+const imageInputTasks = [
+  ...imageOnlyTasks,
+  "visual-question-answering",
+  "document-question-answering",
+  "zero-shot-image-classification",
+  "image-text-to-text",
+  "image-to-image",
+  "image-to-video",
+];
+const audioInputTasks = ["automatic-speech-recognition", 
"audio-classification"];
+const promptRequiredTasks = [
+  "text-generation",
+  "text-classification",
+  "token-classification",
+  "question-answering",
+  "table-question-answering",
+  "zero-shot-classification",
+  "translation",
+  "summarization",
+  "feature-extraction",
+  "fill-mask",
+  "sentence-similarity",
+  "text-ranking",
+  "visual-question-answering",
+  "document-question-answering",
+  "zero-shot-image-classification",
+];
+
+const getSelectedTask = (field: FormlyFieldConfig): string | undefined => {
+  const fromForm = field.form?.get("task")?.value ?? 
field.formControl?.parent?.get("task")?.value;
+  if (typeof fromForm === "string" && fromForm.trim().length > 0) {
+    return fromForm;
+  }
+  const fromModel = field.model?.task;
+  if (typeof fromModel === "string" && fromModel.trim().length > 0) {
+    return fromModel;
+  }
+  return undefined;
+};
+
+/**
+ * Applies the HuggingFace operator's task-driven field behaviour to a single 
formly field: the
+ * `task` field is hidden, and each input field (image/audio uploads and their 
column pickers, the
+ * prompt column, text-generation controls, context, candidate labels, 
sentences) gets the hide
+ * expression and required-validator that the currently selected task calls 
for. A no-op for any
+ * field whose key the HuggingFace operator does not drive. The widget a field 
renders as is decided
+ * separately in {@link customFormlyFieldType}.
+ */
+export function applyHuggingFaceFieldBindings(mappedField: FormlyFieldConfig): 
void {
+  if (mappedField.key === "task") {
+    mappedField.hide = true;
+  }
+
+  if (typeof mappedField.key !== "string") {
+    return;
+  }
+  const hfKey = mappedField.key;
+  if (hfKey === "imageInput") {
+    // type ("huggingface-image-upload") is set by customFormlyFieldType
+    mappedField.expressions = {
+      ...mappedField.expressions,
+      hide: (field: FormlyFieldConfig) => {
+        const t = getSelectedTask(field);
+        return t === undefined || !imageInputTasks.includes(t);
+      },
+    };
+    mappedField.validators = {
+      ...mappedField.validators,
+      requiredImageInput: {
+        expression: (_control: AbstractControl, field: FormlyFieldConfig) => {
+          const t = getSelectedTask(field);
+          if (t === undefined || !imageInputTasks.includes(t)) {
+            return true;
+          }
+          const inputImageCol = field.model?.inputImageColumn;
+          if (typeof inputImageCol === "string" && inputImageCol.trim().length 
> 0) {
+            return true;
+          }
+          const value = field.formControl?.value ?? field.model?.imageInput;
+          return typeof value === "string" && value.trim().length > 0;
+        },
+        message: () => "Upload an image or select an Input Image Column for 
this task.",
+      },
+    };
+    mappedField.validation = {
+      ...mappedField.validation,
+      show: true,
+    };
+  }
+  if (hfKey === "audioInput") {
+    // type ("huggingface-audio-upload") is set by customFormlyFieldType
+    mappedField.expressions = {
+      ...mappedField.expressions,
+      hide: (field: FormlyFieldConfig) => {
+        const t = getSelectedTask(field);
+        return t === undefined || !audioInputTasks.includes(t);
+      },
+    };
+    mappedField.validators = {
+      ...mappedField.validators,
+      requiredAudioInput: {
+        expression: (_control: AbstractControl, field: FormlyFieldConfig) => {
+          const t = getSelectedTask(field);
+          if (t === undefined || !audioInputTasks.includes(t)) {
+            return true;
+          }
+          const inputAudioCol = field.model?.inputAudioColumn;
+          if (typeof inputAudioCol === "string" && inputAudioCol.trim().length 
> 0) {
+            return true;
+          }
+          const value = field.formControl?.value ?? field.model?.audioInput;
+          return typeof value === "string" && value.trim().length > 0;
+        },
+        message: () => "Upload audio or select an Input Audio Column for this 
task.",
+      },
+    };
+    mappedField.validation = {
+      ...mappedField.validation,
+      show: true,
+    };
+  }
+  if (hfKey === "inputImageColumn") {
+    mappedField.expressions = {
+      ...mappedField.expressions,
+      hide: (field: FormlyFieldConfig) => {
+        const t = getSelectedTask(field);
+        return t === undefined || !imageInputTasks.includes(t);
+      },
+    };
+  }
+  if (hfKey === "inputAudioColumn") {
+    mappedField.expressions = {
+      ...mappedField.expressions,
+      hide: (field: FormlyFieldConfig) => {
+        const t = getSelectedTask(field);
+        return t === undefined || !audioInputTasks.includes(t);
+      },
+    };
+  }
+  if (hfKey === "promptColumn") {
+    mappedField.expressions = {
+      ...mappedField.expressions,
+      hide: (field: FormlyFieldConfig) => {
+        const t = getSelectedTask(field);
+        return t !== undefined && (imageOnlyTasks.includes(t) || 
audioInputTasks.includes(t));
+      },
+    };
+    mappedField.validators = {
+      ...mappedField.validators,
+      requiredPromptColumn: {
+        expression: (_control: AbstractControl, field: FormlyFieldConfig) => {
+          const t = getSelectedTask(field);
+          if (t === undefined || !promptRequiredTasks.includes(t)) {
+            return true;
+          }
+          const value = field.formControl?.value ?? field.model?.promptColumn;
+          return typeof value === "string" && value.trim().length > 0;
+        },
+        message: () => "Select a prompt column for this task.",
+      },
+    };
+    mappedField.validation = {
+      ...mappedField.validation,
+      show: true,
+    };
+  }
+  if (["systemPrompt", "maxNewTokens", "temperature"].includes(hfKey)) {
+    mappedField.expressions = {
+      ...mappedField.expressions,
+      hide: (field: FormlyFieldConfig) => {
+        const t = getSelectedTask(field);
+        return t !== "text-generation";
+      },
+    };
+  }
+  if (hfKey === "contextColumn") {
+    mappedField.expressions = {
+      ...mappedField.expressions,
+      hide: (field: FormlyFieldConfig) => getSelectedTask(field) !== 
"question-answering",
+    };
+  }
+  if (hfKey === "candidateLabels") {
+    mappedField.expressions = {
+      ...mappedField.expressions,
+      hide: (field: FormlyFieldConfig) => {
+        const t = getSelectedTask(field);
+        return t !== "zero-shot-classification" && t !== 
"zero-shot-image-classification";
+      },
+    };
+  }
+  if (hfKey === "sentencesColumn") {
+    mappedField.expressions = {
+      ...mappedField.expressions,
+      hide: (field: FormlyFieldConfig) => {
+        const t = getSelectedTask(field);
+        return t !== "sentence-similarity" && t !== "text-ranking";
+      },
+    };
+  }
+}

Reply via email to