Skip to content

fix(core_text): keep one response per row when input cells are empty - #536

Merged
SunnyHaze merged 1 commit into
OpenDCAI:mainfrom
JasonBuildAI:fix/core-text-empty-row-alignment
Oct 12, 2026
Merged

SunnyHaze merged 1 commit into
OpenDCAI:mainfrom
JasonBuildAI:fix/core-text-empty-row-alignment

Conversation

@JasonBuildAI

Copy link
Copy Markdown
Contributor

What this fixes

Four core_text operators build their prompt batch by skipping rows whose
input_key cell is falsy, and then write the model's responses back as a
whole column:

dataframe[output_key] = generated_outputs

Because the skipped rows never made it into the batch, that list is shorter
than the frame, so any empty cell takes the run down with:

ValueError: Length of values does not match length of index

Affected: PromptedGenerator, PromptedEvaluator, PromptedRefiner and
RetrievalGenerator.

This is easy to hit in practice — it's normal for a frame to arrive with a few
blank cells after a source read or an upstream filter — and the worse variant
is silent misalignment rather than a crash: if the lengths happen to work out,
row N ends up with row N+1's answer. Either way the if raw_content guard
already anticipates empty rows, so I don't think the crash on the write side
was the intent.

How it's fixed

Each operator now remembers which row positions actually produced a prompt and
writes responses back at those positions, so the frame keeps exactly one value
per row. For the default value of a skipped row I kept each operator's existing
semantics rather than inventing a new rule:

  • PromptedGenerator / RetrievalGenerator — empty string in the output column.
  • PromptedRefiner — keeps its original text, since it overwrites input_key in
    place and dropping the value would be data loss.
  • PromptedEvaluator — reuses the 0 that _parse_scores already returns for
    an unparseable response, so "no score produced" and "score parsed as
    unparseable" stay the same thing.

PromptedFilter calls PromptedEvaluator.eval() on a pre-filtered frame, so it
is unaffected; there is a test for it to keep that honest.

A batch that comes back with a response count that doesn't match the number of
prompts now raises instead of shifting rows. I followed #525 here, which does
the same thing for Text2QAGenerator — mismatched counts are a serving problem,
and silently reallocating them isn't a useful answer.

I also corrected three log lines that said Running PromptGenerator... inside
the evaluator and the refiner, and updated PromptedRefiner.get_desc, which
used to document the length-mismatch error as expected behaviour.

Testing

test/cpu_only/test_core_text_empty_row_alignment.py covers all four operators
against a stub serving and an in-memory storage, parametrised over which row is
empty (first / middle / last / none), and asserts the untouched rows keep their
identity — the fixture deliberately uses non-contiguous index labels [3, 7, 11]
so a positional/label mix-up can't pass. pytestmark = pytest.mark.cpu, no API
key or GPU needed.

$ pytest test/cpu_only -m "cpu or api"

Running that on upstream main and on this branch gives the same set of
failures (word2number and datasketch are missing from my local venv); the
only delta is the 14 new passes. Ruff over the four changed files reports the
same 10 pre-existing F401/F841 findings before and after.

One caveat on the new tests: RetrievalGenerator imports dataflow.serving at
module level, which needs the Google Cloud extras, so those four cases are behind
pytest.importorskip. They run in the CI matrix. I verified them locally by
stubbing that import, and the alignment holds for all four empty-row positions.
Making that import lazy seemed like a separate change, so I left it alone here.

Notes

No existing issue for this, so no Fixes #. Happy to open one first if you'd
prefer it tracked. The change is confined to core_text and doesn't touch any
operator signature, so test_register.py's conventions are unchanged.

PromptedGenerator, PromptedEvaluator, PromptedRefiner and RetrievalGenerator
skip falsy rows when building the prompt batch, then assign the shorter
response list back as a full column, so any empty cell crashes the run with
"ValueError: Length of values does not match length of index".

Write responses back at the positions they were generated from: skipped rows
keep their original text in PromptedRefiner and default to an empty value in
the others, where _parse_scores' failure value 0 is reused for the score
column so PromptedFilter is unaffected. A batch whose response count does not
match the prompt count now raises instead of silently shifting rows.

@SunnyHaze SunnyHaze left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, sincerely thanks!

@SunnyHaze
SunnyHaze merged commit 4b2a0b8 into OpenDCAI:main Oct 12, 2026
6 checks passed
@yinyueyuan

yinyueyuan commented Oct 12, 2026 via email

Copy link
Copy Markdown

rikkurea-boop added a commit to rikkurea-boop/DataFlow that referenced this pull request Oct 13, 2026
Bring OpenDCAI#536, OpenDCAI#553 and OpenDCAI#551 upstream changes into the existing PR branch
without rebasing away the previously reviewed Text2Model commits.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants