Serving HTTP profiles¶
All TCP and Unix listeners expose the same catalog and HTTP endpoints.
Inference requests select a registered deployment through the model field
in JSON or multipart form data. Names come from
model documents, not checkpoint filenames or an inferred
architecture. An absent model returns 404.
The text, image, embedding, speech and transcription profiles use familiar OpenAI response shapes, but are not the entire OpenAI API. Each profile describes the fields it consumes and the options delegated to the model. Chat requests accept additional fields so clients can send metadata and model-specific extensions. Acceptance alone does not establish that an option affects generation. The server has no built-in authentication or TLS termination.
Request framing¶
Request bodies accept either Content-Length or HTTP/1.1
Transfer-Encoding: chunked. Supplying both rejects. Chunked transfer is decoded
incrementally into the same bounded request body; the complete request is
validated before routing. --request_body_bytes bounds decoded body bytes,
independently of bounded HTTP/chunk framing and multipart metadata. This permits
upload clients that do not know the body length in advance; it does not turn an
inference endpoint into a continuing microphone-input session.
Multipart fields retain their names, ordering and binary contents through
request ownership. Consumers use those parsed fields without interpreting MIME
boundaries again. Part order does not control routing. Unsupported transfer
encodings, ambiguous framing and malformed/truncated multipart bodies reject.
Expect: 100-continue is unsupported; upload examples explicitly clear that
header.
Discovery and administration¶
| Method and path | Input or result |
|---|---|
GET /healthz, GET /readyz |
Server availability: {"status":"ready"}. |
GET /v1/models |
Prepared deployments not being removed, with input/output modalities and a loom capability object. |
GET /v1/status |
Every registration, progress/errors, admission, scheduler state and shared server resources. |
POST /v1/models/register |
Complete model document, optionally with uploaded source files; HTTP 202 accepts registration intent. |
POST /v1/models/drain, POST /v1/models/undrain |
{"model":"name"}; HTTP 200 acknowledges the gate change. |
POST /v1/models/unregister |
{"model":"name","mode":"drain"}; mode may instead be "abort"; HTTP 202 accepts removal. |
POST /v1/models/activate, POST /v1/models/deactivate |
{"model":"name","component":"primary"}; HTTP 202 accepts a residency operation on a healthy drained registration. |
POST /v1/models/pin, POST /v1/models/unpin |
Same component selector; HTTP 200 changes parameter eviction policy. |
GET /v1/models/variants |
Native executable inventory or one candidate's retained compilation diagnostics. |
POST /v1/models/variants |
Stage native providers against a ready base without selecting them. |
POST /v1/models/variants/select |
Select a ready candidate with expected-current protection. |
POST /v1/models/variants/remove |
Remove an unused terminal candidate. |
POST /v1/pools/trim |
{"pool":"interactive","target_bytes":0} selects an explicit pool; HTTP 200 schedules eligible reclamation and returns that pool's current resource accounting. |
POST /v1/devices/trim |
{"device":"gpu"} prunes unused compiled generations on that device without trimming any pool. Live model catalogs retain their compiled products. |
GET /metrics |
Prometheus text counters and ownership gauges. |
GET /logz, GET /logz/stream |
Bounded JSONL polling or SSE with independent cursors. |
GET /v1/requests, GET /v1/sessions |
Bounded live owner inspection; sessions require a retained-text service. |
Prepared but drained or weight-evicted models remain discoverable.
created is the registration timestamp; owned_by is "local".
loom.kind, optional loom.endpoint, and loom.tokenize describe the deployment
role, HTTP inference route, and loaded tokenizer. Native-only activity models
omit loom.endpoint; HTTP inference requests to them return 404. Management and
tokenizer utilities remain available according to their normal capabilities.
Text also reports configured context_length and
limits.max_output_tokens, not training context or total cache-pool capacity.
loom.input_tokens:true advertises exact prepared-chat input counting.
Capabilities do not establish learned quality or support for every optional field.
Prepared text models declare reasoning explicitly. Models with an effort
selector also publish supported_reasoning_levels and
default_reasoning_level, the policy used when a request omits reasoning
controls. For example, a Qwen policy can advertise:
{
"reasoning": true,
"supported_reasoning_levels": ["none", "low", "medium", "xhigh"],
"default_reasoning_level": "none"
}
These are fields of each /v1/models row, not server-wide settings. An absent
declaration means unknown; reasoning:false means the model does not support
reasoning. Accepted aliases need not appear as distinct levels. Discovery
reflects source policy without inferring capabilities from the model name.
Residency controls default to component:"primary". A text deployment with an
image-input component also accepts component:"image"; an image-generation
deployment's normal computation is still primary. An undeclared component
returns 404; an invalid selector returns 400. Drain and unregister apply to the
whole registration and do not accept a component selector.
Operation specifies asynchronous control completion, conflict and overload behavior. Observation specifies model selection, cursors, SSE resume and inspection pagination.
Native executable variants¶
A prepared service supporting native updates reports
loom.variants:{"selected":12,"count":2} in model discovery and status.
Text, sequence, image, embedding and decision services enable this capability through
nonzero variant_capacity. Disabled or custom services without the capability
return 501. A registration must have a healthy prepared service; drained
admission is permitted, removal is not.
GET /v1/models/variants requires X-Loom-Model: scout and an empty body.
It returns object:"list", model, selected, count, offset, data and
next_offset. Optional X-Loom-Offset selects a zero-based index; each page
contains at most 64 entries in creation order. This is live inventory, not a
stable snapshot across concurrent removals. Instead, X-Loom-Variant: 13
selects one full record, including compilation diagnostics. The variant and
offset headers are mutually exclusive.
Staging accepts:
{"model":"scout","base":12,"base_directory":"/srv/models/scout/candidate","sources":["config.loom","advance.loombc"],"wait":true}
base identifies a ready choice in this registration. It asserts that the
new providers preserve its resource contract; it does not merge in the base's
providers. sources supplies the complete native provider set. With a JSON
request, relative paths require an absolute base_directory; otherwise paths
must be absolute. Files are read by server-side compilation workers. A multipart
request instead supplies an immutable source package, described below.
prepare, control and other model fields reject.
Omitted or true wait completes the HTTP request after JIT with 200 for ready
or 422 for failed compilation. False returns 202 after accepting construction.
Both responses contain selected and variant. A variant record contains
object:"variant", id, base, phase (compiling, ready or failed) and
users (explicit consumer references). Terminal failures retain an error
with code/message. Detailed records also include available compilation
evidence. Its stage_duration_sum_ns sums overlapping stage intervals, as
defined in preparation timing.
Staging never changes the preferred choice, including when JIT fails.
Selection accepts {"model":"scout","variant":13,"expected":12}. The request
completes at the service's safe execution boundary and returns
{"selected":13}. A stale expected ID, pending selection, compiling/failed
candidate or candidate from another registration rejects without switching.
Selection failure leaves the previous preference intact. Query inventory after
an error or interrupted connection to inspect the actual preference.
Execution replacement preserves preparation/control VM programs, stage declarations and specialization values, parameter placement, and retained state. It can replace command programs and their reachable kernels, or direct-dispatch implementations including resident pipelines. It does not rerun weight preparation. Text selects between retired epochs; embedding and decision services select outside their active cohort; image services finish the whole image and CPU encoder before selection. Prepared but unsubmitted work may use the new execution because its request/result layout is unchanged. Image command part counts and transient requirements may change without relinking the control VM. Changing preserved model contracts requires a new registration.
Removal accepts {"model":"scout","variant":12} and returns {"removed":12}.
Compiling or consumer-retained entries cannot be removed. Missing entries return
404; conflicts return 409; exhausted pool or management-observation credit
returns 429. Unknown, duplicate or wrongly typed fields return 400.
IDs never recycle within the server process, including after unregister/register. They are not persistent identifiers across restarts. Compilation results remain until explicit removal or model retirement. Disconnect withdraws the reader, not accepted compilation or selection. Unregister withdraws waiting compilation readers with 410 while still joining the compiler's actual model ownership.
Uploaded source packages¶
Registration and native staging also accept multipart/form-data: exactly one
request part contains the same JSON as the corresponding endpoint, and repeated
file parts carry programs and metadata. Each file's filename is a
case-sensitive UTF-8 path inside the package, not a server filesystem
destination. The request filename defaults to
request.json; its directory is the base for relative source references.
For a local candidate.json containing
{"model":"scout","base":12,"sources":["config.loom","advance.loombc"]}:
curl --fail-with-body -H 'Expect:' \
http://127.0.0.1:8080/v1/models/variants \
-F 'request=@candidate.json;type=application/json;filename=model/candidate.json' \
-F 'file=@config.loom;filename=model/config.loom' \
-F 'file=@advance.loombc;filename=model/advance.loombc'
Package filenames use / separators and contain no empty, . or ..
components, absolute paths, backslashes, drive prefixes or control characters.
Authored relative references may use .. to reach a shared provider inside the
package, but cannot escape its root. Missing members fail without falling back
to server-local files. Explicit absolute source paths still name server-local
providers, so an upload may combine changed files with existing local inputs.
Filenames are literal UTF-8, not URL-decoded.
Parts may arrive in any order; duplicates and malformed/truncated bodies reject
before registration or staging. File bytes are retained in memory independently
of the request connection, including binary .loombc. Native staging releases
its source ownership after constructing independent executables and copied
diagnostics. Registration retains the package until the model retires, including
when admission:"drained" defers preparation. These are code packages, not
checkpoint transfers or a persistent package catalog.
For POST /v1/models/register, the request part is the complete
model document. Relative prepare, control, native
providers, tokenizer metadata and source-declared weight-preparation policies
resolve inside the package. An image or finite model's relative asset directory
also applies within that namespace when loading metadata. Absolute locators
still explicitly select server-local files.
Parameter checkpoint bytes always use the server filesystem, independently of
the package namespace. An absolute base_directory in the model document binds
relative checkpoint paths there; it does not redirect relative code or metadata
away from the package. For example, checkpoint:"." or assets:"." can let a
model read uploaded tokenizer metadata while its source-declared weight files
resolve under base_directory:"/srv/checkpoints/model".
The normal --request_body_bytes limit covers the complete multipart body;
there is no implicit upload exception. Both content-length and chunked framing
are accepted. The empty Expect header above suppresses an unsupported
100-continue handshake. This endpoint loads executable code and has the same
trusted-network boundary as model registration.
Chat completions¶
POST /v1/chat/completions accepts a text deployment and a message history:
{
"model": "scout",
"messages": [{"role":"user","content":"What is 2+2?"}],
"stream": true,
"stream_options": {"include_usage": true},
"max_tokens": 16,
"temperature": 0
}
Omitted, false or null stream selects one chat.completion JSON response with
the complete assistant message, finish reason and usage. stream:true selects
incremental chat.completion.chunk SSE events. stream_options.include_usage
adds a final usage-only chunk when streaming; null or omission leaves it off.
stream_options has no effect on a nonstreaming response, and additional members
such as include_obfuscation are ignored. Both modes use the same generation
and retained-session path.
max_completion_tokens sets the output limit. If it is absent or null,
max_tokens applies; if both are absent or null, the deployment default applies.
When both have values, max_completion_tokens takes precedence. The selected
limit must be an integer from 1 through 2147483647 and fit the deployment's
context and output allowance.
The limit counts selected tokens, including terminal EOS when generated, so
completion-token usage can exceed the number of visibly decoded tokens.
Input and reserved output must fit the deployment's context. Overflow rejects
with HTTP 400 instead of silently trimming history or reducing the output
budget. The combined-budget diagnostic reports input tokens, max_tokens,
required stored tokens, and capacity; the last selected output token does not
need another stored position. A source input check may reject earlier, before
the full input count is available.
temperature, top_p, top_k and seed are numeric request fields interpreted
by the model's source policy. Omission uses source-defined deployment defaults;
unsupported values reject before admission. The server carries the validated
policy to the model program without reading logits back to the host. Supported
sampling algorithms and value ranges belong to the model package.
Sampling is separate from thinking: the boolean enable_thinking, string
reasoning_effort, and model_options object are passed to source chat policy,
which validates their meaning and renders the model's framing. Supported effort
levels, aliases, and omitted-control defaults belong to the model package;
unsupported or contradictory controls reject before admission. Reasoning-aware
models return reasoning_content separately from answer content, in both SSE
deltas and complete messages.
preserve_thinking and chat_template_kwargs also pass to source policy. The
model package defines which controls it consumes, their types, and how top-level
and nested spellings combine. Native admission interprets only transport and
resource controls; other request fields remain intact for the model program.
Request compatibility¶
Additional members in request, message, supported content-part, image/video descriptor, and tool-call history objects do not cause rejection. The complete original request remains available to the model program. Required values, supported content types, and the types and bounds of consumed fields are still checked: an unknown content type is an error rather than a silently omitted part of the prompt. Duplicate native fields reject, including a null followed by another value.
Optional native controls accept null as omission. This includes tools, output
limits, stream_options, media_io_kwargs, image detail, and optional message
fields such as reasoning_content and tool_calls. Source-owned sampling and
template options use the model package's defaults and validation rules.
| Request fields | Behavior |
|---|---|
model, messages, stream, output limits, stream_options.include_usage |
Native routing, input preparation, response framing, and resource limits. |
tools, tool_choice, parallel_tool_calls, template and sampling options |
Passed to the model program; supported behavior belongs to its package. |
stop, presence_penalty, frequency_penalty, response_format |
Passed to the model program. Native acceptance does not promise stop-string matching, penalties, or constrained output. A policy that does not consume them ignores them. |
n, logprobs, top_logprobs |
Accepted and passed through. The native chat response currently contains one choice and logprobs:null. |
user, metadata, store, and other unconsumed extensions |
Accepted without a native effect; available to model policy. store does not create a retrievable completion archive or control session/prefix caching. |
Clients can send familiar defaults such as n:1, stop:null, logprobs:false,
zero penalties, and response_format:{"type":"text"} without a native
unsupported-field error. Model documentation specifies which non-default
options affect generation. Model configuration and administration endpoints
retain their own documented schemas.
Tools and output¶
tools accepts up to 4096 entries. The catalog, tool_choice (including
"required" and named-function objects), parameter schemas, and metadata such
as strict pass through to the model program. They are not a native execution
allowlist or a promise of constrained generation. Model-specific prompting and
sampling determine how choices affect generation. The server returns generated
calls without checking their names against the catalog or rejecting them based
on tool_choice. Null or omitted tools supplies an empty catalog.
The source policy owns tool grammar, safe output boundaries and canonical
history. Generated calls become structured tool_calls; the server never runs
the tools. Argument strings are opaque at the HTTP boundary: unknown functions,
missing or extra arguments, duplicate members, and even invalid argument JSON
can reach the client. The harness validates arguments and permissions before
execution, then returns either a result or a validation error as a role:"tool"
message in the next full conversation history. A bad read({"q":"file"}) call
can therefore receive a missing-path error and be corrected by the model
without retrying an earlier successful image-generation call.
For model programs selecting the optional XML codec, schema type hints recover JSON values from untyped parameter text. Unknown parameters remain literal strings; duplicate parameters remain duplicate JSON members in their original order. No required-field, range, enum, or permission checks run in that codec. The model-authoring contract describes its representation rules.
Generation stopped by max_tokens returns finish_reason:"length", including
when complete tool calls precede the limit. An unfinished final XML call is
omitted rather than repaired or presented as executable. Completed calls,
visible text, reasoning, and token usage remain available; the client decides
how to continue. EOS returns tool_calls when calls were generated, or stop
otherwise. History with an omitted suffix cannot reuse different retained
device state.
An output translation failure returns an error object with
"type":"loom_output_error": HTTP 500 for a nonstreaming completion, or an SSE
error followed by [DONE] after streaming has begun. This is distinct from
loom_request_error, which describes a rejected request. Both carry a human
message and a code naming the native failure, such as INVALID_ARGUMENT,
OUT_OF_RANGE, or UNAVAILABLE. Admission and checkpoint failures distinguish
temporary unavailability (503) from internal failure (500).
Broken model framing, including an unterminated XML parameter at EOS, is a
translation error; its diagnostic identifies the call ordinal and byte offset.
A failed tool-call list publishes no partial structured calls.
Earlier successful tool results remain the client's conversation history;
resuming an unfinished turn preserves those results and any created artifacts
rather than repeating their side effects. The server does not automatically
retry generation or execute a repaired call.
Image input¶
An image-capable text deployment reports both text and image input
modalities. Message content may be an ordered array of text and image_url
parts. A text part contains type:"text" and text; an image part contains
type:"image_url" and an image_url object with url and optional detail.
Detail is auto by default, or low or high, with interpretation owned by
the model's source policy.
Image URLs must be base64 data URLs. The server does not fetch network URLs or
read image paths from the request. Image-enabled builds always accept static
PAM (image/x-portable-arbitrarymap) without third-party codecs. PNG, JPEG,
GIF, and WebP are enabled by default and can be omitted individually without
removing image input or other serving modalities. PAM supports standard
black-and-white, grayscale, and RGB tuples, with optional straight alpha and
sample maxima up to 65535, normalized to RGB8 or RGBA8. Animated or multi-image
inputs, MIME/signature mismatches, and images exceeding source-declared byte,
dimension, or pixel limits reject.
The normal HTTP request-body limit still applies.
Multiple images and concurrent requests can share projection cohorts. Features remain device-resident through language input; a disconnected peer does not change the surviving images' association. Full conversation history remains required for retained reuse. Image identity includes the compressed bytes, ordered placement, detail and resolved source metadata, so reuse cannot cross a change in those semantics. A complete retained media prefix skips pixel decoding and projection; it does not require a separate image-feature cache.
Models with temporal source policy accept the vLLM pre-extracted JPEG
sequence convention
through video_url. A request can interleave video, images, and text:
{
"model": "your-vision-model",
"messages": [{
"role": "user",
"content": [
{"type": "video_url", "video_url": {
"url": "data:video/jpeg;base64,<first-JPEG-base64>,<second-JPEG-base64>"
}},
{"type": "text", "text": "Describe how the scene changes."}
]
}],
"media_io_kwargs": {"video": {
"fps": 100,
"frames_indices": [0, 75],
"num_frames": -1,
"do_sample_frames": false
}}
}
Each comma-separated payload is a complete JPEG image. Frame timestamps are
frames_indices[i] / fps, giving 0 and 0.75 seconds in this example. fps
defaults to 1 and must be positive and finite; omitted indices default to
consecutive integers starting at zero. Explicit indices are nonnegative,
nondecreasing integers with exactly one entry per supplied frame. Repeated
indices are valid. Timing metadata applies to every video in the request.
This subset consumes client-selected frames. Set num_frames: -1 to consume
all supplied frames; with the field omitted, clips are limited to 32 frames,
matching vLLM's default without silently discarding additional frames. Positive
num_frames and do_sample_frames: true are unsupported. Optional
total_num_frames must be positive and at least the supplied frame count;
duration must be finite, nonnegative seconds. These describe the original
clip and do not resample the selected sequence.
Each frame counts toward the source-declared image input limit and uses the existing compressed-byte and geometry bounds. The model policy owns temporal grouping, spatial resizing, timestamp tokens, and positional encoding. Remote URLs, MP4/WebM containers, and other media-I/O options are rejected. Frame input uses the JPEG codec and requires no video-container decoder.
Retained conversations¶
Untagged requests can reuse a matching completed conversation still resident in an idle execution row. The server selects the longest reusable canonical history, including exact ordered image identity, and processes only the new suffix. This works without configuring extra cache or checkpoint capacity. Active requests and named sessions are not candidates for this automatic row selection. Reuse does not assign a persistent session identity to the client.
X-Loom-Session: SESSION requests best-effort reuse of a named history. Requests
still supply full history. On an ordinary cache miss the server can replay that
history; a session name is not a durable chat database. Streaming and JSON turns
can alternate without changing the continuation contract.
Usage reports reused tokens in prompt_tokens_details.cached_tokens. Edited
history, source-ineligible completions, and evicted state replay normally.
An idle row advances in place; retaining an older branch point requires an
explicit checkpoint. session_cache_capacity retains additional named
histories beyond execution rows, while prefix_cache_capacity captures shared
intermediate input prefixes. Both remain separately configured limits.
Prefix caching retains exact state at natural prefill boundaries. An unused
cache slot can also retain a later boundary near the prompt's end, reducing
replay when a client drops generated reasoning or changes a later message.
This extension never evicts another prefix. New branches and memory pressure
can still reclaim entries, and a one-slot cache retains its first matching
ancestor. Reuse depends on exact token and media identity, not message labels;
cached_tokens reports the prefix actually used.
X-Loom-Checkpoint: NAME requests an explicit immutable endpoint for the
destination session. It is not best effort: an unknown name returns 404 and a
history mismatch returns 400 before replacing the destination continuation.
Session and checkpoint names are scoped to the selected deployment, so equal
names on two models do not share state.
Named checkpoints¶
The deployment needs nonzero checkpoint_capacity. Control requests carry
X-Loom-Model: scout; this selector may be omitted only when exactly one
deployment is registered. Names contain 1–64 ASCII letters, digits, underscores,
dots or hyphens. They are identifiers, not authentication tokens.
| Request with empty body | Effect |
|---|---|
POST /v1/checkpoints/NAME with X-Loom-Session: SESSION |
Pin an idle completed session; HTTP 201 reports its consumed token position. |
POST /v1/checkpoints/NAME/suspend |
With elastic backing, park the endpoint in host memory; HTTP 200 reports host_snapshot_bytes. |
DELETE /v1/checkpoints/NAME |
Release the explicit pin; HTTP 200 after accepted release. |
Absent sessions or checkpoint names return 404; busy sessions, duplicate names or queued borrowers preventing deletion return 409. Exhausted pin capacity returns 503. Fixed backing rejects suspension with 400.
Deletion remains available while inference admission is drained, including when inference requests are waiting. It uses the normal bounded checkpoint control slot. An unprepared registration returns 404 without preparing the model. Pinning and suspension wait for inference admission to reopen.
Pin completion publishes an immutable consumed frontier and the exact pending token. The next completion still sends full history extending the endpoint's canonical transcript. Another destination session forks that endpoint; the same destination rewinds it. This supports keeping a tool-output summary while discarding the verbose tool text from subsequent context.
Suspension moves the endpoint's placement without resetting live branches. Its host image is process-local, not a file or durable disk cache. Resuming warms it into new pool IDs. On unified-memory hardware, host storage does not create additional physical RAM. Explicit pins stay protected until released; automatic idle caches may yield under pressure.
Accepted pin/suspend work survives client disconnect through actual readiness. Deleting a pin does not invalidate active branches that already own independent references. An impossible reservation or refused restore returns overload without silently replacing the destination's continuation.
Known-token sequences¶
POST /v1/sequences accepts a deployment with kind:"sequence". The caller
supplies exact known IDs; the server does not tokenize text, select an extra
token, or advance beyond those IDs. Each successful request streams SSE.
curl -N http://localhost:8080/v1/sequences \
-H 'Content-Type: application/json' \
-d '{"model":"encoder","position":0,"token_ids":[11,27,42],"options":{}}'
An initial request omits session and has position:0. The attached event
returns a server-issued handle. Each chunk contains that handle, its consumed
position and next_position, exact token_ids, and opaque source bytes in
data with encoding:"base64". Chunk boundaries follow actual execution
cohorts, not the HTTP request's token count. A final done event publishes the
completed position. For example:
event: attached
data: {"session":"0123456789abcdef0123456789abcdef","position":0}
event: chunk
data: {"session":"0123456789abcdef0123456789abcdef","position":0,"next_position":3,"token_ids":[11,27,42],"encoding":"base64","data":"AQIDBA=="}
event: done
data: {"session":"0123456789abcdef0123456789abcdef","position":3}
The bytes above are illustrative; their shape, dtype, and interpretation belong
to the model package. Append with the returned session, the exact completed
position, and only the new token_ids. options is an optional source-defined
object. Unknown, duplicate, or wrongly typed fields reject; IDs are nonnegative
int32 values and the array must be nonempty. Context and shared pool limits
apply before admission. X-Loom-Session is not accepted on this endpoint:
identity is the session body field.
A stale position or concurrent use of the same handle returns 409. A handle that has been evicted, unregistered, or invalidated returns 404. Handles survive execution-row reuse while retained in the model's session cache; they are not durable across registration replacement or server restart.
The existing checkpoint controls can pin a completed
sequence, using its handle as X-Loom-Session. To fork, send
X-Loom-Checkpoint: NAME, omit session, and supply the checkpoint's consumed
position with the new IDs. The response receives a new handle. Sequence
checkpoints have no implicit pending prediction.
Completion means local device retirement and output acceptance, not peer acknowledgement. A disconnected in-flight append invalidates its handle after accepted work retires; an independent pinned checkpoint remains usable. A transport failure after the final response was accepted can be ambiguous to the caller. The endpoint is not an exactly-once retry protocol: recover from a known checkpoint or replay the full known history under a new handle.
Output credit is bounded per active row. A slow reader removes only its row from new cohorts; other credited sequences can continue. Bytes are captured before reusable model scratch is overwritten and downloaded asynchronously for HTTP encoding.
Speech synthesis¶
POST /v1/audio/speech accepts a speech deployment:
The response streams signed little-endian PCM16 at 24 kHz, mono, with
Content-Type: audio/pcm;rate=24000;channels=1. Completion closes the response.
Network reads need not coincide with waveform blocks; consumers retain any
partial sample between reads. Disconnection suppresses further generation
while already accepted work retires.
Requests have independent voice, progress and output credit. Compatible next operations may share numerical batches, including requests arriving after generation has begun. A stalled reader excludes its own request from further work while peers continue. Batch boundaries are internal execution boundaries; they do not add a multiplexing protocol to the client's PCM stream. The streaming walkthrough shows complete requests and playback, and the source contract explains how packages define compatible work.
model, nonempty UTF-8 input, voice and explicit response_format: "pcm"
are required. voice is a package-supported identifier or an object containing
an id string. Optional instructions, speed and seed reach source request
preparation, which rejects controls it cannot implement. Speed defaults to 1
and accepts 0.25–4. The seed extension accepts an unsigned 64-bit integer or
decimal string and defaults to zero.
stream_format may be omitted or set to "audio". Encoded formats, SSE and an
omitted response format return an input error. Uploaded reference recordings
use the separate voice-registration endpoint below. File-based players require
an encoded-audio profile rather than raw PCM.
Voice registration¶
POST /v1/audio/voices registers a reference recording with a speech ABI 2
model. Its multipart fields are model, a nonempty UTF-8 id, and file.
The file accepts the same WAV, MP3, FLAC, and raw PCM inputs as
audio transcription, through the same native parser and
decoder. The model determines which recording lengths and geometry it can use.
curl --fail-with-body -H 'Expect:' \
http://127.0.0.1:8080/v1/audio/voices \
-F model=speaker -F id=narrator \
-F 'file=@reference.wav;type=audio/wav'
A successful response is {"id":"narrator"}. It arrives only after CPU audio
preparation and learned conditioning have completed; subsequent speech requests
can then select "voice":"narrator". Registration decodes the original geometry
once, and the model's CPU Loom program owns mixing, resampling, and trimming.
Synthesis reuses the resulting conditioning. The server does not write a
prepared recording or weight archive to disk.
IDs are local to the loaded model. A duplicate ID returns 409, exhausted voice or admission capacity returns 503, and an unavailable ID returns 404. A service without the upload ABI returns 501. Disconnecting an unfinished upload cancels publication, while accepted CPU and device work finish retiring safely.
Remove a voice with:
curl --fail-with-body http://127.0.0.1:8080/v1/audio/voices/remove \
-H 'Content-Type: application/json' \
-d '{"model":"speaker","id":"narrator"}'
Removal hides the ID from new requests. Already admitted requests retain their conditioning until completion, so its slot becomes reusable only after those readers retire. Uploaded voices live until removal or model unloading. A model card can name original recordings to register on every load.
Audio transcription¶
POST /v1/audio/transcriptions accepts a recognition deployment and a completed
recording as multipart/form-data. Builds with audio codecs accept WAV, MP3,
and FLAC, converting their sample rate and channel layout to the model's input
geometry:
curl --fail-with-body -H 'Expect:' \
http://127.0.0.1:8080/v1/audio/transcriptions \
-F model=recognizer \
-F 'file=@recording.wav;type=audio/wav'
The default response is {"text":"Recognized speech."}. response_format=text
returns UTF-8 plain text. Independent finalized segments appear in input-time
order, separated by newlines; segment identities break equal-time ties.
| Field | Contract |
|---|---|
model |
Required registered recognition deployment. |
file |
Required nonempty recording in an enabled format, or raw PCM with explicit geometry; the filename never names a server path. |
language |
Optional UTF-8 language hint interpreted by the package. |
prompt |
Optional UTF-8 context interpreted by the package. |
temperature |
Finite number from 0 through 1, default 0; source policy may restrict it. |
response_format |
json by default, or text. |
stream |
Omitted or false for this finite-upload profile. |
Container bytes select the decoder, independently of the filename or MIME
label. audio/wav, audio/mpeg, and audio/flac are conventional labels;
application/octet-stream also works for a supported container. A disabled
codec or unsupported container returns an error. Direct WebM/Opus and MP4/AAC
uploads are not accepted; clients must convert them to an enabled format.
Raw PCM16LE and F32LE work in every build. The file part declares audio/pcm
with a positive sample rate and the interleaved channel count required by the
selected model. The server converts the declared rate to the model's rate.
For a 24 kHz mono recording sent to a 16 kHz mono recognizer:
curl --fail-with-body -H 'Expect:' \
http://127.0.0.1:8080/v1/audio/transcriptions \
-F model=recognizer \
-F 'file=@recording.pcm;headers="Content-Type: audio/pcm;rate=24000;channels=1"'
These bytes contain whole PCM16 frames without a WAV header. Add format=f32le
to the media type for interleaved little-endian F32 samples; every sample must
be finite and between -1 and 1. format=s16le is the default. Missing or duplicate
geometry, nonpositive rates, mismatched channel counts, and partial frames reject.
Raw PCM uses bandlimited rate conversion with continuous filter history across
worker chunks; it does not mix channels. Equal rates preserve each sample.
PCM input, its rate conversion, and PCM speech output require no audio codec
dependency.
The request-body byte limit and source-defined input-frame limit apply
independently. The converted raw PCM frame count is checked before model
admission; encoded recordings are checked as decoded frames reach recognition. An
overlong recording rejects without truncation or a partial success response.
Decoding and conversion stage a bounded number of frames on the shared CPU
executor, retaining each recording's codec and resampling history across blocks.
Raw conversion preserves the input duration: N frames produce
ceil(N * model_rate / input_rate) frames, with filter lookahead flushed only
at the recording's end. Conversion storage is bounded per request; a rate ratio
that exceeds that budget rejects without reducing filter quality.
Uploads may share numerical batches while retaining independent sample clocks, model histories, and transcript state. A disconnect cancels that request; upload bytes, decoder state, and model storage remain owned until accepted work retires. Concurrent requests continue through their own input and output credit.
Unknown or duplicate fields, unsupported response formats and stream=true
reject. The finite profile does not emit provisional hypotheses, word timing or
speaker labels. The recognition source contract
preserves revisions and optional alignment/diarization evidence for typed
consumers. A chunked HTTP upload still completes before recognition begins;
it is not an appendable microphone session.
Embeddings¶
POST /v1/embeddings accepts:
input may be one string, an ordered string batch, a token-ID sequence or a batch
of token-ID sequences. encoding_format is "float" by default or "base64";
base64 encodes little-endian IEEE754 F32 values. dimensions requests a
source-supported width, not arbitrary host truncation. Optional user is a
string accepted as client metadata. Unknown fields reject.
The response is an object:"list" with ordered embedding objects, original
per-request indices and token usage. Several requests can share one dispatch
cohort without sharing response identities. Source programs own tokenization,
pooling, projection, normalization and capacity. Cancellation suppresses that
client's response without invalidating peers in the cohort.
Image generation¶
POST /v1/images/generations generates one image at the requested size:
{"model":"illustrator","prompt":"a red fox in the snow","size":"1536x1024","seed":"0","response_format":"b64_json"}
The response has one data[0].b64_json PNG. Only n:1 and
response_format:"b64_json" are supported. Optional size is WIDTHxHEIGHT;
omitting it uses the deployment's configured width and height. The model's
source validates supported geometry. Sizes are exact, never silently rounded.
A new shape can compile asynchronously on demand while sharing resident weights;
later requests reuse its code within the deployment's specialization-cache
limits. Larger images require more work and memory, and a first request can
include compilation time. Request sizes do not change the deployment default.
seed defaults to zero and accepts an unsigned integer or decimal string.
Optional loras selects adapters registered with the deployment, with an
independent strength for each:
{"model":"illustrator","prompt":"a red fox in the snow","loras":[{"id":"watercolor","scale":0.3},{"id":"ink","scale":0.7}]}
This example requires registered watercolor and ink adapters. Each id is
a nonempty string, not a path or URL to load. scale is a finite F32 number,
defaults to one, and may be negative or zero. Omitting loras, or supplying an
empty list, selects the base model. The list preserves order and repeated IDs,
including a separate strength for each occurrence. Model request preparation
resolves those names against the deployment's registrations. Selections affect
only that request, including when it shares a batch with other requests.
The Krea 2 Turbo model guide shows a complete deployment using original adapter checkpoints. Krea rejects unregistered names even at zero strength, shares prepared factors across requests, and removes registered zero-strength occurrences before capture. Floating-point accumulation follows selection order, so deterministic replay uses the same ordered list.
This endpoint is text-to-image; it does not accept reference-image uploads or imply image-editing support. Unknown fields reject.
Finite decisions¶
POST /v1/systemone accepts a decision deployment, string/object/array state,
and a nonempty map of typed questions:
{
"model": "judge",
"state": "The customer cannot log in.",
"questions": {
"route": {
"type": "choice",
"instructions": "Choose a team.",
"criteria": {"support":"Account access","billing":"Payments"}
},
"urgency": {"type":"score","instructions":"Rate urgency.","criteria":["Low","Medium","High"]},
"needs_reply": {"type":"noul","instructions":"Is a reply needed?"}
}
}
Each question requires a type and instructions. Instructions accept a string, object or array. Choice has 1–255 named options and returns the winning ID plus all probabilities. Score has 2–10 ordered levels and returns an expected zero-based score, probabilities and a legend retaining typed criteria. Noul returns the probability of true. Question and option identities are preserved. The native bound is 1024 questions; source capacity can be smaller.
The source produces normalized F32 vectors. The host validates and encodes them; it does not silently normalize or truncate malformed outputs. Usage reports source input tokens and zero output tokens: this is finite evaluation, not autoregressive generation. Schema/source-input failures return 422. Strict JSON is required; comments, trailing commas, malformed Unicode, duplicate decoded IDs, unknown fields and image-input extensions reject.
Count a chat before generating¶
POST /v1/chat/completions/input_tokens accepts the same JSON body as chat
completions and returns one {"input_tokens":N} response. It applies the
deployment's source-defined chat template, tool catalog, thinking options,
generation framing, and image-position accounting. It counts the complete
fresh input, including positions that a later generation might reuse from cache.
With a Chat Completions body saved as request.json, use the same bytes for
counting and generation:
curl http://localhost:8080/v1/chat/completions/input_tokens \
-H 'Content-Type: application/json' --data-binary @request.json
The count runs on shared CPU workers without loading weights, reserving an execution row or KV capacity, projecting images, or changing retained history. It can progress while every generation row is occupied. It shares the model's source-policy invocation, so it waits for an existing CPU borrower to retire; it does not create another VM or block a worker waiting for model capacity. The normal model routing and drain policy still applies.
The request's generation fields remain validated, but stream:true still
returns one JSON count and no output budget is reserved. Source input is bounded
by the deployment's context capacity; an oversized input rejects instead of
returning a truncated count. A successful count is not a promise that input plus
the requested completion budget will fit, or that execution memory is available.
Omit X-Loom-Session and X-Loom-Checkpoint: this stateless endpoint rejects
them rather than selecting cached history. The count queue uses
pending_capacity; a full queue returns 503. Utility requests appear in request
inspection and outcome counters, but never increment inference-token counters.
Raw tokenization¶
POST /tokenize uses the selected deployment's loaded tokenizer:
It returns {"tokens":[...]}. The shown special-token options are defaults.
POST /detokenize accepts {"model":"scout","tokens":[...]} and returns
{"content":"..."}, preserving special tokens. Neither operation applies
chat templates or model-specific prompt framing. Empty text and arrays are valid.
These CPU utilities do not activate weights, occupy execution rows or wait for an inference epoch. They still obey model admission and retain their tokenizer borrow through actual task completion. An unknown model or absent tokenizer returns 404. Unknown, duplicate, wrongly typed or invalid-token fields return 400. A tokenizer execution failure returns 500 for that request.
Both utilities accept complete long-context inputs without a separate token or
decoded-text quota. The server's configurable --request_body_bytes transport
limit applies to their JSON requests; exceeding it returns 413. Allocation or
tokenizer failures never return a truncated success. with_pieces:false is
accepted; true rejects because this decoder contract does not expose raw token
bytes. prompt,
add_special_tokens and chat-tokenization fields are not aliases for this profile.