Streaming tool calls (#29179)
https://github.com/user-attachments/assets/7854a737-ef83-414c-b397-45122e4f32e8 Release Notes: - Create file and edit file tools now stream their tool descriptions, so you can see what they're doing sooner. --------- Co-authored-by: Marshall Bowers <git@maxdeviant.com>
This commit is contained in:
parent
7aa0fa1543
commit
4f2f9ff762
17 changed files with 358 additions and 47 deletions
|
@ -33,8 +33,18 @@ pub struct CreateFileToolInput {
|
|||
pub contents: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||
struct PartialInput {
|
||||
#[serde(default)]
|
||||
path: String,
|
||||
#[serde(default)]
|
||||
contents: String,
|
||||
}
|
||||
|
||||
pub struct CreateFileTool;
|
||||
|
||||
const DEFAULT_UI_TEXT: &str = "Create file";
|
||||
|
||||
impl Tool for CreateFileTool {
|
||||
fn name(&self) -> String {
|
||||
"create_file".into()
|
||||
|
@ -62,7 +72,14 @@ impl Tool for CreateFileTool {
|
|||
let path = MarkdownString::inline_code(&input.path);
|
||||
format!("Create file {path}")
|
||||
}
|
||||
Err(_) => "Create file".to_string(),
|
||||
Err(_) => DEFAULT_UI_TEXT.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn still_streaming_ui_text(&self, input: &serde_json::Value) -> String {
|
||||
match serde_json::from_value::<PartialInput>(input.clone()).ok() {
|
||||
Some(input) if !input.path.is_empty() => input.path,
|
||||
_ => DEFAULT_UI_TEXT.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,3 +128,60 @@ impl Tool for CreateFileTool {
|
|||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn still_streaming_ui_text_with_path() {
|
||||
let tool = CreateFileTool;
|
||||
let input = json!({
|
||||
"path": "src/main.rs",
|
||||
"contents": "fn main() {\n println!(\"Hello, world!\");\n}"
|
||||
});
|
||||
|
||||
assert_eq!(tool.still_streaming_ui_text(&input), "src/main.rs");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn still_streaming_ui_text_without_path() {
|
||||
let tool = CreateFileTool;
|
||||
let input = json!({
|
||||
"path": "",
|
||||
"contents": "fn main() {\n println!(\"Hello, world!\");\n}"
|
||||
});
|
||||
|
||||
assert_eq!(tool.still_streaming_ui_text(&input), DEFAULT_UI_TEXT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn still_streaming_ui_text_with_null() {
|
||||
let tool = CreateFileTool;
|
||||
let input = serde_json::Value::Null;
|
||||
|
||||
assert_eq!(tool.still_streaming_ui_text(&input), DEFAULT_UI_TEXT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ui_text_with_valid_input() {
|
||||
let tool = CreateFileTool;
|
||||
let input = json!({
|
||||
"path": "src/main.rs",
|
||||
"contents": "fn main() {\n println!(\"Hello, world!\");\n}"
|
||||
});
|
||||
|
||||
assert_eq!(tool.ui_text(&input), "Create file `src/main.rs`");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ui_text_with_invalid_input() {
|
||||
let tool = CreateFileTool;
|
||||
let input = json!({
|
||||
"invalid": "field"
|
||||
});
|
||||
|
||||
assert_eq!(tool.ui_text(&input), DEFAULT_UI_TEXT);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,8 +47,22 @@ pub struct EditFileToolInput {
|
|||
pub new_string: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||
struct PartialInput {
|
||||
#[serde(default)]
|
||||
path: String,
|
||||
#[serde(default)]
|
||||
display_description: String,
|
||||
#[serde(default)]
|
||||
old_string: String,
|
||||
#[serde(default)]
|
||||
new_string: String,
|
||||
}
|
||||
|
||||
pub struct EditFileTool;
|
||||
|
||||
const DEFAULT_UI_TEXT: &str = "Edit file";
|
||||
|
||||
impl Tool for EditFileTool {
|
||||
fn name(&self) -> String {
|
||||
"edit_file".into()
|
||||
|
@ -77,6 +91,22 @@ impl Tool for EditFileTool {
|
|||
}
|
||||
}
|
||||
|
||||
fn still_streaming_ui_text(&self, input: &serde_json::Value) -> String {
|
||||
if let Some(input) = serde_json::from_value::<PartialInput>(input.clone()).ok() {
|
||||
let description = input.display_description.trim();
|
||||
if !description.is_empty() {
|
||||
return description.to_string();
|
||||
}
|
||||
|
||||
let path = input.path.trim();
|
||||
if !path.is_empty() {
|
||||
return path.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
DEFAULT_UI_TEXT.to_string()
|
||||
}
|
||||
|
||||
fn run(
|
||||
self: Arc<Self>,
|
||||
input: serde_json::Value,
|
||||
|
@ -181,3 +211,69 @@ impl Tool for EditFileTool {
|
|||
}).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn still_streaming_ui_text_with_path() {
|
||||
let tool = EditFileTool;
|
||||
let input = json!({
|
||||
"path": "src/main.rs",
|
||||
"display_description": "",
|
||||
"old_string": "old code",
|
||||
"new_string": "new code"
|
||||
});
|
||||
|
||||
assert_eq!(tool.still_streaming_ui_text(&input), "src/main.rs");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn still_streaming_ui_text_with_description() {
|
||||
let tool = EditFileTool;
|
||||
let input = json!({
|
||||
"path": "",
|
||||
"display_description": "Fix error handling",
|
||||
"old_string": "old code",
|
||||
"new_string": "new code"
|
||||
});
|
||||
|
||||
assert_eq!(tool.still_streaming_ui_text(&input), "Fix error handling");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn still_streaming_ui_text_with_path_and_description() {
|
||||
let tool = EditFileTool;
|
||||
let input = json!({
|
||||
"path": "src/main.rs",
|
||||
"display_description": "Fix error handling",
|
||||
"old_string": "old code",
|
||||
"new_string": "new code"
|
||||
});
|
||||
|
||||
assert_eq!(tool.still_streaming_ui_text(&input), "Fix error handling");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn still_streaming_ui_text_no_path_or_description() {
|
||||
let tool = EditFileTool;
|
||||
let input = json!({
|
||||
"path": "",
|
||||
"display_description": "",
|
||||
"old_string": "old code",
|
||||
"new_string": "new code"
|
||||
});
|
||||
|
||||
assert_eq!(tool.still_streaming_ui_text(&input), DEFAULT_UI_TEXT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn still_streaming_ui_text_with_null() {
|
||||
let tool = EditFileTool;
|
||||
let input = serde_json::Value::Null;
|
||||
|
||||
assert_eq!(tool.still_streaming_ui_text(&input), DEFAULT_UI_TEXT);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue