LangChain Custom Tools#
LangChain Custom tools are classes that can be used to interact with the world.
Imagine SDK can leverage the tools available on LangChain Community which already contains many third-party integrations.
Perform an Internet search and summarize the results#
The following example performs an internet search with the DuckDuckGo search engine and then summarizes the results:
from langchain_community.tools import DuckDuckGoSearchRun
from langchain_core.globals import set_debug
from langchain_core.prompts.prompt import PromptTemplate
from imagine.langchain import ImagineChat
set_debug(True)
search = DuckDuckGoSearchRun()
title_input = "What are the olympic medals tally for china and USA in 2024"
title_template = PromptTemplate(
input_variables=["subject"],
template="I want to know about following - {subject}. Give me a question i can search on google",
)
outline_template = PromptTemplate(
input_variables=["DuckDuckGo_Search"],
template="Can you give me a one line short summary for this search result - {DuckDuckGo_Search}",
)
llm = ImagineChat(model="Llama-3-8B", max_tokens=500, temperature=0.0)
title_chain = title_template | llm
outline_chain = outline_template | llm
title = title_chain.invoke(title_input)
search_result = search.run(title_input)
outline = outline_chain.invoke(input={"DuckDuckGo_Search": search_result})
print(f"Duck Duck Go search summary: {outline}")
Count the rows of a CSV file with a custom tool#
The following example implements a custom tool to count the number of rows in a CSV file:
import re
import pandas as pd
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts.chat import ChatPromptTemplate
from langchain_core.runnables import RunnableLambda, RunnablePassthrough
from imagine.langchain import ImagineChat
# Define the LLM and prompt template
llm = ImagineChat(max_tokens=1000)
prompt = ChatPromptTemplate.from_template("""You are a data analysis expert.
Generate Python code in one single chunk in ```python``` syntax such that it saves the
output as a string variable `result`. assume that dataframe is already loaded as df
variable. Write this code to perform the following analysis on a Pandas DataFrame:
{task_description}
The dataframe looks like -
```{data_head}```
""")
def extract_code(text):
# Regular expression pattern to match code blocks
pattern = r"\`\`\`[\w\s]+?\n([\s\S]*?)\n\`\`\`"
# Use re.DOTALL flag to allow . to match newline characters
match = re.search(pattern, text, re.DOTALL)
if match:
# Remove the language specification and triple backticks from the matched code
code = match.group(1).strip()
return code # Return the cleaned-up code
else:
return None # No code block found
# Define the tool's functionality
def execute_code(context: dict) -> str:
code = extract_code(context.get("code"))
dataframe = context.get("dataframe")
try:
exec_globals = {"df": dataframe["dataframe"]}
exec(code, exec_globals)
return str(
exec_globals.get("result", "Execution completed without returning a result")
)
except Exception as e:
return f"Error during execution: {str(e)}"
# Modify the runnable to handle the dataframe
code_generation_chain = (
{"task_description": RunnablePassthrough(), "data_head": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
execution_chain = RunnableLambda(execute_code)
# Combine chains to pass both code and dataframe to execute_code
final_chain = {
"code": code_generation_chain,
"dataframe": RunnablePassthrough(),
} | execution_chain
# Example usage
task_description = "How many rows are there?"
your_dataframe = pd.read_csv("../assets/file.csv")
result = final_chain.invoke(
{
"task_description": task_description,
"dataframe": your_dataframe,
"data_head": your_dataframe.head(2),
}
)
print(result)
Using GitHub with a custom tool#
The following example implements a custom tool to interact with GitHub. The tools used are defined here
import os
import re
from github_api_request import fetch_issue_details, fetch_issue_labels
from langchain_core.prompts.prompt import PromptTemplate
from imagine.langchain import ImagineChat
from langchain.tools import BaseTool
# Initialize the language model
llm = ImagineChat(
model="Llama-3-8B",
max_tokens=500,
temperature=0.0,
)
os.environ["GITHUB_TOKEN"] = "<your_github_token>"
os.environ["REPO"] = "PyGithub/PyGithub" # for example
issue_number = 2892 # Replace with the actual issue number
def extract_query(text):
"""
Extracts the query from the given text using a regular expression pattern.
"""
pattern = r"```([\s\S]*?)```"
match = re.search(pattern, text, re.DOTALL)
if match:
query = match.group(1).strip()
return query
else:
return None # No query found
# Define tool classes
class GitHubIssueDetailsTool(BaseTool):
name = "github_issue_details"
description = "Fetches details of a GitHub issue"
def _run(self, issue_number: int) -> str:
return fetch_issue_details(issue_number, print_output=False)
class GitHubLabelsTool(BaseTool):
name = "github_labels"
description = "Fetches labels from a GitHub repository"
def _run(self) -> str:
labels = fetch_issue_labels()
return ", ".join(labels)
# Instantiate the tools
github_issue_details_tool = GitHubIssueDetailsTool()
github_labels_tool = GitHubLabelsTool()
# Define prompt templates
title_template = PromptTemplate(
input_variables=["issue"],
template="Please summarize the GitHub issue \n{issue}. Do a small paragraph to explain the goal of the issue then discuss of the changes made in the commits",
)
outline_template = PromptTemplate(
input_variables=["issue_summary", "labels"],
template="Here is the summary of an issue - \n{issue_summary} suggest between 1 and 3 appropriate labels from this list - \n{labels}",
)
# Create chains for title and outline
title_chain = title_template | llm
outline_chain = outline_template | llm
# search for the issue
issue = github_issue_details_tool.run({"issue_number": issue_number})
print(" issue ".center(100, "="))
print(issue)
# Generate the title
title = title_chain.invoke(input={"issue": issue})
print(" title ".center(100, "="))
print(title)
# Fetch issue labels
labels = github_labels_tool.run({})
print(" labels ".center(100, "="))
print(labels)
# Generate the outline
outline = outline_chain.invoke(input={"issue_summary": title, "labels": labels})
print(" outline ".center(100, "="))
print(outline.content)
Using Jira with a custom tool#
The following example implements a custom tool to interact with JIRA using this Jira lib
import getpass
import os
import re
import urllib3
from jira import JIRA
from langchain_core.prompts.prompt import PromptTemplate
from imagine.langchain import ImagineChat
from langchain.tools import BaseTool
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# Set the variables below to be able to use Jira
username = getpass.getuser()
jira_server = "jira_server_endpoint"
pwd = "your_password"
# Initialize the language model
llm = ImagineChat(
model="Llama-3-8B",
max_tokens=500,
temperature=0.0,
)
# Set environment variables for Jira authentication
os.environ["JIRA_USER"] = username
os.environ["JIRA_PASSWORD"] = pwd
# Basic class that extends langchain BaseTool
class JiraSearchTool(BaseTool):
name = "jira_search"
description = "Searches Jira issues"
def _run(self, query: str, server: str, verify: str = True) -> str:
user = os.environ["JIRA_USER"]
pwd = os.environ["JIRA_PASSWORD"]
jira = JIRA(server=server, basic_auth=(user, pwd), options={"verify": verify})
q = query + " ORDER BY createdDate DESC"
return jira.search_issues(q, maxResults=0)
# Instantiate the JiraSearchTool
jira_search_tool = JiraSearchTool(server=jira_server, user=username, pwd=pwd)
def extract_query(text):
"""
Extracts the query from the given text using a regular expression pattern.
"""
pattern = r"```([\s\S]*?)```"
match = re.search(pattern, text, re.DOTALL)
if match:
query = match.group(1).strip()
return query
else:
return None # No query found
# Define prompt templates
title_template = PromptTemplate(
input_variables=["username"],
template="Please give a jira query for the jira issues assigned to {username} created in the last 180 days. put the query as in answer between ``` like this: ```query```",
)
outline_template = PromptTemplate(
input_variables=["Jira_Search"],
template="Can you give me a short summary for this search result - {Jira_Search}",
)
# Create chains for title and outline
title_chain = title_template | llm
outline_chain = outline_template | llm
# Generate the title
title = title_chain.invoke(input={"username": username})
print(" title ".center(100, "="))
print(title)
# Extract the query from the title
query = extract_query(title.content)
print(" query ".center(100, "="))
print(query)
# Run the Jira search tool with the extracted query
search_result = jira_search_tool.run({"query": query, "server": jira_server})
def create_issue_dict(issue):
"""
Creates a dictionary representation of a Jira issue.
"""
return {
"issue": issue,
"jira_id": issue.key,
"summary": issue.fields.summary,
"description": issue.fields.description,
"status": issue.fields.status,
"resolution": issue.fields.resolution
if hasattr(issue.fields, "resolution")
else None,
"reporter": issue.fields.reporter.displayName,
"assigned_to": issue.fields.assignee.displayName
if issue.fields.assignee
else None,
"reporter_username": issue.fields.reporter.name
if hasattr(issue.fields.reporter, "name")
else None,
"assignee_username": (
issue.fields.assignee.name
if issue.fields.assignee and hasattr(issue.fields.assignee, "name")
else None
),
"created_date": issue.fields.created,
"updated_date": issue.fields.updated,
"components": [component.name for component in issue.fields.components],
}
# Convert search results to a list of dictionaries
dict_result = [create_issue_dict(issue) for issue in search_result]
# Generate the outline
outline = outline_chain.invoke(input={"Jira_Search": dict_result})
print(" summary ".center(100, "="))
print(outline.content)
Using tools with Agents#
Check out how to use tools with crewAI agents.