AI & Agent Dev Bug Sandbox logo
AI & Agent Dev Bug Sandbox
Back to Radar

`Convert_to_openai_tool` Ignores `Strict` For Dicts Already In OpenAI Tool Format

When `convert_to_openai_tool` receives a dict that is already in OpenAI tool format (e.g. `{"type": "function", "function": {...}}`) along with `strict=True`, it returns the dict unchanged without adding `strict` or setting `additionalProperties: false`. This silently disables Structured Outputs for `bind_tools` and `with_structured_output`, potentially causing model outputs that do not match the JSON schema.

highConfidence 95%Langchain-CoreAffected V<=1.6.2

Origin Analysis

In `libs/core/langchain_core/utils/function_calling.py`, the function `convert_to_openai_tool` contains an early return for dicts whose `type` is in `_WellKnownOpenAITools` (which includes `"function"`). This early return was added before the `strict` parameter was introduced and was never updated to handle it. Consequently, the nested `function` definition is not passed through `convert_to_openai_function`, which is where `strict` and `additionalProperties` are normally applied. Additionally, `convert_to_openai_function` unconditionally indexes `parameters` when `strict` is set, causing a `KeyError` for function definitions without parameters.
Run the following Python code with langchain-core installed: ```python from langchain_core.utils.function_calling import convert_to_openai_tool function_def = { "name": "lookup", "description": "Lookup data", "parameters": { "type": "object", "properties": {"query": {"type": "string"}}, }, } wrapped = convert_to_openai_tool({"type": "function", "function": function_def}, strict=True) bare = convert_to_openai_tool(function_def, strict=True) print(wrapped["function"].get("strict")) # None print(wrapped["function"]["parameters"].get("additionalProperties")) # None print(bare["function"].get("strict")) # True print(bare["function"]["parameters"].get("additionalProperties")) # False ```

Fixing Code Block

Edge Case Audit

This change may alter behavior for users who previously passed a pre-wrapped tool dict with `strict=True` and expected the old silent pass-through. Tools with conflicting `strict` values (e.g., existing `strict: false` in the nested function) will now raise a `ValueError` as they should, which may break code that inadvertently relied on the bug. The merge order `{**function_dict, **converted_function}` means fields added by `convert_to_openai_function` override those in the original function definition; if a provider-specific field conflicts with a standard field (unlikely), it may be overwritten. The fix assumes `convert_to_openai_function` handles all edge cases; if further issues arise, rollback by reverting the early-return guard to its original form. Test carefully with tools that have no parameters and with built-in tool types before deploying.

Ecosystem Topology