Skip to content

feat(openai): Reasoning translation for responses to chat completions - #1014

Open
s-akhtar-baig wants to merge 24 commits into
praxis-proxy:mainfrom
s-akhtar-baig:reasoning_translation
Open

s-akhtar-baig wants to merge 24 commits into
praxis-proxy:mainfrom
s-akhtar-baig:reasoning_translation

Conversation

@s-akhtar-baig

@s-akhtar-baig s-akhtar-baig commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a configurable reasoning dialect to the responses_to_chat_completions filter so that raw chain-of-thought returned by a Chat Completions backend is surfaced as a first-class Responses reasoning output item. The first dialect is vllm; the default (none) preserves existing behavior.

A reasoning summary request is currently rejected and is out of scope for #31.

Related issue

Implements part one for #31. Streaming reasoning translation is deferred to #36 and will be added as a follow-up PR once all the bug fixes land in main.

Validation

  • Unit tests
  • Integration or functional tests
  • make lint

Checklist

  • I reviewed every changed line and can explain the change.
  • New capabilities include an example config and functional example test.
  • User-facing behavior and generated documentation are updated.
  • Commits are signed and include a Signed-off-by trailer.

Breaking changes

@s-akhtar-baig
s-akhtar-baig requested review from a team and alexsnaps September 8, 2026 16:28
@praxis-bot-app

praxis-bot-app Bot commented Sep 8, 2026

Copy link
Copy Markdown

Unsigned commits: 64fee79. Please sign your commits.

Signed-off-by: Shabana Baig <43451943+s-akhtar-baig@users.noreply.github.com>
Signed-off-by: Shabana Baig <43451943+s-akhtar-baig@users.noreply.github.com>
Signed-off-by: Shabana Baig <43451943+s-akhtar-baig@users.noreply.github.com>
Signed-off-by: Shabana Baig <43451943+s-akhtar-baig@users.noreply.github.com>
@praxis-bot-app

praxis-bot-app Bot commented Sep 8, 2026

Copy link
Copy Markdown

Missing Signed-off-by: a97af86, c8f8865. All commits require sign-off (via git commit --signoff).

Signed-off-by: Shabana Baig <43451943+s-akhtar-baig@users.noreply.github.com>
…hat_completions filter

Signed-off-by: Shabana Baig <43451943+s-akhtar-baig@users.noreply.github.com>
Signed-off-by: Shabana Baig <43451943+s-akhtar-baig@users.noreply.github.com>

@praxis-bot praxis-bot 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.

praxis-bot review: feat(openai): Reasoning translation for responses to chat completions

Clean feature addition. The reasoning dialect abstraction is well-structured, validation is thorough, and test coverage is strong across request validation, response extraction, dialect fallback, size limits, and the end-to-end integration path. The resolve_vllm_reasoning field-priority logic correctly handles all combinations of current/deprecated/null/empty/malformed field states.

One medium finding on the response path.

Severity Count
Medium 1

Comment thread apis/src/openai/translation/chat_completions.rs Outdated
Signed-off-by: Shabana Baig <43451943+s-akhtar-baig@users.noreply.github.com>

@leseb leseb left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[P1] Reasoning breaks stored continuations. Emitted reasoning items are persisted and rehydrated, but Chat translation rejects type: "reasoning" as unsupported. previous_response_id and agentic tool continuations therefore return 400 after a reasoning-bearing response. Emission, rejection

[P1] Streaming silently discards vLLM reasoning. Streaming requests pass validation, but ChatDelta does not model vLLM’s delta.reasoning; unknown fields are ignored. Either translate reasoning deltas or reject streaming when the dialect is enabled. Validation, stream delta

[P2] Malformed summary controls are silently accepted. summary and generate_summary use Value::as_str, making booleans, objects, arrays, and numbers indistinguishable from absent fields. Requests such as "summary": true are forwarded with the option silently dropped. reasoning.rs

Signed-off-by: Shabana Baig <43451943+s-akhtar-baig@users.noreply.github.com>
Signed-off-by: Shabana Baig <43451943+s-akhtar-baig@users.noreply.github.com>
@s-akhtar-baig
s-akhtar-baig requested a review from a team as a code owner September 10, 2026 21:57
@s-akhtar-baig

Copy link
Copy Markdown
Contributor Author

Thanks, @leseb, for reviewing this PR!

I have updated accordingly and will implement streaming reasoning in a follow-up PR. PTAL when you get a chance.

@leseb

leseb commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

Thanks, @leseb, for reviewing this PR!

I have updated accordingly and will implement streaming reasoning in a follow-up PR. PTAL when you get a chance.

Thanks, another round :)

[P1] Continuation reasoning is silently discarded. The previous 400 was replaced with an empty match arm. Stored continuations and agentic tool rounds now succeed but omit the prior raw reasoning, which reasoning models may require between tool calls. chat_completions.rs:558

[P2] Reasoning-only completions are rejected. Response validation counts only content, refusal, or tool calls as output. A valid vLLM response containing reasoning but no final text fails before the new reasoning item can be emitted. chat_completions.rs:1267

[P2] Non-object reasoning is silently ignored. Values such as "reasoning": true bypass validation, lose their controls during translation, and can receive an ordinary successful response. This proxy-owned field should return 400 when it is neither an object nor null. reasoning.rs:92

@leseb

leseb commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

we also need new tests in tests/integration/sdk/openai/test_openai_responses_vllm.py

Signed-off-by: Shabana Baig <43451943+s-akhtar-baig@users.noreply.github.com>
Signed-off-by: Shabana Baig <43451943+s-akhtar-baig@users.noreply.github.com>
Signed-off-by: Shabana Baig <43451943+s-akhtar-baig@users.noreply.github.com>
@s-akhtar-baig

Copy link
Copy Markdown
Contributor Author

Thanks, @leseb!

[P1] Continuation reasoning is silently discarded. The previous 400 was replaced with an empty match arm. Stored continuations and agentic tool rounds now succeed but omit the prior raw reasoning, which reasoning models may require between tool calls.

For P1, I am not sure where to add reasoning across calls. I looked at the OpenAI and vLLM chat completions docs and neither of those show re-adding raw reasoning as part of input messages or any other field. Did you have anything specific in mind?

Signed-off-by: Shabana Baig <43451943+s-akhtar-baig@users.noreply.github.com>
@s-akhtar-baig

s-akhtar-baig commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

@leseb, can you please re-review when you get a chance? Thanks!

@leseb

leseb commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator

Thanks, @leseb!

[P1] Continuation reasoning is silently discarded. The previous 400 was replaced with an empty match arm. Stored continuations and agentic tool rounds now succeed but omit the prior raw reasoning, which reasoning models may require between tool calls.

For P1, I am not sure where to add reasoning across calls. I looked at the OpenAI and vLLM chat completions docs and neither of those show re-adding raw reasoning as part of input messages or any other field. Did you have anything specific in mind?

The main concern is the silent drop. For this PR, we should either:

  • reject reasoning input items and document continuation as unsupported.
  • combine the rehydrated reasoning item with that assistant turn.

I'm leaning implementing the replay now.

Signed-off-by: Shabana Baig <43451943+s-akhtar-baig@users.noreply.github.com>
Signed-off-by: Shabana Baig <43451943+s-akhtar-baig@users.noreply.github.com>
Signed-off-by: Shabana Baig <43451943+s-akhtar-baig@users.noreply.github.com>
@s-akhtar-baig

Copy link
Copy Markdown
Contributor Author

@leseb, I have implemented replay for reasoning. The prior turn's chain-of-thought now gets added to the assistant message that follows it, wrapped in markers (… by default). Note that these delimiters are configurable. PTAL when you get a chance, thanks!

@leseb leseb left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

One blocker remains.

The silent drop is fixed, but reasoning is replayed as ordinary content:

{"content":"<think>...</think>answer"}

For the vllm dialect, it should instead be:

{"reasoning":"...","content":"answer"}

vLLM supports this field and uses it in its own Responses converter. Marker injection is model/template-dependent.

Recommendation: replace the marker logic with assistant.reasoning, update the tests, then approve. The focused 160 tests pass and the branch merges cleanly, but full CI is still pending. I posted nothing on GitHub.

Signed-off-by: Shabana Baig <43451943+s-akhtar-baig@users.noreply.github.com>
Signed-off-by: Shabana Baig <43451943+s-akhtar-baig@users.noreply.github.com>
@s-akhtar-baig

Copy link
Copy Markdown
Contributor Author

Thanks, @leseb, for pointing this out!

I have removed the markers and replaced with the reasoning field. PTAL when you get a chance.

Signed-off-by: Shabana Baig <43451943+s-akhtar-baig@users.noreply.github.com>
Signed-off-by: Shabana Baig <43451943+s-akhtar-baig@users.noreply.github.com>
@s-akhtar-baig
s-akhtar-baig requested a review from leseb September 23, 2026 15:59
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.

3 participants