How ds4 Handles Tool Calling and DSML Format Conversion: A Complete Technical Breakdown

ds4 uses a three-stage pipeline—DSML generation, streaming state-machine parsing, and JSON conversion—to bridge the model's native DSML markup with OpenAI-compatible tool-calling APIs.

The antirez/ds4 repository implements a complete tool-calling pipeline that seamlessly converts between the model's native DSML (Domain-Specific Markup Language) syntax and standard JSON formats expected by client applications. This article examines the exact mechanisms, source file locations, and implementation details that make this bidirectional conversion possible.

DSML Generation: Encoding Tool Calls in Native Syntax

When the model decides to invoke a tool, ds4 embeds the call inside a special DSML block using <|DSML|tool_calls> tags. The primary function responsible for this transformation is append_dsml_tool_calls_text() located in ds4_server.c (lines 2362–2382):

static void append_dsml_tool_calls_text(buf *b, const tool_calls *calls) {
    if (!calls || calls->len == 0) return;
    if (calls->raw_tool_text && calls->raw_tool_text[0]) {
        buf_puts(b, calls->raw_tool_text);
        return;
    }
    buf_puts(b, "\n\n<|DSML|tool_calls>\n");
    for (int i = 0; i < calls->len; i++) {
        const tool_call *tc = &calls->v[i];
        buf_puts(b, "<|DSML|invoke name=\"");
        append_dsml_attr_escaped(b, tc->name);
        buf_puts(b, "\">\n");
        if (!append_dsml_arguments_from_json(b, tc->arguments, NULL)) {
            buf_puts(b, "<|DSML|parameter name=\"arguments\" string=\"true\">");
            append_dsml_parameter_text(b, tc->arguments);
            buf_puts(b, "</|DSML|parameter>\n");
        }
        buf_puts(b, "</|DSML|invoke>\n");
    }
    buf_puts(b, "</|DSML|tool_calls>");
}

Key behaviors of this DSML format conversion process:

  • Empty tool call lists are silently skipped
  • Pre-serialized raw text can bypass re-encoding
  • Tool names are attribute-escaped via append_dsml_attr_escaped()
  • Arguments convert to nested <|DSML|parameter> tags when JSON conversion fails

DSML Parsing: Streaming State-Machine Decoder

During inference, ds4 parses partially-generated DSML using a resilient streaming decoder. The parser tracks its position through enumerated states defined near line 4570 in ds4_server.c:

enum {
    DSML_TOOL_BETWEEN_INVOKES,
    DSML_TOOL_BETWEEN_PARAMS,
    DSML_TOOL_PARAM_VALUE,
    DSML_TOOL_DONE,
    DSML_TOOL_ERROR,
};

The decoder operates in distinct parsing modes (lines 5831–5850):

enum {
    DSML_DECODE_OUTSIDE,
    DSML_DECODE_STRUCTURAL,
    DSML_DECODE_STRING_BODY,
    DSML_DECODE_JSON_STRUCTURAL,
    DSML_DECODE_JSON_STRING,
};

State transitions occur as the parser encounters tag boundaries. The following logic pattern appears throughout the decoder (approximately lines 6288–6440):

if (ts->state == DSML_TOOL_BETWEEN_INVOKES) {
    ts->state = DSML_TOOL_DONE;      // after </tool_calls>
}
else if (ts->state == DSML_TOOL_BETWEEN_PARAMS) {
    ts->state = DSML_TOOL_BETWEEN_INVOKES;   // after </invoke>
}
else if (ts->state == DSML_TOOL_PARAM_VALUE) {
    ts->state = DSML_TOOL_BETWEEN_PARAMS;    // after </parameter>
}

This streaming DSML parser tolerates malformed or truncated output without crashing, extracting complete tool calls even from incomplete model responses.

JSON Conversion: OpenAI-Compatible API Output

After parsing, ds4 serializes the collected tool_calls structure into standard JSON format using append_tool_calls_json() (lines 5353–5370):

static void append_tool_calls_json(buf *b,
                                   const tool_calls *calls,
                                   const char *id_prefix,
                                   const tool_schema_orders *orders) {
    // ... builds JSON array [...]
}

The function integrates into the HTTP response builder at lines 7471–7472:

buf_puts(&b, ",\"tool_calls\":");
append_tool_calls_json(&b, calls, id, &r->tool_orders);

This produces OpenAI-compatible tool-calling JSON containing:

  • id: Prefixed identifiers (e.g., "call_0")
  • type: Always "function"
  • function: Object with name and arguments

Practical Implementation Examples

Generating DSML from C

tool_calls my_calls = {0};
tool_calls_push(&my_calls, (tool_call){
    .name = "search",
    .arguments = json_from_string("{\"query\":\"hello\"}")
});

buf output = {0};
append_dsml_tool_calls_text(&output, &my_calls);

Output buffer contains:


<|DSML|tool_calls>
<|DSML|invoke name="search">
<|DSML|parameter name="arguments" string="true">{"query":"hello"}</|DSML|parameter>
</|DSML|invoke>
</|DSML|tool_calls>

Parsing Raw Model Output

const char *raw = "<|DSML|tool_calls>…</|DSML|tool_calls>";
tool_calls parsed = {0};
dsml_tracker ts = {.state = DSML_TOOL_BETWEEN_INVOKES};

parse_dsml_block(&raw, &ts, &parsed);  // populates parsed tool_calls

Converting to Client JSON

buf json_out = {0};
append_tool_calls_json(&json_out, &parsed, "call_", NULL);

Produces:

[
  {
    "id": "call_0",
    "type": "function",
    "function": {
      "name": "search",
      "arguments": {"query": "hello"}
    }
  }
]

Source File Reference

File Role in Tool-Calling Pipeline
ds4_server.c Core implementation: DSML generation (append_dsml_tool_calls_text), streaming parser (state machine at lines 4570+, 5831+, 6288+), JSON conversion (append_tool_calls_json)
ds4.h tool_calls structure declarations, DS4_LOG_TOOL logging constant
tests/ds4_test.c Unit tests: test_tool_call_quality(), state-machine assertions
misc/COMPACT.md DSML syntax specification documentation
README.md OpenAI-compatible API overview

Summary

  • DSML generation uses append_dsml_tool_calls_text() in ds4_server.c to embed tool calls in model-native markup
  • Streaming parsing employs a five-state machine (DSML_TOOL_BETWEEN_INVOKES through DSML_TOOL_ERROR) with five decoder modes for resilient extraction
  • JSON conversion via append_tool_calls_json() produces OpenAI-compatible output for HTTP responses
  • The same tool_calls structure synchronizes both representations, ensuring consistency between DSML and JSON formats

Frequently Asked Questions

What is DSML in the ds4 context?

DSML (Domain-Specific Markup Language) is a compact, XML-like syntax using vertical-bar delimiters (<|DSML|tag>) that the model generates natively for tool invocations. It provides a structured yet human-readable format for function calls with named parameters.

How does ds4 handle incomplete or malformed DSML?

The ds4 streaming parser maintains explicit state (DSML_TOOL_ERROR) and continues processing rather than failing. It extracts any complete tool calls found before truncation or syntax errors, enabling graceful degradation during partial model outputs.

Why does ds4 support both DSML and JSON formats?

DSML serves as the model's native output format optimized for generation, while JSON provides the standard interface that OpenAI-compatible clients expect. The dual-format approach allows ds4 to work with existing tooling without requiring client-side DSML parsers.

Where are tool-calling events logged in ds4?

Tool-related events use the DS4_LOG_TOOL logging type declared in ds4.h, enabling operators to filter and monitor tool invocation patterns separately from general inference logs.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →