mirror of
https://github.com/zed-industries/zed.git
synced 2026-10-19 19:15:53 +00:00
edit tool: Support stringified mode parameter (#55498)
Some checks are pending
Congratsbot / check-author (push) Waiting to run
Congratsbot / congrats (push) Blocked by required conditions
deploy_nightly_docs / deploy_docs (push) Waiting to run
run_tests / check_wasm (push) Blocked by required conditions
run_tests / orchestrate (push) Waiting to run
run_tests / check_style (push) Waiting to run
run_tests / extension_tests (push) Blocked by required conditions
run_tests / clippy_windows (push) Blocked by required conditions
run_tests / clippy_linux (push) Blocked by required conditions
run_tests / clippy_mac (push) Blocked by required conditions
run_tests / clippy_mac_x86_64 (push) Blocked by required conditions
run_tests / run_tests_windows (push) Blocked by required conditions
run_tests / run_tests_linux (push) Blocked by required conditions
run_tests / run_tests_mac (push) Blocked by required conditions
run_tests / doctests (push) Blocked by required conditions
run_tests / check_workspace_binaries (push) Blocked by required conditions
run_tests / build_visual_tests_binary (push) Blocked by required conditions
run_tests / check_dependencies (push) Blocked by required conditions
run_tests / check_docs (push) Blocked by required conditions
run_tests / check_licenses (push) Blocked by required conditions
run_tests / check_scripts (push) Blocked by required conditions
run_tests / check_postgres_and_protobuf_migrations (push) Blocked by required conditions
run_tests / tests_pass (push) Blocked by required conditions
Some checks are pending
Congratsbot / check-author (push) Waiting to run
Congratsbot / congrats (push) Blocked by required conditions
deploy_nightly_docs / deploy_docs (push) Waiting to run
run_tests / check_wasm (push) Blocked by required conditions
run_tests / orchestrate (push) Waiting to run
run_tests / check_style (push) Waiting to run
run_tests / extension_tests (push) Blocked by required conditions
run_tests / clippy_windows (push) Blocked by required conditions
run_tests / clippy_linux (push) Blocked by required conditions
run_tests / clippy_mac (push) Blocked by required conditions
run_tests / clippy_mac_x86_64 (push) Blocked by required conditions
run_tests / run_tests_windows (push) Blocked by required conditions
run_tests / run_tests_linux (push) Blocked by required conditions
run_tests / run_tests_mac (push) Blocked by required conditions
run_tests / doctests (push) Blocked by required conditions
run_tests / check_workspace_binaries (push) Blocked by required conditions
run_tests / build_visual_tests_binary (push) Blocked by required conditions
run_tests / check_dependencies (push) Blocked by required conditions
run_tests / check_docs (push) Blocked by required conditions
run_tests / check_licenses (push) Blocked by required conditions
run_tests / check_scripts (push) Blocked by required conditions
run_tests / check_postgres_and_protobuf_migrations (push) Blocked by required conditions
run_tests / tests_pass (push) Blocked by required conditions
Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A
This commit is contained in:
parent
9155bf4e17
commit
db5a9be2ff
1 changed files with 83 additions and 23 deletions
|
|
@ -84,6 +84,7 @@ pub struct StreamingEditFileToolInput {
|
|||
/// - 'edit': Make granular edits to an existing file. Requires 'edits' field.
|
||||
///
|
||||
/// When a file already exists or you just created it, prefer editing it as opposed to recreating it from scratch.
|
||||
#[serde(deserialize_with = "deserialize_maybe_stringified")]
|
||||
pub mode: StreamingEditFileMode,
|
||||
|
||||
/// The complete content for the new file (required for 'write' mode).
|
||||
|
|
@ -96,7 +97,7 @@ pub struct StreamingEditFileToolInput {
|
|||
#[serde(
|
||||
default,
|
||||
skip_serializing_if = "Option::is_none",
|
||||
deserialize_with = "deserialize_optional_vec_or_json_string"
|
||||
deserialize_with = "deserialize_maybe_stringified"
|
||||
)]
|
||||
pub edits: Option<Vec<Edit>>,
|
||||
}
|
||||
|
|
@ -133,11 +134,11 @@ struct StreamingEditFileToolPartialInput {
|
|||
display_description: Option<String>,
|
||||
#[serde(default)]
|
||||
path: Option<String>,
|
||||
#[serde(default)]
|
||||
#[serde(default, deserialize_with = "deserialize_maybe_stringified")]
|
||||
mode: Option<StreamingEditFileMode>,
|
||||
#[serde(default)]
|
||||
content: Option<String>,
|
||||
#[serde(default, deserialize_with = "deserialize_optional_vec_or_json_string")]
|
||||
#[serde(default, deserialize_with = "deserialize_maybe_stringified")]
|
||||
edits: Option<Vec<PartialEdit>>,
|
||||
}
|
||||
|
||||
|
|
@ -149,30 +150,23 @@ pub struct PartialEdit {
|
|||
pub new_text: Option<String>,
|
||||
}
|
||||
|
||||
/// Sometimes the model responds with a stringified JSON array of edits (`"[...]"`) instead of a regular array (`[...]`)
|
||||
fn deserialize_optional_vec_or_json_string<'de, T, D>(
|
||||
deserializer: D,
|
||||
) -> Result<Option<Vec<T>>, D::Error>
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum ValueOrJsonString<T> {
|
||||
Value(T),
|
||||
String(String),
|
||||
}
|
||||
|
||||
fn deserialize_maybe_stringified<'de, T, D>(deserializer: D) -> Result<T, D::Error>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum VecOrJsonString<T> {
|
||||
Vec(Vec<T>),
|
||||
String(String),
|
||||
}
|
||||
|
||||
let value = Option::<VecOrJsonString<T>>::deserialize(deserializer)?;
|
||||
match value {
|
||||
None => Ok(None),
|
||||
Some(VecOrJsonString::Vec(items)) => Ok(Some(items)),
|
||||
Some(VecOrJsonString::String(string)) => serde_json::from_str::<Vec<T>>(&string)
|
||||
.map(Some)
|
||||
.map_err(|error| {
|
||||
D::Error::custom(format!("failed to parse stringified edits array: {error}"))
|
||||
}),
|
||||
match ValueOrJsonString::<T>::deserialize(deserializer)? {
|
||||
ValueOrJsonString::Value(value) => Ok(value),
|
||||
ValueOrJsonString::String(string) => serde_json::from_str::<T>(&string).map_err(|error| {
|
||||
D::Error::custom(format!("failed to parse stringified value: {error}"))
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4180,6 +4174,72 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_input_deserializes_double_encoded_fields() {
|
||||
let input = serde_json::from_value::<StreamingEditFileToolInput>(json!({
|
||||
"display_description": "Edit",
|
||||
"path": "root/file.txt",
|
||||
"mode": "\"edit\"",
|
||||
"edits": "[{\"old_text\": \"hello\\nworld\", \"new_text\": \"HELLO\\nWORLD\"}]"
|
||||
}))
|
||||
.expect("input should deserialize");
|
||||
|
||||
assert!(matches!(input.mode, StreamingEditFileMode::Edit));
|
||||
let edits = input.edits.expect("edits should deserialize");
|
||||
assert_eq!(edits.len(), 1);
|
||||
assert_eq!(edits[0].old_text, "hello\nworld");
|
||||
assert_eq!(edits[0].new_text, "HELLO\nWORLD");
|
||||
|
||||
let input = serde_json::from_value::<StreamingEditFileToolInput>(json!({
|
||||
"display_description": "Edit",
|
||||
"path": "root/file.txt",
|
||||
"mode": "\"edit\""
|
||||
}))
|
||||
.expect("input should deserialize");
|
||||
assert!(input.edits.is_none());
|
||||
|
||||
let input = serde_json::from_value::<StreamingEditFileToolInput>(json!({
|
||||
"display_description": "Edit",
|
||||
"path": "root/file.txt",
|
||||
"mode": "\"edit\"",
|
||||
"edits": null
|
||||
}))
|
||||
.expect("input should deserialize");
|
||||
assert!(input.edits.is_none());
|
||||
|
||||
let input = serde_json::from_value::<StreamingEditFileToolPartialInput>(json!({
|
||||
"display_description": "Edit",
|
||||
"path": "root/file.txt",
|
||||
"mode": "\"edit\"",
|
||||
"edits": "[{\"old_text\": \"hello\\nworld\", \"new_text\": \"HELLO\\nWORLD\"}]"
|
||||
}))
|
||||
.expect("input should deserialize");
|
||||
|
||||
assert!(matches!(input.mode, Some(StreamingEditFileMode::Edit)));
|
||||
let edits = input.edits.expect("edits should deserialize");
|
||||
assert_eq!(edits.len(), 1);
|
||||
assert_eq!(edits[0].old_text.as_deref(), Some("hello\nworld"));
|
||||
assert_eq!(edits[0].new_text.as_deref(), Some("HELLO\nWORLD"));
|
||||
|
||||
let input = serde_json::from_value::<StreamingEditFileToolPartialInput>(json!({
|
||||
"display_description": "Edit",
|
||||
"path": "root/file.txt"
|
||||
}))
|
||||
.expect("input should deserialize");
|
||||
assert!(input.mode.is_none());
|
||||
assert!(input.edits.is_none());
|
||||
|
||||
let input = serde_json::from_value::<StreamingEditFileToolPartialInput>(json!({
|
||||
"display_description": "Edit",
|
||||
"path": "root/file.txt",
|
||||
"mode": null,
|
||||
"edits": null
|
||||
}))
|
||||
.expect("input should deserialize");
|
||||
assert!(input.mode.is_none());
|
||||
assert!(input.edits.is_none());
|
||||
}
|
||||
|
||||
async fn setup_test_with_fs(
|
||||
cx: &mut TestAppContext,
|
||||
fs: Arc<project::FakeFs>,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue