langchain-openai 0.2.0.dev2__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {langchain_openai-0.2.0.dev2 → langchain_openai-0.2.2}/PKG-INFO +2 -2
- {langchain_openai-0.2.0.dev2 → langchain_openai-0.2.2}/langchain_openai/chat_models/azure.py +33 -331
- {langchain_openai-0.2.0.dev2 → langchain_openai-0.2.2}/langchain_openai/chat_models/base.py +142 -54
- {langchain_openai-0.2.0.dev2 → langchain_openai-0.2.2}/langchain_openai/embeddings/base.py +13 -9
- {langchain_openai-0.2.0.dev2 → langchain_openai-0.2.2}/langchain_openai/llms/base.py +2 -5
- {langchain_openai-0.2.0.dev2 → langchain_openai-0.2.2}/pyproject.toml +3 -2
- {langchain_openai-0.2.0.dev2 → langchain_openai-0.2.2}/LICENSE +0 -0
- {langchain_openai-0.2.0.dev2 → langchain_openai-0.2.2}/README.md +0 -0
- {langchain_openai-0.2.0.dev2 → langchain_openai-0.2.2}/langchain_openai/__init__.py +0 -0
- {langchain_openai-0.2.0.dev2 → langchain_openai-0.2.2}/langchain_openai/chat_models/__init__.py +0 -0
- {langchain_openai-0.2.0.dev2 → langchain_openai-0.2.2}/langchain_openai/embeddings/__init__.py +0 -0
- {langchain_openai-0.2.0.dev2 → langchain_openai-0.2.2}/langchain_openai/embeddings/azure.py +0 -0
- {langchain_openai-0.2.0.dev2 → langchain_openai-0.2.2}/langchain_openai/llms/__init__.py +0 -0
- {langchain_openai-0.2.0.dev2 → langchain_openai-0.2.2}/langchain_openai/llms/azure.py +0 -0
- {langchain_openai-0.2.0.dev2 → langchain_openai-0.2.2}/langchain_openai/output_parsers/__init__.py +0 -0
- {langchain_openai-0.2.0.dev2 → langchain_openai-0.2.2}/langchain_openai/output_parsers/tools.py +0 -0
- {langchain_openai-0.2.0.dev2 → langchain_openai-0.2.2}/langchain_openai/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: langchain-openai
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: An integration package connecting OpenAI and LangChain
|
|
5
5
|
Home-page: https://github.com/langchain-ai/langchain
|
|
6
6
|
License: MIT
|
|
@@ -11,7 +11,7 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.10
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
-
Requires-Dist: langchain-core (>=0.3.
|
|
14
|
+
Requires-Dist: langchain-core (>=0.3.9,<0.4.0)
|
|
15
15
|
Requires-Dist: openai (>=1.40.0,<2.0.0)
|
|
16
16
|
Requires-Dist: tiktoken (>=0.7,<1)
|
|
17
17
|
Project-URL: Repository, https://github.com/langchain-ai/langchain
|
{langchain_openai-0.2.0.dev2 → langchain_openai-0.2.2}/langchain_openai/chat_models/azure.py
RENAMED
|
@@ -4,37 +4,13 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
6
|
import os
|
|
7
|
-
from
|
|
8
|
-
from typing import (
|
|
9
|
-
Any,
|
|
10
|
-
Callable,
|
|
11
|
-
Dict,
|
|
12
|
-
List,
|
|
13
|
-
Literal,
|
|
14
|
-
Optional,
|
|
15
|
-
Sequence,
|
|
16
|
-
Type,
|
|
17
|
-
TypedDict,
|
|
18
|
-
TypeVar,
|
|
19
|
-
Union,
|
|
20
|
-
overload,
|
|
21
|
-
)
|
|
7
|
+
from typing import Any, Callable, Dict, List, Optional, Type, TypedDict, TypeVar, Union
|
|
22
8
|
|
|
23
9
|
import openai
|
|
24
|
-
from langchain_core.language_models import LanguageModelInput
|
|
25
10
|
from langchain_core.language_models.chat_models import LangSmithParams
|
|
26
11
|
from langchain_core.messages import BaseMessage
|
|
27
|
-
from langchain_core.output_parsers import JsonOutputParser, PydanticOutputParser
|
|
28
|
-
from langchain_core.output_parsers.base import OutputParserLike
|
|
29
|
-
from langchain_core.output_parsers.openai_tools import (
|
|
30
|
-
JsonOutputKeyToolsParser,
|
|
31
|
-
PydanticToolsParser,
|
|
32
|
-
)
|
|
33
12
|
from langchain_core.outputs import ChatResult
|
|
34
|
-
from langchain_core.runnables import Runnable, RunnableMap, RunnablePassthrough
|
|
35
|
-
from langchain_core.tools import BaseTool
|
|
36
13
|
from langchain_core.utils import from_env, secret_from_env
|
|
37
|
-
from langchain_core.utils.function_calling import convert_to_openai_tool
|
|
38
14
|
from langchain_core.utils.pydantic import is_basemodel_subclass
|
|
39
15
|
from pydantic import BaseModel, Field, SecretStr, model_validator
|
|
40
16
|
from typing_extensions import Self
|
|
@@ -538,6 +514,7 @@ class AzureChatOpenAI(BaseChatOpenAI):
|
|
|
538
514
|
default_factory=from_env("OPENAI_API_TYPE", default="azure")
|
|
539
515
|
)
|
|
540
516
|
"""Legacy, for openai<1.0.0 support."""
|
|
517
|
+
|
|
541
518
|
validate_base_url: bool = True
|
|
542
519
|
"""If legacy arg openai_api_base is passed in, try to infer if it is a base_url or
|
|
543
520
|
azure_endpoint and update client params accordingly.
|
|
@@ -550,6 +527,28 @@ class AzureChatOpenAI(BaseChatOpenAI):
|
|
|
550
527
|
Used for tracing and token counting. Does NOT affect completion.
|
|
551
528
|
"""
|
|
552
529
|
|
|
530
|
+
disabled_params: Optional[Dict[str, Any]] = Field(default=None)
|
|
531
|
+
"""Parameters of the OpenAI client or chat.completions endpoint that should be
|
|
532
|
+
disabled for the given model.
|
|
533
|
+
|
|
534
|
+
Should be specified as ``{"param": None | ['val1', 'val2']}`` where the key is the
|
|
535
|
+
parameter and the value is either None, meaning that parameter should never be
|
|
536
|
+
used, or it's a list of disabled values for the parameter.
|
|
537
|
+
|
|
538
|
+
For example, older models may not support the 'parallel_tool_calls' parameter at
|
|
539
|
+
all, in which case ``disabled_params={"parallel_tool_calls: None}`` can ben passed
|
|
540
|
+
in.
|
|
541
|
+
|
|
542
|
+
If a parameter is disabled then it will not be used by default in any methods, e.g.
|
|
543
|
+
in
|
|
544
|
+
:meth:`~langchain_openai.chat_models.azure.AzureChatOpenAI.with_structured_output`.
|
|
545
|
+
However this does not prevent a user from directly passed in the parameter during
|
|
546
|
+
invocation.
|
|
547
|
+
|
|
548
|
+
By default, unless ``model_name="gpt-4o"`` is specified, then
|
|
549
|
+
'parallel_tools_calls' will be disabled.
|
|
550
|
+
"""
|
|
551
|
+
|
|
553
552
|
@classmethod
|
|
554
553
|
def get_lc_namespace(cls) -> List[str]:
|
|
555
554
|
"""Get the namespace of the langchain object."""
|
|
@@ -574,6 +573,13 @@ class AzureChatOpenAI(BaseChatOpenAI):
|
|
|
574
573
|
if self.n > 1 and self.streaming:
|
|
575
574
|
raise ValueError("n must be 1 when streaming.")
|
|
576
575
|
|
|
576
|
+
if self.disabled_params is None:
|
|
577
|
+
# As of 09-17-2024 'parallel_tool_calls' param is only supported for gpt-4o.
|
|
578
|
+
if self.model_name and self.model_name == "gpt-4o":
|
|
579
|
+
pass
|
|
580
|
+
else:
|
|
581
|
+
self.disabled_params = {"parallel_tool_calls": None}
|
|
582
|
+
|
|
577
583
|
# Check OPENAI_ORGANIZATION for backwards compatibility.
|
|
578
584
|
self.openai_organization = (
|
|
579
585
|
self.openai_organization
|
|
@@ -634,311 +640,6 @@ class AzureChatOpenAI(BaseChatOpenAI):
|
|
|
634
640
|
self.async_client = self.root_async_client.chat.completions
|
|
635
641
|
return self
|
|
636
642
|
|
|
637
|
-
def bind_tools(
|
|
638
|
-
self,
|
|
639
|
-
tools: Sequence[Union[Dict[str, Any], Type, Callable, BaseTool]],
|
|
640
|
-
*,
|
|
641
|
-
tool_choice: Optional[
|
|
642
|
-
Union[dict, str, Literal["auto", "none", "required", "any"], bool]
|
|
643
|
-
] = None,
|
|
644
|
-
**kwargs: Any,
|
|
645
|
-
) -> Runnable[LanguageModelInput, BaseMessage]:
|
|
646
|
-
# As of 05/2024 Azure OpenAI doesn't support tool_choice="required".
|
|
647
|
-
# TODO: Update this condition once tool_choice="required" is supported.
|
|
648
|
-
if tool_choice in ("any", "required", True):
|
|
649
|
-
if len(tools) > 1:
|
|
650
|
-
raise ValueError(
|
|
651
|
-
f"Azure OpenAI does not currently support {tool_choice=}. Should "
|
|
652
|
-
f"be one of 'auto', 'none', or the name of the tool to call."
|
|
653
|
-
)
|
|
654
|
-
else:
|
|
655
|
-
tool_choice = convert_to_openai_tool(tools[0])["function"]["name"]
|
|
656
|
-
return super().bind_tools(tools, tool_choice=tool_choice, **kwargs)
|
|
657
|
-
|
|
658
|
-
# TODO: Fix typing.
|
|
659
|
-
@overload # type: ignore[override]
|
|
660
|
-
def with_structured_output(
|
|
661
|
-
self,
|
|
662
|
-
schema: Optional[_DictOrPydanticClass] = None,
|
|
663
|
-
*,
|
|
664
|
-
method: Literal["function_calling", "json_mode"] = "function_calling",
|
|
665
|
-
include_raw: Literal[True] = True,
|
|
666
|
-
**kwargs: Any,
|
|
667
|
-
) -> Runnable[LanguageModelInput, _AllReturnType]: ...
|
|
668
|
-
|
|
669
|
-
@overload
|
|
670
|
-
def with_structured_output(
|
|
671
|
-
self,
|
|
672
|
-
schema: Optional[_DictOrPydanticClass] = None,
|
|
673
|
-
*,
|
|
674
|
-
method: Literal["function_calling", "json_mode"] = "function_calling",
|
|
675
|
-
include_raw: Literal[False] = False,
|
|
676
|
-
**kwargs: Any,
|
|
677
|
-
) -> Runnable[LanguageModelInput, _DictOrPydantic]: ...
|
|
678
|
-
|
|
679
|
-
def with_structured_output(
|
|
680
|
-
self,
|
|
681
|
-
schema: Optional[_DictOrPydanticClass] = None,
|
|
682
|
-
*,
|
|
683
|
-
method: Literal["function_calling", "json_mode"] = "function_calling",
|
|
684
|
-
include_raw: bool = False,
|
|
685
|
-
**kwargs: Any,
|
|
686
|
-
) -> Runnable[LanguageModelInput, _DictOrPydantic]:
|
|
687
|
-
"""Model wrapper that returns outputs formatted to match the given schema.
|
|
688
|
-
|
|
689
|
-
Args:
|
|
690
|
-
schema:
|
|
691
|
-
The output schema. Can be passed in as:
|
|
692
|
-
- an OpenAI function/tool schema,
|
|
693
|
-
- a JSON Schema,
|
|
694
|
-
- a TypedDict class,
|
|
695
|
-
- or a Pydantic class.
|
|
696
|
-
If ``schema`` is a Pydantic class then the model output will be a
|
|
697
|
-
Pydantic instance of that class, and the model-generated fields will be
|
|
698
|
-
validated by the Pydantic class. Otherwise the model output will be a
|
|
699
|
-
dict and will not be validated. See :meth:`langchain_core.utils.function_calling.convert_to_openai_tool`
|
|
700
|
-
for more on how to properly specify types and descriptions of
|
|
701
|
-
schema fields when specifying a Pydantic or TypedDict class.
|
|
702
|
-
method:
|
|
703
|
-
The method for steering model generation, either "function_calling"
|
|
704
|
-
or "json_mode". If "function_calling" then the schema will be converted
|
|
705
|
-
to an OpenAI function and the returned model will make use of the
|
|
706
|
-
function-calling API. If "json_mode" then OpenAI's JSON mode will be
|
|
707
|
-
used. Note that if using "json_mode" then you must include instructions
|
|
708
|
-
for formatting the output into the desired schema into the model call.
|
|
709
|
-
include_raw:
|
|
710
|
-
If False then only the parsed structured output is returned. If
|
|
711
|
-
an error occurs during model output parsing it will be raised. If True
|
|
712
|
-
then both the raw model response (a BaseMessage) and the parsed model
|
|
713
|
-
response will be returned. If an error occurs during output parsing it
|
|
714
|
-
will be caught and returned as well. The final output is always a dict
|
|
715
|
-
with keys "raw", "parsed", and "parsing_error".
|
|
716
|
-
|
|
717
|
-
Returns:
|
|
718
|
-
A Runnable that takes same inputs as a :class:`langchain_core.language_models.chat.BaseChatModel`.
|
|
719
|
-
|
|
720
|
-
If ``include_raw`` is False and ``schema`` is a Pydantic class, Runnable outputs
|
|
721
|
-
an instance of ``schema`` (i.e., a Pydantic object).
|
|
722
|
-
|
|
723
|
-
Otherwise, if ``include_raw`` is False then Runnable outputs a dict.
|
|
724
|
-
|
|
725
|
-
If ``include_raw`` is True, then Runnable outputs a dict with keys:
|
|
726
|
-
- ``"raw"``: BaseMessage
|
|
727
|
-
- ``"parsed"``: None if there was a parsing error, otherwise the type depends on the ``schema`` as described above.
|
|
728
|
-
- ``"parsing_error"``: Optional[BaseException]
|
|
729
|
-
|
|
730
|
-
Example: schema=Pydantic class, method="function_calling", include_raw=False:
|
|
731
|
-
.. code-block:: python
|
|
732
|
-
|
|
733
|
-
from typing import Optional
|
|
734
|
-
|
|
735
|
-
from langchain_openai import AzureChatOpenAI
|
|
736
|
-
from pydantic import BaseModel, Field
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
class AnswerWithJustification(BaseModel):
|
|
740
|
-
'''An answer to the user question along with justification for the answer.'''
|
|
741
|
-
|
|
742
|
-
answer: str
|
|
743
|
-
# If we provide default values and/or descriptions for fields, these will be passed
|
|
744
|
-
# to the model. This is an important part of improving a model's ability to
|
|
745
|
-
# correctly return structured outputs.
|
|
746
|
-
justification: Optional[str] = Field(
|
|
747
|
-
default=None, description="A justification for the answer."
|
|
748
|
-
)
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
llm = AzureChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
|
|
752
|
-
structured_llm = llm.with_structured_output(AnswerWithJustification)
|
|
753
|
-
|
|
754
|
-
structured_llm.invoke(
|
|
755
|
-
"What weighs more a pound of bricks or a pound of feathers"
|
|
756
|
-
)
|
|
757
|
-
|
|
758
|
-
# -> AnswerWithJustification(
|
|
759
|
-
# answer='They weigh the same',
|
|
760
|
-
# justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.'
|
|
761
|
-
# )
|
|
762
|
-
|
|
763
|
-
Example: schema=Pydantic class, method="function_calling", include_raw=True:
|
|
764
|
-
.. code-block:: python
|
|
765
|
-
|
|
766
|
-
from langchain_openai import AzureChatOpenAI
|
|
767
|
-
from pydantic import BaseModel
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
class AnswerWithJustification(BaseModel):
|
|
771
|
-
'''An answer to the user question along with justification for the answer.'''
|
|
772
|
-
|
|
773
|
-
answer: str
|
|
774
|
-
justification: str
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
llm = AzureChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
|
|
778
|
-
structured_llm = llm.with_structured_output(
|
|
779
|
-
AnswerWithJustification, include_raw=True
|
|
780
|
-
)
|
|
781
|
-
|
|
782
|
-
structured_llm.invoke(
|
|
783
|
-
"What weighs more a pound of bricks or a pound of feathers"
|
|
784
|
-
)
|
|
785
|
-
# -> {
|
|
786
|
-
# 'raw': AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_Ao02pnFYXD6GN1yzc0uXPsvF', 'function': {'arguments': '{"answer":"They weigh the same.","justification":"Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ."}', 'name': 'AnswerWithJustification'}, 'type': 'function'}]}),
|
|
787
|
-
# 'parsed': AnswerWithJustification(answer='They weigh the same.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.'),
|
|
788
|
-
# 'parsing_error': None
|
|
789
|
-
# }
|
|
790
|
-
|
|
791
|
-
Example: schema=TypedDict class, method="function_calling", include_raw=False:
|
|
792
|
-
.. code-block:: python
|
|
793
|
-
|
|
794
|
-
# IMPORTANT: If you are using Python <=3.8, you need to import Annotated
|
|
795
|
-
# from typing_extensions, not from typing.
|
|
796
|
-
from typing_extensions import Annotated, TypedDict
|
|
797
|
-
|
|
798
|
-
from langchain_openai import AzureChatOpenAI
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
class AnswerWithJustification(TypedDict):
|
|
802
|
-
'''An answer to the user question along with justification for the answer.'''
|
|
803
|
-
|
|
804
|
-
answer: str
|
|
805
|
-
justification: Annotated[
|
|
806
|
-
Optional[str], None, "A justification for the answer."
|
|
807
|
-
]
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
llm = AzureChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
|
|
811
|
-
structured_llm = llm.with_structured_output(AnswerWithJustification)
|
|
812
|
-
|
|
813
|
-
structured_llm.invoke(
|
|
814
|
-
"What weighs more a pound of bricks or a pound of feathers"
|
|
815
|
-
)
|
|
816
|
-
# -> {
|
|
817
|
-
# 'answer': 'They weigh the same',
|
|
818
|
-
# 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.'
|
|
819
|
-
# }
|
|
820
|
-
|
|
821
|
-
Example: schema=OpenAI function schema, method="function_calling", include_raw=False:
|
|
822
|
-
.. code-block:: python
|
|
823
|
-
|
|
824
|
-
from langchain_openai import AzureChatOpenAI
|
|
825
|
-
|
|
826
|
-
oai_schema = {
|
|
827
|
-
'name': 'AnswerWithJustification',
|
|
828
|
-
'description': 'An answer to the user question along with justification for the answer.',
|
|
829
|
-
'parameters': {
|
|
830
|
-
'type': 'object',
|
|
831
|
-
'properties': {
|
|
832
|
-
'answer': {'type': 'string'},
|
|
833
|
-
'justification': {'description': 'A justification for the answer.', 'type': 'string'}
|
|
834
|
-
},
|
|
835
|
-
'required': ['answer']
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
llm = AzureChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
|
|
840
|
-
structured_llm = llm.with_structured_output(oai_schema)
|
|
841
|
-
|
|
842
|
-
structured_llm.invoke(
|
|
843
|
-
"What weighs more a pound of bricks or a pound of feathers"
|
|
844
|
-
)
|
|
845
|
-
# -> {
|
|
846
|
-
# 'answer': 'They weigh the same',
|
|
847
|
-
# 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.'
|
|
848
|
-
# }
|
|
849
|
-
|
|
850
|
-
Example: schema=Pydantic class, method="json_mode", include_raw=True:
|
|
851
|
-
.. code-block::
|
|
852
|
-
|
|
853
|
-
from langchain_openai import AzureChatOpenAI
|
|
854
|
-
from pydantic import BaseModel
|
|
855
|
-
|
|
856
|
-
class AnswerWithJustification(BaseModel):
|
|
857
|
-
answer: str
|
|
858
|
-
justification: str
|
|
859
|
-
|
|
860
|
-
llm = AzureChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
|
|
861
|
-
structured_llm = llm.with_structured_output(
|
|
862
|
-
AnswerWithJustification,
|
|
863
|
-
method="json_mode",
|
|
864
|
-
include_raw=True
|
|
865
|
-
)
|
|
866
|
-
|
|
867
|
-
structured_llm.invoke(
|
|
868
|
-
"Answer the following question. "
|
|
869
|
-
"Make sure to return a JSON blob with keys 'answer' and 'justification'.\n\n"
|
|
870
|
-
"What's heavier a pound of bricks or a pound of feathers?"
|
|
871
|
-
)
|
|
872
|
-
# -> {
|
|
873
|
-
# 'raw': AIMessage(content='{\n "answer": "They are both the same weight.",\n "justification": "Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight." \n}'),
|
|
874
|
-
# 'parsed': AnswerWithJustification(answer='They are both the same weight.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight.'),
|
|
875
|
-
# 'parsing_error': None
|
|
876
|
-
# }
|
|
877
|
-
|
|
878
|
-
Example: schema=None, method="json_mode", include_raw=True:
|
|
879
|
-
.. code-block::
|
|
880
|
-
|
|
881
|
-
structured_llm = llm.with_structured_output(method="json_mode", include_raw=True)
|
|
882
|
-
|
|
883
|
-
structured_llm.invoke(
|
|
884
|
-
"Answer the following question. "
|
|
885
|
-
"Make sure to return a JSON blob with keys 'answer' and 'justification'.\n\n"
|
|
886
|
-
"What's heavier a pound of bricks or a pound of feathers?"
|
|
887
|
-
)
|
|
888
|
-
# -> {
|
|
889
|
-
# 'raw': AIMessage(content='{\n "answer": "They are both the same weight.",\n "justification": "Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight." \n}'),
|
|
890
|
-
# 'parsed': {
|
|
891
|
-
# 'answer': 'They are both the same weight.',
|
|
892
|
-
# 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight.'
|
|
893
|
-
# },
|
|
894
|
-
# 'parsing_error': None
|
|
895
|
-
# }
|
|
896
|
-
""" # noqa: E501
|
|
897
|
-
if kwargs:
|
|
898
|
-
raise ValueError(f"Received unsupported arguments {kwargs}")
|
|
899
|
-
is_pydantic_schema = _is_pydantic_class(schema)
|
|
900
|
-
if method == "function_calling":
|
|
901
|
-
if schema is None:
|
|
902
|
-
raise ValueError(
|
|
903
|
-
"schema must be specified when method is 'function_calling'. "
|
|
904
|
-
"Received None."
|
|
905
|
-
)
|
|
906
|
-
tool_name = convert_to_openai_tool(schema)["function"]["name"]
|
|
907
|
-
llm = self.bind_tools([schema], tool_choice=tool_name)
|
|
908
|
-
if is_pydantic_schema:
|
|
909
|
-
output_parser: OutputParserLike = PydanticToolsParser(
|
|
910
|
-
tools=[schema], # type: ignore[list-item]
|
|
911
|
-
first_tool_only=True, # type: ignore[list-item]
|
|
912
|
-
)
|
|
913
|
-
else:
|
|
914
|
-
output_parser = JsonOutputKeyToolsParser(
|
|
915
|
-
key_name=tool_name, first_tool_only=True
|
|
916
|
-
)
|
|
917
|
-
elif method == "json_mode":
|
|
918
|
-
llm = self.bind(response_format={"type": "json_object"})
|
|
919
|
-
output_parser = (
|
|
920
|
-
PydanticOutputParser(pydantic_object=schema) # type: ignore[arg-type]
|
|
921
|
-
if is_pydantic_schema
|
|
922
|
-
else JsonOutputParser()
|
|
923
|
-
)
|
|
924
|
-
else:
|
|
925
|
-
raise ValueError(
|
|
926
|
-
f"Unrecognized method argument. Expected one of 'function_calling' or "
|
|
927
|
-
f"'json_mode'. Received: '{method}'"
|
|
928
|
-
)
|
|
929
|
-
|
|
930
|
-
if include_raw:
|
|
931
|
-
parser_assign = RunnablePassthrough.assign(
|
|
932
|
-
parsed=itemgetter("raw") | output_parser, parsing_error=lambda _: None
|
|
933
|
-
)
|
|
934
|
-
parser_none = RunnablePassthrough.assign(parsed=lambda _: None)
|
|
935
|
-
parser_with_fallback = parser_assign.with_fallbacks(
|
|
936
|
-
[parser_none], exception_key="parsing_error"
|
|
937
|
-
)
|
|
938
|
-
return RunnableMap(raw=llm) | parser_with_fallback
|
|
939
|
-
else:
|
|
940
|
-
return llm | output_parser
|
|
941
|
-
|
|
942
643
|
@property
|
|
943
644
|
def _identifying_params(self) -> Dict[str, Any]:
|
|
944
645
|
"""Get the identifying parameters."""
|
|
@@ -980,6 +681,8 @@ class AzureChatOpenAI(BaseChatOpenAI):
|
|
|
980
681
|
response: Union[dict, openai.BaseModel],
|
|
981
682
|
generation_info: Optional[Dict] = None,
|
|
982
683
|
) -> ChatResult:
|
|
684
|
+
chat_result = super()._create_chat_result(response, generation_info)
|
|
685
|
+
|
|
983
686
|
if not isinstance(response, dict):
|
|
984
687
|
response = response.model_dump()
|
|
985
688
|
for res in response["choices"]:
|
|
@@ -988,7 +691,6 @@ class AzureChatOpenAI(BaseChatOpenAI):
|
|
|
988
691
|
"Azure has not provided the response due to a content filter "
|
|
989
692
|
"being triggered"
|
|
990
693
|
)
|
|
991
|
-
chat_result = super()._create_chat_result(response, generation_info)
|
|
992
694
|
|
|
993
695
|
if "model" in response:
|
|
994
696
|
model = response["model"]
|
|
@@ -33,6 +33,7 @@ from urllib.parse import urlparse
|
|
|
33
33
|
|
|
34
34
|
import openai
|
|
35
35
|
import tiktoken
|
|
36
|
+
from langchain_core._api.deprecation import deprecated
|
|
36
37
|
from langchain_core.callbacks import (
|
|
37
38
|
AsyncCallbackManagerForLLMRun,
|
|
38
39
|
CallbackManagerForLLMRun,
|
|
@@ -62,10 +63,13 @@ from langchain_core.messages import (
|
|
|
62
63
|
ToolMessage,
|
|
63
64
|
ToolMessageChunk,
|
|
64
65
|
)
|
|
65
|
-
from langchain_core.messages.ai import
|
|
66
|
+
from langchain_core.messages.ai import (
|
|
67
|
+
InputTokenDetails,
|
|
68
|
+
OutputTokenDetails,
|
|
69
|
+
UsageMetadata,
|
|
70
|
+
)
|
|
66
71
|
from langchain_core.messages.tool import tool_call_chunk
|
|
67
72
|
from langchain_core.output_parsers import JsonOutputParser, PydanticOutputParser
|
|
68
|
-
from langchain_core.output_parsers.base import OutputParserLike
|
|
69
73
|
from langchain_core.output_parsers.openai_tools import (
|
|
70
74
|
JsonOutputKeyToolsParser,
|
|
71
75
|
PydanticToolsParser,
|
|
@@ -86,7 +90,7 @@ from langchain_core.utils.pydantic import (
|
|
|
86
90
|
TypeBaseModel,
|
|
87
91
|
is_basemodel_subclass,
|
|
88
92
|
)
|
|
89
|
-
from langchain_core.utils.utils import
|
|
93
|
+
from langchain_core.utils.utils import _build_model_kwargs, from_env, secret_from_env
|
|
90
94
|
from pydantic import BaseModel, ConfigDict, Field, SecretStr, model_validator
|
|
91
95
|
from typing_extensions import Self
|
|
92
96
|
|
|
@@ -286,16 +290,10 @@ def _convert_chunk_to_generation_chunk(
|
|
|
286
290
|
) -> Optional[ChatGenerationChunk]:
|
|
287
291
|
token_usage = chunk.get("usage")
|
|
288
292
|
choices = chunk.get("choices", [])
|
|
293
|
+
|
|
289
294
|
usage_metadata: Optional[UsageMetadata] = (
|
|
290
|
-
|
|
291
|
-
input_tokens=token_usage.get("prompt_tokens", 0),
|
|
292
|
-
output_tokens=token_usage.get("completion_tokens", 0),
|
|
293
|
-
total_tokens=token_usage.get("total_tokens", 0),
|
|
294
|
-
)
|
|
295
|
-
if token_usage
|
|
296
|
-
else None
|
|
295
|
+
_create_usage_metadata(token_usage) if token_usage else None
|
|
297
296
|
)
|
|
298
|
-
|
|
299
297
|
if len(choices) == 0:
|
|
300
298
|
# logprobs is implicitly None
|
|
301
299
|
generation_chunk = ChatGenerationChunk(
|
|
@@ -332,6 +330,33 @@ def _convert_chunk_to_generation_chunk(
|
|
|
332
330
|
return generation_chunk
|
|
333
331
|
|
|
334
332
|
|
|
333
|
+
def _update_token_usage(
|
|
334
|
+
overall_token_usage: Union[int, dict], new_usage: Union[int, dict]
|
|
335
|
+
) -> Union[int, dict]:
|
|
336
|
+
# Token usage is either ints or dictionaries
|
|
337
|
+
# `reasoning_tokens` is nested inside `completion_tokens_details`
|
|
338
|
+
if isinstance(new_usage, int):
|
|
339
|
+
if not isinstance(overall_token_usage, int):
|
|
340
|
+
raise ValueError(
|
|
341
|
+
f"Got different types for token usage: "
|
|
342
|
+
f"{type(new_usage)} and {type(overall_token_usage)}"
|
|
343
|
+
)
|
|
344
|
+
return new_usage + overall_token_usage
|
|
345
|
+
elif isinstance(new_usage, dict):
|
|
346
|
+
if not isinstance(overall_token_usage, dict):
|
|
347
|
+
raise ValueError(
|
|
348
|
+
f"Got different types for token usage: "
|
|
349
|
+
f"{type(new_usage)} and {type(overall_token_usage)}"
|
|
350
|
+
)
|
|
351
|
+
return {
|
|
352
|
+
k: _update_token_usage(overall_token_usage.get(k, 0), v)
|
|
353
|
+
for k, v in new_usage.items()
|
|
354
|
+
}
|
|
355
|
+
else:
|
|
356
|
+
warnings.warn(f"Unexpected type for token usage: {type(new_usage)}")
|
|
357
|
+
return new_usage
|
|
358
|
+
|
|
359
|
+
|
|
335
360
|
class _FunctionCall(TypedDict):
|
|
336
361
|
name: str
|
|
337
362
|
|
|
@@ -413,11 +438,11 @@ class BaseChatOpenAI(BaseChatModel):
|
|
|
413
438
|
default_query: Union[Mapping[str, object], None] = None
|
|
414
439
|
# Configure a custom httpx client. See the
|
|
415
440
|
# [httpx documentation](https://www.python-httpx.org/api/#client) for more details.
|
|
416
|
-
http_client: Union[Any, None] = None
|
|
441
|
+
http_client: Union[Any, None] = Field(default=None, exclude=True)
|
|
417
442
|
"""Optional httpx.Client. Only used for sync invocations. Must specify
|
|
418
443
|
http_async_client as well if you'd like a custom client for async invocations.
|
|
419
444
|
"""
|
|
420
|
-
http_async_client: Union[Any, None] = None
|
|
445
|
+
http_async_client: Union[Any, None] = Field(default=None, exclude=True)
|
|
421
446
|
"""Optional httpx.AsyncClient. Only used for async invocations. Must specify
|
|
422
447
|
http_client as well if you'd like a custom client for sync invocations."""
|
|
423
448
|
stop: Optional[Union[List[str], str]] = Field(default=None, alias="stop_sequences")
|
|
@@ -427,6 +452,23 @@ class BaseChatOpenAI(BaseChatModel):
|
|
|
427
452
|
making requests to OpenAI compatible APIs, such as vLLM."""
|
|
428
453
|
include_response_headers: bool = False
|
|
429
454
|
"""Whether to include response headers in the output message response_metadata."""
|
|
455
|
+
disabled_params: Optional[Dict[str, Any]] = Field(default=None)
|
|
456
|
+
"""Parameters of the OpenAI client or chat.completions endpoint that should be
|
|
457
|
+
disabled for the given model.
|
|
458
|
+
|
|
459
|
+
Should be specified as ``{"param": None | ['val1', 'val2']}`` where the key is the
|
|
460
|
+
parameter and the value is either None, meaning that parameter should never be
|
|
461
|
+
used, or it's a list of disabled values for the parameter.
|
|
462
|
+
|
|
463
|
+
For example, older models may not support the 'parallel_tool_calls' parameter at
|
|
464
|
+
all, in which case ``disabled_params={"parallel_tool_calls: None}`` can ben passed
|
|
465
|
+
in.
|
|
466
|
+
|
|
467
|
+
If a parameter is disabled then it will not be used by default in any methods, e.g.
|
|
468
|
+
in :meth:`~langchain_openai.chat_models.base.ChatOpenAI.with_structured_output`.
|
|
469
|
+
However this does not prevent a user from directly passed in the parameter during
|
|
470
|
+
invocation.
|
|
471
|
+
"""
|
|
430
472
|
|
|
431
473
|
model_config = ConfigDict(populate_by_name=True)
|
|
432
474
|
|
|
@@ -435,10 +477,7 @@ class BaseChatOpenAI(BaseChatModel):
|
|
|
435
477
|
def build_extra(cls, values: Dict[str, Any]) -> Any:
|
|
436
478
|
"""Build extra kwargs from additional params that were passed in."""
|
|
437
479
|
all_required_field_names = get_pydantic_field_names(cls)
|
|
438
|
-
|
|
439
|
-
values["model_kwargs"] = build_extra_kwargs(
|
|
440
|
-
extra, values, all_required_field_names
|
|
441
|
-
)
|
|
480
|
+
values = _build_model_kwargs(values, all_required_field_names)
|
|
442
481
|
return values
|
|
443
482
|
|
|
444
483
|
@model_validator(mode="after")
|
|
@@ -545,7 +584,9 @@ class BaseChatOpenAI(BaseChatModel):
|
|
|
545
584
|
if token_usage is not None:
|
|
546
585
|
for k, v in token_usage.items():
|
|
547
586
|
if k in overall_token_usage:
|
|
548
|
-
overall_token_usage[k]
|
|
587
|
+
overall_token_usage[k] = _update_token_usage(
|
|
588
|
+
overall_token_usage[k], v
|
|
589
|
+
)
|
|
549
590
|
else:
|
|
550
591
|
overall_token_usage[k] = v
|
|
551
592
|
if system_fingerprint is None:
|
|
@@ -675,15 +716,11 @@ class BaseChatOpenAI(BaseChatModel):
|
|
|
675
716
|
if response_dict.get("error"):
|
|
676
717
|
raise ValueError(response_dict.get("error"))
|
|
677
718
|
|
|
678
|
-
token_usage = response_dict.get("usage"
|
|
719
|
+
token_usage = response_dict.get("usage")
|
|
679
720
|
for res in response_dict["choices"]:
|
|
680
721
|
message = _convert_dict_to_message(res["message"])
|
|
681
722
|
if token_usage and isinstance(message, AIMessage):
|
|
682
|
-
message.usage_metadata =
|
|
683
|
-
"input_tokens": token_usage.get("prompt_tokens", 0),
|
|
684
|
-
"output_tokens": token_usage.get("completion_tokens", 0),
|
|
685
|
-
"total_tokens": token_usage.get("total_tokens", 0),
|
|
686
|
-
}
|
|
723
|
+
message.usage_metadata = _create_usage_metadata(token_usage)
|
|
687
724
|
generation_info = generation_info or {}
|
|
688
725
|
generation_info["finish_reason"] = (
|
|
689
726
|
res.get("finish_reason")
|
|
@@ -741,7 +778,7 @@ class BaseChatOpenAI(BaseChatModel):
|
|
|
741
778
|
)
|
|
742
779
|
return
|
|
743
780
|
if self.include_response_headers:
|
|
744
|
-
raw_response = self.async_client.with_raw_response.create(**payload)
|
|
781
|
+
raw_response = await self.async_client.with_raw_response.create(**payload)
|
|
745
782
|
response = raw_response.parse()
|
|
746
783
|
base_generation_info = {"headers": dict(raw_response.headers)}
|
|
747
784
|
else:
|
|
@@ -938,6 +975,11 @@ class BaseChatOpenAI(BaseChatModel):
|
|
|
938
975
|
num_tokens += 3
|
|
939
976
|
return num_tokens
|
|
940
977
|
|
|
978
|
+
@deprecated(
|
|
979
|
+
since="0.2.1",
|
|
980
|
+
alternative="langchain_openai.chat_models.base.ChatOpenAI.bind_tools",
|
|
981
|
+
removal="0.3.0",
|
|
982
|
+
)
|
|
941
983
|
def bind_functions(
|
|
942
984
|
self,
|
|
943
985
|
functions: Sequence[Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool]],
|
|
@@ -1005,22 +1047,18 @@ class BaseChatOpenAI(BaseChatModel):
|
|
|
1005
1047
|
|
|
1006
1048
|
Assumes model is compatible with OpenAI tool-calling API.
|
|
1007
1049
|
|
|
1008
|
-
.. versionchanged:: 0.1.21
|
|
1009
|
-
|
|
1010
|
-
Support for ``strict`` argument added.
|
|
1011
|
-
|
|
1012
1050
|
Args:
|
|
1013
1051
|
tools: A list of tool definitions to bind to this chat model.
|
|
1014
1052
|
Supports any tool definition handled by
|
|
1015
1053
|
:meth:`langchain_core.utils.function_calling.convert_to_openai_tool`.
|
|
1016
|
-
tool_choice: Which tool to require the model to call.
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1054
|
+
tool_choice: Which tool to require the model to call. Options are:
|
|
1055
|
+
|
|
1056
|
+
- str of the form ``"<<tool_name>>"``: calls <<tool_name>> tool.
|
|
1057
|
+
- ``"auto"``: automatically selects a tool (including no tool).
|
|
1058
|
+
- ``"none"``: does not call a tool.
|
|
1059
|
+
- ``"any"`` or ``"required"`` or ``True``: force at least one tool to be called.
|
|
1060
|
+
- dict of the form ``{"type": "function", "function": {"name": <<tool_name>>}}``: calls <<tool_name>> tool.
|
|
1061
|
+
- ``False`` or ``None``: no effect, default OpenAI behavior.
|
|
1024
1062
|
strict: If True, model output is guaranteed to exactly match the JSON Schema
|
|
1025
1063
|
provided in the tool definition. If True, the input schema will be
|
|
1026
1064
|
validated according to
|
|
@@ -1028,11 +1066,13 @@ class BaseChatOpenAI(BaseChatModel):
|
|
|
1028
1066
|
If False, input schema will not be validated and model output will not
|
|
1029
1067
|
be validated.
|
|
1030
1068
|
If None, ``strict`` argument will not be passed to the model.
|
|
1069
|
+
kwargs: Any additional parameters are passed directly to
|
|
1070
|
+
:meth:`~langchain_openai.chat_models.base.ChatOpenAI.bind`.
|
|
1031
1071
|
|
|
1032
|
-
|
|
1072
|
+
.. versionchanged:: 0.1.21
|
|
1073
|
+
|
|
1074
|
+
Support for ``strict`` argument added.
|
|
1033
1075
|
|
|
1034
|
-
kwargs: Any additional parameters are passed directly to
|
|
1035
|
-
``self.bind(**kwargs)``.
|
|
1036
1076
|
""" # noqa: E501
|
|
1037
1077
|
|
|
1038
1078
|
formatted_tools = [
|
|
@@ -1168,12 +1208,12 @@ class BaseChatOpenAI(BaseChatModel):
|
|
|
1168
1208
|
Support for ``strict`` argument added.
|
|
1169
1209
|
Support for ``method`` = "json_schema" added.
|
|
1170
1210
|
|
|
1171
|
-
.. note:: Planned breaking changes in version `0.
|
|
1211
|
+
.. note:: Planned breaking changes in version `0.3.0`
|
|
1172
1212
|
|
|
1173
1213
|
- ``method`` default will be changed to "json_schema" from
|
|
1174
1214
|
"function_calling".
|
|
1175
1215
|
- ``strict`` will default to True when ``method`` is
|
|
1176
|
-
"function_calling" as of version `0.
|
|
1216
|
+
"function_calling" as of version `0.3.0`.
|
|
1177
1217
|
|
|
1178
1218
|
|
|
1179
1219
|
.. dropdown:: Example: schema=Pydantic class, method="function_calling", include_raw=False, strict=True
|
|
@@ -1369,14 +1409,13 @@ class BaseChatOpenAI(BaseChatModel):
|
|
|
1369
1409
|
"Received None."
|
|
1370
1410
|
)
|
|
1371
1411
|
tool_name = convert_to_openai_tool(schema)["function"]["name"]
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
tool_choice=tool_name,
|
|
1375
|
-
parallel_tool_calls=False,
|
|
1376
|
-
strict=strict,
|
|
1412
|
+
bind_kwargs = self._filter_disabled_params(
|
|
1413
|
+
tool_choice=tool_name, parallel_tool_calls=False, strict=strict
|
|
1377
1414
|
)
|
|
1415
|
+
|
|
1416
|
+
llm = self.bind_tools([schema], **bind_kwargs)
|
|
1378
1417
|
if is_pydantic_schema:
|
|
1379
|
-
output_parser:
|
|
1418
|
+
output_parser: Runnable = PydanticToolsParser(
|
|
1380
1419
|
tools=[schema], # type: ignore[list-item]
|
|
1381
1420
|
first_tool_only=True, # type: ignore[list-item]
|
|
1382
1421
|
)
|
|
@@ -1400,11 +1439,12 @@ class BaseChatOpenAI(BaseChatModel):
|
|
|
1400
1439
|
strict = strict if strict is not None else True
|
|
1401
1440
|
response_format = _convert_to_openai_response_format(schema, strict=strict)
|
|
1402
1441
|
llm = self.bind(response_format=response_format)
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1442
|
+
if is_pydantic_schema:
|
|
1443
|
+
output_parser = _oai_structured_outputs_parser.with_types(
|
|
1444
|
+
output_type=cast(type, schema)
|
|
1445
|
+
)
|
|
1446
|
+
else:
|
|
1447
|
+
output_parser = JsonOutputParser()
|
|
1408
1448
|
else:
|
|
1409
1449
|
raise ValueError(
|
|
1410
1450
|
f"Unrecognized method argument. Expected one of 'function_calling' or "
|
|
@@ -1423,6 +1463,21 @@ class BaseChatOpenAI(BaseChatModel):
|
|
|
1423
1463
|
else:
|
|
1424
1464
|
return llm | output_parser
|
|
1425
1465
|
|
|
1466
|
+
def _filter_disabled_params(self, **kwargs: Any) -> Dict[str, Any]:
|
|
1467
|
+
if not self.disabled_params:
|
|
1468
|
+
return kwargs
|
|
1469
|
+
filtered = {}
|
|
1470
|
+
for k, v in kwargs.items():
|
|
1471
|
+
# Skip param
|
|
1472
|
+
if k in self.disabled_params and (
|
|
1473
|
+
self.disabled_params[k] is None or v in self.disabled_params[k]
|
|
1474
|
+
):
|
|
1475
|
+
continue
|
|
1476
|
+
# Keep param
|
|
1477
|
+
else:
|
|
1478
|
+
filtered[k] = v
|
|
1479
|
+
return filtered
|
|
1480
|
+
|
|
1426
1481
|
|
|
1427
1482
|
class ChatOpenAI(BaseChatOpenAI):
|
|
1428
1483
|
"""OpenAI chat model integration.
|
|
@@ -2081,7 +2136,7 @@ def _oai_structured_outputs_parser(ai_msg: AIMessage) -> PydanticBaseModel:
|
|
|
2081
2136
|
else:
|
|
2082
2137
|
raise ValueError(
|
|
2083
2138
|
"Structured Output response does not have a 'parsed' field nor a 'refusal' "
|
|
2084
|
-
"field."
|
|
2139
|
+
"field. Received message:\n\n{ai_msg}"
|
|
2085
2140
|
)
|
|
2086
2141
|
|
|
2087
2142
|
|
|
@@ -2096,3 +2151,36 @@ class OpenAIRefusalError(Exception):
|
|
|
2096
2151
|
|
|
2097
2152
|
.. versionadded:: 0.1.21
|
|
2098
2153
|
"""
|
|
2154
|
+
|
|
2155
|
+
|
|
2156
|
+
def _create_usage_metadata(oai_token_usage: dict) -> UsageMetadata:
|
|
2157
|
+
input_tokens = oai_token_usage.get("prompt_tokens", 0)
|
|
2158
|
+
output_tokens = oai_token_usage.get("completion_tokens", 0)
|
|
2159
|
+
total_tokens = oai_token_usage.get("total_tokens", input_tokens + output_tokens)
|
|
2160
|
+
input_token_details: dict = {
|
|
2161
|
+
"audio": (oai_token_usage.get("prompt_tokens_details") or {}).get(
|
|
2162
|
+
"audio_tokens"
|
|
2163
|
+
),
|
|
2164
|
+
"cache_read": (oai_token_usage.get("prompt_tokens_details") or {}).get(
|
|
2165
|
+
"cached_tokens"
|
|
2166
|
+
),
|
|
2167
|
+
}
|
|
2168
|
+
output_token_details: dict = {
|
|
2169
|
+
"audio": (oai_token_usage.get("completion_tokens_details") or {}).get(
|
|
2170
|
+
"audio_tokens"
|
|
2171
|
+
),
|
|
2172
|
+
"reasoning": (oai_token_usage.get("completion_tokens_details") or {}).get(
|
|
2173
|
+
"reasoning_tokens"
|
|
2174
|
+
),
|
|
2175
|
+
}
|
|
2176
|
+
return UsageMetadata(
|
|
2177
|
+
input_tokens=input_tokens,
|
|
2178
|
+
output_tokens=output_tokens,
|
|
2179
|
+
total_tokens=total_tokens,
|
|
2180
|
+
input_token_details=InputTokenDetails(
|
|
2181
|
+
**{k: v for k, v in input_token_details.items() if v is not None}
|
|
2182
|
+
),
|
|
2183
|
+
output_token_details=OutputTokenDetails(
|
|
2184
|
+
**{k: v for k, v in output_token_details.items() if v is not None}
|
|
2185
|
+
),
|
|
2186
|
+
)
|
|
@@ -254,14 +254,14 @@ class OpenAIEmbeddings(BaseModel, Embeddings):
|
|
|
254
254
|
retry_max_seconds: int = 20
|
|
255
255
|
"""Max number of seconds to wait between retries"""
|
|
256
256
|
http_client: Union[Any, None] = None
|
|
257
|
-
"""Optional httpx.Client. Only used for sync invocations. Must specify
|
|
257
|
+
"""Optional httpx.Client. Only used for sync invocations. Must specify
|
|
258
258
|
http_async_client as well if you'd like a custom client for async invocations.
|
|
259
259
|
"""
|
|
260
260
|
http_async_client: Union[Any, None] = None
|
|
261
|
-
"""Optional httpx.AsyncClient. Only used for async invocations. Must specify
|
|
261
|
+
"""Optional httpx.AsyncClient. Only used for async invocations. Must specify
|
|
262
262
|
http_client as well if you'd like a custom client for sync invocations."""
|
|
263
263
|
check_embedding_ctx_length: bool = True
|
|
264
|
-
"""Whether to check the token length of inputs and automatically split inputs
|
|
264
|
+
"""Whether to check the token length of inputs and automatically split inputs
|
|
265
265
|
longer than embedding_ctx_length."""
|
|
266
266
|
|
|
267
267
|
model_config = ConfigDict(
|
|
@@ -558,7 +558,7 @@ class OpenAIEmbeddings(BaseModel, Embeddings):
|
|
|
558
558
|
return [e if e is not None else await empty_embedding() for e in embeddings]
|
|
559
559
|
|
|
560
560
|
def embed_documents(
|
|
561
|
-
self, texts: List[str], chunk_size:
|
|
561
|
+
self, texts: List[str], chunk_size: int | None = None
|
|
562
562
|
) -> List[List[float]]:
|
|
563
563
|
"""Call out to OpenAI's embedding endpoint for embedding search docs.
|
|
564
564
|
|
|
@@ -570,10 +570,13 @@ class OpenAIEmbeddings(BaseModel, Embeddings):
|
|
|
570
570
|
Returns:
|
|
571
571
|
List of embeddings, one for each text.
|
|
572
572
|
"""
|
|
573
|
+
chunk_size_ = chunk_size or self.chunk_size
|
|
573
574
|
if not self.check_embedding_ctx_length:
|
|
574
575
|
embeddings: List[List[float]] = []
|
|
575
|
-
for
|
|
576
|
-
response = self.client.create(
|
|
576
|
+
for i in range(0, len(texts), self.chunk_size):
|
|
577
|
+
response = self.client.create(
|
|
578
|
+
input=texts[i : i + chunk_size_], **self._invocation_params
|
|
579
|
+
)
|
|
577
580
|
if not isinstance(response, dict):
|
|
578
581
|
response = response.dict()
|
|
579
582
|
embeddings.extend(r["embedding"] for r in response["data"])
|
|
@@ -585,7 +588,7 @@ class OpenAIEmbeddings(BaseModel, Embeddings):
|
|
|
585
588
|
return self._get_len_safe_embeddings(texts, engine=engine)
|
|
586
589
|
|
|
587
590
|
async def aembed_documents(
|
|
588
|
-
self, texts: List[str], chunk_size:
|
|
591
|
+
self, texts: List[str], chunk_size: int | None = None
|
|
589
592
|
) -> List[List[float]]:
|
|
590
593
|
"""Call out to OpenAI's embedding endpoint async for embedding search docs.
|
|
591
594
|
|
|
@@ -597,11 +600,12 @@ class OpenAIEmbeddings(BaseModel, Embeddings):
|
|
|
597
600
|
Returns:
|
|
598
601
|
List of embeddings, one for each text.
|
|
599
602
|
"""
|
|
603
|
+
chunk_size_ = chunk_size or self.chunk_size
|
|
600
604
|
if not self.check_embedding_ctx_length:
|
|
601
605
|
embeddings: List[List[float]] = []
|
|
602
|
-
for
|
|
606
|
+
for i in range(0, len(texts), chunk_size_):
|
|
603
607
|
response = await self.async_client.create(
|
|
604
|
-
input=
|
|
608
|
+
input=texts[i : i + chunk_size_], **self._invocation_params
|
|
605
609
|
)
|
|
606
610
|
if not isinstance(response, dict):
|
|
607
611
|
response = response.dict()
|
|
@@ -27,7 +27,7 @@ from langchain_core.callbacks import (
|
|
|
27
27
|
from langchain_core.language_models.llms import BaseLLM
|
|
28
28
|
from langchain_core.outputs import Generation, GenerationChunk, LLMResult
|
|
29
29
|
from langchain_core.utils import get_pydantic_field_names
|
|
30
|
-
from langchain_core.utils.utils import
|
|
30
|
+
from langchain_core.utils.utils import _build_model_kwargs, from_env, secret_from_env
|
|
31
31
|
from pydantic import ConfigDict, Field, SecretStr, model_validator
|
|
32
32
|
from typing_extensions import Self
|
|
33
33
|
|
|
@@ -160,10 +160,7 @@ class BaseOpenAI(BaseLLM):
|
|
|
160
160
|
def build_extra(cls, values: Dict[str, Any]) -> Any:
|
|
161
161
|
"""Build extra kwargs from additional params that were passed in."""
|
|
162
162
|
all_required_field_names = get_pydantic_field_names(cls)
|
|
163
|
-
|
|
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)
|
|
167
164
|
return values
|
|
168
165
|
|
|
169
166
|
@model_validator(mode="after")
|
|
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[tool.poetry]
|
|
6
6
|
name = "langchain-openai"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "An integration package connecting OpenAI and LangChain"
|
|
9
9
|
authors = []
|
|
10
10
|
readme = "README.md"
|
|
@@ -23,7 +23,7 @@ ignore_missing_imports = true
|
|
|
23
23
|
|
|
24
24
|
[tool.poetry.dependencies]
|
|
25
25
|
python = ">=3.9,<4.0"
|
|
26
|
-
langchain-core =
|
|
26
|
+
langchain-core = "^0.3.9"
|
|
27
27
|
openai = "^1.40.0"
|
|
28
28
|
tiktoken = ">=0.7,<1"
|
|
29
29
|
|
|
@@ -41,6 +41,7 @@ omit = [ "tests/*",]
|
|
|
41
41
|
addopts = "--snapshot-warn-unused --strict-markers --strict-config --durations=5 --cov=langchain_openai"
|
|
42
42
|
markers = [ "requires: mark tests as requiring a specific library", "asyncio: mark tests as requiring asyncio", "compile: mark placeholder test used to compile integration tests without running them", "scheduled: mark tests to run in scheduled testing",]
|
|
43
43
|
asyncio_mode = "auto"
|
|
44
|
+
filterwarnings = [ "ignore::langchain_core._api.beta_decorator.LangChainBetaWarning",]
|
|
44
45
|
|
|
45
46
|
[tool.poetry.group.test]
|
|
46
47
|
optional = true
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{langchain_openai-0.2.0.dev2 → langchain_openai-0.2.2}/langchain_openai/chat_models/__init__.py
RENAMED
|
File without changes
|
{langchain_openai-0.2.0.dev2 → langchain_openai-0.2.2}/langchain_openai/embeddings/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{langchain_openai-0.2.0.dev2 → langchain_openai-0.2.2}/langchain_openai/output_parsers/__init__.py
RENAMED
|
File without changes
|
{langchain_openai-0.2.0.dev2 → langchain_openai-0.2.2}/langchain_openai/output_parsers/tools.py
RENAMED
|
File without changes
|
|
File without changes
|