Hermes-agent
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
"""Tests for reply-to pointer injection in _prepare_inbound_message_text.
|
||||
|
||||
The `[Replying to: "..."]` prefix is a *disambiguation pointer*, not
|
||||
deduplication. It must always be injected when the user explicitly replies
|
||||
to a prior message — even when the quoted text already exists somewhere
|
||||
in the conversation history. History can contain the same or similar text
|
||||
multiple times, and without an explicit pointer the agent has to guess
|
||||
which prior message the user is referencing.
|
||||
"""
|
||||
import pytest
|
||||
|
||||
from gateway.config import GatewayConfig, Platform, PlatformConfig
|
||||
from gateway.platforms.base import MessageEvent
|
||||
from gateway.run import GatewayRunner
|
||||
from gateway.session import SessionSource
|
||||
|
||||
|
||||
def _make_runner() -> GatewayRunner:
|
||||
runner = object.__new__(GatewayRunner)
|
||||
runner.config = GatewayConfig(
|
||||
platforms={Platform.TELEGRAM: PlatformConfig(enabled=True, token="fake")},
|
||||
)
|
||||
runner.adapters = {}
|
||||
runner._model = "openai/gpt-4.1-mini"
|
||||
runner._base_url = None
|
||||
return runner
|
||||
|
||||
|
||||
def _source() -> SessionSource:
|
||||
return SessionSource(
|
||||
platform=Platform.TELEGRAM,
|
||||
chat_id="123",
|
||||
chat_name="DM",
|
||||
chat_type="private",
|
||||
user_name="Alice",
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reply_prefix_injected_when_text_absent_from_history():
|
||||
runner = _make_runner()
|
||||
source = _source()
|
||||
event = MessageEvent(
|
||||
text="What's the best time to go?",
|
||||
source=source,
|
||||
reply_to_message_id="42",
|
||||
reply_to_text="Japan is great for culture, food, and efficiency.",
|
||||
)
|
||||
|
||||
result = await runner._prepare_inbound_message_text(
|
||||
event=event,
|
||||
source=source,
|
||||
history=[{"role": "user", "content": "unrelated"}],
|
||||
)
|
||||
|
||||
assert result is not None
|
||||
assert result.startswith(
|
||||
'[Replying to: "Japan is great for culture, food, and efficiency."]'
|
||||
)
|
||||
assert result.endswith("What's the best time to go?")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reply_prefix_still_injected_when_text_in_history():
|
||||
"""Regression test: the pointer must survive even when the quoted text
|
||||
already appears in history. Previously a `found_in_history` guard
|
||||
silently dropped the prefix, leaving the agent to guess which prior
|
||||
message the user was referencing."""
|
||||
runner = _make_runner()
|
||||
source = _source()
|
||||
quoted = "Japan is great for culture, food, and efficiency."
|
||||
event = MessageEvent(
|
||||
text="What's the best time to go?",
|
||||
source=source,
|
||||
reply_to_message_id="42",
|
||||
reply_to_text=quoted,
|
||||
)
|
||||
|
||||
history = [
|
||||
{"role": "user", "content": "I'm thinking of going to Japan or Italy."},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": (
|
||||
f"{quoted} Italy is better if you prefer a relaxed pace."
|
||||
),
|
||||
},
|
||||
{"role": "user", "content": "How long should I stay?"},
|
||||
{"role": "assistant", "content": "For Japan, 10-14 days is ideal."},
|
||||
]
|
||||
|
||||
result = await runner._prepare_inbound_message_text(
|
||||
event=event,
|
||||
source=source,
|
||||
history=history,
|
||||
)
|
||||
|
||||
assert result is not None
|
||||
assert result.startswith(f'[Replying to: "{quoted}"]')
|
||||
assert result.endswith("What's the best time to go?")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_no_prefix_without_reply_context():
|
||||
runner = _make_runner()
|
||||
source = _source()
|
||||
event = MessageEvent(text="hello", source=source)
|
||||
|
||||
result = await runner._prepare_inbound_message_text(
|
||||
event=event,
|
||||
source=source,
|
||||
history=[],
|
||||
)
|
||||
|
||||
assert result == "hello"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_no_prefix_when_reply_to_text_is_empty():
|
||||
"""reply_to_message_id alone without text (e.g. a reply to a media-only
|
||||
message) should not produce an empty `[Replying to: ""]` prefix."""
|
||||
runner = _make_runner()
|
||||
source = _source()
|
||||
event = MessageEvent(
|
||||
text="hi",
|
||||
source=source,
|
||||
reply_to_message_id="42",
|
||||
reply_to_text=None,
|
||||
)
|
||||
|
||||
result = await runner._prepare_inbound_message_text(
|
||||
event=event,
|
||||
source=source,
|
||||
history=[],
|
||||
)
|
||||
|
||||
assert result == "hi"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reply_snippet_truncated_to_500_chars():
|
||||
runner = _make_runner()
|
||||
source = _source()
|
||||
long_text = "x" * 800
|
||||
event = MessageEvent(
|
||||
text="follow-up",
|
||||
source=source,
|
||||
reply_to_message_id="42",
|
||||
reply_to_text=long_text,
|
||||
)
|
||||
|
||||
result = await runner._prepare_inbound_message_text(
|
||||
event=event,
|
||||
source=source,
|
||||
history=[],
|
||||
)
|
||||
|
||||
assert result is not None
|
||||
assert result.startswith('[Replying to: "' + "x" * 500 + '"]')
|
||||
assert "x" * 501 not in result
|
||||
Reference in New Issue
Block a user