Skip to content

Commit d27fb0c

Browse files
authored
feat(langchain,openai): add strict flag to ProviderStrategy structured output (#34149)
1 parent 69dd39c commit d27fb0c

File tree

3 files changed

+32
-8
lines changed

3 files changed

+32
-8
lines changed

libs/langchain_v1/langchain/agents/structured_output.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -255,21 +255,32 @@ class ProviderStrategy(Generic[SchemaT]):
255255
def __init__(
256256
self,
257257
schema: type[SchemaT],
258+
*,
259+
strict: bool = False,
258260
) -> None:
259-
"""Initialize ProviderStrategy with schema."""
261+
"""Initialize ProviderStrategy with schema.
262+
263+
Args:
264+
schema: Schema to enforce via the provider's native structured output.
265+
strict: Whether to request strict provider-side schema enforcement.
266+
"""
260267
self.schema = schema
261-
self.schema_spec = _SchemaSpec(schema)
268+
self.schema_spec = _SchemaSpec(schema, strict=strict)
262269

263270
def to_model_kwargs(self) -> dict[str, Any]:
264271
"""Convert to kwargs to bind to a model to force structured output."""
265272
# OpenAI:
266273
# - see https://platform.openai.com/docs/guides/structured-outputs
267-
response_format = {
274+
json_schema: dict[str, Any] = {
275+
"name": self.schema_spec.name,
276+
"schema": self.schema_spec.json_schema,
277+
}
278+
if self.schema_spec.strict:
279+
json_schema["strict"] = True
280+
281+
response_format: dict[str, Any] = {
268282
"type": "json_schema",
269-
"json_schema": {
270-
"name": self.schema_spec.name,
271-
"schema": self.schema_spec.json_schema,
272-
},
283+
"json_schema": json_schema,
273284
}
274285
return {"response_format": response_format}
275286

libs/langchain_v1/tests/unit_tests/agents/test_response_format.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,18 @@ def test_json_schema(self) -> None:
737737
assert response["structured_response"] == EXPECTED_WEATHER_DICT
738738
assert len(response["messages"]) == 4
739739

740+
def test_provider_strategy_strict_flag(self) -> None:
741+
"""ProviderStrategy should pass through strict flag for provider schemas."""
742+
# Default should not set strict
743+
strategy_default = ProviderStrategy(WeatherBaseModel)
744+
kwargs_default = strategy_default.to_model_kwargs()
745+
assert "strict" not in kwargs_default["response_format"]["json_schema"]
746+
747+
# Explicit strict True should include the flag
748+
strategy_strict = ProviderStrategy(WeatherBaseModel, strict=True)
749+
kwargs_strict = strategy_strict.to_model_kwargs()
750+
assert kwargs_strict["response_format"]["json_schema"]["strict"] is True
751+
740752

741753
class TestDynamicModelWithResponseFormat:
742754
"""Test response_format with middleware that modifies the model."""

libs/partners/openai/langchain_openai/chat_models/base.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1886,9 +1886,10 @@ def bind_tools(
18861886
):
18871887
# compat with langchain.agents.create_agent response_format, which is
18881888
# an approximation of OpenAI format
1889+
strict = response_format["json_schema"].get("strict", None)
18891890
response_format = cast(dict, response_format["json_schema"]["schema"])
18901891
kwargs["response_format"] = _convert_to_openai_response_format(
1891-
response_format
1892+
response_format, strict=strict
18921893
)
18931894
return super().bind(tools=formatted_tools, **kwargs)
18941895

0 commit comments

Comments
 (0)