Skip to content

jgassens/FoldForge

Repository files navigation

FoldForge

FoldForge is a local-first structure-prediction workflow app. The long-term product is a GUI-centered application for preparing, validating, running, and interpreting folding jobs, but the backbone is being built through CLI-first slices so the core models, backend adapters, runners, parsers, and reports are fast to develop and easy to test.

The current slices turn a small, validated FoldSpec YAML file into deterministic AlphaFold 3 input JSON, a dry-run command preview, and a normalized summary of AF3 output directories.

The product center is the deterministic core:

  • FoldSpec is the source of truth.
  • Backend JSON is generated output.
  • The CLI is the development and testing interface for the core.
  • The GUI will become the primary user-facing application once the core workflows are stable.
  • LLM patching remains an optional assistant layer, not the source of truth.

Roadmap Strategy

FoldForge is being built from the inside out:

  1. Deterministic core: FoldSpec, validation, backend exports, runners, parsers, and reports.
  2. CLI backbone: commands that expose every workflow in a scriptable, testable form.
  3. Visualization/report layer: static reports and confidence plots generated from parsed results.
  4. GUI application: a thin visual workflow over the same core modules, with no backend logic hidden in UI code.
  5. Optional assistant layer: natural-language patch generation that proposes inspectable changes to FoldSpec.

The CLI is not the final user experience. It is the scaffold that lets us move quickly without sacrificing correctness before the GUI arrives.

Current Scope

Implemented in this scaffold:

  • Python 3.12 package named foldfoundry
  • CLI command named foldfoundry
  • Guided terminal command named foldforge
  • PyInstaller packaging target for a macOS terminal executable at dist/foldforge
  • YAML validation for inline FoldSpec files
  • AlphaFold 3 export-only JSON
  • AlphaFold 3 local dry-run command preparation
  • AlphaFold 3 result parsing and summarize
  • AlphaFold Server upload JSON and explicit post-login Chrome browser submission automation
  • Plain-language structure-builder inputs with FASTA ingestion, preview, fusion linkers, segment/reference FoldSpec normalization, and AlphaFold Server export
  • DeepSeek/GPT-Nano parser benchmarking through a shared adapter and scorer
  • Protein, DNA, RNA, ligand SMILES, and ligand CCD inputs
  • Golden fixture tests for deterministic export

Explicitly out of scope unless a command says otherwise:

  • job execution, Docker invocation, subprocesses, or network calls
  • GUI code, except for roadmap/design planning
  • GUI code and full Textual-style terminal apps
  • report rendering
  • SDF, MSA, template, modification, userCCD, and path-based non-FASTA inputs
  • AF3 multi-copy entity IDs
  • compatibility with AF3 JSON versions older than version 4

Run The App

For a packaged terminal executable on macOS:

uv sync --group packaging
uv run pyinstaller packaging/foldforge.spec
./dist/foldforge

Running ./dist/foldforge with no arguments opens the guided terminal workflow. Advanced users can pass CLI arguments through the same executable:

./dist/foldforge validate examples/simple_protein.yaml
./dist/foldforge export examples/simple_protein.yaml --backend alphafold3 --out input.json

For development, use the same entry point without building the binary:

uv run foldforge

For scriptable and advanced workflows, keep using the canonical package CLI:

uv run foldfoundry --help
uv run foldfoundry validate examples/simple_protein.yaml

FoldForge keeps biological sequences local unless you explicitly choose a live provider, public resolver lookup, AlphaFold Server browser/session command, or other command documented as remote-facing.

FoldSpec YAML

Minimal example:

name: simple_protein
backend: alphafold3
seeds: [1]
entities:
  - id: A
    type: protein
    sequence: "MKT"
run:
  mode: export_only

This tiny MKT job is the golden fixture for deterministic local tests. For live AlphaFold Server upload checks, use examples/server_toy_protein.yaml; the server rejects very short toy sequences before it will enable draft submission.

Protein, DNA, and RNA entities use inline sequence. Ligands use exactly one of smiles or ccd_code.

name: protein_ligand
backend: alphafold3
seeds: [1, 2]
entities:
  - id: A
    type: protein
    sequence: "MKT"
  - id: L
    type: ligand
    ccd_code: ATP
run:
  mode: export_only

Dry-run local AF3 command preview:

name: dry_run_protein
backend: alphafold3
seeds: [1]
entities:
  - id: A
    type: protein
    sequence: "MKT"
run:
  mode: dry_run
  model_dir: /models/af3
  database_dir: /databases/af3
  container_image: alphafold3:test

Validation rules:

  • backend must be alphafold3
  • run.mode must be export_only or dry_run
  • dry_run mode requires model_dir and database_dir
  • seeds must be a non-empty list of unique strict integers greater than or equal to 1
  • entities must be non-empty
  • entity IDs must be unique strings
  • unknown fields are rejected

CLI

Validate a job:

foldfoundry validate examples/simple_protein.yaml

Export AlphaFold 3 JSON:

foldfoundry export examples/simple_protein.yaml --backend alphafold3 --out runs/simple_protein/input.json

Build structure prediction jobs from plain language:

foldfoundry build \
  "Model HER2 extracellular domain fused to this affibody sequence with GGGGSx3" \
  --fasta AFFIBODY=affibody.fasta \
  --assembly fusion \
  --linker GGGGSx3 \
  --out her2_affibody.foldspec.json

foldfoundry preview her2_affibody.foldspec.json
foldfoundry export her2_affibody.foldspec.json --backend alphafoldserver --out her2_affibody.afserver.json
foldfoundry submit her2_affibody.afserver.json

The builder layer sits above the existing submission system. Existing direct sequence FoldSpecs continue to work; richer builder specs use a sequence registry plus chain segments, then normalize deterministically into export-ready FoldSpec entities. Submission remains the already-automated AlphaFold Server flow; the builder only prepares validated inputs for it.

Repeated FASTA inputs are supported with optional labels:

foldfoundry build "Model these as separate chains" \
  --fasta AFFIBODY=affibody.fasta \
  --fasta CARGO=cargo.fasta \
  --out job.foldspec.json

If the request uses ambiguous language such as "connect" or "attach", build defaults to separate_chains, marks the spec ambiguous, and emits a warning instead of silently connecting sequences. Use --assembly fusion, --linker, and --allow-defaults to make non-interactive choices explicit.

Benchmark DeepSeek and GPT-Nano request parsing without live providers:

foldfoundry benchmark request.txt --fasta affibody.fasta --mock --json

Benchmark behavior is intentionally conservative:

Command Behavior
foldfoundry benchmark request.txt mock/default, no network
foldfoundry benchmark request.txt --mock mock, no network
foldfoundry benchmark request.txt --providers deepseek,gpt_nano error requiring --live
foldfoundry benchmark request.txt --providers deepseek,gpt_nano --live real provider calls after credential preflight
foldfoundry benchmark request.txt --mock --live error

Real provider calls are optional and require --live. API keys are read from environment variables or local .env only; .env is ignored by Git and must not be committed. Supported key names are DeepSeek_API / DEEPSEEK_API_KEY and OpenAI_API / OPENAI_API_KEY. Environment variables take precedence over .env. DEEPSEEK_MODEL and OPENAI_MODEL configure model IDs; the GPT-Nano default is gpt-5.4-nano. Benchmark JSONL records are stored under .foldfoundry/benchmarks/ by default, or at --benchmark-log PATH; raw model output is omitted unless --include-raw-model-output is explicitly supplied, and private FASTA sequences are redacted from those debug logs. Use --live only for sequences you are comfortable sending to the selected provider.

LLMs propose assembly plans only. Deterministic code resolves, validates, assembles, previews, and exports. Auto-found sequences are risky candidate sequences, stored as source="auto" and verified=false; they are shown in preview and require explicit confirmation with --allow-auto-sequence before export.

Interactive terminal menus are available as a thin InquirerPy layer:

./run-app
uv run foldforge

The TUI menu covers build, resolve, benchmark, preview, and export flows without making you remember the CLI flags. The prompts include short inline guidance and contextual help choices for assembly mode, fusion linkers, export backends, and browser/submission boundaries. The interactive prompts call the same services as the non-interactive CLI; tests target the core modules and scriptable commands. After a guided build writes a FoldSpec, the TUI stays in the job workflow and asks for the next action: submit to AlphaFold Server in Chrome, export AlphaFold Server JSON, export AlphaFold 3 JSON, preview again, return to the main menu, or exit. A FoldSpec named qbeta_zher2.foldspec.json defaults to qbeta_zher2.afserver.json for AlphaFold Server and qbeta_zher2.af3.json for AlphaFold 3. If the standalone sequence resolver is given a full structure description, it offers to resolve the inferred molecules separately or jump into the guided build with the same description.

Sequence lookup is federated. When a TUI build has no FASTA input, FoldFoundry asks before searching public databases, then searches the local/private registry, UniProtKB, NCBI Protein, and RCSB/PDB. Candidates are source-labeled and can be opened in Chrome before selection. Public resolvers try the exact search phrase first, then bounded contiguous phrase variants when the exact phrase misses; this keeps search from stopping at the first brittle full-text miss without adding domain-specific keyword rules.

foldfoundry registry add "HER2 nanobody 27A05" --fasta nanobody.fasta --alias "anti HER2 binder"
foldfoundry registry search "anti HER2 binder"
foldfoundry resolve "anti HER2 nanobody" --sources local,uniprot,ncbi,rcsb --limit-per-source 5
foldfoundry resolve "private binder" --local-only

The default private registry is ~/.foldfoundry/sequences.jsonl. Project-local .foldfoundry/sequences.jsonl is ignored by Git if it is created. NCBI Protein lookup requires NCBI_EMAIL; NCBI_TOOL and NCBI_API_KEY are optional. Selected public candidates become resolved FoldSpec sequences only after user selection and deterministic validation, with accession, URL, query, and source provenance retained.

Optional live resolver smoke tests

Normal tests are mock-only and do not call UniProt, NCBI, RCSB, AlphaFold Server, or any model provider. To verify public resolver integration on a machine with network access, opt in explicitly:

FOLDFOUNDRY_RUN_LIVE_TESTS=1 uv run pytest -m live

The live smoke tests cover UniProtKB, NCBI Protein, and RCSB/PDB. NCBI's live test also requires NCBI_EMAIL; NCBI_TOOL and NCBI_API_KEY remain optional. The same live resolver path can be exercised manually through the CLI:

uv run foldfoundry resolve "P04626" --sources uniprot --limit-per-source 3 --live
uv run foldfoundry resolve "NP_004439.2" --sources ncbi --limit-per-source 3 --live
uv run foldfoundry resolve "HER2 extracellular domain" --sources rcsb --limit-per-source 3 --live

Optional live provider smoke tests

Real DeepSeek/OpenAI parser benchmarking is also opt-in. Normal tests and normal foldfoundry benchmark runs are mock-only. To run tiny provider smoke tests on a machine with configured keys:

FOLDFOUNDRY_RUN_PROVIDER_TESTS=1 uv run pytest -m provider_live -rs

Manual live benchmark smoke:

uv run foldfoundry benchmark request.txt \
  --providers deepseek,gpt_nano \
  --live \
  --fasta AFFIBODY=tests/fixtures/affibody_toy.fasta \
  --fasta CARGO=tests/fixtures/cargo_toy.fasta \
  --benchmark-log .foldfoundry/benchmarks/provider-smoke.jsonl \
  --json

Short compatibility flow:

foldfoundry build "HER2 extracellular domain fused to this sequence" --fasta affibody.fasta --allow-defaults --out job.foldspec.json
foldfoundry preview job.foldspec.json
foldfoundry export job.foldspec.json --backend alphafoldserver --out exported_job.json
foldfoundry submit exported_job.json

Prepare an AlphaFold 3 dry-run directory and print the Docker command:

foldfoundry run examples/af3_dry_run.yaml --backend alphafold3-local --dry-run --out-dir runs/dry_run_protein

Summarize an AlphaFold 3 output directory:

foldfoundry summarize tests/fixtures/af3_outputs/hello_fold

The summary is deterministic tab-separated text with one row per ranked prediction. It includes job name, backend, rank, seed, sample, ranking score, mean atom pLDDT when atom_plddts is available, pTM, ipTM, clash status, and relevant result paths.

Inspect login-form detection on a local dummy page:

foldfoundry vault inspect tests/fixtures/dummy_login_site.html --url https://dummy.test/login

This is the first local vault-helper test harness. It detects username/password field candidates from ordinary HTML and prints deterministic selector output. It does not require Carbonyl, Chrome, Google, AlphaFold Server, or network access.

Prepare an AlphaFold Server upload JSON file for manual web submission:

foldfoundry server examples/server_toy_protein.yaml --out runs/toy_protein/alphafold_server.json --open

This writes dialect: "alphafoldserver" JSON for AlphaFold Server's Upload JSON flow and optionally opens AlphaFold Server in your system browser. FoldForge does not log in, click through the site, scrape results, or submit jobs automatically from the export command. AlphaFold Server upload JSON supports the server's allowed CCD ligands; arbitrary SMILES ligands are rejected for this workflow.

The old foldfoundry browser command remains as explicit Carbonyl debug plumbing for preparing upload JSON plus a local debug bundle. It is not the recommended AlphaFold Server workflow; guided submission and the default submit command use Chrome.

foldfoundry browser examples/server_toy_protein.yaml --out-dir runs/toy_protein/browser

If foldfoundry is not on your shell PATH, run it through the local project environment:

python3 -m uv run --no-editable --reinstall-package foldfoundry foldfoundry browser examples/server_toy_protein.yaml --out-dir runs/toy_protein/browser

This writes the same AlphaFold Server upload JSON plus a local debug bundle under the session directory, then launches Carbonyl at AlphaFold Server. The browser command remains completely user-operated. Use --dry-run to prepare the files and print the Carbonyl command without launching a browser:

foldfoundry browser examples/server_toy_protein.yaml --out-dir runs/toy_protein/browser --dry-run

Carbonyl may render AlphaFold Server's upload button without exposing a usable native file picker in the terminal. The browser command prints both the relative and absolute upload JSON paths; use the absolute path if the browser asks for a file path or if a user-directed path-injection workflow is needed.

Carbonyl is not bundled yet; install it separately or pass --carbonyl /path/to/carbonyl. Carbonyl is BSD-3-Clause licensed, so it is a reasonable candidate for future FoldForge release packaging with the required license notices.

Google Sign-In may reject plain Carbonyl with a "browser or app may not be secure" warning. That is an OAuth policy limitation for nonstandard, embedded, headless, automation-capable, or text-based browsers, not a TLS problem. If you have carbonyl-stealth, pass it explicitly:

python3 -m uv run --no-editable --reinstall-package foldfoundry foldfoundry browser examples/server_toy_protein.yaml --out-dir runs/toy_protein/browser --carbonyl carbonyl-stealth --force

For a live server smoke test, stop after AlphaFold Server shows a green check for toy_protein and enables Submit job as draft. That verifies the generated JSON is accepted without consuming quota or submitting a job.

Submit an AlphaFold Server job after manual Google login:

foldfoundry submit examples/server_toy_protein.yaml --out-dir runs/toy_protein/submit

submit is the explicit automation command. It generates alphafold_server.json, opens a browser for manual Google login, then automates the AlphaFold Server upload, submit, poll, and download. FoldForge does not automate Google login, read credentials, interact with hidden AlphaFold Server APIs, or bypass quotas. The default backend is Chrome:

foldfoundry submit examples/simple_protein.yaml --out-dir /tmp/test --dry-run

FoldForge uses a two-stage Chrome flow. If the selected profile already has an AlphaFold Server session, FoldForge opens it for visible upload automation. If Google login is required, FoldForge first opens a normal Chrome window with no CDP, Playwright, or automation flags so you can authenticate the profile safely:

mkdir -p "$HOME/chromium-profiles/foldfoundry"

PYTHONPATH=src uv run --no-sync python -m foldfoundry.cli submit \
  examples/server_toy_protein.yaml \
  --browser chrome \
  --profile "$HOME/chromium-profiles/foldfoundry" \
  --out-dir /tmp/foldfoundry-live-submit \
  --force

If the normal login window appears, log in and complete 2FA there. Once AlphaFold Server shows the Server page, quit that Chrome window and press Enter in the terminal; FoldForge then reopens the same profile for visible upload, submit, poll, and download automation. FoldForge launches that normal login window with extensions disabled so local browser extensions are not injected into AlphaFold Server or Google sign-in. Later runs reuse the same --profile so the session persists.

For local development and packaged runs, run-app is the only repo-root launch shortcut:

./run-app

With no arguments it opens the guided FoldForge terminal workflow. If dist/foldforge exists, the script runs that packaged executable; otherwise it falls back to uv run foldforge for development. Pass any advanced CLI command after ./run-app:

./run-app --help
./run-app submit examples/server_toy_protein.yaml \
  --profile "$HOME/chromium-profiles/foldfoundry" \
  --out-dir /tmp/foldfoundry-live-submit \
  --force
./run-app submit-json /path/to/alphafold_server.json --dry-run

If Chrome is already running with the same profile and you want FoldForge to attach to it rather than open a new window, use --connect-only. This mode connects over CDP, so --port must match the --remote-debugging-port Chrome was started with:

./run-app submit \
  examples/server_toy_protein.yaml \
  --browser chrome \
  --profile "$HOME/chromium-profiles/foldfoundry" \
  --connect-only \
  --port 9222 \
  --out-dir /tmp/foldfoundry-live-submit \
  --force

The browser profile directory is sensitive session state. It can contain Google cookies, auth tokens, local storage, browser history, and cached page data. Keep it outside the repository, never commit it, and do not share it in bug reports. FoldForge defaults to profiles outside the checkout: ~/.local/share/foldfoundry/chrome-profile/ for Chrome. If you pass --profile, prefer an absolute path under your home directory, such as $HOME/chromium-profiles/foldfoundry, rather than a repo-relative path. The project .gitignore also includes defensive browser-profile patterns in case a local profile is accidentally created inside the checkout. Do not run two Chrome instances against the same profile at the same time.

Useful submit options:

  • --browser chrome uses Playwright's persistent context to launch Chrome directly and is the default.
  • --browser carbonyl uses the legacy Carbonyl terminal browser over CDP for explicit debugging.
  • --port 9333 changes the CDP port; relevant for --connect-only and legacy Carbonyl sessions.
  • --timeout-hours 12 extends or shortens result polling.
  • --force overwrites existing generated submit artifacts and ZIP conflicts.
  • --carbonyl /path/to/carbonyl selects a Carbonyl binary.
  • --chrome /path/to/chrome selects a Chrome binary (default: Playwright locates the installed Chrome automatically).
  • --profile PATH selects the persistent browser user-data-dir.
  • --connect-only attaches to an already-running browser over CDP without launching a new one.

Manual acceptance test requiring a Google account and network:

PYTHONPATH=src uv run --no-sync python -m foldfoundry.cli submit \
  examples/server_toy_protein.yaml \
  --browser chrome \
  --profile "$HOME/chromium-profiles/foldfoundry" \
  --out-dir /tmp/live-test \
  --force

Log in to Google in the Chrome window, allow AlphaFold Server to run, and verify a results ZIP appears in /tmp/live-test/. If examples/simple_protein.yaml is rejected by the live server for being too small, use examples/server_toy_protein.yaml.

For foldfoundry export, the CLI --backend value must match the YAML backend. It validates intent but never overrides FoldSpec.

For foldfoundry run, the YAML backend remains alphafold3; --backend alphafold3-local selects the local runner preview. The command requires --dry-run and never executes Docker or AlphaFold.

For foldfoundry summarize, the input is an existing local AF3 output directory. It only reads files and never runs AlphaFold.

For foldfoundry server, the input is a validated local FoldSpec; the command writes a JSON draft for manual upload and never transmits biological data itself.

For foldfoundry browser, the input is a validated local FoldSpec; the legacy command writes a browser session directory and launches Carbonyl only after an explicit user command. FoldForge does not upload or submit anything automatically in this mode.

For foldfoundry submit, the input is a validated local FoldSpec; the command writes a submit directory and, after manual Google login, automates only the visible AlphaFold Server upload, submit, poll, and download flow.

AlphaFold Server Mini-Browser Direction

The CLI now supports a Carbonyl-powered mini-browser session, and the GUI should eventually include the same idea as an embedded Chromium-based mini-browser for AlphaFold Server work. The browser command is user-operated: users sign in, upload JSON, review drafts, and submit jobs themselves. FoldForge's role is to keep the submission context inspectable and debuggable:

  • generate the official AlphaFold Server upload JSON from validated FoldSpec
  • show the current local spec and generated JSON next to the browser
  • expose the absolute upload JSON path because terminal browsers may not support a usable file picker
  • capture browser console and network errors for local debugging when the selected browser backend exposes them
  • capture optional screenshots and downloaded result zips when the user asks
  • associate downloads with the originating FoldSpec

The submit command is the separate direct user action that may click visible upload/submission controls and poll visible result state after manual login. No FoldForge command may automate Google login, bypass quotas, scrape hidden APIs, or upload biological data without an explicit user command.

Output behavior:

  • missing parent directories are created
  • --out must be a file path, not a directory
  • existing files fail unless --force is passed
  • foldfoundry run writes input.json into the run directory and creates an output/ directory for later AF3 results

AlphaFold 3 Export

FoldForge emits one AF3 job per JSON file as a top-level object:

{
  "name": "simple_protein",
  "modelSeeds": [1],
  "sequences": [
    {
      "protein": {
        "id": "A",
        "sequence": "MKT"
      }
    }
  ],
  "dialect": "alphafold3",
  "version": 4
}

Entity mappings:

  • type: protein -> {"protein": {"id": "...", "sequence": "..."}}
  • type: dna -> {"dna": {"id": "...", "sequence": "..."}}
  • type: rna -> {"rna": {"id": "...", "sequence": "..."}}
  • ligand smiles -> {"ligand": {"id": "...", "smiles": "..."}}
  • ligand ccd_code -> {"ligand": {"id": "...", "ccdCodes": ["..."]}}

AlphaFold 3 Summaries

The AF3 parser expects the output layout documented by Google DeepMind:

  • <job_name>_ranking_scores.csv
  • top-ranked root files: <job_name>_model.cif, <job_name>_summary_confidences.json, and optionally <job_name>_confidences.json
  • per-sample directories named seed-<seed>_sample-<sample>/

The parser reads summary confidence metrics such as ranking_score, ptm, iptm, fraction_disordered, and has_clash. It computes mean atom pLDDT only when atom_plddts is present in a confidence JSON.

Development

Install dependencies with uv when available:

uv sync --dev

When testing the installed console script from a source checkout, uv's non-editable install mode can force the local package to rebuild:

uv run --no-editable --reinstall-package foldfoundry foldfoundry --help

For local development, use the normal console command after syncing the project:

uv sync
uv run foldforge
uv run foldfoundry --help
uv run foldfoundry validate examples/simple_protein.yaml

On macOS, editable installs can fail if .pth files in .venv are marked with the Finder hidden flag. Python skips hidden .pth files, so the generated foldfoundry console script may exist while import foldfoundry still fails. Check for this with:

PYTHONPATH=src uv run --no-sync python -m foldfoundry.cli doctor

If doctor reports a hidden FoldFoundry editable .pth file, repair only that project-related file:

PYTHONPATH=src uv run --no-sync python -m foldfoundry.cli doctor --fix

doctor --fix reports unrelated hidden .pth files but does not modify them.

Required checks:

uv run pytest
uv run pytest --cov=foldfoundry --cov-report=term-missing --cov-fail-under=85
uv run ruff check .
uv run ruff format --check .
uv run mypy src

GitHub Actions runs the same checks on Linux and macOS for Python 3.12, including the coverage gate and CLI smoke tests.

No unit test should require network access, Docker, GPUs, model weights, databases, or live AlphaFold installation.

About

an app that turns alphafold into something easier to use

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors