Skip to content

Fix tool execution bug #327

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

Merged
merged 4 commits into from
May 7, 2025
Merged

Fix tool execution bug #327

merged 4 commits into from
May 7, 2025

Conversation

jiwei-aipolabs
Copy link
Contributor

@jiwei-aipolabs jiwei-aipolabs commented May 6, 2025

🏷️ Ticket

https://www.notion.so/duplicate-chat-call-after-tool-execution-1e38378d6a4780f48cb5dca16e11ec21?pvs=4

📝 Description

fix bug duplicate /chat api request
improve ux(add stop button for agent playground)

🎥 Demo (if applicable)

20250506192128-ACI.DEV.Platform-.-1.mp4

📸 Screenshots (if applicable)

✅ Checklist

  • I have signed the Contributor License Agreement (CLA) and read the contributing guide (required)
  • I have linked this PR to an issue or a ticket (required)
  • I have updated the documentation related to my change if needed
  • I have updated the tests accordingly (required for a bug fix or a new feature)
  • All checks on CI passed

Summary by CodeRabbit

  • New Features

    • Added support for tool execution within chat messages, including error handling and result management.
    • Introduced a stop button to interrupt streaming or submitted chat input, enhancing user control during interactions.
  • Improvements

    • Updated chat input UI with modularized send/stop buttons and improved input validation.
    • Enhanced message and tool invocation components to handle additional parameters for account linking and API keys.
    • Streamlined tool result handling by delegating responsibilities to child components.
  • Chores

    • Added a new dependency to support React hooks.

Copy link

vercel bot commented May 6, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
aci-dev-portal ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 6, 2025 1:54pm

Copy link
Contributor

coderabbitai bot commented May 6, 2025

Walkthrough

The changes introduce a new useToolExecution hook for handling tool invocations asynchronously and refactor the playground chat components to use this hook. Several components (FunctionCalling, Messages, message, and page) are updated to pass and handle new props related to tool execution and API keys. The chat input UI is also modularized and improved.

Changes

File(s) Change Summary
frontend/package.json Added the usehooks-ts dependency at version ^3.1.1.
frontend/src/app/playground/chat-input.tsx Refactored ChatInput to use memoized callbacks and modular button components. Added new optional props (stop, setMessages). Improved input validation, submission control, and UI with a stop button for streaming state.
frontend/src/app/playground/function-calling.tsx Enhanced FunctionCalling to use the new useToolExecution hook. Added props: linkedAccountOwnerId, apiKey, and addToolResult. Displays error messages from tool execution.
frontend/src/app/playground/message.tsx Extended PurePreviewMessage props to include linkedAccountOwnerId, apiKey, and addToolResult. Passed these to FunctionCalling.
frontend/src/app/playground/messages.tsx Extended MessagesProps and PureMessages to include linkedAccountOwnerId, apiKey, and addToolResult, passing them to each PreviewMessage.
frontend/src/app/playground/page.tsx Removed onToolCall logic from useChat configuration. Introduced handleAddToolResult function. Passed new tool execution props to Messages and updated ChatInput with setMessages and stop.
frontend/src/hooks/use-tool-execution.ts Introduced new useToolExecution hook to manage tool invocation execution, error handling, and result reporting. Exports useToolExecution and its props type.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ChatInput
    participant Messages
    participant Message
    participant FunctionCalling
    participant useToolExecution
    participant executeFunctionAPI

    User->>ChatInput: Submit message / trigger tool call
    ChatInput->>Messages: Update messages
    Messages->>Message: Render message parts
    Message->>FunctionCalling: Render tool-invocation part
    FunctionCalling->>useToolExecution: Initialize with toolInvocation, API key, etc.
    useToolExecution->>executeFunctionAPI: Call tool with args and credentials
    executeFunctionAPI-->>useToolExecution: Return result or error
    useToolExecution->>FunctionCalling: Provide error state (if any)
    useToolExecution->>Messages: addToolResult callback with result
Loading

Suggested reviewers

  • thisisfixer
  • alex-aipolabs

Poem

In the meadow of code, new hooks take flight,
Tool calls now handled with error insight.
Buttons refactored, the UI refined,
Messages and keys now neatly aligned.
With a hop and a skip, this rabbit declares:
The playground’s improved—let’s see how it fares!
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🔭 Outside diff range comments (2)
frontend/src/app/playground/message.tsx (1)

127-136: 💡 Verification agent

🧩 Analysis chain

Consider updating the memo comparison function.

The memo implementation doesn't compare the new props (linkedAccountOwnerId, apiKey, and addToolResult). If these props could change between renders, consider updating the comparison function.


🏁 Script executed:

#!/bin/bash
# Check if linkedAccountOwnerId or apiKey could change during component lifecycle
# by looking for state updates or context changes related to these values

echo "Searching for code that might update linkedAccountOwnerId or apiKey:"
rg -A 2 -B 2 "setState.*linkedAccountOwnerId|setState.*apiKey|useEffect.*linkedAccountOwnerId|useEffect.*apiKey" --type=tsx --type=ts

echo "Searching for components that might provide these values via context:"
rg -A 2 -B 2 "Provider.*value.*linkedAccountOwnerId|Provider.*value.*apiKey" --type=tsx --type=ts

Length of output: 587


I’m not seeing those props defined in the comparator’s file—let’s confirm whether PurePreviewMessage actually receives linkedAccountOwnerId, apiKey, or addToolResult. If it doesn’t, the original suggestion may be unnecessary. Let’s extract the component signature and look for those props:


🏁 Script executed:

#!/bin/bash
# Locate PurePreviewMessage and any occurrences of the queried props
rg -n "PurePreviewMessage" -C3 frontend/src/app/playground/message.tsx
rg -n "linkedAccountOwnerId|apiKey|addToolResult" -C3 frontend/src/app/playground/message.tsx

Length of output: 1400


Update the memo comparator to include all props

The PreviewMessage memo only checks isLoading, message.id, and message.parts, but PurePreviewMessage also takes linkedAccountOwnerId, apiKey, and addToolResult. If any of those change between renders, the component will be skipped incorrectly.

Please update frontend/src/app/playground/message.tsx around lines 127–136:

 export const PreviewMessage = memo(
   PurePreviewMessage,
-  (prevProps, nextProps) => {
-    if (prevProps.isLoading !== nextProps.isLoading) return false;
-    if (prevProps.message.id !== nextProps.message.id) return false;
-    if (!equal(prevProps.message.parts, nextProps.message.parts)) return false;
+  (prev, next) => {
+    if (prev.isLoading !== next.isLoading) return false;
+    if (prev.message.id !== next.message.id) return false;
+    if (!equal(prev.message.parts, next.message.parts)) return false;
+    if (prev.linkedAccountOwnerId !== next.linkedAccountOwnerId) return false;
+    if (prev.apiKey !== next.apiKey) return false;
+    if (prev.addToolResult !== next.addToolResult) return false;
 
     return true;
   },
 );
  • Compare linkedAccountOwnerId and apiKey by value.
  • Compare the callback addToolResult by reference.
frontend/src/app/playground/messages.tsx (1)

55-62: 🛠️ Refactor suggestion

Potential issue in memo comparison function.

The memo implementation has two concerns:

  1. It doesn't compare the new props (linkedAccountOwnerId, apiKey, and addToolResult)
  2. The condition if (prevProps.status && nextProps.status) return false; will always return false (triggering re-renders) when both statuses are truthy, which may be inefficient
export const Messages = memo(PureMessages, (prevProps, nextProps) => {
  if (prevProps.status !== nextProps.status) return false;
-  if (prevProps.status && nextProps.status) return false;
  if (prevProps.messages.length !== nextProps.messages.length) return false;
  if (!equal(prevProps.messages, nextProps.messages)) return false;
+  // Only re-render if linkedAccountOwnerId or apiKey changes
+  if (prevProps.linkedAccountOwnerId !== nextProps.linkedAccountOwnerId) return false;
+  if (prevProps.apiKey !== nextProps.apiKey) return false;
+  // addToolResult is likely a stable function reference, so skipping that comparison

  return true;
});
🧹 Nitpick comments (6)
frontend/src/app/playground/function-calling.tsx (1)

21-23: Added error handling.

Good addition of error handling, though the error message display could be improved.

  if (error) {
-    return <div>Function Calling Error: {error}</div>;
+    return (
+      <div className="text-red-500 border border-red-200 rounded-md p-2 flex items-center space-x-2">
+        <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
+          <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
+        </svg>
+        <span>Tool execution failed: {error}</span>
+      </div>
+    );
  }
frontend/src/app/playground/page.tsx (1)

59-62: Memoise handleAddToolResult to prevent needless child re-renders

Because handleAddToolResult is created on every render, React will treat it as a new
prop and cause Messages, and anything further down, to re-render even when nothing
else changed. Wrapping the callback in useCallback (or at least useMemo) gives it
stable identity and avoids extra work.

-  const handleAddToolResult = ({ toolCallId, result }: { toolCallId: string; result: object }) => {
-    addToolResult({ toolCallId, result });
-  };
+  const handleAddToolResult = useCallback(
+    ({ toolCallId, result }: { toolCallId: string; result: object }) => {
+      addToolResult({ toolCallId, result });
+    },
+    [addToolResult],
+  );
frontend/src/hooks/use-tool-execution.ts (2)

26-35: Current duplicate-call guard is scoped per component instance

hasStartedChecking lives inside each hook instance, so two components rendering the
same toolCallId at roughly the same time will still both fire the network request.
If the intent is to guarantee global deduplication, consider moving the Set
outside the hook or into a Recoil/Zustand atom.

No code change mandatory, but worth verifying the requirement.


70-75: useLayoutEffect is overkill for network requests

useLayoutEffect blocks the browser from painting; it’s meant for DOM
measurements/patches that must happen before paint.
A plain useEffect is adequate (and avoids React v18 SSR warnings).

-  useLayoutEffect(() => {
+  useEffect(() => {
frontend/src/app/playground/chat-input.tsx (2)

30-34: setMessages((messages) => messages) is a no-op

Calling the setter with the previous state unmodified does nothing besides a wasted
render pass.
If you meant to refresh the UI after stop(), fetch the latest messages from the
server instead; otherwise remove the call.

-        stop();
-        setMessages((messages) => messages);
+        stop();

95-110: Hard-coded status literals scattered in the key handler

"submitted", "streaming" and "ready" are compared in several places. A small
enum (or union type) exported from the chat hook would:

  • avoid typos,
  • make autocomplete work, and
  • keep the UI consistent when new states are added.

Example:

export const ChatStatus = {
  READY: "ready",
  SUBMITTED: "submitted",
  STREAMING: "streaming",
} as const;
export type ChatStatus = typeof ChatStatus[keyof typeof ChatStatus];

Then use ChatStatus.READY, etc.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8441bdb and 7bc3572.

📒 Files selected for processing (9)
  • frontend/package.json (1 hunks)
  • frontend/src/app/appconfigs/[appName]/page.tsx (1 hunks)
  • frontend/src/app/linked-accounts/page.tsx (1 hunks)
  • frontend/src/app/playground/chat-input.tsx (1 hunks)
  • frontend/src/app/playground/function-calling.tsx (1 hunks)
  • frontend/src/app/playground/message.tsx (2 hunks)
  • frontend/src/app/playground/messages.tsx (2 hunks)
  • frontend/src/app/playground/page.tsx (3 hunks)
  • frontend/src/hooks/use-tool-execution.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Compose Tests
  • GitHub Check: Format & Lint
🔇 Additional comments (15)
frontend/src/app/linked-accounts/page.tsx (1)

406-406: Trivial whitespace-only change: the trailing newline at EOF was removed. No functional impact.

frontend/src/app/appconfigs/[appName]/page.tsx (1)

340-340: No-op formatting change
The removal of the trailing newline at the end of the file is purely cosmetic and has no functional impact.

frontend/package.json (1)

63-63: Added usehooks-ts library to support the new tool execution functionality.

Good addition of the usehooks-ts package which provides useful React hooks like debounce functionality that will help prevent duplicate API requests during tool execution.

frontend/src/app/playground/message.tsx (3)

18-20: Props added to support tool execution functionality.

These new props will be passed down to the FunctionCalling component, enabling proper tool execution handling.


24-26: Well-defined prop types for the new parameters.

The TypeScript definitions are clear and properly typed for the new functionality.


112-114: Properly passing required props to FunctionCalling component.

These props will enable the FunctionCalling component to execute tools and report results back through the callback.

frontend/src/app/playground/messages.tsx (3)

12-14: Added necessary props to MessagesProps interface.

These new props will enable tool execution functionality throughout the message components.


17-17: Updated PureMessages component to receive new props.

Properly updated the function signature to include the new parameters.


37-39: Correctly passing new props to PreviewMessage components.

This ensures the tool execution functionality flows down to individual messages.

frontend/src/app/playground/function-calling.tsx (5)

1-1: Added import for the new useToolExecution hook.

This hook will handle the logic for executing tools and prevent duplicate API calls.


6-8: Updated FunctionCallingProps with necessary parameters.

Added the required props to properly type the component.


11-11: Updated component signature to include new props.

Properly destructured the new parameters for use within the component.


14-19: Implemented useToolExecution hook.

The hook is properly configured with all necessary parameters to handle tool execution.


26-28: Improved UI during tool execution.

The loading spinner provides good visual feedback when a tool is being executed.

frontend/src/app/playground/page.tsx (1)

40-57: Guard against an empty apiKey before calling the chat endpoint

apiKey is derived from getApiKey(activeProject) and may be an empty string when a
project has no key yet.
useChat will still issue the request with an empty x-api-key header, which will
(1) fail every call and (2) still count against rate limits on some back-ends.
Consider bailing out early or disabling the input until a valid key is present:

if (!apiKey) {
  return <div>Please configure an API key for this project.</div>;
}

or pass enabled: Boolean(apiKey) to the hook (if supported).

Comment on lines +59 to +66
}, [
toolInvocation.toolCallId,
toolName,
args,
addToolResult,
linkedAccountOwnerId,
apiKey,
]);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

args in dependency list may trigger needless re-creations

Objects coming from props often get recreated on every render, so including args
directly in the dependency array causes executeToolCallback to be regenerated and
debounced work to restart even when the content is unchanged.
Either

  1. rely on toolInvocation.toolCallId (which is unique and already present), or
  2. stabilise args with useMemo / JSON.stringify.
-    args,
+    /* args intentionally omitted – toolCallId uniqueness is sufficient */,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
}, [
toolInvocation.toolCallId,
toolName,
args,
addToolResult,
linkedAccountOwnerId,
apiKey,
]);
}, [
toolInvocation.toolCallId,
toolName,
/* args intentionally omitted – toolCallId uniqueness is sufficient */,
addToolResult,
linkedAccountOwnerId,
apiKey,
]);

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
frontend/src/app/playground/chat-input.tsx (1)

98-99: Remove redundant condition check.

The linkedAccountOwnerId is already checked at the beginning of the handler (line 91).

                if (
                  event.key === "Enter" &&
-                  !event.shiftKey &&
-                  linkedAccountOwnerId
+                  !event.shiftKey
                ) {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1278202 and 4ba3582.

⛔ Files ignored due to path filters (1)
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (7)
  • frontend/package.json (1 hunks)
  • frontend/src/app/playground/chat-input.tsx (1 hunks)
  • frontend/src/app/playground/function-calling.tsx (1 hunks)
  • frontend/src/app/playground/message.tsx (2 hunks)
  • frontend/src/app/playground/messages.tsx (2 hunks)
  • frontend/src/app/playground/page.tsx (3 hunks)
  • frontend/src/hooks/use-tool-execution.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • frontend/package.json
  • frontend/src/app/playground/message.tsx
  • frontend/src/app/playground/function-calling.tsx
  • frontend/src/hooks/use-tool-execution.ts
  • frontend/src/app/playground/messages.tsx
  • frontend/src/app/playground/page.tsx
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Compose Tests
  • GitHub Check: Format & Lint
  • GitHub Check: Format, Lint, and Test
🔇 Additional comments (10)
frontend/src/app/playground/chat-input.tsx (10)

4-6: Good import organization.

The new imports are well-selected for the enhanced functionality. Using lucide-react icons provides consistent visual elements, while the React optimization hooks will help with performance.


14-15: Proper typing for new optional props.

Adding optional stop controls is a good approach for supporting the new functionality while maintaining backward compatibility.


18-39: Well-structured StopButton component.

The PureStopButton component is properly typed with clear props and includes testing attributes. The implementation correctly prevents default form behavior and handles stopping.


41-41: Good use of memoization.

Using memo for the StopButton is appropriate for performance optimization when props rarely change.


43-53: Well-designed SendButton component.

The button properly disables when the input is empty and maintains consistent styling with the StopButton.


55-59: Effective custom memoization comparison.

The custom comparison for SendButton optimization is a good performance enhancement, only re-rendering when input empty state changes.


70-76: Good form handling optimization.

Using useCallback for the form submission handler prevents unnecessary re-renders and properly manages form events.


90-110: Improved keydown handler with proper validation.

The keydown handler now properly validates the linked account and prevents submission during ongoing requests, addressing the duplicate requests issue mentioned in the PR objectives.


111-114: Enhanced textarea styling and behavior.

The increased right padding (pr-20) accommodates the absolutely positioned button overlay, and the disabled state prevents interaction during processing.


115-123: Elegant button positioning and conditional rendering.

The UI implementation for the buttons is well-executed with proper positioning and conditional rendering based on the current status.

<div className="pointer-events-none">
<div className="absolute top-1/2 right-4 -translate-y-1/2 flex flex-row justify-end pointer-events-auto">
{status === "submitted" || status === "streaming" ? (
<StopButton stop={stop!} setMessages={setMessages!} />
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid non-null assertions on optional props.

The use of non-null assertions (!) on optional props could lead to runtime errors if these props aren't provided.

-                  <StopButton stop={stop!} setMessages={setMessages!} />
+                  {stop && setMessages && <StopButton stop={stop} setMessages={setMessages} />}

Or provide default no-op functions:

-                  <StopButton stop={stop!} setMessages={setMessages!} />
+                  <StopButton stop={stop || (() => {})} setMessages={setMessages || (cb => cb([]))} />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<StopButton stop={stop!} setMessages={setMessages!} />
{stop && setMessages && <StopButton stop={stop} setMessages={setMessages} />}
Suggested change
<StopButton stop={stop!} setMessages={setMessages!} />
<StopButton
stop={stop || (() => {})}
setMessages={setMessages || (cb => cb([]))}
/>

@jiwei-aipolabs jiwei-aipolabs merged commit a921e83 into main May 7, 2025
6 checks passed
@jiwei-aipolabs jiwei-aipolabs deleted the fix_tool_execution_bug branch May 7, 2025 13:34
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.

1 participant