Spaces:
Runtime error
Runtime error
"""Util that can interact with Zapier NLA. | |
Full docs here: https://nla.zapier.com/api/v1/docs | |
Note: this wrapper currently only implemented the `api_key` auth method for testing | |
and server-side production use cases (using the developer's connected accounts on | |
Zapier.com) | |
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 and reach out to [email protected] for | |
developer support. | |
""" | |
import json | |
from typing import Dict, List, Optional | |
import requests | |
from pydantic import BaseModel, Extra, root_validator | |
from requests import Request, Session | |
from langchain.utils import get_from_dict_or_env | |
class ZapierNLAWrapper(BaseModel): | |
"""Wrapper for Zapier NLA. | |
Full docs here: https://nla.zapier.com/api/v1/docs | |
Note: this wrapper currently only implemented the `api_key` auth method for | |
testingand server-side production use cases (using the developer's connected | |
accounts on Zapier.com) | |
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 and reach out to | |
[email protected] for developer support. | |
""" | |
zapier_nla_api_key: str | |
zapier_nla_api_base: str = "https://nla.zapier.com/api/v1/" | |
class Config: | |
"""Configuration for this pydantic object.""" | |
extra = Extra.forbid | |
def _get_session(self) -> Session: | |
session = requests.Session() | |
session.headers.update( | |
{ | |
"Accept": "application/json", | |
"Content-Type": "application/json", | |
} | |
) | |
session.params = {"api_key": self.zapier_nla_api_key} | |
return session | |
def _get_action_request( | |
self, action_id: str, instructions: str, params: Optional[Dict] = None | |
) -> Request: | |
data = params if params else {} | |
data.update( | |
{ | |
"instructions": instructions, | |
} | |
) | |
return Request( | |
"POST", | |
self.zapier_nla_api_base + f"exposed/{action_id}/execute/", | |
json=data, | |
) | |
def validate_environment(cls, values: Dict) -> Dict: | |
"""Validate that api key exists in environment.""" | |
zapier_nla_api_key = get_from_dict_or_env( | |
values, "zapier_nla_api_key", "ZAPIER_NLA_API_KEY" | |
) | |
values["zapier_nla_api_key"] = zapier_nla_api_key | |
return values | |
def list(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) | |
""" | |
session = self._get_session() | |
response = session.get(self.zapier_nla_api_base + "exposed/") | |
response.raise_for_status() | |
return response.json()["results"] | |
def run( | |
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._get_action_request(action_id, instructions, params) | |
response = session.send(session.prepare_request(request)) | |
response.raise_for_status() | |
return response.json()["result"] | |
def preview( | |
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 = params if params else {} | |
params.update({"preview_only": True}) | |
request = self._get_action_request(action_id, instructions, params) | |
response = session.send(session.prepare_request(request)) | |
response.raise_for_status() | |
return response.json()["input_params"] | |
def run_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) | |
return json.dumps(data) | |
def preview_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) | |
return json.dumps(data) | |
def list_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() | |
return json.dumps(actions) | |