Source code for prompt_recipe_smith.builder

from __future__ import annotations

from typing import Any

from prompt_recipe_smith.models import PromptBranch, PromptRecipe, PromptResult
from prompt_recipe_smith.providers import ChatGPTProvider, PromptProvider


[docs] class PromptBuilder: """Engine that applies prompt recipes."""
[docs] def __init__(self, provider: PromptProvider | None = None) -> None: self.provider = provider or ChatGPTProvider()
[docs] def build( self, recipe: PromptRecipe, user_input: str, variables: dict[str, Any] | None = None, ) -> PromptResult: selected_branch = self._select_branch(recipe, user_input) template = selected_branch.template if selected_branch else None template = template or recipe.final_template render_variables: dict[str, Any] = { **recipe.defaults, **(variables or {}), "user_input": user_input, "recipe_name": recipe.name, "branch_name": selected_branch.name if selected_branch else "", } prompt = self.provider.adapt(template.render(render_variables)) return PromptResult( prompt=prompt, recipe_name=recipe.name, user_input=user_input, selected_branch=selected_branch.name if selected_branch else None, steps=recipe.steps, provider=self.provider.name, )
[docs] def build_layered( self, recipe: PromptRecipe, user_input: str, answers: dict[str, str], variables: dict[str, Any] | None = None, ) -> PromptResult: selected_branch = self._select_branch(recipe, user_input) template = recipe.layered_final_template or recipe.final_template answer_items = tuple( (question.key, answers.get(question.key, "")) for question in recipe.questions ) answer_variables = { f"answer_{key}": answer for key, answer in answer_items } render_variables: dict[str, Any] = { **recipe.defaults, **(variables or {}), **answer_variables, "user_input": user_input, "recipe_name": recipe.name, "branch_name": selected_branch.name if selected_branch else "", "clarifications": self._format_answers(recipe, answer_items), } prompt = self.provider.adapt(template.render(render_variables)) return PromptResult( prompt=prompt, recipe_name=recipe.name, user_input=user_input, selected_branch=selected_branch.name if selected_branch else None, steps=recipe.steps, provider=self.provider.name, answers=answer_items, )
def _select_branch( self, recipe: PromptRecipe, user_input: str ) -> PromptBranch | None: for branch in recipe.branches: if branch.matches(user_input): return branch return None def _format_answers( self, recipe: PromptRecipe, answer_items: tuple[tuple[str, str], ...], ) -> str: question_text_by_key = { question.key: question.text for question in recipe.questions } lines = [] for key, answer in answer_items: question_text = question_text_by_key.get(key, key) lines.append(f"- {question_text}: {answer}") return "\n".join(lines)