Skip to content

XMLFormatParser coerces numeric XML tag values, silently dropping action inputs #2025

Description

@sxh313

Description

XMLFormatParser — the format parser that handles the <seed:tool_call> / <answer> XML output style — builds its
fast-xml-parser instance with only ignoreAttributes: false:

// multimodal/gui-agent/action-parser/src/FomatParsers.ts:37-41
const builder = new XMLBuilder();
const parser = new XMLParser({
  ignoreAttributes: false,
});
const object = parser.parse(text);

so fast-xml-parser's default parseTagValue: true still applies, and any tag whose text happens to look like a
number comes back as a JS number instead of a string.

Everything downstream is string-typed, so a numeric tag value is either dropped without a trace or reaches a
.trim() / .replace() call on a number:

  • FomatParsers.ts:63-70 — content: value as string pushes the raw value straight into finished.inputs. The cast
    is unsound; this is where the number enters the typed layer.
  • ActionParserHelper.standardizeActionInputsFromXMLObject() (ActionParserHelper.ts:536-571) declares
    Record<string, string> and branches only on typeof value === 'string' and
    value && typeof value === 'object', so a numeric parameter tag matches neither and is discarded silently.
  • ActionParserHelper.standardizeAction() (ActionParserHelper.ts:313 and :335, roughInputStrValue.trim()) and
    parseCoordinates() (:379-380, params.trim()) both declare string parameters.
  • serializeAction() (multimodal/gui-agent/shared/src/utils/actions.ts:14-44) has no numeric branch for
    finished, so the action serializes as finished(unsupported).
  • multimodal/gui-agent/agent-sdk/src/ToolCallEngine.ts:163,167,185-187 stores the value into
    finishMessage: string | null and then calls content.replace(...).

Reproduction

Measured on main (c2ad42e3) through the public entry point new DefaultActionParser().parsePrediction(...),
with @gui-agent/action-parser@0.3.0 and fast-xml-parser@4.5.3 as pinned by multimodal/pnpm-lock.yaml.

Input 1:

<answer>42</answer>
actions:           {"type":"finished","inputs":{"content":42}}
rawActionStrings:  ["finished(unsupported)"]
ToolCallEngine.ts:187  ->  TypeError: content.replace is not a function

Input 2:

<answer>007</answer>
actions:           {"type":"finished","inputs":{"content":7}}   # leading zeros lost
rawActionStrings:  ["finished(unsupported)"]
ToolCallEngine.ts:187  ->  TypeError: content.replace is not a function

Input 3:

<seed:tool_call>
<function=type>
<parameter=content>123456</parameter>
</function>
</seed:tool_call>
actions:           {"type":"type","inputs":{}}   # the text to type is gone
rawActionStrings:  ["type()"]

Input 4:

<think>42</think>
<answer>done</answer>
reasoningContent:  ""        # XMLBuilder().build(42) === "" (FomatParsers.ts:59),
                             # normalized to undefined at DefaultActionParser.ts:96

Expected

A model that answers with a bare number, or emits a numeric-looking parameter such as a verification code, should keep
its payload:

actions:           {"type":"finished","inputs":{"content":"42"}}
rawActionStrings:  ["finished(content='42')"]

Root cause / proposed fix

This is a single construction site. Setting parseTagValue: false on FomatParsers.ts:38-40 restores the string
contract that the rest of the pipeline already declares, and no conversion is needed anywhere downstream —
parseCoordinates() already runs its own Number.parseFloat on the string it receives.

Nothing pins the current coercing behaviour. Searching the whole multimodal/ tree for an XML tag whose text is a bare
number (an answer, a parameter=content, a duration, or anything else) matches no existing fixture or test, and none
of the 141 currently open PRs modifies anything under multimodal/gui-agent/action-parser/.

Environment

  • Package: @gui-agent/action-parser@0.3.0 (multimodal/gui-agent/action-parser)
  • Branch: main at c2ad42e3
  • Node 24, pnpm workspace, fast-xml-parser@4.5.3

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions