Skip to content

feat: add Responses API and reasoning replay - #1151

Open
andreatnvidia wants to merge 1 commit into
mainfrom
andreatnvidia/feat/responses-api
Open

andreatnvidia wants to merge 1 commit into
mainfrom
andreatnvidia/feat/responses-api

Conversation

@andreatnvidia

Copy link
Copy Markdown
Contributor

Summary

Add opt-in Responses API support so DD can preserve OpenAI reasoning across follow-up calls and run normal column generation and MCP tool loops with Responses-backed models. This provides the API path needed for newer OpenAI reasoning/tool workflows and removes a blocker to choosing newer defaults as the existing models are retired.

The facade follows Nabin's proposed approach: explicit responses() / aresponses(), the existing ModelProvider configuration and completion defaults, and caller-owned conversation history. DD handles provider translation, retries, request admission, and usage accounting; Anthropic continues to use its native Messages API and replayable thinking blocks.

Related Issue

Changes

  • Add ModelConfig.api="responses" to route generate() / agenerate(), column generation, and MCP calls through Responses using the existing OpenAI provider.
  • Add explicit facade methods for callers that manage their own multi-turn history; return visible text alongside ordered replay data.
  • Use stateless requests with store=false and encrypted reasoning inclusion; preserve output items and tool-call IDs across rounds.
  • Translate token limits, structured output schemas, images, and function tools; reject incomplete function calls before execution.
  • Preserve existing configuration fingerprints and API selection when editing models in the CLI.
  • Document configuration, replay examples, and supported scope. No new dependencies.

Testing

  • Full config, engine, and interface suites via .venv/bin/pytest: 5,269 passed, 11 skipped.
  • Unit and integration tests for routing, replay, schema/image translation, tool calls, fingerprints, and CLI edits.
  • Live OpenAI and NVIDIA Inference checks: chat and Responses routes, preview/create, dependent code, image input, judge schemas, sync/async encrypted replay, and sequential MCP calls.
  • Changed-file Ruff lint/format and diff checks.
  • NVIDIA Build catalog access succeeded, but generation returned 403 for both tested models; generation compatibility there remains unverified.
  • Additional focused checks confirmed the existing shared-facade behavior where a text parser can accept empty output after reasoning exhausts the token limit. This PR preserves that behavior; non-empty validation remains the parser's responsibility.

Checklist

  • Follows commit message conventions.
  • Commit signed off (DCO).
  • N/A: external-contributor triage requirement.
  • Model configuration and provider documentation updated.

Signed-off-by: Andre Manoel <amanoel@nvidia.com>
@andreatnvidia
andreatnvidia requested a review from a team as a code owner November 10, 2026 22:59
@greptile-apps

greptile-apps Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 4/5

[Medium impact] The PR extends selectable model API routing across generation and conversation workflows.

The PR appears safe to merge, with a non-blocking CLI issue when changing a Responses-backed model to embeddings.

Findings

  1. P2 Switching to embeddings fails ▶
Fix with agent prompt
### Issue 1
packages/data-designer/src/data_designer/cli/forms/model_builder.py:279-280
When a user edits a Responses-backed model and selects Embedding, `run()` keeps `api="responses"` from the old configuration. `ModelConfig` rejects that combination, so the edit cannot be saved. The form has no API field to change it, leaving users to edit the saved configuration manually. Reset `api` to `"chat-completions"` when the selected generation type is not chat completion.

```suggestion
            if initial_data:
                full_data["api"] = (
                    initial_data.get("api", "chat-completions")
                    if generation_type == GenerationType.CHAT_COMPLETION
                    else "chat-completions"
                )
```

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

Adds opt-in Responses API support for generation and MCP calls.

  • OpenAI-backed models can send generation and tool rounds through Responses while replaying prior output.
  • Model configs can select Responses for generation or direct conversation calls.

Acknowledgment: andreatnvidia explicitly identifies empty text after reasoning exhausts the token limit as existing, intentional behavior. Non-empty checks remain the parser's responsibility; no finding is raised for it.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[generate or agenerate] --> B{Model API}
  B -->|chat-completions| C[Existing completion route]
  B -->|responses| D[Responses request translation]
  D --> E[Stateless provider request]
  E --> F[Text, tool calls, and replay data]
  F --> G{Tool calls?}
  G -->|Yes| H[Run MCP tools]
  H --> I[Append replay and tool results]
  I --> D
  G -->|No| J[Parse generated text]
Loading

Reviews (1) · Last reviewed commit: "feat: add Responses API and reasoning re..." · Reviewed by Greptile

Comment on lines +279 to +280
if initial_data:
full_data["api"] = initial_data.get("api", "chat-completions")

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 Switching to embeddings fails

When a user edits a Responses-backed model and selects Embedding, run() keeps api="responses" from the old configuration. ModelConfig rejects that combination, so the edit cannot be saved. The form has no API field to change it, leaving users to edit the saved configuration manually. Reset api to "chat-completions" when the selected generation type is not chat completion.

Suggested change
if initial_data:
full_data["api"] = initial_data.get("api", "chat-completions")
if initial_data:
full_data["api"] = (
initial_data.get("api", "chat-completions")
if generation_type == GenerationType.CHAT_COMPLETION
else "chat-completions"
)
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/data-designer/src/data_designer/cli/forms/model_builder.py
Line: 279-280

Comment:
**Switching to embeddings fails**

When a user edits a Responses-backed model and selects Embedding, `run()` keeps `api="responses"` from the old configuration. `ModelConfig` rejects that combination, so the edit cannot be saved. The form has no API field to change it, leaving users to edit the saved configuration manually. Reset `api` to `"chat-completions"` when the selected generation type is not chat completion.

```suggestion
            if initial_data:
                full_data["api"] = (
                    initial_data.get("api", "chat-completions")
                    if generation_type == GenerationType.CHAT_COMPLETION
                    else "chat-completions"
                )
```

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

@github-actions

Copy link
Copy Markdown
Contributor

Fern preview: https://nvidia-preview-pr-1151.docs.buildwithfern.com/nemo/datadesigner

Fern previews include the docs-website version archive with PR changes synced into latest. Notebook tutorials are rendered without execution outputs in previews.

@andreatnvidia

Copy link
Copy Markdown
Contributor Author

One decision to settle before changing the defaults: generate() follows ModelConfig.api, but completion() currently stays on Chat Completions. Some models we need to adopt as defaults only support Responses, so moving those aliases would break existing direct callers unless we address this routing difference.

I propose having completion() / acompletion() also honor the configured API, with a one-time warning per model alias when a direct call routes through Responses. Existing configs would still default to "chat-completions". Explicit responses() calls and normal generate() calls would not warn.

This is configured routing, not retry-based fallback. It preserves existing call sites when defaults change and makes the migration visible. Although the naming becomes less endpoint-specific, completion() already maps to Anthropic Messages, so treating it as a single model request is consistent with its cross-provider role.

This slightly adjusts @nabinchha's proposal: completion behavior remains unchanged for existing configurations, while Responses-backed defaults work through existing methods. We would still need to validate parameter compatibility for replacement models.

@svcnvidia-nemo-ci svcnvidia-nemo-ci left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I found one Warning: changing a Responses-backed model to an embedding model in the CLI always fails validation because the hidden API selection is retained.

I inspected all 14 changed files and traced the affected configuration, adapter, facade, generation, and MCP paths. This was a static review; I didn't execute tests or independently verify the reported live-provider checks.

Note: formal approval is not available for this request, so this review is posted as a comment only.

Review generated by codex. This was a full review. Request a full review with /review reset=true.

# Merge results
full_data = {**basic_result, "inference_parameters": inference_params}
if initial_data:
full_data["api"] = initial_data.get("api", "chat-completions")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Warning: Reset the API when changing to embedding generation

Failure: Edit an existing ModelConfig(api="responses"), select Embedding, and submit the inference form. This branch unconditionally retains api="responses", but the new model validator rejects that combination. The form has no API field, so retrying cannot produce a valid embedding configuration. The supported generation-type change should save successfully.

Correction: In ModelFormBuilder.run, preserve the initial API only for chat generation. Replace the new conditional with:

if initial_data and generation_type == GenerationType.CHAT_COMPLETION:
    full_data["api"] = initial_data.get("api", "chat-completions")

The embedding branch then uses build_config()'s default API. Keep the validator unchanged: direct construction of an embedding model with api="responses" must remain invalid.

Preservation and acceptance: Add a proposed regression test with an initial Responses model and mocked form answers selecting GenerationType.EMBEDDING, followed by {}. Assert that run() returns an embedding configuration with api == "chat-completions" without entering the retry prompt. Retain both assertions in test_edit_model_preserves_api: ordinary chat edits must preserve either API. Also check that a newly created embedding model still uses the default API. This patch is source-reasoned, not executed; verify it before adopting it.

This branch has not been deployed

No deployments
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.

2 participants