Tools and Routing¶
In [1]:
Copied!
import os
import openai
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv()) # read local .env file
openai.api_key = os.environ['OPENAI_API_KEY']
import os import openai from dotenv import load_dotenv, find_dotenv _ = load_dotenv(find_dotenv()) # read local .env file openai.api_key = os.environ['OPENAI_API_KEY']
In [2]:
Copied!
from langchain.agents import tool
from langchain.agents import tool
In [3]:
Copied!
@tool
def search(query: str) -> str:
"""Search for weather online"""
return "42f"
@tool def search(query: str) -> str: """Search for weather online""" return "42f"
In [4]:
Copied!
search.name
search.name
Out[4]:
'search'
In [5]:
Copied!
search.description
search.description
Out[5]:
'search(query: str) -> str - Search for weather online'
In [6]:
Copied!
search.args
search.args
Out[6]:
{'query': {'title': 'Query', 'type': 'string'}}
In [7]:
Copied!
from pydantic import BaseModel, Field
class SearchInput(BaseModel):
query: str = Field(description="Thing to search for")
from pydantic import BaseModel, Field class SearchInput(BaseModel): query: str = Field(description="Thing to search for")
In [8]:
Copied!
@tool(args_schema=SearchInput)
def search(query: str) -> str:
"""Search for the weather online."""
return "42f"
@tool(args_schema=SearchInput) def search(query: str) -> str: """Search for the weather online.""" return "42f"
In [9]:
Copied!
search.args
search.args
Out[9]:
{'query': {'title': 'Query', 'description': 'Thing to search for', 'type': 'string'}}
In [10]:
Copied!
search.run("sf")
search.run("sf")
Out[10]:
'42f'
In [11]:
Copied!
import requests
from pydantic import BaseModel, Field
import datetime
# Define the input schema
class OpenMeteoInput(BaseModel):
latitude: float = Field(..., description="Latitude of the location to fetch weather data for")
longitude: float = Field(..., description="Longitude of the location to fetch weather data for")
@tool(args_schema=OpenMeteoInput)
def get_current_temperature(latitude: float, longitude: float) -> dict:
"""Fetch current temperature for given coordinates."""
BASE_URL = "https://api.open-meteo.com/v1/forecast"
# Parameters for the request
params = {
'latitude': latitude,
'longitude': longitude,
'hourly': 'temperature_2m',
'forecast_days': 1,
}
# Make the request
response = requests.get(BASE_URL, params=params)
if response.status_code == 200:
results = response.json()
else:
raise Exception(f"API Request failed with status code: {response.status_code}")
current_utc_time = datetime.datetime.utcnow()
time_list = [datetime.datetime.fromisoformat(time_str.replace('Z', '+00:00')) for time_str in results['hourly']['time']]
temperature_list = results['hourly']['temperature_2m']
closest_time_index = min(range(len(time_list)), key=lambda i: abs(time_list[i] - current_utc_time))
current_temperature = temperature_list[closest_time_index]
return f'The current temperature is {current_temperature}°C'
import requests from pydantic import BaseModel, Field import datetime # Define the input schema class OpenMeteoInput(BaseModel): latitude: float = Field(..., description="Latitude of the location to fetch weather data for") longitude: float = Field(..., description="Longitude of the location to fetch weather data for") @tool(args_schema=OpenMeteoInput) def get_current_temperature(latitude: float, longitude: float) -> dict: """Fetch current temperature for given coordinates.""" BASE_URL = "https://api.open-meteo.com/v1/forecast" # Parameters for the request params = { 'latitude': latitude, 'longitude': longitude, 'hourly': 'temperature_2m', 'forecast_days': 1, } # Make the request response = requests.get(BASE_URL, params=params) if response.status_code == 200: results = response.json() else: raise Exception(f"API Request failed with status code: {response.status_code}") current_utc_time = datetime.datetime.utcnow() time_list = [datetime.datetime.fromisoformat(time_str.replace('Z', '+00:00')) for time_str in results['hourly']['time']] temperature_list = results['hourly']['temperature_2m'] closest_time_index = min(range(len(time_list)), key=lambda i: abs(time_list[i] - current_utc_time)) current_temperature = temperature_list[closest_time_index] return f'The current temperature is {current_temperature}°C'
In [12]:
Copied!
get_current_temperature.name
get_current_temperature.name
Out[12]:
'get_current_temperature'
In [13]:
Copied!
get_current_temperature.description
get_current_temperature.description
Out[13]:
'get_current_temperature(latitude: float, longitude: float) -> dict - Fetch current temperature for given coordinates.'
In [14]:
Copied!
get_current_temperature.args
get_current_temperature.args
Out[14]:
{'latitude': {'title': 'Latitude', 'description': 'Latitude of the location to fetch weather data for', 'type': 'number'}, 'longitude': {'title': 'Longitude', 'description': 'Longitude of the location to fetch weather data for', 'type': 'number'}}
In [ ]:
Copied!
from langchain.tools.render import format_tool_to_openai_function
from langchain.tools.render import format_tool_to_openai_function
In [ ]:
Copied!
format_tool_to_openai_function(get_current_temperature)
format_tool_to_openai_function(get_current_temperature)
In [ ]:
Copied!
get_current_temperature({"latitude": 13, "longitude": 14})
get_current_temperature({"latitude": 13, "longitude": 14})
In [ ]:
Copied!
import wikipedia
@tool
def search_wikipedia(query: str) -> str:
"""Run Wikipedia search and get page summaries."""
page_titles = wikipedia.search(query)
summaries = []
for page_title in page_titles[: 3]:
try:
wiki_page = wikipedia.page(title=page_title, auto_suggest=False)
summaries.append(f"Page: {page_title}\nSummary: {wiki_page.summary}")
except (
self.wiki_client.exceptions.PageError,
self.wiki_client.exceptions.DisambiguationError,
):
pass
if not summaries:
return "No good Wikipedia Search Result was found"
return "\n\n".join(summaries)
import wikipedia @tool def search_wikipedia(query: str) -> str: """Run Wikipedia search and get page summaries.""" page_titles = wikipedia.search(query) summaries = [] for page_title in page_titles[: 3]: try: wiki_page = wikipedia.page(title=page_title, auto_suggest=False) summaries.append(f"Page: {page_title}\nSummary: {wiki_page.summary}") except ( self.wiki_client.exceptions.PageError, self.wiki_client.exceptions.DisambiguationError, ): pass if not summaries: return "No good Wikipedia Search Result was found" return "\n\n".join(summaries)
In [ ]:
Copied!
search_wikipedia.name
search_wikipedia.name
In [ ]:
Copied!
search_wikipedia.description
search_wikipedia.description
In [ ]:
Copied!
format_tool_to_openai_function(search_wikipedia)
format_tool_to_openai_function(search_wikipedia)
In [ ]:
Copied!
search_wikipedia({"query": "langchain"})
search_wikipedia({"query": "langchain"})
In [ ]:
Copied!
from langchain.chains.openai_functions.openapi import openapi_spec_to_openai_fn
from langchain.utilities.openapi import OpenAPISpec
from langchain.chains.openai_functions.openapi import openapi_spec_to_openai_fn from langchain.utilities.openapi import OpenAPISpec
In [ ]:
Copied!
text = """
{
"openapi": "3.0.0",
"info": {
"version": "1.0.0",
"title": "Swagger Petstore",
"license": {
"name": "MIT"
}
},
"servers": [
{
"url": "http://petstore.swagger.io/v1"
}
],
"paths": {
"/pets": {
"get": {
"summary": "List all pets",
"operationId": "listPets",
"tags": [
"pets"
],
"parameters": [
{
"name": "limit",
"in": "query",
"description": "How many items to return at one time (max 100)",
"required": false,
"schema": {
"type": "integer",
"maximum": 100,
"format": "int32"
}
}
],
"responses": {
"200": {
"description": "A paged array of pets",
"headers": {
"x-next": {
"description": "A link to the next page of responses",
"schema": {
"type": "string"
}
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Pets"
}
}
}
},
"default": {
"description": "unexpected error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
},
"post": {
"summary": "Create a pet",
"operationId": "createPets",
"tags": [
"pets"
],
"responses": {
"201": {
"description": "Null response"
},
"default": {
"description": "unexpected error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/pets/{petId}": {
"get": {
"summary": "Info for a specific pet",
"operationId": "showPetById",
"tags": [
"pets"
],
"parameters": [
{
"name": "petId",
"in": "path",
"required": true,
"description": "The id of the pet to retrieve",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Expected response to a valid request",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
}
}
},
"default": {
"description": "unexpected error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"Pet": {
"type": "object",
"required": [
"id",
"name"
],
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"name": {
"type": "string"
},
"tag": {
"type": "string"
}
}
},
"Pets": {
"type": "array",
"maxItems": 100,
"items": {
"$ref": "#/components/schemas/Pet"
}
},
"Error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"message": {
"type": "string"
}
}
}
}
}
}
"""
text = """ { "openapi": "3.0.0", "info": { "version": "1.0.0", "title": "Swagger Petstore", "license": { "name": "MIT" } }, "servers": [ { "url": "http://petstore.swagger.io/v1" } ], "paths": { "/pets": { "get": { "summary": "List all pets", "operationId": "listPets", "tags": [ "pets" ], "parameters": [ { "name": "limit", "in": "query", "description": "How many items to return at one time (max 100)", "required": false, "schema": { "type": "integer", "maximum": 100, "format": "int32" } } ], "responses": { "200": { "description": "A paged array of pets", "headers": { "x-next": { "description": "A link to the next page of responses", "schema": { "type": "string" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Pets" } } } }, "default": { "description": "unexpected error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } }, "post": { "summary": "Create a pet", "operationId": "createPets", "tags": [ "pets" ], "responses": { "201": { "description": "Null response" }, "default": { "description": "unexpected error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } }, "/pets/{petId}": { "get": { "summary": "Info for a specific pet", "operationId": "showPetById", "tags": [ "pets" ], "parameters": [ { "name": "petId", "in": "path", "required": true, "description": "The id of the pet to retrieve", "schema": { "type": "string" } } ], "responses": { "200": { "description": "Expected response to a valid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } } } }, "default": { "description": "unexpected error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } } }, "components": { "schemas": { "Pet": { "type": "object", "required": [ "id", "name" ], "properties": { "id": { "type": "integer", "format": "int64" }, "name": { "type": "string" }, "tag": { "type": "string" } } }, "Pets": { "type": "array", "maxItems": 100, "items": { "$ref": "#/components/schemas/Pet" } }, "Error": { "type": "object", "required": [ "code", "message" ], "properties": { "code": { "type": "integer", "format": "int32" }, "message": { "type": "string" } } } } } } """
In [ ]:
Copied!
spec = OpenAPISpec.from_text(text)
spec = OpenAPISpec.from_text(text)
In [ ]:
Copied!
pet_openai_functions, pet_callables = openapi_spec_to_openai_fn(spec)
pet_openai_functions, pet_callables = openapi_spec_to_openai_fn(spec)
In [ ]:
Copied!
pet_openai_functions
pet_openai_functions
In [ ]:
Copied!
from langchain.chat_models import ChatOpenAI
from langchain.chat_models import ChatOpenAI
In [ ]:
Copied!
model = ChatOpenAI(temperature=0).bind(functions=pet_openai_functions)
model = ChatOpenAI(temperature=0).bind(functions=pet_openai_functions)
In [ ]:
Copied!
model.invoke("what are three pets names")
model.invoke("what are three pets names")
In [ ]:
Copied!
model.invoke("tell me about pet with id 42")
model.invoke("tell me about pet with id 42")
Routing¶
In lesson 3, we show an example of function calling deciding between two candidate functions.
Given our tools above, let's format these as OpenAI functions and show this same behavior.
In [ ]:
Copied!
functions = [
format_tool_to_openai_function(f) for f in [
search_wikipedia, get_current_temperature
]
]
model = ChatOpenAI(temperature=0).bind(functions=functions)
functions = [ format_tool_to_openai_function(f) for f in [ search_wikipedia, get_current_temperature ] ] model = ChatOpenAI(temperature=0).bind(functions=functions)
In [ ]:
Copied!
model.invoke("what is the weather in sf right now")
model.invoke("what is the weather in sf right now")
In [ ]:
Copied!
model.invoke("what is langchain")
model.invoke("what is langchain")
In [ ]:
Copied!
from langchain.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages([
("system", "You are helpful but sassy assistant"),
("user", "{input}"),
])
chain = prompt | model
from langchain.prompts import ChatPromptTemplate prompt = ChatPromptTemplate.from_messages([ ("system", "You are helpful but sassy assistant"), ("user", "{input}"), ]) chain = prompt | model
In [ ]:
Copied!
chain.invoke({"input": "what is the weather in sf right now"})
chain.invoke({"input": "what is the weather in sf right now"})
In [ ]:
Copied!
from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser
from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser
In [ ]:
Copied!
chain = prompt | model | OpenAIFunctionsAgentOutputParser()
chain = prompt | model | OpenAIFunctionsAgentOutputParser()
In [ ]:
Copied!
result = chain.invoke({"input": "what is the weather in sf right now"})
result = chain.invoke({"input": "what is the weather in sf right now"})
In [ ]:
Copied!
type(result)
type(result)
In [ ]:
Copied!
result.tool
result.tool
In [ ]:
Copied!
result.tool_input
result.tool_input
In [ ]:
Copied!
get_current_temperature(result.tool_input)
get_current_temperature(result.tool_input)
In [ ]:
Copied!
result = chain.invoke({"input": "hi!"})
result = chain.invoke({"input": "hi!"})
In [ ]:
Copied!
type(result)
type(result)
In [ ]:
Copied!
result.return_values
result.return_values
In [ ]:
Copied!
from langchain.schema.agent import AgentFinish
def route(result):
if isinstance(result, AgentFinish):
return result.return_values['output']
else:
tools = {
"search_wikipedia": search_wikipedia,
"get_current_temperature": get_current_temperature,
}
return tools[result.tool].run(result.tool_input)
from langchain.schema.agent import AgentFinish def route(result): if isinstance(result, AgentFinish): return result.return_values['output'] else: tools = { "search_wikipedia": search_wikipedia, "get_current_temperature": get_current_temperature, } return tools[result.tool].run(result.tool_input)
In [ ]:
Copied!
chain = prompt | model | OpenAIFunctionsAgentOutputParser() | route
chain = prompt | model | OpenAIFunctionsAgentOutputParser() | route
In [ ]:
Copied!
result = chain.invoke({"input": "What is the weather in san francisco right now?"})
result = chain.invoke({"input": "What is the weather in san francisco right now?"})
In [ ]:
Copied!
result
result
In [ ]:
Copied!
result = chain.invoke({"input": "What is langchain?"})
result = chain.invoke({"input": "What is langchain?"})
In [ ]:
Copied!
result
result
In [ ]:
Copied!
chain.invoke({"input": "hi!"})
chain.invoke({"input": "hi!"})
In [ ]:
Copied!
In [ ]:
Copied!
In [ ]:
Copied!
In [ ]:
Copied!
In [ ]:
Copied!
In [ ]:
Copied!
In [ ]:
Copied!
In [ ]:
Copied!
In [ ]:
Copied!
In [ ]:
Copied!
Last update: 2024-10-23
Created: 2024-10-23
Created: 2024-10-23