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]