Creating Custom Skills in Pymirokai

Pymirokai allows you to create skills to enhance its capabilities according to your needs.


How to Create a Custom Skill

1. Use the @skill Decorator

The @skill decorator is required to register the function as a valid skill.

2. Define Access Level

Specify who can use the skill with the access_level parameter.

3. Describe the Skill for Natural Language Interaction

To allow natural triggering of the skill, define verbal_descriptions with example phrases in multiple languages.

Example:

verbal_descriptions={
    "en": ["find rhymes", "words that rhyme", "rhyme finder"],
    "fr": ["trouver des rimes", "mots qui riment", "rime"],
}

4. Define Input Parameters

Each parameter must be described so the LLM understands what values to provide.

Example:

parameters=[
    ParameterDescription(name="word", description="The word to find rhymes for."),
    ParameterDescription(name="max_results", description="Maximum number of rhymes to retrieve."),
]

5. Use Proper Function Signatures

  • Include type annotations for all parameters.

  • If interacting with Pymirokai’s built-in functions, add robot as a parameter.

Correct:

async def fetch_rhymes(robot: Robot, word: str, max_results: int = 5, timeout: int = 5) -> dict:

Incorrect (missing type annotations):

async def fetch_rhymes(word, max_results=5, timeout=5):  # Incorrect

6. Interacting with the Robot

If your skill interacts with Pymirokai functionalities, include the robot parameter:

async def fetch_rhymes(robot: Robot, word: str, max_results: int = 5, timeout: int = 5) -> dict:

This allows using robot actions such as:

await robot.wave().completed()  # Make the robot wave
await robot.say("Here are some rhymes!")  # Make the robot speak

7. Returning the Response

A skill can return:

  • Nothing

  • A string

  • A list

  • A dictionary

  • Or anything else that is JSON-serializable

However, if you want the LLM to reformulate the response, you must return a dictionary with the key "llm_output".

Correct Example (LLM can process and reformulate):

return {"llm_output": "Words that rhyme with 'fun': sun, run, gun, bun, pun."}

Incorrect Example for LLM: (LLM won’t reformulate)

return "Words that rhyme with 'fun': sun, run, gun, bun, pun."  # Just a string

Example: Fetch Rhymes Skill

Here is a complete example of a skill that finds rhyming words for a given input.

        graph TD;
    A["User request"] --> B["Skill receives input"];
    B --> C["Fetch rhymes from API"];
    C -->|"Success"| D["Extract API response"];
    C -->|"Failure"| E["Handle error and output"];
    D --> F["Format response"];
    F --> G["Robot announces rhymes"];
    G --> H["Return structured response"];
    E --> H;
    
import requests
from pymirokai.decorators.skill import skill, ParameterDescription
from pymirokai.enums.enums import AccessLevel, FaceAnim

@skill(
    access_level=AccessLevel.USER,  # Defines access level for the skill
    verbal_descriptions={
        "en": ["find rhymes", "words that rhyme", "rhyme finder"],
        "fr": ["trouver des rimes", "mots qui riment", "rime"],
    },
    parameters=[
        ParameterDescription(name="word", description="The word to find rhymes for."),
        ParameterDescription(name="max_results", description="Maximum number of rhymes to retrieve."),
    ],
)
async def fetch_rhymes(robot: Robot, word: str, max_results: int = 5, timeout: int = 5) -> dict:
    """Fetch words that rhyme with the given input word.

    Args:
        robot (Robot): The robot instance to interact with.
        word (str): The word to find rhymes for.
        max_results (int, optional): The maximum number of rhyming words to retrieve. Defaults to 5.
        timeout (int, optional): The timeout duration for the API request in seconds. Defaults to 5.

    Returns:
        dict: A dictionary containing the response output for the robot to announce.
    """
    try:
        # Fetch rhymes from the Datamuse API
        url = f"https://api.datamuse.com/words?rel_rhy={word}&max={max_results}"
        response = requests.get(url, timeout=timeout)
        response.raise_for_status()

        rhyme_data = response.json()

        if rhyme_data:
            rhymes = [entry["word"] for entry in rhyme_data]
            answer = f"Words that rhyme with '{word}': {', '.join(rhymes)}"
        else:
            answer = f"No rhymes found for '{word}'."

    except requests.RequestException:
        answer = "Couldn't fetch rhymes at this moment."

    await robot.play_face_reaction(FaceAnim.HAPPY_BIG_SMILE).completed()
    return {"llm_output": answer} # if you want the LLM to reformulate the answer

Final Notes

  • Ensure all arguments have type annotations.

  • If using robot, it must be included as a parameter.

  • Properly define verbal descriptions and parameter descriptions for better integration.