# How DeepSeek-Reasonix Generates and Streams Tool Schemas to the Model

> Learn how DeepSeek-Reasonix generates and streams tool schemas. Discover the Schema() method, canonicalization, and alphabetical sorting for efficient model integration.

- Repository: [YHH/DeepSeek-Reasonix](https://github.com/esengine/DeepSeek-Reasonix)
- Tags: how-to-guide
- Published: 2026-06-22

---

**DeepSeek-Reasonix generates tool schemas by requiring each tool to implement a `Schema()` method that returns JSON metadata, canonicalizes those schemas once per run via `provider.CanonicalizeSchema`, and streams them to the model as a stable, alphabetically-sorted list before every turn.**

DeepSeek-Reasonix treats every tool as a self-describing plugin that exposes its own parameter structure through JSON Schema. When the system initializes a chat session, it processes these schemas through a canonicalization pipeline to ensure deterministic ordering, then streams the complete tool definitions to the language model before any user interaction occurs.

## The Tool Interface and Schema Contract

In [`internal/tool/tool.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/tool/tool.go), the system defines a `Tool` interface that every plugin must implement. The critical method for schema generation is `Schema() json.RawMessage`, which returns the raw JSON Schema describing the tool's parameters, allowing the model to understand exactly what arguments a tool accepts.

### Implementing the Tool Interface

Each concrete tool provides its own schema definition. For example, the built-in `write_file` tool in [`internal/tool/builtin/writefile.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/tool/builtin/writefile.go) implements the interface by returning a minimal JSON Schema:

```go
type writeFile struct{}

func (writeFile) Name() string        { return "write_file" }
func (writeFile) Description() string { return "Write a file on the host." }

func (writeFile) Schema() json.RawMessage {
    // Minimal JSON schema for the tool.
    return json.RawMessage(`{
        "type":"object",
        "properties":{
            "path":{"type":"string"},
            "content":{"type":"string"}
        },
        "required":["path","content"]
    }`)
}

```

## Schema Canonicalization and Caching

To avoid recomputing schema order and to ensure byte-level consistency across requests, DeepSeek-Reasonix canonicalizes each schema exactly once upon registration.

### The Registry Cache

The `Registry` type in [`internal/tool/tool.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/tool/tool.go) maintains a `canon` map that stores the canonicalized form of each tool's schema. When `Registry.Add` is called, it immediately invokes `provider.CanonicalizeSchema(t.Schema())` and caches the result:

```go
// Registry.Add – canonicalizes and caches the schema.
r.canon[name] = provider.CanonicalizeSchema(t.Schema())

```

This guarantees that schema normalization happens only once per tool per run, eliminating redundant JSON marshaling during active chat turns.

### Deterministic Ordering with Canonicalization

The `provider` package in [`internal/provider/schema_canonicalize.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/provider/schema_canonicalize.go) normalizes JSON Schema objects by sorting object keys recursively and removing duplicate entries. This process ensures that logically equivalent schemas produce identical byte sequences. When `Registry.Schemas()` is called, it produces an alphabetically-sorted slice of `provider.ToolSchema` structs:

```go
// Registry.Schemas – exports name, description and the cached schema.
out = append(out, provider.ToolSchema{
    Name:        t.Name(),
    Description: t.Description(),
    Parameters:  r.canon[name],
})

```

The combination of canonicalization and alphabetical sorting guarantees that the tool list is deterministic, preventing cache misses or inconsistent model behavior.

## Streaming Schemas to the Model

During each turn of the chat, the server constructs a **"tool-definition"** message containing the slice returned by `Registry.Schemas()`. Because the schemas are pre-canonicalized and cached, the payload is small and fast to generate.

### Building the Tool Definition Message

The request-handling path (implemented in files like [`internal/serve/wire.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/serve/wire.go)) injects the schema list into the outbound protocol message before any user or assistant content. A simplified version of this flow appears as:

```go
func sendTurn(toModel modelClient, reg *internal/tool.Registry, turn *Turn) error {
    defs := reg.Schemas()
    // Build a tool-definition message.
    msg := model.ToolDefinition{Tools: defs}
    if err := toModel.SendMessage(ctx, msg); err != nil {
        return err
    }
    // Follow up with the user/assistant messages for this turn.
    return toModel.SendMessage(ctx, turn.Message)
}

```

This ensures the model receives the complete tool specification at the start of every interaction, enabling it to generate valid function calls based on the accurate parameter definitions.

## Summary

- **Tool interface**: Each tool implements `Schema() json.RawMessage` in [`internal/tool/tool.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/tool/tool.go) to declare its parameter structure.
- **Canonicalization**: `provider.CanonicalizeSchema` in [`internal/provider/schema_canonicalize.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/provider/schema_canonicalize.go) normalizes schemas to ensure byte-level consistency.
- **Registry caching**: The `Registry` type caches canonicalized schemas in a `canon` map during `Add()`, eliminating reprocessing during chat turns.
- **Stable ordering**: `Registry.Schemas()` returns an alphabetically-sorted list of `provider.ToolSchema` structs for deterministic transmission.
- **Streaming**: The server streams the complete tool definition message to the model before user and assistant messages, as implemented in the request-handling path.

## Frequently Asked Questions

### What is the purpose of schema canonicalization?

Schema canonicalization ensures that logically equivalent JSON schemas produce identical byte sequences by sorting object keys and removing duplicates. This guarantees deterministic ordering when the schema list is streamed to the LLM, preventing variations that could confuse the model or break caching mechanisms.

### How does the Registry ensure schemas are only processed once?

The `Registry` type in [`internal/tool/tool.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/tool/tool.go) maintains a `canon` map that stores the canonicalized result of `provider.CanonicalizeSchema(t.Schema())` immediately when `Registry.Add` is called. Subsequent calls to `Registry.Schemas()` retrieve the cached value rather than re-marshaling the JSON, reducing CPU overhead during active chat sessions.

### Why are schemas sent before user and assistant messages?

The tool definition message must reach the model first so it knows exactly which functions are available and what parameters they require before processing any user queries or generating responses. This ordering, implemented in the request-handling path, enables the model to generate valid tool invocations during its initial response generation rather than requiring a secondary pass.

### Where are built-in tool schemas defined?

Built-in tool schemas are defined in individual files within `internal/tool/builtin/`, such as [`internal/tool/builtin/writefile.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/tool/builtin/writefile.go). Each file contains a concrete type implementing the `Tool` interface with a hardcoded or computed `json.RawMessage` representing the tool's JSON Schema parameters.