feat(#1597): persist + apply per-agent claude effort override
This commit is contained in:
parent
d7bba48176
commit
ec343046ea
3 changed files with 166 additions and 1 deletions
|
|
@ -119,6 +119,7 @@ pub async fn serve(
|
|||
.route("/api/cancel", post(post_cancel_turn))
|
||||
.route("/api/compact", post(post_compact))
|
||||
.route("/api/model", post(post_set_model))
|
||||
.route("/api/effort", post(post_set_effort))
|
||||
.route("/api/new-session", post(post_new_session))
|
||||
.route("/api/logout", post(post_logout))
|
||||
.route("/api/loose-ends", get(api_loose_ends))
|
||||
|
|
@ -444,6 +445,15 @@ struct StateSnapshot {
|
|||
/// per entry in this list, so operators can add new models or drop
|
||||
/// ones they don't want without touching the frontend code.
|
||||
available_models: Vec<String>,
|
||||
/// Currently-active claude effort level. Reflected on the page so the
|
||||
/// operator's effort picker shows the live selection. Mutable at
|
||||
/// runtime via `POST /api/effort`; applies on the next session.
|
||||
effort: String,
|
||||
/// Selectable effort levels for the picker, ascending. Fixed set
|
||||
/// (`medium`, `high`, `xhigh`) — sourced from
|
||||
/// [`crate::events::EFFORT_LEVELS`], not operator-configurable like
|
||||
/// `available_models`. The frontend renders one button per entry.
|
||||
available_efforts: Vec<String>,
|
||||
}
|
||||
|
||||
/// One navigation link in the agent page header row. The same JSON
|
||||
|
|
@ -549,6 +559,7 @@ async fn api_state(State(state): State<AppState>) -> axum::Json<StateSnapshot> {
|
|||
.unwrap_or_else(|| crate::events::context_window_tokens(&model));
|
||||
let ctx_usage = state.bus.last_ctx_usage();
|
||||
let cost_usage = state.bus.last_cost_usage();
|
||||
let effort = state.bus.effort();
|
||||
axum::Json(StateSnapshot {
|
||||
seq,
|
||||
label: state.label.clone(),
|
||||
|
|
@ -570,6 +581,11 @@ async fn api_state(State(state): State<AppState>) -> axum::Json<StateSnapshot> {
|
|||
hive_name: crate::identity::hive_name(),
|
||||
swarm_name: crate::identity::swarm_name(),
|
||||
available_models: available_models(),
|
||||
effort,
|
||||
available_efforts: crate::events::EFFORT_LEVELS
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -941,6 +957,33 @@ async fn post_set_model(State(state): State<AppState>, Form(form): Form<ModelFor
|
|||
(axum::http::StatusCode::OK, "ok").into_response()
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct EffortForm {
|
||||
effort: String,
|
||||
}
|
||||
|
||||
/// Switch the claude effort level for future sessions. Operator-only
|
||||
/// (the dashboard picker POSTs here through the gateway). Validated
|
||||
/// server-side against [`crate::events::EFFORT_LEVELS`] — an out-of-set
|
||||
/// value is rejected rather than handed to `claude --effort`, since an
|
||||
/// unknown level would fail every subsequent launch. Applies on the next
|
||||
/// session start (no mid-session swap).
|
||||
async fn post_set_effort(State(state): State<AppState>, Form(form): Form<EffortForm>) -> Response {
|
||||
let level = form.effort.trim();
|
||||
if !crate::events::is_valid_effort(level) {
|
||||
return error_response(&format!(
|
||||
"effort: level must be one of {}",
|
||||
crate::events::EFFORT_LEVELS.join(", ")
|
||||
));
|
||||
}
|
||||
state.bus.set_effort(level);
|
||||
state.bus.emit(crate::events::LiveEvent::Note {
|
||||
text: format!("operator: /effort — claude effort set to '{level}' for future sessions"),
|
||||
});
|
||||
tracing::info!(%level, "operator set effort");
|
||||
(axum::http::StatusCode::OK, "ok").into_response()
|
||||
}
|
||||
|
||||
async fn post_compact(State(state): State<AppState>) -> Response {
|
||||
// Clone the Arc before locking so the guard's lifetime is tied to the
|
||||
// clone (which we can move into the spawn) rather than to `state`.
|
||||
|
|
|
|||
Loading…
Reference in a new issue