Expanding support for graphql tooling for more diverse graphql use cases.
Labels: name: area: agent, name: auto:improvement

### Expanding the previous support for graphql tooling to fix some bugs and make it more accessible to diverse graphql use cases.

Changes:

1. Previous 'custom_headers' flag did not work as it wasn't hooked into the _EXTRA_LLM_TOOLS dict correctly, fixed that
2. Addition of flag for custom_transport_auth which enables signing request with sigV4 (aws appsync benefits from this)
3. Addition of automatic schema loading 
  - runs introspection query when tool is initialized and injects the schema into the tool description for model use
  - removes redundant information from the schema before adding to prompt to avoid model confusion
  - flag disable_schema_load can be used to disable and revert to previous functionality
4. Adds a try ... except around execution as not all graphql errors are serializable as langchain wants, prevents chain from crashing in these cases

#### Examples

Users can now use more diverse auth modes and have a more streamlined code implementation.

```
# Using an Api Key
appsync_tool = load_tools(
  ['graphql'], 
  graphql_endpoint="...", 
  custom_headers={'x-api-key': '...'}, 
  llm=OpenAI()
)
```

```
# Using AWS IAM signing

credentials = boto3.Session().get_credentials()
region = os.getenv("AWS_REGION", "us-east-1")

awsauth = AWS4Auth(
    credentials.access_key,
    credentials.secret_key,
    region,
    "appsync",
    session_token=credentials.token,
)

appsync_signed_requests_tool = load_tools(
  ['graphql'], 
  graphql_endpoint="...", 
  custom_transport_auth=awsauth,
  llm=OpenAI()
)
```

In both cases there is no longer a need for the schema to be provided, its now pulled automatically.

```
# Using an agent like this
agent = initialize_agent(
    appsync_tool,
    llm,
    agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
    verbose=True,
)
```

We can then query with:

```
agent.run('Who is not friends with bailey?')
```

Then results in a prompt like below:

```
Answer the following questions as best you can. You have access to the following tools:

query_graphql:     Input to this tool is a detailed and correct GraphQL query, output is a result from the API.
    If the query is not correct, an error message will be returned.
    If an error is returned with 'Bad request' in it, rewrite the query and try again.
    If an error is returned with 'Unauthorized' in it, do not try again, but tell the user to change their authentication.

    Use the following schema for your queries: 
    type Query {
      """  Gets your own account info"""
      self: User
    
      """  Get all accounts"""
      users: [User]
    }
    
    type User {
      id: String
      name: String
      friends: [UserRelation]
    }
    
    type UserRelation {
      user: User
    }
    
    type Mutation {
      """  Takes in a parameter of post which is a new post object. Its assumed to be created by you
      """
      createPost(post: NewPost): Post
    }
    
    """  A post on the system"""
    type Post {
      id: String
      title: String
      content: String
      author: User
    }
    
    """  A partial object that is used to create a new post"""
    input NewPost {
      title: String
      content: String
    }
    

Use the following format:

Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [query_graphql]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question

Begin!
```



#### Who can review?

@hwchase17