feat(prompt): configurable per-platform system-prompt hint overrides

Add platform_hints config so an admin can append to or replace Hermes'
built-in platform hint for a single messaging platform (WhatsApp, Slack,
Telegram, ...) without affecting other platforms. Enables enterprise
managed profiles to steer platform-aware skills (e.g. invoke a custom
table-formatting skill on WhatsApp where Markdown tables don't render)
while leaving Telegram/Slack/CLI behavior unchanged.

- hermes_cli/config.py: document platform_hints in DEFAULT_CONFIG
- agent/agent_init.py: load platform_hints -> agent._platform_hint_overrides
- agent/system_prompt.py: _resolve_platform_hint() applies append/replace
  (replace wins; bare string = append shorthand); defensive on bad config
- tests: 16 cases covering append/replace/shorthand/isolation/malformed

Override only affects the platform-hint segment of the system prompt;
SOUL/context/memory tiers and general instructions are unchanged.
This commit is contained in:
Victor Kyriazakos 2026-06-18 19:19:48 +03:00 committed by Teknium
parent 2944b3c394
commit 3ead2bdd0d
5 changed files with 193 additions and 2 deletions

View file

@ -1239,6 +1239,23 @@ def init_agent(
# are noisy.
agent._environment_probe = bool(_agent_section.get("environment_probe", True))
# Per-platform prompt-hint overrides (config.yaml → platform_hints).
# Lets an enterprise admin append to or replace Hermes' built-in
# platform hint for a single messaging platform (e.g. WhatsApp) without
# affecting other platforms. Shape:
# platform_hints:
# whatsapp:
# append: "When tabular output would help, invoke the ... skill."
# slack:
# replace: "Custom Slack hint that fully replaces the default."
# Stored verbatim; resolution happens in agent/system_prompt.py against
# the active platform. Invalid shapes are ignored defensively so a bad
# config entry can never break prompt assembly.
_platform_hints_cfg = _agent_cfg.get("platform_hints", {})
if not isinstance(_platform_hints_cfg, dict):
_platform_hints_cfg = {}
agent._platform_hint_overrides = _platform_hints_cfg
# App-level API retry count (wraps each model API call). Default 3,
# overridable via agent.api_max_retries in config.yaml. See #11616.
try:

View file

@ -61,6 +61,55 @@ def _ra():
return run_agent
def _resolve_platform_hint(agent: Any, platform_key: str, default_hint: str) -> str:
"""Apply a per-platform prompt-hint override to the default hint.
Reads ``agent._platform_hint_overrides`` (populated from
``config.yaml`` ``platform_hints`` by ``agent_init``) and resolves the
effective hint for *platform_key*:
* ``replace`` substitute the default hint entirely.
* ``append`` keep the default and append the extra text.
* a bare string value treated as ``append`` (convenience shorthand).
Precedence: ``replace`` wins over ``append`` if both are present.
Override text is added on top of (not instead of) the SOUL/context/
memory tiers it only affects the platform-hint segment, so other
platforms are unaffected and general system instructions still apply.
Defensive: any malformed entry falls back to the unmodified default so
a bad config value can never break prompt assembly or leak across
platforms.
"""
if not platform_key:
return default_hint
overrides = getattr(agent, "_platform_hint_overrides", None)
if not isinstance(overrides, dict) or not overrides:
return default_hint
spec = overrides.get(platform_key)
if spec is None:
return default_hint
# Shorthand: a bare string is treated as append text.
if isinstance(spec, str):
extra = spec.strip()
return f"{default_hint}\n\n{extra}".strip() if extra else default_hint
if not isinstance(spec, dict):
return default_hint
replace_text = spec.get("replace")
if isinstance(replace_text, str) and replace_text.strip():
base = replace_text.strip()
else:
base = default_hint
append_text = spec.get("append")
if isinstance(append_text, str) and append_text.strip():
return f"{base}\n\n{append_text.strip()}".strip()
return base
def build_system_prompt_parts(agent: Any, system_message: Optional[str] = None) -> Dict[str, str]:
"""Assemble the system prompt as three ordered parts.
@ -331,18 +380,25 @@ def build_system_prompt_parts(agent: Any, system_message: Optional[str] = None)
)
platform_key = (agent.platform or "").lower().strip()
# Resolve the built-in/plugin default hint for this platform, then apply
# any per-platform override from config (platform_hints.<platform>).
_default_hint = ""
if platform_key in PLATFORM_HINTS:
stable_parts.append(PLATFORM_HINTS[platform_key])
_default_hint = PLATFORM_HINTS[platform_key]
elif platform_key:
# Check plugin registry for platform-specific LLM guidance
try:
from gateway.platform_registry import platform_registry
_entry = platform_registry.get(platform_key)
if _entry and _entry.platform_hint:
stable_parts.append(_entry.platform_hint)
_default_hint = _entry.platform_hint
except Exception:
pass
_effective_hint = _resolve_platform_hint(agent, platform_key, _default_hint)
if _effective_hint:
stable_parts.append(_effective_hint)
# ── Context tier (cwd-dependent, may change between sessions) ─
context_parts: List[str] = []

View file

@ -2170,6 +2170,22 @@ DEFAULT_CONFIG = {
# User-defined quick commands that bypass the agent loop (type: exec only)
"quick_commands": {},
# Per-platform system-prompt hint overrides. Lets an admin append to or
# replace Hermes' built-in platform hint for a single messaging platform
# (WhatsApp, Slack, Telegram, ...) without affecting other platforms.
# Useful for enterprise/managed profiles that ship platform-aware skills.
# Each key is a platform name; the value is either:
# { "append": "extra text" } — keep the default hint, append text
# { "replace": "full text" } — substitute the default hint entirely
# "extra text" — shorthand for { "append": ... }
# `replace` wins over `append` if both are given. Example:
# platform_hints:
# whatsapp:
# append: >
# When tabular output would be useful, invoke the
# table_formatting skill instead of emitting a Markdown table.
"platform_hints": {},
# Shell-script hooks — declarative bridge that invokes shell scripts
# on plugin-hook events (pre_tool_call, post_tool_call, pre_llm_call,
# subagent_stop, etc.). Each entry maps an event name to a list of

View file

@ -45,6 +45,7 @@ ACP_REGISTRY_MANIFEST = REPO_ROOT / "acp_registry" / "agent.json"
# Auto-extracted from noreply emails + manual overrides
AUTHOR_MAP = {
"victor@rocketfueldev.com": "victor-kyriazakos",
"286497132+srojk34@users.noreply.github.com": "srojk34",
"59806492+sitkarev@users.noreply.github.com": "sitkarev",
"zheng@omegasys.eu": "omegazheng",

View file

@ -0,0 +1,101 @@
"""Tests for per-platform prompt-hint overrides (config.yaml → platform_hints).
Covers agent/system_prompt.py::_resolve_platform_hint the resolver that
applies append/replace overrides to a platform's default hint. Feature added
for enterprise managed profiles (per-platform behavior without affecting other
platforms). See HA Core ticket: configurable per-platform prompt hints.
"""
import types
from agent.system_prompt import _resolve_platform_hint
def _agent(overrides):
"""Minimal stand-in carrying just the override attribute the resolver reads."""
a = types.SimpleNamespace()
a._platform_hint_overrides = overrides
return a
DEFAULT = "You are on WhatsApp. Do not use markdown."
EXTRA = "When tabular output would help, invoke the table_formatting skill."
class TestResolvePlatformHint:
def test_no_overrides_returns_default(self):
assert _resolve_platform_hint(_agent({}), "whatsapp", DEFAULT) == DEFAULT
def test_missing_attr_returns_default(self):
a = types.SimpleNamespace() # no _platform_hint_overrides at all
assert _resolve_platform_hint(a, "whatsapp", DEFAULT) == DEFAULT
def test_platform_not_in_overrides_returns_default(self):
a = _agent({"slack": {"append": "x"}})
assert _resolve_platform_hint(a, "whatsapp", DEFAULT) == DEFAULT
def test_append_dict(self):
a = _agent({"whatsapp": {"append": EXTRA}})
out = _resolve_platform_hint(a, "whatsapp", DEFAULT)
assert out == f"{DEFAULT}\n\n{EXTRA}"
assert DEFAULT in out and EXTRA in out
def test_replace_dict(self):
a = _agent({"whatsapp": {"replace": EXTRA}})
out = _resolve_platform_hint(a, "whatsapp", DEFAULT)
assert out == EXTRA
assert DEFAULT not in out
def test_replace_wins_over_append_but_both_applied(self):
a = _agent({"whatsapp": {"replace": "BASE", "append": "TAIL"}})
out = _resolve_platform_hint(a, "whatsapp", DEFAULT)
# replace substitutes the base, append still tacks on
assert out == "BASE\n\nTAIL"
assert DEFAULT not in out
def test_bare_string_is_append_shorthand(self):
a = _agent({"whatsapp": EXTRA})
out = _resolve_platform_hint(a, "whatsapp", DEFAULT)
assert out == f"{DEFAULT}\n\n{EXTRA}"
def test_other_platform_unaffected(self):
"""An override for whatsapp must not change telegram's hint."""
a = _agent({"whatsapp": {"append": EXTRA}})
tg_default = "You are on Telegram. Markdown works."
assert _resolve_platform_hint(a, "telegram", tg_default) == tg_default
def test_empty_platform_key_returns_default(self):
a = _agent({"whatsapp": {"append": EXTRA}})
assert _resolve_platform_hint(a, "", DEFAULT) == DEFAULT
# --- defensive / malformed input: never break prompt assembly ---
def test_malformed_spec_list_returns_default(self):
a = _agent({"whatsapp": ["not", "valid"]})
assert _resolve_platform_hint(a, "whatsapp", DEFAULT) == DEFAULT
def test_overrides_not_a_dict_returns_default(self):
a = _agent(["nope"])
assert _resolve_platform_hint(a, "whatsapp", DEFAULT) == DEFAULT
def test_empty_append_string_returns_default(self):
a = _agent({"whatsapp": {"append": " "}})
assert _resolve_platform_hint(a, "whatsapp", DEFAULT) == DEFAULT
def test_empty_replace_falls_back_to_default_base(self):
a = _agent({"whatsapp": {"replace": " "}})
assert _resolve_platform_hint(a, "whatsapp", DEFAULT) == DEFAULT
def test_non_string_append_ignored(self):
a = _agent({"whatsapp": {"append": 123}})
assert _resolve_platform_hint(a, "whatsapp", DEFAULT) == DEFAULT
def test_replace_with_empty_default_hint(self):
"""replace works even when the platform had no built-in default."""
a = _agent({"customplat": {"replace": "Custom hint."}})
assert _resolve_platform_hint(a, "customplat", "") == "Custom hint."
def test_append_with_empty_default_hint(self):
"""append on a platform with no default just yields the extra text."""
a = _agent({"customplat": {"append": "Only this."}})
assert _resolve_platform_hint(a, "customplat", "") == "Only this."