mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
Merge c257a11eea into 00c3d848d8
This commit is contained in:
commit
f6cbd4e32c
5 changed files with 61 additions and 2 deletions
|
|
@ -27,6 +27,7 @@ def build_write_denied_paths(home: str) -> set[str]:
|
||||||
os.path.join(home, ".ssh", "id_ed25519"),
|
os.path.join(home, ".ssh", "id_ed25519"),
|
||||||
os.path.join(home, ".ssh", "config"),
|
os.path.join(home, ".ssh", "config"),
|
||||||
str(hermes_home / ".env"),
|
str(hermes_home / ".env"),
|
||||||
|
os.path.join(home, ".hermes", ".env"),
|
||||||
os.path.join(home, ".bashrc"),
|
os.path.join(home, ".bashrc"),
|
||||||
os.path.join(home, ".zshrc"),
|
os.path.join(home, ".zshrc"),
|
||||||
os.path.join(home, ".profile"),
|
os.path.join(home, ".profile"),
|
||||||
|
|
|
||||||
|
|
@ -9403,6 +9403,7 @@ class AIAgent:
|
||||||
_print_preview = _summarize_user_message_for_log(user_message)
|
_print_preview = _summarize_user_message_for_log(user_message)
|
||||||
self._safe_print(f"💬 Starting conversation: '{_print_preview[:60]}{'...' if len(_print_preview) > 60 else ''}'")
|
self._safe_print(f"💬 Starting conversation: '{_print_preview[:60]}{'...' if len(_print_preview) > 60 else ''}'")
|
||||||
|
|
||||||
|
|
||||||
# ── System prompt (cached per session for prefix caching) ──
|
# ── System prompt (cached per session for prefix caching) ──
|
||||||
# Built once on first call, reused for all subsequent calls.
|
# Built once on first call, reused for all subsequent calls.
|
||||||
# Only rebuilt after context compression events (which invalidate
|
# Only rebuilt after context compression events (which invalidate
|
||||||
|
|
|
||||||
|
|
@ -342,6 +342,8 @@ class TestMinimaxSwitchModelCredentialGuard:
|
||||||
agent.client = None
|
agent.client = None
|
||||||
agent._anthropic_client = MagicMock()
|
agent._anthropic_client = MagicMock()
|
||||||
agent._fallback_chain = []
|
agent._fallback_chain = []
|
||||||
|
agent._fallback_activated = False
|
||||||
|
agent._fallback_index = 0
|
||||||
|
|
||||||
with patch("agent.anthropic_adapter.build_anthropic_client") as mock_build, \
|
with patch("agent.anthropic_adapter.build_anthropic_client") as mock_build, \
|
||||||
patch("agent.anthropic_adapter.resolve_anthropic_token", return_value="sk-ant-leaked") as mock_resolve, \
|
patch("agent.anthropic_adapter.resolve_anthropic_token", return_value="sk-ant-leaked") as mock_resolve, \
|
||||||
|
|
|
||||||
|
|
@ -756,7 +756,7 @@ class TestAgentCacheSpilloverLive:
|
||||||
runner = self._runner()
|
runner = self._runner()
|
||||||
|
|
||||||
N_THREADS = 8
|
N_THREADS = 8
|
||||||
PER_THREAD = 20 # 8 * 20 = 160 inserts into a 16-slot cache
|
PER_THREAD = 3 # 8 * 3 = 24 inserts into a 16-slot cache
|
||||||
|
|
||||||
def worker(tid: int):
|
def worker(tid: int):
|
||||||
for j in range(PER_THREAD):
|
for j in range(PER_THREAD):
|
||||||
|
|
@ -949,8 +949,8 @@ class TestAgentCacheIdleResume:
|
||||||
(full teardown — session is done), cache-eviction path uses
|
(full teardown — session is done), cache-eviction path uses
|
||||||
release_clients() (soft — session may resume).
|
release_clients() (soft — session may resume).
|
||||||
"""
|
"""
|
||||||
from run_agent import AIAgent
|
|
||||||
import run_agent as _ra
|
import run_agent as _ra
|
||||||
|
from run_agent import AIAgent
|
||||||
|
|
||||||
# Agent A: evicted from cache (soft) — terminal survives.
|
# Agent A: evicted from cache (soft) — terminal survives.
|
||||||
# Agent B: session expired (hard) — terminal torn down.
|
# Agent B: session expired (hard) — terminal torn down.
|
||||||
|
|
|
||||||
|
|
@ -4797,3 +4797,58 @@ class TestMemoryProviderTurnStart:
|
||||||
import inspect
|
import inspect
|
||||||
src = inspect.getsource(AIAgent.run_conversation)
|
src = inspect.getsource(AIAgent.run_conversation)
|
||||||
assert "on_turn_start(self._user_turn_count" in src
|
assert "on_turn_start(self._user_turn_count" in src
|
||||||
|
|
||||||
|
|
||||||
|
class TestMultimodalMessagePreview:
|
||||||
|
"""run_conversation must not crash when user_message is a list (images)."""
|
||||||
|
|
||||||
|
def test_multimodal_list_message_does_not_crash(self, agent):
|
||||||
|
"""When user_message is a multimodal list, the log preview should
|
||||||
|
extract text parts instead of calling .replace() on a list."""
|
||||||
|
multimodal_msg = [
|
||||||
|
{"type": "text", "text": "Describe this image"},
|
||||||
|
{"type": "image_url", "image_url": {"url": "data:image/png;base64,abc"}},
|
||||||
|
]
|
||||||
|
|
||||||
|
fake_response = MagicMock()
|
||||||
|
fake_response.choices = [
|
||||||
|
MagicMock(
|
||||||
|
message=MagicMock(
|
||||||
|
content="I see an image.",
|
||||||
|
tool_calls=None,
|
||||||
|
role="assistant",
|
||||||
|
),
|
||||||
|
finish_reason="stop",
|
||||||
|
)
|
||||||
|
]
|
||||||
|
fake_response.usage = MagicMock(prompt_tokens=10, completion_tokens=5, total_tokens=15)
|
||||||
|
agent._interruptible_api_call = MagicMock(return_value=fake_response)
|
||||||
|
agent._persist_session = lambda *a, **kw: None
|
||||||
|
agent._save_trajectory = lambda *a, **kw: None
|
||||||
|
agent._save_session_log = lambda *a, **kw: None
|
||||||
|
|
||||||
|
result = agent.run_conversation(multimodal_msg)
|
||||||
|
assert result["completed"] is True
|
||||||
|
assert "final_response" in result
|
||||||
|
|
||||||
|
def test_multimodal_no_text_parts(self, agent):
|
||||||
|
"""A multimodal message with only images should produce a safe preview."""
|
||||||
|
multimodal_msg = [
|
||||||
|
{"type": "image_url", "image_url": {"url": "data:image/png;base64,abc"}},
|
||||||
|
]
|
||||||
|
|
||||||
|
fake_response = MagicMock()
|
||||||
|
fake_response.choices = [
|
||||||
|
MagicMock(
|
||||||
|
message=MagicMock(content="Image.", tool_calls=None, role="assistant"),
|
||||||
|
finish_reason="stop",
|
||||||
|
)
|
||||||
|
]
|
||||||
|
fake_response.usage = MagicMock(prompt_tokens=10, completion_tokens=5, total_tokens=15)
|
||||||
|
agent._interruptible_api_call = MagicMock(return_value=fake_response)
|
||||||
|
agent._persist_session = lambda *a, **kw: None
|
||||||
|
agent._save_trajectory = lambda *a, **kw: None
|
||||||
|
agent._save_session_log = lambda *a, **kw: None
|
||||||
|
|
||||||
|
result = agent.run_conversation(multimodal_msg)
|
||||||
|
assert result["completed"] is True
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue