-
Notifications
You must be signed in to change notification settings - Fork 108
feat: Complete dangling tool requests to avoid API errors #840
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
hadley
left a comment
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 approach of providing a generic "tool request failed" result seems perfect to me!
Co-authored-by: Hadley Wickham <[email protected]>
R/chat.R
Outdated
| tool_results <- lapply(tool_requests, function(req) { | ||
| ContentToolResult( | ||
| error = "Chat ended before the tool could be invoked.", | ||
| request = req | ||
| ) | ||
| }) | ||
| self$add_turn( | ||
| tool_results_as_turn(tool_results), | ||
| AssistantTurn("Acknowledged", tokens = c(0, 0, 0)) | ||
| ) |
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'm in favor of relaxing the user-assistant paired turns constraint, but I also understand that you probably don't want to do that in this release and that it's better to stay internally consistent.
I think this approach is clean and simple except for the faked assistant turn. In my initial implementation, which I'd recommend over adding an assistant turn, was to attach the tool results to the incoming user message.
That approach introduces a dependency on #735 (in the sense that it fixes a bug with tool results and text content being sent out of order), but that PR is almost done. I'll add a commit that takes this PR in that direction so you can see how that feels (and we can revert it if you prefer this approach).
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.
| out <- paste0(prefix, "input=") | ||
|
|
||
| if (tokens[[3]] > 0) { | ||
| if (!is.na(tokens[[3]]) && tokens[[3]] > 0) { |
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.
Small note here, but now that tokens are structured, could this code use names instead of indices? Also, very minor nit, but I think res would be better than out; on my first read I thought out was for output tokens.
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.
Tokens aren't structured in Turn objects yet 😬
| #' will be used. | ||
| chat = function(..., echo = NULL) { | ||
| turn <- user_turn(...) | ||
| finish_tools <- private$complete_dangling_tool_requests() |
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 much better!
Fixes #459 by completing incomplete tool requests with an empty tool result.
The implementation focuses on dangling tool requests only. In other words, when calling
$chat()or$stream()or related async function viaChat, we check if the last message is an assistant message with unanswered tool requests and we add a new user turn with the empty tool results before processing the new input.Here's a simple example with a tool to get a random number:
If we pretend the chat was truncated, i.e. the user interrupted the action before we could send the tool result back to the LLM, the turns would look something like this:
Currently, trying to pick up the conversation at this point would result in an API error because the tool request wasn't completed. With this PR, however, the chat can be continued normally.
Inspecting the chat shows the new user turn with the empty tool request telling the LLM that the chat was interrupted and the tool wasn't invoked.
Note that we insert a separate user message primarily because of the bug discussed in #735: inserting tool results into the new user message hits the incorrect behavior described in the linked comment and doesn't work with OpenAI (and a few other providers). I also think conceptually it's better to have this stored as separate user messages, but it also wouldn't be hard to inject to tool results into the new actual user message after #735.
TODO