refactor: split flat core into capability packages, layer the conversations service, English defaults for every model-facing text

This commit is contained in:
hh
2026-09-02 00:13:20 +02:00
parent b96714338f
commit cae2ed4161
77 changed files with 2987 additions and 2944 deletions
+42
View File
@@ -0,0 +1,42 @@
"""System prompt assembly from a list of source files.
The gateway holds no prompt text: an agent names its granules (paths from
``config.py``) and :func:`assemble` concatenates them in that order, so the
result is byte-for-byte identical for every session of the same agent as
long as the files are. A source is a path, or a ``(tag, path)`` pair whose
content is wrapped in ``<tag>...</tag>`` - the markup lives here, the vault
keeps plain markdown. Each granule's hash is logged at assembly so a
drifted prompt can be traced to the file that changed.
"""
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:
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</{tag}>" if tag else text)
return "\n\n".join(parts) + "\n"