Component handler for prompt strings held in a caller-owned mapping.
Some prompts live outside the agent, for example in a dict a tool reads before it calls an external service. MappingComponentHandler evolves one key of such a mapping, and register_mapping_components registers one handler per key so each key becomes an ordinary component name.
Examples:
Evolve the prompts a tool reads:
from gepa_adk import evolve, register_mapping_components
prompts = {"summarize": "Summarize the text.", "classify": "Label it."}
names = register_mapping_components(prompts)
result = await evolve(
agent,
trainset,
components=names,
component_selector="round_robin",
)
prompts.update({name: result.evolved_components[name] for name in names})
See Also
Notes
The handlers write into the caller's mapping during evaluation and restore it afterwards, so the caller applies result.evolved_components itself once evolution finishes.
MappingComponentHandler
Handler for one string value in a mapping the caller owns.
Serializes, applies and restores mapping[key]. The agent argument of each method is ignored, because the value does not live on the agent.
| ATTRIBUTE | DESCRIPTION |
mapping | The caller's mapping, held by reference so writes are visible to its other readers. TYPE: MutableMapping[str, str] |
key | The mapping key this handler evolves. TYPE: str |
Examples:
prompts = {"greeting": "hello"}
handler = MappingComponentHandler(prompts, key="greeting")
original = handler.apply(None, "hi") # prompts["greeting"] == "hi"
handler.restore(None, original) # prompts["greeting"] == "hello"
Notes
The handler keeps a reference to the mapping, not a copy. Replacing the mapping object elsewhere detaches the handler from it.
Source code in src/gepa_adk/adapters/components/mapping_handler.py
| class MappingComponentHandler:
"""Handler for one string value in a mapping the caller owns.
Serializes, applies and restores ``mapping[key]``. The agent argument of
each method is ignored, because the value does not live on the agent.
Attributes:
mapping (MutableMapping[str, str]): The caller's mapping, held by
reference so writes are visible to its other readers.
key (str): The mapping key this handler evolves.
Examples:
```python
prompts = {"greeting": "hello"}
handler = MappingComponentHandler(prompts, key="greeting")
original = handler.apply(None, "hi") # prompts["greeting"] == "hi"
handler.restore(None, original) # prompts["greeting"] == "hello"
```
Notes:
The handler keeps a reference to the mapping, not a copy. Replacing
the mapping object elsewhere detaches the handler from it.
"""
def __init__(self, mapping: MutableMapping[str, str], key: str) -> None:
"""Bind the handler to one key of the caller's mapping.
Args:
mapping: The caller-owned mapping holding the prompt text.
key: The key whose value this handler evolves.
Raises:
ValueError: If ``key`` is not in ``mapping``.
TypeError: If ``mapping[key]`` is not a ``str``.
Examples:
```python
handler = MappingComponentHandler({"k": "v"}, key="k")
```
"""
if key not in mapping:
raise ValueError(f"Mapping has no key {key!r} to evolve")
value: object = mapping[key]
if not isinstance(value, str):
raise TypeError(
f"Mapping value for key {key!r} must be str, got {type(value).__name__}"
)
self.mapping = mapping
self.key = key
def serialize(self, agent: Any) -> str:
"""Return the current text of the mapping key.
Args:
agent: Ignored; may be ``None``.
Returns:
The value of ``mapping[key]``, or an empty string when the caller
has removed the key or replaced its value with non-text since
registration.
Examples:
```python
text = handler.serialize(None)
```
Notes:
Never raises, per the ``ComponentHandler`` contract. A missing or
non-text value is logged as ``mapping_handler.key_missing``.
"""
value = self.mapping.get(self.key)
if not isinstance(value, str):
logger.warning(
"mapping_handler.key_missing",
key=self.key,
value_type=type(value).__name__,
)
return ""
return value
def apply(self, agent: Any, value: str) -> str:
"""Write new text to the mapping key and return the previous text.
Args:
agent: Ignored; may be ``None``.
value: The new text for the key.
Returns:
The text the key held before this call.
Examples:
```python
original = handler.apply(None, "new text")
```
"""
original = self.mapping[self.key]
self.mapping[self.key] = value
logger.debug(
"mapping_handler.apply",
key=self.key,
original_preview=original[:50],
new_preview=value[:50],
)
return original
def restore(self, agent: Any, original: str) -> None:
"""Write the original text back to the mapping key.
Args:
agent: Ignored; may be ``None``.
original: The text returned by ``apply()``.
Examples:
```python
handler.restore(None, original)
```
"""
self.mapping[self.key] = original
logger.debug("mapping_handler.restore", key=self.key, preview=original[:50])
|
__init__
__init__(
mapping: MutableMapping[str, str], key: str
) -> None
Bind the handler to one key of the caller's mapping.
| PARAMETER | DESCRIPTION |
mapping | The caller-owned mapping holding the prompt text. TYPE: MutableMapping[str, str] |
key | The key whose value this handler evolves. TYPE: str |
| RAISES | DESCRIPTION |
ValueError | If key is not in mapping. |
TypeError | If mapping[key] is not a str. |
Examples:
handler = MappingComponentHandler({"k": "v"}, key="k")
Source code in src/gepa_adk/adapters/components/mapping_handler.py
| def __init__(self, mapping: MutableMapping[str, str], key: str) -> None:
"""Bind the handler to one key of the caller's mapping.
Args:
mapping: The caller-owned mapping holding the prompt text.
key: The key whose value this handler evolves.
Raises:
ValueError: If ``key`` is not in ``mapping``.
TypeError: If ``mapping[key]`` is not a ``str``.
Examples:
```python
handler = MappingComponentHandler({"k": "v"}, key="k")
```
"""
if key not in mapping:
raise ValueError(f"Mapping has no key {key!r} to evolve")
value: object = mapping[key]
if not isinstance(value, str):
raise TypeError(
f"Mapping value for key {key!r} must be str, got {type(value).__name__}"
)
self.mapping = mapping
self.key = key
|
serialize
serialize(agent: Any) -> str
Return the current text of the mapping key.
| PARAMETER | DESCRIPTION |
agent | TYPE: Any |
| RETURNS | DESCRIPTION |
str | The value of mapping[key], or an empty string when the caller |
str | has removed the key or replaced its value with non-text since |
str | |
Examples:
text = handler.serialize(None)
Notes
Never raises, per the ComponentHandler contract. A missing or non-text value is logged as mapping_handler.key_missing.
Source code in src/gepa_adk/adapters/components/mapping_handler.py
| def serialize(self, agent: Any) -> str:
"""Return the current text of the mapping key.
Args:
agent: Ignored; may be ``None``.
Returns:
The value of ``mapping[key]``, or an empty string when the caller
has removed the key or replaced its value with non-text since
registration.
Examples:
```python
text = handler.serialize(None)
```
Notes:
Never raises, per the ``ComponentHandler`` contract. A missing or
non-text value is logged as ``mapping_handler.key_missing``.
"""
value = self.mapping.get(self.key)
if not isinstance(value, str):
logger.warning(
"mapping_handler.key_missing",
key=self.key,
value_type=type(value).__name__,
)
return ""
return value
|
apply
apply(agent: Any, value: str) -> str
Write new text to the mapping key and return the previous text.
| PARAMETER | DESCRIPTION |
agent | TYPE: Any |
value | The new text for the key. TYPE: str |
| RETURNS | DESCRIPTION |
str | The text the key held before this call. |
Examples:
original = handler.apply(None, "new text")
Source code in src/gepa_adk/adapters/components/mapping_handler.py
| def apply(self, agent: Any, value: str) -> str:
"""Write new text to the mapping key and return the previous text.
Args:
agent: Ignored; may be ``None``.
value: The new text for the key.
Returns:
The text the key held before this call.
Examples:
```python
original = handler.apply(None, "new text")
```
"""
original = self.mapping[self.key]
self.mapping[self.key] = value
logger.debug(
"mapping_handler.apply",
key=self.key,
original_preview=original[:50],
new_preview=value[:50],
)
return original
|
restore
restore(agent: Any, original: str) -> None
Write the original text back to the mapping key.
| PARAMETER | DESCRIPTION |
agent | TYPE: Any |
original | The text returned by apply(). TYPE: str |
Examples:
handler.restore(None, original)
Source code in src/gepa_adk/adapters/components/mapping_handler.py
| def restore(self, agent: Any, original: str) -> None:
"""Write the original text back to the mapping key.
Args:
agent: Ignored; may be ``None``.
original: The text returned by ``apply()``.
Examples:
```python
handler.restore(None, original)
```
"""
self.mapping[self.key] = original
logger.debug("mapping_handler.restore", key=self.key, preview=original[:50])
|
register_mapping_components
register_mapping_components(
mapping: MutableMapping[str, str],
*,
registry: ComponentHandlerRegistry | None = None,
) -> list[str]
Register a handler for every key of a mapping.
Each key becomes a component name backed by a MappingComponentHandler. Every key is validated before any handler is registered, so a rejected mapping leaves the registry unchanged.
| PARAMETER | DESCRIPTION |
mapping | The caller-owned mapping of prompt strings. TYPE: MutableMapping[str, str] |
registry | The registry to register into. Defaults to the default component_handlers registry that evolve() consults. TYPE: ComponentHandlerRegistry | None DEFAULT: None |
| RETURNS | DESCRIPTION |
list[str] | The registered component names, in mapping order. |
| RAISES | DESCRIPTION |
ValueError | If the mapping is empty, a key is not a Python identifier, or a key equals instruction or output_schema. |
TypeError | |
Examples:
prompts = {"greeting": "hello", "farewell": "bye"}
names = register_mapping_components(prompts)
# names == ["greeting", "farewell"]
See Also
Notes
Registering a name that already has a handler replaces it.
Source code in src/gepa_adk/adapters/components/mapping_handler.py
| def register_mapping_components(
mapping: MutableMapping[str, str],
*,
registry: ComponentHandlerRegistry | None = None,
) -> list[str]:
"""Register a handler for every key of a mapping.
Each key becomes a component name backed by a
``MappingComponentHandler``. Every key is validated before any handler
is registered, so a rejected mapping leaves the registry unchanged.
Args:
mapping: The caller-owned mapping of prompt strings.
registry: The registry to register into. Defaults to the default
``component_handlers`` registry that ``evolve()`` consults.
Returns:
The registered component names, in mapping order.
Raises:
ValueError: If the mapping is empty, a key is not a Python
identifier, or a key equals ``instruction`` or ``output_schema``.
TypeError: If a value is not a ``str``.
Examples:
```python
prompts = {"greeting": "hello", "farewell": "bye"}
names = register_mapping_components(prompts)
# names == ["greeting", "farewell"]
```
See Also:
- [`MappingComponentHandler`][gepa_adk.adapters.components.MappingComponentHandler]:
The handler registered for each key.
Notes:
Registering a name that already has a handler replaces it.
"""
if not mapping:
raise ValueError("Cannot register components from an empty mapping")
handlers: list[tuple[str, MappingComponentHandler]] = []
for key in mapping:
if not key.isidentifier():
raise ValueError(
f"Mapping key {key!r} must be a Python identifier to be a "
"component name"
)
if key in _RESERVED_NAMES:
raise ValueError(f"Mapping key {key!r} shadows a built-in component name")
handlers.append((key, MappingComponentHandler(mapping, key)))
target = registry if registry is not None else component_handlers
for key, handler in handlers:
target.register(key, handler)
return [key for key, _ in handlers]
|