forked from run-llama/llama-hub
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add PlaygroundsSubgraphInspectorToolSpec to llama_hub (run-llam…
…a#535) feat: Add PlaygroundsSubgraphInspectorToolSpec to llama_hub
- Loading branch information
1 parent
56d29d9
commit a6188c8
Showing
5 changed files
with
333 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# playgrounds_subgraph_inspector | ||
|
||
Playgrounds API is a service provided by [Playgrounds Analytics](https://playgrounds.network) to facilitate interactions with decentralized subgraphs (indexed blockchain datasets). | ||
|
||
The `PlaygroundsSubgraphInspectorToolSpec` is a tool designed for LLM agents to introspect and understand the schema of subgraphs on The Graph's decentralized network via the Playgrounds API. | ||
|
||
This tool is specifically designed to be used alongside [Llama index](https://github.com/jerryjliu/llama_index) or [langchain](https://python.langchain.com/docs/modules/agents/tools/custom_tools). | ||
|
||
- To learn more about Playgrounds API, please visit our website: [Playgrounds Network](https://playgrounds.network/) | ||
- Obtain your Playgrounds API Key and get started for free [here](https://app.playgrounds.network/signup). | ||
- Discover any Subgraph (dataset) you need [here](https://thegraph.com/explorer). | ||
|
||
## Advantages of this tool: | ||
|
||
- **Introspection of Decentralized Subgraphs (Datasets)**: Understand the schema of any subgraph without hassle. | ||
- **LLM x Blockchain Data**: Develop AI applications that leverage introspective insights from blockchain data. | ||
|
||
## Basic Usage: | ||
|
||
To utilize the tool, initialize it with the appropriate `identifier` (Subgraph ID or Deployment ID), `api_key`, and specify if you're using a deployment ID. | ||
|
||
```python | ||
import openai | ||
from llama_index.agent import OpenAIAgent | ||
from llama_hub.tools.playgrounds_subgraph_inspector.base import PlaygroundsSubgraphInspectorToolSpec | ||
|
||
def inspect_subgraph( | ||
openai_api_key: str, | ||
playgrounds_api_key: str, | ||
identifier: str, | ||
use_deployment_id: bool, | ||
user_prompt: str | ||
): | ||
""" | ||
Introspect a subgraph using OpenAIAgent and Playgrounds API with the provided parameters. | ||
Args: | ||
openai_api_key (str): API key for OpenAI. | ||
playgrounds_api_key (str): API key for Playgrounds. | ||
identifier (str): Identifier for the subgraph or deployment. | ||
use_deployment_id (bool): If True, uses deployment ID in the URL. | ||
user_prompt (str): User's question or prompt for the agent. | ||
Returns: | ||
str: Agent's response. | ||
""" | ||
# Set the OpenAI API key | ||
openai.api_key = openai_api_key | ||
|
||
# Initialize the inspector with the provided parameters | ||
inspector_spec = PlaygroundsSubgraphInspectorToolSpec( | ||
identifier=identifier, | ||
api_key=playgrounds_api_key, | ||
use_deployment_id=use_deployment_id | ||
) | ||
|
||
# Integrate the tool with the agent | ||
agent = OpenAIAgent.from_tools(inspector_spec.to_tool_list()) | ||
|
||
# Send the user prompt to the agent | ||
response = agent.chat(user_prompt) | ||
return response | ||
|
||
|
||
if __name__ == "__main__": | ||
query = inspect_subgraph( | ||
openai_api_key='YOUR_OPENAI_API_KEY', | ||
playgrounds_api_key="YOUR_PLAYGROUNDS_API_KEY", | ||
identifier="YOUR_SUBGRAPH_OR_DEPLOYMENT_IDENTIFIER", | ||
use_deployment_id=False, | ||
user_prompt='Which entities will help me understand the usage of Uniswap V3?' | ||
) | ||
``` | ||
Visit here for more in-depth [Examples](https://github.com/Tachikoma000/playgrounds_subgraph_connector/blob/main/introspector_agent_tool/examples.ipynb). | ||
|
||
This inspector is designed to be used as a way to understand the schema of subgraphs and subgraph data being loaded into [LlamaIndex](https://github.com/jerryjliu/gpt_index/tree/main/gpt_index) and/or subsequently used as a Tool in a [LangChain](https://github.com/hwchase17/langchain) Agent. |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,251 @@ | ||
"""PlaygroundsSubgraphInspectorToolSpec.""" | ||
|
||
from typing import Optional, Union | ||
import requests | ||
from llama_hub.tools.graphql.base import GraphQLToolSpec | ||
|
||
class PlaygroundsSubgraphInspectorToolSpec(GraphQLToolSpec): | ||
""" | ||
Connects to subgraphs on The Graph's decentralized network via the Playgrounds API and introspects the subgraph. | ||
Provides functionalities to process and summarize the introspected schema for easy comprehension. | ||
Attributes: | ||
spec_functions (list): List of functions that specify the tool's capabilities. | ||
url (str): The endpoint URL for the GraphQL requests. | ||
headers (dict): Headers used for the GraphQL requests. | ||
""" | ||
spec_functions = ["introspect_and_summarize_subgraph"] | ||
|
||
def __init__(self, identifier: str, api_key: str, use_deployment_id: bool = False): | ||
""" | ||
Initialize the connection to the specified subgraph on The Graph's network. | ||
Args: | ||
identifier (str): The subgraph's identifier or deployment ID. | ||
api_key (str): API key for the Playgrounds API. | ||
use_deployment_id (bool): If True, treats the identifier as a deployment ID. Default is False. | ||
""" | ||
self.url = self._generate_url(identifier, use_deployment_id) | ||
self.headers = { | ||
"Content-Type": "application/json", | ||
"Playgrounds-Api-Key": api_key | ||
} | ||
|
||
def _generate_url(self, identifier: str, use_deployment_id: bool) -> str: | ||
""" | ||
Generate the appropriate URL based on the identifier and whether it's a deployment ID or not. | ||
Args: | ||
identifier (str): The subgraph's identifier or deployment ID. | ||
use_deployment_id (bool): If True, constructs the URL using the deployment ID. | ||
Returns: | ||
str: The constructed URL. | ||
""" | ||
|
||
endpoint = "deployments" if use_deployment_id else "subgraphs" | ||
return f"https://api.playgrounds.network/v1/proxy/{endpoint}/id/{identifier}" | ||
|
||
def introspect_and_summarize_subgraph(self) -> str: | ||
""" | ||
Introspects the subgraph and summarizes its schema into textual categories. | ||
Returns: | ||
str: A textual summary of the introspected subgraph schema. | ||
""" | ||
introspection_query = """ | ||
query { | ||
__schema { | ||
types { | ||
kind | ||
name | ||
description | ||
enumValues { | ||
name | ||
} | ||
fields { | ||
name | ||
args { | ||
name | ||
} | ||
type { | ||
kind | ||
name | ||
ofType { | ||
name | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
""" | ||
response = self._graphql_request(introspection_query) | ||
if "data" in response: | ||
result = response["data"] | ||
processed_subgraph = self._process_subgraph(result) | ||
return self.subgraph_to_text(processed_subgraph) | ||
else: | ||
return "Error during introspection." | ||
|
||
def _graphql_request(self, query: str) -> dict: | ||
""" | ||
Execute a GraphQL query against the subgraph's endpoint. | ||
Args: | ||
query (str): The GraphQL query string. | ||
Returns: | ||
dict: Response from the GraphQL server, either containing the data or an error. | ||
""" | ||
payload = {'query': query.strip()} | ||
try: | ||
response = requests.post(self.url, headers=self.headers, json=payload) | ||
response.raise_for_status() | ||
return response.json() | ||
except requests.RequestException as e: | ||
return {"error": str(e)} | ||
|
||
def _process_subgraph(self, result: dict) -> dict: | ||
""" | ||
Processes the introspected subgraph schema into categories based on naming conventions. | ||
Args: | ||
result (dict): Introspected schema result from the GraphQL query. | ||
Returns: | ||
dict: A processed representation of the introspected schema, categorized into specific entity queries, list entity queries, and other entities. | ||
""" | ||
processed_subgraph = { | ||
"specific_entity_queries": {}, | ||
"list_entity_queries": {}, | ||
"other_entities": {} | ||
} | ||
for type_ in result['__schema']['types']: | ||
if type_['name'].startswith('__'): | ||
continue # Skip meta entities | ||
|
||
entity_name = type_['name'] | ||
fields, args_required = self._get_fields(type_) | ||
if fields: | ||
# Determine category based on naming convention | ||
if entity_name.endswith('s') and not args_required: | ||
processed_subgraph["list_entity_queries"][entity_name] = fields | ||
elif not entity_name.endswith('s') and args_required: | ||
processed_subgraph["specific_entity_queries"][entity_name] = fields | ||
else: | ||
processed_subgraph["other_entities"][entity_name] = fields | ||
|
||
return processed_subgraph | ||
|
||
def _get_fields(self, type_): | ||
""" | ||
Extracts relevant fields and their details from a given type within the introspected schema. | ||
Args: | ||
type_ (dict): A type within the introspected schema. | ||
Returns: | ||
tuple: A tuple containing a list of relevant fields and a boolean indicating if arguments are required for the fields. | ||
""" | ||
fields = [] | ||
args_required = False | ||
for f in (type_.get('fields') or []): | ||
if f['name'] != '__typename' and not (f['name'].endswith('_filter') or f['name'].endswith('_orderBy') or f['name'].islower()): | ||
field_info = {'name': f['name']} | ||
|
||
# Check for enum values | ||
if 'enumValues' in f['type'] and f['type']['enumValues']: | ||
field_info['enumValues'] = [enum_val['name'] for enum_val in f['type']['enumValues']] | ||
|
||
fields.append(field_info) | ||
if f.get('args') and len(f['args']) > 0: | ||
args_required = True | ||
if f.get('type') and f['type'].get('fields'): | ||
subfields, sub_args_required = self._get_fields(f['type']) | ||
fields.extend(subfields) | ||
if sub_args_required: | ||
args_required = True | ||
return fields, args_required | ||
|
||
def format_section(self, category: str, description: str, example: str, entities: dict) -> str: | ||
""" | ||
Formats a given section of the subgraph introspection result into a readable string format. | ||
Args: | ||
category (str): The category name of the entities. | ||
description (str): A description explaining the category. | ||
example (str): A generic GraphQL query example related to the category. | ||
entities (dict): Dictionary containing entities and their fields related to the category. | ||
Returns: | ||
str: A formatted string representation of the provided section data. | ||
""" | ||
section = [ | ||
f"Category: {category}", | ||
f"Description: {description}", | ||
"Generic Example:", | ||
example, | ||
"\nDetailed Breakdown:" | ||
] | ||
|
||
for entity, fields in entities.items(): | ||
section.append(f" Entity: {entity}") | ||
for field_info in fields: | ||
field_str = f" - {field_info['name']}" | ||
if 'enumValues' in field_info: | ||
field_str += f" (Enum values: {', '.join(field_info['enumValues'])})" | ||
section.append(field_str) | ||
section.append("") # Add a blank line for separation | ||
|
||
section.append("") # Add another blank line for separation between sections | ||
return "\n".join(section) | ||
|
||
def subgraph_to_text(self, subgraph: dict) -> str: | ||
""" | ||
Converts a processed subgraph representation into a textual summary based on entity categories. | ||
Args: | ||
subgraph (dict): A processed representation of the introspected schema, categorized into specific entity queries, list entity queries, and other entities. | ||
Returns: | ||
str: A textual summary of the processed subgraph schema. | ||
""" | ||
sections = [ | ||
("Specific Entity Queries (Requires Arguments)", | ||
"These queries target a singular entity and require specific arguments (like an ID) to fetch data.", | ||
""" | ||
{ | ||
entityName(id: "specific_id") { | ||
fieldName1 | ||
fieldName2 | ||
... | ||
} | ||
} | ||
""", | ||
subgraph["specific_entity_queries"]), | ||
|
||
("List Entity Queries (Optional Arguments)", | ||
"These queries fetch a list of entities. They don't strictly require arguments but often accept optional parameters for filtering, sorting, and pagination.", | ||
""" | ||
{ | ||
entityNames(first: 10, orderBy: "someField", orderDirection: "asc") { | ||
fieldName1 | ||
fieldName2 | ||
... | ||
} | ||
} | ||
""", | ||
subgraph["list_entity_queries"]), | ||
|
||
("Other Entities", | ||
"These are additional entities that may not fit the conventional singular/plural querying pattern of subgraphs.", | ||
"", | ||
subgraph["other_entities"]) | ||
] | ||
|
||
result_lines = [] | ||
for category, desc, example, entities in sections: | ||
result_lines.append(self.format_section(category, desc, example, entities)) | ||
|
||
return "\n".join(result_lines) |
1 change: 1 addition & 0 deletions
1
llama_hub/tools/playgrounds_subgraph_inspector/requirements.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
requests==2.31.0 |