mirror of
https://github.com/block/goose.git
synced 2026-07-17 12:56:20 +02:00
chore: upgrade morph to use new model with instruction (#3745)
This commit is contained in:
@@ -36,7 +36,7 @@ export GOOSE_EDITOR_MODEL="claude-3-5-sonnet-20241022"
|
||||
```bash
|
||||
export GOOSE_EDITOR_API_KEY="sk-..."
|
||||
export GOOSE_EDITOR_HOST="https://api.morphllm.com/v1"
|
||||
export GOOSE_EDITOR_MODEL="morph-v0"
|
||||
export GOOSE_EDITOR_MODEL="morph-v3-large"
|
||||
```
|
||||
|
||||
**Relace**
|
||||
|
||||
@@ -86,7 +86,7 @@ pub fn create_editor_model() -> Option<EditorModel> {
|
||||
// Determine which editor to use based on the host
|
||||
if host.contains("relace.run") {
|
||||
Some(EditorModel::Relace(RelaceEditor::new(api_key, host, model)))
|
||||
} else if host.contains("api.morphllm") {
|
||||
} else if host.contains("api.morphllm") || model.contains("morph") {
|
||||
Some(EditorModel::MorphLLM(MorphLLMEditor::new(
|
||||
api_key, host, model,
|
||||
)))
|
||||
|
||||
@@ -19,6 +19,45 @@ impl MorphLLMEditor {
|
||||
model,
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract content between XML tags
|
||||
fn extract_tag_content(text: &str, tag_name: &str) -> Option<String> {
|
||||
let start_tag = format!("<{}>", tag_name);
|
||||
let end_tag = format!("</{}>", tag_name);
|
||||
|
||||
if let (Some(start_pos), Some(end_pos)) = (text.find(&start_tag), text.find(&end_tag)) {
|
||||
if start_pos < end_pos {
|
||||
let content_start = start_pos + start_tag.len();
|
||||
let content = &text[content_start..end_pos];
|
||||
return Some(content.trim().to_string());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn format_user_prompt(original_code: &str, update_snippet: &str) -> String {
|
||||
if let Some(code_content) = Self::extract_tag_content(update_snippet, "code") {
|
||||
// Look for instruction tags which help provide hints
|
||||
if let Some(instruction_content) =
|
||||
Self::extract_tag_content(update_snippet, "instruction")
|
||||
{
|
||||
// Both code and instruction tags found
|
||||
return format!(
|
||||
"<instruction>{}</instruction>\n<code>{}</code>\n<update>{}</update>",
|
||||
instruction_content, original_code, code_content
|
||||
);
|
||||
}
|
||||
// Only code tags found, no instruction
|
||||
return format!(
|
||||
"<code>{}</code>\n<update>{}</update>",
|
||||
original_code, code_content
|
||||
);
|
||||
}
|
||||
format!(
|
||||
"<code>{}</code>\n<update>{}</update>",
|
||||
original_code, update_snippet
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl EditorModelImpl for MorphLLMEditor {
|
||||
@@ -42,11 +81,8 @@ impl EditorModelImpl for MorphLLMEditor {
|
||||
// Create the client
|
||||
let client = Client::new();
|
||||
|
||||
// Format the prompt as specified in the Python example
|
||||
let user_prompt = format!(
|
||||
"<code>{}</code>\n<update>{}</update>",
|
||||
original_code, update_snippet
|
||||
);
|
||||
// Parse update_snippet for <code> and <instruction> tags
|
||||
let user_prompt = Self::format_user_prompt(original_code, update_snippet);
|
||||
|
||||
// Prepare the request body for OpenAI-compatible API
|
||||
let body = json!({
|
||||
@@ -99,6 +135,26 @@ impl EditorModelImpl for MorphLLMEditor {
|
||||
fn get_str_replace_description(&self) -> &'static str {
|
||||
"Use the edit_file to propose an edit to an existing file.
|
||||
This will be read by a less intelligent model, which will quickly apply the edit. You should make it clear what the edit is, while also minimizing the unchanged code you write.
|
||||
|
||||
**IMPORTANT**: in the new_str parameter, you must also provide an `instruction` - a single sentence written in the first person describing what you are going to do for the sketched edit.
|
||||
This instruction helps the less intelligent model understand and apply your edit correctly.
|
||||
|
||||
Examples of good instructions:
|
||||
- I am adding error handling to the user authentication function and removing the old authentication method
|
||||
- The instruction should be specific enough to disambiguate any uncertainty in your edit.
|
||||
|
||||
|
||||
The format for new_str should be like this example:
|
||||
|
||||
<code>
|
||||
new code here you want to add
|
||||
</code>
|
||||
<instruction>
|
||||
adding new code with error handling
|
||||
</instruction>
|
||||
|
||||
provide this to new_str as a single string.
|
||||
|
||||
When writing the edit, you should specify each edit in sequence, with the special comment // ... existing code ... to represent unchanged code in between edited lines.
|
||||
|
||||
For example:
|
||||
@@ -117,3 +173,140 @@ impl EditorModelImpl for MorphLLMEditor {
|
||||
"
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_extract_tag_content_valid() {
|
||||
let text = "<code>fn main() {}</code>";
|
||||
let result = MorphLLMEditor::extract_tag_content(text, "code");
|
||||
assert_eq!(result, Some("fn main() {}".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_tag_content_with_whitespace() {
|
||||
let text = "<instruction> I am adding a print statement </instruction>";
|
||||
let result = MorphLLMEditor::extract_tag_content(text, "instruction");
|
||||
assert_eq!(result, Some("I am adding a print statement".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_tag_content_invalid_order() {
|
||||
let text = "</code>Invalid<code>";
|
||||
let result = MorphLLMEditor::extract_tag_content(text, "code");
|
||||
assert_eq!(result, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_tag_content_missing_end_tag() {
|
||||
let text = "<code>fn main() {}";
|
||||
let result = MorphLLMEditor::extract_tag_content(text, "code");
|
||||
assert_eq!(result, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_tag_content_missing_start_tag() {
|
||||
let text = "fn main() {}</code>";
|
||||
let result = MorphLLMEditor::extract_tag_content(text, "code");
|
||||
assert_eq!(result, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_tag_content_nested_tags() {
|
||||
let text = "<code>fn main() { <code>nested</code> }</code>";
|
||||
let result = MorphLLMEditor::extract_tag_content(text, "code");
|
||||
assert_eq!(result, Some("fn main() { <code>nested".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_user_prompt_no_tags() {
|
||||
let original_code = "fn main() {}";
|
||||
let update_snippet = "Add error handling";
|
||||
let result = MorphLLMEditor::format_user_prompt(original_code, update_snippet);
|
||||
assert_eq!(
|
||||
result,
|
||||
"<code>fn main() {}</code>\n<update>Add error handling</update>"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_user_prompt_with_code_tags_only() {
|
||||
let original_code = "fn main() {}";
|
||||
let update_snippet = "<code>fn main() { println!(\"Hello\"); }</code>";
|
||||
let result = MorphLLMEditor::format_user_prompt(original_code, update_snippet);
|
||||
assert_eq!(
|
||||
result,
|
||||
"<code>fn main() {}</code>\n<update>fn main() { println!(\"Hello\"); }</update>"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_user_prompt_with_both_tags() {
|
||||
let original_code = "fn main() {}";
|
||||
let update_snippet = "<code>fn main() { println!(\"Hello\"); }</code><instruction>I am adding a print statement</instruction>";
|
||||
let result = MorphLLMEditor::format_user_prompt(original_code, update_snippet);
|
||||
assert_eq!(
|
||||
result,
|
||||
"<instruction>I am adding a print statement</instruction>\n<code>fn main() {}</code>\n<update>fn main() { println!(\"Hello\"); }</update>"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_user_prompt_with_whitespace() {
|
||||
let original_code = "fn main() {}";
|
||||
let update_snippet = "<code> fn main() { println!(\"Hello\"); } </code><instruction> I am adding a print statement </instruction>";
|
||||
let result = MorphLLMEditor::format_user_prompt(original_code, update_snippet);
|
||||
assert_eq!(
|
||||
result,
|
||||
"<instruction>I am adding a print statement</instruction>\n<code>fn main() {}</code>\n<update>fn main() { println!(\"Hello\"); }</update>"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_user_prompt_invalid_code_tags() {
|
||||
let original_code = "fn main() {}";
|
||||
let update_snippet = "</code>Invalid<code>";
|
||||
let result = MorphLLMEditor::format_user_prompt(original_code, update_snippet);
|
||||
assert_eq!(
|
||||
result,
|
||||
"<code>fn main() {}</code>\n<update></code>Invalid<code></update>"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_user_prompt_invalid_instruction_tags() {
|
||||
let original_code = "fn main() {}";
|
||||
let update_snippet =
|
||||
"<code>fn main() { println!(\"Hello\"); }</code></instruction>Invalid<instruction>";
|
||||
let result = MorphLLMEditor::format_user_prompt(original_code, update_snippet);
|
||||
assert_eq!(
|
||||
result,
|
||||
"<code>fn main() {}</code>\n<update>fn main() { println!(\"Hello\"); }</update>"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_user_prompt_nested_tags() {
|
||||
let original_code = "fn main() {}";
|
||||
let update_snippet = "<code>fn main() { <code>nested</code> }</code>";
|
||||
let result = MorphLLMEditor::format_user_prompt(original_code, update_snippet);
|
||||
// Should use the first occurrence of <code> and find its matching </code>
|
||||
assert_eq!(
|
||||
result,
|
||||
"<code>fn main() {}</code>\n<update>fn main() { <code>nested</update>"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_user_prompt_tags_in_different_order() {
|
||||
let original_code = "fn main() {}";
|
||||
let update_snippet = "<instruction>I am adding a print statement</instruction><code>fn main() { println!(\"Hello\"); }</code>";
|
||||
let result = MorphLLMEditor::format_user_prompt(original_code, update_snippet);
|
||||
assert_eq!(
|
||||
result,
|
||||
"<instruction>I am adding a print statement</instruction>\n<code>fn main() {}</code>\n<update>fn main() { println!(\"Hello\"); }</update>"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,11 +270,13 @@ impl DeveloperRouter {
|
||||
|
||||
To use the write command, you must specify `file_text` which will become the new content of the file. Be careful with
|
||||
existing files! This is a full overwrite, so you must include everything - not just sections you are modifying.
|
||||
|
||||
To use the edit_file command, you must specify both `old_str` and `new_str` - {}.
|
||||
|
||||
|
||||
To use the insert command, you must specify both `insert_line` (the line number after which to insert, 0 for beginning)
|
||||
and `new_str` (the text to insert).
|
||||
|
||||
To use the edit_file command, you must specify both `old_str` and `new_str`
|
||||
{}
|
||||
|
||||
"#, editor.get_str_replace_description()},
|
||||
"edit_file",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user