Skip to content
Merged
Show file tree
Hide file tree
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
4 changes: 3 additions & 1 deletion lampOS/crates/voice/src/second_provider/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,9 @@ pub fn serve(
.unwrap_or_default();
let room = room_bus.as_ref().map(crate::bus_out::room_now);
let skills = if skills_told.use_skill {
crate::use_skill::section(&crate::tools::skill_tools(&skills_told))
crate::use_skill::section(crate::use_skill::offered(&crate::tools::skill_tools(
&skills_told,
)))
} else {
String::new()
};
Expand Down
55 changes: 48 additions & 7 deletions lampOS/crates/voice/src/session_tools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,8 @@ pub fn not_ready(tools: &[Tool], settings: &Settings) -> Option<String> {
pub fn told_after(tools: &[Tool], settings: &Settings) -> Option<String> {
let mut told = not_ready(tools, settings).unwrap_or_default();
if tools.iter().any(|tool| tool.name == crate::use_skill::NAME) {
told.push_str(&crate::use_skill::section(&crate::tools::skill_tools(
settings,
told.push_str(&crate::use_skill::section(crate::use_skill::offered(
&crate::tools::skill_tools(settings),
)));
}
(!told.is_empty()).then_some(told)
Expand Down Expand Up @@ -457,6 +457,7 @@ pub fn setup_line(tools: &[Tool], settings: &Settings) -> String {
};
if !directory.join(CATALOG_FILE).exists() {
line.push_str("; the executive's catalog is not there");
line.push_str(&skills_note(tools, settings));
return line;
}
let executive = read_names(&directory.join(CATALOG_FILE));
Expand All @@ -481,16 +482,56 @@ pub fn setup_line(tools: &[Tool], settings: &Settings) -> String {
if light == 0 {
line.push_str("; no light functions: the ring is not up");
}
if let Some(reach) = tools
line.push_str(&skills_note(tools, settings));
line
}

/// The setup line's word on the skills (#1668, the reviewer on #1696): how
/// many `use_skill` reaches, the skills a session is not given (past the 32
/// without it, or past its own bound with it), and those its lines name
/// alone or leave out.
fn skills_note(tools: &[Tool], settings: &Settings) -> String {
let mut note = String::new();
let given = names(tools);
let skills = crate::tools::skill_tools(settings);
let reach = tools
.iter()
.find(|tool| tool.name == crate::use_skill::NAME)
{
line.push_str(&format!(
.find(|tool| tool.name == crate::use_skill::NAME);
if let Some(reach) = reach {
note.push_str(&format!(
"; {} skills through use_skill",
crate::use_skill::reached(reach).len()
));
}
line
let not_given: Vec<&str> = skills
.iter()
.map(|skill| skill.name.as_str())
.filter(|name| {
!given.contains(*name) && !given.contains(&format!("{}:{name}", crate::use_skill::NAME))
})
.collect();
if !not_given.is_empty() {
note.push_str(&format!(
"; skills not given: {}{}",
not_given.join(", "),
if reach.is_some() {
" (past use_skill's bound)"
} else {
" (past 32: LAMP_USE_SKILL=on reaches them)"
}
));
}
if reach.is_some() {
let (named, left) = crate::use_skill::cut(crate::use_skill::offered(&skills));
if !named.is_empty() || !left.is_empty() {
note.push_str(&format!(
"; use_skill's lines: {} named alone, {} left out",
named.len(),
left.len()
));
}
}
note
}

/// The names in a catalog file, none if it cannot be read.
Expand Down
65 changes: 55 additions & 10 deletions lampOS/crates/voice/src/tools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,9 @@ pub fn arguments_from_environment() -> Vec<String> {
flags.push(crate::onboarding::FLAG.to_owned());
flags.push(file.display().to_string());
}
if std::env::var(crate::use_skill::ENVIRONMENT).ok().as_deref() == Some("on") {
flags.push(crate::use_skill::FLAG.to_owned());
}
flags.extend(crate::use_skill::arguments(
std::env::var(crate::use_skill::ENVIRONMENT).ok().as_deref(),
));
flags
}

Expand Down Expand Up @@ -745,9 +745,13 @@ pub fn run_call(
Route::Mark(session) => ("the marks", crate::marked::run(session, call, heard)),
Route::Onboarding(file) => ("the onboarding", crate::onboarding::run(file, call)),
Route::UseSkill(settings) => {
match crate::use_skill::translate(call, &skill_tools(settings)) {
let skills = skill_tools(settings);
match crate::use_skill::translate(call, crate::use_skill::offered(&skills)) {
// Only ever the skills' socket (the reviewer on #1696).
Ok(skill) => match crate::tools::route(settings, &skill.name) {
found @ Route::Executive(_) => run_call(&found, &skill, request, heard),
found @ Route::Executive(_) if matches!(&found, Route::Executive(dir) if dir.ends_with(SKILLS_DIR)) => {
run_call(&found, &skill, request, heard)
}
_ => ("nobody", (unavailable(), false)),
},
Err(told) => ("the skills", (told, false)),
Expand Down Expand Up @@ -1954,7 +1958,7 @@ mod tests {
/// with Desk tutor installed (#1668).
fn many_skills(name: &str) -> (PathBuf, Settings) {
let dir = directory(name);
let executive: Vec<Value> = (0..17)
let executive: Vec<Value> = (0..25)
.map(|n| json!({"name": format!("light_{n}"), "description": "A light."}))
.collect();
std::fs::write(
Expand All @@ -1977,7 +1981,8 @@ mod tests {
.to_string(),
)
.unwrap();
// The voice's own thirteen: twelve stand-ins and not_for_me.
// Thirty before the skills, as on a product lamp: the voice's own
// five here, and the executive's twenty-five standing in for the rest.
let settings = Settings {
decline: true,
exec: Some(dir.clone()),
Expand All @@ -1995,9 +2000,20 @@ mod tests {
fn with_use_skill_every_skill_is_reached_through_one_function() {
let (dir, mut settings) = many_skills("use-skill");
let core = core_tools(&settings).len();
assert_eq!(core, 30);
// Without it, Desk tutor's verdict is cut, silently but for the
// setup line (the reviewer on #1696).
let off = declared(&settings);
assert_eq!(off.len(), core + 3);
assert!(off.iter().any(|tool| tool.name == "dance"));
assert_eq!(off.len(), 32);
assert!(off.iter().any(|tool| tool.name == "check_my_work"));
assert!(!off.iter().any(|tool| tool.name == "show_verdict"));
let said = crate::session_tools::setup_line(&off, &settings);
assert!(
said.ends_with(
"; skills not given: show_verdict (past 32: LAMP_USE_SKILL=on reaches them)"
),
"{said}"
);
settings.use_skill = true;
let on = declared(&settings);
assert_eq!(on.len(), core + 1);
Expand All @@ -2011,6 +2027,8 @@ mod tests {
.collect::<Vec<_>>(),
["dance", "check_my_work", "show_verdict"]
);
let said = crate::session_tools::setup_line(&on, &settings);
assert!(said.ends_with("; 3 skills through use_skill"), "{said}");
let (kept, left_out) = admitted(on);
assert!(left_out.is_empty(), "{left_out:?}");
assert_eq!(kept.len(), core + 1);
Expand All @@ -2019,15 +2037,42 @@ mod tests {
Route::UseSkill(Box::new(settings.clone()))
);
// Without the switch, use_skill is nobody's, and a catalog's
// function of that name is never told.
// function of that name is never told: the name is the voice's,
// switch or not (the reviewer on #1696).
settings.use_skill = false;
assert_eq!(
route(&settings, crate::use_skill::NAME),
Route::Executive(dir.clone())
);
let named: Vec<Value> = (0..25)
.map(|n| json!({"name": format!("light_{n}"), "description": "A light."}))
.chain([json!({"name": crate::use_skill::NAME, "description": "Not this."})])
.collect();
std::fs::write(dir.join(CATALOG_FILE), json!({"tools": named}).to_string()).unwrap();
assert!(
!declared(&settings)
.iter()
.any(|tool| tool.name == crate::use_skill::NAME)
);
std::fs::remove_dir_all(dir).unwrap();
}

/// The reviewer on #1696: the worker's flag sets the switch, after the
/// rest of the tools' flags, and leaves what follows.
#[test]
fn the_use_skill_flag_sets_the_switch() {
let plain = ["rest".to_owned()];
let (settings, rest) = take(&plain).unwrap();
assert!(!settings.use_skill);
assert_eq!(rest, ["rest"]);
let mut flags = arguments(Some("on"), None, None, None, None);
flags.extend(crate::use_skill::arguments(Some("on")));
flags.push("rest".into());
let (settings, rest) = take(&flags).unwrap();
assert!(settings.decline && settings.use_skill);
assert_eq!(rest, ["rest"]);
}

/// A call of `use_skill` reaches the skill's own socket as the skill's
/// own call, its choice and yes-or-no under the skill's own names, and
/// the skill's answer goes to the model: on the cascade's path
Expand Down
Loading
Loading