mirror of
https://github.com/aljazceru/goose.git
synced 2026-02-12 01:54:28 +01:00
feat: show recipe explanation (#2530)
Co-authored-by: Michael Neale <michael.neale@gmail.com>
This commit is contained in:
@@ -11,7 +11,7 @@ use crate::commands::project::{handle_project_default, handle_projects_interacti
|
||||
use crate::commands::recipe::{handle_deeplink, handle_validate};
|
||||
use crate::commands::session::{handle_session_list, handle_session_remove};
|
||||
use crate::logging::setup_logging;
|
||||
use crate::recipes::recipe::load_recipe;
|
||||
use crate::recipes::recipe::{explain_recipe_with_parameters, load_recipe_as_template};
|
||||
use crate::session;
|
||||
use crate::session::{build_session, SessionBuilderConfig};
|
||||
use goose_bench::bench_config::BenchRunConfig;
|
||||
@@ -333,6 +333,13 @@ enum Command {
|
||||
)]
|
||||
no_session: bool,
|
||||
|
||||
/// Show the recipe title, description, and parameters
|
||||
#[arg(
|
||||
long = "explain",
|
||||
help = "Show the recipe title, description, and parameters"
|
||||
)]
|
||||
explain: bool,
|
||||
|
||||
/// Maximum number of consecutive identical tool calls allowed
|
||||
#[arg(
|
||||
long = "max-tool-repetitions",
|
||||
@@ -536,9 +543,10 @@ pub async fn cli() -> Result<()> {
|
||||
remote_extensions,
|
||||
builtins,
|
||||
params,
|
||||
explain,
|
||||
}) => {
|
||||
let input_config = match (instructions, input_text, recipe) {
|
||||
(Some(file), _, _) if file == "-" => {
|
||||
let input_config = match (instructions, input_text, recipe, explain) {
|
||||
(Some(file), _, _, _) if file == "-" => {
|
||||
let mut input = String::new();
|
||||
std::io::stdin()
|
||||
.read_to_string(&mut input)
|
||||
@@ -550,7 +558,7 @@ pub async fn cli() -> Result<()> {
|
||||
additional_system_prompt: None,
|
||||
}
|
||||
}
|
||||
(Some(file), _, _) => {
|
||||
(Some(file), _, _, _) => {
|
||||
let contents = std::fs::read_to_string(&file).unwrap_or_else(|err| {
|
||||
eprintln!(
|
||||
"Instruction file not found — did you mean to use goose run --text?\n{}",
|
||||
@@ -564,14 +572,18 @@ pub async fn cli() -> Result<()> {
|
||||
additional_system_prompt: None,
|
||||
}
|
||||
}
|
||||
(_, Some(text), _) => InputConfig {
|
||||
(_, Some(text), _, _) => InputConfig {
|
||||
contents: Some(text),
|
||||
extensions_override: None,
|
||||
additional_system_prompt: None,
|
||||
},
|
||||
(_, _, Some(recipe_name)) => {
|
||||
(_, _, Some(recipe_name), explain) => {
|
||||
if explain {
|
||||
explain_recipe_with_parameters(&recipe_name, params)?;
|
||||
return Ok(());
|
||||
}
|
||||
let recipe =
|
||||
load_recipe(&recipe_name, true, Some(params)).unwrap_or_else(|err| {
|
||||
load_recipe_as_template(&recipe_name, params).unwrap_or_else(|err| {
|
||||
eprintln!("{}: {}", console::style("Error").red().bold(), err);
|
||||
std::process::exit(1);
|
||||
});
|
||||
@@ -581,7 +593,7 @@ pub async fn cli() -> Result<()> {
|
||||
additional_system_prompt: recipe.instructions,
|
||||
}
|
||||
}
|
||||
(None, None, None) => {
|
||||
(None, None, None, _) => {
|
||||
eprintln!("Error: Must provide either --instructions (-i), --text (-t), or --recipe. Use -i - for stdin.");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ use crate::recipes::recipe::load_recipe;
|
||||
/// Result indicating success or failure
|
||||
pub fn handle_validate(recipe_name: &str) -> Result<()> {
|
||||
// Load and validate the recipe file
|
||||
match load_recipe(recipe_name, false, None) {
|
||||
match load_recipe(recipe_name) {
|
||||
Ok(_) => {
|
||||
println!("{} recipe file is valid", style("✓").green().bold());
|
||||
Ok(())
|
||||
@@ -38,7 +38,7 @@ pub fn handle_validate(recipe_name: &str) -> Result<()> {
|
||||
/// Result indicating success or failure
|
||||
pub fn handle_deeplink(recipe_name: &str) -> Result<()> {
|
||||
// Load the recipe file first to validate it
|
||||
match load_recipe(recipe_name, false, None) {
|
||||
match load_recipe(recipe_name) {
|
||||
Ok(recipe) => {
|
||||
if let Ok(recipe_json) = serde_json::to_string(&recipe) {
|
||||
let deeplink = base64::engine::general_purpose::STANDARD.encode(recipe_json);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod github_recipe;
|
||||
pub mod print_recipe;
|
||||
pub mod recipe;
|
||||
pub mod search_recipe;
|
||||
|
||||
72
crates/goose-cli/src/recipes/print_recipe.rs
Normal file
72
crates/goose-cli/src/recipes/print_recipe.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use console::style;
|
||||
use goose::recipe::Recipe;
|
||||
|
||||
pub fn print_recipe_explanation(recipe: &Recipe) {
|
||||
println!(
|
||||
"{} {}",
|
||||
style("🔍 Loading recipe:").bold().green(),
|
||||
style(&recipe.title).green()
|
||||
);
|
||||
println!("{}", style("📄 Description:").bold());
|
||||
println!(" {}", recipe.description);
|
||||
if let Some(params) = &recipe.parameters {
|
||||
if !params.is_empty() {
|
||||
println!("{}", style("⚙️ Recipe Parameters:").bold());
|
||||
for param in params {
|
||||
let default_display = match ¶m.default {
|
||||
Some(val) => format!(" (default: {})", val),
|
||||
None => String::new(),
|
||||
};
|
||||
|
||||
println!(
|
||||
" - {} ({}, {}){}: {}",
|
||||
style(¶m.key).cyan(),
|
||||
param.input_type,
|
||||
param.requirement,
|
||||
default_display,
|
||||
param.description
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_required_parameters_for_template(
|
||||
params_for_template: HashMap<String, String>,
|
||||
missing_params: Vec<String>,
|
||||
) {
|
||||
if !params_for_template.is_empty() {
|
||||
println!(
|
||||
"{}",
|
||||
style("📥 Parameters used to load this recipe:").bold()
|
||||
);
|
||||
for (key, value) in params_for_template {
|
||||
println!(" {}: {}", key, value);
|
||||
}
|
||||
}
|
||||
if !missing_params.is_empty() {
|
||||
println!(
|
||||
"{}",
|
||||
style("🔴 Missing parameters in the command line if you want to run the recipe:")
|
||||
.bold()
|
||||
);
|
||||
for param in missing_params.iter() {
|
||||
println!(" - {}", param);
|
||||
}
|
||||
println!(
|
||||
"📩 {}:",
|
||||
style("Please provide the following parameters in the command line if you want to run the recipe:").bold()
|
||||
);
|
||||
println!(" {}", missing_parameters_command_line(missing_params));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn missing_parameters_command_line(missing_params: Vec<String>) -> String {
|
||||
missing_params
|
||||
.iter()
|
||||
.map(|key| format!("--params {}=your_value", key))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
use anyhow::Result;
|
||||
use console::style;
|
||||
|
||||
use crate::recipes::print_recipe::{
|
||||
missing_parameters_command_line, print_recipe_explanation,
|
||||
print_required_parameters_for_template,
|
||||
};
|
||||
use crate::recipes::search_recipe::retrieve_recipe_file;
|
||||
use goose::recipe::{Recipe, RecipeParameter, RecipeParameterRequirement};
|
||||
use minijinja::{Environment, Error, Template, UndefinedBehavior};
|
||||
@@ -8,12 +12,63 @@ use serde_json::Value as JsonValue;
|
||||
use serde_yaml::Value as YamlValue;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
/// Loads, validates a recipe from a YAML or JSON file, and renders it with the given parameters
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `path` - Path to the recipe file (YAML or JSON)
|
||||
/// * `params` - parameters to render the recipe with
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// The rendered recipe if successful
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if:
|
||||
/// - Recipe is not valid
|
||||
/// - The required fields are missing
|
||||
pub fn load_recipe_as_template(recipe_name: &str, params: Vec<(String, String)>) -> Result<Recipe> {
|
||||
let recipe_file_content = retrieve_recipe_file(recipe_name)?;
|
||||
|
||||
let recipe = validate_recipe_file_parameters(&recipe_file_content)?;
|
||||
|
||||
let (params_for_template, missing_params) =
|
||||
apply_values_to_parameters(¶ms, recipe.parameters, true)?;
|
||||
if !missing_params.is_empty() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Please provide the following parameters in the command line: {}",
|
||||
missing_parameters_command_line(missing_params)
|
||||
));
|
||||
}
|
||||
|
||||
let rendered_content = render_content_with_params(&recipe_file_content, ¶ms_for_template)?;
|
||||
|
||||
let recipe = parse_recipe_content(&rendered_content)?;
|
||||
|
||||
// Display information about the loaded recipe
|
||||
println!(
|
||||
"{} {}",
|
||||
style("Loading recipe:").green().bold(),
|
||||
style(&recipe.title).green()
|
||||
);
|
||||
println!("{} {}", style("Description:").bold(), &recipe.description);
|
||||
|
||||
if !params_for_template.is_empty() {
|
||||
println!("{}", style("Parameters used to load this recipe:").bold());
|
||||
for (key, value) in params_for_template {
|
||||
println!("{}: {}", key, value);
|
||||
}
|
||||
}
|
||||
println!();
|
||||
Ok(recipe)
|
||||
}
|
||||
|
||||
/// Loads and validates a recipe from a YAML or JSON file
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `path` - Path to the recipe file (YAML or JSON)
|
||||
/// * `log` - whether to log information about the recipe or not
|
||||
/// * `params` - optional parameters to render the recipe with
|
||||
///
|
||||
/// # Returns
|
||||
@@ -26,65 +81,43 @@ use std::collections::{HashMap, HashSet};
|
||||
/// - The file doesn't exist
|
||||
/// - The file can't be read
|
||||
/// - The YAML/JSON is invalid
|
||||
/// - The required fields are missing
|
||||
pub fn load_recipe(
|
||||
recipe_name: &str,
|
||||
log: bool,
|
||||
params: Option<Vec<(String, String)>>,
|
||||
) -> Result<Recipe> {
|
||||
/// - The parameter definition does not match the template variables in the recipe file
|
||||
pub fn load_recipe(recipe_name: &str) -> Result<Recipe> {
|
||||
let recipe_file_content = retrieve_recipe_file(recipe_name)?;
|
||||
|
||||
let recipe_parameters = validate_recipe_file_parameters(&recipe_file_content)?;
|
||||
|
||||
let (rendered_content, params_for_template) = if let Some(user_params) = params {
|
||||
let params_for_template = apply_values_to_parameters(&user_params, recipe_parameters)?;
|
||||
(
|
||||
render_content_with_params(&recipe_file_content, ¶ms_for_template)?,
|
||||
Some(params_for_template),
|
||||
)
|
||||
} else {
|
||||
(recipe_file_content, None)
|
||||
};
|
||||
|
||||
let recipe = parse_recipe_content(&rendered_content)?;
|
||||
if log {
|
||||
// Display information about the loaded recipe
|
||||
println!(
|
||||
"{} {}",
|
||||
style("Loading recipe:").green().bold(),
|
||||
style(&recipe.title).green()
|
||||
);
|
||||
println!("{} {}", style("Description:").dim(), &recipe.description);
|
||||
|
||||
if let Some(params) = params_for_template {
|
||||
if !params.is_empty() {
|
||||
println!("{}", style("Parameters:").dim());
|
||||
for (key, value) in params {
|
||||
println!("{}: {}", key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!(); // Add a blank line for spacing
|
||||
}
|
||||
|
||||
Ok(recipe)
|
||||
validate_recipe_file_parameters(&recipe_file_content)
|
||||
}
|
||||
|
||||
fn validate_recipe_file_parameters(recipe_file_content: &str) -> Result<Vec<RecipeParameter>> {
|
||||
pub fn explain_recipe_with_parameters(
|
||||
recipe_name: &str,
|
||||
params: Vec<(String, String)>,
|
||||
) -> Result<()> {
|
||||
let recipe_file_content = retrieve_recipe_file(recipe_name)?;
|
||||
|
||||
let raw_recipe = validate_recipe_file_parameters(&recipe_file_content)?;
|
||||
print_recipe_explanation(&raw_recipe);
|
||||
let recipe_parameters = raw_recipe.parameters;
|
||||
let (params_for_template, missing_params) =
|
||||
apply_values_to_parameters(¶ms, recipe_parameters, false)?;
|
||||
print_required_parameters_for_template(params_for_template, missing_params);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_recipe_file_parameters(recipe_file_content: &str) -> Result<Recipe> {
|
||||
let recipe_from_recipe_file: Recipe = parse_recipe_content(recipe_file_content)?;
|
||||
validate_optional_parameters(&recipe_from_recipe_file)?;
|
||||
validate_parameters_in_template(recipe_from_recipe_file, recipe_file_content)
|
||||
validate_parameters_in_template(&recipe_from_recipe_file.parameters, recipe_file_content)?;
|
||||
Ok(recipe_from_recipe_file)
|
||||
}
|
||||
|
||||
fn validate_parameters_in_template(
|
||||
recipe: Recipe,
|
||||
recipe_parameters: &Option<Vec<RecipeParameter>>,
|
||||
recipe_file_content: &str,
|
||||
) -> Result<Vec<RecipeParameter>> {
|
||||
) -> Result<()> {
|
||||
let template_variables = extract_template_variables(recipe_file_content)?;
|
||||
|
||||
let param_keys: HashSet<String> = recipe
|
||||
.parameters
|
||||
let param_keys: HashSet<String> = recipe_parameters
|
||||
.as_ref()
|
||||
.unwrap_or(&vec![])
|
||||
.iter()
|
||||
@@ -100,7 +133,7 @@ fn validate_parameters_in_template(
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if missing_keys.is_empty() && extra_keys.is_empty() {
|
||||
return Ok(recipe.parameters.unwrap_or_default());
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut message = String::new();
|
||||
@@ -173,15 +206,16 @@ fn extract_template_variables(template_str: &str) -> Result<HashSet<String>> {
|
||||
|
||||
fn apply_values_to_parameters(
|
||||
user_params: &[(String, String)],
|
||||
recipe_parameters: Vec<RecipeParameter>,
|
||||
) -> Result<HashMap<String, String>> {
|
||||
recipe_parameters: Option<Vec<RecipeParameter>>,
|
||||
enable_user_prompt: bool,
|
||||
) -> Result<(HashMap<String, String>, Vec<String>)> {
|
||||
let mut param_map: HashMap<String, String> = user_params.iter().cloned().collect();
|
||||
let mut missing_params: Vec<String> = Vec::new();
|
||||
for param in recipe_parameters {
|
||||
for param in recipe_parameters.unwrap_or_default() {
|
||||
if !param_map.contains_key(¶m.key) {
|
||||
match (¶m.default, ¶m.requirement) {
|
||||
(Some(default), _) => param_map.insert(param.key.clone(), default.clone()),
|
||||
(None, RecipeParameterRequirement::UserPrompt) => {
|
||||
(None, RecipeParameterRequirement::UserPrompt) if enable_user_prompt => {
|
||||
let input_value = cliclack::input(format!(
|
||||
"Please enter {} ({})",
|
||||
param.key, param.description
|
||||
@@ -196,29 +230,16 @@ fn apply_values_to_parameters(
|
||||
};
|
||||
}
|
||||
}
|
||||
match missing_params.is_empty() {
|
||||
true => Ok(param_map),
|
||||
false => {
|
||||
let formatted = missing_params
|
||||
.iter()
|
||||
.map(|key| format!("--params {}=your_value", key))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ");
|
||||
|
||||
Err(anyhow::anyhow!(
|
||||
"Please provide the following parameters in the command line: {}",
|
||||
formatted
|
||||
))
|
||||
}
|
||||
}
|
||||
Ok((param_map, missing_params))
|
||||
}
|
||||
|
||||
fn render_content_with_params(content: &str, params: &HashMap<String, String>) -> Result<String> {
|
||||
// Create a minijinja environment and context
|
||||
let mut env = minijinja::Environment::new();
|
||||
env.set_undefined_behavior(UndefinedBehavior::Strict);
|
||||
let template: Template<'_, '_> = env.template_from_str(content)
|
||||
.map_err(|e: Error| anyhow::anyhow!("Failed to render recipe {}, please check if the recipe has proper syntax for variables: eg: {{ variable_name }}", e.to_string()))?;
|
||||
let template: Template<'_, '_> = env
|
||||
.template_from_str(content)
|
||||
.map_err(|e: Error| anyhow::anyhow!("Invalid template syntax: {}", e.to_string()))?;
|
||||
|
||||
// Render the template with the parameters
|
||||
template.render(params).map_err(|e: Error| {
|
||||
@@ -284,13 +305,11 @@ mod tests {
|
||||
let content = "Hello {{ unclosed";
|
||||
let params = HashMap::new();
|
||||
let err = render_content_with_params(content, ¶ms).unwrap_err();
|
||||
assert!(err
|
||||
.to_string()
|
||||
.contains("please check if the recipe has proper syntax"));
|
||||
assert!(err.to_string().contains("Invalid template syntax"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_load_recipe_success() {
|
||||
fn test_load_recipe_as_template_success() {
|
||||
let instructions_and_parameters = r#"
|
||||
"instructions": "Test instructions with {{ my_name }}",
|
||||
"parameters": [
|
||||
@@ -305,7 +324,7 @@ mod tests {
|
||||
let (_temp_dir, recipe_path) = setup_recipe_file(instructions_and_parameters);
|
||||
|
||||
let params = vec![("my_name".to_string(), "value".to_string())];
|
||||
let recipe = load_recipe(recipe_path.to_str().unwrap(), false, Some(params)).unwrap();
|
||||
let recipe = load_recipe_as_template(recipe_path.to_str().unwrap(), params).unwrap();
|
||||
|
||||
assert_eq!(recipe.title, "Test Recipe");
|
||||
assert_eq!(recipe.description, "A test recipe");
|
||||
@@ -323,7 +342,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_load_recipe_success_variable_in_prompt() {
|
||||
fn test_load_recipe_as_template_success_variable_in_prompt() {
|
||||
let instructions_and_parameters = r#"
|
||||
"instructions": "Test instructions",
|
||||
"prompt": "My prompt {{ my_name }}",
|
||||
@@ -339,7 +358,7 @@ mod tests {
|
||||
let (_temp_dir, recipe_path) = setup_recipe_file(instructions_and_parameters);
|
||||
|
||||
let params = vec![("my_name".to_string(), "value".to_string())];
|
||||
let recipe = load_recipe(recipe_path.to_str().unwrap(), false, Some(params)).unwrap();
|
||||
let recipe = load_recipe_as_template(recipe_path.to_str().unwrap(), params).unwrap();
|
||||
|
||||
assert_eq!(recipe.title, "Test Recipe");
|
||||
assert_eq!(recipe.description, "A test recipe");
|
||||
@@ -356,7 +375,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_load_recipe_wrong_parameters_in_recipe_file() {
|
||||
fn test_load_recipe_as_template_wrong_parameters_in_recipe_file() {
|
||||
let instructions_and_parameters = r#"
|
||||
"instructions": "Test instructions with {{ expected_param1 }} {{ expected_param2 }}",
|
||||
"parameters": [
|
||||
@@ -369,7 +388,7 @@ mod tests {
|
||||
]"#;
|
||||
let (_temp_dir, recipe_path) = setup_recipe_file(instructions_and_parameters);
|
||||
|
||||
let load_recipe_result = load_recipe(recipe_path.to_str().unwrap(), false, None);
|
||||
let load_recipe_result = load_recipe_as_template(recipe_path.to_str().unwrap(), Vec::new());
|
||||
assert!(load_recipe_result.is_err());
|
||||
let err = load_recipe_result.unwrap_err();
|
||||
println!("{}", err.to_string());
|
||||
@@ -384,7 +403,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_load_recipe_with_default_values_in_recipe_file() {
|
||||
fn test_load_recipe_as_template_with_default_values_in_recipe_file() {
|
||||
let instructions_and_parameters = r#"
|
||||
"instructions": "Test instructions with {{ param_with_default }} {{ param_without_default }}",
|
||||
"parameters": [
|
||||
@@ -405,7 +424,7 @@ mod tests {
|
||||
let (_temp_dir, recipe_path) = setup_recipe_file(instructions_and_parameters);
|
||||
let params = vec![("param_without_default".to_string(), "value1".to_string())];
|
||||
|
||||
let recipe = load_recipe(recipe_path.to_str().unwrap(), false, Some(params)).unwrap();
|
||||
let recipe = load_recipe_as_template(recipe_path.to_str().unwrap(), params).unwrap();
|
||||
|
||||
assert_eq!(recipe.title, "Test Recipe");
|
||||
assert_eq!(recipe.description, "A test recipe");
|
||||
@@ -416,7 +435,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_load_recipe_optional_parameters_without_default_values_in_recipe_file() {
|
||||
fn test_load_recipe_as_template_optional_parameters_without_default_values_in_recipe_file() {
|
||||
let instructions_and_parameters = r#"
|
||||
"instructions": "Test instructions with {{ optional_param }}",
|
||||
"parameters": [
|
||||
@@ -429,7 +448,7 @@ mod tests {
|
||||
]"#;
|
||||
let (_temp_dir, recipe_path) = setup_recipe_file(instructions_and_parameters);
|
||||
|
||||
let load_recipe_result = load_recipe(recipe_path.to_str().unwrap(), false, None);
|
||||
let load_recipe_result = load_recipe_as_template(recipe_path.to_str().unwrap(), Vec::new());
|
||||
assert!(load_recipe_result.is_err());
|
||||
let err = load_recipe_result.unwrap_err();
|
||||
println!("{}", err.to_string());
|
||||
@@ -439,7 +458,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_load_recipe_wrong_input_type_in_recipe_file() {
|
||||
fn test_load_recipe_as_template_wrong_input_type_in_recipe_file() {
|
||||
let instructions_and_parameters = r#"
|
||||
"instructions": "Test instructions with {{ param }}",
|
||||
"parameters": [
|
||||
@@ -453,22 +472,22 @@ mod tests {
|
||||
let params = vec![("param".to_string(), "value".to_string())];
|
||||
let (_temp_dir, recipe_path) = setup_recipe_file(instructions_and_parameters);
|
||||
|
||||
let load_recipe_result = load_recipe(recipe_path.to_str().unwrap(), false, Some(params));
|
||||
let load_recipe_result = load_recipe_as_template(recipe_path.to_str().unwrap(), params);
|
||||
assert!(load_recipe_result.is_err());
|
||||
let err = load_recipe_result.unwrap_err();
|
||||
assert!(err.to_string().contains("unknown variant `some_invalid_type`, expected one of `string`, `number`, `date`, `file`"));
|
||||
assert!(err
|
||||
.to_string()
|
||||
.contains("unknown variant `some_invalid_type`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_load_recipe_success_without_parameters() {
|
||||
fn test_load_recipe_as_template_success_without_parameters() {
|
||||
let instructions_and_parameters = r#"
|
||||
"instructions": "Test instructions"
|
||||
"#;
|
||||
let (_temp_dir, recipe_path) = setup_recipe_file(instructions_and_parameters);
|
||||
|
||||
let load_recipe_result = load_recipe(recipe_path.to_str().unwrap(), false, None);
|
||||
assert!(load_recipe_result.is_ok());
|
||||
let recipe = load_recipe_result.unwrap();
|
||||
let recipe = load_recipe_as_template(recipe_path.to_str().unwrap(), Vec::new()).unwrap();
|
||||
assert_eq!(recipe.instructions.unwrap(), "Test instructions");
|
||||
assert!(recipe.parameters.is_none());
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::fmt;
|
||||
|
||||
use crate::agents::extension::ExtensionConfig;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -102,15 +104,36 @@ pub enum RecipeParameterRequirement {
|
||||
UserPrompt,
|
||||
}
|
||||
|
||||
impl fmt::Display for RecipeParameterRequirement {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
serde_json::to_string(self).unwrap().trim_matches('"')
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum RecipeParameterInputType {
|
||||
String,
|
||||
Number,
|
||||
Boolean,
|
||||
Date,
|
||||
File,
|
||||
}
|
||||
|
||||
impl fmt::Display for RecipeParameterInputType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
serde_json::to_string(self).unwrap().trim_matches('"')
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct RecipeParameter {
|
||||
pub key: String,
|
||||
|
||||
Reference in New Issue
Block a user