kiln_ai.adapters.prompt_builders
1from abc import ABCMeta, abstractmethod 2from dataclasses import dataclass 3 4from kiln_ai.datamodel import PromptGenerators, PromptId, Task, TaskRun 5from kiln_ai.utils.exhaustive_error import raise_exhaustive_enum_error 6 7 8@dataclass 9class PromptExample: 10 """A simple input/output example for use in prompt building.""" 11 12 input: str 13 output: str 14 15 16class BasePromptBuilder(metaclass=ABCMeta): 17 """Base class for building prompts from tasks. 18 19 Provides the core interface and basic functionality for prompt builders. 20 """ 21 22 def __init__(self, task: Task): 23 """Initialize the prompt builder with a task. 24 25 Args: 26 task (Task): The task containing instructions and requirements. 27 """ 28 self.task = task 29 30 def prompt_id(self) -> str | None: 31 """Returns the ID of the prompt, scoped to this builder. 32 33 Returns: 34 str | None: The ID of the prompt, or None if not set. 35 """ 36 return None 37 38 def build_prompt(self, include_json_instructions) -> str: 39 """Build and return the complete prompt string. 40 41 Returns: 42 str: The constructed prompt. 43 """ 44 prompt = self.build_base_prompt() 45 46 if include_json_instructions and self.task.output_schema(): 47 prompt = ( 48 prompt 49 + f"\n\n# Format Instructions\n\nReturn a JSON object conforming to the following schema:\n```\n{self.task.output_schema()}\n```" 50 ) 51 52 return prompt 53 54 @abstractmethod 55 def build_base_prompt(self) -> str: 56 """Build and return the complete prompt string. 57 58 Returns: 59 str: The constructed prompt. 60 """ 61 pass 62 63 def chain_of_thought_prompt(self) -> str | None: 64 """Build and return the chain of thought prompt string. 65 66 Returns: 67 str: The constructed chain of thought prompt. 68 """ 69 return None 70 71 def build_prompt_for_ui(self) -> str: 72 """Build a prompt for the UI. It includes additional instructions (like chain of thought), even if they are passed to the model in stages. 73 74 Designed for end-user consumption, not for model consumption. 75 76 Returns: 77 str: The constructed prompt string. 78 """ 79 base_prompt = self.build_prompt(include_json_instructions=False) 80 cot_prompt = self.chain_of_thought_prompt() 81 if cot_prompt: 82 base_prompt += "\n\n# Thinking Instructions\n\n" + cot_prompt 83 return base_prompt 84 85 86class SimplePromptBuilder(BasePromptBuilder): 87 """A basic prompt builder that combines task instruction with requirements.""" 88 89 def build_base_prompt(self) -> str: 90 """Build a simple prompt with instruction and requirements. 91 92 Returns: 93 str: The constructed prompt string. 94 """ 95 base_prompt = self.task.instruction 96 97 if len(self.task.requirements) > 0: 98 base_prompt += ( 99 "\n\nYour response should respect the following requirements:\n" 100 ) 101 # iterate requirements, formatting them in numbereed list like 1) task.instruction\n2)... 102 for i, requirement in enumerate(self.task.requirements): 103 base_prompt += f"{i + 1}) {requirement.instruction}\n" 104 105 return base_prompt 106 107 108class MultiShotPromptBuilder(BasePromptBuilder): 109 """A prompt builder that includes multiple examples in the prompt.""" 110 111 @classmethod 112 def example_count(cls) -> int: 113 """Get the maximum number of examples to include in the prompt. 114 115 Returns: 116 int: The maximum number of examples (default 25). 117 """ 118 return 25 119 120 def build_instruction_and_requirements(self) -> str: 121 """Build the instruction and requirements section of the prompt. 122 123 Returns: 124 str: The instruction and requirements sections. 125 """ 126 base_prompt = f"# Instruction\n\n{self.task.instruction}\n\n" 127 128 if len(self.task.requirements) > 0: 129 base_prompt += "# Requirements\n\nYour response should respect the following requirements:\n" 130 for i, requirement in enumerate(self.task.requirements): 131 base_prompt += f"{i + 1}) {requirement.instruction}\n" 132 base_prompt += "\n" 133 134 return base_prompt 135 136 def build_base_prompt(self) -> str: 137 """Build a prompt with instruction, requirements, and multiple examples. 138 139 Returns: 140 str: The constructed prompt string with examples. 141 """ 142 base_prompt = self.build_instruction_and_requirements() 143 144 valid_examples = self.collect_examples() 145 146 if len(valid_examples) == 0: 147 return base_prompt 148 149 base_prompt += "# Example Outputs\n\n" 150 for i, example in enumerate(valid_examples): 151 base_prompt += self.prompt_section_for_example(i, example) 152 153 return base_prompt 154 155 def prompt_section_for_example(self, index: int, example: TaskRun) -> str: 156 # Prefer repaired output if it exists, otherwise use the regular output 157 output = example.repaired_output or example.output 158 return f"## Example {index + 1}\n\nInput: {example.input}\nOutput: {output.output}\n\n" 159 160 def collect_examples(self) -> list[TaskRun]: 161 valid_examples: list[TaskRun] = [] 162 runs = self.task.runs(readonly=True) 163 164 # first pass, we look for repaired outputs. These are the best examples. 165 for run in runs: 166 if len(valid_examples) >= self.__class__.example_count(): 167 break 168 if run.repaired_output is not None: 169 valid_examples.append(run) 170 171 # second pass, we look for high quality outputs (rating based) 172 # Minimum is "high_quality" (4 star in star rating scale), then sort by rating 173 # exclude repaired outputs as they were used above 174 runs_with_rating = [ 175 run 176 for run in runs 177 if run.output.rating is not None 178 and run.output.rating.value is not None 179 and run.output.rating.is_high_quality() 180 and run.repaired_output is None 181 ] 182 runs_with_rating.sort( 183 key=lambda x: (x.output.rating and x.output.rating.value) or 0, reverse=True 184 ) 185 for run in runs_with_rating: 186 if len(valid_examples) >= self.__class__.example_count(): 187 break 188 valid_examples.append(run) 189 return valid_examples 190 191 192class FewShotPromptBuilder(MultiShotPromptBuilder): 193 """A prompt builder that includes a small number of examples in the prompt.""" 194 195 @classmethod 196 def example_count(cls) -> int: 197 """Get the maximum number of examples to include in the prompt. 198 199 Returns: 200 int: The maximum number of examples (4). 201 """ 202 return 4 203 204 205class CustomExamplePromptBuilder(FewShotPromptBuilder): 206 """A prompt builder that uses custom examples instead of collecting from the dataset.""" 207 208 def __init__(self, task: Task, examples: list[PromptExample] | None = None): 209 super().__init__(task) 210 self._custom_examples = examples or [] 211 212 def collect_examples(self) -> list[TaskRun]: 213 """Override to return an empty list - we handle examples separately.""" 214 return [] 215 216 def build_base_prompt(self) -> str: 217 """Build a prompt with instruction, requirements, and custom examples.""" 218 base_prompt = self.build_instruction_and_requirements() 219 220 if self._custom_examples: 221 base_prompt += "# Example Outputs\n\n" 222 for i, example in enumerate(self._custom_examples): 223 base_prompt += f"## Example {i + 1}\n\nInput: {example.input}\nOutput: {example.output}\n\n" 224 225 return base_prompt 226 227 228class RepairsPromptBuilder(MultiShotPromptBuilder): 229 """A prompt builder that includes multiple examples in the prompt, including repaired instructions describing what was wrong, and how it was fixed.""" 230 231 def prompt_section_for_example(self, index: int, example: TaskRun) -> str: 232 if ( 233 not example.repaired_output 234 or not example.repair_instructions 235 or not example.repaired_output.output 236 ): 237 return super().prompt_section_for_example(index, example) 238 239 prompt_section = f"## Example {index + 1}\n\nInput: {example.input}\n\n" 240 prompt_section += ( 241 f"Initial Output Which Was Insufficient: {example.output.output}\n\n" 242 ) 243 prompt_section += f"Instructions On How to Improve the Initial Output: {example.repair_instructions}\n\n" 244 prompt_section += ( 245 f"Repaired Output Which is Sufficient: {example.repaired_output.output}\n\n" 246 ) 247 return prompt_section 248 249 250def chain_of_thought_prompt(task: Task) -> str: 251 """Standard implementation to build and return the chain of thought prompt string. 252 253 Returns: 254 str: The constructed chain of thought prompt. 255 """ 256 257 cot_instruction = task.thinking_instruction 258 if not cot_instruction: 259 cot_instruction = "Think step by step, explaining your reasoning." 260 261 return cot_instruction 262 263 264class SimpleChainOfThoughtPromptBuilder(SimplePromptBuilder): 265 """A prompt builder that includes a chain of thought prompt on top of the simple prompt.""" 266 267 def chain_of_thought_prompt(self) -> str | None: 268 return chain_of_thought_prompt(self.task) 269 270 271class FewShotChainOfThoughtPromptBuilder(FewShotPromptBuilder): 272 """A prompt builder that includes a chain of thought prompt on top of the few shot prompt.""" 273 274 def chain_of_thought_prompt(self) -> str | None: 275 return chain_of_thought_prompt(self.task) 276 277 278class MultiShotChainOfThoughtPromptBuilder(MultiShotPromptBuilder): 279 """A prompt builder that includes a chain of thought prompt on top of the multi shot prompt.""" 280 281 def chain_of_thought_prompt(self) -> str | None: 282 return chain_of_thought_prompt(self.task) 283 284 285class SavedPromptBuilder(BasePromptBuilder): 286 """A prompt builder that looks up a static prompt.""" 287 288 def __init__(self, task: Task, prompt_id: str): 289 super().__init__(task) 290 prompt_model = next( 291 ( 292 prompt 293 for prompt in task.prompts(readonly=True) 294 if prompt.id == prompt_id 295 ), 296 None, 297 ) 298 if not prompt_model: 299 raise ValueError(f"Prompt ID not found: {prompt_id}") 300 self.prompt_model = prompt_model 301 302 def prompt_id(self) -> str | None: 303 return self.prompt_model.id 304 305 def build_base_prompt(self) -> str: 306 """Returns a saved prompt. 307 308 Returns: 309 str: The prompt string. 310 """ 311 return self.prompt_model.prompt 312 313 def chain_of_thought_prompt(self) -> str | None: 314 return self.prompt_model.chain_of_thought_instructions 315 316 317class TaskRunConfigPromptBuilder(BasePromptBuilder): 318 """A prompt builder that looks up a static prompt in a task run config.""" 319 320 def __init__(self, task: Task, run_config_prompt_id: str): 321 parts = run_config_prompt_id.split("::") 322 if len(parts) != 4: 323 raise ValueError( 324 f"Invalid task run config prompt ID: {run_config_prompt_id}. Expected format: 'task_run_config::[project_id]::[task_id]::[run_config_id]'." 325 ) 326 327 task_id = parts[2] 328 if task_id != task.id: 329 raise ValueError( 330 f"Task run config prompt ID: {run_config_prompt_id}. Task ID mismatch. Expected: {task.id}, got: {task_id}." 331 ) 332 333 run_config_id = parts[3] 334 run_config = next( 335 ( 336 run_config 337 for run_config in task.run_configs(readonly=True) 338 if run_config.id == run_config_id 339 ), 340 None, 341 ) 342 if not run_config: 343 raise ValueError( 344 f"Task run config ID not found: {run_config_id} for prompt id {run_config_prompt_id}" 345 ) 346 if run_config.prompt is None: 347 raise ValueError( 348 f"Task run config ID {run_config_id} does not have a stored prompt. Used as prompt id {run_config_prompt_id}" 349 ) 350 351 # Load the prompt from the model 352 self.prompt = run_config.prompt.prompt 353 self.cot_prompt = run_config.prompt.chain_of_thought_instructions 354 self.id = run_config_prompt_id 355 356 super().__init__(task) 357 358 def prompt_id(self) -> str | None: 359 return self.id 360 361 def build_base_prompt(self) -> str: 362 return self.prompt 363 364 def chain_of_thought_prompt(self) -> str | None: 365 return self.cot_prompt 366 367 368class FineTunePromptBuilder(BasePromptBuilder): 369 """A prompt builder that looks up a fine-tune prompt.""" 370 371 def __init__(self, task: Task, nested_fine_tune_id: str): 372 super().__init__(task) 373 374 # IDs are in project_id::task_id::fine_tune_id format 375 self.full_fine_tune_id = nested_fine_tune_id 376 parts = nested_fine_tune_id.split("::") 377 if len(parts) != 3: 378 raise ValueError( 379 f"Invalid fine-tune ID format. Expected 'project_id::task_id::fine_tune_id', got: {nested_fine_tune_id}" 380 ) 381 fine_tune_id = parts[2] 382 383 fine_tune_model = next( 384 ( 385 fine_tune 386 for fine_tune in task.finetunes(readonly=True) 387 if fine_tune.id == fine_tune_id 388 ), 389 None, 390 ) 391 if not fine_tune_model: 392 raise ValueError(f"Fine-tune ID not found: {fine_tune_id}") 393 self.fine_tune_model = fine_tune_model 394 395 def prompt_id(self) -> str | None: 396 return self.full_fine_tune_id 397 398 def build_base_prompt(self) -> str: 399 return self.fine_tune_model.system_message 400 401 def chain_of_thought_prompt(self) -> str | None: 402 return self.fine_tune_model.thinking_instructions 403 404 405# Our UI has some names that are not the same as the class names, which also hint parameters. 406def prompt_builder_from_id(prompt_id: PromptId, task: Task) -> BasePromptBuilder: 407 """Convert a name used in the UI to the corresponding prompt builder class. 408 409 Args: 410 prompt_id (PromptId): The prompt ID. 411 412 Returns: 413 type[BasePromptBuilder]: The corresponding prompt builder class. 414 415 Raises: 416 ValueError: If the UI name is not recognized. 417 """ 418 419 # Saved prompts are prefixed with "id::" 420 if prompt_id.startswith("id::"): 421 prompt_id = prompt_id[4:] 422 return SavedPromptBuilder(task, prompt_id) 423 424 # Task run config prompts are prefixed with "task_run_config::" 425 # task_run_config::[project_id]::[task_id]::[run_config_id] 426 if prompt_id.startswith("task_run_config::"): 427 return TaskRunConfigPromptBuilder(task, prompt_id) 428 429 # Fine-tune prompts are prefixed with "fine_tune_prompt::" 430 if prompt_id.startswith("fine_tune_prompt::"): 431 prompt_id = prompt_id[18:] 432 return FineTunePromptBuilder(task, prompt_id) 433 434 # Check if the prompt_id matches any enum value 435 if prompt_id not in [member.value for member in PromptGenerators]: 436 raise ValueError(f"Unknown prompt generator: {prompt_id}") 437 typed_prompt_generator = PromptGenerators(prompt_id) 438 439 match typed_prompt_generator: 440 case PromptGenerators.SIMPLE: 441 return SimplePromptBuilder(task) 442 case PromptGenerators.FEW_SHOT: 443 return FewShotPromptBuilder(task) 444 case PromptGenerators.MULTI_SHOT: 445 return MultiShotPromptBuilder(task) 446 case PromptGenerators.REPAIRS: 447 return RepairsPromptBuilder(task) 448 case PromptGenerators.SIMPLE_CHAIN_OF_THOUGHT: 449 return SimpleChainOfThoughtPromptBuilder(task) 450 case PromptGenerators.FEW_SHOT_CHAIN_OF_THOUGHT: 451 return FewShotChainOfThoughtPromptBuilder(task) 452 case PromptGenerators.MULTI_SHOT_CHAIN_OF_THOUGHT: 453 return MultiShotChainOfThoughtPromptBuilder(task) 454 case _: 455 # Type checking will find missing cases 456 raise_exhaustive_enum_error(typed_prompt_generator)
9@dataclass 10class PromptExample: 11 """A simple input/output example for use in prompt building.""" 12 13 input: str 14 output: str
A simple input/output example for use in prompt building.
17class BasePromptBuilder(metaclass=ABCMeta): 18 """Base class for building prompts from tasks. 19 20 Provides the core interface and basic functionality for prompt builders. 21 """ 22 23 def __init__(self, task: Task): 24 """Initialize the prompt builder with a task. 25 26 Args: 27 task (Task): The task containing instructions and requirements. 28 """ 29 self.task = task 30 31 def prompt_id(self) -> str | None: 32 """Returns the ID of the prompt, scoped to this builder. 33 34 Returns: 35 str | None: The ID of the prompt, or None if not set. 36 """ 37 return None 38 39 def build_prompt(self, include_json_instructions) -> str: 40 """Build and return the complete prompt string. 41 42 Returns: 43 str: The constructed prompt. 44 """ 45 prompt = self.build_base_prompt() 46 47 if include_json_instructions and self.task.output_schema(): 48 prompt = ( 49 prompt 50 + f"\n\n# Format Instructions\n\nReturn a JSON object conforming to the following schema:\n```\n{self.task.output_schema()}\n```" 51 ) 52 53 return prompt 54 55 @abstractmethod 56 def build_base_prompt(self) -> str: 57 """Build and return the complete prompt string. 58 59 Returns: 60 str: The constructed prompt. 61 """ 62 pass 63 64 def chain_of_thought_prompt(self) -> str | None: 65 """Build and return the chain of thought prompt string. 66 67 Returns: 68 str: The constructed chain of thought prompt. 69 """ 70 return None 71 72 def build_prompt_for_ui(self) -> str: 73 """Build a prompt for the UI. It includes additional instructions (like chain of thought), even if they are passed to the model in stages. 74 75 Designed for end-user consumption, not for model consumption. 76 77 Returns: 78 str: The constructed prompt string. 79 """ 80 base_prompt = self.build_prompt(include_json_instructions=False) 81 cot_prompt = self.chain_of_thought_prompt() 82 if cot_prompt: 83 base_prompt += "\n\n# Thinking Instructions\n\n" + cot_prompt 84 return base_prompt
Base class for building prompts from tasks.
Provides the core interface and basic functionality for prompt builders.
23 def __init__(self, task: Task): 24 """Initialize the prompt builder with a task. 25 26 Args: 27 task (Task): The task containing instructions and requirements. 28 """ 29 self.task = task
Initialize the prompt builder with a task.
Args: task (Task): The task containing instructions and requirements.
31 def prompt_id(self) -> str | None: 32 """Returns the ID of the prompt, scoped to this builder. 33 34 Returns: 35 str | None: The ID of the prompt, or None if not set. 36 """ 37 return None
Returns the ID of the prompt, scoped to this builder.
Returns: str | None: The ID of the prompt, or None if not set.
39 def build_prompt(self, include_json_instructions) -> str: 40 """Build and return the complete prompt string. 41 42 Returns: 43 str: The constructed prompt. 44 """ 45 prompt = self.build_base_prompt() 46 47 if include_json_instructions and self.task.output_schema(): 48 prompt = ( 49 prompt 50 + f"\n\n# Format Instructions\n\nReturn a JSON object conforming to the following schema:\n```\n{self.task.output_schema()}\n```" 51 ) 52 53 return prompt
Build and return the complete prompt string.
Returns: str: The constructed prompt.
55 @abstractmethod 56 def build_base_prompt(self) -> str: 57 """Build and return the complete prompt string. 58 59 Returns: 60 str: The constructed prompt. 61 """ 62 pass
Build and return the complete prompt string.
Returns: str: The constructed prompt.
64 def chain_of_thought_prompt(self) -> str | None: 65 """Build and return the chain of thought prompt string. 66 67 Returns: 68 str: The constructed chain of thought prompt. 69 """ 70 return None
Build and return the chain of thought prompt string.
Returns: str: The constructed chain of thought prompt.
72 def build_prompt_for_ui(self) -> str: 73 """Build a prompt for the UI. It includes additional instructions (like chain of thought), even if they are passed to the model in stages. 74 75 Designed for end-user consumption, not for model consumption. 76 77 Returns: 78 str: The constructed prompt string. 79 """ 80 base_prompt = self.build_prompt(include_json_instructions=False) 81 cot_prompt = self.chain_of_thought_prompt() 82 if cot_prompt: 83 base_prompt += "\n\n# Thinking Instructions\n\n" + cot_prompt 84 return base_prompt
Build a prompt for the UI. It includes additional instructions (like chain of thought), even if they are passed to the model in stages.
Designed for end-user consumption, not for model consumption.
Returns: str: The constructed prompt string.
87class SimplePromptBuilder(BasePromptBuilder): 88 """A basic prompt builder that combines task instruction with requirements.""" 89 90 def build_base_prompt(self) -> str: 91 """Build a simple prompt with instruction and requirements. 92 93 Returns: 94 str: The constructed prompt string. 95 """ 96 base_prompt = self.task.instruction 97 98 if len(self.task.requirements) > 0: 99 base_prompt += ( 100 "\n\nYour response should respect the following requirements:\n" 101 ) 102 # iterate requirements, formatting them in numbereed list like 1) task.instruction\n2)... 103 for i, requirement in enumerate(self.task.requirements): 104 base_prompt += f"{i + 1}) {requirement.instruction}\n" 105 106 return base_prompt
A basic prompt builder that combines task instruction with requirements.
90 def build_base_prompt(self) -> str: 91 """Build a simple prompt with instruction and requirements. 92 93 Returns: 94 str: The constructed prompt string. 95 """ 96 base_prompt = self.task.instruction 97 98 if len(self.task.requirements) > 0: 99 base_prompt += ( 100 "\n\nYour response should respect the following requirements:\n" 101 ) 102 # iterate requirements, formatting them in numbereed list like 1) task.instruction\n2)... 103 for i, requirement in enumerate(self.task.requirements): 104 base_prompt += f"{i + 1}) {requirement.instruction}\n" 105 106 return base_prompt
Build a simple prompt with instruction and requirements.
Returns: str: The constructed prompt string.
109class MultiShotPromptBuilder(BasePromptBuilder): 110 """A prompt builder that includes multiple examples in the prompt.""" 111 112 @classmethod 113 def example_count(cls) -> int: 114 """Get the maximum number of examples to include in the prompt. 115 116 Returns: 117 int: The maximum number of examples (default 25). 118 """ 119 return 25 120 121 def build_instruction_and_requirements(self) -> str: 122 """Build the instruction and requirements section of the prompt. 123 124 Returns: 125 str: The instruction and requirements sections. 126 """ 127 base_prompt = f"# Instruction\n\n{self.task.instruction}\n\n" 128 129 if len(self.task.requirements) > 0: 130 base_prompt += "# Requirements\n\nYour response should respect the following requirements:\n" 131 for i, requirement in enumerate(self.task.requirements): 132 base_prompt += f"{i + 1}) {requirement.instruction}\n" 133 base_prompt += "\n" 134 135 return base_prompt 136 137 def build_base_prompt(self) -> str: 138 """Build a prompt with instruction, requirements, and multiple examples. 139 140 Returns: 141 str: The constructed prompt string with examples. 142 """ 143 base_prompt = self.build_instruction_and_requirements() 144 145 valid_examples = self.collect_examples() 146 147 if len(valid_examples) == 0: 148 return base_prompt 149 150 base_prompt += "# Example Outputs\n\n" 151 for i, example in enumerate(valid_examples): 152 base_prompt += self.prompt_section_for_example(i, example) 153 154 return base_prompt 155 156 def prompt_section_for_example(self, index: int, example: TaskRun) -> str: 157 # Prefer repaired output if it exists, otherwise use the regular output 158 output = example.repaired_output or example.output 159 return f"## Example {index + 1}\n\nInput: {example.input}\nOutput: {output.output}\n\n" 160 161 def collect_examples(self) -> list[TaskRun]: 162 valid_examples: list[TaskRun] = [] 163 runs = self.task.runs(readonly=True) 164 165 # first pass, we look for repaired outputs. These are the best examples. 166 for run in runs: 167 if len(valid_examples) >= self.__class__.example_count(): 168 break 169 if run.repaired_output is not None: 170 valid_examples.append(run) 171 172 # second pass, we look for high quality outputs (rating based) 173 # Minimum is "high_quality" (4 star in star rating scale), then sort by rating 174 # exclude repaired outputs as they were used above 175 runs_with_rating = [ 176 run 177 for run in runs 178 if run.output.rating is not None 179 and run.output.rating.value is not None 180 and run.output.rating.is_high_quality() 181 and run.repaired_output is None 182 ] 183 runs_with_rating.sort( 184 key=lambda x: (x.output.rating and x.output.rating.value) or 0, reverse=True 185 ) 186 for run in runs_with_rating: 187 if len(valid_examples) >= self.__class__.example_count(): 188 break 189 valid_examples.append(run) 190 return valid_examples
A prompt builder that includes multiple examples in the prompt.
112 @classmethod 113 def example_count(cls) -> int: 114 """Get the maximum number of examples to include in the prompt. 115 116 Returns: 117 int: The maximum number of examples (default 25). 118 """ 119 return 25
Get the maximum number of examples to include in the prompt.
Returns: int: The maximum number of examples (default 25).
121 def build_instruction_and_requirements(self) -> str: 122 """Build the instruction and requirements section of the prompt. 123 124 Returns: 125 str: The instruction and requirements sections. 126 """ 127 base_prompt = f"# Instruction\n\n{self.task.instruction}\n\n" 128 129 if len(self.task.requirements) > 0: 130 base_prompt += "# Requirements\n\nYour response should respect the following requirements:\n" 131 for i, requirement in enumerate(self.task.requirements): 132 base_prompt += f"{i + 1}) {requirement.instruction}\n" 133 base_prompt += "\n" 134 135 return base_prompt
Build the instruction and requirements section of the prompt.
Returns: str: The instruction and requirements sections.
137 def build_base_prompt(self) -> str: 138 """Build a prompt with instruction, requirements, and multiple examples. 139 140 Returns: 141 str: The constructed prompt string with examples. 142 """ 143 base_prompt = self.build_instruction_and_requirements() 144 145 valid_examples = self.collect_examples() 146 147 if len(valid_examples) == 0: 148 return base_prompt 149 150 base_prompt += "# Example Outputs\n\n" 151 for i, example in enumerate(valid_examples): 152 base_prompt += self.prompt_section_for_example(i, example) 153 154 return base_prompt
Build a prompt with instruction, requirements, and multiple examples.
Returns: str: The constructed prompt string with examples.
156 def prompt_section_for_example(self, index: int, example: TaskRun) -> str: 157 # Prefer repaired output if it exists, otherwise use the regular output 158 output = example.repaired_output or example.output 159 return f"## Example {index + 1}\n\nInput: {example.input}\nOutput: {output.output}\n\n"
161 def collect_examples(self) -> list[TaskRun]: 162 valid_examples: list[TaskRun] = [] 163 runs = self.task.runs(readonly=True) 164 165 # first pass, we look for repaired outputs. These are the best examples. 166 for run in runs: 167 if len(valid_examples) >= self.__class__.example_count(): 168 break 169 if run.repaired_output is not None: 170 valid_examples.append(run) 171 172 # second pass, we look for high quality outputs (rating based) 173 # Minimum is "high_quality" (4 star in star rating scale), then sort by rating 174 # exclude repaired outputs as they were used above 175 runs_with_rating = [ 176 run 177 for run in runs 178 if run.output.rating is not None 179 and run.output.rating.value is not None 180 and run.output.rating.is_high_quality() 181 and run.repaired_output is None 182 ] 183 runs_with_rating.sort( 184 key=lambda x: (x.output.rating and x.output.rating.value) or 0, reverse=True 185 ) 186 for run in runs_with_rating: 187 if len(valid_examples) >= self.__class__.example_count(): 188 break 189 valid_examples.append(run) 190 return valid_examples
193class FewShotPromptBuilder(MultiShotPromptBuilder): 194 """A prompt builder that includes a small number of examples in the prompt.""" 195 196 @classmethod 197 def example_count(cls) -> int: 198 """Get the maximum number of examples to include in the prompt. 199 200 Returns: 201 int: The maximum number of examples (4). 202 """ 203 return 4
A prompt builder that includes a small number of examples in the prompt.
196 @classmethod 197 def example_count(cls) -> int: 198 """Get the maximum number of examples to include in the prompt. 199 200 Returns: 201 int: The maximum number of examples (4). 202 """ 203 return 4
Get the maximum number of examples to include in the prompt.
Returns: int: The maximum number of examples (4).
206class CustomExamplePromptBuilder(FewShotPromptBuilder): 207 """A prompt builder that uses custom examples instead of collecting from the dataset.""" 208 209 def __init__(self, task: Task, examples: list[PromptExample] | None = None): 210 super().__init__(task) 211 self._custom_examples = examples or [] 212 213 def collect_examples(self) -> list[TaskRun]: 214 """Override to return an empty list - we handle examples separately.""" 215 return [] 216 217 def build_base_prompt(self) -> str: 218 """Build a prompt with instruction, requirements, and custom examples.""" 219 base_prompt = self.build_instruction_and_requirements() 220 221 if self._custom_examples: 222 base_prompt += "# Example Outputs\n\n" 223 for i, example in enumerate(self._custom_examples): 224 base_prompt += f"## Example {i + 1}\n\nInput: {example.input}\nOutput: {example.output}\n\n" 225 226 return base_prompt
A prompt builder that uses custom examples instead of collecting from the dataset.
209 def __init__(self, task: Task, examples: list[PromptExample] | None = None): 210 super().__init__(task) 211 self._custom_examples = examples or []
Initialize the prompt builder with a task.
Args: task (Task): The task containing instructions and requirements.
213 def collect_examples(self) -> list[TaskRun]: 214 """Override to return an empty list - we handle examples separately.""" 215 return []
Override to return an empty list - we handle examples separately.
217 def build_base_prompt(self) -> str: 218 """Build a prompt with instruction, requirements, and custom examples.""" 219 base_prompt = self.build_instruction_and_requirements() 220 221 if self._custom_examples: 222 base_prompt += "# Example Outputs\n\n" 223 for i, example in enumerate(self._custom_examples): 224 base_prompt += f"## Example {i + 1}\n\nInput: {example.input}\nOutput: {example.output}\n\n" 225 226 return base_prompt
Build a prompt with instruction, requirements, and custom examples.
229class RepairsPromptBuilder(MultiShotPromptBuilder): 230 """A prompt builder that includes multiple examples in the prompt, including repaired instructions describing what was wrong, and how it was fixed.""" 231 232 def prompt_section_for_example(self, index: int, example: TaskRun) -> str: 233 if ( 234 not example.repaired_output 235 or not example.repair_instructions 236 or not example.repaired_output.output 237 ): 238 return super().prompt_section_for_example(index, example) 239 240 prompt_section = f"## Example {index + 1}\n\nInput: {example.input}\n\n" 241 prompt_section += ( 242 f"Initial Output Which Was Insufficient: {example.output.output}\n\n" 243 ) 244 prompt_section += f"Instructions On How to Improve the Initial Output: {example.repair_instructions}\n\n" 245 prompt_section += ( 246 f"Repaired Output Which is Sufficient: {example.repaired_output.output}\n\n" 247 ) 248 return prompt_section
A prompt builder that includes multiple examples in the prompt, including repaired instructions describing what was wrong, and how it was fixed.
232 def prompt_section_for_example(self, index: int, example: TaskRun) -> str: 233 if ( 234 not example.repaired_output 235 or not example.repair_instructions 236 or not example.repaired_output.output 237 ): 238 return super().prompt_section_for_example(index, example) 239 240 prompt_section = f"## Example {index + 1}\n\nInput: {example.input}\n\n" 241 prompt_section += ( 242 f"Initial Output Which Was Insufficient: {example.output.output}\n\n" 243 ) 244 prompt_section += f"Instructions On How to Improve the Initial Output: {example.repair_instructions}\n\n" 245 prompt_section += ( 246 f"Repaired Output Which is Sufficient: {example.repaired_output.output}\n\n" 247 ) 248 return prompt_section
251def chain_of_thought_prompt(task: Task) -> str: 252 """Standard implementation to build and return the chain of thought prompt string. 253 254 Returns: 255 str: The constructed chain of thought prompt. 256 """ 257 258 cot_instruction = task.thinking_instruction 259 if not cot_instruction: 260 cot_instruction = "Think step by step, explaining your reasoning." 261 262 return cot_instruction
Standard implementation to build and return the chain of thought prompt string.
Returns: str: The constructed chain of thought prompt.
265class SimpleChainOfThoughtPromptBuilder(SimplePromptBuilder): 266 """A prompt builder that includes a chain of thought prompt on top of the simple prompt.""" 267 268 def chain_of_thought_prompt(self) -> str | None: 269 return chain_of_thought_prompt(self.task)
A prompt builder that includes a chain of thought prompt on top of the simple prompt.
272class FewShotChainOfThoughtPromptBuilder(FewShotPromptBuilder): 273 """A prompt builder that includes a chain of thought prompt on top of the few shot prompt.""" 274 275 def chain_of_thought_prompt(self) -> str | None: 276 return chain_of_thought_prompt(self.task)
A prompt builder that includes a chain of thought prompt on top of the few shot prompt.
279class MultiShotChainOfThoughtPromptBuilder(MultiShotPromptBuilder): 280 """A prompt builder that includes a chain of thought prompt on top of the multi shot prompt.""" 281 282 def chain_of_thought_prompt(self) -> str | None: 283 return chain_of_thought_prompt(self.task)
A prompt builder that includes a chain of thought prompt on top of the multi shot prompt.
286class SavedPromptBuilder(BasePromptBuilder): 287 """A prompt builder that looks up a static prompt.""" 288 289 def __init__(self, task: Task, prompt_id: str): 290 super().__init__(task) 291 prompt_model = next( 292 ( 293 prompt 294 for prompt in task.prompts(readonly=True) 295 if prompt.id == prompt_id 296 ), 297 None, 298 ) 299 if not prompt_model: 300 raise ValueError(f"Prompt ID not found: {prompt_id}") 301 self.prompt_model = prompt_model 302 303 def prompt_id(self) -> str | None: 304 return self.prompt_model.id 305 306 def build_base_prompt(self) -> str: 307 """Returns a saved prompt. 308 309 Returns: 310 str: The prompt string. 311 """ 312 return self.prompt_model.prompt 313 314 def chain_of_thought_prompt(self) -> str | None: 315 return self.prompt_model.chain_of_thought_instructions
A prompt builder that looks up a static prompt.
289 def __init__(self, task: Task, prompt_id: str): 290 super().__init__(task) 291 prompt_model = next( 292 ( 293 prompt 294 for prompt in task.prompts(readonly=True) 295 if prompt.id == prompt_id 296 ), 297 None, 298 ) 299 if not prompt_model: 300 raise ValueError(f"Prompt ID not found: {prompt_id}") 301 self.prompt_model = prompt_model
Initialize the prompt builder with a task.
Args: task (Task): The task containing instructions and requirements.
Returns the ID of the prompt, scoped to this builder.
Returns: str | None: The ID of the prompt, or None if not set.
306 def build_base_prompt(self) -> str: 307 """Returns a saved prompt. 308 309 Returns: 310 str: The prompt string. 311 """ 312 return self.prompt_model.prompt
Returns a saved prompt.
Returns: str: The prompt string.
314 def chain_of_thought_prompt(self) -> str | None: 315 return self.prompt_model.chain_of_thought_instructions
Build and return the chain of thought prompt string.
Returns: str: The constructed chain of thought prompt.
Inherited Members
318class TaskRunConfigPromptBuilder(BasePromptBuilder): 319 """A prompt builder that looks up a static prompt in a task run config.""" 320 321 def __init__(self, task: Task, run_config_prompt_id: str): 322 parts = run_config_prompt_id.split("::") 323 if len(parts) != 4: 324 raise ValueError( 325 f"Invalid task run config prompt ID: {run_config_prompt_id}. Expected format: 'task_run_config::[project_id]::[task_id]::[run_config_id]'." 326 ) 327 328 task_id = parts[2] 329 if task_id != task.id: 330 raise ValueError( 331 f"Task run config prompt ID: {run_config_prompt_id}. Task ID mismatch. Expected: {task.id}, got: {task_id}." 332 ) 333 334 run_config_id = parts[3] 335 run_config = next( 336 ( 337 run_config 338 for run_config in task.run_configs(readonly=True) 339 if run_config.id == run_config_id 340 ), 341 None, 342 ) 343 if not run_config: 344 raise ValueError( 345 f"Task run config ID not found: {run_config_id} for prompt id {run_config_prompt_id}" 346 ) 347 if run_config.prompt is None: 348 raise ValueError( 349 f"Task run config ID {run_config_id} does not have a stored prompt. Used as prompt id {run_config_prompt_id}" 350 ) 351 352 # Load the prompt from the model 353 self.prompt = run_config.prompt.prompt 354 self.cot_prompt = run_config.prompt.chain_of_thought_instructions 355 self.id = run_config_prompt_id 356 357 super().__init__(task) 358 359 def prompt_id(self) -> str | None: 360 return self.id 361 362 def build_base_prompt(self) -> str: 363 return self.prompt 364 365 def chain_of_thought_prompt(self) -> str | None: 366 return self.cot_prompt
A prompt builder that looks up a static prompt in a task run config.
321 def __init__(self, task: Task, run_config_prompt_id: str): 322 parts = run_config_prompt_id.split("::") 323 if len(parts) != 4: 324 raise ValueError( 325 f"Invalid task run config prompt ID: {run_config_prompt_id}. Expected format: 'task_run_config::[project_id]::[task_id]::[run_config_id]'." 326 ) 327 328 task_id = parts[2] 329 if task_id != task.id: 330 raise ValueError( 331 f"Task run config prompt ID: {run_config_prompt_id}. Task ID mismatch. Expected: {task.id}, got: {task_id}." 332 ) 333 334 run_config_id = parts[3] 335 run_config = next( 336 ( 337 run_config 338 for run_config in task.run_configs(readonly=True) 339 if run_config.id == run_config_id 340 ), 341 None, 342 ) 343 if not run_config: 344 raise ValueError( 345 f"Task run config ID not found: {run_config_id} for prompt id {run_config_prompt_id}" 346 ) 347 if run_config.prompt is None: 348 raise ValueError( 349 f"Task run config ID {run_config_id} does not have a stored prompt. Used as prompt id {run_config_prompt_id}" 350 ) 351 352 # Load the prompt from the model 353 self.prompt = run_config.prompt.prompt 354 self.cot_prompt = run_config.prompt.chain_of_thought_instructions 355 self.id = run_config_prompt_id 356 357 super().__init__(task)
Initialize the prompt builder with a task.
Args: task (Task): The task containing instructions and requirements.
Returns the ID of the prompt, scoped to this builder.
Returns: str | None: The ID of the prompt, or None if not set.
Build and return the complete prompt string.
Returns: str: The constructed prompt.
Build and return the chain of thought prompt string.
Returns: str: The constructed chain of thought prompt.
Inherited Members
369class FineTunePromptBuilder(BasePromptBuilder): 370 """A prompt builder that looks up a fine-tune prompt.""" 371 372 def __init__(self, task: Task, nested_fine_tune_id: str): 373 super().__init__(task) 374 375 # IDs are in project_id::task_id::fine_tune_id format 376 self.full_fine_tune_id = nested_fine_tune_id 377 parts = nested_fine_tune_id.split("::") 378 if len(parts) != 3: 379 raise ValueError( 380 f"Invalid fine-tune ID format. Expected 'project_id::task_id::fine_tune_id', got: {nested_fine_tune_id}" 381 ) 382 fine_tune_id = parts[2] 383 384 fine_tune_model = next( 385 ( 386 fine_tune 387 for fine_tune in task.finetunes(readonly=True) 388 if fine_tune.id == fine_tune_id 389 ), 390 None, 391 ) 392 if not fine_tune_model: 393 raise ValueError(f"Fine-tune ID not found: {fine_tune_id}") 394 self.fine_tune_model = fine_tune_model 395 396 def prompt_id(self) -> str | None: 397 return self.full_fine_tune_id 398 399 def build_base_prompt(self) -> str: 400 return self.fine_tune_model.system_message 401 402 def chain_of_thought_prompt(self) -> str | None: 403 return self.fine_tune_model.thinking_instructions
A prompt builder that looks up a fine-tune prompt.
372 def __init__(self, task: Task, nested_fine_tune_id: str): 373 super().__init__(task) 374 375 # IDs are in project_id::task_id::fine_tune_id format 376 self.full_fine_tune_id = nested_fine_tune_id 377 parts = nested_fine_tune_id.split("::") 378 if len(parts) != 3: 379 raise ValueError( 380 f"Invalid fine-tune ID format. Expected 'project_id::task_id::fine_tune_id', got: {nested_fine_tune_id}" 381 ) 382 fine_tune_id = parts[2] 383 384 fine_tune_model = next( 385 ( 386 fine_tune 387 for fine_tune in task.finetunes(readonly=True) 388 if fine_tune.id == fine_tune_id 389 ), 390 None, 391 ) 392 if not fine_tune_model: 393 raise ValueError(f"Fine-tune ID not found: {fine_tune_id}") 394 self.fine_tune_model = fine_tune_model
Initialize the prompt builder with a task.
Args: task (Task): The task containing instructions and requirements.
Returns the ID of the prompt, scoped to this builder.
Returns: str | None: The ID of the prompt, or None if not set.
Build and return the complete prompt string.
Returns: str: The constructed prompt.
402 def chain_of_thought_prompt(self) -> str | None: 403 return self.fine_tune_model.thinking_instructions
Build and return the chain of thought prompt string.
Returns: str: The constructed chain of thought prompt.
Inherited Members
407def prompt_builder_from_id(prompt_id: PromptId, task: Task) -> BasePromptBuilder: 408 """Convert a name used in the UI to the corresponding prompt builder class. 409 410 Args: 411 prompt_id (PromptId): The prompt ID. 412 413 Returns: 414 type[BasePromptBuilder]: The corresponding prompt builder class. 415 416 Raises: 417 ValueError: If the UI name is not recognized. 418 """ 419 420 # Saved prompts are prefixed with "id::" 421 if prompt_id.startswith("id::"): 422 prompt_id = prompt_id[4:] 423 return SavedPromptBuilder(task, prompt_id) 424 425 # Task run config prompts are prefixed with "task_run_config::" 426 # task_run_config::[project_id]::[task_id]::[run_config_id] 427 if prompt_id.startswith("task_run_config::"): 428 return TaskRunConfigPromptBuilder(task, prompt_id) 429 430 # Fine-tune prompts are prefixed with "fine_tune_prompt::" 431 if prompt_id.startswith("fine_tune_prompt::"): 432 prompt_id = prompt_id[18:] 433 return FineTunePromptBuilder(task, prompt_id) 434 435 # Check if the prompt_id matches any enum value 436 if prompt_id not in [member.value for member in PromptGenerators]: 437 raise ValueError(f"Unknown prompt generator: {prompt_id}") 438 typed_prompt_generator = PromptGenerators(prompt_id) 439 440 match typed_prompt_generator: 441 case PromptGenerators.SIMPLE: 442 return SimplePromptBuilder(task) 443 case PromptGenerators.FEW_SHOT: 444 return FewShotPromptBuilder(task) 445 case PromptGenerators.MULTI_SHOT: 446 return MultiShotPromptBuilder(task) 447 case PromptGenerators.REPAIRS: 448 return RepairsPromptBuilder(task) 449 case PromptGenerators.SIMPLE_CHAIN_OF_THOUGHT: 450 return SimpleChainOfThoughtPromptBuilder(task) 451 case PromptGenerators.FEW_SHOT_CHAIN_OF_THOUGHT: 452 return FewShotChainOfThoughtPromptBuilder(task) 453 case PromptGenerators.MULTI_SHOT_CHAIN_OF_THOUGHT: 454 return MultiShotChainOfThoughtPromptBuilder(task) 455 case _: 456 # Type checking will find missing cases 457 raise_exhaustive_enum_error(typed_prompt_generator)
Convert a name used in the UI to the corresponding prompt builder class.
Args: prompt_id (PromptId): The prompt ID.
Returns: type[BasePromptBuilder]: The corresponding prompt builder class.
Raises: ValueError: If the UI name is not recognized.