-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Recipe slash command parsing #6173
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
fb3c244
5296f11
7780490
157a5b4
832c736
32aaf68
ff25d57
ea9407f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -41,45 +41,42 @@ pub fn list_commands() -> &'static [CommandDef] { | |||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| impl Agent { | ||||||||||||||||||||||||||||||||
| pub async fn execute_command(&self, message_text: &str, session_id: &str) -> Option<Message> { | ||||||||||||||||||||||||||||||||
| pub async fn execute_command( | ||||||||||||||||||||||||||||||||
| &self, | ||||||||||||||||||||||||||||||||
| message_text: &str, | ||||||||||||||||||||||||||||||||
| session_id: &str, | ||||||||||||||||||||||||||||||||
| ) -> Result<Option<Message>> { | ||||||||||||||||||||||||||||||||
| let mut trimmed = message_text.trim().to_string(); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if COMPACT_TRIGGERS.contains(&trimmed.as_str()) { | ||||||||||||||||||||||||||||||||
| trimmed = COMPACT_TRIGGERS[0].to_string(); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if !trimmed.starts_with('/') { | ||||||||||||||||||||||||||||||||
| return None; | ||||||||||||||||||||||||||||||||
| return Ok(None); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| let command_str = trimmed.strip_prefix('/').unwrap_or(&trimmed); | ||||||||||||||||||||||||||||||||
| let (command, params) = command_str | ||||||||||||||||||||||||||||||||
| let (command, params_str) = command_str | ||||||||||||||||||||||||||||||||
| .split_once(' ') | ||||||||||||||||||||||||||||||||
| .map(|(cmd, p)| (cmd, p.trim())) | ||||||||||||||||||||||||||||||||
| .unwrap_or((command_str, "")); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| let params: Vec<&str> = if params.is_empty() { | ||||||||||||||||||||||||||||||||
| let params: Vec<&str> = if params_str.is_empty() { | ||||||||||||||||||||||||||||||||
| vec![] | ||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||
| params.split_whitespace().collect() | ||||||||||||||||||||||||||||||||
| params_str.split_whitespace().collect() | ||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| let result = match command { | ||||||||||||||||||||||||||||||||
| match command { | ||||||||||||||||||||||||||||||||
| "prompts" => self.handle_prompts_command(¶ms, session_id).await, | ||||||||||||||||||||||||||||||||
| "prompt" => self.handle_prompt_command(¶ms, session_id).await, | ||||||||||||||||||||||||||||||||
| "compact" => self.handle_compact_command(session_id).await, | ||||||||||||||||||||||||||||||||
| "clear" => self.handle_clear_command(session_id).await, | ||||||||||||||||||||||||||||||||
| _ => { | ||||||||||||||||||||||||||||||||
| self.handle_recipe_command(command, ¶ms, session_id) | ||||||||||||||||||||||||||||||||
| self.handle_recipe_command(command, params_str, session_id) | ||||||||||||||||||||||||||||||||
| .await | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| match result { | ||||||||||||||||||||||||||||||||
| Ok(msg) => msg, | ||||||||||||||||||||||||||||||||
| Err(e) => { | ||||||||||||||||||||||||||||||||
| Some(Message::assistant().with_text(format!("Error executing /{}: {}", command, e))) | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
@@ -264,7 +261,7 @@ impl Agent { | |||||||||||||||||||||||||||||||
| async fn handle_recipe_command( | ||||||||||||||||||||||||||||||||
| &self, | ||||||||||||||||||||||||||||||||
| command: &str, | ||||||||||||||||||||||||||||||||
| params: &[&str], | ||||||||||||||||||||||||||||||||
| params_str: &str, | ||||||||||||||||||||||||||||||||
| _session_id: &str, | ||||||||||||||||||||||||||||||||
| ) -> Result<Option<Message>> { | ||||||||||||||||||||||||||||||||
| let full_command = format!("/{}", command); | ||||||||||||||||||||||||||||||||
|
|
@@ -284,7 +281,64 @@ impl Agent { | |||||||||||||||||||||||||||||||
| .parent() | ||||||||||||||||||||||||||||||||
| .ok_or_else(|| anyhow!("Recipe path has no parent directory"))?; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| let param_values: Vec<String> = params.iter().map(|s| s.to_string()).collect(); | ||||||||||||||||||||||||||||||||
| let recipe_dir_str = recipe_dir.display().to_string(); | ||||||||||||||||||||||||||||||||
| let validation_result = | ||||||||||||||||||||||||||||||||
| crate::recipe::validate_recipe::validate_recipe_template_from_content( | ||||||||||||||||||||||||||||||||
| &recipe_content, | ||||||||||||||||||||||||||||||||
| Some(recipe_dir_str), | ||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||
| .map_err(|e| anyhow!("Failed to parse recipe: {}", e))?; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| let param_values: Vec<String> = if params_str.is_empty() { | ||||||||||||||||||||||||||||||||
| vec![] | ||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||
| let params_without_default = validation_result | ||||||||||||||||||||||||||||||||
| .parameters | ||||||||||||||||||||||||||||||||
| .as_ref() | ||||||||||||||||||||||||||||||||
| .map(|params| params.iter().filter(|p| p.default.is_none()).count()) | ||||||||||||||||||||||||||||||||
| .unwrap_or(0); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if params_without_default <= 1 { | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
| if params_without_default <= 1 { | |
| if params_without_default == 0 { | |
| let error_message = format!( | |
| "The /{} recipe does not require any parameters, \ | |
| but you provided: `{}`.\n\n\ | |
| Slash command recipes only support passing at most one \ | |
| required parameter.\n\n\ | |
| **To customize this recipe's optional parameters,** \ | |
| run it directly as a recipe (for example from the recipes sidebar).", | |
| command, | |
| params_str | |
| ); | |
| return Err(anyhow!(error_message)); | |
| } else if params_without_default == 1 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When there are no required parameters (params_without_default == 0) but the user provides input, the code creates a vec with params_str. This will cause the parameter to be passed to a recipe that doesn't expect any positional parameters. The condition should be
params_without_default == 1instead of<= 1to avoid this issue.