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
|
```bash
|
||||||
export GOOSE_EDITOR_API_KEY="sk-..."
|
export GOOSE_EDITOR_API_KEY="sk-..."
|
||||||
export GOOSE_EDITOR_HOST="https://api.morphllm.com/v1"
|
export GOOSE_EDITOR_HOST="https://api.morphllm.com/v1"
|
||||||
export GOOSE_EDITOR_MODEL="morph-v0"
|
export GOOSE_EDITOR_MODEL="morph-v3-large"
|
||||||
```
|
```
|
||||||
|
|
||||||
**Relace**
|
**Relace**
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ pub fn create_editor_model() -> Option<EditorModel> {
|
|||||||
// Determine which editor to use based on the host
|
// Determine which editor to use based on the host
|
||||||
if host.contains("relace.run") {
|
if host.contains("relace.run") {
|
||||||
Some(EditorModel::Relace(RelaceEditor::new(api_key, host, model)))
|
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(
|
Some(EditorModel::MorphLLM(MorphLLMEditor::new(
|
||||||
api_key, host, model,
|
api_key, host, model,
|
||||||
)))
|
)))
|
||||||
|
|||||||
@@ -19,6 +19,45 @@ impl MorphLLMEditor {
|
|||||||
model,
|
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 {
|
impl EditorModelImpl for MorphLLMEditor {
|
||||||
@@ -42,11 +81,8 @@ impl EditorModelImpl for MorphLLMEditor {
|
|||||||
// Create the client
|
// Create the client
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
|
|
||||||
// Format the prompt as specified in the Python example
|
// Parse update_snippet for <code> and <instruction> tags
|
||||||
let user_prompt = format!(
|
let user_prompt = Self::format_user_prompt(original_code, update_snippet);
|
||||||
"<code>{}</code>\n<update>{}</update>",
|
|
||||||
original_code, update_snippet
|
|
||||||
);
|
|
||||||
|
|
||||||
// Prepare the request body for OpenAI-compatible API
|
// Prepare the request body for OpenAI-compatible API
|
||||||
let body = json!({
|
let body = json!({
|
||||||
@@ -99,6 +135,26 @@ impl EditorModelImpl for MorphLLMEditor {
|
|||||||
fn get_str_replace_description(&self) -> &'static str {
|
fn get_str_replace_description(&self) -> &'static str {
|
||||||
"Use the edit_file to propose an edit to an existing file.
|
"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.
|
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.
|
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:
|
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
|
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.
|
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)
|
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).
|
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()},
|
"#, editor.get_str_replace_description()},
|
||||||
"edit_file",
|
"edit_file",
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user