Weaver Python SDK
August 21, 2026 · View on GitHub
English | 中文
Python client for the NexWeave Weaver server. The SDK mirrors the REST API exposed by
weaver-server and provides ergonomic helpers for training, sampling, telemetry, and
operations management.
Installing locally
pip install nex-weaver
Configuration
Configuration can be provided via keyword arguments or environment variables:
WEAVER_API_KEYWEAVER_ORGANIZATION_ID/WEAVER_PROJECT_IDfor canonical IDsWEAVER_ORGANIZATION/WEAVER_PROJECTfor UUIDs, slugs, or display names
Canonical IDs take precedence. When no organization or project is configured, the server keeps its stable personal-organization/default-project fallback.
Training tensor transport
Training tensors use inline JSON by default, preserving the behavior of existing clients. To opt into compressed binary transport, configure the service client:
from weaver import ServiceClient
with ServiceClient(
tensor_transport="http-binary",
tensor_compression="zstd", # Optional: Zstandard is the binary-pack default.
) as client:
...
AsyncServiceClient accepts the same options. The runnable Pig Latin examples expose
the same settings as command-line options:
python examples/pig_latin.py \
--tensor-transport http-binary \
--tensor-compression zstd
All clients also honor environment variables, so the same example can be configured without command-line options:
WEAVER_TENSOR_TRANSPORT=http-binary \
WEAVER_TENSOR_COMPRESSION=zstd \
python examples/pig_latin.py
| Transport | Compression | Behavior |
|---|---|---|
default | Ignored | Legacy inline JSON (the default) |
http-binary | raw | Binary tensor packs without compression |
http-binary | zstd | Zstandard-compressed binary tensor packs |
tensor_compression (or WEAVER_TENSOR_COMPRESSION) only takes effect with
http-binary. For cross_entropy requests, the SDK moves eligible dense input tensors
into the binary pack; control metadata and other values remain JSON. When an operation
returns output tensors, the SDK downloads and materializes them back into the legacy
public response shape automatically. Datum construction, training calls, and result
handling therefore do not need to change. Keep the default transport when connecting
to an older Weaver server/trainer deployment that does not support binary tensor packs.
Quickstart
from weaver import ServiceClient
def main():
with ServiceClient() as client:
session = client.ensure_session()
print(session)
if __name__ == "__main__":
main()
Give a new Session an optional experiment name and searchable string labels while selecting its scope by human-readable references:
with ServiceClient(
organization="research",
project="alignment",
name="PPO baseline",
labels={"dataset": "math", "environment": "staging"},
) as client:
client.ensure_session()
Empty name/labels are omitted from the create request, preserving compatibility
with legacy servers and existing call sites.
weaver organizations list
weaver projects list --organization research
weaver scope resolve --organization research --project alignment
Organization slugs are globally unique. Project slugs and names are unique inside their organization; an ambiguous display name is rejected instead of guessed.
Usage
See examples/weaver_walkthrough.ipynb for an interactive
walkthrough of the full SDK workflow using a Pig Latin translation task — covering data
preparation, LoRA / full fine-tuning, sampling, and checkpoint management.
For a complete runnable script, see examples/pig_latin.py.
For large packed datasets, examples/streaming_sft.py
shows bounded token-budget batching and submit-ahead.
Generation control (full fine-tuning only)
RL weight swaps need in-flight generation to stop before new weights land. A sampling client can freeze its inference engine and resume it afterwards:
with sampling_client.paused(mode=PauseMode.ABORT):
path = training_client.save_weights_for_sampler(name="step-42")
new_client = service.create_sampling_client(
model_path=path, model_id=model_id, base_model=base_model
)
Two things to know before using it:
- The pause is engine-wide, not per sampling session. It freezes every in-flight request on the engine serving that model, including ones issued through an earlier sampling session. That is what makes it usable for weight swaps — the requests you want to abort belong to the previous weight epoch — but it also means a pause is never scoped to "just my requests".
- Full fine-tuning only. Those models get a dedicated engine. LoRA adapters are served from one shared engine per base model, where a pause would abort generation for unrelated tenants, so the call is rejected before any request is sent.
Prefer paused() over calling pause_generation() / continue_generation() directly:
a pause that never reaches its resume leaves the engine frozen indefinitely, and there is
no server-side auto-resume. The async client mirrors this as async with.
HuggingFace weights export
Checkpoints are stored in the trainer's native distributed format. export_weights()
converts one into a HuggingFace directory — a full model for full fine-tuning, a PEFT
adapter for LoRA — and download_weights() fetches it to disk:
artifact = training_client.export_weights() # save current weights + export
artifact = training_client.export_weights(checkpoint=ckpt) # export an existing checkpoint
service.download_weights(artifact, "./hf-weights")
weaver checkpoint export weaver://<model>/checkpoints/step-42
weaver checkpoint download weaver://<model>/checkpoints/step-42 -o ./hf-weights
Three things to know:
- Export is explicit, download never triggers one. Converting a full model is minutes
of compute and tens of GB of storage, so
download_weights()fails with a "run export_weights first" error rather than silently starting a conversion. - Artifacts expire independently of their checkpoint (7 days by default,
ttl_secondsto change). Deleting the source checkpoint does not delete the artifact, and vice versa. - LoRA exports an adapter by default. Pass
merge_adapter=Trueto fold it into the base model and get a full HF model instead; it is rejected for full fine-tuning models.
Downloads run in parallel, resume interrupted files, refresh expired URLs, and verify each
file's sha256 before publishing it. Both methods have Async* twins.
Deploying a checkpoint
deploy_checkpoint() publishes a checkpoint as a public, OpenAI-compatible endpoint: the
server converts it to HuggingFace format, launches a dedicated inference workload, and
registers that workload on the NorthGate gateway under the name you choose.
deployment = training_client.deploy_checkpoint(ckpt, name="my-chat-model")
print(deployment.endpoint) # OpenAI-compatible URL
service.list_deployments()
service.get_deployment(deployment.id)
service.delete_deployment(deployment.id)
weaver deployment create weaver://<model>/checkpoints/step-42 --name my-chat-model
weaver deployment list
weaver deployment get <deployment-id>
weaver deployment delete <deployment-id>
Four things to know:
- Publishing is permission-gated and off by default. The
deployment.publishcapability is granted by principal origin, not by Weaver role: an SSO session always qualifies, an API key only when it was minted under an IAMbiz_codeon the server's allowlist, and a service credential never. A server with the feature switched off answers 503. Both cases raise aWeaverAPIErrorthat names what has to change. Listing, reading and deleting your own deployments need no capability — whoever published an endpoint can always take it down. - A deployment is independent and long-lived. It does not share the training inference instance, it outlives the training session, and it holds its GPUs until you delete it. It also pins the source checkpoint and the exported artifact against garbage collection.
- The name is global and must be valid everywhere it lands. It is the served model name,
the gateway's
model_name, and a Kubernetes label at once: at most 63 characters of letters, digits,.,-and_, starting and ending alphanumerically.overwrite=Truereplaces an existing gateway registration; it does not free a name Weaver already uses. - It takes tens of minutes, dominated by the conversion. Pass
wait=Falseto get anOperationHandleinstead of blocking. Every method has anAsync*twin.
Ecosystem
NexRL is the companion RL training framework. In its training-service mode, NexRL orchestrates the full RL loop (rollouts, trajectory collection, policy updates) while Weaver handles the underlying training and inference services.
Use Cases
OpenClaw Autonomous Learning —
MetaClaw has integrated Weaver as an RL
backend. By setting rl.backend=weaver, MetaClaw turns every live conversation into a
learning signal and uses Weaver for cloud-based LoRA training, enabling personal agents
to continuously evolve without a local GPU.
Deep Dive
For more technical details, see Deep Dive into Weaver.