kiln_ai.adapters.repair.repair_task

 1import json
 2
 3from pydantic import BaseModel, Field
 4
 5from kiln_ai.adapters.prompt_builders import (
 6    BasePromptBuilder,
 7    SimplePromptBuilder,
 8    prompt_builder_from_id,
 9)
10from kiln_ai.datamodel import Priority, Project, Task, TaskRequirement, TaskRun
11
12
13# We should add evaluator rating
14class RepairTaskInput(BaseModel):
15    original_prompt: str
16    original_input: str
17    original_output: str
18    evaluator_feedback: str = Field(
19        min_length=1,
20        description="Feedback from an evaluator on how to repair the task run.",
21    )
22
23
24class RepairTaskRun(Task, parent_of={}):
25    def __init__(self, original_task: Task):
26        # Keep the typechecker happy
27        tmp_project = Project(name="Repair")
28        super().__init__(
29            name="Repair",
30            parent=tmp_project,
31            description="Repair a task run, given feedback from an evaluator about how the response can be improved.",
32            instruction="You are an assistant which helps improve output from another assistant (original assistant). You'll be provided a task that the original assistant executed (prompt), \
33the input it was given, and the output it generated. An evaluator has determined that the output it generated did not satisfy the task and should be improved. The evaluator will provide \
34feedback describing what should be improved. Your job is to understand the evaluator's feedback and improve the response.",
35            requirements=[
36                TaskRequirement(
37                    name="Follow Eval Feedback",
38                    instruction="The evaluator's feedback is the most important thing to consider. If it conflicts with the original task instruction or prompt, prioritize the evaluator's feedback.",
39                    priority=Priority.p0,
40                )
41            ],
42            input_json_schema=json.dumps(RepairTaskInput.model_json_schema()),
43            output_json_schema=original_task.output_json_schema,
44        )
45
46    @classmethod
47    def _original_prompt(cls, run: TaskRun, task: Task) -> str:
48        # Get the prompt builder id. Need the second check because we used to store this in a prompt_builder_name field, so loading legacy runs will need this.
49        source_properties = (
50            run.output.source.properties
51            if run.output.source and run.output.source.properties
52            else {}
53        )
54        prompt_id = source_properties.get("prompt_id") or source_properties.get(
55            "prompt_builder_name", None
56        )
57        if prompt_id is not None and isinstance(prompt_id, str):
58            try:
59                prompt_builder = prompt_builder_from_id(prompt_id, task)
60            except ValueError:
61                # Unknown/legacy prompt_id — fall through to fallback below
62                prompt_builder = None
63            if isinstance(prompt_builder, BasePromptBuilder):
64                return prompt_builder.build_prompt(include_json_instructions=False)
65
66        # Fallback to simple prompt builder if prompt_id is missing, unknown, or source/properties are absent (e.g. legacy runs)
67        fallback_builder = SimplePromptBuilder(task)
68        return fallback_builder.build_prompt(include_json_instructions=False)
69
70    @classmethod
71    def build_repair_task_input(
72        cls, original_task: Task, task_run: TaskRun, evaluator_feedback: str
73    ) -> RepairTaskInput:
74        original_prompt = cls._original_prompt(task_run, original_task)
75        return RepairTaskInput(
76            original_prompt=original_prompt,
77            original_input=task_run.input,
78            original_output=task_run.output.output,
79            evaluator_feedback=evaluator_feedback,
80        )
class RepairTaskInput(pydantic.main.BaseModel):
15class RepairTaskInput(BaseModel):
16    original_prompt: str
17    original_input: str
18    original_output: str
19    evaluator_feedback: str = Field(
20        min_length=1,
21        description="Feedback from an evaluator on how to repair the task run.",
22    )

!!! abstract "Usage Documentation" Models

A base class for creating Pydantic models.

Attributes: __class_vars__: The names of the class variables defined on the model. __private_attributes__: Metadata about the private attributes of the model. __signature__: The synthesized __init__ [Signature][inspect.Signature] of the model.

__pydantic_complete__: Whether model building is completed, or if there are still undefined fields.
__pydantic_core_schema__: The core schema of the model.
__pydantic_custom_init__: Whether the model has a custom `__init__` function.
__pydantic_decorators__: Metadata containing the decorators defined on the model.
    This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.
__pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to
    __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.
__pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.
__pydantic_post_init__: The name of the post-init method for the model, if defined.
__pydantic_root_model__: Whether the model is a [`RootModel`][pydantic.root_model.RootModel].
__pydantic_serializer__: The `pydantic-core` `SchemaSerializer` used to dump instances of the model.
__pydantic_validator__: The `pydantic-core` `SchemaValidator` used to validate instances of the model.

__pydantic_fields__: A dictionary of field names and their corresponding [`FieldInfo`][pydantic.fields.FieldInfo] objects.
__pydantic_computed_fields__: A dictionary of computed field names and their corresponding [`ComputedFieldInfo`][pydantic.fields.ComputedFieldInfo] objects.

__pydantic_extra__: A dictionary containing extra values, if [`extra`][pydantic.config.ConfigDict.extra]
    is set to `'allow'`.
__pydantic_fields_set__: The names of fields explicitly set during instantiation.
__pydantic_private__: Values of private attributes set on the model instance.
original_prompt: str
original_input: str
original_output: str
evaluator_feedback: str
model_config: ClassVar[pydantic.config.ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class RepairTaskRun(kiln_ai.datamodel.task.Task):
25class RepairTaskRun(Task, parent_of={}):
26    def __init__(self, original_task: Task):
27        # Keep the typechecker happy
28        tmp_project = Project(name="Repair")
29        super().__init__(
30            name="Repair",
31            parent=tmp_project,
32            description="Repair a task run, given feedback from an evaluator about how the response can be improved.",
33            instruction="You are an assistant which helps improve output from another assistant (original assistant). You'll be provided a task that the original assistant executed (prompt), \
34the input it was given, and the output it generated. An evaluator has determined that the output it generated did not satisfy the task and should be improved. The evaluator will provide \
35feedback describing what should be improved. Your job is to understand the evaluator's feedback and improve the response.",
36            requirements=[
37                TaskRequirement(
38                    name="Follow Eval Feedback",
39                    instruction="The evaluator's feedback is the most important thing to consider. If it conflicts with the original task instruction or prompt, prioritize the evaluator's feedback.",
40                    priority=Priority.p0,
41                )
42            ],
43            input_json_schema=json.dumps(RepairTaskInput.model_json_schema()),
44            output_json_schema=original_task.output_json_schema,
45        )
46
47    @classmethod
48    def _original_prompt(cls, run: TaskRun, task: Task) -> str:
49        # Get the prompt builder id. Need the second check because we used to store this in a prompt_builder_name field, so loading legacy runs will need this.
50        source_properties = (
51            run.output.source.properties
52            if run.output.source and run.output.source.properties
53            else {}
54        )
55        prompt_id = source_properties.get("prompt_id") or source_properties.get(
56            "prompt_builder_name", None
57        )
58        if prompt_id is not None and isinstance(prompt_id, str):
59            try:
60                prompt_builder = prompt_builder_from_id(prompt_id, task)
61            except ValueError:
62                # Unknown/legacy prompt_id — fall through to fallback below
63                prompt_builder = None
64            if isinstance(prompt_builder, BasePromptBuilder):
65                return prompt_builder.build_prompt(include_json_instructions=False)
66
67        # Fallback to simple prompt builder if prompt_id is missing, unknown, or source/properties are absent (e.g. legacy runs)
68        fallback_builder = SimplePromptBuilder(task)
69        return fallback_builder.build_prompt(include_json_instructions=False)
70
71    @classmethod
72    def build_repair_task_input(
73        cls, original_task: Task, task_run: TaskRun, evaluator_feedback: str
74    ) -> RepairTaskInput:
75        original_prompt = cls._original_prompt(task_run, original_task)
76        return RepairTaskInput(
77            original_prompt=original_prompt,
78            original_input=task_run.input,
79            original_output=task_run.output.output,
80            evaluator_feedback=evaluator_feedback,
81        )

Represents a specific task to be performed, with associated requirements and validation rules.

Contains the task definition, requirements, input/output schemas, and maintains a collection of task runs.

RepairTaskRun(original_task: kiln_ai.datamodel.Task)
26    def __init__(self, original_task: Task):
27        # Keep the typechecker happy
28        tmp_project = Project(name="Repair")
29        super().__init__(
30            name="Repair",
31            parent=tmp_project,
32            description="Repair a task run, given feedback from an evaluator about how the response can be improved.",
33            instruction="You are an assistant which helps improve output from another assistant (original assistant). You'll be provided a task that the original assistant executed (prompt), \
34the input it was given, and the output it generated. An evaluator has determined that the output it generated did not satisfy the task and should be improved. The evaluator will provide \
35feedback describing what should be improved. Your job is to understand the evaluator's feedback and improve the response.",
36            requirements=[
37                TaskRequirement(
38                    name="Follow Eval Feedback",
39                    instruction="The evaluator's feedback is the most important thing to consider. If it conflicts with the original task instruction or prompt, prioritize the evaluator's feedback.",
40                    priority=Priority.p0,
41                )
42            ],
43            input_json_schema=json.dumps(RepairTaskInput.model_json_schema()),
44            output_json_schema=original_task.output_json_schema,
45        )

Create a new model by parsing and validating input data from keyword arguments.

Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.

self is explicitly positional-only to allow self as a field name.

@classmethod
def build_repair_task_input( cls, original_task: kiln_ai.datamodel.Task, task_run: kiln_ai.datamodel.TaskRun, evaluator_feedback: str) -> RepairTaskInput:
71    @classmethod
72    def build_repair_task_input(
73        cls, original_task: Task, task_run: TaskRun, evaluator_feedback: str
74    ) -> RepairTaskInput:
75        original_prompt = cls._original_prompt(task_run, original_task)
76        return RepairTaskInput(
77            original_prompt=original_prompt,
78            original_input=task_run.input,
79            original_output=task_run.output.output,
80            evaluator_feedback=evaluator_feedback,
81        )
model_config = {'validate_assignment': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

def model_post_init(self: pydantic.main.BaseModel, context: Any, /) -> None:
337def init_private_attributes(self: BaseModel, context: Any, /) -> None:
338    """This function is meant to behave like a BaseModel method to initialise private attributes.
339
340    It takes context as an argument since that's what pydantic-core passes when calling it.
341
342    Args:
343        self: The BaseModel instance.
344        context: The context.
345    """
346    if getattr(self, '__pydantic_private__', None) is None:
347        pydantic_private = {}
348        for name, private_attr in self.__private_attributes__.items():
349            default = private_attr.get_default()
350            if default is not PydanticUndefined:
351                pydantic_private[name] = default
352        object_setattr(self, '__pydantic_private__', pydantic_private)

This function is meant to behave like a BaseModel method to initialise private attributes.

It takes context as an argument since that's what pydantic-core passes when calling it.

Args: self: The BaseModel instance. context: The context.