feat(deepseek): support strict beta structured output (#32727)

**Description:** This PR adds support for DeepSeek's beta strict mode
feature for structured
outputs and tool calling. It overrides `bind_tools()` and
`with_structured_output()` to automatically use
DeepSeek's beta endpoint (https://api.deepseek.com/beta) when
`strict=True`. Both methods need overriding because they're independent
entry points and user can call either directly. When DeepSeek's strict
mode graduates from beta, we can just remove both overriden methods. You
can read more about the beta feature here:
https://api-docs.deepseek.com/guides/function_calling#strict-mode-beta
  
**Issue:** Implements #32670 


**Dependencies:** None


**Sample Code**

```python
from langchain_deepseek import ChatDeepSeek
from pydantic import BaseModel, Field
from typing import Optional
import os


# Enter your DeepSeek API Key here
API_KEY = "YOUR_API_KEY"


# location, temperature, condition are required fields
# humidity is optional field with default value
class WeatherInfo(BaseModel):
    location: str = Field(description="City name")
    temperature: int = Field(description="Temperature in Celsius")
    condition: str = Field(description="Weather condition (sunny, cloudy, rainy)")
    humidity: Optional[int] = Field(default=None, description="Humidity percentage")


llm = ChatDeepSeek(
    model="deepseek-chat",
    api_key=API_KEY,
)

# just to confirm that a new instance will use the default base url (instead of beta)
print(f"Default API base: {llm.api_base}")



# Test 1: bind_tools with strict=True shoud list all the tools calls
print("\nTest 1: bind_tools with strict=True")
llm_with_tools = llm.bind_tools([WeatherInfo], strict=True)
response = llm_with_tools.invoke("Tell me the weather in New York. It's 22 degrees, sunny.")
print(response.tool_calls)



# Test 2: with_structured_output with strict=True
print("\nTest 2: with_structured_output with strict=True")
structured_llm = llm.with_structured_output(WeatherInfo, strict=True)
result = structured_llm.invoke("Tell me the weather in New York.")
print(f"  Result: {result}")
assert isinstance(result, WeatherInfo), "Result should be a WeatherInfo instance"
```

---------

Co-authored-by: Mason Daugherty <mason@langchain.dev>
Co-authored-by: Mason Daugherty <github@mdrxy.com>
This commit is contained in:
Shahroz Ahmad
2025-11-09 23:24:33 -04:00
committed by GitHub
parent c6801fe159
commit 31b5e4810c
3 changed files with 132 additions and 72 deletions

View File

@@ -370,7 +370,7 @@ wheels = [
[[package]]
name = "langchain-core"
version = "1.0.0"
version = "1.0.4"
source = { editable = "../../core" }
dependencies = [
{ name = "jsonpatch" },
@@ -404,6 +404,7 @@ test = [
{ name = "blockbuster", specifier = ">=1.5.18,<1.6.0" },
{ name = "freezegun", specifier = ">=1.2.2,<2.0.0" },
{ name = "grandalf", specifier = ">=0.8.0,<1.0.0" },
{ name = "langchain-model-profiles", directory = "../../model-profiles" },
{ name = "langchain-tests", directory = "../../standard-tests" },
{ name = "numpy", marker = "python_full_version < '3.13'", specifier = ">=1.26.4" },
{ name = "numpy", marker = "python_full_version >= '3.13'", specifier = ">=2.1.0" },
@@ -420,6 +421,7 @@ test = [
]
test-integration = []
typing = [
{ name = "langchain-model-profiles", directory = "../../model-profiles" },
{ name = "langchain-text-splitters", directory = "../../text-splitters" },
{ name = "mypy", specifier = ">=1.18.1,<1.19.0" },
{ name = "types-pyyaml", specifier = ">=6.0.12.2,<7.0.0.0" },
@@ -475,7 +477,7 @@ typing = [{ name = "mypy", specifier = ">=1.10.0,<2.0.0" }]
[[package]]
name = "langchain-openai"
version = "1.0.0"
version = "1.0.2"
source = { editable = "../openai" }
dependencies = [
{ name = "langchain-core" },
@@ -498,7 +500,6 @@ test = [
{ name = "langchain", editable = "../../langchain_v1" },
{ name = "langchain-core", editable = "../../core" },
{ name = "langchain-tests", editable = "../../standard-tests" },
{ name = "langgraph-prebuilt", specifier = ">=0.7.0rc1" },
{ name = "numpy", marker = "python_full_version < '3.13'", specifier = ">=1.26.4" },
{ name = "numpy", marker = "python_full_version >= '3.13'", specifier = ">=2.1.0" },
{ name = "pytest", specifier = ">=7.3.0,<8.0.0" },
@@ -526,7 +527,7 @@ typing = [
[[package]]
name = "langchain-tests"
version = "1.0.0"
version = "1.0.1"
source = { editable = "../../standard-tests" }
dependencies = [
{ name = "httpx" },