hive-runtime, hive-agent: clear the ACP picker's pending model on a refusal
When an ACP agent answered session/set_config_option with an error, choose() only logged it, so the session's current value never moved and offered_pickers() kept treating the refused model as pending: the picker showed the refused model as current and hid the effort picker. hive-runtime now keeps the refused value per category on Choices, exposed as Choice::refused, set on the RPC error and cleared when the agent next accepts a value for that category. offered_pickers() treats a refused value as not settable, so the picker shows the session's actual model and its effort levels. The fake ACP agent gains REFUSE, which errors the first session/set_config_option to that value. Refs #4832 (ACP half only)
This commit is contained in:
parent
d74f567c59
commit
25a09dd3ec
2 changed files with 110 additions and 19 deletions
|
|
@ -538,9 +538,9 @@ pub(super) fn pickers(bus: &crate::events::Bus) -> Pickers {
|
|||
|
||||
/// Pickers for what an ACP session offers. The runtime sets a requested value
|
||||
/// on the next turn, and only if the session offers it, so each picker shows
|
||||
/// the requested value when offered and the session's own otherwise. The
|
||||
/// effort levels on offer come with the model, so while a new model waits
|
||||
/// for the next turn the effort picker is empty. `configured` narrows the
|
||||
/// the requested value when offered and not refused by the agent, and the
|
||||
/// session's own otherwise. The effort levels on offer come with the model,
|
||||
/// so while a new model waits for the next turn the effort picker is empty. `configured` narrows the
|
||||
/// model list per [`filter_offered_models`]; it never narrows which value is
|
||||
/// shown as current, only which values the picker offers alongside it.
|
||||
fn offered_pickers(
|
||||
|
|
@ -549,14 +549,17 @@ fn offered_pickers(
|
|||
offered: hive_runtime::SessionChoices,
|
||||
configured: Option<&[String]>,
|
||||
) -> Pickers {
|
||||
let settable = |wanted: &String, c: &hive_runtime::Choice| {
|
||||
c.values.contains(wanted) && c.refused.as_ref() != Some(wanted)
|
||||
};
|
||||
let shown = |wanted: String, choice: Option<&hive_runtime::Choice>| match choice {
|
||||
Some(c) if !c.values.contains(&wanted) => c.current.clone(),
|
||||
Some(c) if !settable(&wanted, c) => c.current.clone(),
|
||||
_ => wanted,
|
||||
};
|
||||
let model_pending = offered
|
||||
.model
|
||||
.as_ref()
|
||||
.is_some_and(|c| c.current != model && c.values.contains(&model));
|
||||
.is_some_and(|c| c.current != model && settable(&model, c));
|
||||
let effort_choice = offered.effort.filter(|_| !model_pending);
|
||||
Pickers {
|
||||
model: shown(model, offered.model.as_ref()),
|
||||
|
|
@ -581,6 +584,7 @@ mod tests {
|
|||
Choice {
|
||||
current: current.to_owned(),
|
||||
values: values.iter().map(|v| (*v).to_owned()).collect(),
|
||||
refused: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -628,6 +632,26 @@ mod tests {
|
|||
assert!(pending.available_efforts.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn acp_pickers_show_the_session_s_model_once_the_agent_refuses_the_wanted_one() {
|
||||
let refused = SessionChoices {
|
||||
model: Some(Choice {
|
||||
refused: Some("m/plain".into()),
|
||||
..choice("m/think", &["m/think", "m/plain"])
|
||||
}),
|
||||
effort: Some(choice("low", &["low", "high"])),
|
||||
};
|
||||
assert_eq!(
|
||||
offered_pickers("m/plain".into(), "high".into(), refused, None),
|
||||
Pickers {
|
||||
model: "m/think".into(),
|
||||
available_models: strings(&["m/think", "m/plain"]),
|
||||
effort: "high".into(),
|
||||
available_efforts: strings(&["low", "high"]),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn acp_pickers_are_empty_before_a_session_offers_anything() {
|
||||
let none = offered_pickers(
|
||||
|
|
|
|||
Loading…
Reference in a new issue