fix: restore chat completion multi-choice support#672
Conversation
Greptile SummaryThis PR restores multi-choice chat completion support by adding
|
| Filename | Overview |
|---|---|
| packages/data-designer-engine/src/data_designer/engine/models/clients/types.py | Adds ChatCompletionChoice dataclass, n field to ChatCompletionRequest, and choices/messages to ChatCompletionResponse with post_init backward-compat guard; logic is correct in all construction paths. |
| packages/data-designer-engine/src/data_designer/engine/models/clients/parsing.py | Refactors response parsing into per-choice helpers; normalize_choice_list handles None/non-list gracefully; image count aggregation across all choices is consistent with the multi-choice intent. |
| packages/data-designer-engine/src/data_designer/engine/models/facade.py | Correctly threads n through completion() and strips it for generate()/agenerate() via _drop_multi_choice_request_fields; allow_multiple_choices is popped from kwargs before being passed explicitly, preventing duplicate-keyword errors. |
| packages/data-designer-engine/src/data_designer/engine/models/clients/adapters/anthropic.py | Adds n to the Anthropic exclusion list so it is never forwarded to an API that does not support it; change is minimal and correct. |
| packages/data-designer-engine/tests/engine/models/test_facade.py | New tests cover n forwarding in completion(), n stripping in generate()/agenerate() for top-level, extra_body, and config-sourced n values; async variants are included. |
| packages/data-designer-engine/tests/engine/models/clients/test_parsing.py | Tests cover single-choice compat, multi-choice preservation (sync and async), n forwarding into transport body, and the ChatCompletionResponse.messages property; good coverage of the changed parsing paths. |
| packages/data-designer-engine/tests/engine/models/clients/test_openai_compatible.py | Verifies n is forwarded into the OpenAI-compatible request body; straightforward and correct. |
| packages/data-designer-engine/tests/engine/models/clients/test_anthropic.py | Verifies n is excluded from Anthropic payloads; minimal and correct addition to existing exclusion test. |
| packages/data-designer-engine/src/data_designer/engine/models/clients/init.py | Exports the new ChatCompletionChoice type alongside the existing public API; no issues. |
Sequence Diagram
sequenceDiagram
participant Caller
participant ModelFacade
participant ModelClient
participant Parser
Note over Caller,Parser: completion() path — n is preserved
Caller->>ModelFacade: "completion(messages, n=4)"
ModelFacade->>ModelFacade: "consolidate_kwargs(n=4)"
ModelFacade->>ModelFacade: "_build_chat_completion_request(n=4)"
ModelFacade->>ModelClient: client.completion(request)
ModelClient->>Parser: parse_chat_completion_response(raw)
Parser->>Parser: normalize_choice_list(raw["choices"])
loop for each choice [0..3]
Parser->>Parser: parse_chat_completion_choice(choice)
end
Parser-->>ModelClient: "ChatCompletionResponse(message=choices[0].message, choices=[...])"
ModelClient-->>ModelFacade: response
ModelFacade-->>Caller: response (response.message + response.choices)
Note over Caller,Parser: generate() path — n is stripped
Caller->>ModelFacade: "generate(prompt, n=4)"
ModelFacade->>ModelFacade: "consolidate_kwargs(n=4)"
ModelFacade->>ModelFacade: _drop_multi_choice_request_fields (removes n)
ModelFacade->>ModelFacade: "completion(..., allow_multiple_choices=False)"
ModelFacade->>ModelClient: "client.completion(request, n=None)"
ModelClient-->>ModelFacade: ChatCompletionResponse(single choice)
ModelFacade->>ModelFacade: parser(response.message.content)
ModelFacade-->>Caller: (parsed_output, messages)
Reviews (6): Last reviewed commit: "Merge branch 'main' into nmulepati/fix-6..." | Re-trigger Greptile
Review: PR #672 — fix: restore chat completion multi-choice supportSummaryThe PR restores chat-completion multi-choice support (issue #620) by:
The change is well-scoped and backward-compatible. Existing single-choice callers continue to work via FindingsCorrectness
API design
Tests
Style / conventions
Performance / security
Structural ImpactNo pre-computed structural impact analysis was available ( VerdictApprove with minor follow-ups. The core change is clean, backward-compatible, and well-tested for the sync OpenAI-compatible path. The two items I'd most like to see addressed before merge:
Nice-to-have: async multi-choice parsing test, soft validation of |
|
Addressed the feedback in
Validation:
|
Restore the chat completion n request field and preserve all returned choices in the canonical response while keeping response.message as the first choice. Add coverage for request forwarding, compatibility access, multi-choice parsing, and generate forwarding. Fixes #620 Signed-off-by: Nabin Mulepati <nmulepati@nvidia.com>
Prevent generate and agenerate from forwarding multi-choice requests that they cannot expose, while keeping completion() multi-choice support intact. Add coverage for async parsing and Anthropic n exclusion. Signed-off-by: Nabin Mulepati <nmulepati@nvidia.com>
b2e22f7 to
a0e0fc0
Compare
johnnygreco
left a comment
There was a problem hiding this comment.
I found one remaining issue after the direct n stripping update: generate()/agenerate() can still forward n when it comes from model/provider configuration or nested extra_body, so the single-result APIs may still request multiple choices and discard all but the first.
Validation I ran locally on the reviewed diff:
uv run pytest packages/data-designer-engine/tests/engine/models/clients/test_parsing.py packages/data-designer-engine/tests/engine/models/clients/test_openai_compatible.py packages/data-designer-engine/tests/engine/models/clients/test_anthropic.py packages/data-designer-engine/tests/engine/models/test_facade.py->188 passedgit diff --check 71997624b31045c8b0268055f4a14eb02acce905-> passed
Signed-off-by: Nabin Mulepati <nmulepati@nvidia.com>
Signed-off-by: Nabin Mulepati <nmulepati@nvidia.com>
Signed-off-by: Nabin Mulepati <nmulepati@nvidia.com>
Signed-off-by: Nabin Mulepati <nmulepati@nvidia.com>
johnnygreco
left a comment
There was a problem hiding this comment.
Approved. I verified the follow-up fix addresses the remaining multi-choice sanitization issue: generate()/agenerate() force allow_multiple_choices=False, sanitization runs after consolidate_kwargs(), configured and nested extra_body.n are stripped, and completion(..., n=...) still preserves multi-choice behavior. Focused model tests passed locally (191 passed).
📋 Summary
Restores chat completion multi-choice compatibility by reintroducing the
nrequest field and preserving all returned choices in the canonical response. Existing single-choice callers can continue usingresponse.message, while callers that request multiple completions can useresponse.choices.🔗 Related Issue
Fixes #620
🔄 Changes
ChatCompletionRequest.nand export a newChatCompletionChoiceresponse type.ChatCompletionResponse.choices, while keepingresponse.messageas the first choice for existing callers.nthroughModelFacade.completion()and OpenAI-compatible transport bodies, while excluding it from Anthropic request forwarding.nfromgenerate()andagenerate()before delegating to completion because those APIs return one parsed result.nexclusion, andgenerate(..., n=...)stripping.🔍 Attention Areas
types.py— updates the canonical chat completion response contract while preservingresponse.message.facade.py—completion(..., n=...)exposes multiple choices, whilegenerate(..., n=...)stripsnbecause it returns a single parsed result.🧪 Testing
make testpasses (not run; focused model suite was run instead)PYTHONPATH=packages/data-designer-config/src:packages/data-designer-engine/src:packages/data-designer/src uv run --group dev pytest packages/data-designer-engine/tests/engine/models -qpasses (532 passed)uv run --group dev ruff check ...passes for touched files✅ Checklist