mirror of
https://github.com/Mintplex-Labs/langchain-python.git
synced 2026-07-25 12:35:29 -04:00
e194dc5306
<!-- Thank you for contributing to LangChain! Your PR will appear in our release under the title you set. Please make sure it highlights your valuable contribution. Replace this with a description of the change, the issue it fixes (if applicable), and relevant context. List any dependencies required for this change. After you're done, someone will review your PR. They may suggest improvements. If no one reviews your PR within a few days, feel free to @-mention the same people again, as notifications can get lost. Finally, we'd love to show appreciation for your contribution - if you'd like us to shout you out on Twitter, please also include your handle! --> <!-- Remove if not applicable --> Fixes # (issue) #### Before submitting <!-- If you're adding a new integration, please include: 1. a test for the integration - favor unit tests that does not rely on network access. 2. an example notebook showing its use See contribution guidelines for more information on how to write tests, lint etc: https://github.com/hwchase17/langchain/blob/master/.github/CONTRIBUTING.md --> #### Who can review? Tag maintainers/contributors who might be interested: <!-- For a quicker response, figure out the right person to tag with @ @hwchase17 - project lead Tracing / Callbacks - @agola11 Async - @agola11 DataLoaders - @eyurtsev Models - @hwchase17 - @agola11 Agents / Tools / Toolkits - @hwchase17 VectorStores / Retrievers / Memory - @dev2049 -->
139 lines
3.9 KiB
Python
139 lines
3.9 KiB
Python
from abc import ABC
|
|
from typing import Any, Dict, List, Literal, TypedDict, Union, cast
|
|
|
|
from pydantic import BaseModel, PrivateAttr
|
|
|
|
|
|
class BaseSerialized(TypedDict):
|
|
lc: int
|
|
id: List[str]
|
|
|
|
|
|
class SerializedConstructor(BaseSerialized):
|
|
type: Literal["constructor"]
|
|
kwargs: Dict[str, Any]
|
|
|
|
|
|
class SerializedSecret(BaseSerialized):
|
|
type: Literal["secret"]
|
|
|
|
|
|
class SerializedNotImplemented(BaseSerialized):
|
|
type: Literal["not_implemented"]
|
|
|
|
|
|
class Serializable(BaseModel, ABC):
|
|
@property
|
|
def lc_serializable(self) -> bool:
|
|
"""
|
|
Return whether or not the class is serializable.
|
|
"""
|
|
return False
|
|
|
|
@property
|
|
def lc_namespace(self) -> List[str]:
|
|
"""
|
|
Return the namespace of the langchain object.
|
|
eg. ["langchain", "llms", "openai"]
|
|
"""
|
|
return self.__class__.__module__.split(".")
|
|
|
|
@property
|
|
def lc_secrets(self) -> Dict[str, str]:
|
|
"""
|
|
Return a map of constructor argument names to secret ids.
|
|
eg. {"openai_api_key": "OPENAI_API_KEY"}
|
|
"""
|
|
return dict()
|
|
|
|
@property
|
|
def lc_attributes(self) -> Dict:
|
|
"""
|
|
Return a list of attribute names that should be included in the
|
|
serialized kwargs. These attributes must be accepted by the
|
|
constructor.
|
|
"""
|
|
return {}
|
|
|
|
class Config:
|
|
extra = "ignore"
|
|
|
|
_lc_kwargs = PrivateAttr(default_factory=dict)
|
|
|
|
def __init__(self, **kwargs: Any) -> None:
|
|
super().__init__(**kwargs)
|
|
self._lc_kwargs = kwargs
|
|
|
|
def to_json(self) -> Union[SerializedConstructor, SerializedNotImplemented]:
|
|
if not self.lc_serializable:
|
|
return self.to_json_not_implemented()
|
|
|
|
secrets = dict()
|
|
# Get latest values for kwargs if there is an attribute with same name
|
|
lc_kwargs = {
|
|
k: getattr(self, k, v)
|
|
for k, v in self._lc_kwargs.items()
|
|
if not (self.__exclude_fields__ or {}).get(k, False) # type: ignore
|
|
}
|
|
|
|
# Merge the lc_secrets and lc_attributes from every class in the MRO
|
|
for cls in [None, *self.__class__.mro()]:
|
|
# Once we get to Serializable, we're done
|
|
if cls is Serializable:
|
|
break
|
|
|
|
# Get a reference to self bound to each class in the MRO
|
|
this = cast(Serializable, self if cls is None else super(cls, self))
|
|
|
|
secrets.update(this.lc_secrets)
|
|
lc_kwargs.update(this.lc_attributes)
|
|
|
|
return {
|
|
"lc": 1,
|
|
"type": "constructor",
|
|
"id": [*self.lc_namespace, self.__class__.__name__],
|
|
"kwargs": lc_kwargs
|
|
if not secrets
|
|
else _replace_secrets(lc_kwargs, secrets),
|
|
}
|
|
|
|
def to_json_not_implemented(self) -> SerializedNotImplemented:
|
|
return to_json_not_implemented(self)
|
|
|
|
|
|
def _replace_secrets(
|
|
root: Dict[Any, Any], secrets_map: Dict[str, str]
|
|
) -> Dict[Any, Any]:
|
|
result = root.copy()
|
|
for path, secret_id in secrets_map.items():
|
|
[*parts, last] = path.split(".")
|
|
current = result
|
|
for part in parts:
|
|
if part not in current:
|
|
break
|
|
current[part] = current[part].copy()
|
|
current = current[part]
|
|
if last in current:
|
|
current[last] = {
|
|
"lc": 1,
|
|
"type": "secret",
|
|
"id": [secret_id],
|
|
}
|
|
return result
|
|
|
|
|
|
def to_json_not_implemented(obj: object) -> SerializedNotImplemented:
|
|
_id: List[str] = []
|
|
try:
|
|
if hasattr(obj, "__name__"):
|
|
_id = [*obj.__module__.split("."), obj.__name__]
|
|
elif hasattr(obj, "__class__"):
|
|
_id = [*obj.__class__.__module__.split("."), obj.__class__.__name__]
|
|
except Exception:
|
|
pass
|
|
return {
|
|
"lc": 1,
|
|
"type": "not_implemented",
|
|
"id": _id,
|
|
}
|