Skip to content
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

fix: tool api call broken when user answer no to when asked for confirmation #371

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

jrmi
Copy link
Contributor

@jrmi jrmi commented Dec 30, 2024

This MR fixes a few bug regarding tool call in tool format:

  • When the user choose answer on when asked for confirmation
  • When user edit the file before saving
  • When the user uses Ctrl+c to interrupt gptme

The underlying problems where that sometimes the call_id wasn't set in certain situations or multiple messages where returned by the tooluse.execute call but all were the response from the tool so we needed to merge them.


Important

Fixes tool API call issues by ensuring call_id is set and merging tool responses, with updates to message handling and tests.

  • Behavior:
    • Fixes tool API call issues when users answer "no" to confirmation, edit files before saving, or interrupt with Ctrl+c.
    • Ensures call_id is set correctly in execute_msg() in tools/__init__.py and ToolUse.execute() in tools/base.py.
    • Merges multiple tool responses with the same call_id in _merge_tool_results_with_same_call_id() in llm_openai.py and llm_anthropic.py.
  • Functions:
    • Updates _handle_tools() in llm_anthropic.py and llm_openai.py to handle user role messages with call_id.
    • Modifies _transform_system_messages() in llm_anthropic.py to merge consecutive user messages with the same call_id.
  • Tests:
    • Adds test cases in test_llm_anthropic.py and test_llm_openai.py to verify message conversion and tool handling with call_id.

This description was created by Ellipsis for 2d25236. It will automatically update as commits are pushed.

@jrmi jrmi changed the title Fix tool api call broken when no tool answer fix: tool api call broken when user answer no to confirmation Dec 30, 2024
@@ -235,9 +235,6 @@ def step(
if msg_response:
yield msg_response.replace(quiet=True)
yield from execute_msg(msg_response, confirm)
except KeyboardInterrupt:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now handled in the execute_msg as we need the call_id in the message response.

@jrmi jrmi marked this pull request as ready for review December 31, 2024 10:05
Copy link
Contributor

@ellipsis-dev ellipsis-dev bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Looks good to me! Reviewed everything up to 2d25236 in 1 minute and 12 seconds

More details
  • Looked at 223 lines of code in 7 files
  • Skipped 0 files when reviewing.
  • Skipped posting 2 drafted comments based on config settings.
1. gptme/llm/llm_anthropic.py:203
  • Draft comment:
    The change from message["role"] == "system" to message["role"] == "user" seems incorrect. The original condition was likely correct as it was handling system messages with call_id. Please verify the intent of this change.
  • Reason this comment was not posted:
    Comment did not seem useful.
2. gptme/llm/llm_openai.py:287
  • Draft comment:
    The function _merge_tool_results_with_same_call_id is defined but not used in this file. Consider integrating it where necessary or removing it if not needed.
  • Reason this comment was not posted:
    Comment looked like it was already resolved.

Workflow ID: wflow_Iq0Ct3a5VFKQWQwU


You can customize Ellipsis with 👍 / 👎 feedback, review rules, user-specific overrides, quiet mode, and more.

@jrmi jrmi changed the title fix: tool api call broken when user answer no to confirmation fix: tool api call broken when user answer no to when asked for confirmation Dec 31, 2024
@jrmi jrmi force-pushed the fix-tool-api-call-broken-when-no-tool-answer branch from 2d25236 to ac85af3 Compare January 4, 2025 15:18
@@ -235,9 +235,6 @@ def step(
if msg_response:
yield msg_response.replace(quiet=True)
yield from execute_msg(msg_response, confirm)
except KeyboardInterrupt:
clear_interruptible()
yield Message("system", "Interrupted")
finally:
clear_interruptible()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this is correct, I think the clear_interruptible is no longer called?

I think the finally clause might not run for the generator until the next() call.

Copy link
Owner

@ErikBjare ErikBjare Jan 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevermind, it's there in the except clause for that very reason, but no longer needed.

Not quite clear to me if the new behavior is correct though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm no it's probably not the same. Let me find something.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the MR. To stay on the safe side, I've called clear_interruptible in the execute_msg function. Notice the break in the except clause. Before it was still calling subsequent tools if any after a user interruption 🤦

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also call clear_interruptible inside the handle_keyboard_interrupt itself just before raising the KeyboardInterrupt exception, what do you think?

@jrmi jrmi force-pushed the fix-tool-api-call-broken-when-no-tool-answer branch 2 times, most recently from f09caaf to 871c76e Compare January 4, 2025 16:39
messages_new
and messages_new[-1].role == "user"
and message.role == "user"
and message.call_id == messages_new[-1].call_id
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this condition needed?

Copy link
Contributor Author

@jrmi jrmi Jan 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When using the function call API, multiple calls may occur within a single answer (parallel calls). To handle this properly, ensure there is one tool response per tool call. By verifying that the call_id is distinct, we can ensure messages are not merged unless they originate from the same function call.

clear_interruptible()
yield Message(
"system",
"User hit Ctrl-c to interrupt the process",
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kinda want to make these shorter

Suggested change
"User hit Ctrl-c to interrupt the process",
"Interrupted by user",

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sort of what I did in #384

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the message (and used a constant to have the same value everywhere it's needed.)

@jrmi jrmi force-pushed the fix-tool-api-call-broken-when-no-tool-answer branch from 871c76e to 420b2d9 Compare January 5, 2025 18:08
@jrmi jrmi requested a review from ErikBjare January 5, 2025 18:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants