Using PydanticTaskTemplate for AIKP Development

Introduction

In the OCLI framework, the PydanticTaskTemplate provides a declarative way to define Task and Recipe structures using Pydantic models. This approach offers strong type safety and automated validation.

The Template Object

The only mandatory requirement for an AIKP to be recognized by OCLI is the presence of a Template class within the module path specified when creating a task (e.g., task create --template ocli.aikp.your_module).

When using the Pydantic-based approach, your Template class inherits from PydanticTaskTemplate and must bind a TaskSchema and a RecipeSchema.

from ocli.classes.pydantic_task_template import PydanticTaskTemplate

class Template(PydanticTaskTemplate[YourTaskSchema, YourRecipeSchema]):
    TaskSchema = YourTaskSchema
    RecipeSchema = YourRecipeSchema
    
    # Optional: override hooks like update_recipe or cli_task_mount

There are no strict requirements for where these schemas or your CLI commands are defined; they can be defined in the same file as the Template class, or imported from any other module.

1. Defining Schemas

Schemas are the core of the Pydantic approach. They define the data structure and validation rules for your AIKP.

TaskSchema

The TaskSchema must inherit from ocli.classes.task.TaskHeadSchema. It defines the fields configurable via task set.

from pydantic import ConfigDict, Field
from ocli.classes.task import TaskHeadSchema

class YourTaskSchema(TaskHeadSchema):
    model_config = ConfigDict(validate_assignment=True, extra='allow')

    template: Literal['your.template.path'] = 'your.template.path'
    custom_parameter: int = Field(10, ge=0)
    # Add other fields as needed

RecipeSchema

The RecipeSchema must inherit from ocli.ai.recipe_head_schema.RecipeHeadSchema. This model defines the structure of the final recipe JSON.

from ocli.ai.recipe_head_schema import RecipeHeadSchema, RecipeType, RecipeKind

class YourRecipeSchema(RecipeHeadSchema):
    type: RecipeType = RecipeType.YourType
    kind: RecipeKind = RecipeKind.YourKind
    custom_parameter: int | None = None

2. Implementing the Template

The Template class orchestrates the interaction between the Task config and the generated Recipe. The primary hook for this is update_recipe.

from ocli.pro.helpers.task_helpers import updates_metadata_preset

class Template(PydanticTaskTemplate[YourTaskSchema, YourRecipeSchema]):
    TaskSchema = YourTaskSchema
    RecipeSchema = YourRecipeSchema

    @classmethod
    @updates_metadata_preset
    def update_recipe(cls, task: Task, recipe: dict, roi: dict | None = None) -> list[str]:
        # Populate standard fields (DATADIR, OUTDIR) and validate
        errors = super().update_recipe(task, recipe, roi)

        try:
            # Type-safe access to task configuration
            task_config = YourTaskSchema.model_validate(task.config)
            
            # Map task configuration to recipe properties type-safely
            recipe_model = YourRecipeSchema(**recipe, custom_parameter=task_config.custom_parameter)
            
            # Update the original dictionary with validated/formatted data
            recipe.update(recipe_model.model_dump(mode="json", exclude_unset=True, by_alias=True))
        except Exception as e:
            errors.append(str(e))
        
        return errors

Key Benefits

  • Declarative Validation: Use Pydantic’s native features (types, Field constraints, @validator) to enforce rules instead of manual if/else checks.

  • Automated Recipe Sync: PydanticTaskTemplate automatically handles schema validation for recipes, eliminating the need for a separate recipe_schema.json.

  • Flexibility: You are free to structure your code across one or many files; OCLI only cares about finding the Template object at the module path.

  • Builder Pattern & Resilience: PydanticTaskTemplate treats your TaskSchema as a builder. This means tasks can be created and updated incrementally via task set even if they are in an “invalid” or incomplete state. The framework only enforces strict schema compliance when it’s time to generate a recipe, allowing for a flexible, interactive configuration workflow.