Skip to content

Commit 4935a14

Browse files
authored
core,integrations[minor]: Dont error on fields in model_kwargs (#27110)
Given the current erroring behavior, every time we've moved a kwarg from model_kwargs and made it its own field that was a breaking change. Updating this behavior to support the old instantiations / serializations. Assuming build_extra_kwargs was not something that itself is being used externally and needs to be kept backwards compatible
1 parent 0495b7f commit 4935a14

File tree

17 files changed

+91
-68
lines changed

17 files changed

+91
-68
lines changed

libs/community/langchain_community/chat_models/snowflake.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
get_pydantic_field_names,
1818
pre_init,
1919
)
20-
from langchain_core.utils.utils import build_extra_kwargs
20+
from langchain_core.utils.utils import _build_model_kwargs
2121
from pydantic import Field, SecretStr, model_validator
2222

2323
SUPPORTED_ROLES: List[str] = [
@@ -131,10 +131,7 @@ class ChatSnowflakeCortex(BaseChatModel):
131131
def build_extra(cls, values: Dict[str, Any]) -> Any:
132132
"""Build extra kwargs from additional params that were passed in."""
133133
all_required_field_names = get_pydantic_field_names(cls)
134-
extra = values.get("model_kwargs", {})
135-
values["model_kwargs"] = build_extra_kwargs(
136-
extra, values, all_required_field_names
137-
)
134+
values = _build_model_kwargs(values, all_required_field_names)
138135
return values
139136

140137
@pre_init

libs/community/langchain_community/llms/anthropic.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
get_pydantic_field_names,
2727
pre_init,
2828
)
29-
from langchain_core.utils.utils import build_extra_kwargs, convert_to_secret_str
29+
from langchain_core.utils.utils import _build_model_kwargs, convert_to_secret_str
3030
from pydantic import ConfigDict, Field, SecretStr, model_validator
3131

3232

@@ -69,11 +69,8 @@ class _AnthropicCommon(BaseLanguageModel):
6969
@model_validator(mode="before")
7070
@classmethod
7171
def build_extra(cls, values: Dict) -> Any:
72-
extra = values.get("model_kwargs", {})
7372
all_required_field_names = get_pydantic_field_names(cls)
74-
values["model_kwargs"] = build_extra_kwargs(
75-
extra, values, all_required_field_names
76-
)
73+
values = _build_model_kwargs(values, all_required_field_names)
7774
return values
7875

7976
@pre_init

libs/community/langchain_community/llms/llamacpp.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from langchain_core.language_models.llms import LLM
99
from langchain_core.outputs import GenerationChunk
1010
from langchain_core.utils import get_pydantic_field_names, pre_init
11-
from langchain_core.utils.utils import build_extra_kwargs
11+
from langchain_core.utils.utils import _build_model_kwargs
1212
from pydantic import Field, model_validator
1313

1414
logger = logging.getLogger(__name__)
@@ -199,10 +199,7 @@ def validate_environment(cls, values: Dict) -> Dict:
199199
def build_model_kwargs(cls, values: Dict[str, Any]) -> Any:
200200
"""Build extra kwargs from additional params that were passed in."""
201201
all_required_field_names = get_pydantic_field_names(cls)
202-
extra = values.get("model_kwargs", {})
203-
values["model_kwargs"] = build_extra_kwargs(
204-
extra, values, all_required_field_names
205-
)
202+
values = _build_model_kwargs(values, all_required_field_names)
206203
return values
207204

208205
@property

libs/community/langchain_community/llms/openai.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
pre_init,
3535
)
3636
from langchain_core.utils.pydantic import get_fields
37-
from langchain_core.utils.utils import build_extra_kwargs
37+
from langchain_core.utils.utils import _build_model_kwargs
3838
from pydantic import ConfigDict, Field, model_validator
3939

4040
from langchain_community.utils.openai import is_openai_v1
@@ -268,10 +268,7 @@ def __new__(cls, **data: Any) -> Union[OpenAIChat, BaseOpenAI]: # type: ignore
268268
def build_extra(cls, values: Dict[str, Any]) -> Any:
269269
"""Build extra kwargs from additional params that were passed in."""
270270
all_required_field_names = get_pydantic_field_names(cls)
271-
extra = values.get("model_kwargs", {})
272-
values["model_kwargs"] = build_extra_kwargs(
273-
extra, values, all_required_field_names
274-
)
271+
values = _build_model_kwargs(values, all_required_field_names)
275272
return values
276273

277274
@pre_init

libs/community/tests/unit_tests/chat_models/test_anthropic.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,12 @@ def test_anthropic_model_kwargs() -> None:
3333

3434

3535
@pytest.mark.requires("anthropic")
36-
def test_anthropic_invalid_model_kwargs() -> None:
37-
with pytest.raises(ValueError):
38-
ChatAnthropic(model_kwargs={"max_tokens_to_sample": 5})
36+
def test_anthropic_fields_in_model_kwargs() -> None:
37+
"""Test that for backwards compatibility fields can be passed in as model_kwargs."""
38+
llm = ChatAnthropic(model_kwargs={"max_tokens_to_sample": 5})
39+
assert llm.max_tokens_to_sample == 5
40+
llm = ChatAnthropic(model_kwargs={"max_tokens": 5})
41+
assert llm.max_tokens_to_sample == 5
3942

4043

4144
@pytest.mark.requires("anthropic")

libs/community/tests/unit_tests/llms/test_openai.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,12 @@ def test_openai_model_kwargs() -> None:
2626

2727

2828
@pytest.mark.requires("openai")
29-
def test_openai_invalid_model_kwargs() -> None:
30-
with pytest.raises(ValueError):
31-
OpenAI(model_kwargs={"model_name": "foo"})
32-
33-
# Test that "model" cannot be specified in kwargs
34-
with pytest.raises(ValueError):
35-
OpenAI(model_kwargs={"model": "gpt-3.5-turbo-instruct"})
29+
def test_openai_fields_model_kwargs() -> None:
30+
"""Test that for backwards compatibility fields can be passed in as model_kwargs."""
31+
llm = OpenAI(model_kwargs={"model_name": "foo"}, api_key="foo")
32+
assert llm.model_name == "foo"
33+
llm = OpenAI(model_kwargs={"model": "foo"}, api_key="foo")
34+
assert llm.model_name == "foo"
3635

3736

3837
@pytest.mark.requires("openai")

libs/core/langchain_core/utils/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
)
3333

3434
__all__ = [
35+
"build_extra_kwargs",
3536
"StrictFormatter",
3637
"check_package_version",
3738
"convert_to_secret_str",
@@ -46,7 +47,6 @@
4647
"raise_for_status_with_text",
4748
"xor_args",
4849
"try_load_from_hub",
49-
"build_extra_kwargs",
5050
"image",
5151
"get_from_env",
5252
"get_from_dict_or_env",

libs/core/langchain_core/utils/utils.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,51 @@ def get_pydantic_field_names(pydantic_cls: Any) -> set[str]:
210210
return all_required_field_names
211211

212212

213+
def _build_model_kwargs(
214+
values: dict[str, Any],
215+
all_required_field_names: set[str],
216+
) -> dict[str, Any]:
217+
"""Build "model_kwargs" param from Pydanitc constructor values.
218+
219+
Args:
220+
values: All init args passed in by user.
221+
all_required_field_names: All required field names for the pydantic class.
222+
223+
Returns:
224+
Dict[str, Any]: Extra kwargs.
225+
226+
Raises:
227+
ValueError: If a field is specified in both values and extra_kwargs.
228+
ValueError: If a field is specified in model_kwargs.
229+
"""
230+
extra_kwargs = values.get("model_kwargs", {})
231+
for field_name in list(values):
232+
if field_name in extra_kwargs:
233+
raise ValueError(f"Found {field_name} supplied twice.")
234+
if field_name not in all_required_field_names:
235+
warnings.warn(
236+
f"""WARNING! {field_name} is not default parameter.
237+
{field_name} was transferred to model_kwargs.
238+
Please confirm that {field_name} is what you intended.""",
239+
stacklevel=7,
240+
)
241+
extra_kwargs[field_name] = values.pop(field_name)
242+
243+
invalid_model_kwargs = all_required_field_names.intersection(extra_kwargs.keys())
244+
if invalid_model_kwargs:
245+
warnings.warn(
246+
f"Parameters {invalid_model_kwargs} should be specified explicitly. "
247+
f"Instead they were passed in as part of `model_kwargs` parameter.",
248+
stacklevel=7,
249+
)
250+
for k in invalid_model_kwargs:
251+
values[k] = extra_kwargs.pop(k)
252+
253+
values["model_kwargs"] = extra_kwargs
254+
return values
255+
256+
257+
# DON'T USE! Kept for backwards-compatibility but should never have been public.
213258
def build_extra_kwargs(
214259
extra_kwargs: dict[str, Any],
215260
values: dict[str, Any],

libs/core/tests/unit_tests/utils/test_imports.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
"raise_for_status_with_text",
1818
"xor_args",
1919
"try_load_from_hub",
20-
"build_extra_kwargs",
2120
"image",
21+
"build_extra_kwargs",
2222
"get_from_dict_or_env",
2323
"get_from_env",
2424
"stringify_dict",

libs/partners/anthropic/langchain_anthropic/chat_models.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,13 @@
5656
)
5757
from langchain_core.tools import BaseTool
5858
from langchain_core.utils import (
59-
build_extra_kwargs,
6059
from_env,
6160
get_pydantic_field_names,
6261
secret_from_env,
6362
)
6463
from langchain_core.utils.function_calling import convert_to_openai_tool
6564
from langchain_core.utils.pydantic import is_basemodel_subclass
65+
from langchain_core.utils.utils import _build_model_kwargs
6666
from pydantic import (
6767
BaseModel,
6868
ConfigDict,
@@ -646,11 +646,8 @@ def _get_ls_params(
646646
@model_validator(mode="before")
647647
@classmethod
648648
def build_extra(cls, values: Dict) -> Any:
649-
extra = values.get("model_kwargs", {})
650649
all_required_field_names = get_pydantic_field_names(cls)
651-
values["model_kwargs"] = build_extra_kwargs(
652-
extra, values, all_required_field_names
653-
)
650+
values = _build_model_kwargs(values, all_required_field_names)
654651
return values
655652

656653
@model_validator(mode="after")

libs/partners/anthropic/langchain_anthropic/llms.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
get_pydantic_field_names,
2626
)
2727
from langchain_core.utils.utils import (
28-
build_extra_kwargs,
28+
_build_model_kwargs,
2929
from_env,
3030
secret_from_env,
3131
)
@@ -88,11 +88,8 @@ class _AnthropicCommon(BaseLanguageModel):
8888
@model_validator(mode="before")
8989
@classmethod
9090
def build_extra(cls, values: Dict) -> Any:
91-
extra = values.get("model_kwargs", {})
9291
all_required_field_names = get_pydantic_field_names(cls)
93-
values["model_kwargs"] = build_extra_kwargs(
94-
extra, values, all_required_field_names
95-
)
92+
values = _build_model_kwargs(values, all_required_field_names)
9693
return values
9794

9895
@model_validator(mode="after")

libs/partners/anthropic/tests/unit_tests/test_chat_models.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,12 @@ def test_anthropic_model_kwargs() -> None:
6161

6262

6363
@pytest.mark.requires("anthropic")
64-
def test_anthropic_invalid_model_kwargs() -> None:
65-
with pytest.raises(ValueError):
66-
ChatAnthropic(model="foo", model_kwargs={"max_tokens_to_sample": 5}) # type: ignore[call-arg]
64+
def test_anthropic_fields_in_model_kwargs() -> None:
65+
"""Test that for backwards compatibility fields can be passed in as model_kwargs."""
66+
llm = ChatAnthropic(model="foo", model_kwargs={"max_tokens_to_sample": 5}) # type: ignore[call-arg]
67+
assert llm.max_tokens == 5
68+
llm = ChatAnthropic(model="foo", model_kwargs={"max_tokens": 5}) # type: ignore[call-arg]
69+
assert llm.max_tokens == 5
6770

6871

6972
@pytest.mark.requires("anthropic")

libs/partners/fireworks/langchain_fireworks/chat_models.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
convert_to_openai_tool,
8080
)
8181
from langchain_core.utils.pydantic import is_basemodel_subclass
82-
from langchain_core.utils.utils import build_extra_kwargs, from_env, secret_from_env
82+
from langchain_core.utils.utils import _build_model_kwargs, from_env, secret_from_env
8383
from pydantic import (
8484
BaseModel,
8585
ConfigDict,
@@ -366,10 +366,7 @@ def is_lc_serializable(cls) -> bool:
366366
def build_extra(cls, values: Dict[str, Any]) -> Any:
367367
"""Build extra kwargs from additional params that were passed in."""
368368
all_required_field_names = get_pydantic_field_names(cls)
369-
extra = values.get("model_kwargs", {})
370-
values["model_kwargs"] = build_extra_kwargs(
371-
extra, values, all_required_field_names
372-
)
369+
values = _build_model_kwargs(values, all_required_field_names)
373370
return values
374371

375372
@model_validator(mode="after")

libs/partners/fireworks/langchain_fireworks/llms.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
)
1212
from langchain_core.language_models.llms import LLM
1313
from langchain_core.utils import get_pydantic_field_names
14-
from langchain_core.utils.utils import build_extra_kwargs, secret_from_env
14+
from langchain_core.utils.utils import _build_model_kwargs, secret_from_env
1515
from pydantic import ConfigDict, Field, SecretStr, model_validator
1616

1717
from langchain_fireworks.version import __version__
@@ -93,10 +93,7 @@ class Fireworks(LLM):
9393
def build_extra(cls, values: Dict[str, Any]) -> Any:
9494
"""Build extra kwargs from additional params that were passed in."""
9595
all_required_field_names = get_pydantic_field_names(cls)
96-
extra = values.get("model_kwargs", {})
97-
values["model_kwargs"] = build_extra_kwargs(
98-
extra, values, all_required_field_names
99-
)
96+
values = _build_model_kwargs(values, all_required_field_names)
10097
return values
10198

10299
@property

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@
9090
TypeBaseModel,
9191
is_basemodel_subclass,
9292
)
93-
from langchain_core.utils.utils import build_extra_kwargs, from_env, secret_from_env
93+
from langchain_core.utils.utils import _build_model_kwargs, from_env, secret_from_env
9494
from pydantic import BaseModel, ConfigDict, Field, SecretStr, model_validator
9595
from typing_extensions import Self
9696

@@ -477,10 +477,7 @@ class BaseChatOpenAI(BaseChatModel):
477477
def build_extra(cls, values: Dict[str, Any]) -> Any:
478478
"""Build extra kwargs from additional params that were passed in."""
479479
all_required_field_names = get_pydantic_field_names(cls)
480-
extra = values.get("model_kwargs", {})
481-
values["model_kwargs"] = build_extra_kwargs(
482-
extra, values, all_required_field_names
483-
)
480+
values = _build_model_kwargs(values, all_required_field_names)
484481
return values
485482

486483
@model_validator(mode="after")

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
from langchain_core.language_models.llms import BaseLLM
2828
from langchain_core.outputs import Generation, GenerationChunk, LLMResult
2929
from langchain_core.utils import get_pydantic_field_names
30-
from langchain_core.utils.utils import build_extra_kwargs, from_env, secret_from_env
30+
from langchain_core.utils.utils import _build_model_kwargs, from_env, secret_from_env
3131
from pydantic import ConfigDict, Field, SecretStr, model_validator
3232
from typing_extensions import Self
3333

@@ -160,10 +160,7 @@ class BaseOpenAI(BaseLLM):
160160
def build_extra(cls, values: Dict[str, Any]) -> Any:
161161
"""Build extra kwargs from additional params that were passed in."""
162162
all_required_field_names = get_pydantic_field_names(cls)
163-
extra = values.get("model_kwargs", {})
164-
values["model_kwargs"] = build_extra_kwargs(
165-
extra, values, all_required_field_names
166-
)
163+
values = _build_model_kwargs(values, all_required_field_names)
167164
return values
168165

169166
@model_validator(mode="after")

libs/partners/openai/tests/unit_tests/llms/test_base.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,12 @@ def test_openai_model_kwargs() -> None:
3030
assert llm.model_kwargs == {"foo": "bar"}
3131

3232

33-
def test_openai_invalid_model_kwargs() -> None:
34-
with pytest.raises(ValueError):
35-
OpenAI(model_kwargs={"model_name": "foo"})
33+
def test_openai_fields_in_model_kwargs() -> None:
34+
"""Test that for backwards compatibility fields can be passed in as model_kwargs."""
35+
llm = OpenAI(model_kwargs={"model_name": "foo"})
36+
assert llm.model_name == "foo"
37+
llm = OpenAI(model_kwargs={"model": "foo"})
38+
assert llm.model_name == "foo"
3639

3740

3841
def test_openai_incorrect_field() -> None:

0 commit comments

Comments
 (0)