Summary
classify_trigger = "user_turn" (added in #487) only takes effect when the caller supplies a session id. That requirement is stated in the source but not in the TOML reference that operators read, and the setting that would otherwise work around it is rejected.
Detail
TurnPin in crates/libsy/src/algorithms/util/turn_pin.rs stores the chosen target in the composition's State, and its doc comment is explicit:
Without a session id there is no retained state, and every turn is classified.
That is correct and matches crates/libsy/src/algorithms/fall_through.rs, which keeps one state value per session id and uses "unretained per-run state" otherwise. So with no session id, user_turn behaves the same as every_request.
Two things follow.
1. The TOML reference does not say so. docs/reference/toml_schema.md describes the setting as:
user_turn judges each new user message and holds that target across the tool calls between.
An operator reading only that would reasonably expect the target to be held, and would see one classifier call per request instead, with nothing in the logs indicating why.
2. The fallback that exists for this case is rejected here. message_hash_fallback derives an identity from the first user message for exactly this situation, but llm_class.rs:346 and llm_class.rs:418 reject it unless classify_trigger = "new_session":
if self.message_hash_fallback && self.classify_trigger != ClassifyTrigger::NewSession {
return Err(LibsyError::AlgorithmError {
message: "message_hash_fallback requires classify_trigger = new_session".to_string(),
});
}
This affects callers that do not send a session id, which includes benchmark harnesses and direct API clients.
Suggested changes
- Document the requirement in
docs/reference/toml_schema.md: user_turn requires a session id, and without one it behaves as every_request.
- Allow
message_hash_fallback with classify_trigger = "user_turn", keying the retained target on the hash of the first user message as it already does for new_session.
The first is a documentation fix and stands on its own. The second is a small change to the two validation sites above plus the identity lookup, and I am happy to open a PR for it if that is wanted. If the restriction in (2) is deliberate, then documenting the reason would be enough.
Summary
classify_trigger = "user_turn"(added in #487) only takes effect when the caller supplies a session id. That requirement is stated in the source but not in the TOML reference that operators read, and the setting that would otherwise work around it is rejected.Detail
TurnPinincrates/libsy/src/algorithms/util/turn_pin.rsstores the chosen target in the composition'sState, and its doc comment is explicit:That is correct and matches
crates/libsy/src/algorithms/fall_through.rs, which keeps one state value per session id and uses "unretained per-run state" otherwise. So with no session id,user_turnbehaves the same asevery_request.Two things follow.
1. The TOML reference does not say so.
docs/reference/toml_schema.mddescribes the setting as:An operator reading only that would reasonably expect the target to be held, and would see one classifier call per request instead, with nothing in the logs indicating why.
2. The fallback that exists for this case is rejected here.
message_hash_fallbackderives an identity from the first user message for exactly this situation, butllm_class.rs:346andllm_class.rs:418reject it unlessclassify_trigger = "new_session":This affects callers that do not send a session id, which includes benchmark harnesses and direct API clients.
Suggested changes
docs/reference/toml_schema.md:user_turnrequires a session id, and without one it behaves asevery_request.message_hash_fallbackwithclassify_trigger = "user_turn", keying the retained target on the hash of the first user message as it already does fornew_session.The first is a documentation fix and stands on its own. The second is a small change to the two validation sites above plus the identity lookup, and I am happy to open a PR for it if that is wanted. If the restriction in (2) is deliberate, then documenting the reason would be enough.