Source code for langchain_community.utilities.zapier
"""Util that can interact with Zapier NLA.Full docs here: https://nla.zapier.com/start/Note: this wrapper currently only implemented the `api_key` auth method for testingand server-side production use cases (using the developer's connected accounts onZapier.com)For use-cases where LangChain + Zapier NLA is powering a user-facing application, andLangChain needs access to the end-user's connected accounts on Zapier.com, you'll needto use oauth. Review the full docs above and reach out to nla@zapier.com fordeveloper support."""importjsonfromtypingimportAny,Dict,List,Optionalimportaiohttpimportrequestsfromlangchain_core.pydantic_v1importBaseModel,root_validatorfromlangchain_core.utilsimportget_from_dict_or_envfromrequestsimportRequest,Session
[docs]classZapierNLAWrapper(BaseModel):"""Wrapper for Zapier NLA. Full docs here: https://nla.zapier.com/start/ This wrapper supports both API Key and OAuth Credential auth methods. API Key is the fastest way to get started using this wrapper. Call this wrapper with either `zapier_nla_api_key` or `zapier_nla_oauth_access_token` arguments, or set the `ZAPIER_NLA_API_KEY` environment variable. If both arguments are set, the Access Token will take precedence. For use-cases where LangChain + Zapier NLA is powering a user-facing application, and LangChain needs access to the end-user's connected accounts on Zapier.com, you'll need to use OAuth. Review the full docs above to learn how to create your own provider and generate credentials. """zapier_nla_api_key:strzapier_nla_oauth_access_token:strzapier_nla_api_base:str="https://nla.zapier.com/api/v1/"classConfig:extra="forbid"def_format_headers(self)->Dict[str,str]:"""Format headers for requests."""headers={"Accept":"application/json","Content-Type":"application/json",}ifself.zapier_nla_oauth_access_token:headers.update({"Authorization":f"Bearer {self.zapier_nla_oauth_access_token}"})else:headers.update({"X-API-Key":self.zapier_nla_api_key})returnheadersdef_get_session(self)->Session:session=requests.Session()session.headers.update(self._format_headers())returnsessionasyncdef_arequest(self,method:str,url:str,**kwargs:Any)->Dict[str,Any]:"""Make an async request."""asyncwithaiohttp.ClientSession(headers=self._format_headers())assession:asyncwithsession.request(method,url,**kwargs)asresponse:response.raise_for_status()returnawaitresponse.json()def_create_action_payload(# type: ignore[no-untyped-def]self,instructions:str,params:Optional[Dict]=None,preview_only=False)->Dict:"""Create a payload for an action."""data=paramsifparamselse{}data.update({"instructions":instructions,})ifpreview_only:data.update({"preview_only":True})returndatadef_create_action_url(self,action_id:str)->str:"""Create a url for an action."""returnself.zapier_nla_api_base+f"exposed/{action_id}/execute/"def_create_action_request(# type: ignore[no-untyped-def]self,action_id:str,instructions:str,params:Optional[Dict]=None,preview_only=False,)->Request:data=self._create_action_payload(instructions,params,preview_only)returnRequest("POST",self._create_action_url(action_id),json=data,)@root_validator(pre=True)defvalidate_environment(cls,values:Dict)->Dict:"""Validate that api key exists in environment."""zapier_nla_api_key_default=None# If there is a oauth_access_key passed in the values# we don't need a nla_api_key it can be blankif"zapier_nla_oauth_access_token"invalues:zapier_nla_api_key_default=""else:values["zapier_nla_oauth_access_token"]=""# we require at least one API Keyzapier_nla_api_key=get_from_dict_or_env(values,"zapier_nla_api_key","ZAPIER_NLA_API_KEY",zapier_nla_api_key_default,)values["zapier_nla_api_key"]=zapier_nla_api_keyreturnvalues
[docs]asyncdefalist(self)->List[Dict]:"""Returns a list of all exposed (enabled) actions associated with current user (associated with the set api_key). Change your exposed actions here: https://nla.zapier.com/demo/start/ The return list can be empty if no actions exposed. Else will contain a list of action objects: [{ "id": str, "description": str, "params": Dict[str, str] }] `params` will always contain an `instructions` key, the only required param. All others optional and if provided will override any AI guesses (see "understanding the AI guessing flow" here: https://nla.zapier.com/api/v1/docs) """response=awaitself._arequest("GET",self.zapier_nla_api_base+"exposed/")returnresponse["results"]
[docs]deflist(self)->List[Dict]:"""Returns a list of all exposed (enabled) actions associated with current user (associated with the set api_key). Change your exposed actions here: https://nla.zapier.com/demo/start/ The return list can be empty if no actions exposed. Else will contain a list of action objects: [{ "id": str, "description": str, "params": Dict[str, str] }] `params` will always contain an `instructions` key, the only required param. All others optional and if provided will override any AI guesses (see "understanding the AI guessing flow" here: https://nla.zapier.com/docs/using-the-api#ai-guessing) """session=self._get_session()try:response=session.get(self.zapier_nla_api_base+"exposed/")response.raise_for_status()exceptrequests.HTTPErrorashttp_err:ifresponse.status_code==401:ifself.zapier_nla_oauth_access_token:raiserequests.HTTPError(f"An unauthorized response occurred. Check that your "f"access token is correct and doesn't need to be "f"refreshed. Err: {http_err}",response=response,)raiserequests.HTTPError(f"An unauthorized response occurred. Check that your api "f"key is correct. Err: {http_err}",response=response,)raisehttp_errreturnresponse.json()["results"]
[docs]defrun(self,action_id:str,instructions:str,params:Optional[Dict]=None)->Dict:"""Executes an action that is identified by action_id, must be exposed (enabled) by the current user (associated with the set api_key). Change your exposed actions here: https://nla.zapier.com/demo/start/ The return JSON is guaranteed to be less than ~500 words (350 tokens) making it safe to inject into the prompt of another LLM call. """session=self._get_session()request=self._create_action_request(action_id,instructions,params)response=session.send(session.prepare_request(request))response.raise_for_status()returnresponse.json()["result"]
[docs]asyncdefarun(self,action_id:str,instructions:str,params:Optional[Dict]=None)->Dict:"""Executes an action that is identified by action_id, must be exposed (enabled) by the current user (associated with the set api_key). Change your exposed actions here: https://nla.zapier.com/demo/start/ The return JSON is guaranteed to be less than ~500 words (350 tokens) making it safe to inject into the prompt of another LLM call. """response=awaitself._arequest("POST",self._create_action_url(action_id),json=self._create_action_payload(instructions,params),)returnresponse["result"]
[docs]defpreview(self,action_id:str,instructions:str,params:Optional[Dict]=None)->Dict:"""Same as run, but instead of actually executing the action, will instead return a preview of params that have been guessed by the AI in case you need to explicitly review before executing."""session=self._get_session()params=paramsifparamselse{}params.update({"preview_only":True})request=self._create_action_request(action_id,instructions,params,True)response=session.send(session.prepare_request(request))response.raise_for_status()returnresponse.json()["input_params"]
[docs]asyncdefapreview(self,action_id:str,instructions:str,params:Optional[Dict]=None)->Dict:"""Same as run, but instead of actually executing the action, will instead return a preview of params that have been guessed by the AI in case you need to explicitly review before executing."""response=awaitself._arequest("POST",self._create_action_url(action_id),json=self._create_action_payload(instructions,params,preview_only=True),)returnresponse["result"]
[docs]defrun_as_str(self,*args,**kwargs)->str:# type: ignore[no-untyped-def]"""Same as run, but returns a stringified version of the JSON for insertting back into an LLM."""data=self.run(*args,**kwargs)returnjson.dumps(data)
[docs]asyncdefarun_as_str(self,*args,**kwargs)->str:# type: ignore[no-untyped-def]"""Same as run, but returns a stringified version of the JSON for insertting back into an LLM."""data=awaitself.arun(*args,**kwargs)returnjson.dumps(data)
[docs]defpreview_as_str(self,*args,**kwargs)->str:# type: ignore[no-untyped-def]"""Same as preview, but returns a stringified version of the JSON for insertting back into an LLM."""data=self.preview(*args,**kwargs)returnjson.dumps(data)
[docs]asyncdefapreview_as_str(# type: ignore[no-untyped-def]self,*args,**kwargs)->str:"""Same as preview, but returns a stringified version of the JSON for insertting back into an LLM."""data=awaitself.apreview(*args,**kwargs)returnjson.dumps(data)
[docs]deflist_as_str(self)->str:# type: ignore[no-untyped-def]"""Same as list, but returns a stringified version of the JSON for insertting back into an LLM."""actions=self.list()returnjson.dumps(actions)
[docs]asyncdefalist_as_str(self)->str:# type: ignore[no-untyped-def]"""Same as list, but returns a stringified version of the JSON for insertting back into an LLM."""actions=awaitself.alist()returnjson.dumps(actions)