"""System prompt assembly from a list of source files. :func:`assemble` concatenates an agent's granules in order. A source is a path, or a ``(tag, path)`` pair whose content is wrapped in ``...``. """ from __future__ import annotations import hashlib import logging from pathlib import Path from typing import TYPE_CHECKING if TYPE_CHECKING: from collections.abc import Iterable __all__ = ["PromptSource", "assemble"] _log = logging.getLogger("beaver_gateway.agents.prompts") PromptSource = str | Path | tuple[str, str | Path] def assemble(sources: Iterable[PromptSource]) -> str: """Concatenate ``sources`` in order into one system prompt.""" parts: list[str] = [] for source in sources: tag, raw = source if isinstance(source, tuple) else (None, source) path = Path(raw) text = path.read_text(encoding="utf-8").strip() digest = hashlib.sha256(text.encode("utf-8")).hexdigest()[:12] _log.info( "prompt granule %s tag=%s sha=%s bytes=%d", path, tag, digest, len(text) ) if not text: continue parts.append(f"<{tag}>\n{text}\n" if tag else text) return "\n\n".join(parts) + "\n"