Skip to content

fix(api): repair enrich() data path + stop sending OpenAI-only kwarg to Anthropic - #188

Merged
ptimizeroracle merged 2 commits into
mainfrom
fix/1.11.1-enrich-anthropic
Jul 31, 2026
Merged

ptimizeroracle merged 2 commits into
mainfrom
fix/1.11.1-enrich-anthropic

Conversation

@ptimizeroracle

@ptimizeroracle ptimizeroracle commented Jul 31, 2026 •

Copy link
Copy Markdown
Owner

Three bugs shipped in 1.11.0, all found by installing the published wheel into a clean venv and calling the real APIs. None were visible to CI.

1. enrich(schema=...) was completely broken

ondine.enrich(df, prompt="Category of: {p}", output_columns=["category"], schema=MySchema)
# ValueError: Either dataframe or source_path must be provided

Failed for both DataFrame and CSV-path input — i.e. the entire structured-output half of the flagship new API.

PipelineBuilder.from_specifications() copies the five spec objects but never carried the data, and QuickPipeline attaches an in-memory frame even when given a path. So the structured-output rebuild always dropped it. from_specifications() now takes an explicit dataframe= argument.

Why CI missed it: test_enrich.py mocks Pipeline.execute, so the rebuilt pipeline never reached the loader stage. The tests asserted configuration and never exercised behaviour.

2. enrich(polars_df, ...) crashed on a default install

ModuleNotFoundError: No module named 'pyarrow'

polars is a core dependency and polars support is documented, but DataFrame.to_pandas() goes through Arrow and pyarrow ships only in the parquet/all extras. Both conversion directions now fall back to a column-wise copy needing no extra dependency.

Why CI missed it: uv sync --all-extras installs pyarrow in the dev venv, masking the missing declaration.

3. Anthropic structured output broken

AsyncMessages.create() got an unexpected keyword argument 'parallel_tool_calls'

This was the cause of the 3 failing integration tests. instructor ≥1.15 normalises from_anthropic(mode=ANTHROPIC_TOOLS) down to Mode.TOOLS, so the existing mode check matched the native Anthropic client and injected an OpenAI-only parameter its SDK rejects outright. The guard now excludes the direct-Anthropic path.

Introduced by #182 raising instructor's floor from >=1.0.0 to >=1.15.4.

Verification

  • 1066 unit tests pass, mypy clean (126 files), ruff clean
  • Regression tests added for (1) and (2), each verified to fail without the fix and pass with it
  • Polars round-trip confirmed live against a venv with no pyarrow installed

Honest caveats: (3) is proven by root-cause analysis and unit tests, not a live call — the integration model claude-3-haiku-20240307 is retired (404). enrich(schema=) could not be confirmed end-to-end either, because no free model I had access to supports structured output; the data-path fix is proven structurally (rebuilt.dataframe is not None).

Not fixed here — see #187

Structured output has no mode fallback (the working mode differs per model: ling-3.0-flash needs TOOLS, DeepSeek rejects both TOOLS and JSON_SCHEMA), deepseek is missing from PROVIDER_CAPABILITIES entirely, and a run where every row fails is silently returned as a DataFrame of None. That last part is the same failure class as #166 and is behaviour-changing, so it doesn't belong in a patch.

Targets 1.11.1.

Summary by CodeRabbit

  • Bug Fixes
    • Improved structured-output requests across supported AI providers.
    • Preserved in-memory data when rebuilding pipelines from specifications.
    • Improved Polars data handling when Arrow support is unavailable.
  • Tests
    • Added regression coverage for pipeline data retention and Arrow-independent Polars conversion.

…g to Anthropic

Three bugs shipped in 1.11.0, all found by installing the published wheel
into a clean venv and calling the real APIs. None were visible to CI.

1. enrich(schema=...) was completely broken — ValueError: "Either dataframe
   or source_path must be provided", for BOTH DataFrame and CSV-path input.
   PipelineBuilder.from_specifications() copies the five spec objects but
   never carried the data, and QuickPipeline attaches an in-memory frame even
   for a path. The structured-output rebuild therefore always dropped it.
   from_specifications() now takes an explicit dataframe= argument.

   Missed by tests because test_enrich.py mocks Pipeline.execute, so the
   rebuilt pipeline never reached the loader stage — configuration was
   asserted, behaviour was not.

2. enrich(polars_df, ...) raised ModuleNotFoundError: No module named
   'pyarrow'. polars is a core dependency and polars support is documented,
   but DataFrame.to_pandas() goes through Arrow and pyarrow ships only in the
   parquet/all extras. Both conversion directions now fall back to a
   column-wise copy that needs no extra dependency.

   Missed by tests because `uv sync --all-extras` installs pyarrow in the dev
   venv, masking the missing declaration.

3. Anthropic structured output failed with "AsyncMessages.create() got an
   unexpected keyword argument 'parallel_tool_calls'" (3 integration tests).
   instructor >=1.15 normalises from_anthropic(mode=ANTHROPIC_TOOLS) down to
   Mode.TOOLS, so the existing mode check matched the native Anthropic client
   and injected an OpenAI-only parameter its SDK rejects outright. The guard
   now excludes the direct-Anthropic path.

   Introduced by #182 raising instructor's floor from >=1.0.0 to >=1.15.4.

Adds regression tests for (1) and (2) that exercise the real failure — both
verified to fail without the fix and pass with it.

Not fixed here, filed as #187: structured output has no mode fallback, and a
run where every row fails is silently returned as a DataFrame of None.

Verified: 1066 unit tests pass, mypy clean (126 files), ruff clean. Polars
round-trip confirmed live against a pyarrow-free venv.
@coderabbitai

coderabbitai Bot commented Jul 31, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@ptimizeroracle, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 42 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 951a4465-ef01-497b-ab4f-b61afa3a9bd8

📥 Commits

Reviewing files that changed from the base of the PR and between 7efd39c and 671d32f.

📒 Files selected for processing (1)
  • tests/unit/test_enrich.py
📝 Walkthrough

Walkthrough

The change refines structured-output tool-call handling for native Anthropic clients. It also preserves dataframes during pipeline reconstruction and adds Arrow-independent Polars conversion paths with regression tests.

Changes

Structured-output client handling

Layer / File(s) Summary
Tool-call safeguard handling
ondine/adapters/unified_litellm_client.py
Native Anthropic Instructor clients no longer receive parallel_tool_calls=False. LiteLLM-backed tool modes retain the safeguard.

Dataframe preservation and conversion

Layer / File(s) Summary
Pipeline reconstruction with dataframe data
ondine/api/pipeline_builder.py, ondine/api/enrich.py, tests/unit/test_enrich.py
from_specifications accepts and stores an optional dataframe. Structured-output reconstruction passes the existing dataframe, and tests verify that execution receives the original data.
Arrow-independent Polars conversion
ondine/api/enrich.py, tests/unit/test_enrich.py
Polars input and result conversion use helpers with column-wise fallbacks when Arrow support is unavailable. Tests cover the missing-pyarrow path.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • ptimizeroracle/ondine#64: Introduced the native Anthropic Instructor path addressed by the structured-output handling change.
  • ptimizeroracle/ondine#66: Refines the same parallel_tool_calls handling for native Anthropic and non-native Instructor clients.
  • ptimizeroracle/ondine#182: Changes related Instructor and LiteLLM initialization and structured-output compatibility.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes two primary fixes: repairing the enrich() data path and preventing an OpenAI-only argument from reaching Anthropic.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/1.11.1-enrich-anthropic

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
tests/unit/test_enrich.py (1)

359-376: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the _to_polars fallback.

This test only exercises _polars_to_pandas. Simulate result.to_polars() raising ModuleNotFoundError and assert that _to_polars() returns equivalent Polars data.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/test_enrich.py` around lines 359 - 376, Extend
test_polars_conversion_without_pyarrow to also cover _to_polars: monkeypatch
result.to_polars() to raise ModuleNotFoundError, invoke _to_polars() with
representative data, and assert it returns equivalent Polars data with the
expected columns and values.
ondine/adapters/unified_litellm_client.py (1)

987-996: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a regression test for the excluded native-Anthropic path.

The provided regression test only verifies the positive case: LiteLLM-backed TOOLS mode sends parallel_tool_calls=False. No test verifies the negative case that this change fixes: a native Anthropic Instructor client (_uses_direct_anthropic_instructor=True) must NOT receive parallel_tool_calls in call_kwargs. Add a test that constructs a client with provider="anthropic" and no router, spies on instructor_client.create, and asserts parallel_tool_calls is absent from the captured kwargs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ondine/adapters/unified_litellm_client.py` around lines 987 - 996, Add a
regression test covering the native Anthropic path in the client
construction/test suite: create the client with provider="anthropic" and no
router, spy on instructor_client.create, invoke the relevant completion flow in
TOOLS mode, and assert the captured call_kwargs do not contain
parallel_tool_calls. Keep the existing LiteLLM positive-case test unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@ondine/adapters/unified_litellm_client.py`:
- Around line 987-996: Add a regression test covering the native Anthropic path
in the client construction/test suite: create the client with
provider="anthropic" and no router, spy on instructor_client.create, invoke the
relevant completion flow in TOOLS mode, and assert the captured call_kwargs do
not contain parallel_tool_calls. Keep the existing LiteLLM positive-case test
unchanged.

In `@tests/unit/test_enrich.py`:
- Around line 359-376: Extend test_polars_conversion_without_pyarrow to also
cover _to_polars: monkeypatch result.to_polars() to raise ModuleNotFoundError,
invoke _to_polars() with representative data, and assert it returns equivalent
Polars data with the expected columns and values.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 60ee4ab1-3dfa-499e-b41e-8d1650c9b413

📥 Commits

Reviewing files that changed from the base of the PR and between c5c011e and 7efd39c.

📒 Files selected for processing (4)
  • ondine/adapters/unified_litellm_client.py
  • ondine/api/enrich.py
  • ondine/api/pipeline_builder.py
  • tests/unit/test_enrich.py

CI resolves ruff 0.16.0 (floor is >=0.15.21); the appended regression
test class was formatted under an older local resolution.
@ptimizeroracle
ptimizeroracle merged commit 2ea8be4 into main Jul 31, 2026
35 checks passed
@ptimizeroracle
ptimizeroracle deleted the fix/1.11.1-enrich-anthropic branch July 31, 2026 10:27
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