Skip to content

How to Write a Custom Plugin

Eliran Wong edited this page Jan 22, 2024 · 13 revisions

How to Write a Custom Plugin - Step-by-step Guide

This is a step-by-step guide to demonstrate how to write a custom LetMeDoItAI plugin.

You may refer to our original post at https://github.com/eliranwong/letmedoit/issues/31

Decide what you want to do with the plugin

In this demonstration, I am going to develop a LetMeDoIt AI plugin to check the latest news about something.

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)

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)

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"],
    },
}

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 ")

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

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 ""

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:

  • "~/letmedoit/plugins" is the default storage directory for custom plugins
  • You may change the storage directory via LetMeDoIt action menu
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 ")

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.

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

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

All plugins that shared with other users are made available at:

https://github.com/eliranwong/letmedoit/blob/main/package/letmedoit/plugins/

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

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

We finally uploaded this example plugin to:

https://github.com/eliranwong/letmedoit/blob/main/package/letmedoit/plugins/search%20latest%20news.py

Read more

About LetMeDoIt AI plugins

https://github.com/eliranwong/letmedoit/wiki/Plugins-%E2%80%90-Overview

About LetMeDoIt AI Plugins that support Function Calling

https://github.com/eliranwong/letmedoit/wiki/Plugins-%E2%80%90-Function-Calling

Installation

Installation
Installation on Android
Install a Supported Python Version
Install ffmpeg
Android Support
Install LetMeDoIt AI on Android Termux App Automatic Upgrade Option

Video Demonstration

Video Demo

Basics

Quick Guide
Action Menu
ChatGPT API Key
Use GPT4 Models
Google API Setup
ElevenLabs API Setup
OpenWeatherMap API Setup
Run Local LLM Offline
Token Management
Command Line Interface Options
Command Execution
Chat-only Features
Developer Mode
Save Chart Content Locally
Work with Text Selection
Work with File Selection
System Tray
Custom Key Bindings

Selective Features

Examples
Features
Change Assistant Name
Setup Multiple LetMeDoIt Assistants
Memory Capabilities
Data Visualization
Analyze Files
Analyze Images
Analyze Audio
Google and Outlook Calendars
Python Code Auto‐heal Feature
Integration with AutoGen
Integration with Google AI Tools
Integration with Open Interpreter
Speak to LetMeDoIt AI
LetMeDoIt Speaks
Speak multi‐dialects
Create a map anytime
Modify your images with simple words
Work with Database Files
Create a Team of AI Assistants
Search and Load Chat Records
Search Weather Information
Search Financial Data
Social Media

Plugins

Plugins ‐ Overview
Plugins - How to Write a Custom Plugin
Plugins ‐ Add Aliases
Plugins ‐ Input Suggestions
Plugins ‐ Install Additional Packages
Plugins ‐ Predefined Contexts
Plugins ‐ Transform Text Output
Plugins ‐ Work with LetMeDoIt AI Configurations
Plugins ‐ Function Calling
Plugins ‐ Run Codes with Specific Packages
Plugins ‐ Work with Non‐conversational Model
Plugins ‐ Integrate Text‐to‐speech Feature
Plugins ‐ Integrate Other Shared Utilities

Comparison

Compare with ChatGPT
Compare with Siri and Others

Clone this wiki locally