aignosi/langchaing-docs-chatgpt-plugin huggingface dataset:
Notes: A connection to the OpenWeatherMap API (https://api.openweathermap.org), specifically the /data/2.5/weather endpoint.
Requires LLM: No
Extra Parameters: openweathermap_api_key (your API key to access this endpoint)
previous
Tools
next
Defining Custom Tools
 Contents
  
List of Tools
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
103     tool_input: Union[str, Dict],
   (...)
    107     **kwargs: Any,
    108 ) -> str:
    109     """Run the tool."""
--> 110     run_input = self._parse_input(tool_input)
    111     if not self.verbose and verbose is not None:
    112         verbose_ = verbose
File ~/code/lc/lckg/langchain/tools/base.py:71, in BaseTool._parse_input(self, tool_input)
     69 if issubclass(input_args, BaseModel):
     70     key_ = next(iter(input_args.__fields__.keys()))
---> 71     input_args.parse_obj({key_: tool_input})
     72 # Passing as a positional argument is more straightforward for
     73 # backwards compatability
     74 return tool_input
File ~/code/lc/lckg/.venv/lib/python3.11/site-packages/pydantic/main.py:526, in pydantic.main.BaseModel.parse_obj()
File ~/code/lc/lckg/.venv/lib/python3.11/site-packages/pydantic/main.py:341, in pydantic.main.BaseModel.__init__()
ValidationError: 1 validation error for ToolInputSchema
__root__
  Domain google is not on the approved list: ['langchain', 'wikipedia'] (type=value_error)
previous
Multi-Input Tools
next
Apify
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
return_direct=True
    )
]
llm = OpenAI(temperature=0)
agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
agent.run("whats 2**.12")
> Entering new AgentExecutor chain...
 I need to calculate this
Action: Calculator
Action Input: 2**.12Answer: 1.086734862526058
> Finished chain.
'Answer: 1.086734862526058'
previous
Getting Started
next
Multi-Input Tools
 Contents
  
Completely New Tools - String Input and Output
Tool dataclass
Subclassing the BaseTool class
Using the tool decorator
Custom Structured Tools
StructuredTool dataclass
Subclassing the BaseTool
Using the decorator
Modify existing tools
Defining the priorities among Tools
Using tools to return directly
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
> Entering new AgentExecutor chain...
 I need to multiply two numbers
Action: Multiplier
Action Input: 3,4
Observation: 12
Thought: I now know the final answer
Final Answer: 3 times 4 is 12
> Finished chain.
'3 times 4 is 12'
previous
Defining Custom Tools
next
Tool Input Schema
 Contents
  
Multi-Input Tools with a string format
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
service, and you’re ready to start receiving data and triggering actions 🎉
Finishing up#
To get your webhook URL go to https://ifttt.com/maker_webhooks/settings
Copy the IFTTT key value from there. The URL is of the form
https://maker.ifttt.com/use/YOUR_IFTTT_KEY. Grab the YOUR_IFTTT_KEY value.
from langchain.tools.ifttt import IFTTTWebhook
import os
key = os.environ["IFTTTKey"]
url = f"https://maker.ifttt.com/trigger/spotify/json/with/key/{key}"
tool = IFTTTWebhook(name="Spotify", description="Add a song to spotify playlist", url=url)
tool.run("taylor swift")
"Congratulations! You've fired the spotify JSON event"
previous
Human as a tool
next
Metaphor Search
 Contents
  
Creating a webhook
Configuring the “If This”
Configuring the “Then That”
Finishing up
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
Thought:I need to navigate to the URL provided in the search results to find the tweet.
> Finished chain.
'I need to navigate to the URL provided in the search results to find the tweet.'
previous
IFTTT WebHooks
next
OpenWeatherMap API
 Contents
  
Metaphor Search
Call the API
Use Metaphor as a tool
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
Number of Results#
You can use the k parameter to set the number of results
search = GoogleSearchAPIWrapper(k=1)
tool = Tool(
    name = "I'm Feeling Lucky",
    description="Search Google and return the first result.",
    func=search.run
)
tool.run("python")
'The official home of the Python Programming Language.'
‘The official home of the Python Programming Language.’
Metadata Results#
Run query through GoogleSearch and return snippet, title, and link metadata.
Snippet: The description of the result.
Title: The title of the result.
Link: The link to the result.
search = GoogleSearchAPIWrapper()
def top5_results(query):
    return search.results(query, 5)
tool = Tool(
    name = "Google Search Snippets",
    description="Search Google for recent results.",
    func=top5_results
)
previous
Google Places
next
Google Serper API
 Contents
  
Number of Results
Metadata Results
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
.ipynb
.pdf
Google Places
Google Places#
This notebook goes through how to use Google Places API
#!pip install googlemaps
import os
os.environ["GPLACES_API_KEY"] = ""
from langchain.tools import GooglePlacesTool
places = GooglePlacesTool()
places.run("al fornos")
"1. Delfina Restaurant\nAddress: 3621 18th St, San Francisco, CA 94110, USA\nPhone: (415) 552-4055\nWebsite: https://www.delfinasf.com/\n\n\n2. Piccolo Forno\nAddress: 725 Columbus Ave, San Francisco, CA 94133, USA\nPhone: (415) 757-0087\nWebsite: https://piccolo-forno-sf.com/\n\n\n3. L'Osteria del Forno\nAddress: 519 Columbus Ave, San Francisco, CA 94133, USA\nPhone: (415) 982-1124\nWebsite: Unknown\n\n\n4. Il Fornaio\nAddress: 1265 Battery St, San Francisco, CA 94111, USA\nPhone: (415) 986-0100\nWebsite: https://www.ilfornaio.com/\n\n"
previous
File System Tools
next
Google Search
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
Thought:I now know the final answer
Final Answer: Julius Caesar said the quote "Veni, vidi, vici" which means "I came, I saw, I conquered".
> Finished chain.
'Julius Caesar said the quote "Veni, vidi, vici" which means "I came, I saw, I conquered".'
previous
HuggingFace Tools
next
IFTTT WebHooks
 Contents
  
Configuring the Input Function
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
.ipynb
.pdf
HuggingFace Tools
HuggingFace Tools#
Huggingface Tools supporting text I/O can be
loaded directly using the load_huggingface_tool function.
# Requires transformers>=4.29.0 and huggingface_hub>=0.14.1
!pip install --upgrade transformers huggingface_hub > /dev/null
from langchain.agents import load_huggingface_tool
tool = load_huggingface_tool("lysandre/hf-model-downloads")
print(f"{tool.name}: {tool.description}")
model_download_counter: This is a tool that returns the most downloaded model of a given task on the Hugging Face Hub. It takes the name of the category (such as text-classification, depth-estimation, etc), and returns the name of the checkpoint
tool.run("text-classification")
'facebook/bart-large-mnli'
previous
GraphQL tool
next
Human as a tool
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
'engines': ['github'],
  'category': 'it'},
 {'snippet': 'This repository contains the code, data, and models of the paper '
             'titled "XL-Sum: Large-Scale Multilingual Abstractive '
             'Summarization for 44 Languages" published in Findings of the '
             'Association for Computational Linguistics: ACL-IJCNLP 2021.',
  'title': 'xl-sum',
  'link': 'https://github.com/csebuetnlp/xl-sum',
  'engines': ['github'],
  'category': 'it'}]
previous
Search Tools
next
SerpAPI
 Contents
  
Custom Parameters
Obtaining results with metadata
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
.ipynb
.pdf
Python REPL
Python REPL#
Sometimes, for complex calculations, rather than have an LLM generate the answer directly, it can be better to have the LLM generate code to calculate the answer, and then run that code to get the answer. In order to easily do that, we provide a simple Python REPL to execute commands in.
This interface will only return things that are printed - therefor, if you want to use it to calculate an answer, make sure to have it print out the answer.
from langchain.agents import Tool
from langchain.utilities import PythonREPL
python_repl = PythonREPL()
python_repl.run("print(1+1)")
'2\n'
# You can create the tool to pass to an agent
repl_tool = Tool(
    name="python_repl",
    description="A Python shell. Use this to execute python commands. Input should be a valid python command. If you want to see the output of a value, you should print it out with `print(...)`.",
    func=python_repl.run
)
previous
OpenWeatherMap API
next
Requests
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
write_tool.run({"file_path": "example.txt", "text": "Hello World!"})
'File written successfully to example.txt.'
# List files in the working directory
list_tool.run({})
'example.txt'
previous
DuckDuckGo Search
next
Google Places
 Contents
  
The FileManagementToolkit
Selecting File System Tools
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
Thought:The available t shirts in Klarna are Lacoste Men's Pack of Plain T-Shirts, Hanes Men's Ultimate 6pk. Crewneck T-Shirts, Nike Boy's Jordan Stretch T-shirts, Polo Classic Fit Cotton V-Neck T-Shirts 3-Pack, and adidas Comfort T-shirts Men's 3-pack.
Final Answer: The available t shirts in Klarna are Lacoste Men's Pack of Plain T-Shirts, Hanes Men's Ultimate 6pk. Crewneck T-Shirts, Nike Boy's Jordan Stretch T-shirts, Polo Classic Fit Cotton V-Neck T-Shirts 3-Pack, and adidas Comfort T-shirts Men's 3-pack.
> Finished chain.
"The available t shirts in Klarna are Lacoste Men's Pack of Plain T-Shirts, Hanes Men's Ultimate 6pk. Crewneck T-Shirts, Nike Boy's Jordan Stretch T-shirts, Polo Classic Fit Cotton V-Neck T-Shirts 3-Pack, and adidas Comfort T-shirts Men's 3-pack."
previous
Bing Search
next
DuckDuckGo Search
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
.ipynb
.pdf
Wolfram Alpha
Wolfram Alpha#
This notebook goes over how to use the wolfram alpha component.
First, you need to set up your Wolfram Alpha developer account and get your APP ID:
Go to wolfram alpha and sign up for a developer account here
Create an app and get your APP ID
pip install wolframalpha
Then we will need to set some environment variables:
Save your APP ID into WOLFRAM_ALPHA_APPID env variable
pip install wolframalpha
import os
os.environ["WOLFRAM_ALPHA_APPID"] = ""
from langchain.utilities.wolfram_alpha import WolframAlphaAPIWrapper
wolfram = WolframAlphaAPIWrapper()
wolfram.run("What is 2x+5 = -3x + 7?")
'x = 2/5'
previous
Wikipedia
next
YouTubeSearchTool
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
{'snippet': '<b>Apples</b> boast many vitamins and minerals, though not in high amounts. However, <b>apples</b> are usually a good source of vitamin C. Vitamin C. Also called ascorbic acid, this vitamin is a common ...',
  'title': 'Apples 101: Nutrition Facts and Health Benefits',
  'link': 'https://www.healthline.com/nutrition/foods/apples'},
 {'snippet': 'Weight management. The fibers in <b>apples</b> can slow digestion, helping one to feel greater satisfaction after eating. After following three large prospective cohorts of 133,468 men and women for 24 years, researchers found that higher intakes of fiber-rich fruits with a low glycemic load, particularly <b>apples</b> and pears, were associated with the least amount of weight gain over time.',
  'title': 'Apples | The Nutrition Source | Harvard T.H. Chan School of Public Health',
  'link': 'https://www.hsph.harvard.edu/nutritionsource/food-features/apples/'}]
previous
Shell Tool
next
ChatGPT Plugins
 Contents
  
Number of results
Metadata Results
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
Now, we are trying to find information about non-existing article. In this case, the response is “No good Arxiv Result was found”
docs = arxiv.run("1605.08386WWW")
docs
'No good Arxiv Result was found'
previous
Apify
next
AWS Lambda API
 Contents
  
The ArXiv API Wrapper
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
.ipynb
.pdf
Zapier Natural Language Actions API
 Contents 
Zapier Natural Language Actions API
Example with Agent
Example with SimpleSequentialChain
Zapier Natural Language Actions API#
Full docs here: https://nla.zapier.com/api/v1/docs
Zapier Natural Language Actions gives you access to the 5k+ apps, 20k+ actions on Zapier’s platform through a natural language API interface.
NLA supports apps like Gmail, Salesforce, Trello, Slack, Asana, HubSpot, Google Sheets, Microsoft Teams, and thousands more apps: https://zapier.com/apps
Zapier NLA handles ALL the underlying API auth and translation from natural language –> underlying API call –> return simplified output for LLMs. The key idea is you, or your users, expose a set of actions via an oauth-like setup window, which you can then query and execute via a REST API.
NLA offers both API Key and OAuth for signing NLA API requests.
Server-side (API Key): for quickly getting started, testing, and production scenarios where LangChain will only use actions exposed in the developer’s Zapier account (and will use the developer’s connected accounts on Zapier.com)
User-facing (Oauth): for production scenarios where you are deploying an end-user facing application and LangChain needs access to end-user’s exposed actions and connected accounts on Zapier.com
This quick start will focus on the server-side use case for brevity. Review full docs or reach out to nla@zapier.com for user-facing oauth developer support.
This example goes over how to use the Zapier integration with a SimpleSequentialChain, then an Agent.
In code, below:
%load_ext autoreload
%autoreload 2
import os
# get from https://platform.openai.com/
%autoreload 2
import os
# get from https://platform.openai.com/
os.environ["OPENAI_API_KEY"] = os.environ.get("OPENAI_API_KEY", "")
# get from https://nla.zapier.com/demo/provider/debug (under User Information, after logging in): 
os.environ["ZAPIER_NLA_API_KEY"] = os.environ.get("ZAPIER_NLA_API_KEY", "")
Example with Agent#
Zapier tools can be used with an agent. See the example below.
from langchain.llms import OpenAI
from langchain.agents import initialize_agent
from langchain.agents.agent_toolkits import ZapierToolkit
from langchain.agents import AgentType
from langchain.utilities.zapier import ZapierNLAWrapper
## step 0. expose gmail 'find email' and slack 'send channel message' actions
# first go here, log in, expose (enable) the two actions: https://nla.zapier.com/demo/start -- for this example, can leave all fields "Have AI guess"
# in an oauth scenario, you'd get your own <provider> id (instead of 'demo') which you route your users through first
llm = OpenAI(temperature=0)
zapier = ZapierNLAWrapper()
toolkit = ZapierToolkit.from_zapier_nla_wrapper(zapier)
agent = initialize_agent(toolkit.get_tools(), llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
agent.run("Summarize the last email I received regarding Silicon Valley Bank. Send the summary to the #test-zapier channel in slack.")
> Entering new AgentExecutor chain...
 I need to find the email and summarize it.
Action: Gmail: Find Email
Action Input: Find the latest email from Silicon Valley Bank
> Finished chain.
'{"message__text": "Dear Silicon Valley Bridge Bank, \\n\\nThank you for your email and the update regarding your new CEO Tim Mayopoulos. We appreciate your dedication to keeping your clients and partners informed and we look forward to continuing our relationship with you. \\n\\nBest regards, \\n[Your Name]", "message__permalink": "https://langchain.slack.com/archives/D04TKF5BBHU/p1678859968241629", "channel": "D04TKF5BBHU", "message__bot_profile__name": "Zapier", "message__team": "T04F8K3FZB5", "message__bot_id": "B04TRV4R74K", "message__bot_profile__deleted": "false", "message__bot_profile__app_id": "A024R9PQM", "ts_time": "2023-03-15T05:59:28Z", "message__blocks[]block_id": "p7i", "message__blocks[]elements[]elements[]type": "[[\'text\']]", "message__blocks[]elements[]type": "[\'rich_text_section\']"}'
previous
YouTubeSearchTool
next
Agents
 Contents
  
Zapier Natural Language Actions API
Example with Agent
Example with SimpleSequentialChain
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
Job Status: Status.IN_QUEUE eta: 42.49370198879602
Job Status: Status.IN_QUEUE eta: 21.314297944849187
Observation: /var/folders/bm/ylzhm36n075cslb9fvvbgq640000gn/T/tmp5snj_nmzf20_cb3m.mp4
Thought: Do I need to use a tool? No
AI: Here is a video of a painting of a dog sitting on a skateboard.
> Finished chain.
previous
Google Serper API
next
GraphQL tool
 Contents
  
Using a tool
Using within an agent
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
repl_tool = Tool(
    name="python_repl",
    description="A Python shell. Use this to execute python commands. Input should be a valid python command. If you want to see the output of a value, you should print it out with `print(...)`.",
    func=search.run,
)
previous
SearxNG Search API
next
Wikipedia
 Contents
  
Custom Parameters
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
loader = apify.call_actor(
    actor_id="apify/website-content-crawler",
    run_input={"startUrls": [{"url": "https://python.langchain.com/en/latest/"}]},
    dataset_mapping_function=lambda item: Document(
        page_content=item["text"] or "", metadata={"source": item["url"]}
    ),
)
Initialize the vector index from the crawled documents:
index = VectorstoreIndexCreator().from_loaders([loader])
And finally, query the vector index:
query = "What is LangChain?"
result = index.query_with_sources(query)
print(result["answer"])
print(result["sources"])
 LangChain is a standard interface through which you can interact with a variety of large language models (LLMs). It provides modules that can be used to build language model applications, and it also provides chains and agents with memory capabilities.
https://python.langchain.com/en/latest/modules/models/llms.html, https://python.langchain.com/en/latest/getting_started/getting_started.html
previous
Tool Input Schema
next
ArXiv API Tool
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
.ipynb
.pdf
YouTubeSearchTool
YouTubeSearchTool#
This notebook shows how to use a tool to search YouTube
Adapted from venuv/langchain_yt_tools
#! pip install youtube_search
from langchain.tools import YouTubeSearchTool
tool = YouTubeSearchTool()
tool.run("lex friedman")
"['/watch?v=VcVfceTsD0A&pp=ygUMbGV4IGZyaWVkbWFu', '/watch?v=gPfriiHBBek&pp=ygUMbGV4IGZyaWVkbWFu']"
You can also specify the number of results that are returned
tool.run("lex friedman,5")
"['/watch?v=VcVfceTsD0A&pp=ygUMbGV4IGZyaWVkbWFu', '/watch?v=YVJ8gTnDC4Y&pp=ygUMbGV4IGZyaWVkbWFu', '/watch?v=Udh22kuLebg&pp=ygUMbGV4IGZyaWVkbWFu', '/watch?v=gPfriiHBBek&pp=ygUMbGV4IGZyaWVkbWFu', '/watch?v=L_Guz73e6fw&pp=ygUMbGV4IGZyaWVkbWFu']"
previous
Wolfram Alpha
next
Zapier Natural Language Actions API
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
> Finished chain.
'["https://blog.langchain.dev/", "https://discord.gg/6adMQxSpJS", "https://docs.langchain.com/docs/", "https://github.com/hwchase17/chat-langchain", "https://github.com/hwchase17/langchain", "https://github.com/hwchase17/langchainjs", "https://github.com/sullivan-sean/chat-langchainjs", "https://js.langchain.com/docs/", "https://python.langchain.com/en/latest/", "https://twitter.com/langchainai"]'
previous
AWS Lambda API
next
Bing Search
 Contents
  
Use with Agents
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
    verbose=True
)
agent_chain.run("What's the weather like in London?")
> Entering new AgentExecutor chain...
 I need to find out the current weather in London.
Action: OpenWeatherMap
Action Input: London,GB
Observation: In London,GB, the current weather is as follows:
Detailed status: broken clouds
Wind speed: 2.57 m/s, direction: 240°
Humidity: 56%
Temperature: 
  - Current: 20.11°C
  - High: 21.75°C
  - Low: 18.68°C
  - Feels like: 19.64°C
Rain: {}
Heat index: None
Cloud cover: 75%
Thought: I now know the current weather in London.
Final Answer: The current weather in London is broken clouds, with a wind speed of 2.57 m/s, direction 240°, humidity of 56%, temperature of 20.11°C, high of 21.75°C, low of 18.68°C, and a heat index of None.
> Finished chain.
'The current weather in London is broken clouds, with a wind speed of 2.57 m/s, direction 240°, humidity of 56%, temperature of 20.11°C, high of 21.75°C, low of 18.68°C, and a heat index of None.'
previous
Metaphor Search
next
Python REPL
 Contents
  
Use the wrapper
Use the tool
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
Thought: I now know the final answer
Final Answer: The current weather in Pomfret is mainly cloudy with snow showers around in the morning. The temperature is around 40F with winds NNW at 5 to 10 mph. Chance of snow is 40%.
> Finished chain.
'The current weather in Pomfret is mainly cloudy with snow showers around in the morning. The temperature is around 40F with winds NNW at 5 to 10 mph. Chance of snow is 40%.'
previous
SceneXplain
next
SearxNG Search API
 Contents
  
Google Serper API Wrapper
SerpAPI
GoogleSearchAPIWrapper
SearxNG Meta Search Engine
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
.rst
.pdf
Retrievers
Retrievers#
Note
Conceptual Guide
The retriever interface is a generic interface that makes it easy to combine documents with
language models. This interface exposes a get_relevant_documents method which takes in a query
(a string) and returns a list of documents.
Please see below for a list of all the retrievers supported.
Arxiv
Azure Cognitive Search Retriever
ChatGPT Plugin
Self-querying with Chroma
Cohere Reranker
Contextual Compression
Stringing compressors and document transformers together
Databerry
ElasticSearch BM25
kNN
Metal
Pinecone Hybrid Search
Self-querying
SVM
TF-IDF
Time Weighted VectorStore
VectorStore
Vespa
Weaviate Hybrid Search
Wikipedia
previous
Zilliz
next
Arxiv
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.
)
Hopefully this highlights what is going on under the hood of VectorstoreIndexCreator. While we think it’s important to have a simple way to create indexes, we also think it’s important to understand what’s going on under the hood.
previous
Indexes
next
Document Loaders
 Contents
  
One Line Index Creation
Walkthrough
By Harrison Chase
    
      © Copyright 2023, Harrison Chase.
      
  Last updated on May 18, 2023.