Ironclad / Ironclad/rivet

[Bug]: Tool/Function calling not handling mutliple requested calls

Open
#431 1 comment 0 reactions 0 assignees View on GitHub
bug
Dominant language
TypeScript
Stars
4.7k
Forks
388
PR merge metrics
No merged PRs in 30d

Description

### What happened?

Working on a ChatNode plugin that directly extends ChatNode.ts and realized that there is an error.

Looking at this code:
```
if (functionCalls.length > 0) {
if (isMultiResponse) {
output['function-call' as PortId] = {
type: 'object[]',
value: functionCalls.map((functionCalls) => ({
name: functionCalls[0]?.name,
arguments: functionCalls[0]?.lastParsedArguments,
id: functionCalls[0]?.id,
})),
};
} else {
if (this.data.parallelFunctionCalling) {
console.dir({ functionCalls });
output['function-calls' as PortId] = {
type: 'object[]',
value: functionCalls[0]!.map((functionCall) => ({
name: functionCall.name,
arguments: functionCall.lastParsedArguments,
id: functionCall.id,
})),
};
} else {
output['function-call' as PortId] = {
type: 'object',
value: {
name: functionCalls[0]![0]?.name,
arguments: functionCalls[0]![0]?.lastParsedArguments,
id: functionCalls[0]![0]?.id,
} as Record,
};
}
}
}
```

In the case of multiResponse and single response not parallel. if OpenAI is requesting to run multiple tools, those are not being passed any further and are being dropped, as can be seen. In the first case we take the first function call for the choice (inner array) and in the other case we create a record of the first case in first choice (ok since single choice).

I believe the code should be adjusted as follow:
```
if (functionCalls.length > 0) {
if (isMultiResponse) {
output['function-call' as PortId] = {
type: 'object[]',
value: functionCalls.flat().map((functionCall) => ({
name: functionCall.name,
arguments: functionCall.lastParsedArguments,
id: functionCall.id,
})),
}
} else {
output['function-call' as PortId] = {
type: 'object[]',
value: functionCalls[0]!.map((functionCall) => ({
name: functionCall.name,
arguments: functionCall.lastParsedArguments,
id: functionCall.id,
})),
}
}
}
```
This would always send all tool call requests as intended and do it in a standard way. The parallel argument is more to the receiver to decide how to handle the results IMHO.

What I can see here is that if the input is a single gptFunction and a single choice, then maybe there could have been expectation set that this would not be returned as an array and that case should be singled out to return a single object as it does currently. But again I am not sure that this is even correct, since even with a single tool, open AI could as to call it multiple times with different argument. Imagine a weather tool and I ask for the weather in NY and LA. Obviously 2 calls will more than likely be requested by OpenAI, even if we have a single tool declared.

I can make a PR if there is agreement.

### What was the expected functionality?

Rivet did not work as expected!

### Describe your environment

Not applicable here

### Relevant log output

_No response_

### Relevant screenshots

_No response_

### Code of Conduct

- [X] I agree to follow this project's Code of Conduct

Contributor guide

Open the contributing guide

Research direction

Start in ChatNode.ts at the function-call output handling shown in the issue, and trace how functionCalls is shaped for multi-response and single-response results. Verify that every requested tool call is preserved in both paths, including repeated calls to one tool; done means no requested calls are dropped and the resulting output shape matches the receiving node's expectations.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
ai
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.