#useCopilotAction triggers resolve in interrupt

1 messages · Page 1 of 1 (latest)

white lion
#

Hey. I have a slight problem. I have a LangGraph agent that does some tool calls and then makes an interrupt to get some data from user. I'm playing around with useCopilotAction, however for some reason this 'triggers' resolve function of useLangGraphInterrupt.

So here is the basic flow without useCopilotAction : User makes a prompt -> Agent uses tools which end up being ActionExecutionMessage on frontend when I log visibleMessages -> Agent does an interrupt and a custom UI element with resolve button is shown on frontend -> User accepts -> custom interrupt element dissapears -> Agent proceeds and sends a message something along the line: "Prompt accepted I will continue..."

If I try to incorporate useCopilotAction into the flow to essentially visualize tool calls it does that, however Agent acts as if interrupt was resolved and proceeds to send a message "Prompt not accepted..." (in chat, this message is above the actual interrupt UI element, but it starts 'streaming' after the interrupt UI element shows, before its the loading dots). On the frontend side of things the interrupt UI element remains and the "send message" button is gone from input field so its impossible to send further messages. And when I press the resolve button, the custom UI dissapears and thats it.

Basically as if resolve happens on the agent side, but not on the frontend side.

Am I using useCopilotAction wrong? All I want to do is make custom UI for tool calls to make UX better.

marsh dagger
#

useCopilotAction does support HITL and generative UI, but it looks like the langgraph integration has its own hooks for specific integration points.

#

Someone on the CK team can probably confirm / deny approach if I'm off the mark here

white lion
marsh dagger
#

Yeah they might be intefering with each other, what does your useCopilotAction call look like?

white lion
#
useCopilotAction({
    name: "toolCall",
    description: "Say hello to the user",
    parameters: [
      {
        name: "name",
        type: "string",
        description: "The name of the user to say hello to",
        required: true,
      },
    ],
    handler: async ({ name }) => {
      console.log('here in action');
      return;
    },
    render: ({ args: any }) => {
    return (<div>Custom DIV by useCopilotAction</div>)
    }
  });
marsh dagger
#

Yeah I wonder if toolCall being the name is too generic / conflicting with something else on the backend. It might be that the LLM is emitting multiple tool calls where it thinks it needs to always call that action.

If all you want is some UI to track tool calls rather than actually forcing a pause, you can do something like:

useCopilotAction({
  name: "*",
  render: (renderData) => {
    return (
      <>
        // Markup here
      </>
  }
});

Which acts as a catch-all tool call renderer, renderData has all the same stuff your render method normally has access to status args etc

white lion
marsh dagger
#

Giving it a handler registers it as a tool the LLM might decide to call, but just having a render method won't let the LLM call it, but it will still be involved in the render loop for any tool calls matching that name (or the * wildcard for catch-all)

white lion
white lion
marsh dagger
#

You might even be able to use copilot actions without handlers that have the name of the tool you want to render something for on a case-by-case basis, but I haven't tested that over a wildcard action.

white lion
#

I tried removing handler and only keeping name & render function, but it did not fix the problem

marsh dagger
#

Weird, yeah that might be a bug, but glad to hear the wildcard renderer worked