Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
luca.gobbi committed Jun 14, 2024
1 parent 7eb4984 commit 7481f24
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 149 deletions.
18 changes: 1 addition & 17 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,10 @@ permissions:
env:
PLUGIN_JSON: "0.0.1"
TAG_EXISTS: false
PLUGIN_NAME: "my_plugin"
PLUGIN_NAME: "white_rabbit_in_action"

jobs:
# This will be deleted by setup.py
check:
runs-on: ubuntu-latest
outputs:
plugin_name: ${{ steps.init.outputs.plugin_name }}
steps:
- name: Get plugin name
id: init
run: |
echo "plugin_name=${{ env.PLUGIN_NAME }}" >> $GITHUB_OUTPUT
# This is the end of the removed section
release:
# This will be deleted by setup.py
needs: check
if: startsWith(needs.check.outputs.plugin_name, 'MY_PLUGIN') == false
# This is the end of the removed section
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand Down
58 changes: 43 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,54 @@
# My plugin
# White Rabbit In Action

<img src="./assets/white-rabbit-in-action.png" width=400>

[![awesome plugin](https://custom-icon-badges.demolab.com/static/v1?label=&message=awesome+plugin&color=383938&style=for-the-badge&logo=cheshire_cat_ai)](https://)
[![Awesome plugin](https://custom-icon-badges.demolab.com/static/v1?label=&message=Awesome+plugin&color=000000&style=for-the-badge&logo=cheshire_cat_ai)](https://)
[![awesome plugin](https://custom-icon-badges.demolab.com/static/v1?label=&message=awesome+plugin&color=F4F4F5&style=for-the-badge&logo=cheshire_cat_black)](https://)

Write here all the useful information about your plugin.
White Rabbit In Action is a Cheshire Cat plugin that seamlessly integrates scheduling capabilities into your plugins and tools without requiring you to write specific scheduling code.

Let the White Rabbit decide when your tools are executed!

## Overview

The White Rabbit In Action introduces a new Python annotation into the framework called `@white_rabbit_tool`, an enhanced version of the standard Cheshire Cat `@tool`. The `@white_rabbit_tool` allows you to bypass writing specific scheduling code by utilizing the White Rabbit component within the Cheshire Cat framework. It wraps your function similarly to the standard `@tool`, but with added capabilities for handling scheduling functions. This means the Agent can determine whether the triggered tool needs immediate execution (as usual) or should be scheduled to run in the future or according to a specified cron expression.

This repository is the template to automate the release of official Cheshire Cat AI plugins.
## Key Features

- **Automatic Scheduling**: The White Rabbit component manages the timing of tool execution, freeing you from the complexity of scheduling code.
- **Intelligent Execution**: The Agent understands if a tool needs instant execution or should be scheduled for the future.
- **Flexible Scheduling Options**: Schedule tools to run at specific times or intervals using cron expressions.

## Usage

1. Create a new repository clicking on the `Use this template` button.
2. Clone your new repo directly in the Cat's `plugins` folder.
3. Run the `setup.py` script:
```bash
python setup.py
### How to use the annotation

To enable scheduling capabilities of White Rabbit In Action, apply the `@white_rabbit_tool` annotation to your functions, just like you would normally do with standard Cheshire Cat tools. This allows the Agent to manage their execution schedule.

```python

from white_rabbit_in_action import white_rabbit_tool

@white_rabbit_tool
def turn_on_lights(cat):
"""
Call this tool whenever the user wants to turn on the lights.
"""

cat.send_ws_message("Turning on the lights", msg_type="chat")

```
The script will prompt you to write the name of your plugin and make an initial setup setting the name in the files.

4. Start developing!
> **Warning ⚠️**
>
> This implementation modifies some mechanisms within the Cheshire Cat framework. As a consequence, certain features, such as `return_direct`, will not work as expected. Please consider this limitation when developing your own plugins.

### Managing scheduled jobs

In addition to scheduling, White Rabbit In Action provides basic Cheshire Cat tools to manage scheduled jobs in a conversational manner. These tools include:

> **Important**
> A new release of your plugin is triggered every time you set a new `version` in the `plugin.json` file.
> Please, remember to set it correctly every time you want to release an update.
- **Retrieve Scheduled Jobs**: View a list of all scheduled jobs.
- **Pause a Scheduled Job**: Temporarily halt a scheduled job without removing it.
- **Resume a Scheduled Job**: Reactivate a paused job.
- **Remove a Scheduled Job**: Delete a scheduled job.

Binary file added assets/white-rabbit-in-action.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 0 additions & 31 deletions my_plugin.py

This file was deleted.

14 changes: 7 additions & 7 deletions plugin.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"name": "My plugin",
"name": "White Rabbit In Action",
"version": "0.0.1",
"description": "Description of my_plugin.",
"author_name": "Me",
"author_url": "https://mywebsite.me",
"plugin_url": "https://github.com/my_name/my_plugin",
"tags": "cat, template, example",
"thumb": "https://raw.githubusercontent.com/my_repo_path/my_plugin.png"
"description": "White Rabbit In Action is a Cheshire Cat's plugin designed to enhance the capabilities of the White Rabbit scheduler.",
"author_name": "Luca Gobbi",
"author_url": "https://github.com/lucagobbi",
"plugin_url": "https://github.com/lucagobbi/white-rabbit-in-action",
"tags": "cat, white rabbit, scheduler, tools, planner",
"thumb": "https://raw.githubusercontent.com/lucagobbi/white-rabbit-in-action/main/assets/white-rabbit-in-action.png"
}
79 changes: 0 additions & 79 deletions setup.py

This file was deleted.

25 changes: 25 additions & 0 deletions utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import inspect
import re
from typing import Callable


def extract_args_as_kwargs(func: Callable, *args, **kwargs) -> dict:
sig = inspect.signature(func)
param_names = list(sig.parameters.keys())

for name, arg in zip(param_names, args):
if name not in kwargs:
kwargs[name] = arg
return kwargs


def parse_cron_expression(cron_expression: str):
keys = ['minute', 'hour', 'day', 'month', 'day_of_week']
return dict(zip(keys, cron_expression.split()))


def extract_int_from_string(s: str):
match = re.search(r'\d+', s)
if match:
return int(match.group())
raise ValueError("No integer found in the string.")
130 changes: 130 additions & 0 deletions white_rabbit_in_action.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
from typing import Callable, Union, List

from cat.plugins.white_rabbit_in_action.utils import extract_int_from_string, parse_cron_expression, \
extract_args_as_kwargs
from cat.mad_hatter.decorators.tool import CatTool
from cat.mad_hatter.decorators import tool
from cat.log import log

from cat.looking_glass.stray_cat import StrayCat


def classify_schedule(stray_cat: StrayCat) -> str:
user_message = stray_cat.working_memory.user_message_json.text
example_labels = {
"fixed_date": ["Turn on the light in 30 seconds"],
"cron_based": ["Turn on the light every morning at 7am"],
"unscheduled": ["Turn on the light"]
}
schedule_type = stray_cat.classify(f"Extract the exact schedule from the following message: {user_message}",
example_labels)
return schedule_type


def schedule_function(type_of_schedule: str, func: Callable[[str], str], *args, **kwargs) -> Callable[[], None]:
stray_cat = kwargs["cat"]
user_message = stray_cat.working_memory.user_message_json.text

def extract_seconds() -> int:
seconds_expression = stray_cat.llm(
f"Extract the exact number of seconds from the following message: {user_message}")
return extract_int_from_string(seconds_expression)

if type_of_schedule == "fixed_date":
seconds = extract_seconds()
log.debug(f"WhiteRabbitInAction - Schedule date: {seconds}")
return stray_cat.white_rabbit.schedule_job(func, seconds=seconds, *args, **kwargs)
elif type_of_schedule == "cron_based":
cron_expression = stray_cat.llm(f"Deduce the exact cron expression (e.g. 0 7 * * *) from this message: {user_message}")
log.debug(f"WhiteRabbitInAction - Schedule cron: {cron_expression}")
kwargs = extract_args_as_kwargs(func, *args, **kwargs)
return stray_cat.white_rabbit.schedule_cron_job(func, **parse_cron_expression(cron_expression), **kwargs)
else:
raise ValueError(f"WhiteRabbitInAction - Unknown schedule type: {type_of_schedule}")


def white_rabbit_tool(*args: Union[str, Callable], return_direct: bool = False, examples: List[str] = []) -> Callable:
def _make_with_name(tool_name: str) -> Callable:
def _make_tool(func: Callable[[str], str]) -> CatTool:

def white_rabbit_func(*in_args, **kwargs):
stray_cat = kwargs['cat']
schedule_type = classify_schedule(stray_cat)
log.debug(f"WhiteRabbitInAction - Classified schedule_type: {schedule_type}")

if schedule_type == "unscheduled":
log.debug(f"WhiteRabbitInAction - Running function {func.__name__}")
return func(*in_args, **kwargs)

log.debug(f"WhiteRabbitInAction - Scheduling function {func.__name__}")
schedule_function(schedule_type, func, *in_args, **kwargs)
return "Roger that. I'll run it later."

assert func.__doc__, "Function must have a docstring"
white_rabbit_func.__doc__ = func.__doc__

tool_ = CatTool(
name=tool_name,
func=white_rabbit_func,
return_direct=return_direct,
examples=examples,
)

return tool_

return _make_tool

if len(args) == 1 and isinstance(args[0], str):
return _make_with_name(args[0])
elif len(args) == 1 and callable(args[0]):
return _make_with_name(args[0].__name__)(args[0])
elif len(args) == 0:
def _partial(func: Callable[[str], str]) -> CatTool:
return _make_with_name(func.__name__)(func)

return _partial
else:
raise ValueError("Too many arguments for tool decorator")


@tool
def get_running_jobs(user_input, cat: StrayCat):
"""
Call this tool whenever the user wants to get the running scheduled jobs.
"""
return [{"id": job["id"], "name": job["name"], "next_run": job["next_run"].isoformat() if job["next_run"] else None}
for job in cat.white_rabbit.get_jobs()]


@tool(return_direct=True)
def remove_job_by_id(job_id: str, cat: StrayCat):
"""
Call this tool whenever the user wants to remove a scheduled job by id.
"""
return cat.white_rabbit.remove_job(job_id)


@tool(return_direct=True)
def pause_job_by_id(job_id: str, cat: StrayCat):
"""
Call this tool whenever the user wants to pause a scheduled job by id.
"""
return cat.white_rabbit.pause_job(job_id)


@tool(return_direct=True)
def resume_job_by_id(job_id: str, cat: StrayCat):
"""
Call this tool whenever the user wants to resume a scheduled job by id.
"""
return cat.white_rabbit.resume_job(job_id)


# Example usage of the white rabbit tool
#
# @white_rabbit_tool
# def turn_on_lights(cat: StrayCat):
# """
# Call this tool whenever the user wants to turn on the lights.
# """
# cat.send_ws_message("Turning on the lights", msg_type="chat")

0 comments on commit 7481f24

Please sign in to comment.