Repository navigation
CapyCTL 0.1.2
CapyCTL 0.1.2
Engines
- vLLM 0.30.0 is verified beside 0.29.0. Its
--enable-scale-outis
reserved, and--watermark-configand--engram-configneed a named host
approval like every other*-configoption. vLLM 0.29 and 0.30 stream a
reasoning trace asdelta.reasoning; CapyCTL now relays it, where before a
deployment with--reasoning-parserfailed its readiness check. - SGLang 0.5.21 is verified beside 0.5.20. A request to a parked thinking
model now wakes it on both versions; before, the wake check's few tokens went
to the thinking trace and the wake was leftuncertain. - TensorFold 0.6.2, 0.6.3 and 0.6.5 are verified beside 0.6.0 and 0.6.1.
0.6.4 was not checked and showscustom yes. TensorFold 0.6.5's
--api-key,--api-key-fileand--metrics-openare reserved: CapyCTL owns
authentication, and a key on the engine would lock CapyCTL out of its routes
and metrics. - A new guide, Install an engine, walks through
installing vLLM, SGLang or TensorFold in auvenvironment and registering
it. The engines guide has a first model on each engine, with deployment
files indocs/examples/.
Memory
- 16 GB cards. A model whose charge reaches the card's managed limit
starts: memory the card already holds outside CapyCTL (driver reservation,
display server) now comes out of the device reserve instead of being counted
twice, which left such a deploymentqueued. Amemory.requestdeclared for
a card derives its KV cache from the request. Checked with a 9 GB model on a
16 GB RTX 4090 Laptop GPU on vLLM 0.29 and 0.30 and SGLang 0.5.21. - Standalone memory limits are settable.
host.resource_policy.memory.system.managed_limitandfree_reservetake
auto(50 % and 20 %, as before), a size (90GiB) or a percentage (75%),
in YAML, with--setor withCAPYCTL_SET__.... On a discrete machine they
set host RAM; card limits stay derived. See "Standalone memory limits" in the
settings reference. - TensorFold is capped by its declaration. CapyCTL launches TensorFold
withTENSORFOLD_CUDA_MEMORY_LIMIT_GBset to the deployment's Ready
allocation. Before, TensorFold sized itself from the machine's free memory
and could grow past its declaration under long prompts. - Kernel builds leave the startup peak alone. A first start of vLLM,
SGLang or TensorFold that compiles kernels no longer records the compilers'
memory as the deployment's startup peak, which on a unified-memory host left
every later startcapacity_blocked. Builds inside the engine process
(Triton, torch.compile workers) are still counted. - A parked model is charged what it holds. After its first park, CapyCTL
measures the memory a parked engine keeps and charges that, never below the
placeholder (1 GiB on a card, 2 GiB unified) and never above its Ready
charge.capyctl status deployment <name> --jsonshows it underparked. - SGLang
memory.kv_cacheis the KV cache. It now sets SGLang's KV pool,
as on vLLM. A hybrid model's per-request state is sized beside it for
max_concurrent_requests, or as many requests as fit, up to 32. An explicit
memory.requesttoo small for it is refused before launch, naming the
smallest request that fits; a derived one runs as many requests as fit, and
status shows the limit. On a discrete GPU, a derived request keeps the state
inside the KV cache CapyCTL chose, up to half of it, and the fitted context
is held to the rest; status and the start name the same request. Before, a
hybrid model could be held to 2 running requests. - First starts and draft models. The first-start reservation on vLLM and
SGLang covers CUDA graph capture and the load transient
(max(request + graphs, weights x 2.25 + margin)for new deployments). A
draft model's weights and KV layers are counted in the request and in the
fitted context. vLLM gets--max-num-seqs 32by default, and the fit for
gated-delta-net hybrids follows vLLM's own block layout.
Reliability
- An engine is no longer stopped when helper processes it started exit on
their own, such as the torch inductor compile workers SGLang starts with
CUDA graphs on. Only the engine's own processes exiting stops it; a stop
still ends the helpers. - Long prompts. A 256k-token prompt on TensorFold is bounded by the request
deadline until its first token, instead of being cut by the 120 s idle bound
during prefill. SGLang's refusal of a prompt longer than its KV pool now
reaches the client as400 engine_rejectedwith SGLang's message, instead of
a 500 or a stream with no[DONE]. A request the router cuts for its bounds
is cancelled on the engine, as a hang-up is. - Standalone queue bounds (
host.resource_policy.queue) are settable, and
a restart applies changed bounds. - Refusals.
capacity_blockednames the host, the memory domain, what the
deployment needs, what is free and the limit. A start during a slow stop is
answeredstill_stopping(retryable, exit 25) instead of
reconciliation_required. A launch refusal a retry cannot change (an SGLang
sizing refusal,checkpoint_mismatch, a missing capability) gives up after
one attempt.checkpoint_mismatchexplains thatmodel.content_fingerprint
is CapyCTL's digest over the whole model directory, and status prints the
declared and measured digests. --wait.start deployment --waitanddeploy --activate --waitend
at once with exit 13 and the reason when the start gives up, instead of
waiting out the Initialize window. After a redeploy,--waitwaits for the
previous stop instead of returningstill_stopping.- TensorFold first request. The readiness check waits out the kernels
TensorFold 0.6.1 and later build on their first request, within the startup
budget, instead of killing the engine mid-build after 60 s. A build lock left
by a killed start is removed once no recorded process is alive. - Engine registrations. On standalone, a registered profile's approvals,
security.extra_args,envandlog_policyare now applied, as on a host.
Before, standalone dropped them, so an approved drafter path was refused and
extra_args: deniedwas not enforced.capyctl engine addtakes
--approve-optionand--approve-path. capyctl engine removeworks with no role running: it editsengines.yaml
and the role publishes the removal when it starts. A role skips a profile
for an engine it does not know, with a warning, instead of refusing to start.- A local model path outside the model store (for example a Hugging Face
snapshot directory) is measured inside its own root. A foreign listener on a
leased engine port is refusedport_conflictbefore the launch. A failed
deployment can be updated withdeploy --revision.
Features
- Parsers by model family. For Qwen3, Qwen3.5, Qwen3.6 and Qwen3.8
checkpoints, CapyCTL picks vLLM's and SGLang's tool-call and reasoning
parsers from the checkpoint'sconfig.jsonand chat template, so tool calls
come back structured and reasoning comes back apart fromcontentwith no
extra_args. Set them withengine_config.vllm|sglang.tool_call_parserand
reasoning_parser(auto,noneor a parser name). A parser in
extra_argsor host-fixed arguments wins overauto. Status shows a
Parsersline. - TensorFold runs requests together.
engine_config.max_concurrent_requests
renders TensorFold's--parallel, 8 when undeclared; before, TensorFold
decoded one request at a time. Status showsStreams. TensorFold deployments
can turn drafts off withextra_args: [--no-drafts]. - SGLang CUDA graphs are on by default while a model can park, about 75 %
faster decoding on a 16 GB card.engine_config: {cuda_graphs: false}turns
them off. capyctl init hostandinit serverwrite YAML to.yamland.yml
paths. JSON results are indented on a terminal and stay one line when piped.
A missing--configfile is named in the error. Status splits a startup
charge into card and host RAM, and shows a pending operation's reason under
the table. Non-streamed vLLM answers now carryusage.
Packaging
The release archive and the installer no longer ship the guides; the
installer and the systemd units point to
https://edurdias.github.io/capyctl/docs/install/. An upgrade removes the old
share/capyctl/docs.
Upgrade
Replace the binary and restart the roles, the server before its hosts. 0.1.1
state is reused; the server store migrates on first start, so back up each
role's state directory first if you may roll back (see "State and migrations"
in the install guide). Then check these:
- A deployment refused
capacity_blockedsince a first start that built
kernels keeps the peak 0.1.1 recorded. Delete it and deploy it again once. - SGLang with speculative decoding (
--speculative-algorithmin its
arguments) now defaults torestart_only: it is stopped and started instead
of parked, because a wake would not restore the draft model's weights. A
deployment that statesresidency: deeporhost_backedwith it is refused;
remove the residency or setrestart_only, and deploy it again. - SGLang hybrid models with a stated
memory.requestandkv_cacheare
refused when the request cannot hold the per-request state beside the
KV cache. For example, a 48 GiB request with a 16 GiB KV cache and a
DFlash2 draft model no longer fits 8 running requests. Raise the request to
the value the message names, or lowermax_concurrent_requests. - TensorFold deployments now start with
--parallel 8and a memory cap at
their Ready allocation. Aresourcesblock sized for one stream may be
refused at start with "TensorFold's memory cap cannot hold context_length
beside its streams"; lowermax_concurrent_requestsorcontext_length, or
raise the Ready allocation. A--parallelinextra_argsstill wins, but
is refused besidemax_concurrent_requests. Remove--api-key,
--api-key-fileand--metrics-openfromextra_args. - SGLang deployments created on 0.1.1 keep CUDA graphs off. State
engine_config: {cuda_graphs: true}and deploy again to turn them on. - vLLM and SGLang Qwen deployments without parsers get them from their
model family at their next launch, which changes the response shape for
clients that parsed tool calls or<think>out ofcontent. Set the
parser tononeto keep the old output. - Standalone memory limits now follow the role document at every start.
With no limit stated, the default is unchanged.