mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-11 13:41:53 +00:00
159 lines
5 KiB
Python
159 lines
5 KiB
Python
import random
|
|
|
|
from gateway.status_phrases import (
|
|
classify_status_context,
|
|
choose_status_phrase,
|
|
resolve_status_phrase_catalog,
|
|
)
|
|
|
|
|
|
def test_long_running_context_uses_status_bucket():
|
|
assert classify_status_context("status") == "status"
|
|
assert classify_status_context("heartbeat") == "status"
|
|
assert classify_status_context("long_running") == "status"
|
|
|
|
|
|
def test_non_status_context_falls_back_to_generic_bucket():
|
|
assert classify_status_context("tool", tool_name="terminal") == "generic"
|
|
assert classify_status_context("thinking") == "generic"
|
|
assert classify_status_context("interim_assistant") == "generic"
|
|
|
|
|
|
def test_status_phrase_does_not_leak_raw_preview_or_args():
|
|
msg = choose_status_phrase(
|
|
"status",
|
|
preview="actual private scratch text should not be sent",
|
|
args={"secret": "SECRET-123"},
|
|
rng=random.Random(4),
|
|
)
|
|
|
|
assert "actual private scratch" not in msg
|
|
assert "SECRET-123" not in msg
|
|
assert msg
|
|
|
|
|
|
def test_status_phrase_avoids_recent_repetition():
|
|
recent: list[str] = []
|
|
first = choose_status_phrase("status", rng=random.Random(2), recent=recent)
|
|
second = choose_status_phrase("status", rng=random.Random(2), recent=recent)
|
|
|
|
assert first != second
|
|
assert recent[-2:] == [first, second]
|
|
|
|
|
|
def test_builtin_catalog_is_loaded_from_external_asset_and_is_status_only():
|
|
catalog = resolve_status_phrase_catalog({}, "whatsapp")
|
|
|
|
assert set(catalog) == {"status", "generic"}
|
|
assert len(catalog["status"]) >= 25
|
|
assert len(catalog["generic"]) >= 10
|
|
|
|
|
|
def test_relative_status_phrase_path_loads_from_hermes_home(tmp_path, monkeypatch):
|
|
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
|
|
phrase_file = tmp_path / "phrases.yaml"
|
|
phrase_file.write_text("mode: replace\nstatus:\n - relative safe status text\n", encoding="utf-8")
|
|
|
|
catalog = resolve_status_phrase_catalog(
|
|
{"display": {"status_phrases": {"path": "phrases.yaml"}}},
|
|
"whatsapp",
|
|
)
|
|
|
|
assert catalog["status"] == ["relative safe status text"]
|
|
|
|
|
|
def test_status_phrase_path_can_load_relative_directory(tmp_path, monkeypatch):
|
|
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
|
|
phrase_dir = tmp_path / "phrase-catalog"
|
|
phrase_dir.mkdir()
|
|
(phrase_dir / "01-status.yaml").write_text("status:\n - relative dir status text\n", encoding="utf-8")
|
|
|
|
catalog = resolve_status_phrase_catalog(
|
|
{"display": {"status_phrases": {"path": "phrase-catalog"}}},
|
|
"whatsapp",
|
|
)
|
|
|
|
assert "relative dir status text" in catalog["status"]
|
|
|
|
|
|
def test_absolute_or_parent_phrase_paths_are_ignored(tmp_path, monkeypatch):
|
|
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
|
|
outside = tmp_path.parent / "outside-phrases.yaml"
|
|
outside.write_text("mode: replace\nstatus:\n - should not load\n", encoding="utf-8")
|
|
|
|
catalog = resolve_status_phrase_catalog(
|
|
{"display": {"status_phrases": {"path": str(outside)}}},
|
|
"whatsapp",
|
|
)
|
|
escaped = resolve_status_phrase_catalog(
|
|
{"display": {"status_phrases": {"path": "../outside-phrases.yaml"}}},
|
|
"whatsapp",
|
|
)
|
|
|
|
assert catalog["status"] != ["should not load"]
|
|
assert escaped["status"] != ["should not load"]
|
|
|
|
|
|
def test_conventional_relative_status_phrase_file_is_loaded(tmp_path, monkeypatch):
|
|
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
|
|
(tmp_path / "status_phrases.yaml").write_text(
|
|
"mode: replace\nstatus:\n - conventional status text\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
catalog = resolve_status_phrase_catalog({}, "whatsapp")
|
|
|
|
assert catalog["status"] == ["conventional status text"]
|
|
|
|
|
|
def test_global_custom_status_phrase_catalog_appends_to_builtin():
|
|
catalog = resolve_status_phrase_catalog(
|
|
{
|
|
"display": {
|
|
"status_phrases": {
|
|
"status": ["custom long-running placeholder"],
|
|
}
|
|
}
|
|
},
|
|
"whatsapp",
|
|
)
|
|
|
|
assert "custom long-running placeholder" in catalog["status"]
|
|
assert len(catalog["status"]) > 1
|
|
|
|
|
|
def test_platform_custom_status_phrase_catalog_can_replace_surface():
|
|
catalog = resolve_status_phrase_catalog(
|
|
{
|
|
"display": {
|
|
"platforms": {
|
|
"whatsapp": {
|
|
"status_phrases": {
|
|
"mode": "replace",
|
|
"status": ["custom status placeholder"],
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"whatsapp",
|
|
)
|
|
|
|
assert catalog["status"] == ["custom status placeholder"]
|
|
assert len(catalog["generic"]) > 1
|
|
|
|
|
|
def test_choose_status_phrase_uses_custom_catalog_without_leaking_args():
|
|
catalog = resolve_status_phrase_catalog(
|
|
{"display": {"status_phrases": {"mode": "replace", "status": ["custom safe status text"]}}},
|
|
"whatsapp",
|
|
)
|
|
|
|
msg = choose_status_phrase(
|
|
"status",
|
|
args={"query": "SECRET SEARCH"},
|
|
catalog=catalog,
|
|
)
|
|
|
|
assert msg == "custom safe status text"
|
|
assert "SECRET" not in msg
|