Skip to content

refactor(predibase): migrate transform_request and transform_response… - #25249

Merged
krrish-berri-2 merged 6 commits into
BerriAI:litellm_oss_branchfrom
Jerry-SDE:refactor/predibase-transformation
Apr 25, 2026
Merged

refactor(predibase): migrate transform_request and transform_response…#25249
krrish-berri-2 merged 6 commits into
BerriAI:litellm_oss_branchfrom
Jerry-SDE:refactor/predibase-transformation

Conversation

@Jerry-SDE

@Jerry-SDE Jerry-SDE commented Apr 6, 2026

Copy link
Copy Markdown
Contributor

… to transformation.py

Relevant issues

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have Added testing in the tests/test_litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Delays in PR merge?

If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).

CI (LiteLLM team)

CI status guideline:

  • 50-55 passing tests: main is stable with minor issues.
  • 45-49 passing tests: acceptable but needs attention
  • <= 40 passing tests: unstable; be careful with your merges and assess the risk.
  • Branch creation CI run
    Link:

  • CI run for the last commit
    Link:

  • Merge / cherry-pick CI run
    Links:

Type

🆕 New Feature
🐛 Bug Fix
🧹 Refactoring
📖 Documentation
🚄 Infrastructure
✅ Test

Changes

  • Migrated Predibase request building logic from litellm/llms/predibase/chat/handler.py into litellm/llms/predibase/chat/transformation.py by implementing PredibaseConfig.transform_request().
  • Migrated Predibase response parsing logic from handler.py into PredibaseConfig.transform_response() (including generated text parsing, finish reason/logprob extraction, best-of handling, usage calculation, and x-* header forwarding).
  • Added PredibaseConfig.get_complete_url() to centralize Predibase URL construction and /generate vs /generate_stream selection.
  • Moved output_parser() logic to transformation.py and removed duplicate logic from the handler.
  • Slimmed PredibaseChatCompletion in handler.py to delegate transformation concerns to PredibaseConfig while preserving existing sync/async + streaming execution paths.
  • Added focused unit tests in tests/test_litellm/llms/test_predibase_transformation.py for:
    • request transformation
    • custom prompt branch
    • stream/non-stream URL resolution
    • response transformation success path (including best-of + headers)
    • response error handling (invalid json, error field)

Relevant issues

Fixes #25205

Pre-Submission checklis

  • I have Added testing in the tests/test_litellm/ directory
  • My PR passes all unit tests on make test-unit
    (local environment blocked by optional enterprise dependency import in full suite; targeted tests for this change pass)
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

Type

🧹 Refactoring
✅ Test

@vercel

vercel Bot commented Apr 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
litellm Ready Ready Preview, Comment Apr 13, 2026 2:06am

Request Review

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@codspeed-hq

codspeed-hq Bot commented Apr 6, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 16 untouched benchmarks


Comparing Jerry-SDE:refactor/predibase-transformation (6d4db7e) with main (09cd7e3)

Open in CodSpeed

@greptile-apps

greptile-apps Bot commented Apr 6, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR completes the migration of Predibase's request-building, response-parsing, and URL-construction logic from handler.py into PredibaseConfig in transformation.py, making it consistent with the rest of the LiteLLM provider architecture. All previously flagged P0/P1 issues have been resolved: encoding is now typed Any, the async_completion path reuses the existing predibase_config instance, the missing-tenant-id error path is tested, and the best_of class-level default is now captured via the request_data["parameters"] fallback in transform_response.

Confidence Score: 5/5

Safe to merge — clean refactoring with all prior P1 concerns resolved and comprehensive mock test coverage.

All previously raised P1 findings (encoding type, config-instance reuse, best_of class-default visibility, missing tenant_id test) have been addressed. The only remaining finding is a P2 style issue (unused api_key param in get_complete_url). No security concerns, no real network calls in tests, and functional behavior is preserved.

No files require special attention.

Important Files Changed

Filename Overview
litellm/llms/predibase/chat/transformation.py Receives full transform_request, transform_response, get_complete_url, and output_parser implementations; encoding annotation corrected to Any, best_of fallback now reads from request_data["parameters"] to capture class-level config defaults. Minor: unused api_key param in get_complete_url.
litellm/llms/predibase/chat/handler.py Slimmed to delegation only; process_response and output_parser removed; predibase_config instance correctly forwarded to async_completion with a None fallback guard; streaming paths unchanged.
tests/test_litellm/llms/test_predibase_transformation.py New mock-only test file covering request transform, custom prompt, stream/non-stream URL resolution, missing tenant_id ValueError, best_of from optional_params and from request_data, invalid JSON, error-field, non-dict payload, empty generated_text, invalid best_of fallback, usage fallbacks, and async config reuse. No real network calls.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant Handler as PredibaseChatCompletion<br/>(handler.py)
    participant Config as PredibaseConfig<br/>(transformation.py)
    participant HTTP as HTTP Client

    Caller->>Handler: completion(model, messages, ...)
    Handler->>Config: validate_environment(...)
    Config-->>Handler: headers
    Handler->>Config: get_complete_url(api_base, model, tenant_id, stream)
    Config-->>Handler: completion_url
    Handler->>Config: transform_request(model, messages, optional_params)
    Config-->>Handler: data {inputs, parameters}
    Handler->>HTTP: POST completion_url, data
    HTTP-->>Handler: raw_response
    Handler->>Config: transform_response(model, raw_response, ...)
    Config-->>Handler: ModelResponse
    Handler-->>Caller: ModelResponse
Loading

Reviews (7): Last reviewed commit: "test(predibase): cover invalid best_of f..." | Re-trigger Greptile

@@ -135,9 +143,117 @@ def transform_response(
api_key: Optional[str] = None,

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.

P2 encoding typed as str but used as tokenizer object

The encoding parameter is annotated as str, but lines 228–231 call .encode() on it and take the length of the result — the calling convention of a tiktoken or similar tokenizer object, not a Python string. The annotation is misleading and will produce MyPy errors on every .encode() invocation.

Suggested change
api_key: Optional[str] = None,
encoding: Any,

@Jerry-SDE Jerry-SDE Apr 6, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@greptile-apps I have fixed it

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.

The fix is already in this PR! Line 142 of the diff shows encoding: Any instead of encoding: str:

def transform_response(  # noqa: PLR0915
    ...
    encoding: Any,

This correctly reflects that encoding is a tokenizer object (likely tiktoken's Encoding type) that has an .encode() method, not a string.

Tip: You can customize Greptile's behavior for this repo with .greptile/rules and .greptile/config.

Comment thread litellm/llms/predibase/chat/handler.py Outdated
Comment on lines 232 to 243
return litellm.PredibaseConfig().transform_response(
model=model,
response=response,
raw_response=response,
model_response=model_response,
stream=stream,
logging_obj=logging_obj,
api_key=api_key,
data=data,
request_data=data,
messages=messages,
print_verbose=print_verbose,
optional_params=optional_params,
litellm_params=litellm_params or {},
encoding=encoding,
)

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.

P2 async_completion creates a redundant fresh PredibaseConfig() instance

async_completion calls litellm.PredibaseConfig().transform_response(...) — constructing a brand-new instance — while the parent completion() method already holds a predibase_config instance created at line 72. That instance is never forwarded to async_completion, so the async path silently discards it and duplicates work. If instance-level state is added to PredibaseConfig in the future, the async path will silently ignore it.

Consider adding a predibase_config parameter to async_completion and passing the existing instance from completion():

# In completion(), pass the existing instance:
return self.async_completion(
    ...,
    predibase_config=predibase_config,
)

# In async_completion(), accept and use it:
async def async_completion(self, ..., predibase_config: PredibaseConfig, ...) -> ModelResponse:
    ...
    return predibase_config.transform_response(...)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@greptile-apps I have fixed it.

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.

Confirmed! The fix has been properly applied:

  1. Parameter added (line 210): async_completion now accepts predibase_config parameter
  2. Instance passed (line 152): completion() passes its existing predibase_config instance to async_completion
  3. Fallback handled (lines 211-212): Creates new instance only if None is passed
  4. Used correctly (line 236): Calls transform_response() on the passed/fallback instance

The async path now reuses the config instance from the parent method instead of silently discarding it. ✓

Comment thread tests/test_litellm/llms/test_predibase_transformation.py
)
from litellm.types.utils import LiteLLMLoggingBaseClass
from litellm.utils import Choices, CustomStreamWrapper, Message, ModelResponse, Usage
from litellm.utils import CustomStreamWrapper, ModelResponse

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@github-advanced-security I have fixed it.

Comment thread litellm/llms/predibase/chat/transformation.py Fixed
Comment thread litellm/llms/predibase/chat/transformation.py Fixed
@codecov

codecov Bot commented Apr 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

- Fix type annotation for 'encoding' (str -> Any) to match usage
- Optimize async_completion by passing PredibaseConfig instance
- Resolve CodeQL circular import by using litellm.types.utils
- Add comments to empty except blocks for better clarity
- Ensure 100% test parity for transformation logic
@Jerry-SDE

Copy link
Copy Markdown
Contributor Author

Predibase-targeted tests pass; proxy/prompts failures appear unrelated (MagicMock awaited)

Add focused Predibase tests to cover remaining transformation and handler branches highlighted by Codecov, including env URL fallback and async/sync delegation paths.

Made-with: Cursor
Comment on lines +185 to 191
optional_params=request_optional_params,
api_key=api_key,
data=data,
request_data=data,
messages=messages,
print_verbose=print_verbose,
litellm_params=request_litellm_params,
encoding=encoding,
)

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.

P1 Config-level best_of default invisible to transform_response

transform_request merges class-level config defaults (e.g. PredibaseConfig.best_of = 2) into the outgoing request body on its own internal copy, but transform_response only sees the raw per-request request_optional_params (no config defaults merged). If best_of is non-None at the class level, the API will return best_of_sequences, but the "best_of" in optional_params guard in transform_response will be False, silently dropping all but the first choice.

The original handler.py avoided this by mutating optional_params in-place with config defaults before passing it to process_response. Now those two paths diverge.

One clean fix: check the already-merged request body instead of optional_params in transform_response:

# In transform_response, replace:
if "best_of" in optional_params and optional_params["best_of"] > 1:

# With:
effective_best_of = optional_params.get("best_of") or request_data.get("parameters", {}).get("best_of", 0)
if effective_best_of > 1:

This makes transform_response self-consistent regardless of whether best_of came from a per-request param or a class-level default.

@Jerry-SDE Jerry-SDE closed this Apr 22, 2026
@Jerry-SDE Jerry-SDE reopened this Apr 22, 2026
@krrish-berri-2

Copy link
Copy Markdown
Contributor

@Jerry-SDE can you include a screenshot of this working as expected?

@Jerry-SDE

Copy link
Copy Markdown
Contributor Author

@Jerry-SDE can you include a screenshot of this working as expected?

t1 t2 t3

@krrish-berri-2
krrish-berri-2 changed the base branch from main to litellm_oss_branch April 25, 2026 15:08
@krrish-berri-2
krrish-berri-2 merged commit d4c0d55 into BerriAI:litellm_oss_branch Apr 25, 2026
91 of 95 checks passed
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.

refactor(predibase): migrate transform_request and transform_response from handler.py to transformation.py

4 participants