refactor: Factored out the macros structs from the large config module

This commit is contained in:
2025-11-05 15:50:39 -07:00
parent 0938119e99
commit ecd4d6587c
2 changed files with 112 additions and 102 deletions
+109
View File
@@ -0,0 +1,109 @@
use crate::config::{Config, GlobalConfig, RoleLike};
use crate::repl::{run_repl_command, split_args_text};
use crate::utils::{multiline_text, AbortSignal};
use anyhow::anyhow;
use indexmap::IndexMap;
use parking_lot::RwLock;
use serde::Deserialize;
use std::sync::Arc;
#[async_recursion::async_recursion]
pub async fn macro_execute(
config: &GlobalConfig,
name: &str,
args: Option<&str>,
abort_signal: AbortSignal,
) -> anyhow::Result<()> {
let macro_value = Config::load_macro(name)?;
let (mut new_args, text) = split_args_text(args.unwrap_or_default(), cfg!(windows));
if !text.is_empty() {
new_args.push(text.to_string());
}
let variables = macro_value
.resolve_variables(&new_args)
.map_err(|err| anyhow!("{err}. Usage: {}", macro_value.usage(name)))?;
let role = config.read().extract_role();
let mut config = config.read().clone();
config.temperature = role.temperature();
config.top_p = role.top_p();
config.enabled_tools = role.enabled_tools().clone();
config.enabled_mcp_servers = role.enabled_mcp_servers().clone();
config.macro_flag = true;
config.model = role.model().clone();
config.role = None;
config.session = None;
config.rag = None;
config.agent = None;
config.discontinuous_last_message();
let config = Arc::new(RwLock::new(config));
config.write().macro_flag = true;
for step in &macro_value.steps {
let command = Macro::interpolate_command(step, &variables);
println!(">> {}", multiline_text(&command));
run_repl_command(&config, abort_signal.clone(), &command).await?;
}
Ok(())
}
#[derive(Debug, Clone, Deserialize)]
pub struct Macro {
#[serde(default)]
pub variables: Vec<MacroVariable>,
pub steps: Vec<String>,
}
impl Macro {
pub fn resolve_variables(&self, args: &[String]) -> anyhow::Result<IndexMap<String, String>> {
let mut output = IndexMap::new();
for (i, variable) in self.variables.iter().enumerate() {
let value = if variable.rest && i == self.variables.len() - 1 {
if args.len() > i {
Some(args[i..].join(" "))
} else {
variable.default.clone()
}
} else {
args.get(i)
.map(|v| v.to_string())
.or_else(|| variable.default.clone())
};
let value =
value.ok_or_else(|| anyhow!("Missing value for variable '{}'", variable.name))?;
output.insert(variable.name.clone(), value);
}
Ok(output)
}
pub fn usage(&self, name: &str) -> String {
let mut parts = vec![name.to_string()];
for (i, variable) in self.variables.iter().enumerate() {
let part = match (
variable.rest && i == self.variables.len() - 1,
variable.default.is_some(),
) {
(true, true) => format!("[{}]...", variable.name),
(true, false) => format!("<{}>...", variable.name),
(false, true) => format!("[{}]", variable.name),
(false, false) => format!("<{}>", variable.name),
};
parts.push(part);
}
parts.join(" ")
}
pub fn interpolate_command(command: &str, variables: &IndexMap<String, String>) -> String {
let mut output = command.to_string();
for (key, value) in variables {
output = output.replace(&format!("{{{{{key}}}}}"), value);
}
output
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct MacroVariable {
pub name: String,
#[serde(default)]
pub rest: bool,
pub default: Option<String>,
}
+3 -102
View File
@@ -1,5 +1,6 @@
mod agent; mod agent;
mod input; mod input;
mod macros;
mod role; mod role;
mod session; mod session;
@@ -9,6 +10,7 @@ pub use self::role::{
Role, RoleLike, CODE_ROLE, CREATE_TITLE_ROLE, EXPLAIN_SHELL_ROLE, SHELL_ROLE, Role, RoleLike, CODE_ROLE, CREATE_TITLE_ROLE, EXPLAIN_SHELL_ROLE, SHELL_ROLE,
}; };
use self::session::Session; use self::session::Session;
pub use macros::macro_execute;
use mem::take; use mem::take;
use crate::client::{ use crate::client::{
@@ -18,9 +20,9 @@ use crate::client::{
use crate::function::{FunctionDeclaration, Functions, ToolResult}; use crate::function::{FunctionDeclaration, Functions, ToolResult};
use crate::rag::Rag; use crate::rag::Rag;
use crate::render::{MarkdownRender, RenderOptions}; use crate::render::{MarkdownRender, RenderOptions};
use crate::repl::{run_repl_command, split_args_text};
use crate::utils::*; use crate::utils::*;
use crate::config::macros::Macro;
use crate::mcp::{ use crate::mcp::{
McpRegistry, MCP_INVOKE_META_FUNCTION_NAME_PREFIX, MCP_LIST_META_FUNCTION_NAME_PREFIX, McpRegistry, MCP_INVOKE_META_FUNCTION_NAME_PREFIX, MCP_LIST_META_FUNCTION_NAME_PREFIX,
}; };
@@ -2948,107 +2950,6 @@ impl WorkingMode {
} }
} }
#[async_recursion::async_recursion]
pub async fn macro_execute(
config: &GlobalConfig,
name: &str,
args: Option<&str>,
abort_signal: AbortSignal,
) -> Result<()> {
let macro_value = Config::load_macro(name)?;
let (mut new_args, text) = split_args_text(args.unwrap_or_default(), cfg!(windows));
if !text.is_empty() {
new_args.push(text.to_string());
}
let variables = macro_value
.resolve_variables(&new_args)
.map_err(|err| anyhow!("{err}. Usage: {}", macro_value.usage(name)))?;
let role = config.read().extract_role();
let mut config = config.read().clone();
config.temperature = role.temperature();
config.top_p = role.top_p();
config.enabled_tools = role.enabled_tools().clone();
config.enabled_mcp_servers = role.enabled_mcp_servers().clone();
config.macro_flag = true;
config.model = role.model().clone();
config.role = None;
config.session = None;
config.rag = None;
config.agent = None;
config.discontinuous_last_message();
let config = Arc::new(RwLock::new(config));
config.write().macro_flag = true;
for step in &macro_value.steps {
let command = Macro::interpolate_command(step, &variables);
println!(">> {}", multiline_text(&command));
run_repl_command(&config, abort_signal.clone(), &command).await?;
}
Ok(())
}
#[derive(Debug, Clone, Deserialize)]
pub struct Macro {
#[serde(default)]
pub variables: Vec<MacroVariable>,
pub steps: Vec<String>,
}
impl Macro {
pub fn resolve_variables(&self, args: &[String]) -> Result<IndexMap<String, String>> {
let mut output = IndexMap::new();
for (i, variable) in self.variables.iter().enumerate() {
let value = if variable.rest && i == self.variables.len() - 1 {
if args.len() > i {
Some(args[i..].join(" "))
} else {
variable.default.clone()
}
} else {
args.get(i)
.map(|v| v.to_string())
.or_else(|| variable.default.clone())
};
let value =
value.ok_or_else(|| anyhow!("Missing value for variable '{}'", variable.name))?;
output.insert(variable.name.clone(), value);
}
Ok(output)
}
pub fn usage(&self, name: &str) -> String {
let mut parts = vec![name.to_string()];
for (i, variable) in self.variables.iter().enumerate() {
let part = match (
variable.rest && i == self.variables.len() - 1,
variable.default.is_some(),
) {
(true, true) => format!("[{}]...", variable.name),
(true, false) => format!("<{}>...", variable.name),
(false, true) => format!("[{}]", variable.name),
(false, false) => format!("<{}>", variable.name),
};
parts.push(part);
}
parts.join(" ")
}
pub fn interpolate_command(command: &str, variables: &IndexMap<String, String>) -> String {
let mut output = command.to_string();
for (key, value) in variables {
output = output.replace(&format!("{{{{{key}}}}}"), value);
}
output
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct MacroVariable {
pub name: String,
#[serde(default)]
pub rest: bool,
pub default: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelsOverride { pub struct ModelsOverride {
pub version: String, pub version: String,