chore: upgrade morph to use new model with instruction (#3745)

This commit is contained in:
Michael Neale
2025-08-05 14:43:19 +10:00
committed by GitHub
parent 2f313646e8
commit c6848efc26
4 changed files with 205 additions and 10 deletions
@@ -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>"
);
}
}
+5 -3
View File
@@ -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",
)