Skip to content

Prompts

Generate LLM instruction prompts from a block Registry (or a single block type) so models emit blocks the same registry can parse. See the Generating Prompts guide for usage. Registry.to_prompt(), Registry.register_template(), and Registry.serialize_block() are documented on the Registry & Models page.

hother.streamblocks.prompts

Prompt generation from block registries and block classes.

TemplateManager

Manage Jinja2 templates with version support for prompt A/B testing.

Built-in templates live in the package templates/ directory and are named <mode>.jinja2 (default version) or <mode>_<version>.jinja2. Custom templates can be registered at runtime via :meth:register_template.

Source code in src/hother/streamblocks/prompts/manager.py
class TemplateManager:
    """Manage Jinja2 templates with version support for prompt A/B testing.

    Built-in templates live in the package ``templates/`` directory and are
    named ``<mode>.jinja2`` (default version) or ``<mode>_<version>.jinja2``.
    Custom templates can be registered at runtime via :meth:`register_template`.
    """

    def __init__(self) -> None:
        """Initialize the manager with the package template loader."""
        self._custom_templates: dict[tuple[str, str], Template] = {}
        self._env = Environment(
            loader=PackageLoader("hother.streamblocks.prompts", "templates"),
            autoescape=False,
            trim_blocks=True,
            lstrip_blocks=True,
        )

    def register_template(self, version: str, template: str | Path, mode: str = _MODE_BOTH) -> None:
        """Register a custom template for one or both render modes.

        Args:
            version: Version identifier (e.g. "concise").
            template: Template string, or a Path to a template file.
            mode: "registry", "single", or "both".
        """
        template_str = template.read_text(encoding="utf-8") if isinstance(template, Path) else template
        template_obj = Template(template_str, autoescape=False, trim_blocks=True, lstrip_blocks=True)

        if mode in (_MODE_REGISTRY, _MODE_BOTH):
            self._custom_templates[(_MODE_REGISTRY, version)] = template_obj
        if mode in (_MODE_SINGLE, _MODE_BOTH):
            self._custom_templates[(_MODE_SINGLE, version)] = template_obj

    def get_template(self, version: str = _DEFAULT_VERSION, mode: str = _MODE_REGISTRY) -> Template:
        """Return the template for the given version and mode.

        Custom-registered templates take priority over built-in package
        templates.
        """
        custom = self._custom_templates.get((mode, version))
        if custom is not None:
            return custom

        filename = f"{mode}.jinja2" if version == _DEFAULT_VERSION else f"{mode}_{version}.jinja2"
        return self._env.get_template(filename)

    def render(self, context: dict[str, Any], version: str = _DEFAULT_VERSION, mode: str = _MODE_REGISTRY) -> str:
        """Render the resolved template with the given context."""
        return self.get_template(version, mode).render(context)

get_template

get_template(
    version: str = _DEFAULT_VERSION,
    mode: str = _MODE_REGISTRY,
) -> Template

Return the template for the given version and mode.

Custom-registered templates take priority over built-in package templates.

Source code in src/hother/streamblocks/prompts/manager.py
def get_template(self, version: str = _DEFAULT_VERSION, mode: str = _MODE_REGISTRY) -> Template:
    """Return the template for the given version and mode.

    Custom-registered templates take priority over built-in package
    templates.
    """
    custom = self._custom_templates.get((mode, version))
    if custom is not None:
        return custom

    filename = f"{mode}.jinja2" if version == _DEFAULT_VERSION else f"{mode}_{version}.jinja2"
    return self._env.get_template(filename)

register_template

register_template(
    version: str,
    template: str | Path,
    mode: str = _MODE_BOTH,
) -> None

Register a custom template for one or both render modes.

Parameters:

Name Type Description Default
version str

Version identifier (e.g. "concise").

required
template str | Path

Template string, or a Path to a template file.

required
mode str

"registry", "single", or "both".

_MODE_BOTH
Source code in src/hother/streamblocks/prompts/manager.py
def register_template(self, version: str, template: str | Path, mode: str = _MODE_BOTH) -> None:
    """Register a custom template for one or both render modes.

    Args:
        version: Version identifier (e.g. "concise").
        template: Template string, or a Path to a template file.
        mode: "registry", "single", or "both".
    """
    template_str = template.read_text(encoding="utf-8") if isinstance(template, Path) else template
    template_obj = Template(template_str, autoescape=False, trim_blocks=True, lstrip_blocks=True)

    if mode in (_MODE_REGISTRY, _MODE_BOTH):
        self._custom_templates[(_MODE_REGISTRY, version)] = template_obj
    if mode in (_MODE_SINGLE, _MODE_BOTH):
        self._custom_templates[(_MODE_SINGLE, version)] = template_obj

render

render(
    context: dict[str, Any],
    version: str = _DEFAULT_VERSION,
    mode: str = _MODE_REGISTRY,
) -> str

Render the resolved template with the given context.

Source code in src/hother/streamblocks/prompts/manager.py
def render(self, context: dict[str, Any], version: str = _DEFAULT_VERSION, mode: str = _MODE_REGISTRY) -> str:
    """Render the resolved template with the given context."""
    return self.get_template(version, mode).render(context)

generate_block_prompt

generate_block_prompt(
    block_class: type[Block[Any, Any]],
    syntax: BaseSyntax,
    *,
    include_examples: bool = True,
    template_version: str = "default",
    description: str = "",
) -> str

Generate an instruction prompt for a single block type.

Parameters:

Name Type Description Default
block_class type[Block[Any, Any]]

The block class to document.

required
syntax BaseSyntax

Syntax used to describe the format and serialize examples.

required
include_examples bool

Whether to render examples from get_examples().

True
template_version str

Template version for A/B testing.

'default'
description str

Optional description overriding the block docstring.

''

Returns:

Type Description
str

The rendered prompt for this block type.

Source code in src/hother/streamblocks/prompts/builder.py
def generate_block_prompt(
    block_class: type[Block[Any, Any]],
    syntax: BaseSyntax,
    *,
    include_examples: bool = True,
    template_version: str = "default",
    description: str = "",
) -> str:
    """Generate an instruction prompt for a single block type.

    Args:
        block_class: The block class to document.
        syntax: Syntax used to describe the format and serialize examples.
        include_examples: Whether to render examples from ``get_examples()``.
        template_version: Template version for A/B testing.
        description: Optional description overriding the block docstring.

    Returns:
        The rendered prompt for this block type.
    """
    context: dict[str, Any] = {
        "syntax_name": type(syntax).__name__,
        "syntax_format": syntax.describe_format(),
        "block": build_block_context(
            block_class,
            syntax,
            include_examples=include_examples,
            description=description,
        ),
    }
    return TemplateManager().render(context, template_version, mode="single")