importimportlibimportjsonimportosfromtypingimportAny,Optionalfromlangchain_core._apiimportbetafromlangchain_core.load.mappingimport(_JS_SERIALIZABLE_MAPPING,_OG_SERIALIZABLE_MAPPING,OLD_CORE_NAMESPACES_MAPPING,SERIALIZABLE_MAPPING,)fromlangchain_core.load.serializableimportSerializableDEFAULT_NAMESPACES=["langchain","langchain_core","langchain_community","langchain_anthropic","langchain_groq","langchain_google_genai","langchain_aws","langchain_openai","langchain_google_vertexai","langchain_mistralai","langchain_fireworks","langchain_xai","langchain_sambanova",]# Namespaces for which only deserializing via the SERIALIZABLE_MAPPING is allowed.# Load by path is not allowed.DISALLOW_LOAD_FROM_PATH=["langchain_community","langchain",]ALL_SERIALIZABLE_MAPPINGS={**SERIALIZABLE_MAPPING,**OLD_CORE_NAMESPACES_MAPPING,**_OG_SERIALIZABLE_MAPPING,**_JS_SERIALIZABLE_MAPPING,}
[docs]classReviver:"""Reviver for JSON objects."""
[docs]def__init__(self,secrets_map:Optional[dict[str,str]]=None,valid_namespaces:Optional[list[str]]=None,secrets_from_env:bool=True,additional_import_mappings:Optional[dict[tuple[str,...],tuple[str,...]]]=None,)->None:"""Initialize the reviver. Args: secrets_map: A map of secrets to load. If a secret is not found in the map, it will be loaded from the environment if `secrets_from_env` is True. Defaults to None. valid_namespaces: A list of additional namespaces (modules) to allow to be deserialized. Defaults to None. secrets_from_env: Whether to load secrets from the environment. Defaults to True. additional_import_mappings: A dictionary of additional namespace mappings You can use this to override default mappings or add new mappings. Defaults to None. """self.secrets_from_env=secrets_from_envself.secrets_map=secrets_mapor{}# By default, only support langchain, but user can pass in additional namespacesself.valid_namespaces=([*DEFAULT_NAMESPACES,*valid_namespaces]ifvalid_namespaceselseDEFAULT_NAMESPACES)self.additional_import_mappings=additional_import_mappingsor{}self.import_mappings=({**ALL_SERIALIZABLE_MAPPINGS,**self.additional_import_mappings,}ifself.additional_import_mappingselseALL_SERIALIZABLE_MAPPINGS)
def__call__(self,value:dict[str,Any])->Any:if(value.get("lc")==1andvalue.get("type")=="secret"andvalue.get("id")isnotNone):[key]=value["id"]ifkeyinself.secrets_map:returnself.secrets_map[key]else:ifself.secrets_from_envandkeyinos.environandos.environ[key]:returnos.environ[key]returnNoneif(value.get("lc")==1andvalue.get("type")=="not_implemented"andvalue.get("id")isnotNone):msg=("Trying to load an object that doesn't implement "f"serialization: {value}")raiseNotImplementedError(msg)if(value.get("lc")==1andvalue.get("type")=="constructor"andvalue.get("id")isnotNone):[*namespace,name]=value["id"]mapping_key=tuple(value["id"])if(namespace[0]notinself.valid_namespaces# The root namespace ["langchain"] is not a valid identifier.ornamespace==["langchain"]):msg=f"Invalid namespace: {value}"raiseValueError(msg)# Has explicit import path.elifmapping_keyinself.import_mappings:import_path=self.import_mappings[mapping_key]# Split into module and nameimport_dir,name=import_path[:-1],import_path[-1]# Import modulemod=importlib.import_module(".".join(import_dir))elifnamespace[0]inDISALLOW_LOAD_FROM_PATH:msg=("Trying to deserialize something that cannot ""be deserialized in current version of langchain-core: "f"{mapping_key}.")raiseValueError(msg)# Otherwise, treat namespace as path.else:mod=importlib.import_module(".".join(namespace))cls=getattr(mod,name)# The class must be a subclass of Serializable.ifnotissubclass(cls,Serializable):msg=f"Invalid namespace: {value}"raiseValueError(msg)# We don't need to recurse on kwargs# as json.loads will do that for us.kwargs=value.get("kwargs",{})returncls(**kwargs)returnvalue
[docs]@beta()defloads(text:str,*,secrets_map:Optional[dict[str,str]]=None,valid_namespaces:Optional[list[str]]=None,secrets_from_env:bool=True,additional_import_mappings:Optional[dict[tuple[str,...],tuple[str,...]]]=None,)->Any:"""Revive a LangChain class from a JSON string. Equivalent to `load(json.loads(text))`. Args: text: The string to load. secrets_map: A map of secrets to load. If a secret is not found in the map, it will be loaded from the environment if `secrets_from_env` is True. Defaults to None. valid_namespaces: A list of additional namespaces (modules) to allow to be deserialized. Defaults to None. secrets_from_env: Whether to load secrets from the environment. Defaults to True. additional_import_mappings: A dictionary of additional namespace mappings You can use this to override default mappings or add new mappings. Defaults to None. Returns: Revived LangChain objects. """returnjson.loads(text,object_hook=Reviver(secrets_map,valid_namespaces,secrets_from_env,additional_import_mappings),)
[docs]@beta()defload(obj:Any,*,secrets_map:Optional[dict[str,str]]=None,valid_namespaces:Optional[list[str]]=None,secrets_from_env:bool=True,additional_import_mappings:Optional[dict[tuple[str,...],tuple[str,...]]]=None,)->Any:"""Revive a LangChain class from a JSON object. Use this if you already have a parsed JSON object, eg. from `json.load` or `orjson.loads`. Args: obj: The object to load. secrets_map: A map of secrets to load. If a secret is not found in the map, it will be loaded from the environment if `secrets_from_env` is True. Defaults to None. valid_namespaces: A list of additional namespaces (modules) to allow to be deserialized. Defaults to None. secrets_from_env: Whether to load secrets from the environment. Defaults to True. additional_import_mappings: A dictionary of additional namespace mappings You can use this to override default mappings or add new mappings. Defaults to None. Returns: Revived LangChain objects. """reviver=Reviver(secrets_map,valid_namespaces,secrets_from_env,additional_import_mappings)def_load(obj:Any)->Any:ifisinstance(obj,dict):# Need to revive leaf nodes before reviving this nodeloaded_obj={k:_load(v)fork,vinobj.items()}returnreviver(loaded_obj)ifisinstance(obj,list):return[_load(o)foroinobj]returnobjreturn_load(obj)