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.
Document shape
Section titled “Document shape”{ "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.
Common parameter fields
Section titled “Common parameter fields”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.
Parameter kinds
Section titled “Parameter kinds”| 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.
Defaults and overrides
Section titled “Defaults and overrides”The normative merge order is:
spec default → selected enum option overrides → explicit request valueLater 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.
Constraints
Section titled “Constraints”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.
Enum metadata and overrides
Section titled “Enum metadata and overrides”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
Section titled “Results”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.
Examples
Section titled “Examples”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.
Evolution
Section titled “Evolution”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.