Jump to content

Connect SuperML | Leeroopedia MCP: Equip your AI agents with best practices, code verification, and debugging knowledge. Powered by Leeroo — building Organizational Superintelligence. Contact us at founders@leeroo.com.

Implementation:Mlc ai Web llm Get Tool Call From Output

From Leeroopedia

Template:Knowledge

Overview

Get_Tool_Call_From_Output implements the Mlc_ai_Web_llm_Tool_Call_Extraction principle by providing the getToolCallFromOutputMessage function that parses raw model output JSON into typed tool call objects, and the engine integration that invokes it during chat completion processing.

Source Reference

  • Parser function: src/support.ts, Lines 129-204
  • Engine integration (non-streaming): src/engine.ts, Lines 854-868
  • Engine integration (streaming): src/engine.ts, Lines 626-654
  • Repository: mlc-ai/web-llm

Code Reference

Function Signature (Overloaded)

// src/support.ts L129-136
export function getToolCallFromOutputMessage(
  outputMessage: string,
  isStreaming: false,
): Array<ChatCompletionMessageToolCall>;

export function getToolCallFromOutputMessage(
  outputMessage: string,
  isStreaming: true,
): Array<ChatCompletionChunk.Choice.Delta.ToolCall>;

Implementation

// src/support.ts L137-204
export function getToolCallFromOutputMessage(
  outputMessage: string,
  isStreaming: boolean,
):
  | Array<ChatCompletionMessageToolCall>
  | Array<ChatCompletionChunk.Choice.Delta.ToolCall> {
  // 1. Parse outputMessage to JSON object
  let toolCallsObject;
  try {
    toolCallsObject = JSON.parse(outputMessage);
  } catch (err) {
    throw new ToolCallOutputParseError(outputMessage, err as Error);
  }

  // 2. Expect to be an array
  if (!(toolCallsObject instanceof Array)) {
    throw new ToolCallOutputInvalidTypeError("array");
  }

  // 3. Parse each tool call and populate tool_calls
  const numToolCalls = toolCallsObject.length;
  const tool_calls = [];
  for (let id = 0; id < numToolCalls; id++) {
    const curToolCall = toolCallsObject[id];
    if (curToolCall.name === undefined || curToolCall.arguments === undefined) {
      throw new ToolCallOutputMissingFieldsError(
        ["name", "arguments"],
        curToolCall,
      );
    }
    tool_calls.push({
      name: curToolCall.name,
      arguments: JSON.stringify(curToolCall.arguments),
    });
  }

  // 4. Return based on whether it is streaming or not
  if (isStreaming) {
    const tool_calls_result: Array<ChatCompletionChunk.Choice.Delta.ToolCall> = [];
    for (let id = 0; id < numToolCalls; id++) {
      const curToolCall = tool_calls[id];
      tool_calls_result.push({
        index: id,
        function: {
          name: curToolCall.name,
          arguments: curToolCall.arguments,
        },
        type: "function",
      });
    }
    return tool_calls_result;
  } else {
    const tool_calls_result: Array<ChatCompletionMessageToolCall> = [];
    for (let id = 0; id < numToolCalls; id++) {
      const curToolCall = tool_calls[id];
      tool_calls_result.push({
        id: id.toString(),
        function: {
          name: curToolCall.name,
          arguments: curToolCall.arguments,
        },
        type: "function",
      });
    }
    return tool_calls_result;
  }
}

Engine Integration (Non-Streaming)

// src/engine.ts L854-888
// 3. Post processing for function calling
const isFunctionCalling =
  request.tools !== undefined && request.tools !== null;
let tool_calls: Array<ChatCompletionMessageToolCall> | undefined;
if (
  selectedPipeline.getFinishReason() === "stop" &&
  isFunctionCalling
) {
  // If stopped due to length or abort, cannot output return tool_calls field
  finish_reason = "tool_calls";
  tool_calls = getToolCallFromOutputMessage(
    outputMessage,
    /*isStreaming=*/ false,
  );
}

choices.push({
  finish_reason: finish_reason,
  index: i,
  logprobs: /* ... */,
  message: isFunctionCalling
    ? {
        content: null,
        tool_calls: tool_calls,
        role: "assistant",
      }
    : {
        content: outputMessage,
        role: "assistant",
      },
});

I/O Contract

Function signature:

Parameter Type Description
outputMessage string Raw JSON string from the model output
isStreaming boolean Whether the response is streaming

Return type (non-streaming, isStreaming: false):

Array<ChatCompletionMessageToolCall>
// Each element:
{
  id: string;           // Array index as string ("0", "1", ...)
  function: {
    name: string;       // Function name
    arguments: string;  // JSON-serialized arguments string
  };
  type: "function";
}

Return type (streaming, isStreaming: true):

Array<ChatCompletionChunk.Choice.Delta.ToolCall>
// Each element:
{
  index: number;        // Array index as number (0, 1, ...)
  function: {
    name: string;       // Function name
    arguments: string;  // JSON-serialized arguments string
  };
  type: "function";
}

Error types thrown:

Error When Contains
ToolCallOutputParseError JSON.parse fails Original message string and underlying Error
ToolCallOutputInvalidTypeError Parsed result is not an array Expected type string ("array")
ToolCallOutputMissingFieldsError Element missing required fields List of required fields and the offending object

Key transformation: The arguments field is transformed from a JSON object (as output by the model) to a JSON string (as required by the OpenAI API convention) via JSON.stringify(curToolCall.arguments).

Usage Examples

Internal usage (how the engine calls it):

// This is internal engine code -- users do not call this directly
import { getToolCallFromOutputMessage } from "./support";

// Model outputs: '[{"name":"get_weather","arguments":{"location":"NYC"}}]'
const rawOutput = '[{"name":"get_weather","arguments":{"location":"NYC"}}]';

const toolCalls = getToolCallFromOutputMessage(rawOutput, false);
// Result:
// [
//   {
//     id: "0",
//     function: {
//       name: "get_weather",
//       arguments: '{"location":"NYC"}'  // Note: string, not object
//     },
//     type: "function"
//   }
// ]

Multiple tool calls extraction:

// Model outputs two function calls
const rawOutput = JSON.stringify([
  { name: "get_weather", arguments: { location: "Pittsburgh, PA", unit: "celsius" } },
  { name: "get_weather", arguments: { location: "Tokyo, Japan", unit: "celsius" } },
]);

const toolCalls = getToolCallFromOutputMessage(rawOutput, false);
// Result: Array of 2 ChatCompletionMessageToolCall objects
// toolCalls[0].id === "0"
// toolCalls[1].id === "1"
// toolCalls[0].function.arguments === '{"location":"Pittsburgh, PA","unit":"celsius"}'

Streaming mode extraction:

const rawOutput = '[{"name":"get_weather","arguments":{"location":"NYC"}}]';

const toolCalls = getToolCallFromOutputMessage(rawOutput, true);
// Result:
// [
//   {
//     index: 0,              // number, not string
//     function: {
//       name: "get_weather",
//       arguments: '{"location":"NYC"}'
//     },
//     type: "function"
//   }
// ]

Consuming extracted tool calls in application code:

const reply = await engine.chat.completions.create(request);

if (reply.choices[0].finish_reason === "tool_calls") {
  for (const toolCall of reply.choices[0].message.tool_calls!) {
    const functionName = toolCall.function.name;
    const args = JSON.parse(toolCall.function.arguments);
    console.log(`Call ${functionName} with`, args);
  }
}

Related Pages

Page Connections

Double-click a node to navigate. Hold to expand connections.
Principle
Implementation
Heuristic
Environment