-
Notifications
You must be signed in to change notification settings - Fork 7.6k
Simplify tool implemetations #4160
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
Conversation
- Handles errors in function/tool/cmd calls using `Result<String, FunctionCallError>` instead of wrapping outputs in `ResponseInputItem`. - Removes unnecessary `ResponseInputItem` conversions and related From impls. - Refactors exec handling, sandbox error, patch, plan/mcp tool call, and tool output mapping to propagate errors via Result. - Demotes exec_command result_into_payload; now directly uses text output or error
| @@ -0,0 +1,7 @@ | |||
| use thiserror::Error; | |||
|
|
|||
| #[derive(Debug, Error, PartialEq)] | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Plan to put more function-tool related code here.
| /// of sandbox, because the user explicitly approved it. This is the | ||
| /// result to use with the `shell` function call that contained `apply_patch`. | ||
| Output(ResponseInputItem), | ||
| Output(Result<String, FunctionCallError>), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the part that's I'm not sure is the cleanest.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I definitely prefer this than the pub success: Option<bool>,
# Conflicts: # codex-rs/core/src/codex.rs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting
Move conversion of MCP tool call results to structured output (ResponseInputItem) into handle_mcp_tool_call. Simplify error handling for invalid arguments and return errors as structured responses. Remove in-place stringification of results—conversion now occurs in higher layers.
| /// of sandbox, because the user explicitly approved it. This is the | ||
| /// result to use with the `shell` function call that contained `apply_patch`. | ||
| Output(ResponseInputItem), | ||
| Output(Result<String, FunctionCallError>), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I definitely prefer this than the pub success: Option<bool>,
| } | ||
| .into(), | ||
| SafetyCheck::Reject { reason } => InternalApplyPatchInvocation::Output(Err( | ||
| FunctionCallError::RespondToModel(format!("patch rejected: {reason}")), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PatchRejected could be a variation of the FunctionCallError
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Frankly this function should itself be returning Result<... > today the return type is strange in that it encapsulates an error deep inside the return value.
But I don't want to poke apply_patch internals too much yet.
| content, | ||
| success: Some(true), | ||
| }, | ||
| Err(FunctionCallError::RespondToModel(msg)) => FunctionCallOutputPayload { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can implement a From to make this cleaner as I suspect we may need this somewhere else and we will grow FunctionCallError
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't want to make API types dependent on the agent loop types. Feels like the wrong dependency direction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to split client into a separate crate one day.
| success: Some(false), | ||
| }, | ||
| }; | ||
| return Err(FunctionCallError::RespondToModel(format!( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another type of error I guess ?
You will have multiple of them but would be cool to have a few standard one and then you can create a generic FunctionCallError::RawRespondToModel(String) for specific use cases
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why another type?
| @@ -2414,15 +2439,10 @@ async fn handle_function_call( | |||
| name: String, | |||
| arguments: String, | |||
| call_id: String, | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, always print the error with the Debug trait (i.e. {err:?})
Otherwise, if the error implement the Display trait you don't see the exact type of the error. This is mainly true for thiserror where if you have this:
pub enum FunctionCallError {
#[error("{0}")]
RespondToModel(String),
}
...
FunctionCallError::RespondToModel("An error".to_string())
You would see "An error" with the Display print but RespondToModel("An error") with the debug one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What was this a comment for? Seems not to fit the line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm it got shifted. Basically everywhere where you do {e}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will fix, good point!
| } | ||
| }; | ||
| let args: ApplyPatchToolArgs = serde_json::from_str(&arguments).map_err(|e| { | ||
| FunctionCallError::RespondToModel(format!( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you have more specific error, you can use the #from in the error definition and just use ? here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The extra formatted message part stopped me failed to parse function arguments from doing a generic from serde conversion.
Changed SerializedUnifiedExecResult to own the output field (String) instead of borrowing (&str) to simplify serialization. Removed obsolete comments in related code.
|
For the record: remaining points were discussed on Slack |
- Use debug formatting ({err:?}, {output:?}, etc.) for error messages in function call handling and execution paths for more informative diagnostics.
Use Result<String, FunctionCallError> for all tool handling code and rely on error propagation instead of creating failed items everywhere.