Skip to content

feat: enable reasoning support for DeepSeek models on Azure#412

Merged
mikehostetler merged 2 commits intoagentjido:mainfrom
shelvick:feat/deepseek-reasoning-support
Feb 14, 2026
Merged

feat: enable reasoning support for DeepSeek models on Azure#412
mikehostetler merged 2 commits intoagentjido:mainfrom
shelvick:feat/deepseek-reasoning-support

Conversation

@shelvick
Copy link
Copy Markdown
Contributor

Summary

  • Add deepseek_model?/1 helper to AdapterHelpers for detecting DeepSeek model IDs
  • Stop stripping thinking config from additional_model_request_fields for DeepSeek models in pre_validate_options (it was being removed with an "Anthropic-specific" warning, but DeepSeek supports it natively)
  • Default to thinking: %{type: "enabled"} in format_request for DeepSeek models, enabling reasoning by default
  • Users can override via additional_model_request_fields (e.g., custom budget_tokens) or suppress entirely by setting thinking: nil
  • Fix model ID extraction in pre_validate_options to use provider_model_id when available, matching effective_model_id/1 used elsewhere in the Azure module

Notes

  • DeepSeek R1 models always reason regardless of the thinking parameter — confirmed via live test that sending it is harmless (ignored, no error)
  • mai-ds-r1 (Microsoft's DeepSeek R1 derivative) uses the "mai-ds" model family prefix and is correctly not matched by deepseek_model?/1

Test plan

  • 10 new tests covering deepseek_model?/1, pre_validate_options thinking preservation, format_request default injection, user override, opt-out via nil, and regression checks for non-DeepSeek models
  • Full suite: 2196 tests, 0 failures

@shelvick shelvick force-pushed the feat/deepseek-reasoning-support branch from afcc129 to 02ae450 Compare February 11, 2026 00:34
DeepSeek V3 models support opt-in reasoning via a `thinking` parameter,
but the Azure OpenAI adapter was stripping it as Anthropic-specific.

- Add `deepseek_model?/1` helper to AdapterHelpers
- Stop stripping thinking config in pre_validate_options for DeepSeek
- In format_request, pass through user-provided thinking config from
  additional_model_request_fields for DeepSeek models (opt-in, no
  default change)
- Fix model ID extraction in pre_validate_options to use
  provider_model_id when available (matching effective_model_id/1)
- Fix extract_usage to infer reasoning_tokens from reasoning_content
  in choices when completion_tokens_details is absent (affects DeepSeek
  and any other model that surfaces reasoning via reasoning_content)
- Add tests for all new behavior
@shelvick shelvick force-pushed the feat/deepseek-reasoning-support branch from 02ae450 to 3559e2e Compare February 11, 2026 01:42
Copy link
Copy Markdown
Contributor

@mikehostetler mikehostetler left a comment

Choose a reason for hiding this comment

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

Hey @shelvick — thanks so much for this contribution! Really nice work here. The test coverage is thorough, the deepseek_model?/1 helper fits perfectly alongside the existing model predicates, and the provider_model_id fix is a great catch. Love the opt-in design too — much safer than defaulting thinking on.

A couple of things I'd love your thoughts on before we merge:


infer_reasoning_from_choices over-reports reasoning tokens

When completion_tokens_details is absent and reasoning_content is present, the fallback sets reasoning_tokens = completion_tokens. Since the response also has a content field with the actual answer, this attributes all output tokens to reasoning — which could mislead downstream analytics or billing calculations.

A couple of ideas (totally open to whatever you think is best):

  • Simple/honest: just leave reasoning_tokens as 0 when we can't get an authoritative breakdown — at least we're not over-reporting.
  • Proportional estimate: use the relative text lengths of reasoning_content vs content to approximate the split: round(completion_tokens * reasoning_len / (reasoning_len + answer_len)). Not perfect, but much closer to reality.

Either way would be great — the key thing is avoiding the "100% reasoning" assumption when we know there's also answer content.


Minor: inline comment in function body

On L97 of openai.ex there's a comment inside warn_and_remove_anthropic_thinking_config/2:

# DeepSeek models support thinking config natively; don't strip it

The project has a convention of no comments inside function bodies (enforced by a custom Credo rule). The code is quite readable without it — the function name and the deepseek_model? guard make the intent clear. Would you mind removing it?


That's it! Everything else looks really solid. Thanks again for putting this together 🙏

When completion_tokens_details is absent, the fallback
infer_reasoning_from_choices was attributing all completion tokens to
reasoning even when the response also contained answer content. Use the
relative text lengths of reasoning_content vs content to approximate the
split instead.

Also remove inline comment in warn_and_remove_anthropic_thinking_config
to follow project convention of no comments inside function bodies.
@shelvick
Copy link
Copy Markdown
Contributor Author

@mikehostetler This was fixed too, I just didn't leave a comment. (Is the comment needed? Sorry, I'm bad at GitHub.)

@mikehostetler
Copy link
Copy Markdown
Contributor

@shelvick You're all good - I sw th e commits - I'll work these through shortly!

@mikehostetler mikehostetler merged commit bec3641 into agentjido:main Feb 14, 2026
7 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.

2 participants