Skip to content
SupportGo to app

AutoScientist API

Python SDK usage for client.autoscientist — create a run, poll it, download the checkpoint.

What AutoScientist does and how to read its results is covered in AutoScientist.

Requires adaption >= 0.6.0 and, for best results, a dataset that has completed a datasets.run adaptation. See Getting started for that step.

run = client.autoscientist.create(
dataset_id=dataset_id,
max_iterations=3,
target_win_rate=0.7,
)
print(run.id, run.status) # "pending"
ParameterRequiredNotes
dataset_idyesAdvanced: use passthrough mode to skip Adaptive Data.
max_iterationsno1–10.
target_win_rateno0 < x <= 1. Stops the loop early once reached.
modelnoId from training_models.list(). Omitted: platform picks by dataset size.
training_typenolora (default) or full, if the model supports it.
hyperparamsnoOverrides; unset keys use platform defaults.
augmentation_domain_rowsnoDomain-targeted rows added before training.
augmentation_general_rowsnoGeneral-diversity rows added before training.
idempotency_keynoRepeat calls with the same key return the existing run.
column_mappingnoSee below.

Training is supervised (sft); there is no method parameter.

We recommend targeting at least 20,000 domain rows and adding 8,000 general rows:

  • Domain rows add additional data specific to your dataset’s domain, strengthening the behavior you want the model to learn. Your existing dataset counts toward the 20,000-row target.
  • General rows add diverse, general-purpose examples that help the model retain broader capabilities.

The rows are added to the training dataset before AutoScientist starts its training iterations. Use augmentation_domain_rows and augmentation_general_rows to request them:

domain_rows = max(0, 20_000 - dataset.row_count)
run = client.autoscientist.create(
dataset_id=dataset_id,
...
augmentation_domain_rows=domain_rows,
augmentation_general_rows=8_000,
)

This targets 20,000 domain rows in total—your existing dataset plus domain rows—and adds 8,000 general rows, for up to 28,000 training rows. If the dataset already has at least 20,000 rows, domain_rows is zero. Omitting either augmentation parameter skips that addition.

Status is one of pending, running, succeeded, failed, cancelled.

from adaption import TrainingTimeout
try:
run = client.autoscientist.wait_for_completion(run.id)
except TrainingTimeout as e:
print(e.last_status) # you stopped waiting; the run did not stop

Backs off 10s → 60s, default timeout 4 hours (timeout=). TrainingTimeout carries resource_id and last_status; call wait_for_completion again to resume tracking.

For progress between polls, call get on your own schedule:

run = client.autoscientist.get(run.id)
print(run.iterations_completed, run.max_iterations, run.best_win_rate)

succeeded means the loop either reached target_win_rate or used its last iteration — check best_win_rate to tell which. On failed, error holds the failing iteration’s reason. cancel(run.id) stops a run in flight.

with client.autoscientist.with_streaming_response.download(run.id) as response:
response.stream_to_file("best-checkpoint.tgz")

Gated on download_available. Returns the best iteration’s checkpoint, not the last. Unlike datasets.download, which returns a presigned URL, this streams bytes — use the streaming response for large artifacts.

The payload is a compressed tar archive; tar xf detects the compression. Contents for a lora run:

adapter_config.json
adapter_model.safetensors
tokenizer.json
trainer_state.json
...
for run in client.autoscientist.list(dataset_id=dataset_id, limit=50):
print(run.id, run.status, run.best_win_rate)

Auto-paginated. limit defaults to 20, caps at 100.

import os
from adaption import Adaption
client = Adaption(api_key=os.environ["ADAPTION_API_KEY"])
result = client.datasets.upload_file("alpaca.parquet")
dataset_id = result.dataset_id
client.datasets.run(
dataset_id,
column_mapping={"prompt": "instruction", "completion": "response"},
)
client.datasets.wait_for_completion(dataset_id, timeout=3600)
run = client.autoscientist.create(
dataset_id=dataset_id,
max_iterations=3,
target_win_rate=0.7,
idempotency_key=f"autoscientist-{dataset_id}",
)
run = client.autoscientist.wait_for_completion(run.id)
if run.status != "succeeded":
raise RuntimeError(f"{run.status}: {run.error}")
with client.autoscientist.with_streaming_response.download(run.id) as response:
response.stream_to_file("best-checkpoint.tgz")

The column_mapping on datasets.run refers to your uploaded file’s columns — that one is required and unrelated to the caveat above.

client.training_jobs runs one recipe, without the search loop. Same lifecycle: create, wait_for_completion, get, cancel, download, list.

job = client.training_jobs.create(
dataset_id=dataset_id,
model="Qwen/Qwen3.5-9B",
method="sft",
)

Differences from autoscientist.create: takes method (sft or dpo, default sft), takes no max_iterations or target_win_rate, and returns final_loss instead of best_win_rate. For dpo, column_mapping roles are chosen and rejected rather than completion — with the same schema caveat.

Two supporting endpoints:

client.training_models.list() # valid `model` ids, with supported methods and training types
client.training_jobs.recommend_hyperparams(dataset_id=..., model=...) # defaults sized to your data

recommend_hyperparams starts nothing and costs nothing. Pass its hyperparams back into create, edited or not. If you plan to augment, pass the same row counts so the recommendation is sized to the effective training set.

Every method has an async variant on AsyncAdaption; list becomes an async iterator.

run = await client.autoscientist.wait_for_completion(run.id)

Full field-by-field schemas are in the API Reference.

Passthrough mode lets you run AutoScientist on a dataset that has not gone through Adaptive Data. It materializes the uploaded rows directly as a trainable dataset, without Adaptive Data’s optimization.

Set processing_mode when you create the dataset, and map the raw prompt and completion columns. Complete the presigned upload, wait for the dataset to finish processing, and then pass its ID directly to AutoScientist; do not call datasets.run.

import hashlib
from pathlib import Path
import httpx
path = Path("training_data.csv")
data = path.read_bytes()
dataset = client.datasets.create(
source={
"type": "file",
"name": path.stem,
"file_format": "csv",
"processing_mode": "passthrough",
"column_mapping": {
"prompt": "instruction",
"completion": "response",
},
},
)
response = httpx.put(dataset.upload_instructions.url, content=data)
response.raise_for_status()
client.datasets.upload.complete_by_id(
dataset.dataset_id,
file_size_bytes=len(data),
sha256=hashlib.sha256(data).hexdigest(),
)
client.datasets.wait_for_completion(dataset.dataset_id)
run = client.autoscientist.create(
dataset_id=dataset.dataset_id,
)