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

@@ -90,6 +90,74 @@ fn determine_controller_context_management() {
}
}
#[test]
fn determine_controller_sender_context() {
use super::ConfigTextGenerationSettingRelatedControllerType;
use super::ControllerType;
use crate::entity::roomconfig::TextGenerationSenderContextMode;
struct TestCase {
name: &'static str,
input: &'static str,
expected: Result<ConfigTextGenerationSettingRelatedControllerType, ControllerType>,
}
let test_cases = vec![
TestCase {
name: "sender-context-mode getter ok",
input: "sender-context-mode",
expected: Ok(ConfigTextGenerationSettingRelatedControllerType::GetSenderContextMode),
},
TestCase {
name: "sender-context-mode getter extra args",
input: "sender-context-mode some values here",
expected: Err(ControllerType::Error(
crate::strings::cfg::configuration_getter_used_with_extra_text(
"sender-context-mode",
"some values here",
),
)),
},
TestCase {
name: "sender-context-mode setter matrix_user_id",
input: "set-sender-context-mode matrix_user_id",
expected: Ok(
ConfigTextGenerationSettingRelatedControllerType::SetSenderContextMode(Some(
TextGenerationSenderContextMode::MatrixUserId,
)),
),
},
TestCase {
name: "sender-context-mode setter uppercase",
input: "set-sender-context-mode MATRIX_USER_ID_AND_TIMESTAMP",
expected: Ok(
ConfigTextGenerationSettingRelatedControllerType::SetSenderContextMode(Some(
TextGenerationSenderContextMode::MatrixUserIdAndTimestamp,
)),
),
},
TestCase {
name: "sender-context-mode setter invalid",
input: "set-sender-context-mode non-Enum-Value",
expected: Err(ControllerType::Error(
crate::strings::cfg::configuration_value_unrecognized("non-Enum-Value"),
)),
},
TestCase {
name: "sender-context-mode unsetter",
input: "set-sender-context-mode",
expected: Ok(
ConfigTextGenerationSettingRelatedControllerType::SetSenderContextMode(None),
),
},
];
for test_case in test_cases {
let result = super::determine(test_case.input);
assert_eq!(result, test_case.expected, "Test case: {}", test_case.name);
}
}
#[test]
fn determine_controller_prefix_requirement_type() {
use super::ConfigTextGenerationSettingRelatedControllerType;