river-client
Python client for the River ML training API — sampling, LoRA fine-tuning, and reinforcement learning against River-hosted models.
Installation
pip install river-client
Requires Python 3.12+.
Quick start
River provides a low-level API for sampling, training, and checkpointing, plus an integrated RL library that manages rollouts and training from your environment and reward function. The example below uses the low-level API.
import river_client as river
client = river.Client(api_key="your-key", endpoint="api.river.ai")
# Stateless sampling from a base model
samples = client.sample(
"What is 2+2?",
base_model="Qwen/Qwen3.6-35B-A3B-FP8",
max_tokens=50,
)
print(samples[0].text)
# Training with a session
with client.session() as session:
model = session.create_model(
base_model="Qwen/Qwen3.6-35B-A3B-FP8",
lora=river.LoraConfig(rank=16),
)
# Forward + backward, then an optimizer step
result = model.forward_backward(data, loss_fn="cross_entropy")
model.optim_step(lr=1e-4)
# Sample from the current weights
sample_groups = model.sample("Continue:", max_tokens=100)
Reinforcement learning
river_client.rl manages rollouts and training around your environment, tools,
and reward function. It supports multi-turn conversations, images, KV-cache
reuse, and synchronous or bounded asynchronous training.
import operator
import os
from typing import Literal
import river_client as river
from river_client import rl
from river_client.renderers import get_renderer, get_text_content
# Tool schemas come from type hints and docstrings; execution stays in your code.
@rl.tool
async def calculator(a: float, operation: Literal["+", "-", "*", "/"], b: float) -> str:
"""Calculate the result of an arithmetic operation on two numbers."""
operations = {"+": operator.add, "-": operator.sub, "*": operator.mul, "/": operator.truediv}
return f"{operations[operation](a, b):g}"
class AnswerEnv(rl.Env):
tools = [calculator] # The engine runs tool calls and feeds results back.
async def reset(self, row):
return [{"role": "user", "content": (
row["question"] + " Use the calculator if helpful. Return only the answer."
)}]
async def reward(self, traj, row):
# Score the final answer, excluding the model's reasoning.
answer = get_text_content(traj.messages[-1]).strip()
return float(answer == row["answer"])
# Replace this toy dataset and reward with your task.
dataset = [
{"question": "What is 17 * 23?", "answer": "391"},
{"question": "What is 144 / 12?", "answer": "12"},
]
base_model = "zai-org/GLM-5.3-Flash"
renderer = get_renderer(base_model)
client = river.Client(api_key=os.environ["RIVER_API_KEY"], endpoint="api.river.ai")
with client.session() as session:
model = session.create_model(
base_model=base_model,
tokenizer=renderer.tokenizer,
lora=river.LoraConfig(rank=16),
)
trainer = rl.AsyncTrainer(
engine=rl.RolloutEngine(
model, env=AnswerEnv, renderer=renderer,
budget=rl.Budget(max_turns=4, max_generated_tokens=2048),
# Sampling stays on one policy for the whole trajectory by default.
),
optimizer=rl.Adam(lr=1e-5),
advantage=rl.GroupCentered(), # Center rewards within each prompt group.
completion=rl.GroupCompletion(mode="wait"),
normalize="token",
groups_per_step=2, group_size=8, # Two prompts, eight rollouts each.
max_staleness=0, # Synchronous; >0 allows bounded sampling ahead.
)
rl.run(trainer, dataset, steps=10, on_step=lambda step: print(step.n, step.metrics))
See the bundled agent skill for image observations, cache policies, checkpoint/resume, and evaluation.
Dedicated streaming inference
Gated feature — disabled by default. Contact River to enable dedicated deployments for your team and the checkpoint's base model before using these APIs. Use a team API key with that access; personal API keys cannot create deployments.
client.create_deployment(checkpoint, ...) provisions capacity for a
checkpoint and returns a base URL that the standard OpenAI client streams
from unchanged; list_deployments, get_deployment_usage, scale_on_target
and delete_deployment manage it from there. See the
deployment walkthrough
for a complete example. The bundled agent skill below
carries the full workflow: replica roles, scale-to-zero and resume, streaming
error handling, and usage accounting.
AI agent skill
The package bundles an agent skill — a SKILL.md that teaches AI coding
agents (Claude Code and compatible tools) the current training API:
train_step semantics, data formats, RL/SFT/distillation loop patterns,
image uploads and handles, and fault-tolerant auto-recovery. Because it ships
inside the wheel, the skill always matches the installed client version.
Install it into your agent's skills directory:
python -m river_client.skill --install
This copies the skill into ~/.claude/skills/; pass --dest for a
different location (e.g. a project's .claude/skills/). Run without
--install to print the bundled skill's path instead.
License
Apache-2.0. See LICENSE.
Export downloaded checkpoints to Hugging Face
The converter is available in river-client 0.13.0 and later. In Console, open
an inference checkpoint and choose Download HF export script.
Install the script's dependencies and run it with your API key:
pip install -U "river-client[hf-export]>=0.13.0" requests
RIVER_API_KEY=... python download.py
The script downloads to a temporary directory and writes an HF/PEFT LoRA adapter
under the checkpoint ID. Load that directory with PEFT and the corresponding
base model. Conversion reads the base model's safetensors headers from Hugging
Face; it does not download base weights. Set HF_TOKEN for gated base models.
For a checkpoint already on disk, install the export extra and run the converter:
pip install -U "river-client[hf-export]>=0.13.0"
python -m river_client.hf_export ./river-checkpoint ./hf-adapter
The source directory must contain adapter_config.json and either
adapter_model.safetensors or its sharded index and referenced files. The output
contains adapter_config.json and adapter_model.safetensors in HF/PEFT adapter
format. The converter uses base_model_name_or_path from the adapter
configuration unless you override it:
python -m river_client.hf_export ./river-checkpoint ./hf-adapter \
--base-model "<base-model-id>" --revision "<base-model-commit>"
Use --base-model /path/to/local/base to read a local base model's safetensors
headers. In Python, call the same converter directly:
from river_client.hf_export import export_hf_adapter
output = export_hf_adapter("./river-checkpoint", "./hf-adapter")
# Optional keyword arguments: base_model="...", revision="...".
print(output)
The converter checks every target name and tensor shape against the base model. It restores text-model prefixes, splits supported fused projections, preserves rank/alpha scaling, and combines indexed adapter shards into PEFT's single safetensors file. It runs on CPU with memory proportional to adapter size. The source is preserved, and an existing output directory is never overwritten. Unsupported layouts fail before an output directory is published. This exports LoRA adapters, not merged base weights or training/optimizer state.
Conversion preserves adapter values and dtype; inference compatibility also
depends on the loader. In our tests with Transformers 5.8.1 and PEFT 0.18.1,
loading BF16 adapters onto native FP8 layers cast the adapter weights to FP8,
changing their values. Use a loader that preserves BF16 adapters alongside the
FP8 base. The workaround verified in that test was to load the base with
FineGrainedFP8Config(dequantize=True) before attaching the adapter. This
requires more memory and does not quantize the adapter.
For unsupported layouts, including NVFP4 expert adapters, choose Download raw
script in Console. Run RIVER_API_KEY=... python download-raw.py after installing
requests. It saves the original files under <checkpoint-id>-raw without
conversion; those files are not guaranteed to load in HF/PEFT.
Metadata
Release files for river-client 0.14.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| river_client-0.14.0.tar.gz | 262.5 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| river_client-0.14.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 553.8 kB
Release files / river_client-0.14.0.tar.gz
| Download URL | river_client-0.14.0.tar.gz |
|---|---|
| Size | 262.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f5800a3df34f8d2270c1e66ed32d63ce15ae0d8bc01d96e1b2f7fc90459048d5
|
|
BLAKE2b-256 checksum How to use checksums |
a4a785f503b0b0bef8b855a36b32c0a1d3c48964aa62d039dc0cea59734a079a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.30 {"installer":{"name":"uv","version":"0.11.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / river_client-0.14.0-py3-none-any.whl
| Download URL | river_client-0.14.0-py3-none-any.whl |
|---|---|
| Size | 291.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c36b418ac647ca1bac475a93d7c02cca208dc7b7ca3b6c6c8e215be3fa5fa323
|
|
BLAKE2b-256 checksum How to use checksums |
df3c93ad85034209ed094969158244fc16400363f38a09a4c4558bff80b23cfd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.30 {"installer":{"name":"uv","version":"0.11.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for river-client 0.14.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.