Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,13 @@ object HuggingFaceCodegenBase {
| "Candidate Labels are required for zero-shot-classification. "
| "Provide a comma-separated list of labels."
| )
| if task == "zero-shot-image-classification":
| labels = [l.strip() for l in str(self.CANDIDATE_LABELS).split(",") if l.strip()]
| if len(labels) < 2:
| raise ValueError(
| "zero-shot-image-classification requires at least 2 Candidate Labels. "
| "Provide a comma-separated list of labels."
| )
| if task == "question-answering":
| ctx_col = self.CONTEXT_COLUMN
| if not (ctx_col and ctx_col in table.columns):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,17 +90,10 @@ object ImageTaskCodegen extends TaskCodegen {
| use_raw_binary_body = True
| raw_binary_headers = image_headers
| elif task == "zero-shot-image-classification":
| # Prefer the dedicated candidateLabels property; fall back to
| # the prompt column for backward compatibility.
| label_source = (self.CANDIDATE_LABELS or "").strip() if self.CANDIDATE_LABELS else ""
| if not label_source and prompt_value:
| label_source = prompt_value
| labels = [s.strip() for s in label_source.split(",") if s.strip()]
| if len(labels) < 2:
| raise ValueError(
| "zero-shot-image-classification requires at least 2 candidate "
| "labels: provide a comma-separated list in the Candidate Labels field."
| )
| # Labels come from the Candidate Labels property; the >= 2
| # check runs pre-loop in HuggingFaceCodegenBase (fail-fast),
| # so no per-row validation is needed here.
| labels = [s.strip() for s in str(self.CANDIDATE_LABELS).split(",") if s.strip()]
| payload = {
| "inputs": self._image_input_as_base64(current_image_bytes),
| "parameters": {"candidate_labels": labels},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -411,24 +411,19 @@ class HuggingFaceInferenceOpDescSpec extends AnyFlatSpec with Matchers {
}

it should
"fail fast at runtime when zero-shot-image-classification has fewer than 2 candidate labels" in {
// Without a dedicated candidateLabels field (lands in PR 5), zero-shot
// reuses prompt_value as a comma-
// separated list. Two failure modes the bare list comprehension hides
// are both caught by the >= 2 check:
// 1. Empty prompt column → labels = [] → HF API rejects
// candidate_labels: [] with an opaque 400.
// 2. Missing prompt column → upstream falls back to "What is shown in
// this image?" (no comma) → labels = ["What is shown in this image?"],
// a single nonsense label that returns a useless 1.0 score.
// Zero-shot classification needs >= 2 candidate labels to be meaningful,
// so the fix raises ValueError before the request goes out and the user
// sees a clear configuration error instead of a generic HTTP failure or
// misleading 100%-confidence garbage.
"validate zero-shot-image-classification candidate labels before the row loop" in {
// #7199 Part B: the >= 2 candidate-labels check is a config validation, so it
// runs in the pre-loop validation block (fail-fast with a clear ValueError),
// consistent with the other config checks — instead of being raised inside the
// per-row payload build, where an uncaught ValueError crashed the operator.
// Labels come from the Candidate Labels property; the old prompt-column
// fallback is dropped.
val code = makeDesc(task = "zero-shot-image-classification").generatePythonCode()
code should include("if len(labels) < 2:")
code should include("raise ValueError(")
code should include("at least 2 candidate")
code should include("requires at least 2 Candidate Labels")
// The per-row prompt-column fallback is gone.
code should not include ("label_source")
}

it should
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,14 @@ class ImageTaskCodegenSpec extends AnyFlatSpec with Matchers {
out should include(""""question": prompt_value""")
}

it should "validate that zero-shot classification supplies at least two candidate labels" in {
it should "build the candidate-labels payload for zero-shot image classification (validation is pre-loop)" in {
val out = ImageTaskCodegen.payloadPython(makeCtx())
out should include("""elif task == "zero-shot-image-classification":""")
out should include("if len(labels) < 2:")
out should include("raise ValueError")
out should include("candidate_labels")
// #7199 Part B: the >= 2 labels check moved to the pre-loop validation in
// HuggingFaceCodegenBase; the per-task payload build no longer raises.
out should not include ("raise ValueError")
out should not include ("if len(labels) < 2:")
}

"ImageTaskCodegen.parsePython" should "extract chat-style content for image-text-to-text" in {
Expand Down
Loading