Skip to content

DeepSeek

Use DeepSeek through its Chat Completions or Responses API. The deepseek: prefix selects Chat Completions; use OpenAIResponsesModel explicitly for Responses.

Use deepseek-flash for the current Flash model. DeepSeek also accepts the legacy deepseek-v4-flash name and serves it with the current Flash model.

Install

Install Pydantic AI with the OpenAI SDK used by this integration:

Terminal
pip install "pydantic-ai-slim[openai]"

Configuration

To use the DeepSeek provider, first create an API key by following the Quick Start guide.

You can then set the DEEPSEEK_API_KEY environment variable and use DeepSeekProvider by name:

from pydantic_ai import Agent

agent = Agent('deepseek:deepseek-flash')
...

Or initialise the model and provider directly:

from pydantic_ai import Agent
from pydantic_ai.models.openai import OpenAIChatModel
from pydantic_ai.providers.deepseek import DeepSeekProvider

model = OpenAIChatModel(
    'deepseek-flash',
    provider=DeepSeekProvider(api_key='your-deepseek-api-key'),
)
agent = Agent(model)
...

You can customize the HTTP client:

from httpx2 import AsyncClient

from pydantic_ai import Agent
from pydantic_ai.models.openai import OpenAIChatModel
from pydantic_ai.providers.deepseek import DeepSeekProvider

custom_http_client = AsyncClient(timeout=30)
model = OpenAIChatModel(
    'deepseek-flash',
    provider=DeepSeekProvider(
        api_key='your-deepseek-api-key', http_client=custom_http_client
    ),
)
agent = Agent(model)
...

OpenAI-compatible providers also accept a legacy httpx.AsyncClient during Pydantic AI v2, but emit a deprecation warning. Use httpx2.AsyncClient for new code; legacy HTTPX client support will be removed in Pydantic AI v3.

Structured output and thinking

deepseek-flash and DeepSeek’s V4 models think by default, and DeepSeek rejects a forced tool choice while thinking is on, answering Thinking mode does not support this tool_choice. Pydantic AI therefore sends tool_choice='auto' on those requests, which leaves the model free to answer in prose instead of calling the output tool — on deepseek-v4-pro that costs a retry often enough to exhaust the retry budget. Turn thinking off when you need structured output to be reliable, and forcing is used again:

from pydantic import BaseModel

from pydantic_ai import Agent
from pydantic_ai.models.openai import OpenAIChatModel, OpenAIChatModelSettings


class Answer(BaseModel):
    text: str


agent = Agent(
    OpenAIChatModel('deepseek-v4-pro', provider='deepseek'),
    output_type=Answer,
    model_settings=OpenAIChatModelSettings(thinking=False),
)
...

Passing tool_choice='required' explicitly while thinking is on raises a UserError rather than failing at the API.

DeepSeek also rejects a thinking request when a tool-calling assistant turn after the last user message lacks reasoning_content, such as one served by another model or with thinking off. Whenever the request thinks, DeepSeekProvider sends the field empty on every tool-calling turn that lacks it. Set OpenAIModelProfile.openai_chat_thinking_requires_field_on_tool_calls on your own profile, alongside openai_chat_send_back_thinking_parts='field' and openai_chat_thinking_field='reasoning_content', if you serve DeepSeek’s Chat Completions shape from another endpoint.

Responses API

As an alternative to the Chat Completions API shown above, DeepSeek also serves an OpenAI-compatible Responses API for both V4 models. Use it by pairing OpenAIResponsesModel with DeepSeekProvider:

from pydantic_ai import Agent
from pydantic_ai.models.openai import OpenAIResponsesModel
from pydantic_ai.providers.deepseek import DeepSeekProvider

model = OpenAIResponsesModel(
    'deepseek-flash',
    provider=DeepSeekProvider(api_key='your-deepseek-api-key'),
)
agent = Agent(model)
...

DeepSeek documents which parts of the Responses API it implements, and unsupported fields are silently ignored rather than rejected, so it’s worth knowing what does nothing:

  • The API is stateless, so openai_conversation_id, background mode and message compaction are unavailable. Pass message history back on each run instead.
  • Leave openai_previous_response_id unset. Setting it makes Pydantic AI drop the earlier turns it assumes the server already holds, and DeepSeek stores nothing, so the model silently loses the conversation instead of erroring.
  • WebSearchTool works with deepseek-v4-pro and is ignored with deepseek-flash. DeepSeek ignores the other native tools instead of reporting an error.
  • Image and document inputs are replaced with placeholder text rather than rejected.
  • Reasoning is configured with openai_reasoning_effort (or the unified thinking setting); openai_reasoning_summary is accepted but produces no summary.
  • NativeOutput is available here but not on Chat Completions: DeepSeek honors a strict JSON Schema on the Responses API, while its Chat Completions endpoint rejects one with This response_format type is unavailable now.

The one difference Pydantic AI handles for you: DeepSeek merges each function call into its adjacent assistant message. Replaying a turn that interleaves calls with thinking or text would therefore create separate messages with unanswered calls, which DeepSeek rejects with No tool output found for tool call .... Pydantic AI moves the calls after the other items when building the request. This reorders only the request; your message history is unchanged.

Reordering applies only when every function call has a result and the turn contains no provider-owned native tool or compaction items. DeepSeek rejects unresolved calls in any order, while provider-owned items are left unchanged. Set OpenAIModelProfile.openai_responses_supports_interleaved_function_calls on your own profile if you serve DeepSeek’s Responses shape from another endpoint, or to turn the reordering off.