> ## Documentation Index
> Fetch the complete documentation index at: https://docs.drkm.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Python SDK

> One small module for jobs, fleet discovery and, inside a sandbox, the agent’s tools and models.

## Connect once

Everything is in the `drkm` module. Inside an agent sandbox no connection step is needed. From anywhere else, `connect()` sends the job and fleet calls to an outpost’s HTTPS API. The outpost is the address you pass, else `$DRKM_URL`, else the active `drkm console` profile. The credential is `token` (an API key an administrator minted under **Settings → Keys → API keys**, or an ID token), else `session` (a password sign-in’s cookie), else `$DRKM_TOKEN`, else what `drkm console login` stored. HTTPS only, except `localhost`.

```python Connect and inspect theme={null}
import drkm

# 1. Reuse the active drkm console profile: address, credential and CA.
drkm.connect()

# 2. Name the outpost and sign in with an API key.
drkm.connect("https://outpost.example", token="drkm_ak_...")

# 3. A private certificate authority.
drkm.connect("https://outpost.example", ca_file="ca.pem")

print(drkm.outposts())   # the fleet: name, outpost_id, version, local, stale
print(drkm.jobs())       # your 50 most recent jobs, newest first
```

> [!NOTE]
>
> After connect() the SDK serves submit, estimate, job, jobs, wait, cancel, answer, results, rows, the Dataset verbs and outposts. Agent tasks, sources and model calls stay inside a sandbox and raise BridgeError elsewhere; drkm.tools exists only inside one.

## Records, workers and bundles

A job runs one operation over input records. Each record is `{"id", "data", "files"}`: an ID that is unique within the job, any JSON as data, and up to 16 workspace-relative file paths. A job holds at most 100,000 records.

`package()` describes a script operation: the entrypoint `module.py:function`, the workspace files sealed with the job (the entrypoint module among them, 64 at most), the models and sources it needs and whether it is safe to replay. The platform calls `run(records, context)` once with every record, as one partition, and the return value is the job’s output. Per-row Python spread over the fleet is a Broom sweep, in `drkm.broom`.

Inside an attempt, `context.read(path)` returns a staged input file as bytes, `context.models["alias"]` a model you declared, and `context.sources["alias"]` a declared read-only source.

```python Worker contracts theme={null}
# worker.py: called once with every record
def run(records, context):
    return [{"id": r["id"], "alert": r["data"]["temperature"] > 80} for r in records]


# detect.py: the declared detector over every record's image, 16 at a time
import itertools

def detect(records, context):
    detector = context.models["detector"]
    found = []
    for batch in itertools.batched(records, 16):
        images = [context.read(r["files"][0]) for r in batch]
        found.extend(detector.predict_batch(images))
    return found


# The operations those workers describe
import drkm

worker = drkm.package("worker.py:run", ["worker.py"])
detection = drkm.package(
    "detect.py:detect", ["detect.py"],
    models={"detector": "overwatch-obb-r3-int8"},
)
```

## Submit, wait, collect

`submit()` returns the job ID at once. Its keyword arguments:

* **`name`**: What operators read in the job list, at most 120 characters. Say what it does and where.
* **`inputs`**: The records inline, or `inputs_file` naming a workspace JSON file when the manifest is large.
* **`key`**: Makes a retry idempotent: the same key with the same work returns the same job; the same key with different work is refused.
* **`limits`**: Default first, then the ceiling: `partition_size` 16 (up to 512; a script is always one partition), `max_attempts` 3 (up to 10), `attempt_timeout_s` 600 (up to 86400), `concurrency` 64 (up to 256).
* **`allowed`, `preferred`**: A hard limit and a soft hint, as outpost IDs from `outposts()`. `preferred=["self"]` prefers the submitting outpost.

`wait()` polls until the job settles and returns its snapshot: `state`, `coverage` (expected, completed, failed, running, pending), `error`, `blocked`. `results()` writes the manifest and every accepted partition under `jobs/<id>/`; `rows()` reads them back as `{"record_id", "output"}` in partition order; a script’s one row has a null `record_id`. Until a job settles, `rows()` holds only the partitions accepted so far.

```python Reopen, follow, cancel theme={null}
import drkm

drkm.connect()
job = drkm.Job("your-job-id")

print(job.status())               # state, coverage, error, clarification, blocked
snapshot = job.wait(timeout_s=60) # returns the unsettled snapshot on timeout
if snapshot["state"] in ("completed", "partial"):
    print(job.rows())
# job.cancel()

# Limits and an idempotent key
job_id = drkm.submit(
    drkm.package("worker.py:run", ["worker.py"], replay_safe=False),
    inputs=[{"id": "a", "data": {"temperature": 72}}],
    key="nightly-2026-10-27",
    limits={"max_attempts": 2, "attempt_timeout_s": 300},
    name="Nightly sensor check",
)
```

> [!NOTE]
>
> A job for an agent is a job too: submit(drkm.agent\_task("count the ships")). When it asks a question the job enters input\_required; answer it with wait(job\_id, on\_input=...) or answer(job\_id, input\_id, text).

## The dataset interface

A served detector over many images reads like a dataframe pipeline: describe every input and the model in one chain, and the scheduler decides partitions, placement and retries. `parallelize()` takes inline records and `read.json()` a workspace file of them. A dataset has one stage, `.predict(model, conf=..., classes=...)`, and each record names its one image in `files`. `.explain()` answers what the scheduler would do and runs nothing; `.submit()` returns a `Job`. Both work inside a sandbox and after `connect()`.

Run this example from your own Python environment after [installing the SDK](https://docs.drkm.dev/installation.md#get-the-python-sdk). Inside an agent sandbox, omit `connect()`; the sandbox already supplies a bridge.

```python A detector over records theme={null}
import drkm

drkm.connect()
records = [{"id": "tile-01", "files": ["uploads/tile-01.png"]}]

job = (drkm.parallelize(records)          # or drkm.read.json("inputs.json")
       .predict("overwatch-obb-r3-int8", conf=0.4)
       .submit(name="Ships in the uploaded harbor tiles"))
job.wait()
rows = job.rows()                         # one row per image: its detections

# The plan, with nothing run
plan = drkm.parallelize(records).predict("overwatch-obb-r3-int8").explain()
```

## Inside a sandbox: tasks, tools, sources

Code the agent runs on its computer can do more than submit jobs. `task()` pushes a prompt to this outpost’s agent, to a named peer from `outposts()`, or to the operator-configured remote, and returns a handle; `gather()` waits for one or several and returns their output. Configured connections expose typed functions under `drkm.tools`, generated from the outpost’s current settings; the host performs each call with the turn’s authority, and the program never holds a credential or an address. `drkm.sources(alias).read(...)` reads bounded data from a declared source.

The same calls are available from bash as the sandbox’s `drkm` command, printed as JSON. They work in a background run too, for as long as it runs.

```python Tools and tasks theme={null}
import drkm
from drkm import tools

help(tools)                       # the configured connector modules
# help(tools.compass)             # one module's functions

handle = drkm.task("self", prompt="Summarize the rows in report.csv")
lines = drkm.gather(handle)       # [{"t": "text", "text": "..."}, ...]

peers = drkm.outposts()           # peers a task may target: name, version, model, tools

# From bash inside the sandbox
# drkm help
# drkm call compass.feed '{"video_id": 7}'
# drkm jobs
# drkm job ID         # also: wait ID, cancel ID, results ID, answer ID INPUT TEXT
```

## Errors you will meet

* `BridgeError` carries the broker’s reason verbatim: a refusal, a failed task, a lost connection, or an HTTP error from the outpost.
* `BridgeUnavailable` means this machine has no bridge: outside a sandbox, `connect()` found no outpost or no credential, and the message names the fix. Inside one, the exec or background run that started the process has ended.
* *not signed in* after `connect()`: the terminal stored its credential in the OS keyring and the script’s environment lacks the keyring package. Install it with `python -m pip install keyring`. For automation without a keyring, inject a token from your secret manager into `DRKM_TOKEN` at runtime.
* *runs only inside a sandbox* after `connect()`: agent tasks, tools, sources and models need a sandbox. From a laptop, submit a job instead.
* Missing rows: the job has not settled, so `rows()` holds only the partitions accepted so far. Call `wait()` first.
* A 409 on submit: the key was used for different work.

`import drkm` always succeeds, so `help(drkm)` answers everywhere.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.