Federated Jobs Service
client.jobs submits local or federated Ray jobs. Existing callers may keep
using run, submit_async, wait, and cancel without delegated access.
Governed delegated access
Use DelegatedAccess to name exact receiver resources and operations. The SDK
validates the same closed vocabulary as Core before making a request. Wildcards,
duplicates, unknown operations, and an empty access block are rejected.
from kamiwaza_sdk import (
DatasetDelegatedAccess,
DelegatedAccess,
ModelDelegatedAccess,
)
job_id = client.jobs.submit_async(
target_cluster="receiver",
entrypoint="python summarize.py",
delegated_access=DelegatedAccess(
datasets=(
DatasetDelegatedAccess(
urn=DATASET_URN,
operations=("discover", "read", "retrieve"),
),
),
models=(
ModelDelegatedAccess(
deployment_id=DEPLOYMENT_ID,
operations=("discover", "chat"),
),
),
),
)
Passing a mapping with the same shape remains supported. Omitting
delegated_access preserves the ordinary job wire format and execution path.
Approved Python dependencies
An isolated delegated job can request exact Python packages from the receiver operator's approved catalog:
job_id = client.jobs.submit_async(
target_cluster="receiver",
entrypoint="python summarize.py",
delegated_access=access,
python_packages=["humanize==4.13.0"],
)
Only exact name==version coordinates are accepted. URLs, version ranges,
extras, markers, duplicate package names, and more than 32 packages are rejected
before the request is sent. The receiver resolves each coordinate to its
operator-approved SHA-256 hashes and installs it from the configured private
Python index. Repository URLs, credentials, and trust material are never part
of the SDK request or the Ray job environment. Air-gapped deployments can point
the operator-managed repository Secret at an internal PEP 503/691-compatible
mirror without changing job code.
The older pip= convenience parameter belongs to ordinary Ray runtime
environments. Use python_packages= for isolated delegated jobs.
In-job runtime client
Managed job code uses JobRuntimeClient, which connects only to the private
Unix socket created for that job:
from kamiwaza_sdk import JobRuntimeClient
with JobRuntimeClient.from_environment() as receiver:
datasets = receiver.datasets.list_granted()
models = receiver.models.list_granted()
rows = receiver.retrieval.collect(dataset_urn=DATASET_URN)
answer = receiver.models.chat(
deployment_id=DEPLOYMENT_ID,
messages=[{"role": "user", "content": summarize(rows)}],
)
models.chat and models.stream_chat take optional generation options. Set
enable_thinking=False to turn off a reasoning model's thinking section, and
max_tokens (1 to 32,768) to cap the reply:
from kamiwaza_sdk import JobChatOptions
answer = receiver.models.chat(
deployment_id=DEPLOYMENT_ID,
messages=[{"role": "user", "content": summarize(rows)}],
options=JobChatOptions(enable_thinking=False, max_tokens=2048),
)
A plain mapping such as {"enable_thinking": False} works too. Unset options
keep the model's defaults. On a model that thinks, thinking counts against
max_tokens, so a small cap without enable_thinking=False can return an empty
reply. A receiver that predates generation options refuses a request that sets
them, which raises JobAgentRequestRefusedError (see Errors below); a request
without options works on any receiver. Options that set no field, such as {} or
JobChatOptions(max_tokens=None), count as no options.
Streaming variants are retrieval.stream and models.stream_chat. Each
operation re-reads the platform-owned agent identity and verifies the connected
Unix peer before sending a request, so a restarted agent is used safely on the
next operation. The client never falls back to a bearer token, password, API
key, refresh token, or public HTTP endpoint.
Errors
Denied resources raise DelegatedResourceNotFoundError; lifecycle revocation
raises DelegationRevokedError; attestation failure raises
JobIdentityUnavailableError; grant denial raises
DelegatedOperationDeniedError; and authority, dependency, or replay-store
outages raise DelegationUnavailableError.
A dataset read that would return more than 10,000 rows raises
DelegatedRowLimitExceededError. The receiver refuses the read rather than
truncating it, and retrying the same read does not help: read a smaller
dataset or view instead, one the job's delegated_access names.
The error subclasses DelegationUnavailableError, so existing handlers still
catch it, including one that retries outages. Catch it first:
from kamiwaza_sdk.exceptions import (
DelegatedRowLimitExceededError,
DelegationUnavailableError,
)
try:
rows = receiver.retrieval.collect(dataset_urn=DATASET_URN)
except DelegatedRowLimitExceededError:
raise # refused, not truncated: read a smaller dataset or view instead
except DelegationUnavailableError:
rows = retry_later() # your own retry for outages
Against a receiver that predates this refusal, the error never occurs, and a read past the cap can come back cut to the first 10,000 rows, with no error. On such a receiver, a result of exactly 10,000 rows may be incomplete.
A request the job agent cannot read raises JobAgentRequestRefusedError, also
in kamiwaza_sdk.exceptions: the agent rejected the request frame or timed out
reading it. On a platform older than this SDK, the likely cause is a field the
agent predates, such as chat options. A request refused for that reason is
refused every time, so leave the field unset or upgrade the platform. The error
subclasses DelegationUnavailableError, so existing handlers still catch it,
and a handler that retries outages retries this refusal too, which helps only
when reading the request timed out. To stop retrying a chat the platform
refuses for its options, catch JobAgentRequestRefusedError first, as with the
row-limit refusal above.