Skip to content

test: give mocked chat completion clients a real payload so the no-choices guard does not trip - #40517

Closed
mateo-berri wants to merge 1 commit into
litellm_internal_stagingfrom
litellm_fix_mocked_completion_tests_no_choices
Closed

mateo-berri wants to merge 1 commit into
litellm_internal_stagingfrom
litellm_fix_mocked_completion_tests_no_choices

Conversation

@mateo-berri

@mateo-berri mateo-berri commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

How it solves it:

  • Each mock now returns a minimal real chat completion (one assistant message)
  • The three tests/llm_translation tests share one chat_completion_response conftest fixture
  • The novita test reuses test_completion.py's existing _openai_mock_response
  • Every test still asserts on the same create call kwargs it was written for
  • Test-only change, no proxy or SDK behavior involved

User Flow

Before: a contributor opens a PR against litellm_internal_staging and two CircleCI jobs come back red on tests their change never touched

  1. They push a branch and open a PR on https://github.com/BerriAI/litellm against litellm_internal_staging
  2. On the PR's Checks tab, ci/circleci: local_testing_part1 and ci/circleci: llm_translation_testing show as failed
  3. They open the job pages on https://app.circleci.com/pipelines/github/BerriAI/litellm and see test_completion_novita_ai, test_openai_max_retries_0, test_azure_gpt_4o_with_tool_call_and_response_format[2024-10-21], and test_openai_o_series_max_retries_0 failing with litellm.APIError: LiteLLM: provider returned a response with no 'choices'. Raw keys: []
  4. They rerun the jobs and get the same four failures, so they cannot tell whether their own change broke anything in those two jobs

After: the same PR gets green local_testing_part1 and llm_translation_testing jobs unless the contributor's own change breaks something

  1. They push a branch and open a PR on https://github.com/BerriAI/litellm against litellm_internal_staging
  2. On the PR's Checks tab, ci/circleci: local_testing_part1 and ci/circleci: llm_translation_testing show as passed
  3. The four tests above show as passed on their job pages, so a red job in either of them now points at the contributor's own change

Relevant issues

Follow-up to #40294

Linear ticket

Pre-Submission checklist

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

  • I have added meaningful tests
  • The handful of test files covering my change pass locally, e.g. uv run pytest tests/test_litellm/<your_test_file>.py -v. Leave the suites (make test-unit-*, make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more
  • My PR passes all required CI/CD checks (e.g., lint, schema.d.ts sync check, etc.)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment @greptileai to re-request a review after pushing changes)

Screenshots / Proof of Fix

This PR changes only test code, so there is no proxy or end-user behavior to drive: the observable behavior is the four tests themselves and the two CircleCI jobs that run them. Both legs run the same four node ids from a fresh worktree with AZURE_API_KEY and AZURE_API_BASE set the way CircleCI injects them (the o-series test builds an Azure client before it reaches the mock, and it needs those two variables to get that far)

Shared setup:

T=("tests/local_testing/test_completion.py::test_completion_novita_ai" "tests/llm_translation/test_openai.py::test_openai_max_retries_0" "tests/llm_translation/test_azure_openai.py::test_azure_gpt_4o_with_tool_call_and_response_format" "tests/llm_translation/test_azure_o_series.py::test_openai_o_series_max_retries_0")
AZURE_API_KEY=... AZURE_API_BASE=... .venv/bin/python -m pytest "${T[@]}" -p no:cacheprovider -q --no-header

Before (2662634, the merge base on litellm_internal_staging)

  1. Run the command above
  2. Observed output:
FAILED tests/llm_translation/test_azure_openai.py::test_azure_gpt_4o_with_tool_call_and_response_format[2024-10-21]
FAILED tests/local_testing/test_completion.py::test_completion_novita_ai
FAILED tests/llm_translation/test_openai.py::test_openai_max_retries_0
FAILED tests/llm_translation/test_azure_o_series.py::test_openai_o_series_max_retries_0
4 failed in 0.52s
  1. Every failure is the same error, wrapped per provider:
litellm.exceptions.APIError: litellm.APIError: AzureException APIError - litellm.APIError: LiteLLM: provider returned a response with no 'choices'. Raw keys: []
litellm.exceptions.APIError: litellm.APIError: LiteLLM: provider returned a response with no 'choices'. Raw keys: []
litellm.exceptions.InternalServerError: litellm.InternalServerError: InternalServerError: NovitaException - litellm.APIError: LiteLLM: provider returned a response with no 'choices'. Raw keys: []
litellm.exceptions.InternalServerError: litellm.InternalServerError: InternalServerError: OpenAIException - litellm.APIError: LiteLLM: provider returned a response with no 'choices'. Raw keys: []
litellm.llms.azure.common_utils.AzureOpenAIError: litellm.APIError: LiteLLM: provider returned a response with no 'choices'. Raw keys: []
litellm.llms.openai.common_utils.OpenAIError: litellm.APIError: LiteLLM: provider returned a response with no 'choices'. Raw keys: []

After (74a9b42, PR tip)

  1. Run the same command
  2. Observed output:
4 passed in 0.60s
  1. The two CircleCI jobs that run these tests are green at the same tip: local_testing_part1 and llm_translation_testing

Type

✅ Test

Caveats (if any)

Final Attestation

  • The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

Note

Low Risk
Test-only changes to mock return values; no runtime or production code paths are modified.

Overview
Fixes four CI failures where mocked OpenAI/Azure completion tests started erroring with provider returned a response with no 'choices' after the stricter response validation from #40294.

Adds a shared chat_completion_response pytest fixture in tests/llm_translation/conftest.py (minimal ChatCompletion with one assistant choice). Three llm_translation tests wire it into mocked with_raw_response.create.parse return values so completion parsing succeeds while assertions on max_retries, tool/response_format kwargs, etc. stay the same.

In test_completion_novita_ai, the bare MagicMock on create is replaced with MagicMock(return_value=_openai_mock_response()), reusing the file’s existing helper.

Test-only — no production/SDK behavior changes.

Reviewed by Cursor Bugbot for commit 74a9b42. Bugbot is set up for automated code reviews on this repo. Configure here.

…oices guard does not trip

The four tests only assert on the kwargs the SDK client's create was called with, but since #40294 a bare MagicMock response raises the new no-choices APIError before those assertions run. Each mock now returns a minimal chat completion with one assistant message.
@mateo-berri
mateo-berri requested a review from a team September 10, 2026 02:40
@greptile-apps

greptile-apps Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR gives four mocked chat completion tests structurally valid parsed responses so the no-choices guard no longer prevents their request assertions from running

  • Adds a shared ChatCompletion fixture for three translation tests
  • Configures the OpenAI and Azure mock chains to return that fixture from parse()
  • Reuses the existing OpenAI mock response helper for the Novita test
  • Leaves the tests' request argument assertions unchanged

Confidence Score: 5/5

The test-only changes appear safe to merge and preserve the existing request behavior checks

The new payloads satisfy the production response guard without changing assertions, production code, or provider request behavior

Important Files Changed

Filename Overview
tests/llm_translation/conftest.py Adds a valid, reusable OpenAI ChatCompletion fixture for mocked translation tests
tests/llm_translation/test_azure_o_series.py Configures the mocked Azure o-series client to return a parsed completion before checking max_retries
tests/llm_translation/test_azure_openai.py Gives the Azure raw-response mock a valid parse result while preserving response_format assertions
tests/llm_translation/test_openai.py Configures the mocked OpenAI client to return a parsed completion before checking max_retries
tests/local_testing/test_completion.py Reuses the existing realistic raw response helper in the Novita request argument test

Reviews (1): Last reviewed commit: "test: give mocked chat completion client..." | Re-trigger Greptile

@codecov

codecov Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@mateo-berri

Copy link
Copy Markdown
Contributor Author

bugbot run

@cursor cursor 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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 74a9b42. Configure here.

@mateo-berri

Copy link
Copy Markdown
Contributor Author

Superseded by #40638, which landed on litellm_internal_staging and repairs the same four mocked completion fixtures, so closing this one

auto-merge was automatically disabled September 11, 2026 00:57

Pull request was closed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant