--- title: AutoScientist API | Adaption description: Python SDK usage for client.autoscientist — create a run, poll it, download the checkpoint. --- **Try it in Google Colab** Follow the [AutoScientist API notebook](https://colab.research.google.com/drive/16Zly8_sDqQSP86MvdgTJ1I1T6Vjq6bkJ?usp=sharing) for an interactive walkthrough. What AutoScientist does and how to read its results is covered in [AutoScientist](/guides/autoscientist/index.md). Requires `adaption >= 0.6.0` and, for best results, a dataset that has completed a `datasets.run` adaptation. See [Getting started](/introduction/getting-started/index.md) for that step. ## Create a run ``` run = client.autoscientist.create( dataset_id=dataset_id, max_iterations=3, target_win_rate=0.7, ) print(run.id, run.status) # "pending" ``` | Parameter | Required | Notes | | --------------------------- | -------- | ------------------------------------------------------------------------------------- | | `dataset_id` | yes | *Advanced*: use [passthrough mode](#passthrough-mode-advanced) to skip Adaptive Data. | | `max_iterations` | no | 1–10. | | `target_win_rate` | no | `0 < x <= 1`. Stops the loop early once reached. | | `model` | no | Id from `training_models.list()`. Omitted: platform picks by dataset size. | | `training_type` | no | `lora` (default) or `full`, if the model supports it. | | `hyperparams` | no | Overrides; unset keys use platform defaults. | | `augmentation_domain_rows` | no | Domain-targeted rows added before training. | | `augmentation_general_rows` | no | General-diversity rows added before training. | | `idempotency_key` | no | Repeat calls with the same key return the existing run. | | `column_mapping` | no | See below. | Training is supervised (`sft`); there is no `method` parameter. `column_mapping` is validated against the **adapted** dataset’s schema, not the headers of the file you uploaded. Adaptation adds columns such as `enhanced_prompt`, and which ones exist varies per run. Omit it and the platform infers. A name that isn’t present fails the run: `Selected column '...' for prompt is not in this dataset`. ## Data Augmentation Augmentation is optional, but strongly recommended when your dataset is small or lacks diversity. Additional training rows give the model more examples to learn from, helping reduce overfitting and the loss of general capabilities during training. 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. ## Poll 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. ## Download ``` 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 ... ``` ## List ``` 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. ## Full example ``` 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. ## Single training runs `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. ## Async 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](/api/index.md). ## Passthrough mode 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. For best results, adapt your data first. Use passthrough only when you intentionally want AutoScientist to train on the original data. 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, ) ```