Ma77Ball commented on code in PR #5568:
URL: https://github.com/apache/texera/pull/5568#discussion_r3384858733


##########
frontend/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.ts:
##########
@@ -541,6 +815,203 @@ export class OperatorPropertyEditFrameComponent 
implements OnInit, OnChanges, On
         mappedField.type = "inputautocomplete";
       }
 
+      if (mappedField.key === "huggingFaceModel") {
+        mappedField.type = "huggingface";
+      }
+
+      if (mappedField.key === "modelId" && 
this.currentOperatorSchema?.operatorType === "HuggingFace") {
+        mappedField.type = "huggingface";
+      }
+
+      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",
+        ];
+        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") {
+          mappedField.type = "huggingface-image-upload";
+          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") {
+          mappedField.type = "huggingface-audio-upload";
+          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" && t !== undefined;
+            },
+          };
+        }
+        if (hfKey === "contextColumn") {
+          mappedField.expressions = {
+            ...mappedField.expressions,
+            hide: (field: FormlyFieldConfig) => getSelectedTask(field) !== 
"question-answering",

Review Comment:
   contextColumn shows only for the exact string `question-answering`, so 
`table-question-answering` has no context field at all: candidateLabels, 
sentencesColumn, and contextColumn are all hidden for it, leaving only 
promptColumn, so the user cannot supply the table. If table QA (or 
document/visual QA) is meant to use this field, widen the condition. Manual 
change, confirm against the backend which QA tasks consume contextColumn first:
   
   ```
   hide: (field) => {
     const t = getSelectedTask(field);
     return t !== "question-answering" && t !== "table-question-answering";
   }
   ```



##########
frontend/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.ts:
##########
@@ -541,6 +815,203 @@ export class OperatorPropertyEditFrameComponent 
implements OnInit, OnChanges, On
         mappedField.type = "inputautocomplete";
       }
 
+      if (mappedField.key === "huggingFaceModel") {
+        mappedField.type = "huggingface";
+      }
+
+      if (mappedField.key === "modelId" && 
this.currentOperatorSchema?.operatorType === "HuggingFace") {
+        mappedField.type = "huggingface";
+      }
+
+      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",
+        ];
+        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") {
+          mappedField.type = "huggingface-image-upload";
+          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") {
+          mappedField.type = "huggingface-audio-upload";
+          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" && t !== undefined;

Review Comment:
   Inverted fallback: these text-generation-only params show when no task is 
selected. With `t !== "text-generation" && t !== undefined`, an undefined task 
yields `true && false` = `false`, so the field is NOT hidden. Every other 
HuggingFace field hides on `t === undefined`; here 
systemPrompt/maxNewTokens/temperature appear before any task is chosen. Drop 
the undefined clause so they hide unless the task is exactly text-generation:
   
   ```suggestion
                 return t !== "text-generation";
   ```



##########
frontend/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.ts:
##########
@@ -541,6 +815,203 @@ export class OperatorPropertyEditFrameComponent 
implements OnInit, OnChanges, On
         mappedField.type = "inputautocomplete";
       }
 
+      if (mappedField.key === "huggingFaceModel") {
+        mappedField.type = "huggingface";
+      }
+
+      if (mappedField.key === "modelId" && 
this.currentOperatorSchema?.operatorType === "HuggingFace") {
+        mappedField.type = "huggingface";
+      }
+
+      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",

Review Comment:
   Drift note (not blocking): `image-text-to-text` is listed as an image-input 
task but has no entry in `huggingFaceTaskPreviewSamples`, so selecting it falls 
through to the generic text fallback even though it is an image task. More 
broadly, these task lists plus the preview map and the selector component 
duplicate the backend task set and have already diverged. Consider lifting the 
task set and per-task media-kind metadata into one shared constant so the 
lists, preview map, and validators stay in sync.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to