Skip to content

Contract DSL

Every model manifest carries its API at serving.spec. The release’s pinned code generates the spec during publication, and scratch code generates the same document for every verified revision. Clients can render controls and pre-validate requests without maintaining a separate endpoint schema.

The router remains authoritative: it validates the effective request and passes it to the runtime. The runtime performs semantic checks that need decoded data.

{
"spec_dsl": 1,
"interaction": "request_response",
"title": "fmtts text-to-speech",
"description": "Reference-conditioned text-to-speech.",
"params": [],
"constraints": [],
"results": {"per_item": [], "delivery": ["inline", "upload"]},
"examples": [
{"title": "Minimal request", "request": {}, "notes": "Optional notes"}
]
}

spec_dsl is 1 or 2 — v2 adds the curve kind, both as a parameter and as a nested result field (v1 forbids it in results); nothing else changes. Existing models keep the version they were published with, since manifests are immutable. interaction is exactly request_response; session or streaming interaction will require a different versioned shape and front door.

Specs declare model parameters only. Target identity, aliases, output_targets, result IDs, auth, and timing belong to the platform envelope.

Every parameter requires name, kind, and a non-empty description written for both people and agents.

Field Meaning
name Lowercase snake case, at most 64 characters.
kind One of the closed v1 kinds below.
description What the parameter changes and when to use it.
required Defaults to false.
default Any value valid for the declaration.
nullable Whether null is accepted; absent is equivalent to null.
null_means Required exactly when nullable: model_default or resolved.
advanced UI hint to hide the control behind advanced settings.
unit Optional display unit.

null_means: "resolved" means the server chooses an effective value and reports it in the result’s resolved object, as FMTTS does for a random seed.

Kind Additional fields and representation
number Finite JSON number; optional min, max, exclusive bounds, step, and integer. step is declarative UI metadata in v1: the schema accepts it, but the router does not snap or reject off-step values.
boolean JSON boolean. Use nullable for a true tri-state.
text String; optional max_chars.
text_list String array; optional item and character bounds.
enum options array. Each option has value and optional label, description, metadata, and top-level parameter overrides.
audio An object containing exactly one transport permitted by the declaration’s accepts list (inline_b64, asset_ref, or url); optional byte and duration bounds.
tuple Numeric array with exactly one value per named slots entry. Slots can declare bounds; per-slot default is declarative UI metadata in v1 and is not materialized by the router — a defaulted tuple must still be sent whole or omitted whole.
object Object with recursively declared fields; use for true composites, not arbitrary grab-bags.
opaque Any JSON value. Optional stability: "research" marks an unstable escape hatch.
curve (v2) A function-valued control: the caller sends {"interpolation": "linear", "points": [[x, y], ...]} with 2 to representation.max_points control points, x inside the declared domain and y inside the declared range (each with min/max and display labels). FMTTS uses curves for its guidance schedules (guidance_partial, guidance_full). Declaring one requires spec_dsl: 2.

For audio, the router validates transport shape, base64, URL scheme, and max_bytes. max_seconds requires decoding and is enforced by the runtime.

The normative merge order is:

spec default → selected enum option overrides → explicit request value

Later values replace earlier ones. An object parameter with an object default merges shallowly: request keys win, unnamed default keys remain, and nested objects or arrays replace completely. Set a defaulted field to null only if its declaration is nullable.

The router materializes this cascade before dispatch. Runtime-private defaults cannot silently override the published contract.

Version 1 has four cross-field rules. Three of these examples are FMTTS’s real constraints; the exactly_one example uses placeholder names because FMTTS currently declares none — its duration controls (length_factor, target_duration_seconds) are at_most_one, so omitting both is valid and the model chooses:

{"rule":"exactly_one","params":["<one-of-these>","<or-this>"]}
{"rule":"at_most_one","params":["length_factor","target_duration_seconds"]}
{"rule":"requires_together","params":["solver_atol","solver_rtol"]}
{"rule":"requires","if":{"param":"solver_method","equals":"euler"},"then":["solver_steps"]}

A requires condition can test equals, present: true, or present: false. Every referenced name and condition value is statically checked during publication. Add a new rule only with a DSL version bump; never encode executable predicates into a spec.

Enums can describe large per-model catalogs directly:

{
"name": "voice",
"kind": "enum",
"required": true,
"description": "Target voice.",
"options": [
{
"value": "samantha",
"label": "Samantha",
"meta": {"gender": "f", "accent": "us"},
"overrides": {"temperature": 0.8, "top_p": 0.95}
}
]
}

There is no option-count cap. Specs are per model, so a large speaker catalog does not inflate every other model’s schema. Option overrides may target declared top-level parameters only, and their values must satisfy those declarations.

results.per_item describes model-specific result objects. Supported result kinds are audio, number, text, boolean, and object.

{
"per_item": [
{
"name": "audio",
"kind": "audio",
"format": {"container": "wav", "encoding": "pcm_s16le"},
"description": "The synthesized speech."
},
{"name": "sample_rate", "kind": "number"},
{
"name": "resolved",
"kind": "object",
"fields": [
{"name": "seed", "kind": "number"},
{"name": "implied_length_factor", "kind": "number"}
]
}
],
"delivery": ["inline", "upload"]
}

On the HTTP wire, binary fields use <name>_b64; upload receipts omit the binary field. Request/result IDs, identity, attestation, timing, and errors are part of the outer platform envelope rather than results.

Each example has title, request, and optional notes. Publication validates every example against its own spec. Examples should be realistic enough to drive generated snippets and replay smoke requests, while replacing bulky or secret inputs with documented placeholders.

The meaning of a versioned construct does not change in place. New kinds, rules, or semantics require a spec_dsl bump — the curve kind is exactly such a bump (v1 → v2). We control the current clients, so models may be republished with a newer contract rather than accumulating legacy compatibility branches in one runtime.

Clients should retain descriptions and raw JSON when they encounter an unfamiliar future parameter kind, while relying on the server for authoritative validation.