Deploy and serve
Dedicated streaming deployments
Use a dedicated deployment to serve your trained model to applications and agents. River reserves serving capacity for a saved checkpoint and exposes an OpenAI-compatible endpoint, including streaming responses. Training sessions and production deployments have separate lifecycles: ending a training session does not stop a deployment. Use a team API key with deployment access for the checkpoint's base model; contact River to enable access. Personal API keys cannot create deployments.
pip install river-client openaiSave the trained model with model.save_weights("serving-v1", mode="inference"),
then use the returned checkpoint path:
import os
import river_client as river
from openai import OpenAI
api_key = os.environ["RIVER_API_KEY"]
client = river.Client(api_key=api_key, endpoint="api.river.ai")
deployment = client.create_deployment(
checkpoint="river://YOUR_RUN_ID/sampler_weights/serving-v1",
unified_replicas=1,
idempotency_key="serving-v1",
wait=True,
)
print("Deployment:", deployment.id, deployment.base_url)
with OpenAI(api_key=api_key, base_url=deployment.base_url) as inference:
with inference.chat.completions.create(
model=deployment.model,
messages=[{"role": "user", "content": "Hello"}],
stream=True,
) as stream:
for chunk in stream:
if chunk.choices:
print(chunk.choices[0].delta.content or "", end="", flush=True)The URL selects the checkpoint. It already includes the OpenAI API prefix;
pass it unchanged as base_url. Creation is asynchronous: wait=True waits
for serving capacity. To require every requested replica to be ready, call
client.wait_for_deployment(deployment.id, until=("ready",)).
Reuse an idempotency key when retrying the same creation request; use a new key
for a new deployment.
Capacity is specified in replicas. River selects the GPU shape for the model;
one replica does not necessarily mean one GPU. Choose either
unified_replicas or both prefill_replicas and decode_replicas at creation.
The topology cannot be changed afterwards.
Scale, stop, and delete
client.list_deployments()
client.scale_on_target(deployment.id, unified_replicas=2)
client.get_deployment_usage(deployment.id)
# Release serving capacity while keeping the URL and checkpoint.
client.scale_on_target(deployment.id, unified_replicas=0)
client.wait_for_deployment(deployment.id, until=("scaled_to_zero",))
# Resume on the same URL.
client.scale_on_target(deployment.id, unified_replicas=1)
client.wait_for_deployment(deployment.id)
# Release the deployment when it is no longer needed.
client.delete_deployment(deployment.id, wait=True)Scaling sets absolute targets, not increments. For a prefill/decode deployment, scale both roles to zero together and both back up to resume. Scale-down and deletion drain active work before releasing resources. Requested GPU-hours start at create/scale acceptance and stop at scale-to-zero/delete acceptance; they include provisioning and outage time. Usage records report observed allocation separately.
Use a fresh checkpoint name and a new deployment to update weights while the old deployment continues serving. Saving new weights at an existing checkpoint path does not reload running workers. To update at the same URL, scale to zero, wait for draining to finish, save the new weights, then restore capacity.
Deployments also support stateless /responses requests: set store=False
and send conversation history on each request. Stored responses,
previous_response_id, and background processing are unsupported. If a stream
fails after it starts, the application must decide whether to start a new
request; an interrupted stream cannot be resumed.
See the Python API reference for the deployment methods and parameters.