Create a New Task#
In this tutorial, we detail the adding of tasks within the openCHA framework and their introduction as external sources to the Task Planner. We will describe the types of tasks that can be added to the framework, outline the implementation process for tasks, and present an example of a task that has been implemented, along with sample prompts associated with it.
Types of Implementation#
Tasks may be implemented locally or as a service:
Local Implementation: This involves coding all necessary components on-site to ensure successful task execution. This may involve providing a Python library, a GitHub repository, or a complete task implementation.
Service-based Implementation: This involves hosting services on a server and offering APIs for utilization. The service provider is responsible for maintaining the task and providing comprehensive documentation for users, including registration procedures, API key acquisition, and privacy policies.
Task Example#
First you need to create a python file inside the tasks folder. If your task is a complecated task which contains multiple python files, create a new folder inside the tasks folder. The main task implementaion should inherit from tasks.task.BaseTask from the task.py file. This file contains some high level implementations and abstract methods which needs to be implemented by you for your specific task. The configuration parameters inside the task file which you need to implement in openCHA framework are as follows:
- name
- Purpose:
Uniquely identifies the task within the system; this name will appear in the interface for task selection.
- Convention:
Generally in lowercase underscore_case, often derived from the task’s functionality for better readability and maintainability.
- Example:
name = "google_search"
- chat_name
- Purpose:
Used for referencing the task in user interfaces or chats, particularly for explainability.
- Convention:
Typically in CamelCase, should be descriptive yet concise.
- Example:
chat_name = "GoogleSearch"
- description
- Purpose:
Provides an explanation of the task’s function and helps the Task Planner LLM decide when to deploy the task.
- Convention:
The description should be comprehensive, outlining task capabilities, prioritization, or specific conditions for its use.
- Example:
description = "Uses Google to search the internet for the requested query and returns the URL of the top website."
- dependencies
- Purpose:
Lists other tasks or services this task depends on, informing the Task Planner LLM of these dependencies during planning.
- Convention:
An array of task identifiers that match the name attribute of dependent tasks.
- Example:
dependencies = ['fetch_weather_data']
- inputs
- Purpose:
Specifies the inputs required by the task.
- Convention:
An array of strings, each describing a specific input.
- Example:
inputs = ['It should be a search query.']
- outputs
- Purpose:
Details what the task returns, assisting the Task Planner LLM in understanding the return structure.
- Convention:
An array of strings describing each output, with a full description of the return data format.
- Example:
outputs = ["It returns a JSON object containing key: url. For example: {'url': 'http://google.com'}"]
- output_type
- Purpose:
Instructs the Orchestrator on how to handle the output of this task.
- Convention:
Boolean values (True or False).
- _execute Function
- Purpose:
This function must be implemented correctly. It is invoked by the Task Planner LLM with the appropriate inputs.
- Example:
The “_execute” function, utilizing the google_engine Python library, searches for the query online and returns the URL. This aligns with the “outputs” parameter, demonstrating coherence between the task setup and its execution.
After defining your task, you need to register it inside the task pool to be accessible through the interface as well as the Task Planner. To do so, you need to introduce your new task in the tasks list and point to your main class file. First, edit task_types.py file.
Tasks Types#
This enumeration class defines different types of task. This ensures consistency in case the task developer decides to change the name of their task, the end user need not to change their code cause they use the keys. It inherits from the str class and the Enum class in Python’s enum module. Each value in this enumeration represents a specific type of task. The key naming convention should be all uppercase with underscore, and the value naming convention should be underscore_case: NAME_OF_TASK = this_is_a_sample_task_name
from enum import Enum
class TaskType(str, Enum):
SERPAPI = "serpapi"
EXTRACT_TEXT = "extract_text"
AFFECT_SLEEP_GET = "affect_sleep_get"
AFFECT_ACTIVITY_GET = "affect_activity_get"
AFFECT_SLEEP_ANALYSIS = "affect_sleep_analysis"
AFFECT_ACTIVITY_ANALYSIS = "affect_activity_analysis"
GOOGLE_TRANSLATE = "google_translate"
ASK_USER = "ask_user"
TEST_FILE = "test_file"
RUN_PYTHON_CODE = "run_python_code"
PPG_GET = "affect_ppg_get"
PPG_ANALYSIS = "affect_ppg_analysis"
STRESS_ANALYSIS = "affect_stress_analysis"
QUERY_NUTRITIONIX = "query_nutritionix"
CALCULATE_FOOD_RISK_FACTOR = "calculate_food_risk_factor"
GOOGLE_SEARCH = "google_search"
The last step is to point the key you created to the main class of your task implementation by editing the types.py file.
Types#
This dictionary is used to map each TaskType value to its corresponding Task class. It allows for easy retrieval of the appropriate class based on the planner type.
from typing import Dict
from typing import Type
from openCHA.tasks import AskUser
from openCHA.tasks import BaseTask
from openCHA.tasks import ExtractText
from openCHA.tasks import GoogleSearch
from openCHA.tasks import GoogleTranslate
from openCHA.tasks import RunPythonCode
from openCHA.tasks import SerpAPI
from openCHA.tasks import TaskType
from openCHA.tasks import TestFile
from openCHA.tasks.affect import ActivityAnalysis
from openCHA.tasks.affect import ActivityGet
from openCHA.tasks.affect import PPGAnalysis
from openCHA.tasks.affect import PPGGet
from openCHA.tasks.affect import SleepAnalysis
from openCHA.tasks.affect import SleepGet
from openCHA.tasks.affect import StressAnalysis
from openCHA.tasks.nutritionix import (
CalculateFoodRiskFactor,
)
from openCHA.tasks.nutritionix import QueryNutritionix
TASK_TO_CLASS: Dict[TaskType, Type[BaseTask]] = {
TaskType.SERPAPI: SerpAPI,
TaskType.EXTRACT_TEXT: ExtractText,
TaskType.AFFECT_SLEEP_GET: SleepGet,
TaskType.AFFECT_ACTIVITY_GET: ActivityGet,
TaskType.AFFECT_SLEEP_ANALYSIS: SleepAnalysis,
TaskType.AFFECT_ACTIVITY_ANALYSIS: ActivityAnalysis,
TaskType.GOOGLE_TRANSLATE: GoogleTranslate,
TaskType.ASK_USER: AskUser,
TaskType.TEST_FILE: TestFile,
TaskType.RUN_PYTHON_CODE: RunPythonCode,
TaskType.PPG_GET: PPGGet,
TaskType.PPG_ANALYSIS: PPGAnalysis,
TaskType.STRESS_ANALYSIS: StressAnalysis,
TaskType.QUERY_NUTRITIONIX: QueryNutritionix,
TaskType.CALCULATE_FOOD_RISK_FACTOR: CalculateFoodRiskFactor,
TaskType.GOOGLE_SEARCH: GoogleSearch,
}
After doing these steps, you should be able to see your new task in the task list inside the interface and also you can use the key to add it as available tasks in the without interface mode.