Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions crates/genie-core/src/tools/quick.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2613,6 +2613,14 @@ fn home_status_target(text: &str) -> Option<String> {
&target,
&["thermostat", "thermostats", "temperature", "climate"],
) {
// "temperature outside" / "outdoor temperature" is a weather question,
// not an indoor thermostat reading — there is no "temperature outside"
// device, so emitting home_status{entity:"temperature outside"} is a
// garbled misroute. Abstain so the LLM grounds it as weather. The attic
// arm below already shows this branch is scoped to indoor climate.
if contains_any(&target, &["outside", "outdoor"]) {
return None;
}
if target.contains("attic") {
return Some("attic temperature".into());
}
Expand Down Expand Up @@ -5986,6 +5994,35 @@ mod tests {
assert_eq!(contracted.arguments["entity"], spelled.arguments["entity"]);
}

#[test]
fn outdoor_temperature_query_abstains_instead_of_thermostat_status() {
// "what's the temperature outside" is a weather question, not an indoor
// thermostat reading — there is no "temperature outside" device, so the
// router emitted a garbled home_status{entity:"temperature outside"}. An
// outdoor-qualified temperature/climate query must abstain so the LLM
// grounds it (as weather).
for utterance in [
"what's the temperature outside",
"what is the temperature outdoors",
"what's the outdoor temperature",
"how's the climate outside",
] {
assert!(
route(utterance).is_none(),
"{utterance:?} must abstain, not report a thermostat status"
);
}

// Indoor thermostat/climate queries still resolve.
let call = route("what's the temperature").unwrap();
assert_eq!(call.name, "home_status");
assert_eq!(call.arguments["entity"], "thermostat");

let call = route("is the climate control on").unwrap();
assert_eq!(call.name, "home_status");
assert_eq!(call.arguments["entity"], "climate control");
}

#[test]
fn status_entity_drops_both_state_word_and_time_qualifier() {
// A status query can trail a state word AND a time qualifier. The entity
Expand Down
Loading