GEPA Native (SkyDiscover)
Stored-parent reflective evolution with an elite pool, rejection history, and LLM-mediated merge.
"""Reflective prompt builder for SkyDiscover's GEPA Native search strategy."""
from __future__ import annotations
import re
from ...components.prompt import DefaultPromptBuilder
from ...models.base import Prompt
from ...records import LoopContext, Selection
from .population import GepaNativePopulation, genome_score
_TEXT_LANGUAGES = {"text", "prompt", "text/plain"}
def _prompt_optimization_task(language: str, timeout_warning: str) -> str:
"""SkyDiscover ``full_rewrite_prompt_opt_user_message.txt`` from its ``# Task`` onward."""
return (
"# Task\n"
"Suggest improvements to the prompt that will improve its COMBINED_SCORE.\n"
"The system maintains diversity across these dimensions: score, complexity.\n"
"Different prompts with similar combined_score but different strategies are valuable.\n"
"\n"
"Provide the complete rewritten prompt.\n"
"\n"
"IMPORTANT: Your rewritten prompt must serve the same task as the original prompt,\n"
"but with improved instructions, structure, or reasoning guidance.\n"
"Do NOT change the task the prompt is designed for - only improve HOW it instructs the LLM.\n"
"\n"
f"```{language}\n"
"# Your rewritten prompt here\n"
"```\n"
"\n"
"Be thoughtful about your changes and explain your reasoning thoroughly.\n"
"Consider these prompt engineering strategies:\n"
"- Clearer step-by-step reasoning instructions\n"
"- Better output format constraints\n"
"- More precise task framing and role definitions\n"
"- Adding or refining examples\n"
"- Reducing ambiguity in instructions\n"
"\n"
'IMPORTANT: If an instruction header of "## IMPORTANT: ..." is given below the '
'"# Current Solution", you MUST follow it. Otherwise,\n'
"focus on targeted improvements of the prompt.\n"
"\n"
f"{timeout_warning}"
)
class GepaNativePromptBuilder(DefaultPromptBuilder):
"""Insert recent stored-parent rejections immediately before the normal mutation task."""
def __init__(
self,
population: GepaNativePopulation,
max_recent_failures: int = 5,
**kwargs,
):
super().__init__(**kwargs)
self.population = population
self.max_recent_failures = int(max_recent_failures)
def build(
self,
selection: Selection,
memory=None,
ctx: LoopContext | None = None,
) -> Prompt:
prompt = super().build(selection, memory, ctx)
guidance = self._build_search_guidance()
user = prompt.user
if (
self.mutation_approach.name == "full_rewrite"
and self.language.lower() in _TEXT_LANGUAGES
):
head, separator, _tail = user.rpartition("\n# Task\n")
if separator:
head = head.replace(
"# Current Solution Information",
"# Current Prompt Information",
1,
).replace(
"# Program Generation History",
"# Prompt Generation History",
1,
)
user = (
f"{head.rstrip()}\n\n"
f"{_prompt_optimization_task(self.language, self._timeout_warning())}"
)
if guidance:
head, separator, tail = user.rpartition("\n# Task\n")
if separator:
user = f"{head.rstrip()}\n\n{guidance}\n\n# Task\n{tail}"
# Upstream collapses the blank placeholder left by an empty guidance section too.
user = re.sub(r"\n{3,}", "\n\n", user)
return Prompt(system=prompt.system, user=user)
def _build_search_guidance(self) -> str:
rejected = self.population.get_rejection_history(self.max_recent_failures)
if not rejected:
return ""
rejection_section = self._format_rejection_history(rejected)
if not rejection_section:
return ""
header = (
"## Reflective Analysis\n"
"Review the evaluation results and diagnostics in the program "
"information above. Identify root causes and domain-specific "
"insights. Address these failure modes in your solution."
)
return f"{header}\n\n{rejection_section}"
def _format_rejection_history(self, rejected: list) -> str | None:
if not rejected:
return None
entries: list[str] = []
for index, genome in enumerate(rejected, 1):
parent_score = ""
parent = self.population.get(genome.parent_id)
if parent is not None:
parent_score = f", parent_score: {genome_score(parent):.4f}"
error_message = ""
if genome.scores:
error_message = (
genome.scores.get("error", "")
or genome.scores.get("error_message", "")
)
changes = genome.metadata.get("changes", "") if genome.metadata else ""
lines = [
f"#### Attempt {index} "
f"(score: {genome_score(genome):.4f}{parent_score})"
]
if changes:
lines.append(f"Changes: {changes}")
if error_message:
lines.append(f"Error: {error_message}")
if genome.content:
code_lines = genome.content.splitlines()
snippet = (
"\n".join(code_lines[:30]) + "\n... (truncated)"
if len(code_lines) > 30
else genome.content
)
lines.append(f"Code tried:\n```\n{snippet}\n```")
entries.append("\n".join(lines))
return "\n\n".join([
"### Recent Rejected Attempts",
"The following mutations were rejected because they did not "
"improve on the parent. Avoid repeating these approaches:",
*entries,
])