Adaptation
Adaptation turns planner-specific output into schema-2 Candidates. An adapter understands one raw format; the workflow decides how to preserve ranks, failures, and benchmark targets.
Choose A Workflow
| Goal | Python | Rust | Result |
|---|---|---|---|
| Adapt one planner payload | retrocast.adapt(...) |
adapt_candidates_with_workers(...) |
ranked candidates |
| Adapt and collect in memory | retrocast.ingest(...) |
adapt::ingest(...) |
predictions grouped by target |
| Read, adapt, and collect a file | retrocast.ingest_file(...) |
adapt::ingest_file(...) |
predictions grouped by target |
Use adapt to inspect a payload. Use ingest for evaluation because it maps every candidate onto a task target and returns the value consumed by scoring.
Terms
raw_payload is the planner artifact passed to an adapter.
RawRouteEntry is the envelope produced when the adapter traverses that artifact. It contains one raw route record and provenance such as source order and target hints.
Route is the canonical chemistry tree produced by a successful cast.
Candidate stores a one-based planner rank and exactly one of route or failure. A failure is a result, not a missing list element.
Adapt A Payload
import retrocast
candidates = retrocast.adapt(
raw_payload,
"paroutes",
mode="strict",
max_candidates=50,
workers=12,
)
for candidate in candidates:
if route := candidate.get("route"):
print(candidate["rank"], route["target"]["smiles"])
else:
print(candidate["rank"], candidate["failure"]["code"])
use retrocast_core::{
adapters::{adapt_candidates_with_workers, built_in},
route::AdaptMode,
};
let adapter = built_in("paroutes").expect("built-in adapter");
let candidates = adapt_candidates_with_workers(
raw_payload,
adapter.as_ref(),
AdaptMode::Strict,
None,
None,
Some(50),
12,
)?;
for candidate in candidates {
match (candidate.route, candidate.failure) {
(Some(route), None) => println!("{} {}", candidate.rank, route.target.smiles),
(None, Some(failure)) => println!("{} {}", candidate.rank, failure.code),
_ => unreachable!("Candidate validates exactly one outcome"),
}
}
from retrocast import adapt_candidates, get_adapter
adapter = get_adapter("paroutes")
candidates = adapt_candidates(
raw_payload,
adapter,
mode="strict",
max_candidates=50,
)
for candidate in candidates:
if candidate.route is not None:
print(candidate.rank, candidate.route.target.smiles)
else:
print(candidate.rank, candidate.failure.code)
max_candidates means the first N raw prediction slots. Failed slots consume a rank and remain visible. This is required for honest Solv-0 and MRR accounting.
Supply A Target Hint
Some raw formats need the expected target to validate or interpret a route. The target uses the schema-2 Target shape.
candidates = retrocast.adapt(
raw_route_record,
"synplanner",
target=target_dict,
source_key="target-001",
)
let candidates = adapt_candidates_with_workers(
raw_route_record,
adapter.as_ref(),
AdaptMode::Strict,
Some(&target),
Some("target-001"),
None,
1,
)?;
from retrocast import adapt_candidates, get_adapter
adapter = get_adapter("synplanner")
candidates = adapt_candidates(
raw_route_record,
adapter,
target=target,
source_key="target-001",
)
If the adapted root does not match the supplied target, the slot becomes an adapter.target_mismatch failure.
Ingest For A Task
Ingest combines adaptation and collection. Successful candidates are collected by route target identity. Failed candidates use the target hints preserved in their FailureRecord.
predictions = retrocast.ingest(
raw_payload,
"aizynthfinder",
task,
max_candidates=50,
workers=12,
)
use retrocast_core::adapt::ingest;
let predictions = ingest(
raw_payload,
adapter.as_ref(),
&task,
AdaptMode::Strict,
Some(50),
12,
)?;
from retrocast import get_adapter, ingest_candidates
adapter = get_adapter("aizynthfinder")
predictions = ingest_candidates(
raw_payload,
adapter,
task,
max_candidates=50,
)
Python 0.8.x returns NativePredictions; Rust and Python 0.7.1 return target-id maps of ranked candidates, using Rust types and Pydantic models respectively.
Stream A Large Artifact
The file entry point opens JSON or JSON gzip in Rust. For multi-target map payloads, it decodes and adapts target-by-target instead of materializing the complete raw graph in Python.
predictions = retrocast.ingest_file(
"results.json.gz",
"aizynthfinder",
"benchmark.json.gz",
workers=12,
)
predictions.write("candidates.json.gz")
let predictions = ingest_file(
raw_path,
adapter.as_ref(),
&task,
AdaptMode::Strict,
None,
12,
)?;
retrocast_core::io::write_json(output_path, &predictions)?;
from retrocast import get_adapter, ingest_candidates
from retrocast.io import load_benchmark, load_json_gz, save_collected_candidates
raw_payload = load_json_gz("results.json.gz")
task = load_benchmark("benchmark.json.gz")
adapter = get_adapter("aizynthfinder")
predictions = ingest_candidates(raw_payload, adapter, task)
save_collected_candidates(predictions, "candidates.json.gz")
Adapt Modes
strict rejects a raw route with invalid chemistry, cycles, or an impossible route structure.
prune allows an adapter to return the longest valid target-rooted prefix when an invalid branch can be removed unambiguously. It still fails if pruning removes the target or leaves a reaction without reactants.
candidates = retrocast.adapt(raw_payload, "paroutes", mode="prune")
let mode = AdaptMode::Prune;
candidates = adapt_candidates(raw_payload, adapter, mode="prune")
Available Adapters
Built-in adapter identifiers are lowercase and stable:
aizynthfinder askcos directmultistep dreamretroer
molbuilder multistepttl paroutes retrochimera
retrostar synllama synplanner syntheseus
ursa
The standalone CLI prints the registry with retrocast list-adapters. See Writing a Custom Adapter for the Rust adapter contract and raw-shape patterns.