Source code for evoproc_procedures.prompts

"""Prompt builders for procedure creation and step execution.

These helpers produce **LLM prompts** used by the GA scaffold and your runners:

- :func:`create_direct_prompt` — a simple “ask the question directly” prompt
  (handy for baselines and ARC-style multiple choice).
- :func:`create_procedure_prompt` — asks the model to emit a *global-state*
  procedure JSON that validates your Pydantic schema.
- :func:`create_execution_prompt` — runs a single step by showing inputs,
  the step action, and the required outputs, while reminding the model to
  return **strict JSON** conforming to a supplied schema.

All functions are pure string builders and have no network side-effects.
They are safe to use in tests and with any LLM backend.

Example
-------
>>> from evoproc_procedures.prompts import create_procedure_prompt
>>> p = create_procedure_prompt("Natalia sold clips to 48 friends in April...")
>>> isinstance(p, str)
True
"""

from __future__ import annotations

import json
from typing import Any, Dict, Iterable, Optional

# Import from your plugin package, not from "src."
from evoproc_procedures.models import Procedure

__all__ = [
    "create_direct_prompt",
    "create_procedure_prompt",
    "create_execution_prompt",
]


[docs] def create_direct_prompt(item: str) -> str: """Build a direct Q→A prompt (baseline / ARC-style). Args: item: The task text (e.g., question stem or problem statement). Returns: A concise prompt string suitable for a direct answer call. """ # Keep it minimal to reduce unintended reasoning scaffolds in baselines. return f"Solve this problem: {item}"
[docs] def create_procedure_prompt(item: str, example_prompt: Optional[str] = None) -> str: """Ask the LLM to synthesize a *global-state* procedure JSON. The model is instructed to return **exactly one** JSON object that validates the Pydantic schema produced by :class:`uhj_procedures.models.Procedure`. Args: item: Natural-language task to decompose. example_prompt: Optional extra hint or in-context example description (kept as a single string you can preformat upstream). Returns: A prompt string suitable for a structured generation call. Notes: - The schema is injected verbatim via ``Procedure.model_json_schema()``. - Constraints match your GA scaffold: step 1 uses ``problem_text`` only, later steps may read any earlier variables (global state), and the final step emits ``final_answer`` **as a description only**. """ schema_json = json.dumps(Procedure.model_json_schema(), ensure_ascii=False) example = f"\n# Example (hint)\n{example_prompt}\n" if example_prompt else "" # No fenced code blocks; some providers echo fences literally. return ( "Decompose the task into small, single-action steps to solve the problem.\n" f"# Task\n{item}\n" f"{example}" "# Output Contract\n" "Return exactly one JSON object that validates against this schema (verbatim):\n" f"{schema_json}\n" "## Global IO Constraints (must follow)\n" "- Global state: Steps may read any variable produced by earlier steps.\n" "- Step 1 inputs: exactly ['problem_text'].\n" "- Inputs resolvable: Every step input must come from problem_text or from some earlier " "step's outputs (by name).\n" "- Variable names: snake_case; consistent across steps.\n" "- Descriptions: concise and concrete.\n" "- No numeric results: do not compute or reveal numeric values or the final answer.\n" "- Final step: outputs exactly ['final_answer'] (description only).\n" "## Step rules\n" "- 'step_description' is a single, imperative action.\n" "## Validation Checklist (self-check before returning)\n" "- JSON validates against schema.\n" "- Each step has id, input(s), step_description, output(s).\n" "- Step 1 input is exactly problem_text.\n" "- All step inputs are available in the global state (problem_text or prior outputs).\n" "- Final step outputs exactly final_answer with a descriptive definition only.\n" "Return ONLY the JSON object. Do not include commentary." )
[docs] def create_execution_prompt( visible_inputs: Dict[str, Any], action: str, schema: Dict[str, Any], expected_outputs: Iterable[str], output_descriptions: Optional[Dict[str, str]] = None, *, is_final: bool = False, ) -> str: """Build a prompt to execute a **single step** with strict JSON output. Args: visible_inputs: The subset of global state the step may read (already extracted variables). action: The step's imperative instruction (e.g., \"extract the two numbers a and b\"). schema: JSON Schema that the **output object** must validate against (for regular steps use your step-output schema; for the last step you can pass your answer schema, e.g. GSM/ARC). expected_outputs: Names of keys the model must return in the JSON object. output_descriptions: Optional mapping of key → human description (shown to the model). is_final: If ``True``, annotate that these outputs constitute the final answer. Returns: A prompt string that instructs the model to return only a JSON object matching ``schema`` and containing exactly ``expected_outputs``. """ output_lines = [] output_descriptions = output_descriptions or {} for name in expected_outputs: desc = output_descriptions.get(name, "") output_lines.append(f"- {name}: {desc}".rstrip()) outputs_block = "\n".join(output_lines) if output_lines else "(see schema)" inputs_block = json.dumps(visible_inputs, ensure_ascii=False, indent=2) schema_block = json.dumps(schema, ensure_ascii=False, indent=2) final_note = " (final_answer)" if is_final else "" return ( f"{action}\n" f"# Inputs (JSON)\n{inputs_block}\n" "# Required Outputs\n" f"Return a JSON object with exactly these keys{final_note}:\n" f"{outputs_block}\n\n" "# Format\n" "- Return ONLY a JSON object that conforms to the provided schema.\n" "- Do NOT include extra keys.\n" "- Do NOT include commentary.\n\n" "# Schema (verbatim)\n" f"{schema_block}" )