Skip to content

Conversation

arnavsinghvi11
Copy link
Collaborator

adds support for ChainOfThought to use the built-in reasoning when used with reasoning models.
previous behavior would not make use of this reasoning content, and instead still create a reasoning field during the generation. instead now we turn off CoT if the model is detected to generate reasoning (using litellm.supports_reasoning)
and flagged in dspy.settings as use_native_reasoning

Examples:

lm = dspy.LM(
        model="anthropic/claude-3-7-sonnet-20250219",
        reasoning_effort="low",
        max_tokens=4000,
        temperature=1.0
    )
dspy.configure(lm=lm)
cot = dspy.ChainOfThought("question -> answer")
result = cot(question="What is 19 * 23?

#Result has the reasoning field with the contents from the model thinking

backward compatible with non-reasoning models, which default to the CoT logic of adding the reasoning OutputField on the fly.

(Note that OpenAI reasoning models do not expose the content and require setting the summary parameter to get a reasoning summary trace)

lm = dspy.LM(
        model="openai/gpt-5-mini",
        model_type="responses",
        reasoning={"effort": "low", "summary": "auto"},
        max_tokens=16000,
        temperature=1.0
    )

@arnavsinghvi11 arnavsinghvi11 requested a review from okhat September 4, 2025 06:20
@chenmoneygithub
Copy link
Collaborator

chenmoneygithub commented Sep 6, 2025

Moving the offline discussion here for visibility:

The current implementation has problems in multithreading setup, if settings.use_native_reasoning is modified in thread 0, then other threads will hit error dspy.settings can only be changed by the thread that initially configured it. There are two options:

  1. Just check if the LM is a reasoning model inside ChainOfThought, which might miss the case where users don't want reasoning on reasoning model. But on my search I don't find litellm provides the option to completely turn off the reasoning for reasoning models.
  2. Let the adapter's response to tell ChainOfThought if reasoning /reasoning_content is available from the LM call, which means the adapter's return value has a reserved field reasoning /reasoning_content , if this value exists, then ChainOfThought will use this value to set the reasoning field's value.

2 is more robust than 1, but it could waste a few tokens on reasoning model because we still have the reasoning output field. Meanwhile, I feel we shouldn't let dspy.Predict call returns reasoning field on reasoning model because it's not requested by the user, and it can cause unexpected regression when users switch model in their legacy code. So even if we go with 1, we need to ensure that normal dspy.Predict doesn't include reasoning as the output.

Another thing we need to consider is streaming, now we allow users to stream the reasoning field of CoT because that's an output field that we can listen to, if we remove this when using the reasoning model, then the same code will break for the user. Keeping the extra field partially resolves the issue, with a caveat that the reasoning could be different from the response.reasoning_content as natively generated by the model. This can be a followup PR though, and feel free to leave it on me.

cc @arnavsinghvi11 @okhat @TomeHirata since this one is important, and could be more important in the future due to dominance of reasoning model.

@vacmar01
Copy link
Contributor

I wanted such a feature for a long time and I thought about implementing it myself but never got around to it, so I really appreciate this PR.

My dream would be that if I use ChainOfThought with reasoning models, their reasoning tokens would be parsed into the reasoning field of the resulting Prediction. For a bare Predict call it would just be discarded.

I know that this will probably be a nightmare to maintain because of the different implementations of returning the thinking tokens for different models, but supporting this feature at least for all "OpenAI reasoning-style" models would be great imo.

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