-
Notifications
You must be signed in to change notification settings - Fork 4.4k
fix: widen MOIM allowlist to apply fix_conversation repairs for orphaned tool blocks #7684
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
81bcac5
0036b81
698d417
59d4485
ec45e8f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,6 +35,12 @@ pub async fn inject_moim( | |
| let has_unexpected_issues = issues.iter().any(|issue| { | ||
| !issue.contains("Merged consecutive user messages") | ||
| && !issue.contains("Merged consecutive assistant messages") | ||
| && !issue.contains("Merged text content") | ||
| && !issue.contains("Removed orphaned tool response") | ||
| && !issue.contains("Removed orphaned tool request") | ||
| && !issue.contains("Removed empty message") | ||
| && !issue.contains("Removed leading assistant message") | ||
| && !issue.contains("Removed trailing assistant message") | ||
|
wpfleger96 marked this conversation as resolved.
Comment on lines
+42
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Allowlisting Useful? React with 👍 / 👎. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Allowlisting Useful? React with 👍 / 👎.
Comment on lines
+42
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Allowing Useful? React with 👍 / 👎. |
||
| }); | ||
|
|
||
| if has_unexpected_issues { | ||
|
|
@@ -140,4 +146,117 @@ mod tests { | |
| "MOIM should be in message before latest assistant message" | ||
| ); | ||
| } | ||
|
|
||
| // After the allowlist fix: MOIM now applies the fix when fix_conversation detects an | ||
| // orphaned tool request, removing it from the conversation. | ||
| #[tokio::test] | ||
| async fn test_moim_fixes_orphaned_tool_request() { | ||
| let temp_dir = tempfile::tempdir().unwrap(); | ||
| let em = ExtensionManager::new_without_provider(temp_dir.path().to_path_buf()); | ||
| let working_dir = PathBuf::from("/test/dir"); | ||
|
|
||
| let broken_conv = Conversation::new_unvalidated(vec![ | ||
| Message::user().with_text("Do something"), | ||
| Message::assistant() | ||
| .with_text("I'll call a tool") | ||
| .with_tool_request("orphan_tool_1", Ok(CallToolRequestParams::new("some_tool"))), | ||
| ]); | ||
|
|
||
| // Control: fix_conversation alone correctly removes the orphaned tool request. | ||
| let (_, issues) = fix_conversation(Conversation::new_unvalidated( | ||
| broken_conv.messages().clone(), | ||
| )); | ||
| assert!( | ||
| issues | ||
| .iter() | ||
| .any(|i| i.contains("Removed orphaned tool request")), | ||
| "fix_conversation alone should detect the orphan, but issues were: {:?}", | ||
| issues | ||
| ); | ||
|
|
||
| // inject_moim now applies the fix (orphan removal is in the allowlist). | ||
| let result = inject_moim("test-session-id", broken_conv.clone(), &em, &working_dir).await; | ||
| let msgs = result.messages(); | ||
|
|
||
| // The orphaned ToolRequest should be removed. | ||
| let has_orphan = msgs.iter().any(|m| { | ||
| m.content.iter().any(|c| { | ||
| matches!(c, crate::conversation::message::MessageContent::ToolRequest(tr) if tr.id == "orphan_tool_1") | ||
| }) | ||
| }); | ||
| assert!( | ||
| !has_orphan, | ||
| "Orphaned tool request should have been removed by MOIM's fix_conversation" | ||
| ); | ||
|
|
||
| // MOIM should have been injected (conversation was fixed). | ||
| let has_moim = msgs.iter().any(|m| { | ||
| m.content | ||
| .iter() | ||
| .any(|c| c.as_text().is_some_and(|t| t.contains("<info-msg>"))) | ||
| }); | ||
| assert!( | ||
| has_moim, | ||
| "MOIM should have been injected after fixing the orphan" | ||
| ); | ||
| } | ||
|
|
||
| // After the allowlist fix: MOIM now fixes the cancellation scenario — removes the | ||
| // orphaned tool request and empty user message, preventing the Anthropic 400 error. | ||
| #[tokio::test] | ||
| async fn test_moim_fixes_cancellation_orphan() { | ||
| let temp_dir = tempfile::tempdir().unwrap(); | ||
| let em = ExtensionManager::new_without_provider(temp_dir.path().to_path_buf()); | ||
| let working_dir = PathBuf::from("/test/dir"); | ||
|
|
||
| // Simulates what the agent produces after cancellation: | ||
| // - Valid prior exchange | ||
| // - Assistant issues a tool call | ||
| // - Pre-allocated user message was never populated (cancellation fired first) | ||
| let broken_conv = Conversation::new_unvalidated(vec![ | ||
| Message::user().with_text("Search for something"), | ||
| Message::assistant().with_text("I searched and found results"), | ||
| Message::user().with_text("Now do something else"), | ||
| Message::assistant() | ||
| .with_text("I'll call a tool") | ||
| .with_tool_request( | ||
| "cancelled_tool", | ||
| Ok(CallToolRequestParams::new("some_tool")), | ||
| ), | ||
| // Pre-allocated Message::user() never populated due to cancellation. | ||
| Message::user(), | ||
| ]); | ||
|
|
||
| let result = inject_moim("test-session-id", broken_conv.clone(), &em, &working_dir).await; | ||
| let msgs = result.messages(); | ||
|
|
||
| // The orphaned ToolRequest should be removed. | ||
| let has_orphan = msgs.iter().any(|m| { | ||
| m.content.iter().any(|c| { | ||
| matches!(c, crate::conversation::message::MessageContent::ToolRequest(tr) if tr.id == "cancelled_tool") | ||
| }) | ||
| }); | ||
| assert!( | ||
| !has_orphan, | ||
| "Orphaned tool request should have been removed by MOIM's fix_conversation" | ||
| ); | ||
|
|
||
| // The empty user message should be removed. | ||
| let has_empty = msgs.iter().any(|m| m.content.is_empty()); | ||
| assert!( | ||
| !has_empty, | ||
| "Empty user message should have been removed by MOIM's fix_conversation" | ||
| ); | ||
|
|
||
| // MOIM should have been injected. | ||
| let has_moim = msgs.iter().any(|m| { | ||
| m.content | ||
| .iter() | ||
| .any(|c| c.as_text().is_some_and(|t| t.contains("<info-msg>"))) | ||
| }); | ||
| assert!( | ||
| has_moim, | ||
| "MOIM should have been injected after fixing the cancellation orphan" | ||
| ); | ||
| } | ||
| } | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.