Skip to main content

Om Python SDK (omtx)

Official Python SDK for the Om API.

The SDK talks to the public Om API /v2/* surface for Om-owned workflows and to RCSB public file URLs for exact PDB structure downloads. It covers:

  • Diligence workflows and job polling
  • Active public Hub workflows through client.hub.submit(...) and selected typed helpers
  • Hosted LULA-1 and LULA-2 protein-sequence plus SMILES scoring
  • Artifact upload for artifact-backed Hub jobs
  • Entitlement-scoped dataset catalog, shard exports, and Polars-backed OmData loaders
  • Private data-generation order helpers for subscription quota and invoice-backed orders
  • Om Accessible Space molecule availability, quote, Wallet Credits order, and order status helpers
  • Public RCSB PDB/mmCIF structure download helpers
  • Health and Wallet Credits helpers

Public docs: https://omtx.ai/docs

Installation

pip install omtx

Compatibility

For best compatibility:

  • Linux: modern Ubuntu on x86_64 or arm64
  • macOS: Apple Silicon with a native arm64 Python interpreter
  • macOS Python distribution: Miniforge or Mambaforge recommended

The dataframe helpers in omtx use polars:

  • load_binders(...)
  • load_nonbinders(...)
  • load_data(...)

If you are on Apple Silicon, use a native arm64 shell and Python. Avoid Rosetta / x86_64 Python for dataframe-backed workflows.

If you are on older x86_64 hardware, or if the default polars runtime fails with CPU-feature errors, install the compatibility runtime:

pip install "polars[rtcompat]"

Or install both in one step:

pip install omtx "polars[rtcompat]"

JSON-based SDK methods may still work without rtcompat, but dataframe helpers are not guaranteed on older x86_64 CPUs unless the compatibility runtime is installed.

Setup

export OMTX_API_KEY="your-api-key"

The SDK targets https://api.omtx.ai.

Quick Start

from omtx import OmClient

with OmClient() as client:
    print(client.status())

    job = client.diligence.deep_diligence(
        query="CRISPR applications in cancer therapy",
        preset="quick",
    )

    result = client.jobs.wait(
        job["job_id"],
        result_endpoint="/v2/jobs/deep-diligence/{job_id}",
    )
    print(result.get("result", {}).get("total_claims"))

Hub Quick Start

from omtx import OmClient

with OmClient() as client:
    artifact = client.artifacts.upload("target.cif")

    job = client.hub.boltzgen(
        protocol="protein_anything",
        target_cif_artifact_id=artifact["artifact_id"],
        target_chain_id="A",
        binder_length_min=90,
        binder_length_max=110,
        idempotency_key="boltzgen-demo-20260325",
    )

    status = client.jobs.wait(job["job_id"], poll_interval=5, timeout=3600)
    print(status["status"])

For active public Hub models without a dedicated typed helper, use client.hub.submit(job_type="hub.<model>", payload=...).

Hosted LULA Scoring

from omtx import OmClient

with OmClient() as client:
    job = client.lula2.score(
        protein_sequence="MEEPQSDPSV",
        smiles=["CCO", "c1ccccc1"],
    )
    print(job["status"], job["job_ids"])

Hosted client.lula1.score(...) and client.lula2.score(...) submit async LULA scoring jobs for explicit SMILES or fixed-price Om Accessible Space tiers. For Om space scoring, pass source="om", a wallet-credit tier, and n. The SDK does not expose mode; LULA-1 and LULA-2 are separate product namespaces. Legacy async client.hub.lula1(...) remains available for current Hub job compatibility.

with OmClient() as client:
    job = client.lula2.score(
        protein_sequence=sequence,
        source="om",
        tier=50,
        n=100000,
    )
    print(job["batch_id"], job["job_count"], job["status"])

Hosted score calls return a launch envelope with batch_id, job_ids, job_count, total_molecules, and status. Completed LULA score records include score, rank, and top_percentile_in_batch. score is a bounded 0-1 model score intended for ranking and enrichment, not a calibrated binding probability. Rank and percentile are numeric values computed within the submitted batch.

Local LULA Open-Weight Scoring

Install the optional local scorer only when you want to run public LULA weights on your own machine:

pip install "omtx[lula]>=2.0.18"
hf auth login
omtx lula download --model lula1.1
omtx lula verify

Model card, weights, license, and notebook: huggingface.co/omtx/lula-1.1. Quickstart notebook: lula1_quickstart.ipynb. LULA-1 open-weight releases are public gated on Hugging Face. By downloading, accessing, or using LULA-1, you agree to the Om LULA Community License 1.1. Commercial use requires a separate Om commercial license; email dmc@omtx.ai for commercial licensing. This includes commercial hosted inference, paid API/SaaS access, resale, paid support/deployment, bundling model access into a paid product, and competing model services.

Use --model lula1 for LULA-1 and --model lula1.1 for LULA-1.1. In Python, pass the same public selector to load_model(...). The SDK also accepts --model lula2 and load_model("lula2") for the epoch3656 cross-attention package once public LULA-2 weights are published; until then, it fails fast if no compatible local model/best.pt, model_config.json, and inference_config.json are present.

Hugging Face login is required for gated LULA weights. In Colab, omtx>=2.0.18 does not pin NumPy below 2.

Score a real target:

from omtx.lula import load_model

CA2 = (
    "MSHHWGYGKHNGPEHWHKDFPIAKGERQSPVDIDTHTAKYDPSLKPLSVSYDQATSLRIL"
    "NNGHAFNVEFDDSQDKAVLKGGPLDGTYRLIQFHFHWGSLDGQGSEHTVDKKKYAAELHL"
    "VHWNTKYGDFGKAVQQPDGLAVLGIFLKVGSAKPGLQKVVDVLDSIKTKGKSADFTNFDP"
    "RGLLPESLDYWTYPGSLTTPPLLECVTWIVLKEPISVSSEQVLKFRKLNFNGEGEPEELM"
    "VDNWRPAQPLKNRQIKASFK"
)
mols = [
    "CC(=O)Nc1nnc(s1)S(N)(=O)=O",
    "Cc1ccc(cc1)S(=O)(=O)N",
    "CC(C)Cc1ccc(cc1)C(C)C(=O)O",
    "CCN(CC)CCNC(=O)c1ccc(N)cc1",
    "c1ccc(cc1)C(=O)O",
    "CCO",
]

model = load_model("lula1.1")
for row in sorted(model.score(protein_sequence=CA2, smiles=mols), key=lambda r: r["rank"]):
    print(row["rank"], round(row["score"], 4), row["smiles"])

Local scoring uses downloaded weights and cached encoder assets. It does not contact Om or Hugging Face during explicit-SMILES scoring unless you explicitly run a download. Local scoring over Om Accessible Space requires an authenticated OmClient so the SDK can fetch orderable Om rows with source metadata before running the local model. The local scorer returns the same customer-facing score fields as hosted LULA: score, rank, and top_percentile_in_batch. The canonical score remains the 0-1 model score; rank and percentile are batch-ranking aids. Do not interpret any LULA score field as an experimental binding probability unless you have calibrated it against your own assay data. If you pass --encoder-cache-dir to omtx lula download, pass the same value to omtx lula score.

from omtx import OmClient
from omtx.lula import load_model

with OmClient(api_key="omtx_...") as client:
    model = load_model("lula1.1")
    scores = model.score(
        protein_sequence=CA2,
        source="om",
        tier=50,
        n=50000,
        client=client,
    )

Local LULA Fine-Tuning

Fine-tuning uses only the CSV you provide. The SDK freezes ESM2 and ChemBERTa, precomputes embeddings locally, and evaluates the test split after each epoch. For LULA-1/LULA-1.1 it trains the protein and ligand projectors. For LULA-2, once a compatible LULA-2 checkpoint is installed, it trains residual cross-attention adapters plus attention-pooling/scoring layers while keeping the encoders and base cross-attention backbone frozen. There is no hidden replay of Om training data and no upload of proteins, SMILES, labels, scores, or checkpoints.

target_id,protein_sequence,smiles,label,split
STAT6,MEEPQSDPSV,CCOc1ccc(CCN)cc1,1,train
STAT6,MEEPQSDPSV,CN1CCN(CC1)c1ccccc1,0,train
STAT6,MEEPQSDPSV,CC(=O)Nc1ccccc1,1,test
STAT6,MEEPQSDPSV,CCN(CC)CC,0,test
omtx lula finetune customer.csv --out stat6_lula

For LULA-2 after the public checkpoint package exists:

omtx lula finetune customer.csv --model lula2 --out stat6_lula2

The default base is lula1.1, epochs default to 10, the projector learning rate defaults to 1e-6, and intermediate checkpoints are saved every epoch. Use --save-every 0 to write only the final checkpoint. Continue from a prior derived checkpoint with:

omtx lula finetune customer_round2.csv --base stat6_lula/final --out stat6_lula_round2

Outputs:

stat6_lula/
  checkpoints/
  final/
  training_curve.csv
  metrics.json
  split_manifest.json
  training_config.json
  provenance.json

Molecule Fulfillment

from uuid import uuid4

from omtx import OmClient
from omtx.lula import load_model

protein_sequence = "MEEPQSDPSV"

with OmClient() as client:
    model = load_model("lula1.1")
    scores = model.score(
        protein_sequence=protein_sequence,
        source="om",
        tier=50,
        n=50000,
        client=client,
    )
    selected = sorted(scores, key=lambda row: row["score"], reverse=True)[:96]

    addresses = client.molecules.shipping_addresses()
    shipping_address_id = addresses["default_shipping_address_id"]

    order = client.molecules.order(
        items=selected,
        shipping_address_id=shipping_address_id,
        idempotency_key=f"molecule-order-{uuid4()}",
    )

The molecule namespace is a thin Om API wrapper for Wallet Credits-funded fulfillment. Om Accessible Space score rows include source metadata that lets Om route procurement internally after the customer orders. Customers see fixed Wallet Credit cost per SMILES and do not select a vendor. Search and quote helpers remain available for direct explicit-SMILES compatibility, while the Om tier path is just score(...) then order(...). Shipping address reads call /v2/molecules/fulfillment/shipping-addresses; order creation calls /v2/molecules/fulfillment/orders. Orders require an Idempotency-Key.

Data-Generation Orders

from omtx import OmClient

with OmClient() as client:
    sequences = [{"name": "target", "sequence": "M" * 120}]
    quota_order = client.data_generation.quota_order(
        sequences=sequences,
        idempotency_key="dg-quota-target-001",
    )
    invoice_order = client.data_generation.invoice_order(
        sequences=sequences,
        idempotency_key="dg-invoice-target-001",
    )

Public API data-generation order creation is private by default. Extra data-generation orders are invoice-only through the SDK/API/MCP surface; card checkout remains a frontend/internal flow.

Data Access

Browse published protein-specific models with client.models.catalog(). Generated and covered raw-data access is entitlement-scoped; fetch a protein_uuid from client.datasets.catalog() or client.datasets.generated_protein_uuids() for the authenticated account before loading generated or otherwise covered data.

Primary training flow (single call):

loaded = client.load_data(
    protein_uuid="YOUR_GENERATED_PROTEIN_UUID",
    binders=50000,          # required
    nonbinder_multiplier=5, # optional, default 5x background negatives
    # nonbinders=200000,    # optional explicit override (wins over multiplier)
    sample_seed=42,         # optional: deterministic sampling
)

binders = loaded["binders"]
nonbinders = loaded["nonbinders"]
print(binders.shape, nonbinders.shape)
binders.show(top_n=24)  # defaults: smiles_col="smiles", sort_by="binding_score"
binders.show(top_n=24, sort_by="selectivity_score")
# show() renders inline in notebooks; no extra display() wrapper needed.

load_data(...) samples non-binders from the Gateway-provided non-binder source. New binder-only vintages use canonical NEGATIVE-control binder shards; the SDK adaptively reads a bounded randomized NEGATIVE window distributed across returned shard URLs, excludes molecules already seen as target binders, and reads up to the full NEGATIVE pool only when needed. Requested non-binders are capped at 20x requested binders; if the anti-joined NEGATIVE pool is smaller than the requested count, the SDK returns the available non-overlapping rows. Canonical NEGATIVE responses must include explicit source metadata (source_protein_uuid and source_vintage_id); missing metadata is treated as a contract error. Legacy target-specific non-binder shards remain supported.

Explicit per-pool loading (advanced control):

binders = client.load_binders(
    protein_uuid="YOUR_GENERATED_PROTEIN_UUID",
    n=1000,          # optional: random sample size
    sample_seed=42,  # optional: deterministic sampling
)
nonbinders = client.load_nonbinders(
    protein_uuid="YOUR_GENERATED_PROTEIN_UUID",
    n=10000,         # optional: random sample size
    sample_seed=42,  # optional: deterministic sampling
)
print(binders.shape, nonbinders.shape)

# Omit n (or set n=None) to load the full pool.
# binders = client.load_binders(protein_uuid="...")
# nonbinders = client.load_nonbinders(protein_uuid="...")  # legacy or derived pools

Manual shard export URLs (advanced use):

urls = client.binders.urls(
    protein_uuid="YOUR_GENERATED_PROTEIN_UUID",
)
print("Binder shard URLs:", len(urls["binder_urls"]))
print("Non-binder shard URLs:", len(urls["non_binder_urls"]))
print("First binder URL:", urls["binder_urls"][0] if urls["binder_urls"] else None)

Generated proteins available now:

protein_uuids = client.datasets.generated_protein_uuids()
print("Generated protein UUIDs:", protein_uuids[:5])

Module-level convenience:

import omtx as om

loaded = om.load_data(
    protein_uuid="YOUR_GENERATED_PROTEIN_UUID",
    binders=50000,
    nonbinder_multiplier=5,
    sample_seed=42,
)
print(loaded["binders"].shape, loaded["nonbinders"].shape)

binders = om.load_binders(
    protein_uuid="YOUR_GENERATED_PROTEIN_UUID",
    n=1000,
    sample_seed=42,
)
nonbinders = om.load_nonbinders(
    protein_uuid="YOUR_GENERATED_PROTEIN_UUID",
    n=10000,
    sample_seed=42,
)
print(binders.shape, nonbinders.shape)

Idempotency

  • Every non-GET call gets an idempotency key automatically.
  • Auto-generated keys are per-call convenience and are not retry-stable.
  • For retry dedupe, pass and reuse your own idempotency_key (logical operation ID).

Example (retry-safe launch):

request_key = "search-protein-x-20260303-001"

job = client.diligence.search(
    query="MKNK2 inhibitor landscape",
    idempotency_key=request_key,
)

# If you retry the same logical launch, reuse the same idempotency key.
# retried = client.diligence.search(query="MKNK2 inhibitor landscape", idempotency_key=request_key)

Wallet Funding

Use client.wallet.topup(...) to explicitly fund Wallet Credits by saved card or invoice. Saved-card top-ups are capped at $500 per request; invoice top-ups can be larger. Wallet funding requires an explicit retry-stable idempotency_key and exact user approval text.

confirmation = client.wallet.expected_topup_confirmation(
    amount_cents=50_000,
    payment_mode="saved_card",
)

topup = client.wallet.topup(
    amount_cents=50_000,
    payment_mode="saved_card",
    user_approval_confirmation=confirmation,
    idempotency_key="wallet-topup-target-x-001",
)

Helper Surface

  • diligence.deep_diligence(query, preset=None, idempotency_key=None)
  • diligence.synthesize_report(gene_key, idempotency_key=None)
  • diligence.search(query, idempotency_key=None)
  • diligence.gather(query, preset=None, idempotency_key=None)
  • diligence.crawl(url, preset=None, idempotency_key=None)
  • diligence.list_gene_keys()
  • artifacts.upload(file_path, content_type=None), artifacts.upload_bytes(...), artifacts.get(artifact_id)
  • hub.submit(job_type, payload, idempotency_key=None) for the full active public Hub route set
  • selected typed hub.<model>(...) helpers for boltz2, boltzgen, rosettafold3, chai1, rfd3, bindcraft, alphafold, proteinttt, diffdock, flowdock, neuralplexer, openfold3, lula1, protein_specific_models
  • lula1.score(protein_sequence, smiles=None, source=None, tier=50, n=None, threshold=0.3, top_k=None, seed=42, job_name=None, protein_uuid=None, idempotency_key=None)
  • lula2.score(protein_sequence, smiles=None, source=None, tier=50, n=None, threshold=0.3, top_k=None, seed=42, job_name=None, protein_uuid=None, idempotency_key=None)
  • optional local LULA open-weight helpers through omtx.lula.load_model(...) after installing omtx[lula]
  • optional CLI commands installed by the omtx package: omtx lula download, omtx lula verify, omtx lula score, and omtx lula finetune
  • data_generation.quota_order(sequences, ...)
  • data_generation.invoice_order(sequences, ...)
  • molecules.pricing()
  • molecules.search(smiles_list, ...)
  • molecules.quote(items, ...)
  • molecules.shipping_addresses()
  • molecules.order(items, shipping_address_id, idempotency_key)
  • molecules.orders(limit=20)
  • molecules.status(order_number)
  • models.catalog(protein_uuid=None, q=None, limit=100, offset=0)
  • structures.pdb_download(pdb_id, format="pdb")
  • structures.pdb_save(pdb_id, output_dir=".", format="pdb", overwrite=False)
  • wallet.expected_topup_confirmation(amount_cents, payment_mode)
  • wallet.topup(amount_cents, payment_mode, user_approval_confirmation, idempotency_key)
  • vibe_video.submit(music_prompt, visual_prompt=None, artist_name=None, title_hint=None, duration_seconds=890, credit_artist=True, idempotency_key=None)
  • vibe_video.list(limit=20)
  • vibe_video.status(submission_id)
  • jobs.history(...), jobs.status(job_id), jobs.wait(job_id, ...)
  • binders.get_shards(...)
  • binders.urls(...)
  • load_binders(...)
  • load_nonbinders(...)
  • load_data(...) (combined binder/non-binder load)
  • datasets.catalog()
  • datasets.generated_protein_uuids()
  • status()
  • users.profile()

Visualization column contract:

  • OmData.show(...) is strict (no column fallback aliases).
  • Default columns are smiles and binding_score.
  • For selectivity views, pass sort_by="selectivity_score".

Route policy:

  • /v2/diligence/getTargetDiligenceReport remains an alias route and is not a separate SDK helper.
  • /v2/rag/search is intentionally not exposed in the SDK.
  • Public Hub coverage follows the active public model set in the canonical gateway route inventory.
  • hub.submit(...) covers the full active public model set.
  • Typed helpers are the selected subset listed above.
  • Molecule fulfillment helpers call only the canonical /v2/molecules/* Gateway routes. Public molecule orders are invoice-only.
  • models.catalog(...) calls /v2/models/catalog for published protein-specific model discovery.
  • structures.pdb_download(...) fetches exact public structure files from https://files.rcsb.org/download/{PDB_ID}.{format}. It accepts only canonical pdb or cif formats.
  • Data-generation order helpers call only the canonical /v2/data-generation/* Gateway routes. Public extra orders are invoice-only; quota orders consume active subscription capacity.
  • Vibe video helpers call only the canonical /v2/vibe-video/* Gateway routes.

Hub and Artifacts

artifact = client.artifacts.upload("target.pdb")

job = client.hub.diffdock(
    protein_artifact_id=artifact["artifact_id"],
    ligand_smiles="CCO",
    idempotency_key="diffdock-demo-20260316",
)

status = client.jobs.wait(job["job_id"], poll_interval=5, timeout=1800)
history = client.jobs.history(limit=20)

Notes:

  • Artifact-backed Hub workflows upload via client.artifacts.* first, then pass artifact IDs into canonical client.hub.* request fields.
  • client.hub.submit(...) is the generic escape hatch for active Hub models using canonical job_type="hub.<model>".
  • Active public models without a dedicated helper, such as neuralplexer, are launched through client.hub.submit(...).
  • jobs.history(...) returns recent user jobs newest-first and paginates with limit and cursor.

Migration

Breaking changes in 2.0.0:

  • OMTXClient removed.
  • OmClient is now the only supported client class.
  • Legacy pricing helpers removed from SDK surface.
  • Legacy binder batch-cost helper removed from SDK surface.
  • Shard access now resolves latest accessible dataset by protein_uuid.
  • Generated data access is granted by qualifying covered data-generation sequences for the matching protein_uuid, including legacy-public snapshots where the account has covered access.
  • client.status() is the primary health helper.
  • load_data(...) is the primary training-set helper; it samples target binders and background negatives from binder pools.
  • load_binders(...) remains the primary binder loader; load_nonbinders(...) remains available for legacy target non-binders and canonical NEGATIVE-control derived pools.
  • Flat shard URL aliases are available as binder_urls / non_binder_urls.
  • Core SDK runtime includes polars + rdkit.

Migration mapping (1.x -> 2.x):

  • from omtx import OMTXClient -> from omtx import OmClient
  • OMTXClient(...) -> OmClient(...)

Breaking changes in 1.0.0:

  • binders.get(...) removed from core SDK.
  • binders.iter(...) removed from core SDK.
  • pandas removed from required dependencies.

Migration mapping (0.x -> 1.x):

  • binders.get(...) -> client.load_binders(...) / client.load_nonbinders(...) or binders.get_shards(...)
  • binders.iter(...) -> binders.get_shards(...) + application-level streaming
  • pip install omtx (with pandas) -> pip install omtx (with polars + rdkit)

Full details: see MIGRATION.md.

Requirements

  • Python >=3.9
  • OMTX API key

License

MIT. See LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

omtx-2.0.18.tar.gz (102.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

omtx-2.0.18-py3-none-any.whl (74.5 kB view details)

Uploaded Python 3

File details

Details for the file omtx-2.0.18.tar.gz.

File metadata

  • Download URL: omtx-2.0.18.tar.gz
  • Upload date:
  • Size: 102.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.10.14

File hashes

Hashes for omtx-2.0.18.tar.gz
Algorithm Hash digest
SHA256 76361c683a3f60bf88abcffc27af01c3920705cf653eccdc43a093befbae64f9
MD5 8370335f873659a3d6dbd3b165749184
BLAKE2b-256 9bdd720877a6d7b8c90bb04f562f461e13d4216059b93dc6ef9b57c9ee5024b8

See more details on using hashes here.

File details

Details for the file omtx-2.0.18-py3-none-any.whl.

File metadata

  • Download URL: omtx-2.0.18-py3-none-any.whl
  • Upload date:
  • Size: 74.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.10.14

File hashes

Hashes for omtx-2.0.18-py3-none-any.whl
Algorithm Hash digest
SHA256 0d47474ec409ba47779bd890427e313d8083de16f614e2cf41a0f9e982045a9d
MD5 4d8855e53d39a5698bb204fbabbdd16f
BLAKE2b-256 73f40e7bdfd41fd6d38a6ebc56ffa157b4d314184bb5e60b2fa8f593b4b8f7f0

See more details on using hashes here.

Release history Release notifications | RSS feed

2.0.23

2 files

2.0.22

2 files

2.0.21

2 files

2.0.20

2 files

2.0.19

2 files

This release

2.0.18 This release

2 files

2.0.17

2 files

2.0.16

2 files

2.0.15

2 files

2.0.14

2 files

2.0.13

2 files

2.0.12

2 files

2.0.11

2 files

2.0.10

2 files

2.0.9

2 files

2.0.8

2 files

2.0.7

2 files

2.0.6

2 files

2.0.5

2 files

2.0.4

2 files

2.0.2

2 files

2.0.1

2 files

2.0.0

2 files

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

0.3.4

2 files

0.3.3

2 files

0.3.2

2 files

0.3.1

2 files

0.3.0

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page