How to get multiple tool_calls entries when setting tool_choice?

I have the following tools excerpt:

{
      tools: [
        {
          type: "function",
          function: {
            name: "output_json_ts_summary_keys",
            description: "Identify in the output possible distinct moments and provide list of these moments in a valid array of JSON objects",
            parameters: {
              type: "object",
              properties: {
                ts: {
                  type: "number",
                  description: "starting timestamp for the moment in decimal format",
                },
                summary: {
                  type: "string",
                  description: "short summary of the topic during this timeframe. Limit to no more than 12 words.",
                },
              },
            },
          },
        },
      ],
      tool_choice: {
        type: "function",
        function: { "name": "output_json_ts_summary_keys" },
      },
}

As result I have only 1 entry for tools_calls:

"tool_calls"=>[{"id"=>"call_vjnanTvmajYV6EbLBKoOnKYo", "type"=>"function", "function"=>{"name"=>"output_json_ts_summary_keys", "arguments"=>"{\"ts\":438.02,\"summary\":\"Item here\"}"}}]

When setting tool_choice to "auto" I get multiple entries in that array, which is the desired behavior.

So how can I make sure I get multiple objects in that array when specifying a function?

Programming language/library?

You can just process that into a list of items, which can be a list of one, handle each, iterating over the list (or parallel and async if you want the spirit of parallel tool calls), and then return a list back in your own format for return-writing.

The IDs must be placed properly in the tools return, as you can imagine.

I manage multiple functions calling as follow.

messages.push({ role: "user", content: message });
    const tools = [
      {
        type: "function",
        function: {
          name: "get_products_with_price",
          description:
            "Recupera la lista de precios de todos los productos en inventario.",
          parameters: {
            type: "object",
            properties: {},
          },
        },
      },
      {
        type: "function",
        function: {
          name: "get_Business_Hours",
          description: "Devuelve los horarios de atencion al cliente",
          parameters: {
            type: "object",
            properties: {},
          },
        },
      },
    ];

    const response = await openai.chat.completions.create({
      model: "gpt-4-1106-preview", //"gpt-3.5-turbo-1106",
      messages: messages,
      tools: tools,
      tool_choice: "auto", // auto is default, but we'll be explicit
    });
    const responseMessage = response.choices[0].message;

    // Step 2: check if the model wanted to call a function
    const toolCalls = responseMessage.tool_calls;
    let flagPrecios = false;
    if (responseMessage.tool_calls) {
      // Step 3: call the function
      // Note: the JSON response may not always be valid; be sure to handle errors
      const availableFunctions = {
        get_products_with_price: getPriceList,
        get_Business_Hours: getBusinessHours,
      }; //multiple functions
      messages.push(responseMessage); // extend conversation with assistant's reply
      for (const toolCall of toolCalls) {
        const functionName = toolCall.function.name;
        if (functionName === "get_products_with_price") {
          flagPrecios = true;
        }
        const functionToCall = availableFunctions[functionName];
        const functionArgs = JSON.parse(toolCall.function.arguments);
        const functionResponse = await functionToCall();
        messages.push({
          tool_call_id: toolCall.id,
          role: "tool",
          name: functionName,
          content: functionResponse,
        }); // extend conversation with function response
      }
      const secondResponse = await openai.chat.completions.create({
        model: "gpt-3.5-turbo-1106",
        messages: messages,
      }); // get a new response from the model where it can see the function response
      console.log(secondResponse.choices[0].message.content);
1 Like

Thanks, everyone. I am sorry if I am not following but I believe my issue is a little different.

  • I need "tool_calls" to have more than 1 item in the response;
  • This does happen when I set "tool_choice" with "auto";
  • But not when I am explicit by setting the function name, which I always get 1 entry only;
  • I can’t choose “auto” because I can’t rely on the model to decide whether it will output as JSON or not; it must always be a parsable JSON content.

Really appreciate all the help so far.

PS: I am using Rails 7

1 Like

I got it now. When you force the use of specific function, the model call the function only once.
I’ve never faced that situation, but I would try to change the model to gpt-4 (in the current model is gpt3.5).
If the behavior persist, I would try changing the parameter type from “object” to “array”. Then your function will expect an array of {ts, resumen} .

Not sure it was solved, but there are many closed threads about this change, yet the documentation is outdated, and the examples in the github repository as well.

Maybe that will help someone ending on the only non closed thread about the “new” tools API.

The calls, warning free and using today’s gh state, are like (add the usual parts around):

// function definition
import type { ChatCompletionMessageParam } from 'openai/resources/chat/completions';
const tools: OpenAI.ChatCompletionTool[] = [
    {
    type: 'function',
    function: {
      name: 'check_job_market',
      description: 'Get current job market information for a specific role',
      parameters: {
        type: 'object',
        properties: {
          role: { type: 'string' },
          industry: { type: 'string' },
          region: { type: 'string' },
          currentYear: { type: 'number' }
        },
        required: ['role']
      }
    }
  }];

async function checkJobMarket(args: JobMarketInfo): Promise<string> {
  // In a real application, this would fetch data from job market APIs or databases
  const marketData = {
    levels: 'somelevel',
   found: 123,
   avgPerMonth: 4
  };

  return JSON.stringify(marketData);
}

const tool_choice: OpenAI.Chat.ChatCompletionToolChoiceOption = "auto";

      const completion = await openai.chat.completions.create({
        model: "gpt-4o",
        messages: messages,
        tools: tools,
        tool_choice: tool_choice,
      });
    const messages: ChatCompletionMessageParam[] = [
      systemMessage,
      userMessage
    ];
//...
      const toolCallPromises = message.tool_calls.map(async (toolCall) => {
        if (toolCall.type === 'function') {
          let functionArgs;
          try {
            functionArgs = JSON.parse(toolCall.function.arguments);
          } catch (error) {
            console.error("Error parsing arguments:", error);
            return {
              tool_call_id: toolCall.id,
              role: "tool" as const,
              content: JSON.stringify({ error: "Invalid arguments" })
            };
          }

          const result = await callFunction(toolCall.function.name, functionArgs);

          // Return the tool response in the expected format
          return {
            tool_call_id: toolCall.id,
            role: "tool" as const,
            content: result
          };
        }
        return {
          tool_call_id: toolCall.id,
          role: "tool" as const,
          content: JSON.stringify({ error: "Unsupported tool type" })
        };
      });