refactor: Migrated the .skills command completion to use StateFlags and updated the help messages
This commit is contained in:
@@ -672,6 +672,8 @@ bitflags::bitflags! {
|
|||||||
const RAG = 1 << 3;
|
const RAG = 1 << 3;
|
||||||
const AGENT = 1 << 4;
|
const AGENT = 1 << 4;
|
||||||
const FUNCTION_CALLING = 1 << 5;
|
const FUNCTION_CALLING = 1 << 5;
|
||||||
|
const AUTO_CONTINUE = 1 << 6;
|
||||||
|
const SKILLS_ENABLED = 1 << 7;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -374,9 +374,29 @@ impl RequestContext {
|
|||||||
if self.app.config.function_calling_support {
|
if self.app.config.function_calling_support {
|
||||||
flags |= StateFlags::FUNCTION_CALLING;
|
flags |= StateFlags::FUNCTION_CALLING;
|
||||||
}
|
}
|
||||||
|
if self.auto_continue_config().enabled {
|
||||||
|
flags |= StateFlags::AUTO_CONTINUE;
|
||||||
|
}
|
||||||
|
if self.resolved_skills_enabled() {
|
||||||
|
flags |= StateFlags::SKILLS_ENABLED;
|
||||||
|
}
|
||||||
flags
|
flags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resolved_skills_enabled(&self) -> bool {
|
||||||
|
if let Some(agent) = &self.agent
|
||||||
|
&& let Some(value) = agent.skills_enabled()
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
let app = &self.app.config;
|
||||||
|
self.session
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|s| s.skills_enabled())
|
||||||
|
.or_else(|| self.role.as_ref().and_then(|r| r.skills_enabled()))
|
||||||
|
.unwrap_or(app.skills_enabled)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn messages_file(&self) -> PathBuf {
|
pub fn messages_file(&self) -> PathBuf {
|
||||||
match &self.agent {
|
match &self.agent {
|
||||||
None => match env::var(get_env_name("messages_file")) {
|
None => match env::var(get_env_name("messages_file")) {
|
||||||
@@ -453,6 +473,22 @@ impl RequestContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn todo_info(&self) -> Result<String> {
|
||||||
|
if !self.auto_continue_config().enabled {
|
||||||
|
bail!(
|
||||||
|
"Auto-continuation is disabled. Enable it by setting `auto_continue: true` in your config or running `.set auto_continue true`."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.todo_list.is_empty() {
|
||||||
|
return Ok("No todos in the running list.\n".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut out = self.todo_list.render_for_model();
|
||||||
|
out.push('\n');
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn tools_info(&self) -> Result<String> {
|
pub fn tools_info(&self) -> Result<String> {
|
||||||
if !self.app.config.function_calling_support {
|
if !self.app.config.function_calling_support {
|
||||||
bail!(
|
bail!(
|
||||||
@@ -2239,11 +2275,6 @@ impl RequestContext {
|
|||||||
super::map_completion_values(values)
|
super::map_completion_values(values)
|
||||||
}
|
}
|
||||||
".macro" => super::map_completion_values(paths::list_macros()),
|
".macro" => super::map_completion_values(paths::list_macros()),
|
||||||
".skill" => super::map_completion_values(vec![
|
|
||||||
"loaded".to_string(),
|
|
||||||
"load".to_string(),
|
|
||||||
"unload".to_string(),
|
|
||||||
]),
|
|
||||||
".starter" => match &self.agent {
|
".starter" => match &self.agent {
|
||||||
Some(agent) => agent
|
Some(agent) => agent
|
||||||
.conversation_starters()
|
.conversation_starters()
|
||||||
@@ -4151,6 +4182,43 @@ mod tests {
|
|||||||
assert!(ctx.state().contains(StateFlags::FUNCTION_CALLING));
|
assert!(ctx.state().contains(StateFlags::FUNCTION_CALLING));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn state_includes_skills_enabled_when_app_enables_it() {
|
||||||
|
let ctx = create_test_ctx();
|
||||||
|
|
||||||
|
assert!(ctx.state().contains(StateFlags::SKILLS_ENABLED));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn state_omits_skills_enabled_when_app_disables_it() {
|
||||||
|
let mut ctx = create_test_ctx();
|
||||||
|
|
||||||
|
ctx.update_app_config(|app| app.skills_enabled = false);
|
||||||
|
|
||||||
|
assert!(!ctx.state().contains(StateFlags::SKILLS_ENABLED));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn state_skills_enabled_respects_session_override() {
|
||||||
|
let mut ctx = create_test_ctx();
|
||||||
|
let mut session = Session::default();
|
||||||
|
session.set_skills_enabled(Some(false));
|
||||||
|
|
||||||
|
ctx.session = Some(session);
|
||||||
|
|
||||||
|
assert!(!ctx.state().contains(StateFlags::SKILLS_ENABLED));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn state_skills_enabled_respects_role_override() {
|
||||||
|
let mut ctx = create_test_ctx();
|
||||||
|
let role = Role::new("r", "---\nskills_enabled: false\n---\nbody");
|
||||||
|
|
||||||
|
ctx.role = Some(role);
|
||||||
|
|
||||||
|
assert!(!ctx.state().contains(StateFlags::SKILLS_ENABLED));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn state_omits_function_calling_when_app_disables_it() {
|
fn state_omits_function_calling_when_app_disables_it() {
|
||||||
let app_state = {
|
let app_state = {
|
||||||
@@ -4200,6 +4268,61 @@ mod tests {
|
|||||||
assert!(state.contains(StateFlags::SESSION_EMPTY));
|
assert!(state.contains(StateFlags::SESSION_EMPTY));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn todo_info_errors_when_auto_continue_disabled() {
|
||||||
|
let ctx = create_test_ctx();
|
||||||
|
let err = ctx.todo_info().unwrap_err();
|
||||||
|
|
||||||
|
let msg = err.to_string();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
msg.contains("Auto-continuation is disabled"),
|
||||||
|
"expected error to mention auto-continuation, got: {msg}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn todo_info_returns_empty_message_when_list_is_empty() {
|
||||||
|
let mut ctx = create_test_ctx();
|
||||||
|
|
||||||
|
ctx.update_app_config(|app| app.auto_continue = true);
|
||||||
|
|
||||||
|
let info = ctx.todo_info().unwrap();
|
||||||
|
assert!(
|
||||||
|
info.contains("No todos in the running list"),
|
||||||
|
"expected 'No todos' message, got: {info}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn todo_info_renders_running_list() {
|
||||||
|
let mut ctx = create_test_ctx();
|
||||||
|
ctx.update_app_config(|app| app.auto_continue = true);
|
||||||
|
ctx.init_todo_list("Map Labs");
|
||||||
|
ctx.add_todo("Discover columns");
|
||||||
|
ctx.add_todo("Write report");
|
||||||
|
|
||||||
|
ctx.mark_todo_done(1);
|
||||||
|
|
||||||
|
let info = ctx.todo_info().unwrap();
|
||||||
|
assert!(
|
||||||
|
info.contains("Goal: Map Labs"),
|
||||||
|
"expected goal in output, got: {info}"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
info.contains("Progress: 1/2 completed"),
|
||||||
|
"expected progress line, got: {info}"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
info.contains("Discover columns"),
|
||||||
|
"expected first task, got: {info}"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
info.contains("Write report"),
|
||||||
|
"expected second task, got: {info}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tools_info_returns_message_when_no_tools_enabled() {
|
fn tools_info_returns_message_when_no_tools_enabled() {
|
||||||
let ctx = create_test_ctx();
|
let ctx = create_test_ctx();
|
||||||
|
|||||||
+30
-6
@@ -49,7 +49,7 @@ pub const DEFAULT_CONTINUATION_PROMPT: &str = indoc! {"
|
|||||||
4. Continue with the next pending item now. Call tools immediately."
|
4. Continue with the next pending item now. Call tools immediately."
|
||||||
};
|
};
|
||||||
|
|
||||||
static REPL_COMMANDS: LazyLock<[ReplCommand; 45]> = LazyLock::new(|| {
|
static REPL_COMMANDS: LazyLock<[ReplCommand; 49]> = LazyLock::new(|| {
|
||||||
[
|
[
|
||||||
ReplCommand::new(".help", "Show this help guide", AssertState::pass()),
|
ReplCommand::new(".help", "Show this help guide", AssertState::pass()),
|
||||||
ReplCommand::new(".info", "Show system info", AssertState::pass()),
|
ReplCommand::new(".info", "Show system info", AssertState::pass()),
|
||||||
@@ -168,6 +168,11 @@ static REPL_COMMANDS: LazyLock<[ReplCommand; 45]> = LazyLock::new(|| {
|
|||||||
"Clear the todo list and stop auto-continuation",
|
"Clear the todo list and stop auto-continuation",
|
||||||
AssertState::pass(),
|
AssertState::pass(),
|
||||||
),
|
),
|
||||||
|
ReplCommand::new(
|
||||||
|
".info todo",
|
||||||
|
"Show the current todo list driving auto-continuation",
|
||||||
|
AssertState::True(StateFlags::AUTO_CONTINUE),
|
||||||
|
),
|
||||||
ReplCommand::new(
|
ReplCommand::new(
|
||||||
".rag",
|
".rag",
|
||||||
"Initialize or access RAG",
|
"Initialize or access RAG",
|
||||||
@@ -201,13 +206,28 @@ static REPL_COMMANDS: LazyLock<[ReplCommand; 45]> = LazyLock::new(|| {
|
|||||||
ReplCommand::new(".macro", "Execute a macro", AssertState::pass()),
|
ReplCommand::new(".macro", "Execute a macro", AssertState::pass()),
|
||||||
ReplCommand::new(
|
ReplCommand::new(
|
||||||
".skill",
|
".skill",
|
||||||
"List, load, unload, or create skills",
|
"Create a new skill",
|
||||||
AssertState::pass(),
|
AssertState::True(StateFlags::SKILLS_ENABLED),
|
||||||
|
),
|
||||||
|
ReplCommand::new(
|
||||||
|
".skill load",
|
||||||
|
"Load a skill into the current context",
|
||||||
|
AssertState::True(StateFlags::SKILLS_ENABLED),
|
||||||
|
),
|
||||||
|
ReplCommand::new(
|
||||||
|
".skill loaded",
|
||||||
|
"List currently-loaded skills",
|
||||||
|
AssertState::True(StateFlags::SKILLS_ENABLED),
|
||||||
|
),
|
||||||
|
ReplCommand::new(
|
||||||
|
".skill unload",
|
||||||
|
"Unload a skill from the current context",
|
||||||
|
AssertState::True(StateFlags::SKILLS_ENABLED),
|
||||||
),
|
),
|
||||||
ReplCommand::new(
|
ReplCommand::new(
|
||||||
".edit skill",
|
".edit skill",
|
||||||
"Modify an existing skill by name",
|
"Modify an existing skill by name",
|
||||||
AssertState::pass(),
|
AssertState::True(StateFlags::SKILLS_ENABLED),
|
||||||
),
|
),
|
||||||
ReplCommand::new(
|
ReplCommand::new(
|
||||||
".file",
|
".file",
|
||||||
@@ -489,6 +509,10 @@ pub async fn run_repl_command(
|
|||||||
let info = ctx.tools_info()?;
|
let info = ctx.tools_info()?;
|
||||||
print!("{info}");
|
print!("{info}");
|
||||||
}
|
}
|
||||||
|
Some("todo") => {
|
||||||
|
let info = ctx.todo_info()?;
|
||||||
|
print!("{info}");
|
||||||
|
}
|
||||||
Some(_) => unknown_command()?,
|
Some(_) => unknown_command()?,
|
||||||
None => {
|
None => {
|
||||||
let app = Arc::clone(&ctx.app.config);
|
let app = Arc::clone(&ctx.app.config);
|
||||||
@@ -1391,8 +1415,8 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn repl_commands_has_45_entries() {
|
fn repl_commands_has_49_entries() {
|
||||||
assert_eq!(REPL_COMMANDS.len(), 45);
|
assert_eq!(REPL_COMMANDS.len(), 49);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user