feat(gateway): optional per-message ISO-8601 timestamp prefix#26514
feat(gateway): optional per-message ISO-8601 timestamp prefix#26514Siyfion wants to merge 2 commits into
Conversation
Add an opt-in 'timestamp_messages' flag to GatewayConfig that prepends each inbound user message with an ISO-8601 timestamp (rendered in the gateway host's local timezone) before it enters the agent loop. Motivation: in long-running messaging-platform sessions the only clock signal the model has is the system prompt's 'session start' line. Once a session has been alive for several hours, the model has no way to tell whether the most recent user message arrived seconds or hours ago, which leads to gaffes like saying 'morning!' in the evening. The timestamp is sourced from the platform's own message timestamp (MessageEvent.timestamp) rather than the gateway's wall clock at processing time, so it stays accurate even if messages queue. - New config: timestamp_messages: false (default off — opt-in to avoid surprising consumers that grep transcript text) - Wired up in _prepare_inbound_message_text so both the normal inbound path and the queued follow-up path get the prefix consistently - Currently exercised on Telegram (which already populates event.timestamp from message.date); other platforms with a populated MessageEvent.timestamp will work automatically - Failures in timestamp formatting are caught and logged; never block message delivery - 5 unit tests covering: on/off, numeric offset preservation, interaction with the shared-group sender prefix, malformed timestamp robustness - User-guide docs entry under 'Per-Message Timestamps'
|
I like this PR and it works well on my local machine, but for continuous conversations, it's usually not necessary to add timestamps to every message. I think we can add a configurable threshold — timestamps are only prepended when the gap between messages |
|
Yeah, you're right... that's a better design than mine. Always-on timestamping was the simplest thing that worked for my 12+ hour Telegram sessions (model latching onto session-start time and cheerily saying "morning!" at 9pm), but during active back-and-forth it's just token waste... the model already has all the temporal context it needs from the recent message flow. Proposed shape: timestamp_messages: true
message_timestamp_threshold_seconds: 600 # default 10 minutes
Working on the update now. Thanks for the nudge 👍 |
…hold_seconds Per @Maxwelltoo review on NousResearch#26514: prefixing every inbound message wastes tokens during active back-and-forth where the model already has temporal context. Add a configurable gap threshold so the prefix only re-anchors after a meaningful pause. - New: GatewayConfig.message_timestamp_threshold_seconds (default 600 / 10min) - 0 preserves legacy always-on behaviour - Tracked per-session via in-memory dict; rolling gap (anchor advances on every message, prefixed or not) - First message after gateway restart re-anchors naturally (no prior ts) - Negative gaps (out-of-order delivery) treated as 'within window' — never re-anchor retroactively Adds 7 tests covering: first-message-prefix, in-window suppression, post-gap re-anchor, threshold=0 always-on, per-session independence, rolling anchor across suppressed messages, out-of-order graceful no-op. Docs updated under configuration.md → Per-Message Timestamps.
|
Pushed in 38665a20e — gap-gated prefix is live on the branch. Shape: timestamp_messages: true
message_timestamp_threshold_seconds: 600 # default 10min; set to 0 for legacy always-onBehaviour:
Tests: 7 new cases covering first-message-always-prefixes, in-window suppression, post-gap re-anchor, threshold=0 always-on, per-session independence, rolling-anchor across suppressed messages, and out-of-order graceful no-op. 145/145 across Docs updated under Ready for another look 👀 |
Closes #9628.
Summary
Adds an opt-in
timestamp_messagesflag toGatewayConfig. When enabled, every inbound user message is prepended with an ISO-8601 timestamp before it enters the agent loop:Motivation
Same problem #9628 describes: in long-running messaging-platform sessions, the only clock signal the model has is the "session start" line in the system prompt. Once a session has been alive for several hours, the model has no way to tell whether the most recent user message arrived seconds or hours ago — leading to gaffes like cheerily saying "morning!" twelve hours into a session, or losing track of "pepperami for dinner" being a 7pm thing and not a breakfast thing.
Real-world prompt for this PR: I was 12.5 hours into a Telegram session this evening and the agent kept lighting up with "morning!" because it had latched onto the original session-start timestamp and never updated its mental clock.
Differences from #9628's sketch
The issue proposes
[MM-dd HH:mm]in UTC, keyed undergateway.message_timestamp_prefix. This PR ships:[2026-05-15T19:25:00+01:00]) rather than compact UTC — full ISO is unambiguous, includes the year (matters for multi-week sessions), and shows the user's local time directly without requiring the model to do tz mathtimestamp_messages: trueto match existing top-level gateway flags (group_sessions_per_user,thread_sessions_per_user,unauthorized_dm_behavior) rather than the nested formMessageEvent.timestamp(i.e. the platform'sdatefield) rather thandatetime.now()at processing time — stays accurate even if messages queueDesign
timestamp_messages: false(default off — opt-in to avoid surprising consumers that grep transcript text)datetime.astimezone(), with the standardTZenv var honoured_prepare_inbound_message_textso both the normal inbound path and the queued follow-up path get the prefix consistently[2026-05-15T19:25:00+01:00] [Alice] helloevent.timestampfrommessage.date); other platforms with a populatedMessageEvent.timestampwill work automaticallyTest plan
New file:
tests/gateway/test_timestamp_messages.py— 5 cases covering:[sender]prefixDocs
User-guide entry added under
configuration.md→ "Per-Message Timestamps".Notes
falseso this is a pure additive change for existing installs🤖 Drafted with Hermes Agent