Skip to content

TASK-36: widen mixed int/float list elements on the native engine - #19

Closed
claude-agent-ahrzb[bot] wants to merge 1 commit into
masterfrom
task-36-list-widening
Closed

TASK-36: widen mixed int/float list elements on the native engine#19
claude-agent-ahrzb[bot] wants to merge 1 commit into
masterfrom
task-36-list-widening

Conversation

@claude-agent-ahrzb

Copy link
Copy Markdown

Fixes a silent cross-engine value divergence on the default serving engine.

What a user hits

SELECT [bedrooms, price_per_sqft] AS dims FROM __THIS__   -- int, float
transform() over the DataFrame  ->  [3.0, 12.5]     (DataFusion oracle + codegen)
infer() on a single row         ->  [3,   12.5]     (native, un-widened)

No error, no warning. The int element silently stays int on the serving path and becomes float on the training path. Anything dtype-sensitive downstream — a numpy cast, a model expecting float64, a feature vector checked against a stored schema — then sees different data in training vs production, and nothing points at the list literal.

Root cause (measured)

native's unify_list_element_types (src/types.rs) was exact-FieldType-equality only, so it refused to widen mixed numeric elements and fell back to Base::Other. DataFusion (oracle) and codegen both widen to list<double>.

The fix — AC#1: mirror the COALESCE/CASE widening the oracle already uses

// before: exact FieldType equality, or Base::Other
if iter.all(|t| t == first) { first.clone() } else { Base::Other, nullable: true }

// after: the same common_base widening as COALESCE/CASE
FieldType {
    base: common_base(item_types),          // int + float -> float
    nullable: item_types.iter().any(|t| t.nullable),
}

common_base is the existing function COALESCE/NULLIF and CASE already use, and it already matches the oracle. So this inherits the oracle-verified promotion rule rather than defining a new one — no promotion lattice invented. The output model types the list as list[float] and pydantic coerces the runtime int to float, exactly as codegen does.

AC#4 — root-cause sweep

unify_list_element_types was the only exact-equality-only unification site where DataFusion widens. Verified the other type-combination sites are already correct:

site behavior verdict
common_base (COALESCE/CASE/NULLIF) int+float → float already widens
binary_op_type (arithmetic) int op float → float already widens
compatible() (struct/list matching) (Int, Float) => true already accepts widening

No other silent-divergence site of this class.

Verification

  • AC#2: SELECT [x, y] with int+float now yields [1.0, 2.5] on native, matching the oracle.
  • AC#3: the xfail_on_native marker on test_list_construct_mixed_numeric_widens is removed in this same commit; it now passes on both engines.
  • Mutation-checked: reverting the widening (rebuild) makes the now-unmarked test fail on [native] while [codegen] still passes — so it genuinely catches the regression.
  • Suite: 573 passed, 11 xfailed (was 572 / 12 — the flipped test now passes on both engines). Diff scoped to src/types.rs + tests/test_diff_types.py.

🤖 Generated with Claude Code

… — TASK-36

unify_list_element_types was exact-FieldType-equality only, so a list literal
mixing an int column with a float column ([bedrooms, price_per_sqft]) left the
int element un-widened on the native serving path -- [3, 12.5] -- while the
DataFusion oracle and codegen both produced list<double> [3.0, 12.5]. No error,
no warning: a silent cross-engine value divergence on the default serving
engine, exactly the class the model consumes positionally.

Now it uses common_base -- the same COALESCE/CASE widening the oracle uses
(int+float -> float) -- so both engines emit list<double>. The output model
types the list as list[float] and pydantic coerces the runtime int to float.

AC#4 sweep: unify_list_element_types was the only exact-equality-only
unification site where DataFusion widens. common_base already widens
(COALESCE/CASE), binary_op_type widens int+float -> float, and compatible()
already accepts Int -> Float. No other silent-divergence site of this class.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@claude-agent-ahrzb

Copy link
Copy Markdown
Author

Closing as obsolete: the SQLTransform line this targets has been reset (#54). The DataFusion native engine, the codegen backend, and the batch transform() path are removed; what remains is the confit-served fit-and-serve path.

Nothing here is lost — the branch and its commits stay reachable if this work is ever revived.

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.

1 participant