Add sender context mode for text generation (#104)

Add a per-room/global `sender_context_mode` setting that optionally
prefixes conversation messages with sender metadata before sending them
to the model provider.

This helps models distinguish between participants in multi-user rooms.
                                                                                                                                                                                                                                                             
Three modes are supported:
- `disabled` (default, no change)
- `matrix_user_id` (prefixes with `[sender=@user:server]`)
- `matrix_user_id_and_timestamp` (adds `send_at`; Example: `[sender=@user:server sent_at=<ISO 8601>]`)
                                                                                                                                                                                                                                                             
Sender context is applied to user and assistant text messages only,
skipping system prompts and non-text content.

Mixed-sender merged turns (something we intentionally do for Anthropic)
have their `sender_id` cleared to avoid misattribution.
This commit is contained in:
kschwank
2026-03-25 19:14:34 +01:00
committed by GitHub
parent 661e7263fb
commit 2d659964a7
25 changed files with 719 additions and 14 deletions

View File

@@ -5,8 +5,9 @@ use super::roomconfig::RoomConfig;
use crate::entity::roomconfig::{
SpeechToTextFlowType, SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages,
TextGenerationAutoUsage, TextGenerationPrefixRequirementType, TextToSpeechBotMessagesFlowType,
TextToSpeechUserMessagesFlowType, defaults as roomconfig_defaults,
TextGenerationAutoUsage, TextGenerationPrefixRequirementType, TextGenerationSenderContextMode,
TextToSpeechBotMessagesFlowType, TextToSpeechUserMessagesFlowType,
defaults as roomconfig_defaults,
};
#[derive(Debug)]
@@ -135,6 +136,20 @@ impl RoomConfigContext {
.unwrap_or(false)
}
pub fn text_generation_sender_context_mode(&self) -> TextGenerationSenderContextMode {
self.room_config
.settings
.text_generation
.sender_context_mode
.or({
self.global_config
.fallback_room_settings
.text_generation
.sender_context_mode
})
.unwrap_or(roomconfig_defaults::TEXT_GENERATION_SENDER_CONTEXT_MODE)
}
pub fn text_generation_prefix_requirement_type(&self) -> TextGenerationPrefixRequirementType {
self.room_config
.settings

View File

@@ -1,5 +1,7 @@
use super::{SpeechToTextFlowType, SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages};
use super::{TextGenerationAutoUsage, TextGenerationPrefixRequirementType};
use super::{
TextGenerationAutoUsage, TextGenerationPrefixRequirementType, TextGenerationSenderContextMode,
};
use super::{TextToSpeechBotMessagesFlowType, TextToSpeechUserMessagesFlowType};
pub const TEXT_GENERATION_PREFIX_REQUIREMENT_TYPE: TextGenerationPrefixRequirementType =
@@ -7,6 +9,9 @@ pub const TEXT_GENERATION_PREFIX_REQUIREMENT_TYPE: TextGenerationPrefixRequireme
pub const TEXT_GENERATION_AUTO_USAGE: TextGenerationAutoUsage = TextGenerationAutoUsage::Always;
pub const TEXT_GENERATION_SENDER_CONTEXT_MODE: TextGenerationSenderContextMode =
TextGenerationSenderContextMode::Disabled;
pub const TEXT_TO_SPEECH_BOT_MESSAGES_FLOW_TYPE: TextToSpeechBotMessagesFlowType =
TextToSpeechBotMessagesFlowType::OnDemandForVoice;

View File

@@ -16,7 +16,9 @@ pub use handler::RoomSettingsHandler;
pub use speech_to_text::{
SpeechToTextFlowType, SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages,
};
pub use text_generation::{TextGenerationAutoUsage, TextGenerationPrefixRequirementType};
pub use text_generation::{
TextGenerationAutoUsage, TextGenerationPrefixRequirementType, TextGenerationSenderContextMode,
};
pub use text_to_speech::{TextToSpeechBotMessagesFlowType, TextToSpeechUserMessagesFlowType};
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]

View File

@@ -14,6 +14,9 @@ pub struct RoomSettingsTextGeneration {
/// When enabled, the bot will automatically tokenize messages and try to shorten the message context intelligently.
pub context_management_enabled: Option<bool>,
/// Controls how each message in the conversation context is annotated with sender metadata.
pub sender_context_mode: Option<TextGenerationSenderContextMode>,
/// Allows customizing the system prompt that the agent would use
pub prompt_override: Option<String>,
@@ -111,3 +114,46 @@ impl std::fmt::Display for TextGenerationAutoUsage {
}
}
}
#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)]
pub enum TextGenerationSenderContextMode {
#[serde(rename = "disabled")]
Disabled,
#[serde(rename = "matrix_user_id")]
MatrixUserId,
#[serde(rename = "matrix_user_id_and_timestamp")]
MatrixUserIdAndTimestamp,
}
impl TextGenerationSenderContextMode {
pub fn choices() -> Vec<Self> {
vec![
Self::Disabled,
Self::MatrixUserId,
Self::MatrixUserIdAndTimestamp,
]
}
pub fn from_str(s: &str) -> Option<Self> {
match s {
"disabled" => Some(Self::Disabled),
"matrix_user_id" => Some(Self::MatrixUserId),
"matrix_user_id_and_timestamp" => Some(Self::MatrixUserIdAndTimestamp),
_ => None,
}
}
}
impl std::fmt::Display for TextGenerationSenderContextMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TextGenerationSenderContextMode::Disabled => write!(f, "disabled"),
TextGenerationSenderContextMode::MatrixUserId => write!(f, "matrix_user_id"),
TextGenerationSenderContextMode::MatrixUserIdAndTimestamp => {
write!(f, "matrix_user_id_and_timestamp")
}
}
}
}

View File

@@ -6,8 +6,8 @@ use mxlink::helpers::account_data_config::RoomConfigManager as AccountDataRoomCo
pub use entity::{RoomConfig, RoomConfigCarrierContent, RoomSettings, RoomSettingsHandler};
pub use entity::{
SpeechToTextFlowType, SpeechToTextMessageTypeForNonThreadedOnlyTranscribedMessages,
TextGenerationAutoUsage, TextGenerationPrefixRequirementType, TextToSpeechBotMessagesFlowType,
TextToSpeechUserMessagesFlowType,
TextGenerationAutoUsage, TextGenerationPrefixRequirementType, TextGenerationSenderContextMode,
TextToSpeechBotMessagesFlowType, TextToSpeechUserMessagesFlowType,
};
pub type RoomConfigurationManager =