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. Supported fields are explicit; unsupported execution options reject rather than being silently ignored. 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/devices/trim |
{"device":0,"target_bytes":0}; HTTP 200 schedules eligible reclamation and returns current resource accounting. |
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.
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. 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 and requires streaming. Both modes use the same
generation and retained-session path.
Either max_tokens or max_completion_tokens sets the output limit, from 1
through 16384; supplying both rejects. Omission uses the deployment default.
The limit counts selected tokens, including terminal EOS when generated, so
completion-token usage can exceed the number of visibly decoded tokens.
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: enable_thinking and the model_options
object are passed to source chat policy, which validates their meaning and
renders the model's framing. Reasoning-aware models return reasoning_content
separately from answer content, in both SSE deltas and complete messages.
tools accepts up to 4096 function declarations. Omitted tool_choice or
"auto" allows tool calls. "none" disables tools for the current response,
including when a client retains its tool catalog and prior tool calls while
requesting a conversation summary. Supplied declarations are still validated
and remain in the source-visible catalog, preserving the conversation prefix.
Source sampling receives the original request, and the completion boundary
rejects forbidden structured calls before publication.
Required or named tool choices reject. store may be omitted or false.
The source policy owns tool grammar, safe output boundaries and canonical
history. Generated calls become structured tool_calls; the server never runs
the tools. Invalid generated tool syntax fails rather than publishing a
partially executable call. The client executes tools and returns their results
in the next full conversation history.
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.
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. Voice registration and sample
persistence are not supplied by this endpoint; conditioning assets belong to
the loaded package. File-based players require an encoded-audio profile rather
than raw PCM.
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 PCM16LE 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 works in every build. Its file part explicitly 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. 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. Unknown or repeated IDs reject the request.
Selections affect only that request, including when it shares a batch with
other requests.
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.
Each utility has a 262144-token and 1 MiB decoded-text bound, independent of the
model context and transport body limits. Exceeding a bound returns 413 rather
than 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.