eliranwong / letmedoit

An advanced AI assistant that leverages the capabilities of ChatGPT API, Gemini Pro, AutoGen, and open-source LLMs, enabling it both to engage in conversations and to execute computing tasks on local devices.
https://letmedoit.ai
GNU General Public License v3.0
118 stars 25 forks source link

Wiki: How to Write a Custom Plugin - a step-by-step guide #31

Closed eliranwong closed 8 months ago

eliranwong commented 8 months ago

I will write a wiki page to demonstrate step-by-step how to Write a Custom Plugin.

The purpose of this example plugin is to check the latest news about something.

Before I write a full wiki page, I will update this post when I complete a step.

After I complete all steps, I will put all steps together in https://github.com/eliranwong/letmedoit/wiki/How-to-Write-a-Custom-Plugin

eliranwong commented 8 months ago

STEP 1 - Research

To research about how to perform the task for searching the latest news, I used LetMeDoIt AI to ask Codey to provide me with related information.

In LetMeDoIt prompt, I entered:

Ask Codey how to check the latest news in my area, for example, London, UK, with python?

I got the following response:

import feedparser

# Get the latest news from a specific RSS feed
feed_url = "https://news.google.com/rss/search?q=London+UK&hl=en-US&gl=US&ceid=US:en"
feed = feedparser.parse(feed_url)

# Print the title and link of each news item
for entry in feed.entries:
    print(entry.title)
    print(entry.link)
eliranwong commented 8 months ago

STEP 2 - Develop a Function Method for Task Execution

I need to develop a function method that accepts keywords as argument for searching the latest news.

Based on the research in Step 1 above, the url for searching the news is:

"https://news.google.com/rss/search?q=London+UK&hl=en-US&gl=US&ceid=US:en"

I observed two things:

  1. Two keywords "London" and "UK" placed between "q=" and "&"
  2. The keywords are connected with a "+" sign

Therefore, I modified the code and developed a simple function method below that can accept a list for keywords for searching the latest news:

import feedparser

# Get the latest news from a specific RSS feed
def search_latest_news(keywords: str) -> None:
    feed_url = f"https://news.google.com/rss/search?q={keywords}&hl=en-US&gl=US&ceid=US:en"
    feed = feedparser.parse(feed_url)

    # Print the title and link of each news item
    for entry in feed.entries:
        print(entry.title)
        print(entry.link)
eliranwong commented 8 months ago

STEP 3 - Develop a Function Signature

As I want LetMeDoIt AI to extract the keywords from user input, which is natural language, I need to develop a function signature to work with ChatGPT model that supports function calling.

I decide that I need only one variable in the signature, i.e. keyword

Below is the signature that I prepared for the plugin:

functionSignature = {
    "name": "search_latest_news",
    "description": "Search the latest news with given keywords",
    "parameters": {
        "type": "object",
        "properties": {
            "keywords": {
                "type": "string",
                "description": "The keywords for searching the latest news, delimited by plus sign '+'.  For example, return 'London+UK' if keywords are 'London' and 'UK'.",
            },
        },
        "required": ["keywords"],
    },
}
eliranwong commented 8 months ago

STEP 4 - Integration into LetMeDoIt AI

Add the following lines to the plugin. You may read the self-explanatory comments in this snippet.

# Add the following line to tell LetMeDoIt AI that this plugin work with function calling feature.
config.pluginsWithFunctionCall.append("search_latest_news")

# The following line adds the function signature, that we prepared in STEP 3, to the full list of all function signatures that work with LetMeDoIt AI.
config.chatGPTApiFunctionSignatures.append(functionSignature)

# The following line tells LetMeDoIt AI to call the method "search_latest_news" that we added in this plugin when the function signature "search_latest_news" is loaded.
config.chatGPTApiAvailableFunctions["search_latest_news"] = search_latest_news

# The following line is optional. It adds an input suggestion to LetMeDoIt AI user input prompt
config.inputSuggestions.append("Give me the latest news about ")
eliranwong commented 8 months ago

STEP 5 - Modify the Task Execution Method

In this step, we modified the task execution method we developed in STEP 2.

As LetMeDoIt AI passes all arguments to the called function as a dictionary, we made the following changes:

  1. change the argument type to dictionary and name the argument as "function_args".
  2. add a line, the first line, in the method to get the string "keywords" from the dictionary "function_args".
  3. Return an empty string to tell LetMeDoIt AI not to generate a follow-up response after the method is executed.
import feedparser

# Get the latest news from a specific RSS feed
def search_latest_news(function_args: dict) -> str:
    keywords = function_args.get("keywords")
    feed_url = f"https://news.google.com/rss/search?q={keywords}&hl=en-US&gl=US&ceid=US:en"
    feed = feedparser.parse(feed_url)

    # Print the title and link of each news item
    for entry in feed.entries:
        print(entry.title)
        print(entry.link)
    return ""

In this example, we make it simple to print all the information when the function method "search_latest_news" is called and return an empty string.

Depending on what you want, you can finish the function method differently. For example, if you want to pass the retrieved information to ChatGPT to generate a response based on the retrieved information, instead of printing all information directly, you can modify the method like this one below:

import feedparser, json

# Get the latest news from a specific RSS feed
def search_latest_news(function_args: dict) -> str:
    keywords = function_args.get("keywords")
    feed_url = f"https://news.google.com/rss/search?q={keywords}&hl=en-US&gl=US&ceid=US:en"
    feed = feedparser.parse(feed_url)

    # Pass the retrieved information to ChatGPT to generate a further response
    info = {}
    for index, entry in enumerate(feed.entries):
        info[f"news {index}"] = {
            "title": entry.title,
            "link": entry.link,
        }
    return json.dumps(info)

Read more at https://github.com/eliranwong/letmedoit/wiki/Plugins-%E2%80%90-Function-Calling#returning-function-call-response-to-chatgpt

eliranwong commented 8 months ago

STEP 6 - Fine Tune Text Display Style [OPTIONAL]

This step is optional. I modified the function method further to display the information with print methods shared in object "config". In object "config", method "print" supports word wrap feature, and method "print2" print all content in color. In this modified method, each entry is divided by "config.divider". "config.stopSpinning" is added to stop spinning before displaying retrieved information. In addition, I limit the results up to 10 entries.

from letmedoit import config
import feedparser

# Get the latest news from a specific RSS feed
def search_latest_news(function_args: dict) -> str:
    keywords = function_args.get("keywords")
    feed_url = f"https://news.google.com/rss/search?q={keywords}&hl=en-US&gl=US&ceid=US:en"
    feed = feedparser.parse(feed_url)

    # Print the title and link of each news item
    config.stopSpinning()
    config.print2(config.divider)
    for index, entry in enumerate(feed.entries):
        if index < 10:
            if not index == 0:
                config.print2(config.divider)
            config.print(entry.title)
            print(entry.link)
    config.print2(config.divider)
    return ""
eliranwong commented 8 months ago

STEP 7 - Save the Plugin

  1. Open a text editor
  2. Save the following content with a filename "search latest news.py" in "\~/letmedoit/plugins"

Remarks:

from letmedoit import config
import feedparser

# Function method to get the latest news from a specific RSS feed
def search_latest_news(function_args: dict) -> str:
    keywords = function_args.get("keywords")
    feed_url = f"https://news.google.com/rss/search?q={keywords}&hl=en-US&gl=US&ceid=US:en"
    feed = feedparser.parse(feed_url)

    # Print the title and link of each news item
    config.stopSpinning()
    config.print2(config.divider)
    for index, entry in enumerate(feed.entries):
        if index < 10:
            if not index == 0:
                config.print2(config.divider)
            config.print(entry.title)
            print(entry.link)
    config.print2(config.divider)
    return ""

# Function signature to work with ChatGPT function calling
functionSignature = {
    "name": "search_latest_news",
    "description": "Search the latest news with given keywords",
    "parameters": {
        "type": "object",
        "properties": {
            "keywords": {
                "type": "string",
                "description": "The keywords for searching the latest news, delimited by plus sign '+'.  For example, return 'London+UK' if keywords are 'London' and 'UK'.",
            },
        },
        "required": ["keywords"],
    },
}

# Add the following line to tell LetMeDoIt AI that this plugin work with function calling feature.
config.pluginsWithFunctionCall.append("search_latest_news")

# The following line adds the function signature, that we prepared in STEP 3, to the full list of all function signatures that work with LetMeDoIt AI.
config.chatGPTApiFunctionSignatures.append(functionSignature)

# The following line tells LetMeDoIt AI to call the method "search_latest_news" that we added in this plugin when the function signature "search_latest_news" is loaded.
config.chatGPTApiAvailableFunctions["search_latest_news"] = search_latest_news

# The following line is optional. It adds an input suggestion to LetMeDoIt AI user input prompt
config.inputSuggestions.append("Give me the latest news about ")
eliranwong commented 8 months ago

STEP 8 - Preparation for Testing

Check if your plugin's dependencies are installed with LetMeDoIt AI package by default at https://github.com/eliranwong/letmedoit/blob/main/package/letmedoit/requirements.txt

If not, activate your LetMeDoIt AI environment and install dependencies.

In this case, we install package "feedparser", run in terminal:

cd \~/apps/letmedoit/bin/activate

pip install feedparser

Remarks: "\~/apps/letmedoit" is where we set up an environment for installing LetMeDoIt AI on our tested device.

eliranwong commented 8 months ago

STEP 9 - Testing

I launched LetMeDoIt AI and entered the following prompt:

Give me the latest news about ChatGPT

Below is the screenshot of the result.

search_latest_news

eliranwong commented 8 months ago

STEP 10 - Share Your Plugin With Other Users [OPTIONAL]

To share your plugin with other users, submit a pull request to our repository at:

https://github.com/eliranwong/letmedoit/

eliranwong commented 8 months ago

completed https://github.com/eliranwong/letmedoit/wiki/How-to-Write-a-Custom-Plugin