Default implementations of the selection ports.
This module provides the default component selector and evaluation policy beside the protocols they implement. They live in ports so that both the engine, which falls back on them when no strategy is passed, and the adapter selection modules, which re-export them, may import them under ADR-000.
| ATTRIBUTE | DESCRIPTION |
RoundRobinComponentSelector | Default ComponentSelectorProtocol implementation; cycles through components sequentially. TYPE: class |
FullEvaluationPolicy | Default EvaluationPolicyProtocol implementation; scores all validation examples every iteration. TYPE: class |
Examples:
Use the defaults directly:
from gepa_adk.ports.defaults import (
FullEvaluationPolicy,
RoundRobinComponentSelector,
)
selector = RoundRobinComponentSelector()
components = await selector.select_components(["a", "b"], 1, 0)
policy = FullEvaluationPolicy()
batch = policy.get_eval_batch([0, 1, 2], state)
See Also
Notes
These classes depend only on the standard library and the domain layer, so they satisfy both the import-linter layers contract and the ADR-000 boundary script without an exception.
RoundRobinComponentSelector
Selects components in a round-robin fashion.
This selector cycles through the list of components one by one, maintaining state per candidate index to ensure consistent rotation.
| ATTRIBUTE | DESCRIPTION |
_next_index | Mapping of candidate_idx to next component index. TYPE: dict[int, int] |
Examples:
selector = RoundRobinComponentSelector()
# First call selects first component
c1 = await selector.select_components(["a", "b"], 1, 0) # ["a"]
# Second call selects second component
c2 = await selector.select_components(["a", "b"], 2, 0) # ["b"]
Notes
Alternates through components sequentially, ensuring balanced evolution across all candidate parts.
Source code in src/gepa_adk/ports/defaults.py
| class RoundRobinComponentSelector:
"""Selects components in a round-robin fashion.
This selector cycles through the list of components one by one, maintaining
state per candidate index to ensure consistent rotation.
Attributes:
_next_index (dict[int, int]): Mapping of candidate_idx to next component index.
Examples:
```python
selector = RoundRobinComponentSelector()
# First call selects first component
c1 = await selector.select_components(["a", "b"], 1, 0) # ["a"]
# Second call selects second component
c2 = await selector.select_components(["a", "b"], 2, 0) # ["b"]
```
Notes:
Alternates through components sequentially, ensuring balanced evolution
across all candidate parts.
"""
def __init__(self) -> None:
"""Initialize the round-robin selector.
Notes:
Creates empty index tracking dictionary for per-candidate rotation state.
"""
self._next_index: dict[int, int] = defaultdict(int)
async def select_components(
self, components: list[str], iteration: int, candidate_idx: int
) -> list[str]:
"""Select a single component to update using round-robin logic.
Args:
components: List of available component keys.
iteration: Current global iteration number (unused by this strategy).
candidate_idx: Index of the candidate being evolved.
Returns:
List containing the single selected component key.
Raises:
ValueError: If components list is empty.
Examples:
```python
selected = await selector.select_components(["a", "b"], 1, 0)
```
Notes:
Outputs one component per call, advancing the rotation index for
the specified candidate.
"""
if not components:
raise ValueError("No components provided for selection")
# Get current index for this candidate
current_idx = self._next_index[candidate_idx]
# Select component
selected = components[current_idx % len(components)]
# Advance index for next time
self._next_index[candidate_idx] = (current_idx + 1) % len(components)
return [selected]
|
__init__
Initialize the round-robin selector.
Notes
Creates empty index tracking dictionary for per-candidate rotation state.
Source code in src/gepa_adk/ports/defaults.py
| def __init__(self) -> None:
"""Initialize the round-robin selector.
Notes:
Creates empty index tracking dictionary for per-candidate rotation state.
"""
self._next_index: dict[int, int] = defaultdict(int)
|
select_components async
select_components(
components: list[str],
iteration: int,
candidate_idx: int,
) -> list[str]
Select a single component to update using round-robin logic.
| PARAMETER | DESCRIPTION |
components | List of available component keys. TYPE: list[str] |
iteration | Current global iteration number (unused by this strategy). TYPE: int |
candidate_idx | Index of the candidate being evolved. TYPE: int |
| RETURNS | DESCRIPTION |
list[str] | List containing the single selected component key. |
| RAISES | DESCRIPTION |
ValueError | If components list is empty. |
Examples:
selected = await selector.select_components(["a", "b"], 1, 0)
Notes
Outputs one component per call, advancing the rotation index for the specified candidate.
Source code in src/gepa_adk/ports/defaults.py
| async def select_components(
self, components: list[str], iteration: int, candidate_idx: int
) -> list[str]:
"""Select a single component to update using round-robin logic.
Args:
components: List of available component keys.
iteration: Current global iteration number (unused by this strategy).
candidate_idx: Index of the candidate being evolved.
Returns:
List containing the single selected component key.
Raises:
ValueError: If components list is empty.
Examples:
```python
selected = await selector.select_components(["a", "b"], 1, 0)
```
Notes:
Outputs one component per call, advancing the rotation index for
the specified candidate.
"""
if not components:
raise ValueError("No components provided for selection")
# Get current index for this candidate
current_idx = self._next_index[candidate_idx]
# Select component
selected = components[current_idx % len(components)]
# Advance index for next time
self._next_index[candidate_idx] = (current_idx + 1) % len(components)
return [selected]
|
FullEvaluationPolicy
Evaluation policy that scores all validation examples every iteration.
This is the default evaluation policy, providing complete visibility into solution performance across all validation examples.
Notes
Always returns all valset IDs, ensuring complete evaluation coverage each iteration.
Examples:
policy = FullEvaluationPolicy()
batch = policy.get_eval_batch([0, 1, 2, 3, 4], state)
# Returns: [0, 1, 2, 3, 4]
Source code in src/gepa_adk/ports/defaults.py
| class FullEvaluationPolicy:
"""Evaluation policy that scores all validation examples every iteration.
This is the default evaluation policy, providing complete visibility
into solution performance across all validation examples.
Notes:
Always returns all valset IDs, ensuring complete evaluation coverage
each iteration.
Examples:
```python
policy = FullEvaluationPolicy()
batch = policy.get_eval_batch([0, 1, 2, 3, 4], state)
# Returns: [0, 1, 2, 3, 4]
```
"""
def get_eval_batch(
self,
valset_ids: Sequence[int],
state: ParetoState,
target_candidate_idx: int | None = None,
) -> list[int]:
"""Return all validation example indices.
Args:
valset_ids (Sequence[int]): All available validation example indices.
state (ParetoState): Current evolution state (unused for full evaluation).
target_candidate_idx (int | None): Optional candidate being evaluated
(unused).
Returns:
list[int]: List of all valset_ids.
Notes:
Outputs the complete valset for comprehensive evaluation coverage.
Examples:
```python
policy = FullEvaluationPolicy()
batch = policy.get_eval_batch([0, 1, 2], state)
assert batch == [0, 1, 2]
```
"""
return list(valset_ids)
def get_best_candidate(self, state: ParetoState) -> int:
"""Return index of candidate with highest average score.
Args:
state: Current evolution state with candidate scores.
Returns:
Index of best performing candidate.
Raises:
NoCandidateAvailableError: If state has no candidates.
Notes:
Outputs the candidate index with the highest mean score across
all evaluated examples.
Examples:
```python
policy = FullEvaluationPolicy()
best_idx = policy.get_best_candidate(state)
```
"""
if not state.candidates:
raise NoCandidateAvailableError("No candidates available")
best_idx = None
best_score = float("-inf")
for candidate_idx in range(len(state.candidates)):
score = self.get_valset_score(candidate_idx, state)
if score > best_score:
best_score = score
best_idx = candidate_idx
if best_idx is None:
raise NoCandidateAvailableError("No scored candidates available")
return best_idx
def get_valset_score(self, candidate_idx: int, state: ParetoState) -> float:
"""Return mean score across all evaluated examples for a candidate.
Args:
candidate_idx (int): Index of candidate to score.
state (ParetoState): Current evolution state.
Returns:
float: Mean score across all examples, or float('-inf') if no scores.
Notes:
Outputs the arithmetic mean of all scores for the candidate,
or negative infinity if no scores exist.
Examples:
```python
policy = FullEvaluationPolicy()
score = policy.get_valset_score(0, state)
```
"""
scores = state.candidate_scores.get(candidate_idx)
if not scores:
return float("-inf")
return fmean(scores.values())
|
get_eval_batch
get_eval_batch(
valset_ids: Sequence[int],
state: ParetoState,
target_candidate_idx: int | None = None,
) -> list[int]
Return all validation example indices.
| PARAMETER | DESCRIPTION |
valset_ids | All available validation example indices. TYPE: Sequence[int] |
state | Current evolution state (unused for full evaluation). TYPE: ParetoState |
target_candidate_idx | Optional candidate being evaluated (unused). TYPE: int | None DEFAULT: None |
| RETURNS | DESCRIPTION |
list[int] | list[int]: List of all valset_ids. |
Notes
Outputs the complete valset for comprehensive evaluation coverage.
Examples:
policy = FullEvaluationPolicy()
batch = policy.get_eval_batch([0, 1, 2], state)
assert batch == [0, 1, 2]
Source code in src/gepa_adk/ports/defaults.py
| def get_eval_batch(
self,
valset_ids: Sequence[int],
state: ParetoState,
target_candidate_idx: int | None = None,
) -> list[int]:
"""Return all validation example indices.
Args:
valset_ids (Sequence[int]): All available validation example indices.
state (ParetoState): Current evolution state (unused for full evaluation).
target_candidate_idx (int | None): Optional candidate being evaluated
(unused).
Returns:
list[int]: List of all valset_ids.
Notes:
Outputs the complete valset for comprehensive evaluation coverage.
Examples:
```python
policy = FullEvaluationPolicy()
batch = policy.get_eval_batch([0, 1, 2], state)
assert batch == [0, 1, 2]
```
"""
return list(valset_ids)
|
get_best_candidate
Return index of candidate with highest average score.
| PARAMETER | DESCRIPTION |
state | Current evolution state with candidate scores. TYPE: ParetoState |
| RETURNS | DESCRIPTION |
int | Index of best performing candidate. |
Notes
Outputs the candidate index with the highest mean score across all evaluated examples.
Examples:
policy = FullEvaluationPolicy()
best_idx = policy.get_best_candidate(state)
Source code in src/gepa_adk/ports/defaults.py
| def get_best_candidate(self, state: ParetoState) -> int:
"""Return index of candidate with highest average score.
Args:
state: Current evolution state with candidate scores.
Returns:
Index of best performing candidate.
Raises:
NoCandidateAvailableError: If state has no candidates.
Notes:
Outputs the candidate index with the highest mean score across
all evaluated examples.
Examples:
```python
policy = FullEvaluationPolicy()
best_idx = policy.get_best_candidate(state)
```
"""
if not state.candidates:
raise NoCandidateAvailableError("No candidates available")
best_idx = None
best_score = float("-inf")
for candidate_idx in range(len(state.candidates)):
score = self.get_valset_score(candidate_idx, state)
if score > best_score:
best_score = score
best_idx = candidate_idx
if best_idx is None:
raise NoCandidateAvailableError("No scored candidates available")
return best_idx
|
get_valset_score
get_valset_score(
candidate_idx: int, state: ParetoState
) -> float
Return mean score across all evaluated examples for a candidate.
| PARAMETER | DESCRIPTION |
candidate_idx | Index of candidate to score. TYPE: int |
state | TYPE: ParetoState |
| RETURNS | DESCRIPTION |
float | Mean score across all examples, or float('-inf') if no scores. TYPE: float |
Notes
Outputs the arithmetic mean of all scores for the candidate, or negative infinity if no scores exist.
Examples:
policy = FullEvaluationPolicy()
score = policy.get_valset_score(0, state)
Source code in src/gepa_adk/ports/defaults.py
| def get_valset_score(self, candidate_idx: int, state: ParetoState) -> float:
"""Return mean score across all evaluated examples for a candidate.
Args:
candidate_idx (int): Index of candidate to score.
state (ParetoState): Current evolution state.
Returns:
float: Mean score across all examples, or float('-inf') if no scores.
Notes:
Outputs the arithmetic mean of all scores for the candidate,
or negative infinity if no scores exist.
Examples:
```python
policy = FullEvaluationPolicy()
score = policy.get_valset_score(0, state)
```
"""
scores = state.candidate_scores.get(candidate_idx)
if not scores:
return float("-inf")
return fmean(scores.values())
|