fix(runtime): skip hidden human messages in journal (#3698)

Co-authored-by: rayhpeng <rayhpeng@gmail.com>
This commit is contained in:
Huixin615
2026-06-23 06:58:31 +08:00
committed by GitHub
co-authored by rayhpeng
parent 46fd28136d
commit 90fdda77e8
2 changed files with 71 additions and 1 deletions
@@ -225,7 +225,7 @@ class RunJournal(BaseCallbackHandler):
if not self._first_human_msg and messages:
for batch in reversed(messages):
for m in reversed(batch):
if isinstance(m, HumanMessage) and m.name != "summary":
if isinstance(m, HumanMessage) and m.name != "summary" and m.additional_kwargs.get("hide_from_ui") is not True:
caller = self._identify_caller(tags)
self.set_first_human_message(m.text)
self._put(
+70
View File
@@ -854,6 +854,76 @@ class TestChatModelStartHumanMessage:
assert j._first_human_msg == "Real question"
@pytest.mark.anyio
async def test_skips_hidden_human_messages(self, journal_setup):
"""HumanMessages hidden from the UI are internal context, not user input."""
from langchain_core.messages import HumanMessage
j, store = journal_setup
messages_batch = [
[
HumanMessage(content="What is the weather today?"),
HumanMessage(
content="Your todo list from earlier...",
name="todo_reminder",
additional_kwargs={"hide_from_ui": True},
),
],
]
j.on_chat_model_start({}, messages_batch, run_id=uuid4(), tags=["lead_agent"])
await j.flush()
assert j._first_human_msg == "What is the weather today?"
assert j.get_completion_data()["message_count"] == 1
events = await store.list_events("t1", "r1")
human_events = [e for e in events if e["event_type"] == "llm.human.input"]
assert len(human_events) == 1
assert human_events[0]["content"]["content"] == "What is the weather today?"
@pytest.mark.anyio
async def test_only_hidden_human_messages_are_not_captured(self, journal_setup):
"""A prompt containing only internal HumanMessages has no user input."""
from langchain_core.messages import HumanMessage
j, store = journal_setup
hidden_message = HumanMessage(
content="Internal context",
additional_kwargs={"hide_from_ui": True},
)
j.on_chat_model_start({}, [[hidden_message]], run_id=uuid4(), tags=["lead_agent"])
await j.flush()
assert j._first_human_msg is None
assert j.get_completion_data()["message_count"] == 0
events = await store.list_events("t1", "r1")
assert not any(e["event_type"] == "llm.human.input" for e in events)
@pytest.mark.anyio
async def test_visible_human_message_after_hidden_only_prompt_is_captured(self, journal_setup):
"""Skipping an internal-only prompt does not block later user input."""
from langchain_core.messages import HumanMessage
j, store = journal_setup
hidden_message = HumanMessage(
content="Internal context",
additional_kwargs={"hide_from_ui": True},
)
j.on_chat_model_start({}, [[hidden_message]], run_id=uuid4(), tags=["lead_agent"])
j.on_chat_model_start(
{},
[[HumanMessage(content="Real question")]],
run_id=uuid4(),
tags=["lead_agent"],
)
await j.flush()
assert j._first_human_msg == "Real question"
assert j.get_completion_data()["message_count"] == 1
events = await store.list_events("t1", "r1")
human_events = [e for e in events if e["event_type"] == "llm.human.input"]
assert len(human_events) == 1
assert human_events[0]["content"]["content"] == "Real question"
@pytest.mark.anyio
async def test_only_first_human_message_captured(self, journal_setup):
"""Subsequent on_chat_model_start calls do not overwrite the first message."""