Skip to content

Send ThinkingPart back for OpenAIChatModel models which require reasoning content #2701

@aravindh28

Description

@aravindh28

Description

See @DouweM's latest comments on #2040

Context:

We have a custom API that converts between OpenAI's /chat/completions endpoint format and Bedrock Converse API format. So that our users can use OpenAI-compatible syntax with Bedrock models. Our custom API also supports thinking, tool calling, and streaming.

With thinking + tool calling, the assistant response will look like the following:

{
    "role": "assistant",
    "content": "...",
    "tool_calls": [
        {
            "id": "...",
            "type": "function",
            "function": {
                "name": "...",
                "arguments": "..."
            }
        }
    ],
    "reasoning_content": "...",
    "reasoning_signature": "..."
}

where the reasoning_content and reasoning_signature is required by Bedrock Converse API for the next conversation turn. These fields are not part of OpenAI syntax but we added them to make Bedrock compatible.

However, intializing the model like this -

model = OpenAIModel(
    "us.anthropic.claude-sonnet-4-20250514-v1:0",
    provider=OpenAIProvider(
        base_url= END_POINT_URL,
        api_key= API_KEY,
    ),
)

Gives the following issue -

[ERROR] Exception in agent run: 400: An error occurred (ValidationException) when calling the ConverseStream operation: The model returned the following errors: messages.3.content.0.type: Expected thinking or redacted_thinking, but found text. When thinking is enabled, a final assistant message must start with a thinking block (preceeding the lastmost set of tool_use and tool_result blocks). We recommend you include thinking blocks from previous turns. To avoid this requirement, disable thinking. Please consult our documentation at https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking

@DouweM 's suggestion -

@aravindh28 OpenAIChatModel doesn't currently send thinking parts back:

elif isinstance(item, ThinkingPart):
# NOTE: We don't send ThinkingPart to the providers yet. If you are unsatisfied with this,
# please open an issue. The below code is the code to send thinking to the provider.
# texts.append(f'<think>\n{item.content}\n</think>')
pass

We could add an OpenAIModelProfile option to do so (similar to BedrockModelProfile.bedrock_send_back_thinking_parts), but we'd need to make sure we send them back in the same field where we received them -- i.e. reasoning_content for DeepSeek and your API, or in <think> tags for APIs that returned the thoughts embedded in text parts. We can likely track that on ThinkingPart.id.

I'm guessing it would not be an option for your Pydantic AI users to use the Bedrock Converse API directly, while users of other frameworks can use the Chat Completions-compatible API?

This is worth creating a new issue for, by the way.

Originally posted by @DouweM in #2040

References

#2040 (comment)
#2040 (comment)

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions