diff --git a/nim-skills/genomic-intelligence-nim/SKILL.md b/nim-skills/genomic-intelligence-nim/SKILL.md new file mode 100644 index 0000000..d71451e --- /dev/null +++ b/nim-skills/genomic-intelligence-nim/SKILL.md @@ -0,0 +1,249 @@ +--- +name: genomic-intelligence-nim +description: > + Predict regulatory features, gene structure, and expression directly from DNA sequence using Genomic Intelligence's hosted DNA language models. Six tasks over one hosted REST contract — promoter regions, splice donor/acceptor sites, enhancer activity, chromatin state, sequence-to-expression (log TPM), and de-novo gene/transcript annotation. Use for regulatory genomics, promoter/enhancer/splice/chromatin scanning, expression prediction, and gene annotation from a gene name, a genomic region, or a FASTA. Bearer auth; no local GPU or model weights. +license: Apache-2.0 AND CC-BY-4.0 +compatibility: "requests>=2.28" +allowed-tools: Bash, Read, Write, AskUserQuestion +--- + +# Genomic Intelligence NIM + +One skill, six DNA-sequence prediction tasks, served by the hosted Genomic +Intelligence API. Give it a **gene name**, a **genomic region**, or a **FASTA**; +it resolves a sequence, calls that task's own predict operation, and writes a +report + machine-readable JSON. Inference is remote — no model weights, GPU, or +heavyweight Python stack; the only dependency is `requests`. + +Load supplemental files only when needed: + +- `references/tasks.md`: per-task model, bounds, output fields, and biology. +- `references/api.md`: endpoints, request/response envelope, async, errors. +- `references/authentication.md`: `GI_API_KEY`, base URL, partner tiers. +- `references/sequence-acquisition.md`: gene/region → FASTA, TSS window, species. +- `references/errors.md`: error envelope, rate limits, async polling detail. + +> **Hosted, third-party service.** Genomic Intelligence is operated by Genomic +> Intelligence, not NVIDIA. The interface is the hosted-NIM shape (HTTPS + +> `Authorization: Bearer` + JSON). There is no local Docker mode. +> +> Research and development use. Not for clinical or diagnostic decisions. + +## The six tasks + +Each task is its own published operation — `POST /v1/tasks/promoter/predict`, +`/v1/tasks/splice/predict`, and so on — with its own request schema, its own +minimum length, and its own closed `options` object. The URLs are unchanged from +what callers already send. + +| Task | What it predicts | Recommended mode | Accepted length | `context_window_bp` | +|---|---|---|---|---| +| `promoter` | Promoter regions (sliding window) | sync | 300–500,000 bp | 2,000 bp (300 bp models exist) | +| `splice` | Splice donor/acceptor sites | sync | 100–500,000 bp | 15,000 bp | +| `enhancer` | Developmental & housekeeping enhancer activity | sync | 50–500,000 bp | 249 bp | +| `chromatin` | Chromatin state across hundreds of tracks | sync | 200–500,000 bp | 1,000 bp | +| `expression` | Expression as log(TPM+1) | sync | **9,198–500,000 bp** | n/a (`trained_window_bp` 9,198) | +| `annotation` | De-novo gene/transcript structure | async | 1,000–500,000 bp | n/a | + +`Recommended mode` is guidance, not a constraint — every task accepts both. Omit `Prefer` for a synchronous `200`; send `Prefer: respond-async` for a `202` plus `GET /v1/tasks/jobs/{job_id}`. Only the composite workflow enforces a mode, rejecting sync above 50,000 bp with `413 sync_too_large`. + +The minimum is **admission control, not regime**. A sequence above the floor but +shorter than the selected model's `bio_spec.context_window_bp` is *accepted and +scored* — against a window padded out to the context window. So a 100 bp enhancer +request succeeds, but the model saw ~150 bp of padding; compare your length +against `context_window_bp` (from `GET /v1/tasks/{task}/models`) to know whether +it scored real sequence. Longer-than-context input is fine: the scanner steps a +prediction window at a time and pads only the final partial window. All lengths +are measured **after whitespace is stripped**, so a line-wrapped FASTA body can be +pasted verbatim. Under the floor and over the cap are both +`422 validation_failed` — over-length is *not* a `413`. + +`expression` additionally needs a cell-type/assay context string +(`--description`, e.g. `"K562 cells"`). The model always scores exactly one +9,198 bp TSS-centred window, so 9,198 bp is a hard floor — but the endpoint +accepts up to 500,000 bp and will cut the window for you if you pass +`--tss-index` (the 0-based TSS offset into the sequence). `--tss-index` is +required for any expression sequence that is not exactly 9,198 bp. +`annotation` defaults to `Prefer: respond-async` and polls to completion; the +mode is the runner's choice, not an API constraint. +Details: `references/tasks.md`. + +`options` is closed (`additionalProperties: false`) on every task, and each task +declares different keys — `description` exists only on `expression`. An +unrecognised key is a hard `422 validation_failed`, never ignored, so never +forward an option you have not confirmed against the live schema. + +## Authentication + +This skill calls a hosted API and requires a partner bearer key (`gi_…`): + +```bash +export GI_API_KEY=gi_yourkeyhere +``` + +Request a key at **contact@genomicintelligence.ai**. Do not commit or hard-code a +key — it is resolved from the environment. Optional override: `GI_BASE_URL` +(default `https://api.genomicintelligence.ai`). See +`references/authentication.md`. + +## Install + +Python ≥3.8 and one package — no weights, no GPU: + +```bash +pip install requests +``` + +## Provided scripts + +This skill ships a small, self-contained (`requests`-only) runner rather than +inline snippets: the surface spans six tasks plus an async job (`annotation`) +and a windowing contract (`expression`) that do not inline cleanly. The runner +is the same client Genomic Intelligence's other integrations use. + +- **`scripts/gi_predict.py`** — one CLI, six tasks: FASTA → prediction → + `report.md` + `result.json` + `reproducibility/`, and a compact JSON summary on + stdout. Owns auth, length validation, the sync/async split, and error handling. +- **`scripts/gi_fetch.py`** — optional acquisition: gene symbol or region → + reference FASTA via Ensembl (public, no key), including TSS-centring for + `expression`. +- **`scripts/gi_client.py`**, **`scripts/gi_ensembl.py`** — the `/v1` client and + Ensembl helpers the two CLIs import. + +**Use the provided scripts — do not hand-roll `curl`, an Ensembl fetch, or an +inline HTTP client.** They own the length/async/expression contract. + +## Quick start + +Each task ships a real reference FASTA in `assets/demo/`: + +```bash +# Promoter scan of the TP53 locus (chr17, GRCh38) +python scripts/gi_predict.py --task promoter --demo --output out/promoter + +# Splice sites in HBB +python scripts/gi_predict.py --task splice --demo --output out/splice + +# Expression (needs a cell-type context; fixture is a 9,198 bp TSS window) +python scripts/gi_predict.py --task expression --demo --description "K562 cells" --output out/expr + +# De-novo annotation (async submit → poll, no extra flags) +python scripts/gi_predict.py --task annotation --demo --output out/annot +``` + +**By gene name** (fetch → predict, the common real case): + +```bash +FASTA=$(python scripts/gi_fetch.py --gene TP53 --out out/tp53.fa) +python scripts/gi_predict.py --task promoter --input "$FASTA" --output out/promoter + +# Expression of HBB in K562 — the exact 9,198 bp TSS window is built for you +FASTA=$(python scripts/gi_fetch.py --gene HBB --for-expression --out out/hbb.fa) +python scripts/gi_predict.py --task expression --input "$FASTA" --description "K562 cells" --output out/expr + +# Or hand over a whole locus and name the TSS; the server slices TSS +/- 4,599 bp. +# +# STRAND: expression scores whatever you send, in the orientation you send it. +# It never reverse-complements, and nothing in the request or the response +# reports strand -- a wrong-strand window returns a confident number, not an +# error. Always submit gene-sense sequence. --region returns the strand you ask +# for and defaults to --strand 1, so a minus-strand gene needs --strand -1 +# explicitly. HBB is minus-strand. +LOCUS=$(python scripts/gi_fetch.py --region chr11:5,220,000-5,240,000 --strand -1 \ + --out out/locus.fa) +# Offset of the TSS into the returned sequence, 0-based, whitespace stripped. +# Plus strand: TSS_INDEX = TSS - REGION_START +# Minus strand: TSS_INDEX = REGION_END - TSS (the sequence is reverse-complemented) +# Must satisfy 4599 <= TSS_INDEX <= len(sequence) - 4599. +# HBB 5' end on the minus strand is 5,229,395 (Ensembl, GRCh38). +TSS_INDEX=$(( 5240000 - 5229395 )) +python scripts/gi_predict.py --task expression --input "$LOCUS" --description "K562 cells" \ + --tss-index "$TSS_INDEX" --output out/expr +``` + +Prefer `--for-expression` when you have a gene symbol: it resolves the canonical +transcript and cuts the window for you, so there is no offset to get wrong. A +`--tss-index` that is in range but wrong is not an error — it scores the wrong +window and returns `200`. + +`gi_predict.py` prints a compact JSON summary to **stdout** (headline scalars +only; bulky per-item arrays stay in `result.json`). Progress/verification lines +go to **stderr**: + +``` +[gi-] OK — wrote out//report.md (NNN ms wall) +``` + +## Minimal inline call (no scripts) + +For the simplest sync tasks you can call the endpoint directly: + +```python +import os, requests + +base = os.environ.get("GI_BASE_URL", "https://api.genomicintelligence.ai").rstrip("/") +resp = requests.post( + f"{base}/v1/tasks/promoter/predict", + headers={"Authorization": f"Bearer {os.environ['GI_API_KEY']}", + "Content-Type": "application/json", + "User-Agent": "BioNeMo-GI-Skill/0.1.0"}, + json={"sequence": "ACGT...", "sequence_name": "example"}, + timeout=300, +) +resp.raise_for_status() +body = resp.json() # {"data": {...}, "meta": {...}} +print(body["data"]["summary"]) +``` + +Prefer the runner for `expression` (window/`tss_index` bounds + `description`) and +`annotation` (async) — those are error-prone to inline. + +## Standard workflow + +1. **Identify the task** from the request (map to one of the six above; if + ambiguous between promoter/enhancer/chromatin, ask — they are distinct models). +2. **Resolve the sequence.** If the user attached a FASTA, use it. If they named + a gene, `gi_fetch.py --gene ` (add `--for-expression` for expression). + If they gave a region, `gi_fetch.py --region `. Add + `--species ` for non-human (default human/GRCh38). +3. **Predict:** + ```bash + python scripts/gi_predict.py --task --input --output \ + [--model ] [--description ""] [--tss-index ] + # --description and --tss-index: expression only + ``` +4. **Read the result:** parse the stdout JSON for the headline; open + `/report.md` or `/result.json` for detail. + +## Validate and report + +Treat an invalid alphabet, an out-of-bounds length, a missing `expression` +window/description/`--tss-index`, or a non-2xx response as **hard failures** (the +runner exits non-zero and names the cause on stderr). Treat zero hits on a +sequence you expected to be feature-bearing as a **warning**. Record +`meta.model` and `meta.request_id` for audit. For `expression`, also check the +`scored_window` / `tss_index` echoed in the stdout summary: a `--tss-index` that +is in range but wrong is not an error, it just scores the wrong window. + +## Troubleshooting + +| Symptom (stderr) | Cause | Fix | +|---|---|---| +| `GI_API_KEY is not set` | No key | `export GI_API_KEY=gi_…` | +| `sequence too short: … < 9,198 bp minimum` | Expression sequence below the window size | Use `gi_fetch.py --gene X --for-expression` | +| `sequence too short: … bp minimum` (other tasks) | Below the task floor (promoter 300, splice 100, enhancer 50, chromatin 200, annotation 1,000) | Fetch more sequence; server-side this is a `422`, not a `413` | +| `API error: [413 payload_too_large]` | Raw request body over 16 MiB | Split the input; this is the body cap, not the sequence cap | +| `--tss-index is required unless the sequence is exactly 9,198 bp` | Longer locus, no TSS named | Add `--tss-index <0-based offset>` | +| `--tss-index … outside the allowed range` | TSS too close to an edge | Submit more flanking sequence | +| `--description is required` | expression w/o context | `--description "K562 cells"` | +| `API error: [401 …]` | Bad/revoked key | Re-check `GI_API_KEY` | +| `API error: [422 …]` | Body/model rejected | Check `--model` in `references/tasks.md` | +| `API error: [429 …]` | Rate limit | Back off; partner tiers have caps | +| `API error: [504 timeout]` | Large sync req, cold GPU | Retry or shorten | +| `parsed an empty sequence` | Empty/invalid FASTA | Check the file is a single ACGT record | +| `invalid input — …: sequence contains characters outside ACGTN` | IUPAC ambiguity codes or gap characters | Resolve them to explicit bases; the parser refuses rather than deleting them, because deleting shifts every downstream coordinate | +| `invalid input — …: expected a single FASTA record` | Multi-record FASTA | Split the file and submit one record per request | +| `network error reaching the API` | DNS/TLS/connection failure or read timeout | Transport-level, not a request problem; retry | +| `timed out waiting for the job` | Async job outlived the poll deadline | Retry, or shorten the input | + +More: `references/errors.md`. diff --git a/nim-skills/genomic-intelligence-nim/assets/demo/annotation_tp53.fa b/nim-skills/genomic-intelligence-nim/assets/demo/annotation_tp53.fa new file mode 100644 index 0000000..d8cbe73 --- /dev/null +++ b/nim-skills/genomic-intelligence-nim/assets/demo/annotation_tp53.fa @@ -0,0 +1,241 @@ +>human|TP53|chr17:7668402-7687550|GRCh38 +CACCCCTCAGACACACAGGTGGCAGCAAAGTTTTATTGTAAAATAAGAGATCGATATAAAAATGGGATATAAAAAGGGAG +AAGGAGGGGAAGGGTGGGGTGAAAATGCAGATGTGCTTGCAGAATGTAAAAGATGTTGACCCTTCCAGCTGGACGTGGTG +GCTCACAATTGTAATCCCAGCACTCTGGGAGGCTGAGACAGGTGGATCGCCTGAGCCCAGGAGTTTGAGACCAGCCTGGG +CAACACTGTGAGACCCCATCTCTACAAAACATGCAAAAGTTGGCTGGCCATGGTGGCATGAACCTGTGGTCCCAGCTACT +CCGGAGGCTGAGGCAGGACTGCTCGAGCCGGGGAGGCAAAGGCTGCAGTAAGCCAAGATCACGCCACTCCACTCCAGCCT +GGGCAACAAAGCGAGACCCAGTCTCAAAGAAAAAGAAAAAAAAAAAAAAAAAAGAAAAAAGAAATTGACCCTGAGCATAA +AACAAGTCTTGGTGGATCCAGATCATCATATACAAGAGATGAAATCCTCCAGGGTGTGGGATGGGGTGAGATTTCCTTTT +AGGTACTAAGGTTCACCAAGAGGTTGTCAGACAGGGTTTGGCTGGGCCAGCAGAGACTTGACAACTCCCTCTACCTAACC +AGCTGCCCAACTGTAGAAACTACCAACCCACCGACCAACAGGGAGAGGGAACAAGCACCCTCAAGGGGGTCAAGTTCTAG +ACCCCATGTAATAAAAGGTGGTTTCAAGGCCAGATGTACATTATTTCATTAACCCTCACAATGCACTCTGTGAGGTAGGT +GCAAATGCCAGCATTTCACAGATATGGGCCTTGAAGTTAGAGAAAATTCAACAGTGAGGGACAGCTTCCCTGGTTAGTAC +GGTGAAGTGGGCCCCTACCTAGAATGTGGCTGATTGTAAACTAACCCTTAACTGCAAGAACATTTCTTACATCTCCCAAA +CATCCCTCACAGTAAAAACCTTAAAATCTAAGCTGGTATGTCCTACTCCCCATCCTCCTCCCCACAACAAAACACCAGTG +CAGGCCAACTTGTTCAGTGGAGCCCCGGGACAAAGCAAATGGAAGTCCTGGGTGCTTCTGACGCACACCTATTGCAAGCA +AGGGTTCAAAGACCCAAAACCCAAAATGGCAGGGGAGGGAGAGATGGGGGTGGGAGGCTGTCAGTGGGGAACAAGAAGTG +GAGAATGTCAGTCTGAGTCAGGCCCTTCTGTCTTGAACATGAGTTTTTTATGGCGGGAGGTAGACTGACCCTTTTTGGAC +TTCAGGTGGCTGTAGGAGACAGAAGCAGGGAGGAGAGATGACATCACATGAGTGAGAGGGTCTGTGCCCCTTTTCCCTGA +CCAATGCTTTGAAGGGCCTAAGGCTGGGACAACGGGAATTCAAATCAAGATGGTGGCCACACCCCATGCAAATATGTTTA +CTGAGCACCTCAGAGTATTAGTGTGTATTAGTCTCGTAATCTTCCCTTACCCCATTTTACTTTATTTATCTTTTTTGAGA +CGGAGTTTCACTCTTGTTGCCCAGGCTGGAGTGTAATGGTGAGATCTCAGCTCACCGCAACCTCTGCCTCCCGGGTTCAA +GCGATTCTCCTGCCTCAGCCTCCCGAGTAGGTAGCTGGGATTACAGGCATGCATCACCACGCCCGGCTACTTTTGTATTT +TTAGTAGAGATGGGGTTTCTCCATGTTGGTCAGGCTGGGCTCAAACTCCCGACCTCAGGTGATCCACTCGCCTTGGCCTC +CCAGAGTGTGGGATTCGTGAGCCACTGCGCCCGGCCCCCTTACCCCATTTTATATATAAGGAAACTGAGTTTGACGGGGG +TCACCTAGGACCTGCCGGTGCATGGCAGGGCTGAGTATATGACCTGAAACTCTGGCTGTATTCAGTATTACACAATTATT +AGGCCCCTCCTTGAGACCCTCCAGCTCTGGGCTGGGAGTTGCGGAGAATGGCAAAGAAGTATCCACACTCGTCCCTGGGT +TTGGATGTTCTGTGGATACACTGAGGCAAGAATGTGGTTATAGGATTCAACCGGAGGAAGACTAAAAAAATGTCTGTGCA +GGGCTGGGACCCAATGAGATGGGGTCAGCTGCCTTTGACCATGAAGGCAGGATGAGAATGGAATCCTATGGCTTTCCAAC +CTAGGAAGGCAGGGGAGTAGGGCCAGGAAGGGGCTGAGGTCACTCACCTGGAGTGAGCCCTGCTCCCCCCTGGCTCCTTC +CCAGCCTGGGCATCCTTGAGTTCCAAGGCCTCATTCAGCTCTCGGAACATCTCGAAGCGCTCACGCCCACGGATCTGCAG +CAACAGAGGAGGGGGAGAAGTAAGTATATACACAGTACCTGAGTTAAAAGATGGTTCAAGTTACAATTGTTTGACTTTAT +GACGGTACAAAAGCAACATGCATTTAGTAGAAACTGCACTTCAAGTACCTATACAGCTGACTTTTAAAAATATTTATTTA +TTTATTTTGAGATGGGGTCTCACTCTGTTGCCCAGGCGGGAGTGCAATGGTGCAATCTTGGCTGATTGCAATCTCCGCCT +CTGGGGTTCAAGTGATTCTTGTGCCTCAGCCTCCCGAGTAGCTGGGACTACAGGCGTGTGCTACCACACCTGGCTAATTT +TTGTGTTTTTAGTAGAGATGGGGCTTCACCATGTTAGCCAGGCTGGTTTCCAACTCCTGACGTCAGGTGATCTACCCACC +TCCACCTCCCAAAGTGCTGGGATTACAGGTGTGAGCCACTGTGCCCGGCCCTTTTTTAAATTTTAGAGATGATGTCTTGC +TATGTTGTTCAGGCTGGACTCAAACTCTTGGGCTCAAGAGATCCTCCTGCCTTAGCCTCTCAAGTAACTGGGACTACATG +TGCATGCGACTGTGCCTCGTTTCTTTTCTTTTTTTTCTGAGACGGAGTCTCACTCTATCGCCCAGGCTGGAGTGCAGTGG +CGCCATCTTGGCTCCCTGCAACCTCCGCCTCCTGGTTCAAGCGATTCTCCTGCCTCAGCCTCCCAAGTAGCTGGGATTAC +AGGCACCTGCCATCACGCCCGGTTAATTTTTGTATTTTAGTAGAGACGGGGTTTCACCATGTTGGCTAGGCTGGTCTTGA +ACTCCTGACCTCAGGTGATCCACCCGCCTCAGCCTCCCGAAATGCTGGGATTACAGGCGTGAGCCAGTGCGCCTGGCCTT +TTCTTTTTTTGAGTCTCGCTCTGCGCCCAGGCTGTGCCTGGCTCGACTGTGCCTCCTTTCATGCAACCATGCTGTTTCTC +ACTTTCAGTAACAATATTCAATAAATCACATGAGATATACAACATTTTATTACTATAAAAAGGGCTTTGTGTTAGATGAC +TTTGCCCAACTGTAGGGTAACTTAAATGCTCTGAACACGTTTCAAGTAGGCTAGGGCTGAGTGTGGTAGCTCATGCCTGT +AACCCCAATACTTGGGGAGGCTGAGGTGGAAGGATTGATTGAGCCCAGGGGTTTGATACCAGCATGGGCAACGTAGCAAG +ACCTTGACTTCACAGAAAATAAAAAATTAGCTGGGTGTCGTGGCATGTGCCTGTAGTCCTAGCTACTTGGGAGGGTGAAA +TCACCGGAGCCCAGGGAGGTCAAGGCTGCAGTGAGCTGAGATGGTGCCACTGCACTCTAGCCTGAGTGACAGAGTGAGAC +TCTGTCTTTAAATAAATAAATAAAAATTAGCCGGGCGTGGTGGCTCACACCTGTAATCCCAGCACTTTGGGAGGCCGAGG +CGGGCGGATCACATGGTCAGAAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCTGTCTCTACTAAAAATACAAAAAT +TAGCTGGGCGTGGTAGCAGGCGCTTGTAGTCCTAGCTATTCGGGAGGCTGAGGCAGGAGAATCACTTGAACCCAGGAGGC +AGAGGTTGCAGTGAGCCGAGATCATGCCACTGCACTCCAGCCTGGGCGACAGAGTGAGACTGAGTCTCAAAAAAATAAAA +TAAAATAAAATAAAAATAAATAAATAAAAATTAGCCAGGCATGGTGGTGCAGGCCTGTAGTTGAAGCAACTTGGGAGGCT +GAGCTGGGAGGATGGATGGAGCCTGGGAGGTGGAGGCTGCAGTGAGCTGTGACTGCACTACTGCACTCTATCCAGCCTGG +GTGACAGAGCAAGACCTTGTCTCAAAAAAGTAGGCTAGAGACCAGCCTGGGCAACATAGTGAGACTCTATCTATCTACAA +AAAATTTTAAAAATTAGCTGGGTATGGTGGTGTATGCCTGTGGTCCTAGCTACTGGGGAGGCAGAGTTAGGGGGATTGCT +TGAGCCCAGGAGGGTATAATGAGCTATGATCACATCACTGTAATCCAGCCTGGGCAACAGAGCAAGATGCTGTCTCCATT +AAAAATAAAATAAAAGTAGGCTAGGCAGGCCGGGTGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCAAGGC +AGGCAGATCACAAGGTCAGGAGTTCGAGACTAGCCTGGCCAACATGGTGAAACCTCATCTCTACTAAAAAAAAAAATAAA +TAAATAACAAAAAATTAGCTGGGCGTCGGGGCAGGTGCCTGTAATCCCAGCTACTCAGTGGGCTGAGGCAGGAGAATCGC +TTGAACCCAGAAGGCGGAGGTTGCAGTGAGCCGAGATCCCGCCACTGCACTCCAGCCTGGGTGACAGAGTGAGACTCTGT +CTCCAAAAAAAAAAAAAAAAAAAGCAGGCTAGGCTAAGCTATGATGTTCCTTAGATTAGGTGTATTAAATCCATTTTCAA +CTTACAATATTTTCAACTTACGACGAGTTTATCAGGAAGTAACACCATCGTAAGTCAAGTAGCATCTGTATCAGGCAAAG +TCATAGAACCATTTTCATGCTCTCTTTAACAATTTTCTTTTTGAAAGCTGGTCTGGTCCTTTAAAATATATATTATGGTA +TAAGTTGGTGTTCTGAAGTTAGTTAGCTACAACCAGGAGCCATTGTCTTTGAGGCATCACTGCCCCCTGATGGCAAATGC +CCCAATTGCAGGTAAAACAGTCAAGAAGAAAACGGCATTTTGAGTGTTAGACTGGAAACTTTCCACTTGATAAGAGGTCC +CAAGACTTAGTACCTGAAGGGTGAAATATTCTCCATCCAGTGGTTTCTTCTTTGGCTGGGGAGAGGAGCTGGTGTTGTTG +GGCAGTGCTAGGAAAGAGGCAAGGAAAGGTGATAAAAGTGAATCTGAGGCATAACTGCACCCTTGGTCTCCTCCACCGCT +TCTTGTCCTGCTTGCTTACCTCGCTTAGTGCTCCCTGGGGGCAGCTCGTGGTGAGGCTCCCCTTTCTTGCGGAGATTCTC +TTCCTCTGTGCGCCGGTCTCTCCCAGGACAGGCACAAACACGCACCTCAAAGCTGTTCCGTCCCAGTAGATTACCACTAC +TCAGGATAGGAAAAGAGAAGCAAGAGGCAGTAAGGAAATCAGGTCCTACCTGTCCCATTTAAAAAACCAGGCTCCATCTA +CTCCCAACCACCCTTGTCCTTTCTGGAGCCTAAGCTCCAGCTCCAGGTAGGTGGAGGAGAAGCCACAGGTTAAGAGGTCC +CAAAGCCAGAGAAAAGAAAACTGAGTGGGAGCAGTAAGGAGATTCCCCGCCGGGGATGTGATGAGAGGTGGATGGGTAGT +AGTATGGAAGAAATCGGTAAGAGGTGGGCCCAGGGGTCAGAGGCAAGCAGAGGCTGGGGCACAGCAGGCCAGTGTGCAGG +GTGGCAAGTGGCTCCTGACCTGGAGTCTTCCAGTGTGATGATGGTGAGGATGGGCCTCCGGTTCATGCCGCCCATGCAGG +AACTGTTACACATGTAGTTGTAGTGGATGGTGGTACAGTCAGAGCCAACCTAGGAGATAACACAGGCCCAAGATGAGGCC +AGTGCGCCTTGGGGAGACCTGTGGCAAGCAGGGGAGGCCTTTTTTTTTTTTTTTTGAGATGGAATCTCGCTCTGTCGCCC +AGGCTGGAGTGCAGTGGCGTGATCTCAGCTCACTGCAAGCTCCACCGCCCAGGTTCACGCCATTCTCCTTCCTCAGCCTC +CCGAGTAGCTGGGACTACAGGTGCCCAGCACCACGCCCGGCTAATTTTTTTTTGTATTTTTCAGTAGAGACGGGGTTTCA +CCGTTAGCCAGGATGGTCTCGATCTCCCAACCTCGTGATCCGCCTGCCTTGGCCTCCCAAAGTGCTGGGATTACAGGCAT +GAGCCACTGCGCCCAGCCAAGCAGGGGAGGCCCTTAGCCTCTGTAAGCTTCAGTTTTTTCAACTGTGCAATAGTTAAACC +CATTTACTTTGCACATCTCATGGGGTTATAGGGAGGTCAAATAAGCAGCAGGAGAAAGCCCCCCTACTGCTCACCTGGAG +GGCCACTGACAACCACCCTTAACCCCTCCTCCCAGAGACCCCAGTTGCAAACCAGACCTCAGGCGGCTCATAGGGCACCA +CCACACTATGTCGAAAAGTGTTTCTGTCATCCAAATACTCCACACGCAAATTTCCTTCCACTCGGATAAGATGCTGAGGA +GGGGCCAGACCTAAGAGCAATCAGTGAGGAATCAGAGGCCTGGGGACCCTGGGCAACCAGCCCTGTCGTCTCTCCAGCCC +CAGCTGCTCACCATCGCTATCTGAGCAGCGCTCATGGTGGGGGCAGCGCCTCACAACCTCCGTCATGTGCTGTGACTGCT +TGTAGATGGCCATGGCGCGGACGCGGGTGCCGGGCGGGGGTGTGGAATCAACCCACAGCTGCACAGGGCAGGTCTTGGCC +AGTTGGCAAAACATCTTGTTGAGGGCAGGGGAGTACTGTAGGAAGAGGAAGGAGACAGAGTTGAAAGTCAGGGCACAAGT +GAACAGATAAAGCAACTGGAAGACGGCAGCAAAGAAACAAACATGCGTAAGCACCTCCTGCAACCCACTAGCGAGCTAGA +GAGAGTTGGCGTCTACACCTCAGGAGCTTTTCTTTTTTTTTTTTTTTTTTGAGATAGGGTCTTGCTCTGTCACTCAGGCT +GGAGCACAGTGGTGTGATCACAGCTCACTGCAGCCTCCATCTCCTGGCCTCAAGTGATCTTCCCACCTCAGCCTCCTAAG +TGGCTGGGACTATAGGTGTGCACCACCATGCCTGGCTAATTTTTTGTATTTTTTTGTAGAGACGAGGTTTCATCATGTTA +CCCAGGCTGGTCTTGAACTCCTGGGCTCAGGTGATCTGCCTGCCTTGGCCTCTTTGAGAGTGCTGGGATTGCAGGTGTGA +GCCACCAAGCCTGGTCAGGAGCTTATTTTCAAAAGCCAAGGAATACACGTGGATGAAGAAAAAGAAAAGTTCTGCATCCC +CAGGAGAGATGCTGAGGGTGTGATGGGATGGATAAAAGCCCAAATTCAAGGGGGGAATATTCAACTTTGGGACAGGAGTC +AGAGATCACACATTAAGTGGGTAAACTATAAAAAAACACTGACAGGAAGCCAAAGGGTGAAGAGGAATCCCAAAGTTCCA +AACAAAAGAAATGCAGGGGGATACGGCCAGGCATTGAAGTCTCATGGAAGCCAGCCCCTCAGGGCAACTGACCGTGCAAG +TCACAGACTTGGCTGTCCCAGAATGCAAGAAGCCCAGACGGAAACCGTAGCTGCCCTGGTAGGTTTTCTGGGAAGGGACA +GAAGATGACAGGGGCCAGGAGGGGGCTGGTGCAGGGGCCGCCGGTGTAGGAGCTGCTGGTGCAGGGGCCACGGGGGGAGC +AGCCTCTGGCATTCTGGGAGCTTCATCTGGACCTGGGTCTTCAGTGAACCATTGTTCAATATCGTCCGGGGACAGCATCA +AATCATCCATTGCTTGGGACGGCAAGGGGGACTGTAGATGGGTGAAAAGAGCAGTCAGAGGACCAGGTCCTCAGCCCCCC +AGCCCCCCAGCCCTCCAGGTCCCCAGCCCTCCAGGTCCCCAGCCCAACCCTTGTCCTTACCAGAACGTTGTTTTCAGGAA +GTCTGAAAGACAAGAGCAGAAAGTCAGTCCCATGGAATTTTCGCTTCCCACAGGTCTCTGCTAGGGGGCTGGGGTTGGGG +TGGGGGTGGTGGGCCTGCCCTTCCAATGGATCCACTCACAGTTTCCATAGGTCTGAAAATGTTTCCTGACTCAGAGGGGG +CTCGACGCTAGGATCTGACTGCGGCTCCTCCATGGCAGTGACCCGGAAGGCAGTCTGGCTGCTGCAAGAGGAAAAGTGGG +GATCCAGCATGAGACACTTCCAACCCTGGGTCACCTGGGCCTGCAGAGAAGGAACCCCCTCCCCCAACACCATGCCAGTG +TCTGAGACAGCTCGGCTTCCTGTGGAGCAGGAAAAGAATGGCTGCTTCACATTCTCTCTTCCAATGTTTCACCACAACCC +AAGCACTCCTGCCCCACCCCTCACCAGCCATGCACTTCTTTGAGGAAAAGACAATCAGAGAGGGACTTCCAACCTTCCCA +CCACTAAATCCCCAAGACTTCCTAAATGTGCACCCTATTCCCAACTCCCTTCCTGTATTTTTTTTTTTTTTTTGAGATGG +AGTCTCTCTCTGTCACCTAGGCTGGAGCACAGTGGCATGATCTCAGCTCACTGCAACCTCTACCTTCCGGGTTCAAGCCA +TTCTCCTGCCTCAGTCTCCCGAGTAGCTGGGATTACAGGCGAGTACCACCACACCCAGCTAATTTTTGTATTTTTAGTAG +AGACAGGGCTTTGCATGTTGGCCAGGCTGGTCTCGAACTCCTTACTTCAGGTGATCGGCCCGCCTCAGCCTCCTAAAGTG +CCAAGATTACAGGTGTGAGCTACCGTGCCCTGCTCCCACCTCCTGTTAACAAGGATATAGTCATTCTCAGCCTGCAATCT +CTGTATGGGGAAGGACACCCCCTTGGCCCCCACCCTTCCCCACCTGATACACGGCTCCATTTCTTTGATTCCTTTCACTG +CAAAGCTTCTGGAAGAACAACTGTCTCACCGCTCACCTGCCCATTCTCTTCGGACACTCCTCAGCCCTGCATTACAAACC +CCTCACGAATGGCCCGTCTCGGCTTCTTTAATCTCATCTCTTAACAACCACTCCCTCTTCCCCAAAAGCTCTAGCTAGAC +TGGCTGCCCTTCTCTGCTAATCAACTGGTGGTTCCTTGGCTAGCCAGGAACATGGGGGTAGGCTCCTTCCCGTGCAGACT +TTAAGTCATCCTATTTTAATTCACATCACCTCATTTGCATTCTCATAGCACTTACATTGTCTGATACTTTTCCTTGTTTA +TTTTATCTGTTTCCTCTAATAGCATATACACTTCCTAAGGGCAGGGCAGTGATCTATCTTGTTGTCTTGCTGACCAAAGT +ATTAGATCACAATGCCTTGCACCTGCTTGGGCTCAATAAATGTGAATAACACACAAGCCTGTTATATGAGAGGTTAAGAG +AGCGAGAAAGAGCAAGGGGCAGCCCCTGTGTGGACCAGCATCTTGCACGAAGTTATGCAACTATCATCGCACCTTCTCCC +AGACAAGCTTTCAAAGGCTTTGCCATGTTTTCTTTTGTTTTGTTTTTTTGTTTGTTTTTTGAGATGGAGTTTGGCTCTTT +TCGCCCAGGCTGGAGTGCAGTGGTGCAGTCTAGGCTCACTGCAACCTCTGCCTTCTGGTTTCAAGCAATTCTCCTGCCTC +AGCCTCCCGAGTAGCTGGGATTACAGGCCCCTGCCACCATGCCTGGCTGATTTTTTGTATTTTTAGTAGAGACAGGGTTT +CACCATGTTGGCCAGGCTGGTGTGGAACTCCTGACCTTGTGATCCACCTGCCTCGGCCTCTCAAAGCGCTGGGATTACAG +GTGTGAGCCACTGTGCCTGGCCCGCCATGTTCTTTCTTTCTTTCTTTTTTCTTTTGAGGCAGGGTCTTGCTTTGTTGCCC +AAGCTAGGGTACAGTGGTGCAATCATGGCTCACTACAGCCTCGGACTCCTGGGCTCAGTGATTCTCCTGCCTCAGCCTCC +CAAGTAGCTGGGACCACAGAGGCCTGCCTGGCTAATTTTTTAGTCTTTTTCTTTTTCTTTTTTTTTTTGGAGACGGAGTC +TCGCCCTGTCACCCAGGCTAGAGTGCAGTGGTGTGATCTCGACTCACTGTAACCTCCACCTCCCAGATTCAAGCGATCCT +CTTGCCTCAACTTCCTGAGTAGCTGGGATTACAGGCGCCCACCAATGCGCCTGATTAATTTTTTGTATTTTTAGTAGAGA +TGGGGTTTTGCCATGTTGGCCAGGCTGGTTTCGAACTCCTGACCTCAGGTGATCCTCCCGCCTCCGACTCCCAAAGTGCT +GGGATTACAGGTGTGAGCCACTGCACCCGGCCAATTTTTGAGTTTTTTTGTAGAGGCAGGGTTTCACTATGTTGCCCAGG +CAGGATGCTCTCTTTCTTTTTCTTTTTTTTTTTTTCAGGGATGCTCTCTTTCTTTATGCCAAATTTGTCATCAGATTTGC +TAAGAAACATGCCTACTGTAAGTGTTTGTTACACTTTTCTGTTTTTTTTTTTTTTTGAGACAGAGTTTTGCTCTCGTCCA +GGCTGGAGTGCAATGGTGCGATCTCGGCTCACCGCAACCTCTGCCTCCCAGGTTCAAGCGATTCTCCTGCCTCAGCCTCC +CGAGTAGCTGGGATTACAGGCATGCGCCACTGCGCCCGGCTAATTTTGTATTTTTAGTAGAGACGGGGTTTCTCCATATT +GGTCAGGCTGGTCTTGAACTCCCAACTTCAGGTGATCCGCCCGCCTTGGCCTCCCAAAGTGCTGGGATTACAGGCATGAA +CCACTGTGCCCAGCCCACTTTTCTGTTGTTTGCACTGACAAAACATCCCCTACCAAACAGCTCCTTTAATGGCAGGCTCT +TTTCTTTTTTTATTTTATTTTATTTTATTTTATTTTATTTTATTTTATTTTGAGACGGAGTCTCAGCTCTTATTGCCCAG +GCTGGAGTACAGTGGCACTATCTCCGCTTACTGCAACCTCCTCCCGGATTCAAATGATTCTCCTGCCTCAGCCTGCTGAG +TAGCTAGGACTACAAGCATGTGCCACCACACCTGGCTAATTTTGTACTTCTAGTAGAGACGGGGTTTCACCATGTTGGTC +AGGCTGGTCTTGAACTCCCGACCTCAGGTGATCCACCCCCCTTGGCCTCCTAAAGTGCTGGGATTACAGGCGTGAGCCAC +CAAGCCTGGCCTACCTAGTACTCTGTGTATTATGGGAAATGTAGAGTTGAGGAAAGTGCTGGGCACACAGTAAGAGCTCA +ACAAAGGTTAGCTCTTTCTGCAATTGTTCTATTTCACTTGTTCTATATTATTATTCTAGAGAGAACTGTGTGATTGTTAG +TGCGGATCTGTGGTACTGCTCCCACCCCCACTCCATTAATGCAAGTACACCTCCTTCAGGGATCTATTCAGTCAACAGGC +CAGGAGGTGCTGTCCTGAAATGGGGGGCCCAAAGTCTCAATCCCACTTGGAGGGACACAGGTCTACAGACAGGTCTCCCT +GTCTTTATCTCTCAAATCTTCAGTAGCAACTAAAATCTCCGTGTTTTTCAGAGCAGGACCTTCCCAGGGGTACCAGCATC +AGTGGGCCAGGATACAAATGTGCCAGGCTGAACTAGGCCTTCCAAATGGCCAGGGAGCCAAGAGAAATGCAGGTGCCCTT +GGCTGGGTGGGAAGGCAATGAGATCAACTGAGACCCCAAACAGGGGCAGGCCTGACCAGAATCTTAACAGTGGCTGCTGG +TATCAGTCTTGAAGGCCTATATGTCCAGTGATTCCCTAAACAATATAGTACAAGTACTTACATACATACTTGCATGTACA +TTAGCATTTTCATTGTATGGGGGTAATGTAAGTAATCTAGAGATAATTTAAACTATATGGGAGGATATGTGTAGGTTAAA +TCCAAATACTATACCGTCTTATATATGGGACTTAGACATCTGTGGGTTTGGTGTGTGAGGAGTCCCAGAACCAAGCCCCT +ACAGATAGAGGGATAACTATATTGCCCTGTAACCTGCAACCCTGCTATATTTATTTATTAGTTTTGGTAGCTTTTTATGG +ATTTTCCATTAGGACTTTTTTTTTTTTTTGAGATAGAGTTTCACTCTTTTTTTTTTTTTTTGAGACGGAATCTCGCTCTG +TTGCCCGGCGTGGTGTGCAATGGCATGATCTCAGCTCACTGCAACCTCCACCTCCTGGGTTCAAGCAATTCTTCTGTCTC +AGCCTCCCAAGTAGCTGGGATTACAGGCGCCCACCACTACACCCAGCTAATTCTTGTATTTTTAGTAGAGACGGGGTTTC +ACCATGTTAGGTTGGTCTCAAACTCCTGACCTCAGGTGATCGGCCTGCCTCAGCCTCCCGAAGTGCTGGGATTACAGGCG +TAAGCCACCACACCCTGCCGGAGTTTCACTCTTGTTGCCCAGGCTGGAGTGCAATAGCGCGATCTCGGCTCACTGCAACC +TCTGCCTCCCAGGTCCAAGCAATTCTCCTGCCTCAGCCTCCTGAGTAGCTGGGATTAGAGGTGCCCGTCACCACGCCTGG +CTGATTTTTTGTATTTTTATTAGAGTTGGGGTTTCACCATGTTGGCCAGGCTGGTCCCAGGGAAGCCACCTGCCTCAGCC +TCCTAAAAGTGCTAGGATTACAGGCATGAGCCACCACGCCTGGCCCCATTAGGACATGTATGTATAGAATCATACTGGCT +GTGAATGTGTTTTATTTCTTCCTTTCTAATCGTTATTTTTTTTCTTTCCTTTTTTTTTTTTTTTTTTGACATGGAATTTT +GCTCTTGTCGCCTAGGCTGGAGTGCAATGGGACAATCTCGGCTCACTGCAACCTCTGCCTCCTGGGTTCAAGTGATTCTC +CTGCCTCTGCCTCCTGAGTAGCTGGGACTACAGGCGTTCACCACTACCCCTGGCTAATTCTTTTTTTTTTGAGACGGAGT +TTTTGCTTTTGTCACCCAGGCTGGAGTGCAATGGTGCAATCTCGGCTCACTACAACCTCCGCCTCCCAGGTTCAAGCGAT +TCTCCTGCCTAAGCCTCCCAAGTAGCTGGGATTACAGGCGCCCGCCACTACGCCCGGCTAATTTTTGTATTTTTAGTAGA +GATGGGGTTTCACCATGTTGGCCAGGCTGGTGTTGAACTCCTGACCTCAGGTGATCCACCCACCTCGGCCTCCCAAAGTG +CTGGGATTACAGGCATGAGCCACTGTACCCGGCCAACGCCTGGCTATTTTTTTAATATTTTAATAGAGACGAGGTTTCAC +CATCTTTGTCAGGCTGGTCTCCAACTCCAGACCTCAGGTGATCTGCCCACCTCGGCCTCCCAAAGTGCTGGGATTATAGG +CGTGAGACATCGGGCCACTAATCATTATTTCTTTTTCTTTTTTTTTTTTTGAGACACAGTCTTGCTCTGTCGCCCAGGCT +GGAGTGCAGTGGCTCGATCTCAGCTCACTGCAAGCTCCGCCCCCTGAGTTCACGCCATTCTCCTGCCTCAGCCTCCCGAG +TAGCTGGGACTACAGGCGCCCGCCACTACGCCCGGCTAATTTTTTGTATATTTAGTAGAGACAGGGTTTCACCGTGTTAG +CCAGGATGGTCTCGATCTCCTGACCTCGTGATCCACCCGTCTCGGCTTCCCAAAGTGCTGGGATTACAGGCCTGAGCCAC +CGCACCCGGCCCTCATTATTTCTTTTTCTTGCCTGGTTAAAACCTCCAGTATGGTATCAACGTTGTGAGAGTCAAATCCT +TTTCTAGTTCCTGATCTTAGAGGAAAAAGCGTTGAGTTTTCTTTTCTTTTTTTTTTTTTTTTTTTTTTGAGACGAAGTCT +CACTCTGTCACCCAGGCTGGAGTGCAGTGGCACGATCTAGGCTCTGCAAGCTCCGCCTCCCGGGTTCACGCCATTCTCTC +GCCTCAGCCTCCCGAGTAGCTGGGACTATAGGCGCCCGCCACCATGCCCGGCTAATTTTTTGTTTTTGTATTTTTAGTAG +AGACGGGGTTTCAGCATGCTAGTCAGGACAGTCTCGATCTCCTGACCTCGTGATCCGCCCGCCTAGGCCTCCCAAAGTGC +TGGCATTACAGGCGTGAGCCACCGCGCCCGGCAGCACTGAGTTTTCTACCATTATGTATGCTGCTAGTGGAACTCCGACT +GTGGACGCCCTGTTATCAAACTAGGTTAAGTTTCCTTTCCCTAGTTTGCTAGGAGGTTGGTTGGTTTGTAATCATGCATA +TGTGTTGAATATCATTAACTGCTTTTGCTACATCTGTTGAAATGATCATAGGGTTTTTATGTTTCCCTTTGTTAATGTGG +TGAATTACACAGACTGATTTTTTTCCCCCCAGTAAAGACCAGTCTGACTATGTTGCCCAGGCTGGTCTTGAAATCCTGGG +CTCAAGAGATCTTCCTGCCTCAGCCTCCTAAAATGTTGGGATTACAGGCCTGAGCTACTGCACCAGGCCAATTTTTGAAT +GTTGAATCAGCTACAATCATGAGATAAACATTATTTGGTTAGAATGTATTTATCCTTTTTCTTTTTCTTTTTTTGAGATG +GAGTCTCACTCTGTTGCCCAGGCTGGAGTGCAATGGTGTGATCCCAGCTCAGTGCAACTTCTGCCTCCTGGGTTCAAGCG +ATTCTCCTGCCTCAGACTCCCGAGTAGCTGGGATTTCAGGTGCCCACCACCATGCCCAGCTAATTTTTTTTTTTTTTTGA +GATGAAGTCTTGCTCTGTCGCCCAGGCTGGAGTGCAGTGGCACGATCTTGGCTCACTGCAACCTCTGCCTCCCGGGTTCA +AGCAATTCTGCCTCAGCCTCCTGAGTAGCTGGGATTACAGGCAGGTGCCACCACACCGGCTGATTTTTGTATTTTTAGTA +GAGATGGCGTTTCACCACATTGGTCAGGCTGGTCTTGAACTCCTGACCTCGTGATCTGCCCACCTCGGCCTCCCAAAGTG +CTGGGATTACAGGCGTGAGCCACCGTGCCCAGCCTGATTTTTGTATTTTTATTAGAAACGGGGTTTCACCATGTTGGTCA +GGCTGGTCTCAAACTCCTGACCTCAAGTGATCTGCCTGCCTCAGCCTCCCAAAGTGCTGGGATTACAGGCGTGAGCCACC +GCGACTGGCCTATTTATCCTTTTTTCTATATTACCAGGTTTGGTTTGCTAAAATTGGTTAGCTGTTGCATGTCTATGCTA +ACAGGAATATTGGTCTATATTTTCTTTTCTTATAATGTCCTTGTTTGGTTTTGGTACCAGGATTATGCTGGCTTCGAAAA +CAAGTTGGGAAATATTCCTCTATTTTTTCTTTCTTTCTTTTTTTTTCTGAGACAGGGTCTCACTCTGTTGCCCAGGCTGG +AGTGCAGTGGCGCAATCTCGGCTCACTGCAACCTCCGCTTCCCAGGTTCAAGGGATTCTTGTGCCTCAGCCTCCTGAGTA +ACTGGCATTACAGGTATGTGTCACCACGCCTAGCTACTTTTTGTATTTTTAGTAGAGATGGGGTTTCGCCGTGTTGGCCA +GGCTGGTCTCGAACTCCCGACCTCAAATGATCCCCTGCCTCAGCGTACCAAAGTGCTGAGATTACAGGTATGAGCCACCG +CGCCCAGTCTGTTCCTCTGTTTTCTGAAGAGTTTGTGTAAGATGGGTACTGTTTCTTCCTCCAACGTTTAAAGAGAGCAG +AGAACAGAGGAGATAAATAGAAAATAGCACTAAGAGGTCAGGCATGGTGGCTCACACCTGTAATCCCAGCACTTTGGGAG +GCCGAGATGGGATGAAAGCTTGAGGCCAGCAGTTTGAGACCAGCCTGGGCAACATAGTGAGATCCTGTCAATACAAAAAA +ATAAAATAGTTAGCTGGGTGTGGTGGAGTAATCCCAGCTACTCGTGAGGCTGCTAGAGGACTGCTTGAGCCCAGGGGTTC +GAGGCTGCAGTAAGCCTTGATTGTGCCATTGTACTCTAGCCTGGGCAACAGTGTGAGTCCCTGTCTCAAAAATTAACAAA +GAAAAAAAGAAAATAGCACTAAAATGGTAGCCCTATACTCCAACTGTAAAATAATTAAAATTAAAAGCATTCAGTAGAGA +AAGGAAGCCTATTTCAACAAGTGGAGACAGAATAACTGGATTTCCATACAGGAAAGATACCAGAGACTGACTCCTACACC +TCACACCATAAACAATTAATTTTAAGAATTAATTAATGGCTCAAGGACCTTACTGTAAAACTTACAACCATAAAGGTCCT +AAAAGAAAATGTAAGATAATATCTTCATGACCCTGGGGTTAAAAAAAAAAAAAAAGATGTCCTAAACAGGACAAGGCAAA +TACTGAACATAAAAAAGATAAATCCACTCCTCTTAAGATACTGTAAACTCTGTAAAGCAAACAAATAGGCAAGCAACAGA +TCAGAAGAAAACATTCACGACACATGGATCTGATAAAGGACTTGTATCCAGAATGTATAAAGCAGTCCCACAACTGAACA +ATAAAAACAAACAAAAAACCAAAATAACAGGTAAAAGACTCGAAGAGCTACTTTACAAACAAAATACGAATGGCCAATAG +GCACATGAAAAAATGCTGAACATCCTTAGTCAATAGAGAACTGTAAATTACAACCACAAGGATATACCACATTAGAAAGA +CTGACAATACCTAATGTCCGGAAGGCTGTGGCACAACCATAATAACTCCCATACCTTGCTAGTTGGAGTGTAAAATGGTA +CAACCGCTCTGGAAAACTCAGAGCTTCTGAAAAAGTTCAAAATACAGCTACTTTTTACTTCCAAACTCGCAATTCCCCTC +CTAAGTATTTCTCCAAGAAACACGAAAACATATGATCACAAAAAGAATTGTACAAGAATGTTTATAGCAGCTTTATTTCA +TAACCGCAAATGGGAAACAACTCAAAAGGCCATCAAAAGGACGGATATACAATCGATGGACTATACTAAATGAAAAGGAG +CAAAATACTGATATATACAACATGAACGAATGTCAGACAGTACATTGAAGGACAGAAGCCCGACAAAAATGAGCACATAA +TGTATGATTCCCCCCTTTTTTTTGAGACGGAGTTTCGTTCTTGTCGCCCAGGCTGGAGTGTAGTGGCACGATCTTGGCTC +ACTGCAACCTCTGCCTCCCGGGTTCAAGCGATTCTCCTGCCTCACCCTCCCGAATAGCTGGGATTACAGGCACCCACCAC +GCCCAGCTAATTTTTGTATTTTTTAGTAGAGACGGGGATTCACCACGTTGGCCACGCTGGTCTGGAACTCCTATCCTCAA +GTAATCCGCCCGCCTCGGCCTCCCAAAGTGCAGGCGTGAGCCACAGCGCCCAGCCTGATTCCATTCTATATGAAGTTCTC +CAACAGGCAAAATGGTTATGGAGATCAAAATAAAGGTGGGGTCGGGAATCGACTGGGAAGAGACGTGATGAAACGTTTCT +GGGACGATGAAAAGGGTCTGTGACTTGGTAGGCATCACGGAGCGGTTAGGGGCCAAAACTCATCTTCCTGTGCACTTGCT +GTGTGCACTGGCGCTGTGTGTAAATGCCACCTCGATTTAGGAAAAAGATGACGTAAGTACGGCACAAAGTGGCCGGTACG +CGGCAGGTGCATGGGAAGAAACTGCGGAATGAAACAACCGCGAGCTAAGAGATGGGGCAGCGGGAGAAATGAATTCGAGT +TCCGCCTCCTACCAGGAAGAACCGGCTCGGGCCGGAGGGCTGCACGGAGGACCACACGGACGCCTGCGGGCCCGCCCCTT +CCGCTTCACGACGTTCAGCCTGCGTCTGGAACTGGAATGGCCTAGCCCAAAGCTAGATAACAGGTAGATTGTTTTTCCGA +CAAATTATCAAACGACCCATCATTGCACTCTTTCAAAATTTGATTCTCAGACGTACCCATTCTTTTTTTTTTTCCTCCGG +GAAGATGAGATATACTCATTCTTGAAAATACCTCCGGGCTTGCCTTCTGCACACTTCTTTCCCTCCCTGTCTCACGCCAT +GGTAGCGTCCGCCTAGGTTGCAGGCGACCCGCGGGGTGGGGCACACCATTCAAAGAAGGGGAGGGATTGAGGTTTGCATC +AAAACAAATACCCCTGCCTTTGCAAAGGCCATAACTAAGTAATCCAGAAAAAGAAATGCAGGCGGAGAATAGCAGCCTCC +CTCTGCCAAGTAAGAGGAACCGGCCTAAAGGACATTTTCTCTCTCTCTCCTCCCCTCTCATCGGGTGAATAGTGAGCTGC +TCCGGCAAAAAGAAACCGGAAATGCTGCTGCAAGAGGCAGAAATGTAAATGTGGAGCCAAACAATAACAGGGCTGCCGGG +CCTCTCAGATTGCGACGGTCCTCCTCGGCCTGGCGGGCAAACCCCTGGTTTAGCACTTCTCACTTCCACGACTGACAGCC +TTCAATTGGATTTTCTCCATCTAGCGGAGCCGGGGGCTGCCTGGAAAGATCGCTCCAGGAAGGACAAAGGTCCGGAAGTT +GTGGGACCTTAGCAGCTTGGGCTCCCCGGATCACCCCCAAATGATCATTTCGGAATGGAGCCCCAGTTTTCACTAGGATG +CCATGGGCTCTAAAATATACAGCTATGAGTTCTCAATGTTTCGAGATCCAAAAGTCTCAGACCTCAATGCTTTGTGCATC +TTTTATTTCAGGGATTCCCTACGCCCAGCACCGGGTGGATGTGCAAAGAAGTACGCTTTAGGCCGGCTCAAGGTTCCCCA +AAGCTCCACTCCTCTGCCTAGGCGTTCAACTTTGAGTTCGGATGGTCCTAACATCCCCATCATCTACACCCAGGTCTCCC +AACAATGCAACTCCTATGATGATCCCTCTAGCCAAGCTTCCATCCCACTCACCCCCAAACTCGCTAAGTCCCCACTGCCC +CACCCCCAGCCCCAGCGATTTTCCCGAGCTGAAAATACACGGAGCCGAGAGCCCGTGACTCAGAGAGGACTCATCAAGTT +CAGTCAGGAGCTTACCCAATCCAGGGAAGCGTGTCACCGTCGTGGAAAGCACGCTCCCAGCCCGAACGCAAAGTGTCCCC +GGAGCCCAGCAGCTACCTGCTCCCTGGACGGTGGCTCTAGACTTTTGAGAAGCTCAAAACTTTTAGCGCCAGTCTTGAGC +ACATGGGAGGGGAAAACCCCAATCCCATC diff --git a/nim-skills/genomic-intelligence-nim/assets/demo/chromatin_active_promoter_chr19.fa b/nim-skills/genomic-intelligence-nim/assets/demo/chromatin_active_promoter_chr19.fa new file mode 100644 index 0000000..2b97a66 --- /dev/null +++ b/nim-skills/genomic-intelligence-nim/assets/demo/chromatin_active_promoter_chr19.fa @@ -0,0 +1,502 @@ +>human|chr19:35546347-35586347|GRCh38 +GGACCTATCCTCTATCTCCCCGATTCCTGTAATTTTGCCACCATCTGGTGGTTCCTCTTCCAGATGGGCCCCCAGAACCT +GTCCTGCCCTCTTCCTACCCACGAACTTCCCACAAATCCCACGTGGTTTATCTTGATCCCCTCACCTTGAAGTGACCTCT +TTCTGCTTTCTGTTCTCAGATGCTGTTCTTGGCCACTTTCTTTCCCACCTGGGAAGGCGGCATCTATGACTTCATTGGGG +TGAGAGGGGCCAGGGAAGGGAAGGGAGTTCAGGAATGGGGCTCCCTGTCCCCCTGTGCTTACTTAAGCCTCAACCTGACC +CGCAGGAGTTCATGAAGGCCAGCGTGGATGTGGCAGACCTGATAGGTCTAAACCTTGTCATGTCCCGGAATGCCGGCAAG +GGAGAGTACAAGATCATGGTTGCTGCCCTGGGCTGGGCCACTGCTGAGCTTATTATGTCCCGGTGCGTACAGCAGCCTGG +AGCCCAGACCCCTGAGAAGGGACACCTGGGTTCCACGGGGGTGCTGGAGGGCAGGGGCTCAAAGCCTGGTGCTGAAGGTG +TCTGAGTACTGGAGAATCCCATCCTTTGCCTTCCTCAGCTGCATTCCCCTATGGGTCGGAGCCCGGGGCATTGAGTTTGA +CTGGAAGTACATCCAGATGAGCATAGACTCCAACATCAGTCTGGTAGGCAGTCGTGCTCTCCCACATACACATTTCTGCT +GGCGGCCATACTCCTCCCCAAGGCCTGGCCCCGACTTTCTGCCTCCCTCTAGGTCCATTACATCGTCGCGTCTGCTCAGG +TCTGGATGATAACACGCTATGATCTGTACCACACCTTCCGGCCAGCTGTCCTCCTGCTGATGTTCCTCAGTGTCTACAAG +GCCTTTGTTATGGAGTGAGTTGGGTGGGGTTTAGGGCTGGGTCCAAAGTGGGGTGGGTTATCTAGTCTCCCTTCCTTATT +GTGACATTTTCCTGCAGGACCTTCGTCCACCTCTGCTCGCTGGGCAGTTGGGCAGCTCTACTGGCCCGAGCAGTGGTAAC +GGGGCTGCTGGCCCTCAGCACTTTGGCCCTGTATGTCGCCGTTGTCAATGTGCACTCCTAGGCTTGGTGTCTCAGACATT +GATGTACCTTTTCCCTGCCTCACTCCAGGTTTTAGTGAAGTAAACAGTATTTGGAAAGTTGTTGCTGCCTCCATTTCTCT +CTCTTGGGAACTGTCTCCCAATACCGTGTCCACCTGGGTCTCAGAGGCCCTGGTTCTGTCTCAGGAGCCAGGTAGACAAG +CTGGAAGCTAGCCAGTCACTGACTTGTCCCATGTCTTGTTCCTCAGGCTCCTGGTTTGCCAGGAGTAGACAGAAGGTTTG +GATGATCTTTGAGCAGTGGCAGAGGCCAGGGCCCTCAGGGAACAGATGATAGAGGGGAGCTAGAATCCAAGAGAAGGCCC +TTGGGGGGCTCTTCCTCCTCACAGCCCCAACCTGGGCCTCCTCACATGGGCCCTTCCCGGGCTGGTTGCCTCTGAGGCTC +CTGGCCCCAGTGTCCCCCTCCAATCCATCCTCTGTATGGCAGCCAGGGGATCTATCTGAAACCCGTCTAACCAGGTCATC +CTCCCACTTGCAGCCACTTGCGGCCCCTTGTTACACAGTGGACAGTCCAATTGCTTGGCCTTTGGTTTTACCCAACCAGC +AAAACCAGCTTTTCTGAAACTGCTCCCTAGAATAATTTGTCCAGCCAGATTCTTAACATCGTTCAAGCCATGGGGTCAGC +ATGGGGCTGGGGGGAGAGTTGTGACTGTGAGCTCCTCAGTTCTATGGCCCTCAGATCAGATCCCAGCCTGAGCCTTCTCA +GGGTGGAGTGGAGAGACAGCTGCAGCCAAGAAGCAGAGCAGTGGGGCCCCCAGCTTGGACATTGTCCTGGATGCCCCCCT +CCACCCTCAGCCTTCCTTGGTCCACTGAAGCCGGCTCCCCGCCTTTCTCTGAGGGGGATGTGTCAGAAGTTTTGAATGTC +ATGTTTAAGCTCCACTTTGATGTACACCCTCCCCACTCAGGAAGATGCTCCCCATCCTTGGTGTTCCCAGTGGGGTACCC +CAGGGAGTAGGATAGCACCATTGCCCCTCCTCCTCCACCAGGCCGTTGAAGTTCCACCTGATTTTTTTTAAGCTTAGGCC +TAGGAAAGCTCACTATGACCATCTCGATGTTTCCAGAGGGAGCATTTGCCTTCAGACGGCAGCTGCCATCTAGGCCACTC +TTCCTCATTGTTTGGAGTAAAGACAGGGTCCATGGGTCTCTTGGGAGCTGGAAGTGATTGATCACCTTGACTTTGATGTA +GAAAGGAAGTAGATGGGGCAGTCTATCTGGGTGGACTTGTGACAAGGTCACTTTCTCCCACACTTCCATGCCCCACATAG +CTCTTCACACATATTGAGACAAGTGTAGGATGCAAAATTACCAACTGGAATAATCCCAGCTTACATGGGGTTCAGGGAGA +GAGACTGGAGTGGCTGGGCCTGAGTTGGCAGAGGACGGTGAAGCCTGGGCGGTTGGACTGTGGGGAGCCAGGCTTCAGGT +GATCGGGGTTATGATGGGAAAACCCCGGGTTATGGGGACCCAGAGGAGCCGCCTGGCCTGCCGTGGAAGCAGTGCGGGCT +CCCCTGAGCAGAGGACATGTGAGCTGAGACCTAAAGAATGAGTGATTGGGGCAAGGCAGAGGTAATGGTCTTGAGGTCAG +AGAGGGGGCCTGTTTTTCCCAAGGGAACATAACTGGTTGGAATGAGTTCAAGGGGACTTGTAAGACTACAGAAGCTGGCA +GGGGTCAGATCACAGGAGGCTTTGGGAACCAAGGAGAGCTCTAGGCAGAGGAAGGACAGGGTCAGATTTGGCTTTAGGAA +GCTCTGCATGGCTGTTGTCTGAAATGGGAAAGCTGAGGCCACGAGGCCAGGGCTGGACCAGGGCAGGGCTTTGTGGGGTT +AGGAGAGTGACTAGAAGCCAAGCCCAGGTGTTGGTGATCAGTGGCTGTGTGTATTTATTTACCGGCAGGGGGCGCTAACA +GGTAAGGGAGGAGCAAAGAGTTCCAGGATAACTGGAGTTTGGGGCTTGTAAGCCCGAGGGCTCACCACGTTTCCAAGGGA +GAAGGGACCACACTGATGGGACAGTGGGACTATCAGATTGGGTCAGTGCATGGGTACAGGCATGGGGGGTGATGAAGAGG +CCAGTGTAGGTCACCAACACTGGGGGAAGCTGGGGACAGAGGTGGGTCTCACTGCAGAAACAACTGAAGCCATGGGGGAC +TGGGGGGCTGTGGTCACAAGGAAGGAGAAGCCCAGGCCCTAGCCAGAGAAAGCCACATAAGAAAGAGGAAGAGAGGTGAA +CAGAGGGGCTGGGAAAGGAGCGGCAGAGATGGGAGGTAGGGGGATGGGGTTAACCTTTCAGGTGAGGGGTGTGAGGAGGG +GACCTCCAGGAGTTGTTCAGGAACTGGCCATTGCCGGGACAGAGGAGTAAGCAGGTAGGCTCAGAAAGGAGACACCGCTG +GGGGGTGGGGGATGCTTGATGTGGAAATGACTTGGGCCCCCTTGTGTCTGGGAGCCTCCAGGAGGGAGAAGGGTGCTGAG +AACAGAGAAAGCCCAGAGGTGGTAGGGGGTGCCTATCGCCACGGCCCAGCTTGCGTTTAGATGCCATGACCCTGGGCAAA +GGAATAGGAATCATTTCCCCAAGGCAGGTTTATTGAGGACCTACTATGTGCCAGGCTGTGTACTAGGCACTGGAGGTGCA +GCCCTGGATAAACAGCCCCCATGTACAGGACTGTGGGAGATCACAAGCACTAATGAGAGACGCTGAGGACAGTGCCTGGC +ACCAGATACAGCACAGTCAGCATGAGCTGGTTTTATTACCATCGCCCAGGACACAAGCGTGTCTTTAACGAAGGGCCCTC +AGGCAGCCGTCCAGCCTGGACTGGAGTCCTAAGAACAGAAACACCCTCCAGAAGCGGTCAGCTGTACTCCCTGTCAGAGC +CCCACCGCCACCACAGGTGGCGGCTTTCCCGAGGCCAGCCCAGAGGACTGCCCAGGCGCTGCTGCTCCAGGAGGTGCGAA +CCTTGGGAATGGGGGAGGGGAGTGGGCAGGTCCCTCCAAGTTTGGGGTGCCGTGGGCTACAGAAGCAGATACTGGTGGGG +CTGGGACTCTTGGTTGCTCAGATATCTTGGTGGCTGTCCAGAGGGTCCCACGAGCCCTGCCCCCACCTGCTGTGGCAGTT +GCAGGGATGCTTGAAGGCAGTCGTCCCTCTAATAGTAGAGTTCCTGGTCCCACCAGCCTGGGAGAGAGAGGGAGAAAGGA +GACTCAGTGCTGGGGGTGCCACTTAGGCAGGGCCAGGGGCTCAGAGGTCAGTGCTGGTTGCTATGGAGGGTCAAGGGCTT +AGAGGCCAAGTGTTGAGGATCAAAGGTGGGTGCAGCTGCTCAAACGTTTCAGTCCCCTGCCCCCAGGCTCCCAGTCTCCA +CACTCACTCCCTGGGCAACAGCGAACTCCAAGCTTCCGGATCTCATCATAGACGAAGATGAGGATGCCGTAGGGCAGGGG +GACCAGCCACCACTGGAACCTGAAGGCACATGGCAAGGTGAAGGCCATCCCAGGCCTGTGCCCTACAGCCCCCTCCCTGT +CCTTGCCCCTCACAGCCTCTCACCGAATGGGCATGAAGTTGAAGATGTTGGGCATGCCGGGGCAGTAGCACAGGAAGCAG +CCGATGCAGACCTGGAACACGATGGCGATCACCAGGATCTTATTCCTGGGGGTGGGCAGAATGGGACAGGCCATTAGGAA +TTGGGGACGTGATGGAAATCAGGATACTGTGGGTCAAAGTAGGTCAGATGTCAGCAGCAGCAGGGAGGTGTTCTACACAC +TGAACACTGGAGTGGCCCGGGCCTCTCAGCACCACCCCTTTAGTGAAATGTGGAGGGGGCCGTGGGGGGAGTTATTGGCC +AGTTAGAAAGGTTTTTTTGGCATGTCACCATCTGGCACTGGCACCCGCTGGCATTTGCCGGCTGTTCTAAACCACAGTCA +GGATCTGATGGGAGTTGGGACCAGGGGTTGGAGGGCAGGAAGAGGGCCAGCCAGGGACACCTGAAGAAGCCTTGCTGGAA +GGCAGAGAGACGGCGCGTCTTGCGGATGAGGACATCGGCGATCTGGCACACCTCAATGCTGATGAAGAACACGGTGTAGC +AGGTGTACTGCTGGTACAGGCGCTGCCCGAATGTCTGCAGGCCAGGGGCAAACGGAAACAGCCTGAGTCCAGCCTGAGTC +CCGGCGGAGAGCCTCTGCAGCCCACCGGGCATAGGGTCCCAGGGCCGTGAACCCCTAAATGCTCTCTCTGCCTTGCATCA +GAGTGTGGGGGTGGGGGGAAGGAGAGAGGCAGGGTCTGCCAGGTGTGCAGGACCCCAGAACGTGGGGCCCAGAAACCTCA +GCCTAGCAGAGCTTGGATGACAGTGGCCGGGAAAACTGTAGGCTGGGCCGACTGCTTTCATAGTGAAAGGGGGGAGGCAC +CCAAAAGAAAGGGCCTGGGAAAGGGGAATTGAAATCCTCAGATGGGATTTGCAAAGTCCAGTGACTCCAGGGGCCAGGCA +GATGGCAAAAATAGGTGAGGCAGTCACCGCGGGCCTTTGGGAAAAGAGGGCAGATGCCCCATTTAAAGGGGGCAGCCACT +ATGCAGCCCATTTCTTCAACACAGCTACTTTTTTTTTTGAGATGGAATCTCGCTCTGTCACCCAGGCTGGAGTGCAGTGG +TGCAATCTCAGCTCACTGCAACTTCCGAATCCCAGGTTCAGGAGATTCTCCTGCCTTAGCCTCCCAAGTAGCTGGAATTA +CAAGCATGTGCCACCATGCCTGGCAACACAACTCCTAATACCAGCTAACTGTGGGGATCACTGACTGCTCCAGGTGCCGA +GTGTTGCACGTGCATTAGCTCATTTAATCCGCTGGGTAACCCTAGGAGGTAAATCAGTGGTGCTGGAACTTGAACATGCA +TGAGAATCCCAGCAAGGGCTCCCAGAGGGCTGGGATAGAGCCTGAGAATTTGCACTGCAAACATGTTCCCAGGAGATGGT +GATGCTGCTGGTCTGGGAGCAAACTGAAAACCACTGAGGTAGACTTTGATATTCTCTCCACTTTGCAGGTGAGAAAACAG +GCACAGAGAGGCAAAGCAACCTGCCCAGGGTTACACAGCTTGTGAGAAGCAGTACCAGACCATAAGTCCAGGCAGTCTGG +CTTCAGTGTCTACCTTAACCATGATAGTAAATGTTTAAGGGAAGTATCCTCGTCTGTATGTAACTCTACAGGTGAACTAA +ACACATCTGCTGGCCAGATCTAGCCTCACTGTGCTCCAAACCTTCATTAAAATAGACCACATGTGCAAATACCCTGTGGG +AGTGATGCTGTAGGGAAAATAGCAGAATAGGGGCCTGGTTCCACATCTGAGATGATGCATTAGTGTCAGGGCAGAGACAC +CCATCTGCAGTTGCGATCATCAGAAGCAGGAGTTTGGAGCTGGAGATGGTGGGAAAGGCCCTTGCCCCCCCGAACTGGCA +GGGAGTCTGGGGGTCCGGGATGCTCTGGCTGAACTCAGTCACACGTGGAGGAACAAGTGGGCCGCACACAAGGCAAGTTG +CCCTGGGAAGGAGGGAGCCCAGGGATGGGATGGGGCGGGGCAGGGCTCACCCACTCCTGGCCGTAGCTGTCCTGCAGATC +TTGTAGGTGGTGGTCCTCCCACTGCGCCCGCAGCCCCACGCACAGCAGTGGGAACCAGCCCTCCTGGGCCATTGCCGTGA +AGTAGTCAGTGAAGCCAGCAAAGGACTGAATGGCACCTGGAGAGAGACAAGGGGACACAGGGAGACAGAGATGGACACAG +AGACAGGGACACAGGAAGAGAGGGACATGGAGAGACAGGGACACAGATACACAAAGGTCAAGGACACACAGACAGGGACA +CGGGAAGAGAGACAGGGACACAGATACACAAGGTCAAGGACACACAGAGCCAGGGACACAGAAACAGTGACAGAGACACA +GAGGCAGAGAGAGAAGCAGGGACAGAGAGACAGAGGCAGGGACAGAGAGAAAAAGACACATCCAGGACCCAAAAAGACAG +AAATAGGCAACAACTCAGAGACAGGGTCATGGAGAGCAAGGTCAGAGAGAGAGCAGGGACACAGCAGAACCAGAGAGACT +GTCACAAACAGAGAGGGACACGGAGGACAACGGAGACCCAGGACCAAATGCAGACACTGAGGTCCAGAACCAGCCGGAGC +CCAAGGCAGGGCCTGGGGCAGGCGAGCAGCCCAGCGCAGAGCCCCCCACCTCCAGGCTCCCCGGCCCACGGGCACCCACC +AATCTGGAAGTAGGAGTAGGCAGCCAGGGGCTCGTTGACCAATCTGTCACGCTTTGGGTTGCGTGGACGCAGGTGCATGA +TGTCACTCTCGGCCTTTTCATATGCCAGGGACACAGATGGGAACTGGCCAGGAGTGGAAGGAACTGGGACTGAGGGTTTG +GCTGGGCCCTTGTCCCCTCCACTTCGGGTCCCCCTTCCCCCACTTGTGAGCAGGAACAGGACTGAGGTTAGCAGGCAGGA +CCTGCAGGGACGGCACAGCCACACCAGCCTGGACAGCCTGGGCCCCACTGGCCCCTGGGTGTTGGCATCCTGGTGCCCAT +CTTCGTAAGAACATCTGCTTTTATCTTCTCCCTCTGTCTCCCTCCCTCTCTTTCTCTGTTCCTCACTCTGTTTCTCTGTC +CCTGTCTCCAGTGTAATTCTCTGTGACTGTATCTGCTCTTGTGTATTTCTGTGTCACTCCCTATCTCTGTGTATCTTCAG +TCTCTCAGTGTCGCCGTGCCCCTTCATCTGCTTGTGCCTAGAGTGGCAGCAGAGCATACTGACCACGACCACAGGCTGTG +GGACCTGTGACTGCTCAAGCTCAAGTCACAGCCGGGCCACTTATTTGCCCAGTGACCTGGGACAATCCACTCTACCTGTC +TATGCCTTGATGAGCTCATCAGTAAAGTACGGGTGATAGTAACAAAACTCATCTCATAGTGTGGTTATGAGTACAAAATT +AAGGAAATGATGGTGTGTCTGGCCCAGAGAAAGCACTGTGTAAATGTTACCGAATAAATACCTGTCAACACAGCTGCGCC +TCTGGCTGCATTCGTGACTGTATCTGTGGTTCTTTTCTGTCTGTCTGCCATGGGGTCTATCTGAGGGGGTTTATTTGTCC +GTGTCTGTTTAGGGGCACGTGTCTGTGATTCAAGTTTGGCCAGATACCTGTTCTGTTTCCATCTCTGTCTGAGTATCTGT +ATCTGAGTCATGTGTGTGCATGGGTCGGCCCGGGCCTACCTGAGTGTCCGCCTGTGTGTGCATGGCTGTATGTCCATTTA +GGTCTGTTCACCTGCCTGCCACCCATGTGTCCTGCACTGGCTGTCATTGCACACACAGGTCTTGTCTGTCACTCTGTGCC +CAAGAGTGTCTGTGGTGGTCTCTGTCCCTCCTGTCCATCTGCAAGCAAGTGTCTCTGGGCACCCTGTGGATGGGTACCCT +GGGCTGTGGACTTACAATGTCAGTGCAGAGTTCGATGAAGAGGATGGTGATGCACCCGAGGGGCAGGGGCACGCTGACGG +TGATGTAGATGAGGTAGGGTGTCAGCTCTGGGATGTTCTTGGTCAATGTGTAGGCAATAGACTTCTTCAGGTTGTCGAAG +ATCAGTCGACCTGTGGGGTAGGGTGGGCACCTCAGCCTCCTCACAGCCCTCTCCCTCCTGTGCCCACACTGCCTGCCCTC +CCCCTGGCGTGGCTCGGACCCTGCTCCACGCCTGTCACAATGGAGGCAAAGTTGTCATCCAGCAGGATCATGTCAGCTGC +ATTTTTGGCAGCATCTGAGCCAGCGATGCCCATGGCTACTCCGATGTCTGCCTTCTTCAGAGCTGGGGAGTCATTCACAC +CATCCCCCGTGACGGCCACAATCGCACCCTGCAGGCAGTGGGTGCAGGTGGTGGGTGGGTGGTCAGTGAGAGGCCGGTCC +AAGACCAGCCCCGCCTGTCTGCCCGCCTGCCCACCCTCATCAGCAGGGCTCACCAGCCGCTGGCAGCTCTCCACGATCAC +CAGCTTCTGCTGGGGGCTGGTGCGCGCAAACACCATCTCGGGGTGGGTGCGCAGGGCCTCGACCAGTTCCGATGGGTCCA +TGTCCTTCAGCTGCATGCCATTGATCACACAGGCACGGGCATCCCTGGGGAGGAGATGGGAGGACCTCGCTGGGACCTCG +GTCTGTGCCAGATGTGGGGAGAACCCCGGGGAGGTCTGGGGGGGCTTACTTGCGATTAACCTGGTCTACGGGCACACGGA +GGCGGGCAGCGATGTCCTCCACTGTCTCGCTGCCTTCCGAGATGATGCCCACACTGGCTGCAATGGCCTTGGCGGTGATG +GGGTGGTCACCCGTTACCATGATCACCTGTAGGGGGAACCAGTGGATCACTGACCCCTTCAGATCAGCCCAATCTCCCTG +TCCTCCCTGGGAGACATCTGCTGATACACGTGTTCATTTACTTGACCAAGCGTGACCACCTCCTACGTGCCTGGGATATA +GCAGAGACAAAAGAGACAAAAATCCCTGTCCTTGAGGAGCTCTAGTGAGGGTGACAGAAAATAAAGAAACGAATTATATA +GTGTGTTAGAAAGTGGAAAAAAACATATTAAGCTTGGTGAAGGAAACGGTGACTCGATTTTAAGTGTACCCAGGGAAGGG +CTTATTGAGAAGGTGATATCTGAGCAGAGACCTGTAAGAGGTGAGAGAGTCAGCCACGTGGAAAGCTGGGAGAAGAGTGC +TCCTGGCAGGGAGAACAGCAGGTGCAAAGGCCCTGAGGTGAGGACAGGCCCGAACTGTCGGAGGAACAGCAGGGAGGACA +CCATAACTGCAGAGGAGTGAACAAGGAAAGAGGGCTAACGAACAAGCTTAGAAAGGTAACAAGGCCCGATGTAACAGGGC +CGTAGAGACACGGGAAGGGCTTGGGCATTGCCTGCCTGGCCTTGGCTTTCCCACCCTTGTTTTGTGTTTGGGGAACCACT +CCTCTCCCCTACACCATCAGTTGCAGTGAGGTCCCTGGCTAAGATTGGGTAAGTAAGCCCAGACCTGGCCAATCAAGGTC +TTCCATCCCCCAGCCCCAGTGACTGGCTCAGGGATAAATGAGTGACCCAATCCAGTGCTACTGCAAAAGAGGCACTCTCT +TTTTGTGGGGTGGCTAAGCAGGTGGGAGTTAAGTTTGAGCTGCTGGAGCAGGGGTGTGTGTGTCCCCAAGTGAAGGGAGT +GCCTGCAAATAAAGCCAGCAAAGAGAACAGCAGAGCTGAGACATGGAGAAAGGAAAATTCCTAATGACACTGAGCTCCTG +GATCTGGCTAGACCTGAAGCTTAATTCCCCAGAATTTTTCAGTATCATAAACCAATGCATTTTGGTTCAAGCCAGTTTCA +GGTGGGTTTGTCATGGGGTTCTTCAACCCAAATCCTGCTGCCATCCTCTGTCCTCACTCCACTTGTTCCTCCCCACAGTG +GCATACCCGGATGCCTGCGGTGCGACACTTGAGCACAGCATCAGGGACGGTGGCCCGGGGTGGGTCAATCATGGATACAA +GTCCCGCAAAGCAGAGGCCGCTAGATGGAAAGTTCATGGCCTCTACGTCGAAGGCATAGCCAGGCGGGTAGTCCTTCTCA +TTCAGGTAGAGCTGGCAGAAGCCTGACCGGAAACGGGGAAGTCAGGGAAGAGCCCTGGGCACACCCTTTCTTAGCAGGGC +CAGGAAATGGGTAAAATAACCAGGCCCCTTGCACCAAACACCTATGGATGCCTGACCTTGTGCTGACCCCTTCACTCACA +TTATCTGAATCTACCCTCACAACCACCCTGTGAGGCCCATTCTCATCTTACAGATGAGGAAACTGAGGCTCAGAGAGGGG +ACATTTCTTGCCAGAGGTCACACAGCAAGTAAAAGGCCCAGAATTGGACCACAGATCTGCTTTCTAGGGTAGAGGCAGCG +AAGTTTAAGGCGTCAGGACAAAAATTGGGAGAGGTTAGAGGTCAGGATACGGATAAAAGTCCAGGTGAGGACTGGGGTCA +AGGTAGAAAGTGAGGACAGACAGGGGTCAGGACTGGTACAGGGAAAGTCAAGGGTGAGGCTGTGGACTGCGACAAATCAG +CCAGCAGCCAGGGATGAGGACGGTCAGGGCTGGGCCGGGAGTGGTGGGCAGGGTCTGTGCTAGCTCCTCCTCGCACCTGG +AGTCTCCTCCCCTGCCCAGGGGTCTCACCGAGCACGCGTTCGCCCAGGCCTCCCAGGCTGAGGTAGGCGGTCTGGAAGGC +CTCGCGCCACTGCTCGTCCAGCGGCAGCTCCTGGCCCTTGATAAGGATGGAGCTGCAGCGCTCCAGCACGCGCTCGGGGG +CGCCCTTCATCACCAGCAAGTGTCGCGGGTCCCGCGGGTCCTCCAGCGTATGGATGGACAGCTGTGGGCGGGGGGGAGAG +GCGAGGCTGTGGACGGGGGAACGGGGCGGGGCTGTGGACGAGGGAACGGGGCGGGGCTGAGGAGAGGGGCGGGGCCGAGA +GCTCGGTGCGGGCTCTGAGAGCTGCGGGGAAGGGTGAAGGTGGAAGATGGAGGCCTTGTGTGGAGGGGTCCTTGGTAGAA +GGTAAGCGTTAAGGCGGGGCTAGGTGCAGATTTGGAGTCCTGGACGCAGGGAATGAACAGAATTAGGGTGCGGAGTTGGG +CTGGGGGCGGATTTGGAGAGCGAGGTGCTCCCCATGGACAGTCCCGCCGAGGAGAAGCTGTGGGCGGGGCTGGGTGGTGG +GCGGGGCCTTGCCTCTGGTGGACGGGGCCATAGGCGGAGCGGGAGATGGGGTGGGGTTTGGCTGCGGAGAGAAGGGGCAA +GGAGCGAAGCCCCTCGTGGCCCGCTGATGTGGGTGTGGCCTGGGGCGGGGCCCGAGGTGGGCGGGCCCAGGCCGTGGGCG +GGGCCGGCTGCGCACCTGGAACTTGTTGGTGGAGTTGAAGGGTATCTCGCAGACTTTTGGGAAGCGGTCCCGGTAGCCCA +TGGCGTTGCCCAGCGTCAGCTCCGAGAACTTGAGCAGCGCCGTCTCCGATGCGTCTCCAATCACGATGCGCTGGGAGCGG +GGACCGGTGTCAGGGGCGAAGCCGGCTACACCAGCCTCCCGGGATTCCCTGGAGGCCCCCTGGCTCTCACCTTGGGCACA +GGCACTGCATCCTGGCCGGACTTGAAGGCGGCGCGGTTGCACAGGGTGAGCACCCGGCACAGCGCCCGCCACGTCTCCGA +GGACTGGTCAAACGTCTGCCCTGCAGACCAGGCGTCCAGGCTGGGTCCCGCACGGCGGCTCTCCCGGACCAGAACCGAGC +CCCCTCCTCCTAGGCTCATATCGCGGGCCCCCTCCCCAGACCTGGGTGGAATTGGGTTCCACCCAACCCTGAGGGACCCA +GCCCCCGGATGACCCTTCCCTCTAGACCCGGTAGCGAGTCTCCTTTGAGACCTGGAGCCGAGCCGCCCCGCCTTCGTACA +AAGCCCTCCCTACCTCCCTATCCCTCTTCAGGTCTCCACCATCCACCAGATCCTGCCCTGGCGCCTGTGCCCTCCCTCCC +CCACACCTGACTGGTCTTCCGTGGTGTCAGCTGTGTGGATGTGGTTGTCAAACCACAGATGGGACACAGTCATGCGGTTC +TGAGTGAGAGTCCCTGTCTTGTCCGAGCAGATCACCGAAGTGGAGCCCAATGTCTCCACCGCCTCCAGGTTCTTGACCAC +GCAGTTCTTACTGGCCAGGCGCTTGGCTGTCAGGGACAGGCAGACCTGGGGAAGGGGTGAGCACCGCAGGCTGGGGACCC +ACCCTGGCTTCCAGTCCTCTTCCCCGCGTCAAAGAACGGGGAAGGCTTTACCCCAGCCGCGGGGCTGCGTGTGCAACGTG +CTTCTGCAAACACCAGGTGTTTTCTTGGCCCCAGCTTTTGTTCAGCCAGTGCTTTGTTTGGGACTCCTTCCCCAGTCTGC +CCAGATACAGTAGCGAGGCCCCTCTCTGAGCTGTCCCAGCCGAGGTTCTGAAAATTCTCTTCACACAATCAAAATGAGAA +GAGGAACCTTCCCCGCACCTCCCTGGGGACTGGTCTGGAGCTGGGTGCTGACTGTGGAAGGTCTGGTGACATGCTGGCTG +CCTGCACAGGACATCAGCCTCTTCCAGTTAAGGACCCGGCCTCGTCATCTCTGCGCCCTCAATGTGTGGCACAGGACTTG +CCCCAGGGTTGCCTCGGGAAGGACTTGCTGAATGAGTGGATGATGGGAAGGCAGGAGAATGGATGGGAGCTAAGTGGACA +GATAGACAGGCAGGGAGGTGATGGGGGAAATGTGGAGGAAAGAACAGATGGTTGAGCAGGCCCCTCAGCTCCCTGCATCC +CCGCCTGCCCCCACTCACTGTGACAGTGGCCAGCAGCCCCTCAGGCACATAGGCCACCACGATGGCCATGAAGAAGACCA +TGGCCCGCAGGAAGGTGTAGCCAATGCACATGGCCACAATAAAAAATGTGGCACCGAAGAGAATGGCCAGGCCCGCGATG +ATGTCCACAAAATGCTCGATCTCGATAGCGATGGGTGTCTTCTCGTTTTCCACCCCCGACGCCAGCGATGCGATGCGCCC +AATGATGGTGCGGTCGCCCGTGTTCACCACCAGGCCCTGCACGGTGCCTGCAGGGGGGCCAAGGCGCGACTCAGGGATAG +GGGGCGGCAGTGGGGTGTGCACTGCCGTGTGAGCTGAAGGACAGCCAGTCACTGCCAGTGGAGGATGTGACCTGGGAGAG +GGTAGTGACAGTGGGAGACAGAGAAGCAGTGTGCCCTGGGGAGGTGGCAGTCACGGGGAGGTGGCAGTCATGCAGGAGAG +AGACAGGGAGGCTGAAGCCCCCTGTCCTAGAAGATAGCAGGAGAGAGGCCAGTGGAGGCAGCAGTTACTGGGCAGGCAGG +CGGGGGCTTCACAGACCCTCAAGGCACATGGTGGAGAAGAAGGCGATGTTGCGGGTCTCCAGAGGGCTCTCGTGCGTGCA +CTCGGGTGAGCGGGTCTGTGGCTCAGACTCCCCTGTCAGCGAGGAGTTGTCCACCTTGCAGCCCTGGGCCGCCAGGATGC +GGATGTCGGCGGGCACTCTGTCCCCACCTTTCATCTCCACCAGGTCGCCCACCACCAGTTGGTCAGCGTTGATCTGGAAT +TTGTCTCCATCGCGGATGACAGTGGCTTGCTGCGGGGCAGGGGCACCAAAGTTGAGGTGGACGGGGGTGGGGGTGGGAGC +TGCTGCATGTGGGGAGGTAAAGGATGAGGAGAGCTGGGACCCATGGGGAGAGATGGAGGCCACAGATGAGGGACAGGGGC +CTGATGGAAGGAGGCTACAGGAGCAGTTTGGAGTCTCTGGGATCTGGAGTGGCTGGGTGCTGGGGAACCCACCTGTGGCA +CAAGGTTCTTAAAGCTGGCGATGATGTTGGTGCTCTTGAATTCCTGGTAGTAGCCAAAGCAGCCGGTGACGACAACCACA +GCAATGAGAGCGATTGCCAGGTACAGCTGGGGACAGGGAAGGGGTGGGGTTATTCAGAGGGGCCGGAAGCTGCCTGCCTG +AGGCCACCGACCTGCTCCCTGGTGCCCTGGTTTTCTTGGTTTTCCCCATGCTTTTCCCCACCTACTAGGAAGCTCCATGC +ACCATTCCCTGTAGCCTTTAGCCCCCTCCCATGTCCCTCATGTCCCACACTCGCCTGCTCTGTCTTCTCAGTGTCTTGTG +TTTCTGTGTATATTTCATATCCTCTGGCCCTGAATCCTCAATCTTCCATGTCCCCATATCTGTGACTTGTGCCCCAAATC +ACTGTGTGCCTTGGTCTGACGTGTTCCCTTCTCCTGTGTCCCCAAGCCTTGCATTCCCCATGTCCACTGTCCCATGTCTC +CTCCTGTCCCCATGGCCTGTATTCCCCGTGTCTCCAGTTTCACATCCTGGGTTTTCCATAATCTGCTGCCACCCTCTGTG +TGTCCCCTGGGCCCCCTTGTCCCTGCATCCTCACATCCCCGTCCCTGCGTGTGCTGCAGACCTTGTACGTCATGGTCTGT +GTCCTCTGTGGCCACGGCCCCCATGTTCTAGCTCCCCAGTCACTGTGTCCCGTGTCTCCCATGTCTGTGTATCTCCGAGT +TCCCATCCTGTGTCCCATGGCCATGACTTTTCGAGCCTCTGTCCCATGTCACATGTCCCTGAGTCCCCATGTCCCTTGCC +CTCATCTCTGCCATATGTCCAATGTCCCTTGCCCTGCACCTCCATTTCCTCTCTGTCTCTGTGCTTCTATGTCTCATGTT +TTGTGTCTCTCTATGTGCTGTGTCCTGAGGCTTGGGTCCCTGCCCTGGTGTCCTATGTCTGAGTCCCAGGGTCTCCTATT +CCCCGTGCCTCAGTCTGTCCTGTGTCTGCACTGCATGTCCTCTGCTTCCAAGTCCCATCTCTTTTTTTTTTTTTTTTTTT +TTTTTTTAAGATGCAGTCTCGCTCTGTCGCCCAGGCTGGAGTGCAGTGGCATGGTCTTGGCTCACTGCAACATCCACCTC +CTGGGTTCAAATGATTCTCCTGCCTCAGCCTCCCAAGTAACTGGGATTACAGGTGCCCGCCACCATGTCTGGCTAATTTT +TGTAATTTTAGTAGAGATGGGGTTTTGCCATGTTGGCCAGGCTGGTCTCGAACTCCTGACCTCAGGTGATCTGCCCGCCT +CGGCCTCCCAAAGTGCTGGGATTACAGGCATGGGATTACACGCCCGGCCTCCAAATCCCATCTCTTACGTATCACATTGC +CATACTCTTTATGCCCCCACCTATGTCCCCATACCCAAGATTCACCCCAGTGTCCCTGTGTCCTGTGTCCTTGGGTCTGC +CCCATCCATATCCCAATGTCCTTCACCCTCCATGACCCCTGTCTTGAGACTGCCTTTCGTGTTCGTCTATCCCCATCCTT +CTCTGCCCCTCTTGTCCCAAGTCCCAGGCTCAGTGTCTTCCATCCCCAGGTCCCCATGTCCTGAGCCTGTCCCCACAACA +TGGCTCACATTGTCGTCGGTGGTGAGGTCCCCCTCACTAGCCTGGATGGCAAAGGCGATGAGGCAGATGGCGGCGGCAAC +CCACATGAGGCACTGCAGGCCCCCGGCCAGCTGCCTCGCGAACTTGACGTACTCTGGGGTGCCCCGTGGTGGCCGCAGTG +CGTTGGGCCCATCCCGCAGCAGCAGCTCAGCAGCCAGGCTCGCAGAGAGGCCCTGGGACAGAGGGGCAGGGCGAGGCGGT +CCTGGGGGCTTTCCTCCACATGCACACCCCGTGGAAAGCCCCCTGCTTCAGTTTCCCTTCTCCAGCTTGGTCTGTCCTGC +TCCTTCTCATCTCTTTGTCTCTTGGTTCCTGCCCCCACCCCATCTCTCCCTCCCTCCTTCATCTCTCCCTTTGTCTCTCT +CTCCTCGCTCCCCTTTCTATGTGTCTCTCTCCTGCTGCTCTCTCCTTATTTGTCTCTGCCTTCTCTGTCTTGCTGTCTCT +TTTCAGCTACTCCTCTCTCCGTCTCTGTGTGTCACCCTTTCTCTCTAATGCTTTCTCCCTTCTCTCCCTCTCCCTCTCTC +TCCCTCTCTCCATCTCCCTCCCTCTCTCCTCCCCTCCCTCCATCCTTTCCTCTCTCCCTCCCTCCCTCTTTCCTTTTCTG +CCTCCCTCCCTCTCTCTATTTCTCCATATCTTCCTCTCTTCATCTCTCCCTCTCTCTCCCTCTCTCCATCTCCCTCCTTC +CATCTCCCTCTCCCTCCCTCTCTCCTCCTCCCCTTTCTGTACGCTCCCAGTCTCCAGCTCACCTTGGTGGCACTGGTCTG +GTATTTCTGTTCCAGCTCCGCCACTGACAGCTGGTGGTCGTTCTGTGTGGTGGGGTGGGGCAGGGTGCTTGCTCTGGGCT +CTCCTGGCCCCCTCCCCGCCCACTGCCTGGTTCCCAGCCTACCCTCCAGCTCCCGCCCCTCCCCAGTCAGAGCTCACAAT +CTCCATCTCCTTCTTCATGTTCTCCAGCTTCTCCTTCCTCTTGCCACCCCCGCCACCCGCCTTCTTCTTCTTGCTCATCT +TGGCAGCCATGTCCCCGCCAGGGCCAGGACCCAGCTCCACCGAGTAGAGCTCATAGTTCTCCTGGGAATGGACAGGATGG +AGGGAGGGAGAACTCAGATTCCACTGCAACCCCTGTCCCCACTGCACCCCGGACCCCTGGGCCCCACTCACGGCCTTCCC +CATGGTGCCCGGTGCCTGTGCTCCCACCCAACAGAGCCTGGGCCCGGACCAGTCCTGATATACCCAGGGAGGGCGGTGGG +GTGGAGCAGAACCGCCCGACCACAGCTGATTACAGGCTCCTTGGGGTGACCTCTAGGCCTGGGCACCTCAGGCATTCCCT +CCTGGGCTGGCCCCCCCCTCCCCTGCCCAGATCCCCCCATGTCAACCTGCCCTGAGATCAGGGAGCCCCTGGGAACCGCT +GCAGCAACAAGAAGAGCAAACATAAGGTGCTCATTATGCGCTGGGCATTGTGCTGACGGCTTCCATGTGCTAACTTATTT +AACCTTCCAATAACCCTTTGAGGGGACAAGACCCTTTACAGATGGGGAAACTGAGGCATGGAGGGGTTGAGTCATTCTCC +TATGGCTCCATCACAGGCTAGGACATAATGGAGTGAGAACCAGATCAGCTGGCTGCAGAGTGTCCTCTTTCAGTCTAAAT +AGCACGTGCCTGATCAACTGGAAGAGGAGGGTGCTCCCACACTCTTTTTTTTTTTCTTTTTTTTTTTGAGATGGAGTGTC +ACTCTGACACCCAGGTTGGAGTGCAGTGGTGCAATCTCAGCTCGCTGCAACCTCCGCCTCCCGGGTTCAAGTGATTCTCC +TGCCTCAGCCTCTTGAGTAGCTGGGACTACAGGCACATGCCACCACACCCAGCTAATTTTTGTACTTTTAGTAGAGACGG +GGTTTCACCAGGTTGGCCAGGCTGGTCTCGAACTCCTGACCTCAGGTGATCCACCCGCCTTGGCCTCCCAAAGTGCTGGG +ATTACAGGCGTGAGCCACCATGCCCTGCCATGCTTACAACACTCTCTATGGTGGCTGAATGTTTACTGAACTCTGACTAT +TCACTATGGGCTGGGTGTGCTCTGTGTGTCACTCAAATGGTTTCCTTAGCATCTAACAGCAGCCCTGTAAAGCAATATTC +TCTTCTTAGCCCCCATCAGGGACTTTGCAGAAGATTTCAACGTCTAAGAATTAGGGACAGGGTTCCAGCCCGGGCTTGAC +CCCAGGGCTTGACCCCAGAGCCTAGCTCTTCTATGGCACCACCAATGGAAAAACCCAAACTTCACTGCAGCAGCACCACA +CACATGCCAGGCTCTGCACGTGTGACCTCACGGGTCTTTGCAAAAAACCCTGTGCAGGCAGAACTATCATTCCTGTTGTG +CAGAGGGGGAAACTGAGGCTCACAGGGACTCACATATCCAGCTACAGACTTTACAGCTCTCAACCATTCCCCGTCCCTAC +CCTAGATGGGGAAACCAAGGTGCAACATGGGGAAAGGATGTGTCCAAGGGTCTTGGCTTGATCTTGGTGTTGGGTGACAG +ATGCCAGGAGGCCTGGGGACTCCCTCCTCATCCCTCAGGTAGGGACCTCCATGGGCAGCAGCAGCGAGGGGCTGGGCCAG +TGGAATGGTGGGGACCTTGGTATGTTATGGGGTGAGCGTGTCTCCAGGAGCAGGTGAGCGTGCGCCTGATAAGATAACTG +CCCTGGCAATGTCCTTCTCTACTGGGGCAGGTCACCTGGGAAACTCAAAGCTGTTGGGCAGCCAGAGCTCCTCCCCGCAC +CCACACGGCTGGTCATCCCCAGCCTGCTAGAAGAAACCTCCAAAGAAGAGGTGGCGTGGGGCCACATGCTGTCCTTGTAG +TGAATCTACTCGCTCACTGGGCTCCCATCAGCTCTGAGCCATGTATGTCAAACAGCTTGTGTCACCCCCTGCTTACAACC +CTCCAAAGCCTTCCCTTCCCACTCAGAATAAACTCTGCACTCCTTCCTGCAACATCTGGCCCTGGTCACCTGTCCTGCCT +CTGTTGCTGCTAGGTTTCCAGGGGCACTGACCTCTCTGCTGCACTTTGCACAGACCAAGGTCGTTCCTGCTTCAGGACCT +TGGTACTTACAGTTCCTTCTGCCTGGGCCCCCTTCCTTAGATGTCTGCACTGGCTTTCTCACCTCTTTCAGGTCTCAGCT +CAAATGTCACCTCTTCAGAGAGTCCATTCTGTAATACTGCACACTGTGTTCTGTTCCTTTACCCCACTGTATTTACACCT +CCTACTCCAACCTGACACTGTGTTTTCTTTTTATTTGTTTGGTGTTCATTAGAAGGAGGACTGGGCCGGACACTGTGACT +CACATCTGTAATCCCAGCACTCTGGAAGGCTGAGGTGGACGGATCATGAGGTCAGGAGTTCGAGACCAGCCTGGTCAACA +TGGTGAAACCCTGTCTCTACTAAAAATACAAAAATTAGCTGGATGTGGTGGTGTGTGCCTGTAATCCCAGCTACCAGGGA +GGCCGAGGCAGGAGAATCACTTGAACCTGGGAGGTGAAAGTTGCAGTGAGCCGAGATCATGCCACTGCACTCCAGCCTGG +GTGACAGAGTGAGACTCTGTCTCAAAAAAAAAAAAAAAAAAAAATAGAAAAATAGATAGATAAGAGATGTTAGACAGATA +GATGGATACATAAATGATAGATGATTGATAGATTTTTTTTTTTTTTCTTTTTGAGACAGAGTCTTTCTCTGTCGCCCAGG +CTGTAGTGCAGTGTCATGATCTCGGCTCACTGCAACCTCTGTCTCCTGGATTCAAGCAATTCTTGTGCCTCAGCCTCCCA +AGCAGCTGGGATTACAGGCACCCGCCACCATGCCCAGCTAATTTCTGTATTTTTAGTAGAGACGGGGTTTCACCATGTTG +GCCAGGATGGTCTAGAACTCCTGGTCTCAAGTGATCCACCCGCCTTGGCCTCCCAAAGTGCTGGGATTACAGGTGTGAGC +CACCATGCCTGGCCAGAATTTTTTAAATTTTTGGCTGAATCATTTGGAAGCAAGTTGGAGACAGGATCCCCTTTATCCAT +ACTTAGGCATGTGTCTTCTATGAATAAGGACATTTTCTTACATAAGCACAGTCTAATAATCAAATTCAGGAAATTAACAT +TAATAAAACACTAGTAAATAATCTAGAGCAAGCTTGTCCAACCCATGGCCTGCAGGCCATATGTGGCCCAGGACTGCTTT +GAATGCTGCCCAATACAAATTCGTAGACTTTCTTAAAACATTATGAGATTTGTTTGCGATTTTTTTTTTTTTTAGCTCAT +CAGCTATTGATAGTGTTAATGTATTTTATGTGTGACCCAGGACAATTCTTCTTCCAATGTGGCCCAGGGAAGCCAAAAGA +TTGGACACCCATGATTTGGAGGATTTATTTGAATTTCCCCAGTTTTCCCACTAATGTCCTTTTCCTAGTCCAGGATCCAG +TCCGGGACCACACCTCACATTTTGGCTTCCTCCATTGCGTAACAATTCCTCCATCTTTCCTTGTCTTTCATGACCTGGGC +GTTTTGGAAGAGTATGATTCAGTGATTTTGTAGACTTCGCTCAGTGTGGCTTTGCTGCTGGTTCTTCGTGATTAAGTTCG +GGTTGTGTGCTGGCAGGAACACAGAAGGGATGTGTGTCCCCCCCATCCCCTGCCCCAGAGCATCATCGTGTCAGGCCATG +CCCCATTTTGGAGTCTGTAACTTGTCTAGAGCAGGGTGGACATGGTGGGGAGGGGTCTCAAGAGCCTTAGGGAGTGATCA +GGAAACCATTCTATGCCCTCTTTAATTTTTACTCAAGACAGTGCCACAGCCAGGGCTGGCACCCAGCTGCCCTGGTAGTT +AAAGTAATTGAAAGAAATACAGCCACTGCCTGAGCCCCCAGCACTTCCCCTAAGACTCCAGACCTCCCACACCCAACCCC +TTAAAGTGGATGCCTGTGCCAGTCCCAAGGCCTTGTCCTTTTTTTTTTTTTTTTTTTTTGAGACGGAGTCTGGCTCTGTC +ACCTAGGCTGAAGTGCAGTGGCACGATCTTGGCTTACTGCAACCTCCACCTCCTAGGTTCAAGCGATTCTCCTGCCTCAG +CCTCCCAAGTAGCTGGGACTACAGGCACGTACCACCATGCCCAGCTAATTTTTGTATTTTTAGTAGAGATGGGGTTTTGC +CATGTTGGCCAGGCTGGTCTCCAACTTCTTACCTCAAGTAATCTGCCCACTTCAGCCTCCCAAAATGCTGGGATAACAGG +TGTGAGCCACCACGCCCAGCCACCCTTGTCCATTCTGACCATCCCCTGCTCAGCTAGGTTCATGGGTAAATACTTTTACC +TACTCCCCTAATTTTGCAGTCTTCAGATGGTATGCAAGTTGTGTGTTTTTGTGGGGAAGGTCTTTGCTTGGGTCATTTAT +TATTTTACCGTGTGTCTAAGCAATACTAACCCCTGTCTCTGGGAGGTGAACACTGAGGCCTTAGTCCACAGGCAGGATCT +GGATAAGTTGGGAAGTGTTCCAAGTCTTTGGCACCCTGGAATGCAGCCTGGGTGGACCGCACGATGCCATTTCAGGCGGA +TGGACAACCCCTAGCACAACTCAGGGGCAAAGAAACTTCTCACAAGAATGTTGCAAGTGTGACATGGTCCACAGTAGTGC +AAGTTTCTCTGAGCCTGGGGCACACAGAACGTTGGAAACTTTTTTGAGAACAATTAGTGGTCAGAATTGAAAAAAATTTT +CTTTTCTTGAGAAAGAGGGGCAGAGGACAGACTATTAAATGGACGGTGGTCCAGGAGCAGTGGCTCATACCTGTAATCCC +AGCACTAGGAGGCTGAGGCGGGCATATCACTGGAGGTCAGGAGTTCGAGACCAGCCTGGCCAGCATGGTGAAACCCTGCC +TCTACTAAAAATACAAAAATCTGGCCGGGCGCAGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCAAGGTGGGTG +GATCACGAGGTCAGGAGTTCAAGACCAGCCTGACCAACAGGGTAAAACCCCGTCTCTACTAAAAATACAAAAATCAGCTG +GGCATGGTGGCGCATGCCTGTAATCCCAGCTACTCAGGAGGCTGAGGCAGAAGAATCGCTTGAACCTGGGAGGTGGAGGT +TGCAGTGAGCCAAGATTGCGCCATTGCACTCCAGCCTGGGTGACAGAGTGAGACTGCATCTCAAAAAAAATAAAAAATAA +AAAATAAAAAAATTTGCCAAGTGCAGTGGCGGGTGCCTGTAGTCCCAGCAACTCGGGAGGCTGAGGCAGGAGAACCTCTT +GAATCTGGGAGGCGCAGGTTGCAGTGAGCCAAGATTGCACCACTGCACTCCAGCCTGGGCGACAGAACAAAACTCCGTCT +CAAAAAAAAAAAAAAATGTGGATGGTGAACATTACTTGGGTTTGAAGGGGAATTTGAACAATAATGTCCTAGAATTGTAG +CCATACACACTCTTAGGAACCCAGCGCCCTGTAAACCTTGATGGGATCAGACCCCTGGCTATTCCTCCTCCTTCCCATGT +TCTACTTTTCCCTTTCATGCTTTGGGATCCTGCCCGCCCAGGCCTTCTGCTTTTCCAAAGGATCATTTCAAAACCTCAGC +ACATGCAATTTCCTCTACCTGAAGTGTGCACCCTACCTCCTCCCCATGTTTAACCACTCCTCACCTTTCCTGTTAAGCAC +AGATGATCCTTCCTCCGGGAAGCCTTGGACTACCCACCAGGCTGAGTCAGGCACCTCCCTGGGCTTCCACAGACCCCTGG +GGTCCCCATCACAGCCCCCACCTCCCTGCCTGTGCTTCCCCCATCCCAGTCCTGACGACTCTGACCCTATAATTCCTCCA +TCACAGCCCTGACCACTCTGGGCTGTCATGCTTTGGTGACATTTCATCTCCCCCTCCAGATCCATGAGGGCAGGACCCAG +GATGTCTCAGTCACCACTGTGTGCCCTGCATTGCCTGGCCCAGGCTGGCCTTGGGGAGTGTTTGCCAAGTGACAGGAGTC +ATTGTGACTGTGACTTTGGGTACAGTGTTTGCATCTCCCTGATAAGATAATGGGCCCAGCAATGCCCTTCACCTCCTGAG +CAGGTCACCCCGCCCCCTCGACCCTGGGAGGATGAAAGCAAGGGGCTGGGCATACAGGGACCTTGCTTCTCCCGCCTCTC +CAGGCCAGGTGCAGGGGCAGGTGGTCAGAATTGAAAAAATTGTCTAGGAGTCAAGGAGTTGGCCCCCAGCCCAGCTAAGC +TGGCAGCCAGGCCCCTGCGTTCTGTCCCTCTCCCATCTTTGTGCAGATTGGAAAGGTCAGCCATCCTGGGCTGGGTGCAG +AGCCCTTCACTTCTTCAGGAAATTTCCAGGCTGTACTTGCTCTGGGATTTTAAAAAATCATATTCAGGGCCGGGCGCAGT +GGCTCACACCTGTAATCCCAGCACTTTGTGAGGCCGAGGTGGGCAGATCACCTGAGATCAGGAGTTTGAGACCAGCCTGG +CCAATATGGTGAAACCCCGTCTCTGCTAAAAATACAAAAATCAGCCAGGCATGGTAGCGGGCGCCTGTAATCCCAGATAC +TCGGGAGGCTGAGGCAGGAGAATCACTGGAACCTGGGAGGTGGAGGTGGCAGTGAGCCGAGATTGTGCCACTGCACTCCA +GCCTGGGCAACAGAATGAGACTCCAGCTCAAAAAAAAAATAAAAATAAAAAAAATAAGGGTACTGGTGGAGAGACTTCCT +CCATGGGATGGGGCAAGGGCAGAGCGCTGATGTCCAAATCCAAGCTCAAAGATTCCCAGGGGAGCAAGGAGGCCTTGTGG +TTCCTAGAACTGTGAAGGGTACAGGGCGATTGCAAGAGGGCTTCTGGCTCGTGGGTCTCCTGCTTCTGGGCTGTCTGCTG +TCACAAGAACCTGCAGGATCATTAACAAGCTCAGAACGATGGCCCTGATGCTGAAGCCCAAAGCTAGCCTGGAATTTCTC +TTTGTGCTGACCTCGTTTTCCAAACGGCTCACTATCCGTGCCTATGGAGATGAAGACAGGCCGATGGGGGTGTTTTTCTA +AGAAGGGGTGAAATTGCAGAAGGGCCACCTGGAGTGAAACTGAAGGCCATGATCAGGGTTGGGGGAGGGAGGATTCAGCA +GAGGAGAGGAGGAGGCAATGCAGGAAGACAGTGCGGGGATCCTGGGCACGCCCAGGTAGGAGGGCATGCTGGGAGACCTT +GGGAAGAAGTGGGGTTGGCAGACTGTGAACAAAGTGGGGACAAGTCCCAGGGCAGCAGGAAGGGTGGGATGGAGAAGGGG +TTGGGGCTGGTATCTAGAGAAGAGGGGAAGCTTCCGAGGCATCCATGGAAAGGGGTATAAGGTGGGTCAGGGGATGTTTC +ACCATGGGGGACAGAGGGTCACTCTCCACCCACCAAGCAGGATGGCACAATGGCAAGTATTCCTAGCAGGAAGAAACAAA +AGATGAAAAAGATGCAGAGAGGCAGGCAGTCATTCACAGGAACACTTAAAGGCATTTTGTAGATGGTCATCTCCATGACT +TTGACCTCTGGGCTCCGGAGGTCAGTCACACAGGCATGAACCCTGGTGGAGGGAGGGGCAGGGTCTGCAGAAGGACTGAG +AAGGATCAGCAGATGCGAAGGAATGGGGACCAACAGAATCAATCCCATCAACAGGAATGTGCTGGGAACACTCAACCCAC +AGTGAACTTGGCCTTTGTTGTGGGAAAAGAACCCTGCAATGCCCCCACTGACCTCTATGATACCCTTGCTAGCCCTCAAA +GTATCCTGTTGGCTGCTTTTTTTTTTTTTTTTTTTGAGATGGAGTCTCACTCTGTCACCCAGGCTGGAGTGCAGCGGTGT +GATCTCAGCTCACAACAACCTCCACCTCCTAGGTTGGGATCCTCCCACCTCAGCTTCTTGAGTAGCTGGGATTACAAGCA +TGCACCACCATGCCCAGCTAATTTTTGTATTTTTAATAGAGGCAGGGTTTCACCATGTTGGCTAGGCTGGTCTCAAACTC +CTGACCTCAAGTAAATCACCCACCTTGGTCTCCCAAAGTACTGGGATTACAGGCATGAGCCACCGTGCCCAACCCCTGTT +GTCTTCTTCTGTCTCCCTAAACCCATCCCTGTTCTCCAGGAAGCACTACCTCTTGGTTTGCCAGTGCTTCCAGTTAGGTC +TGGTCCTAGCAAGTTATTCTGAACTCCCCTTTCAGTCTAAAAAATGCTCAGTTTGAATTTTAAATCCAGTCACTCATTGC +TTTAATCCAGTCACTCAATGGGGATAAGTTCTGAGAAATCCAGTCACTCAATGGGGATAAGTTCTGAGAAATGCATTGTT +AGGTGATGTCATCATTGTTTGAACATCATAGCTTGTACTCACACAAACCTAAATGGAGAGCCTACTGCACACCTAGGCTA +TATGGGATAGCCTTTTGCTCCTAGGCTACAAACCTGCACAGCACCTTACTGTATTGAATACTGTAGGCAACTTATAACAC +AATGGTAAGTATTTGTGTATCTAAACATATCTGAACATAGAAAAGATACAGTAAAAGTACAGTATAAAAGATAAAAGTGG +TATACATCTGATATGATTTGCTGTGTCCCCACCCAAATCTCATCTTGAATTGTACCTCATCTGCCTGAATCAGTGCTGAA +CCACTGAGGCTTAAAACAGTGCCTGGCACTGTTACCCAATAAATGTTCATTGAATATATGAGAGTCCCATGGTATCTGGC +AACGTGGACACACAGGAGATGTGAACATGTGGGCACACATACGTGCATGCAACAGATGAATCAGAAAGTGGAAACCTCCT +CATAAACTTTACTGCTCTTAAAAATGCTGATGGATGAAGGGGTGGGTTACAGATTAATAGCATCTCAAGGCAGAAGAATT +TTTCTTAGTACAGAACAAAATGGAGTCTCCTGTGTCTACTTCTTTCTACACAGACACAGTAACAATCTGATCTCTCTTTC +TTTTCCCCATATTTCCCCCTTTTCTTTTCAACAAAACCGCCATCGTCATCATGGCTCTTTCTCGATGGTCGCTGTCTCTT +CGGAGCTGTTGGGTACCCCTGCAGACTAACAACAGACAGAACAGGCACACAAGGATTAATATGAAATTTATAATTATAGT +ACTTCCGATGGTCTTAACCCAAGTGACAGGGTTAAGATTTACGAGGCCATCAGCAACTCCTGCAATTGCCTCAGTTCCTG +GCACCAAATTTAAATGGGCTTTTGATGTTTTGGAAATTTGTTCTTTTAATTTGGAAATGTCTAAAGTGAGATTATCTTCT +CTTCCCTGTAGATGGCGTCTAACCATGTCCCAGTGATGCTCAGACTCATTATAAACTTGGGGTGTAATACAAAAATCTGA +CGTATTCCAGTCACACTGTAACTGGAAACGATGTTCTAAGCTCATGAGCCTGTCTCCCATCCAAATGACAGTTTGTCTAA +GATCATTAATTTGGTTTGCCAATTTTTGATCAATACTAGATTGTGAATTCCACAATCTTGTAGAACTTTTTTGCCAATCA +TTAACAAAGTTTACTGACTGAACAGAAGAGTGCAATGCAACTCCTGCCACAGCAGCCATAGCTGTGACTGCAATTAATCC +CATAATCACTGTAATTAAAGTAAAAATGAATCTTTTGGATCTATTTAAAACGCCTTTTAATACTTCAGTCAAAATATGAA +CGGATGGTGAGGCCTCCCACAGTCGGTCCATGGACACAAGGATCCACACGCCTTCCCTTGCTCTCACCAGCAGAATACGG +TGTTGCCAATTAAAAGTTGAATCAATGCAAGTAAACAATCTGCAGTTTTCACAGGTTATAGTTTGAGAGTCTGGTTTAAT +AACTATATTTCCTATAACTAGCATATAAAGGGACTTTACACAGCTTTGTAAAGGAACTGTTAGACTGGAATTTAGGTCGA +TAGTATAAAATGGCTTATGATCTCGTTTCTAAAGTTTGATTTCCAGACCAAATTCTAATGTAGTATGAGGCTACACTAAG +CCTCCACCATTCTGGATGTTCAGGACCAGAAACAGGACTTATTATTTTTGGTCATGGGGTAGAGATTCCTTTTTCTCCCC +ATTTCCAAGGGTAGAAAGACTGTAATTTTTTATGCTTATGTTCGTCTAAACTTTCTATGAAGTCGCTATCAACAGTTGGA +CTCACTTGTGCACTGGGACACGACTGAGTTTGTCCTGTGCAATTGTGGTAGAATTGACCTCGAGGTGCCCAATCTATAAT +AGTTCGGAATTCACTGTTTTGTAATATCACTGCACTATTGGCCACACATTCTTCCCAAACTAAAGCTTCTACATTTTTTG +ATCCTTTGGAAATTTCCTCGGGGCAAGGTTTCCCTTTAGGTCTAAATTTTAATGATCTTTGATAAGAAAAGTCTTGTAAA +TAATTTACCTGTGGCCTGAGCGACATCCCGCTTACCATGTGATAAGTGAATCTTCTGATGGGACTGACAGTAGGTACTTC +TACCAACCAATTTTGGACTGCAGGCATTAAACATCCTGGTGCTCTCCCCAGGCAAATAGGAGGATAATGATACCCAATGA +AAATATTTATCATCATCCCTTCTTCCTCAGGTTTGGCAGGGCAACGATCATCTGTTGGACCAGGTACCCATACACTATCA +TTAACATATACTTCCATAGGATTATCCATCCATGTGACTGCCTGAATTAAGGGCGGGAAAGGCACATAGGCCCAGTAATT +ATAATTAGCTGCAGCTGCTCCTGCAGGCATAGGGAGACTTACCACCATTGATACAATCATCAAAGCTGCAAGCAGCATAC +TCTCTGGAGTTTGTGTCACCTTTGTGTTCTCTAGATATTTTGTAGCTAACTGTGTCAGCTTCTTTAATTGTGCCCAAGTC +AGCAGCTCTGCCTTCTTGGTGGATGGCAACTTCATCTGTTCTTCTGACATCACCATTCTGTTCATCTTCTGAGTCGATGG +TTCTCGATGACGGTGCTCGATTGCGGTGTCTCCGTCTCCGTGGAGGTGCTTTTCTTTGCATCTCTGATGGGTTCATTGTA +GAACTTCAAATGTCTAGTGGGTATCCAAACAGGACGCTGATTTTCTCCTGGTGAAACGCAAGCAAAACCTCTCCCCCACG +TTACCACCTTCCATATTTCCCATGTCTTATTTTTATTATCTTTCCACCAAATCAGTTTTCCTTCATGTGGGCTGTTCTTT +TTACCAGTAAGATGTTCTGCAGAAGTAGTAGTCTGATTTCTATAAATGTTTAAAAAATTTAAAGTATAGAGTGCTAGACT +AAGTTGCATCTGAGGAGTGGTACACTCCTTACTGTCTCCCCCTTCTTTTTGTTTAACTAATTGAGTTTTGAGTGTTCTAT +TAGTTCTTTCAACTATGGCCTGTCCTTGGGAATTATAGGGAATTCCTGTTGTATGTGTAATTTTCTTGGAAAGCTTTACT +ACAGTATCCTGGTCCATTGTTAGTTTTGATTTTTTCTGGAACTCCCATTACAGCAAAACAAGATAATAAATGTTTTTTAA +CATAAGAAGTACTTTCTCCTGTCTGGCAAGTTGCCCATATGAAATGTGAATAAGTATCAACTGTTACATGAACATATGAT +AATCTTCCGAATGAAGGTACATGTGTGACATCCATTTGCCATAACACATTAGGACACAGACCTCTGGGATTAACTCCTGC +CTCTTGAGTGGGCAGGTGTAGGACTTGACACTGGGTGCAATGTGGTACAGTATCTTTTGCCTGTTTCCATGTGACATCAA +ATTTGTTTTTTAATCCTGCTGCATTTACATGAGTTAAAGCATGAAGTTCTTGTGCTTTTATGAATGCAGATGATACCAGT +AAGTCAGCTTGTTAATTTGCTTTAGTCAAAGGCCCTAGTAAATTAGTGTGTGCTCGAATATGAGTAACATAAAATGGGAA +ATTTCTTTTTCTTACAGTTTGTTGTAATAAATTGAATAGCTGGTTTAACTGATCATCCATGCTATATTTGATTAGAGCTG +TCTCAACATCCCTTGTAGCCTGTACTACATATGCAGAATCTGATACAATATTGATAGGTTGATCAAAATCTTGTAACACT +GTAATGACTGCAACCAACTCTGCTCTTTGAGTCAATTGATATTGAGTTTTGATTACTCGCTCATCAGACCCCAGAAAAGG +TGTTGGTTGATATAGACAGCAGGACGGTGGCCATGGAAGTCAGAATCCACTAAGGAGTGTGTAACAACTCACCTGCCGAA +TCAACTAGCCCTGAAAATGGATGGCACTGGAGCATCAGGCCCATACCCGGCCATCGCCAGCAGTCAATGGAATGTGAGTG +CCTTAGAGGTCTTGGGGCCGAAACGATCTCAACCTATTCTCAAACTTTAAATGGGCAAGAAGCCCAGCTAGCTCAGTCGG +TAGAGCATAAGACTCTTAATCTCAGGGTTGTGGATTCGTGCCCCATGCTGGGTGCCAGATGAAGAGGTGAGTTGCCCCTA +AAAAGGATACCCTATTCCTTCTCTTTCTTAATTTCTTTTAGTCTCAATTGGGACTTTAATGCCATTTTCATTTTTTCCTA +GTCCCTTTCCTGGTATATATCCCATCTCAGTCATGATTTTTTGATTTGCGGGGCTGTATAATGGAGCAGGCATAGTGATT +TCCGCACCCCGTTGTTGTAATAAATCTCGACCCCACAGATTAAGAGGAATCGAAGTAGTCATTGGCTGAACAGTACTTTC +TTGATTATCTGACCCTAAACAATGTAAAATCTTAGTACTTTGATACACTTCTGAGGCTGTGCCTACGCCGATAAGTCCTA +TAACAGCCTTTGTTTAGGCCAATTTTTTGGCCACTGATTGAAAGCAATGATAGAGACATCTGCTCCAGTGTCTACTAACC +CTTCAAACTGTTTTCCTTGAATAATGGCCTTACACACAGGTCTGTTTTCTGAGACCTGACTTGCCCAATATGCAGCCTTT +CCTGTCGGATCAGTGCTTCCAAACTCTCCTATTCTTTTTATTTTACTATTTCCAATCTTAATATAAGGCAGGAGTAATAA +CTGAGCAATCCTGTCTCCTGGACAGGAGGGGGGACTTCCTCCAACACAGGCAGTCACTGATGTCCTAGTTCAAATGCTCC +AGGGGGAACAGTCCATTACTCACTGAGGGAGGCAGGCTTTCCTATTTTCTGTTTCAAGCTTTTGATCACAATGGAGCAAA +TGATCACAATGCAGCCCACCAGGATGCTGGTGATGCTGAATCCAAAAAGTAGTCTGGAATTGATCTGTGCTTTTCTCTGA +TTCCCAAGGAAGTTGGCTTCCTGGGTCTGTGGGGGTAAAGGCAAGCTGGTGTGAATGTTTCCAAGATGGGGAGGGGGAAG +AAAATCCCTGGGGAGAGGTGAAGAGGGCAAGATCAGGATGATGGCATGATGGAGGTCAGGGCAGGGCTCAGTCTCCCAGA +GGAAAAAGGAAGAAACCAGGGAGCCAGGATGAGGGAGTAGATCTAGGCAGAATACAGAGGGAGGAGCAGAGAGGTGGCCC +TGGAGAGGTGCAAGGGCAAGCAGATGGTGAAGAGGGAGGAGGCAAATCCCCGAGAAACACGAAGAGGTCCCTGGGGAGAA +GCAGGGCATCGGCAGGAAAGAGAAAGAAGATCTGGGGACAAAGAGAGTTGGTAGCTGGGTGGTGCCCATCCTGAGGGAGA +ACACCAGAGCGGGGATTGCCAGCAGGACGCAGAAGAAGGATACGACAGCGAGCAGTAAATAGTCTCTTTCTGGAGGACAC +AAGGGCATCAGGGTGACTGGTTGCATTGGGGCGAGCAGCTGCACTGGGATGAAGGGCTGCATGGGGATGAGTGGCTGCAT +CGGGGTGACCAGGGACTTCAAGTCCCTGGGGCTCATTGCAGTAGCCATAGGCCTTGGCCTGAGGAGGAGATGCTGGGTGA +GTGGAATCTGCAGACAGGGGTCATGGGGTCCGGGAAGGGGACATCTGAGTCCCCAAGCAACTGAGACCACCCAAACCAAC +CCCTCTGGCTAGAAAGGTACTGAAAGTTGACACTGTCCCAAGCCAGGGGACAGCGCTCCAAGCCAGAACCCACCACAGCC +CCGTGCTCCTGTAGAGTAGCCTTGCTGGCTCTGGAACACCCACGCCACCCTCCTCTAGCCTCTCACAACCCCCTCCCTGC +TCCCCTCTGGCTGTTGCACTTTCCTCCTCTAAACTCTCCCTACCCCTCCTTGCCCCGCAAACTCACACACCCCCACTGTT +TTCTCAGCCTCCCAAAGTGCTGGAATTACAGGCGTGAGCCACTGTGCCCGGCCTATTTATTTATTTTTTGTAGAGGTCTC +ACTATGTTGCCCAGTCTGGTCTTGAACTCCTGGCCTCAAGCAATCCTCCTGCCTCGGCCTCCCAAAATGTTGGGATTACA +GGCGTGAATCACTGTGCCCAGCCACTCTTCCTTTTTGTTGTTGTTGCCAGTTTGAACGCCTGATGGGTTCAATATATAGT +GTGTGGATCTCACATCCAGATTGAATATAAGTATAATGGGCAGATTTATCACTATTAGGAGGCTACTGTACATTAGGCCC +AGGCTGGCCCCTGAGGGCCACATGGTAACTAAGGCAGACAAGACCATAACTCAGATAGAATAATAGTCTCCAATCCCATC +CAGGTTGCTGTGAATGCCATTAATTCATTCCTTTTCATGGCTGAGTAGTATTCCACCATATATATATGTAAAATCAGTTT +CTTTATCCACTCATTCATTGATGGGCATCTGGGTTGGCTCCACATTTTTGCAATTGCAAATTGTGCTGCTATAAACATGT +GTGTACAAGTATCTTTTTCACATAATGACTTCTTTTCCTTTGAGTAGATACCCAGTAGTGGGATTGCTGGATCAAATGGT +AGTTCTACTTTTAGTTCTTTAAGGAATCTCCACACTGTTTTCCATAGTTATACTAGTTTACATTCCCACCAGCAATCAGG +GAAATGCAAATCAAAACCACAATGCGATACCACCTTATTCCTGCAAGAATGGCCATAATCAAAAACTCAAAAAATAATAG +ATATTGGCATGGATGGAGTGAACAGGGAACACTTTCCTTGAGTTTTAACTCCAGACTGGCTCCTTTGCTGTTTTTCTGGT +GGGGCTCTTACCACATTTCCCACAGCCATTGGCTGTGTGCTCCTGCATACTTGGATGCTCCTCTCCTGCCTGGGCCCCAC +CAGGACCCCTGAGATCCAGGAGCCGTGGCTTTTGGGGGCTGGTAGAGAAGAAACAGGGAGCTACGTGAAGGCCAATGGCA +GAAACCAAGAGAGTTTCAAGAAAGCAAGAAAGAGAAACCAGCTCTGAAAATAGCAAAAGGAACAACGGGAAAAAATAAAA +TAGTTTCATGTAACCTAAAAACAGACATTGAGTCTTCGAATCAAATTGAAAGCACTTGCTAGCGTCAGAGGCATGTGAAC +CAGAGCAACTCCATCTTGAATGGGGCTGAGTAAAATGAGGCTGACGTCTACTGGGCTGCATTCCCAGACAGCTAAGGCAT +TCTAAGTCACACGATGAGATAGGAGGTTGGCACAAAATACAGATCATAAAGACTTTGCTGATAAAACAGGTTACAGTAAA +GAAGCCAGCTAAAACCCACCAAAAACAAAAAGTGGCGATGAGAGTAACCTGTGGTCATCCCCACTGCTCATTATACACTA +ATTAGAATGCATTAGCATGCTAAGAGACACTCCCACCAGCGCCATGGCAGCTTACAAATGCCATGGCAACATCAGGAAGT +TACCCTATATGGTCTAAAAAGAGAAGACATGAGGCCAGGCACGGTGGCTCACGCCTGTAATCCCAGCACTTTGGAAGACC +AAGGCAGGCAGATAGGGTCACCTGAGGTCAGGAATTCCAGACCAGCCTGGGCAACATGGTGAAACCCTGTCTCTACTAAA +AACAGGAAAAATTAACTGGGCATGGTGGCACGCACCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCTCTTA +AACCCGGGAAGCAGAGGTTGCAGTGAGCCAAGATTGTGCCACTGCACTCCAGCCTAGGCAACAGAGCGAGACTCCATCTC +GAAAATTTAAAAATAAATAAATAAATAAAGGGGAATATCACAAGCAACGTATGGCAACAAATTAGACAGCTTAGATGACA +GCTAAAAATTCTTAGATGAATTACCAAAACCAACTCAAGAAGAAATAGAAAATCTGAATAGATGTATAACAAATAAAAAG +AGATTTGTAATTTAAAATACTCCCGCAAAGAAAAGCTCAGGCCTAGATGGTTTCATTGGTGAATTTCACAAAACATTAAA +GCATAAATAATATTAATTCTCTACAATCTGTTCCAAAATATGGAGAAAGGGAGCACTTTCCCAGCTTATTATATGAGGTC +AGTATTACCCTAATATCAAAGCCAGACAGTGGCATCTCAAGACAACTAGAGACCAATCTCTTTCATAAATATGATATAAA +AGTCATTGGTAAAATATCAGTAAGCCCAATACAGCAATATATAAAAAGGGTTATACACCATGATCAAGTGGGATTTACCC +CAGAAATGCAAGATTGGTTTAACTTCTGAAAATCAATTAATGTAACACACCATAGTAAAAGAATAAATTTTATTCATTTA +TTTTAAAGATGGGGTCTCACTCTGTCACCCAGGTGAGATTGCAGCTCACTGCAGCCTCAAACTCCTGGGCTCAAGCAATC +CTCCTGCCTTGGCCTCCCAGAGTGTTGGGATTACAGATGTGAGCCGTTGCACCTGCCCCTGTAAAAGAAGACACTTTTTT +TTTTTTTGAGATGGAGTCTCACTCTGTCGCCCAGGCTGGAGTGAAAGACTAATTTCCGGGTGGGCGCGGTGGCTCACGCC +TGTAATCCCAGCACTTTGGGAGGCCAAGGCAGGTGGATCACGAGGTCAGGAGATCGAGACCATCCTGGCTAACACGGTGA +AACCCCGTCTCTACTAAAAAATACAAAAAAATCAGCCAGGCGTGGTGGCGGGTGCCTGTAGTCCCAGCTACTCGGGAGGC +TGAGGCAGGAGAATGGCATGAACCCAGGAGGTGGAGCTTGCAGTGAGCCGAGATCATGCCACTGCACTCCAGCCTGGGCG +ACAGAGCAAGACTCTGTCTCAAAAAAAATAAATAAATAAAAGAAAAGAAAGACTAAATTCTTCCTGGCTAAGATAGGGAA +CAAAACAAGAATGTTCATTCTCACCACTTTTATTCAACAGTATAGTGGAGGTCCTAGCCAGTACAATTAGTCAATAAAAA +GAAATAAAAGGCATCTGGGTTGGAAAGGAAGAAGAAAAAACTGTTTTGACCCACAGTTGACTTAAATTTGCTTGTAGAAA +ACTCTAATGAATCCACAAATAACTACTAGAAGAAATGAGTCCAGCAATGCTGCACAAGATCAATATCCAAAAATCAATTG +TATGTCAATAAGCTACCAATGAACAATCAAAAAATGAAATTAATAAAATGAATTCATTCCCAATAGGATCAAAAAGAATA +ATAAATTCAGGCTGGGCGTGGTGACTTATGCCTGTAATCCCAGCACTTTGTTAGGCCGAGGCGGGCAGATCATCTGAAGT +CAGGACTTCAAGACGAGTTTGGGCAACATGGTGAAACCCTGTCTCTACTAAAAAAAAATACAAAAATCAGCTGGGCATGG +TGGCGGGTGCCTGTAATCCTAGCTACTTGGGAGGCTGAGGCAGGAGAATTGCTTGAACCTGGGAAGTGAAGGTTGCAGTG +AGCCACGATCGCACCACTGCACTCCAGCTTGGGTAACAGAGAGAGGCTATCTCAAAAAAAAAAAAAGAAGAAATTCAATT +AAAAAAAGTAATAAATTTAACAAAAATACAAGAGTTGTACAATAAAAACTATAAATCTTGGCTGGCGGCGTGATGGCTCA +CGCCTGTAATCCTAGCACTTTGGGAGGCCGAGGTGGGCGGATCATGAGGTCAGGAGATCGAGACCATCCTGGCTAACACG +GTGAAACCCCGTCTCTACTAAAAATACAAAAAAATTAGCCGGGCGTAGTGGCAGGCGCCTGTAGTCCCAGCTACTCGGGA +GGCTGAGGCAGGAGAATGGCATGAACCTGGGAGGCGGAGCTTGCAGTGAGCCGAGATCACGCCGCTGCACTCCAGCCTGG +GCAATAGAGTGAGACTCTGCCTCAAAAAAACAAACAAACAAGCAAACAAACAAACTATAAATCATTCCTTTAAAAATTTA +AAGATCTAAATAAATGGAGTGGCATCCCATGTTCATGGTTTGGAAAACTCAATGTTAGCAAAATGGCAGTACTCCCCTAA +TTGACCTAAAGAGTCAATGCAATCTTTATCGAAACCTCAACTGGCTTGTTTTTTTTTTTTTGCAGAATTTGACAAGTTGA +TCCTAAAAGTCATGTGGAAATGAAAAGGATGCAGAATAACCTAGCAATGTTGAAAAAGTAAAATAAAGTTGGAGGACTTC +CAGTTACCAATTTCAAAATTTATTCCTGATTACAAAGCTATAGGAATAAAGACAGTGTGGTACTGGCATAAGGATGGACG +TATAGCTCAATGAAGCAAAATTGAAAGTCCAGAAATAACTTTCATATTTATGGTCAAGTGATTTTTGACAAAAGTGGCAA +AACCATTCAATGTGAAAAGGAGATCCAACAAATGGTGCTAGAACAATTGGATCTCTCTCTCTCTCTCTTTTTTTTTTTTT +TTCTTTTTGGAGATGGAGCCTCGCTCTGTCACCCAGGCTGGAGTGCAGTGGCATGATCTCAGCTCACTGCAACCTCTGCC +TCCCGGGCTCAAGCAATTCTCCTGCCTCAGCATCCCAAGTAGCTTGGGGCTACAGGTAACCACCACCAAGCCCAGCTAAT +TTTTGTATTTTTAGTAGAGACAGGTTTCACCATGTTGGCCAGGCCAATCTTGAACTCCTGGCCATTATTTTCAAATGATT +CTCACATGCTCTGAACCCCACTCATTCATCATTATTATCTCTTTTTTTTTCTTGAGACAAGGTCTCCCTCTTGTCCAGGC +TGGAGTAGAGGGGTGCGACCATGGCTCACCACAGCCTCAAATTCCTGAGCAAAAGCAAACCTCCCATCACACCCTCCCAA +GCAGCTGAGAATACAGAAGCCTGCCCTCAGAGCCTGATATATATATATATCTGTCTAAAATATATACATTTTTTTTCTAG +ATACATGGTCCCACTGTATTGATCGGGCTAGGTTTCACTATCCTGGTCTCATGTGATCCTCTCCCCTTAGCCATTATCAC +CTTATAGAGTGCACATTTTGTTCCATGCACTGTTCAATACCACTTACATACATTAATTTAATCCTCACAATGAAACAAGA +GAGAAATAATTATTATTCTCATTTTAAAGATGAGAAAACTGGTACACAGAGAGGTTGCACAAGGTGATATTGCTAGTTAG +TGGCATGGTGGGGATCCAAACCTAGGTAGTCTAGTTCCAAGGTGTGTTTATTCAGCCCATGTTTACTGAGCACCTACTAC +TATGTGTTGGGCTCTGCTCTAGGTGTTAGAGCTATAGACAGTATAAAAGAACTACCTCGCCCCCATTAAGACATCTCTCC +TGGCTGGGCATGGTGGCTCATTCCTGTAATCCTAGCACTTTGGGAGGCCAAGGTGGGTGGATCACTTGAGGTCAGGAGTT +CAACACCAGCCTGGCCAACGTGGTGAAACCCCATATCTACTAAAAATACAAAAATAAGCCAAGCATGGTGGTGGGCACCT +GTAATCCCAGCTACTCGGGAGGCTCAGGCAGGAAAATCACTTAGAACCCAGGAGGTGGAGGTTTCAGTGAGCCAAGGTTG +CACCATTGCACTCCGCCCTGGGCAACAAGAGCAAAACTCCATCTCAAGAAAAAAAAAAAAAAAAGACATCTCTCCCATAT +CTCTCTCTTATTGAGACATGGTCTCTCTCACCTTTCCCACCAAAAATTTTTTTTAAAAAAACAAAACCCTTTCATCACTC +AGCCCCCTCAAGTTATGGATCTGGTTTTTGGTTTTTAATTCCAGAGACAAAAAAGGGGGCAGAACATAGACCCATGCTAC +CATCTTGACAGAGTCTCCATTTCAGTTCCATACGCTCAAGTCCCTCCTATCTTTAAAACAAACAAGCAAAATCATCATTC +TCACCCTTTTGCATCTACCCTAGTCCTGCCTTTCCTGGCTCTTCATAGGCAAACTTTTTTTTTTTTGAAACGGAGTCTCA +CTCTGTCACCCAGGCTGGAGTGCAGTGACACAATCTTGGTTCACTGCAAACTCCACCTCCCGGGTTCAAGCAATTTTCTG +CCTCAGCTTCCCAAGTAGCTGGGATTACAGGCACCCACCACCACATCTGGCTAAATTTTGTATTTTTAGTAGAGACAGGG +TTTCACCATCTTGGCCAGGCTTGTCTTGAACTCCTGACCTCGTGATCCACCCACCTTGGCCTCCCAAAGTGCTGGGATTA +CAGGCATGAGCCACTGCACCCAGCCATAGGCAAAATTCTTGAAGCAAATGTATACACTGTCTCCAATTCCACACCTCACA +CTCAATCCATGGCTCTCTGAAACCAGGTGTTGGCTTACAACATTCCAGAAACTGGAACCCATGTTCTTCTCAGCTGTGGC +ACTGCTCTCCTTACTCTTTCGCCTCCAAGCTCTTGGAAGAAATGTCTACATTGTCTATACTACCCCTTTCTTGCTCACTG +CCTGGATCCCTGCAATATGGCCACTGCTTCTGCTGATCCTCTGATACTGCCCTTACCAAAGGAGCTAATTCATTCCCGCC +TTCAGAGCTGGAGTACATGTCTCAGGGCTTGACACACTTGTCCATGTACTTGTTTCTAAGCCTGTCTTAGTTTGTTTGTT +CCACTATCCAAGGGACTTGCCAGCAACCAATACACATGCTTCATCTCACCCAACCTTCACTTCTGTCTTCCCCCACCTCG +GCGCCCCATGTTCTGTTGTCCTGGAACATCCACTTGGTCCTTTGAAAGGCTCTGAATCAATTTCCTCCATGAAGAATATC +CTGACTTCTGGATTGCATGTGTTGTCACTGTTCTTCTAACTACTTCTAGGTCCAGGACTGGTCTTAAATACCCAGGTTGT +GTCTTGGACTAGCCATACTAAGGTCTACATTTGTGGACTGCACAGTGTCACCTCCCACCCACCCCCAACTTCCTGAAAAG +TGGCTCCTCAAACCCTCTACTCCCCTAGAACCACCAGCTAAAGGAAGCATCAGCTAAAACCCCACAGTATTAGCATTCAT +TAAAAGCTCCTACCTGAATCAGCAATTACTATGGTATTTGAAAATGTTCTCTAATGGTGCATAACAAATTCCCCACAGGT +ATTAACTCACAGTTCTGTAGGTCAGAATGGCTGCTTCTCTGCTCAGAATCTACCAAAGCTAAAATTAAGGTGTCAGCTGG +GCTGGGTTCTCATGTGGAGCTCAGGATCCTCTTCCAAATGAATGTGATTACAGCAGGATTCACATCCTTGCTGTTGCAGT +ACTAAGGACCCCGTTTCCTTGCTGGCTGTCAGCTGGGAGCCATTCTCAGCCAGAGGCTGCCTGGATTCCTCCCTACGTGG +CCCTGGTGACATCTTCATGCCAGCAATGGAGACCTTTGCTTGCCTCCAATCCTCTCACACCTGAAACCTCTTTTGCCAGG +AAGACTCAGTCCTTTTCAAGAACTCACCTCATTAGGTGAGGCCCCCAGAGGATATTCTTCCTAACTTAAAGTTAACTGAT +TTCAGACCCTACCTACGTCTGCAAGACGCCTTCCCAGCAGCATACAGACTAGTACTTGACTGAGTGACTGGAGAAGGAGT +GTGTACACCAGGGAGCAGGAATCTTCGGGTCATCTCAGAGTTCTGCCTACCACATTGGGGATTTCCTGTTCCATCGTTCC +ATTAGGTTGTGTTCTACTTTAAGAAAGATCTTCCCCCTTTTCCCCTTCTTTCTCTATTCTTTCCTTTTTTCTTTCTGTCA +GCATAAACCAAGGAATTCTTTTTTTTTTTTTTTTTTGTCATTTTTTCTTGAGATGGAGTCTCACTCTGTCACCCAGGCTG +GAGTGTGCAGTGTGATCTCGAATCACTGCAACCTCCGCCTCCCGGGTTCAAACGATTCTCCTGCCTCATCCTCCCTAGTA +GCTGGGATTACGGGCGCCTGCCACCACACCCGGTTAATTTTTGTATTTTTAGTAGAGACGGGGTTTCACCACGTTGGTCA +GGCTAGTCTCGAACTCCTGATCGCAGGTAATCCGCCCGCTTTGGCCTCCCAAAGTGTTGGGATTACAGGCATGAGCCACT +GCGCCCAGCCGGAATTCATTTTTAATTCAAGACTTTACAATCCACTTCTGACACTACTCATTTTCCTCCCCTCAGTAAAT +CTCTTACTCTTACTGGGCGTGTCTAGTTTCTCTCTTCCACAACTCCTGGGAGACCCTCCTGACATCGCCCATCCGCGCAG +GCTGCAGAGAGAGATCAGTGGGAGGAGGAGGAGCCGAGAGCCACCACTTAGGCTTCCAACCAATCCCTACCAGGGTGGGC +GGAGCCCACTTCCTGATTGGCTGCACTCTCTGTGTCTTGGGGTGGGCGAGAACGGCGGGGCCACGCCCCCTAACTAGGCA +GCCAATCAGGACGTGGGGTGCTGGTTTCTCCATTGCGAAGCTTCAGCCTTTGATGGTTTGGGTCCTGGGAGTCTGGTTAG +TACAAGGGGAAGCCTAGTGGGTCTGGCGCTCCGTTTTCAAGACACTCGGAGTCCGTCTTTGAGGGGAAAGGTCATGGCCC +TGAAACCACCTTCTGCCACCCAGCCTGCTCCCAACGCGCCAGCTACCCCAGACGCCCCCCCTACCACAGGTGATCCAGGT +G diff --git a/nim-skills/genomic-intelligence-nim/assets/demo/enhancer_eve.fa b/nim-skills/genomic-intelligence-nim/assets/demo/enhancer_eve.fa new file mode 100644 index 0000000..92dd9ca --- /dev/null +++ b/nim-skills/genomic-intelligence-nim/assets/demo/enhancer_eve.fa @@ -0,0 +1,168 @@ +>drosophila|eve|chr2R:9972000-9982000|BDGP6|strand:1|gene-sense +CCTCGTGTTCTTCGATGTTCGGGAGCGCCAGCAAATGGGCGTCTACTTCGAAAGCCATGA +GGACGATATCACCTCGTTGCGGTTCCATGCCCAGAATCCAGACCTTCTGGCCACGGGCAG +TGTGGACGGGCTAGTAAACGTGTTCGATGTGAAGGAGCCGGACGAGGATGAAGCACTGCT +TAACACCTTCAACACGGAGAGCAGTGTGGCCCGGTTGGCGTGGCACAGAAACGTCTACGA +CAAGGACATCATATCCTGCGTCACGACCACTGGCGATTTCAAGAGCTACGAGTGCGAGGA +GGGCGACGAGGTGGCATCCTTCGAGCGGCCGGATGTTACGGCGGCAATAAGGAGAAAAAA +AGCCGCAAACTTCAACCTGATAAATGCCCACAATCAGGAAGACGGAGGAGTCTTTCTGTT +GGCGGGCACCAACTTCAACAAGGGGTTCGTTATTAACTGACTTTGGCCTAATACCCAAGC +AACTGAATAACCTTCTTTCAGCGAGATCCTGCGCTCCGTTAGCGTTACCTCGAAGAACAG +CCTCCAGCCATTGGCCAATTTCCAGGGAAACAAGCAGATCGTCAGAGATAGTCTTTTCGA +TTCAAAGAGAAGTTTACTTTTTACAGGCGGAGAATCCGGCATTGTTACGGTTTGGGCACA +GGACGCAAGTGGAACGGCGTTCAGCAGTGAAAAACTGAAGGCCAGGAAGGAGAAGAAATC +ACGCAAGCAAGCGCCATATTAAATGTGTATTTATGTTTTAAATAAAGGTTTTCTAAATAC +GAAATTTCAAATTTAATGGGTAGCAGCGTTGCCAGATAAAGGGACATGTTTACTGGCTGA +CAGGAGCTCACTGATGTGAGCGTATGTCTACGAGAGCTGCTTACGTAATTTGAGAAAGTG +TTGCATACTTTTGAGATTGCGTTTGAGCTACGTTACTTACATTTTTCCCACATGAGTCGG +GCATACACATATGTAAATAAAAATTGGGTTCACTGTATATAAAATAGAGTATAAATTATT +CATCTTATCTAACAAATATTAGAAATAATCAAAACACCTTATAAGTTTATCGAATTTACG +AATTTACGAATTTATTTCTACATATAAATGTTGACCGCCATTTGGACTCCACTTAACCGC +TTAATTTGCTTAAGTAACATAAGTAACTTTTAAGATTAATAGTATTGTATTTTGAATGGT +AAAAGTCCATCCTAATCTATTGAAAAAATCCGAAGGAACCAAAGTCATTCAAAGTTGATT +CAATTCACCCCCACCCTGCGCACTCAGTGCTCAATTTCTGCTCGCAGCAAGGTCTGCTGA +GTAAAAAATTAGCACATCCTCAGCGCAGATAAACGCATCCTTAAAACACCCCCTGGCCAT +CCCACAAGCTAGCCATCCATCCATCCGTTCATCCTGCATCCGCATCCCTTCGCACAGAGA +GCGGCCCAATTAGTGCTGCAGTAGGCATTTCGAATCCGTACCCACAAATCGGCTAATCCG +CTGCCACTGCCTGCCGGCCCAGAGTCCAAAAGTCCTGTGTGCAATGTCCAGATTTCCGAG +TCGCAGAGTCTAAATGTTGACAAATACAACACCTACGATACAAATAAAATTGCTACAGAA +GTACCACCTAAAAACGGGCACAACCGGAACGACTTAATTTTCTCAAATTTACAGAAACAG +ACGATGACATAGGAATGGGAATGGTGATGGTGATGGAAACACAGGAAGATATCTCGACGC +CGCCACTGAAGCTGTGGGTTTGCTCCTGCCGAGCGAATCCAACGCGAGTAGGGTCCCATT +CGGGGCCCGAGTAGCCAGAGTCCTGCAGCTCACTCGAAACCGCCACTCACCGTGGCTAAT +TGCCCATCAATAAAGGGCCCGGGCAGTGAGGAATTCCTCCGAAAGTCGGGTCCTCCGTTC +TCCAGCCGAAGATTTTTTCGAGCAACCAAAATATTATGGTGTGCCCCGCTGTTCTCGCAC +AGTCAGCGCGAATTTGCTGCGGTGAGTCGATGCTGTTTCGCAGGACCTTCTTCCATTTTC +GTCTCCCTCTTGCTCAGCCTGTCCCTGTTCCTCTGCAGTTCCCTATCTCCTGATGCCTGT +GCTCCTTTGGCGGCACTGTGTCCTGTCGTCGTTGTTTTCCTGTGATTTGACATGTCTGTT +AGCAGGATGCCTGACCCTGAGGCCGAGCCCTGGTCTCAGTGTCCACTGTTCCACTTTGAT +GTGATTCGTCAGTGCGGTGGACTACTGCTACTGCTCTCTTGCTGGACTGCGTCTTGAGTC +CTGTTCGGCTGCCCCCTCCCGTGACCTCTGACCCTGCACTCTGCGGCTTTCCAGCGGCGT +TTGTTGGCGAATCTGACCCCGAGCTCCTGCTGCTCCTTCGCTCCTTCGCTCCTTCTCCGC +ATCTCCGCTCTTTGGACTTCGTACGAATCAAAATTGGTCACAGCACCGAGTGAATTGCCC +CGGAGACCGCAATGCGCTGTATTTATAGTAAACGTGTCCGATTGATTTGGCCACCCGTGG +CGGCTCTGTCACAGATGCCTCAATTTGCATCTATCGAATGGTTTACATGGCTCTAAAAAG +GTACCTCGATGGGTTGGTCACAATGTGGTGGCCTCTCAACATTGCAAGGCTCTTACTTGT +GAATTATTAAGTTATTAACTGCTGCGATGTAAGTCATGGCAGTTTCTGTTTTCTTTATAG +GATATATATAGGAAGGATTAAAGGAGGCATGTACAATAATATGAGTATGATTTAGCTCAA +ATTCCAAATATGATAAAAGTACAAAGCATACGATAATATAATCAAATTACGCTGACAATC +ACGATAATGTTCTTGTAGTAGTATTTGTGTAATATTTATGTTTTTTTAAGATAAGAAACG +GTAATAAAATCCACGTAAGTGTAAAAAATGGATGCCCTAATCTATGCCATGATGTGTTCT +ACTTTCGAGATTTCGCCTCTGCCCTCATTGATGGTTTCCCGGGGCTACTTGGCCCAAAAA +TCCCGGCCGTCCAAAAAGACGATCCTTAAAAAAGAAACCGCTAATCATTGGGCCGCACAA +AGAGCGGACAATCGCTCACCTAATTATTTGGCCCGATTGTGAGGAGCGGACAGTCGGCTC +GTGGACGCTTTTTGTGGCCTCTTTTTGTTTCGACAAAAAGCGAGCCAATTTTTTTTCTTT +CTGGGCCACTTTGTTGCTCTTTTTATGAGTTTTTTCCATTGTCAGTTTTTCCGGGCCTGT +CTCGCAGCCCTCGATTCCCGCGATGCCTGCCCTACAAACCTCCTAATTACGGCAGTTAGT +CGTTGTCCGGGACAGGAGAGTATGCGGAAGGACATGCGTGAGTTTATTGCCCGCTCGAAT +TTCCACTAAAAATTGGGCCGAAAAAAAAACAACTAGGTAGGACTAGGAACTGCAAACTAG +CAAAGCGGACGCGCCTTTTTATTGGTGCACCTTCGGCGGAACCGCAGGATAACAGCAGTA +AAAGCGACGACGAGGACACAAGGATCCTCGAAATCGAGAGCGACCTCGCTGCATTAGAAA +ACTAGATCAGTTTTTTGTTTTGGCCGACCGATTTTTGTGCCCGGTGCTCTCTTTACGGTT +TATGGCCGCGTTCCCATTTCCCAGCTTCTTTGTTCCGGGCTCAGAAATCTGTATGGAATT +ATGGTATATGCAGATTTTTATGGGTCCCGGCGATCCGGTTCGCGGAACGGGAGTGTCCTG +CCGCGAGAGGTCCTCGCCGGCGATCCTTGTCGCCCGTATTAGGAAAGTAGATCACGTTTT +TTGTTCCCATTGTGCGCTTTTTTCGCTGCGCTAGTTTTTTTCCCCGAACCCAGCGAACTG +CTCTAATTTTTTAATTCTTCACGGCTTTTCATTGGGCTCCTGGAAAAACGCGGACAAGGT +TATAACGCTCTACTTACCTGCAATTGTGGCCATAACTCGCACTGCTCTCGTTTTTAAGAT +CCGTTTGTTTGTGTTTGTTTGTCCGCGATGGCATTCACGTTTTTACGAGCTCGTTCCTTC +GGGTCCAAAATTATGCCAGTTTGTTTTGTCTCTGGCAATTATTGGAAATTTCATTGGGTC +GATTTCGCTGCCTTCCTTGCTCTTCCCTTGAGAAAAGTGAATAGGTTGTGCCATAAAAAT +CGCTGCTCCTGAAGACCAAATGAAATGGATTTGTGTAAGCATTAAAAACGCGAGGCAAGC +CCCAAGATTCCTCCACTGCTTTTTTTATATTGCCCACTGCTAAATGCAGCTAATTCGTCG +ATTGTTTAAAAATTAAATTACTTATGTTGCCATTCATACATCCCCTCACATTTTATGGCC +ATTTGAGTGCGGGGTGCACAGTTCTGTCTTAAGTGGCGGATGGAAACCACCACATTTACT +CGAGGGATGATGTGCTCTAATATCTCCTCATCAAATGGGATGGTTTCTATGGAAAGGCAA +AATCGTTGTAAAGTGAGGCGGAGTTAAAAAATACCTTGTTATAGCCTTTTTAAAATAACA +CAAGATCGTTCGAATTGACTAGAAATATCAAAGTCTTTTTGTATTGAAGCGAGTGTAGTC +TCAATTTATGCTTAATTTTAAGAAATACATCTCTTTATTAGCCCCAAAATGAAACAAATG +GTCTACTAATTAAGCAAGTCAACAGAATTTTTATGCAATTATTCAAAATGAAATAATATA +TACATAAGATGTTTTTGGGAATCTGTCATGGGGTTTCTGAAATAGGTTTGCCAAACAAAT +TTTAAGTATAAATGTATACATATGTCAACTAATAAATTTAGCAAATAAAATGTACCTGCA +AGTATCTATAAATTTATTGGACCAATTTTGTGTAAAAAACTGAACTGGCACTCTTCCCAA +GAATGGGACTTCGAGGACTCCTTGCTGAATCACTTACTCAACCCATTCCAACTCATCCAA +TCCGCGCAATCATCATAAATTTTGGCCTTTTTGTTGTAATTGTTTTATGGCAGAAATTAC +TCAATCATCAAGCATAATTCCCTCGTTTTCGCCGTTTTATTGCCAATTTTTGCACTGCCT +TTGCCTTTTTCCCGCCCTTTCCTCAGCGTTTTGCGAATCTTTGCCGGCATTTCTATTGCG +CGGACAATCCGGCCAGTGTGTTGGCCATTTACTTGCCATGATGACGGGCATAATCAGCGA +GATCGGCGCTTTGTGAGTGCAGAATGTGCAATAAAGCGGCAACAATCGGCAGGGATTCGC +CTTCCCATATTCCGGGTATTGCCGGCCCGGGAAAATGCGAAAGTGTTTGCGGATCGAGAT +GGAAGATAGAGGATTGAGTATTGAAACGAGGAAGGTACTTCCGCCGGCGGACACTTTCGC +CTAACCAAGCCAATCCAACCCATCCCAATCCAATCCAACCCACCCGATCGCCATAAAGGG +TATTTACTGTCGCTGCCGCAGAGCCTCGCTTGACGACTTAACCCAAGCGGTCGTTTCGCG +TCCATTCTCCGGACGGAGTCAAAGACAAAGGCCGGCGGAGGTGGACAATAGGCAAGGTTG +TTGCTTGTGGGTAGGGTTTGAGCTATGAGCTATGAGCTGTGAGCTGTTAGCCCTGAACCC +CGAACCTCGAGAATTGAACCTTTCCCGGGGCAAGAAGGCTTGCATGTGGGCCTTTTCCAG +GTCGGCCAGTAGGTAGAGTTGTTGCGATGCGGCTATGCCGGGCGAGTTAATGCCAATGCA +AATTGCGGGCGCAATATAACCCAATAATTTGAAGTAACTGGCAGGAGCGAGGTATCCTTC +CTGGTTACCCGGTACTGCATAACAATGGAACCCGAACCGTAACTGGGACAGATCGAAAAG +CTGGCCTGGTTTCTCGCTGTGTGTGCCGTGTTAATCCGTTTGCCATCAGCGAGATTATTA +GTCAATTGCAGTTGCAGCGTTTCGCTTTCGTCCTCGTTTCACTTTCGAGTTAGACTTTAT +TGCAGCATCTTGAACAATCGTCGCAGTTTGGTAACACGCTGTGCCATACTTTCATTTAGA +CGGAATCGAGGGACCCTGGACTATAATCGCACAACGAGACCGGGTTGCGAAGTCAGGGCA +TTCCGCCGATCTAGCCATCGCCATCTTCTGCGGGCGTTTGTTTGTTTGTTTGCTGGGATT +AGCCAAGGGCTTGACTTGGAATCCAATCCCGATCCCTAGCCCGATCCCAATCCCAATCCC +AATCCCTTGTCCTTTTCATTAGAAAGTCATAAAAACACATAATAATGATGTCGAAGGGAT +TAGGGGCGCGCAGGTCCAGGCAACGCAATTAACGGACTAGCGAACTGGGTTATTTTTTTG +CGCCGACTTAGCCCTGATCCGCGAGCTTAACCCGTTTTGAGCCGGGCAGCAGGTAGTTGT +GGGTGGACCCCACGATTTTTTTGGCCAAACCTCCAAGCTAACTTGCGCAAGTGGCAAGTG +GCCGGTTTGCTGGCCCAAAAGAGGAGGCACTATCCCGGTCCTGGTACAGTTGGTACGCTG +GGAATGATTATATCATCATAATAAATGTTTTGCCCAACGAAACCGAAAACTTTTCAAATT +AAGTCCCGGCAACTGGGTTCCCATTTTCCATTTTCCATGTTCTGCGGGCAGGGGCGGCCA +TTATCTCGCTACAGCAGTTCCCAAATGGTTATGGCTGGACACCCCTGCCGCCGCTCCAAC +GGGGTGGATGAAGCCCCCAAAACCCGAAAGTCATGGCAGCCATGGCAGTGTGGGGCTGTT +AAACGTGCGGCATAATATTAAGACTTCATAAAAGCGCAAATAATTCGCTGGCAGGCGATC +GATAATACATACATACAAATATATAGTGGGATACACACACTCTCTGCCGGCAAACACACA +CCACCCGACCCGACTGAGCGGCATAATGCCATATCATTCTTGATGAAGCCGATAAAATCC +CATTATTAAGGGGGCCCGCCCGTCCCGCTCGCTCCTGCGGAGCAACCGCCTGCGGGCGGG +CGAGACAAAAGATTCGCTCATCCGCTATGAATACCAAATCGGAACTCTCTCTCTCTCCAG +CTCGGGAGTGCCATGGCCAGCATGGCCAGGACCTCCTCATGGTCCTGCCGAGCAGAGAAC +GCGGCTCCATCCCGCTGCTCCGGGTCCTGCTCCTCCGCTTTGTCCCGCCTCGTTATCGCC +GCTCAGCACCGAGAGCACAGCAGCGCATCCACTCTCAGCACCGCACGATTAGCACCGTTC +CGCTCAGGCTGTCCCGCTCGCACCTGCCTGGGTCGCTGCGATTGGCCGCTCCCAGCGACG +GCGGCCATTTGCCTGCAGAGCGCAGCGGTATAAAAGGGCGCGGGGTGGCTGAGAGCAGCA +CACTCGAGCTGTGACCGCCGCACAGTCAACAACTAACTGCCTTCGTTAATATCCTCTGAA +TAAGCCAACTTTGAATCACAAGACGCATACCAAACATGCACGGATACCGAACCTACAACA +TGGAGAGCCACCATGCCCATCACGACGCCAGTCCCGTGGACCAGAAGCCCCTGGTTGTGG +ACCTCTTGGCCACCCAGTACGGCAAGCCCCAGACACCGCCTCCCTCGCCAAATGGTAAGT +TTAAAGATAAAGCCGAGCAAACGTGACGAGTTACTTACACCCAATCTTTCCTCTGTCCAA +AACAGAATGCCTATCCAGTCCGGATAACTCCTTGAACGGCAGCCGCGGCTCGGAGATTCC +CGCCGACCCGTCGGTACGCCGCTATCGCACCGCCTTCACCCGTGACCAGCTGGGTCGCTT +GGAGAAGGAGTTCTACAAGGAGAACTACGTGTCCCGTCCCCGTCGCTGCGAACTGGCCGC +CCAGCTGAACCTCCCGGAGAGCACGATCAAGGTGTGGTTCCAGAACCGCCGCATGAAGGA +CAAGCGTCAGAGGATCGCCGTCGCCTGGCCCTACGCAGCCGTCTACTCCGATCCCGCCTT +CGCCGCCTCCATCCTCCAGGCCGCCGCCAACAGCGTGGGCATGCCCTATCCGCCCTACGC +CCCCGCTGCTGCCGCCGCTGCTGCCGCCGCCGCTGCCGTGGCCACCAATCCGATGATGGC +CACCGGAATGCCCCCGATGGGCATGCCCCAGATGCCCACAATGCAGATGCCCGGACACTC +GGGACATGCCGGCCATCCATCGCCCTACGGACAGTACCGCTACACGCCCTACCACATCCC +CGCCCGCCCGGCGCCGCCACATCCCGCTGGTCCTCATATGCATCATCCGCACATGATGGG +ATCCAGCGCAACGGGATCGTCGTACTCCGCCGGTGCCGCCGGCCTTTTGGGCGCTCTGCC +CTCCGCCACCTGCTATACCGGACTGGGTGTGGGTGTGCCCAAGACCCAGACGCCGCCGCT +GGATCTGCAGTCGTCGTCATCGCCGCACTCCTCCACGCTGTCGCTCTCGCCAGTGGGATC +CGATCACGCCAAGGTGTTCGACCGCAGTCCAGTGGCTCAATCCGCTCCATCAGTTCCTGC +TCCCGCTCCACTGACCACCACCAGCCCGCTGCCCGCTCCAGGCCTCCTGATGCCCAGTGC +CAAGCGGCCTGCCTCCGACATGTCGCCGCCGCCGACGACAACTGTGATTGCGGAGCCCAA +GCCGAAGCTCTTCAAGCCCTACAAGACTGAGGCGTAAGCCCGCGATCCACACACACTCTC +TCCCCCCCCCCCATGCTCCCCCAAAAGATTGTACAAACTAGTCTTAGTCAGCCTCATCTA +TTTATTCCCGAAGATTGTACAGATTGTAGAGTAGCTAATTGTAGTCATAATTAAGGCGCA +AAATCAAATTAAGAAATAAATGCGAAAATAACATTGAAAATTATACGACACACACTGTTT +ATTTGAACTACCTGGTACCTGGTATTATCTACATATATATCAACATATATTCATATACCT +GAGCTGAATCGCTTTTATGGACCTGGCTCTCTGGATTAGCTGTGGAGAAACCTGGCGGGA +CAGCTGCGCTGCTGACTGCTGACCTTCCCAGGGAAACTGTGGCAAATCGAACGGAAGAGC +GGCAGAGCGGCAGAGCCGCAGAGCGTGCCGAGTGTCCTTCGGAGTGGCCACCCACAATCA +GGAGTAAGGGTTCCCGATGCGGCACCGGCGGGGAAGGGAACAACATCCACGGGGGATTGG +CACAAGCCAATGGCTTATTAATGCGATCCGAACGGATCCAAGCGGGAGGGCCCATTGCAA +ATGCAGAGCCACCCCGCTGGGCGGCGACCTTGGCGAGGAGTTGGGCCCACTCCCTGCCAC +GCGTCAGGTTGCATTGCCAATGGAATCAATTTCGGTGTCCTCGGATTGCATTGGGCTCGG +CTAATTAATTGTCACCTCACCCGTTGAGTGACTTTTTTGTTTGTTGGTCCTGGCCTTATG +TTTGCTTTCTCTTCTCCATCGCCAGGCAATTGTCATAATTGAGAAATCATCGGAAGGACG +AGCCAGTGCGAGAAAGAGACAGAGAGAGAGAGAGAGAGGGCTGGTGGTAGAGTCCTTCTC +GAATCAAATCCCAATTTAATTACCGTGCAAAACATGTGTTTCAATTGCTCAGCCGTGGTG +TAAAGGGGTGTCAATTGCTCATAATCCCACAGCTAAAAAACGAGAGTCAACCCAAAGGCA +GGATAAACGATGGGTAAACGATGCGAGCGAGGCACAAGGATGTAGATACCAGCCACGGAT +GCTGATACACCCAGCCAGCCACCCCTGATCCCGGCTGTGTCTAATATCTAATTGAGCGCC +TGGCATCAAAACATGTTTTGACACTTGCCAGCCGAGCCAAATGGGCATAAAAAATGGTCC +TTGAAGTGTTATCCTTGCAGTTGGAGTGCCACGCACACACCCCTTTCCTTTGCTGGTGTT +TTGCGGAGTTTCGTCTCGTGTTTTCACTTGGGGGATGCGAACACACAAGTGGCAGCATTT +TATGATTTGATAATAAAGATACGCGCCACGGTTCCGCCCTGGGGAATTCCCCAGCAGATT +CGGATACAGTAAATTAAAAAATAGCCAAACAGGAAATCTCT diff --git a/nim-skills/genomic-intelligence-nim/assets/demo/expression_hbb_k562.fa b/nim-skills/genomic-intelligence-nim/assets/demo/expression_hbb_k562.fa new file mode 100644 index 0000000..e870154 --- /dev/null +++ b/nim-skills/genomic-intelligence-nim/assets/demo/expression_hbb_k562.fa @@ -0,0 +1,116 @@ +>human|HBB|TSS-centered|chr11:5222473-5231670|GRCh38|RC(gene-sense) +GTTAACTGTTTTGATCACAACATTTTGAATTGACTGGCAGCAGAAGCTCTTTTATATCCATGTGTTTTCCTTAAGTCATT +ATACATAGTAGGCACTGAGAACTCTTTATATCTGAATAAGATATTTAGGAACCACTGGTTTACATATCAGAAGCAGAGCT +ACTCAGGGCATTTTGGGGAAGATCACTTTCACATTCCTGAGCATAGGGAAGTTCTCATAAGAGTAAGATATTAAAAGGAG +ATACTTGTGTGGTATTCGAAAGACAGTAAGAGAGATTGTAGACCTTATGATCTTGATAGGGAAAACAAACTACATTCCTT +TCTCCAAAAGTCAAAAAAAAAGAGCAAATATAGCTTACTATACCTTCTATTCCTACACCATTAGAAGTAGTCAGTGAGTC +TAGGCAAGATGTTGGCCCTAAAAATCCAAATACCAGAGAATTCATGAGAACATCACCTGGATGGGACATGTGCCGAGCAC +ACACAATTACTATATGCTAGGCATTGCTATCTTCATATTGAAGATGAGGAGGTCAAGAGATGAAAAAAGACTTGGCACCT +TGTTGTTATATTAAAATTATTTGTTAGAGTAGAGCTTTTGTAAGAGTCTAGGAGTGTGGGAGCTAAATGATGATACACAT +GGACACAAAAAATAGATCAACAGACACCCAGGCCTACTTGAGGGTTGAGGGTGGGAAGAGGGAGACGATGAAAAAGAACC +TATTGGGTATTAAGTTCATCACTGAGTGATGAAATAATCTGTACATCAAGACCCAGTGATATGCAATTTACCTATATAAC +TTGTACATGTACCCCCAAATTTAAAATGAAAGTTAAAACAAAGTATAGGAATGGAATTAATTCCTCAAGATTTGGCTTTA +ATTTTATTTGATAATTTATCAAATGGTTGTTTTTCTTTTCTCACTATGGCGTTGCTTTATAAACTATGTTCAGTATGTCT +GAATGAAAGGGTGTGTGTGTGTGTGAAAGAGAGGGAGAGAGGAAGGGAAGAGAGGACGTAATAATGTGAATTTGAGTTCA +TGAAAATTTTTCAATAAAATAATTTAATGTCAGGAGAATTAAGCCTAATAGTCTCCTAAATCATCCATCTCTTGAGCTTC +AGAGCAGTCCTCTGAATTAATGCCTACATGTTTGTAAAGGGTGTTCAGACTGAAGCCAAGATTCTACCTCTAAAGAGATG +CAATCTCAAATTTATCTGAAGACTGTACCTCTGCTCTCCATAAATTGACACCATGGCCCACTTAATGAGGTTAAAAAAAA +GCTAATTCTGAATGAAAATCTGAGCCCAGTGGAGGAAATATTAATGAACAAGGTGCAGACTGAAATATAAATTTTTCTGT +AATAATTATGCATATACTTTAGCAAAGTTCTGTCTATGTTGACTTTATTGCTTTTTGGTAAGAAATACAACTTTTTAAAG +TGAACTAAACTATCCTATTTCCAAACTATTTTGTGTGTGTGCGGTTTGTTTCTATGGGTTCTGGTTTTCTTGGAGCATTT +TTATTTCATTTTAATTAATTAATTCTGAGAGCTGCTGAGTTGTGTTTACTGAGAGATTGTGTATCTGCGAGAGAAGTCTG +TAGCAAGTAGCTAGACTGTGCTTGACCTAGGAACATATACAGTAGATTGCTAAAATGTCTCACTTGGGGAATTTTAGACT +AAACAGTAGAGCATGTATAAAAATACTCTAGTCAAGTGCTGCTTTTGAAACAAATGATAAAACCACACTCCCATAGATGA +GTGTCATGATTTTCATGGAGGAAGTTAATATTCATCCTCTAAGTATACCCAGACTAGGGCCATTCTGATATAAAACATTA +GGACTTAAGAAAGATTAATAGACTGGAGTAAAGGAAATGGACCTCTGTCTCTCTCGCTGTCTCTTTTTTGAGGACTTGTG +TGTGTGTGTGTGTGTGTGTGTGTGTGTGTGTTGTGGTCAGTGGGGCTGGAATAAAAGTAGAATAGACCTGCACCTGCTGT +GGCATCCATTCACAGAGTAGAAGCAAGCTCACAATAGTGAAGATGTCAGTAAGCTTGAATAGTTTTTCAGGAACTTTGAA +TGCTGATTTAGATTTGAAACTGAGGCTCTGACCATAACCAAATTTGCACTATTTATTGCTTCTTGAAACTTATTTGCCTG +GTATGCCTGGGCTTTTGATGGTCTTAGTATAGCTTGCAGCCTTGTCCCTGCAGGGTATTATGGGTAATAGAAAGAAAAGT +CTGCGTTACACTCTAGTCACACTAAGTAACTACCATTGGAAAAGCAACCCCTGCCTTGAAGCCAGGATGATGGTATCTGC +AGCAGTTGCCAACACAAGAGAAGGATCCATAGTTCATCATTTAAAAAAGAAAACAAAATAGAAAAAGGAAAACTATTTCT +GAGCATAAGAAGTTGTAGGGTAAGTCTTTAAGAAGGTGACAATTTCTGCCAATCAGGATTTCAAAGCTCTTGCTTTGACA +ATTTTGGTCTTTCAGAATACTATAAATATAACCTATATTATAATTTCATAAAGTCTGTGCATTTTCTTTGACCCAGGATA +TTTGCAAAAGACATATTCAAACTTCCGCAGAACACTTTATTTCACATATACATGCCTCTTATATCAGGGATGTGAAACAG +GGTCTTGAAAACTGTCTAAATCTAAAACAATGCTAATGCAGGTTTAAATTTAATAAAATAAAATCCAAAATCTAACAGCC +AAGTCAAATCTGCATGTTTTAACATTTAAAATATTTTAAAGACGTCTTTTCCCAGGATTCAACATGTGAAATCTTTTCTC +AGGGATACACGTGTGCCTAGATCCTCATTGCTTTAGTTTTTTACAGAGGAATGAATATAAAAAGAAAATACTTAAATTTT +ATCCCTCTTACCTCTATAATCATACATAGGCATAATTTTTTAACCTAGGCTCCAGATAGCCATAGAAGAACCAAACACTT +TCTGCGTGTGTGAGAATAATCAGAGTGAGATTTTTTCACAAGTACCTGATGAGGGTTGAGACAGGTAGAAAAAGTGAGAG +ATCTCTATTTATTTAGCAATAATAGAGAAAGCATTTAAGAGAATAAAGCAATGGAAATAAGAAATTTGTAAATTTCCTTC +TGATAACTAGAAATAGAGGATCCAGTTTCTTTTGGTTAACCTAAATTTTATTTCATTTTATTGTTTTATTTTATTTTATT +TTATTTTATTTTGTGTAATCGTAGTTTCAGAGTGTTAGAGCTGAAAGGAAGAAGTAGGAGAAACATGCAAAGTAAAAGTA +TAACACTTTCCTTACTAAACCGACATGGGTTTCCAGGTAGGGGCAGGATTCAGGATGACTGACAGGGCCCTTAGGGAACA +CTGAGACCCTACGCTGACCTCATAAATGCTTGCTACCTTTGCTGTTTTAATTACATCTTTTAATAGCAGGAAGCAGAACT +CTGCACTTCAAAAGTTTTTCCTCACCTGAGGAGTTAATTTAGTACAAGGGGAAAAAGTACAGGGGGATGGGAGAAAGGCG +ATCACGTTGGGAAGCTATAGAGAAAGAAGAGTAAATTTTAGTAAAGGAGGTTTAAACAAACAAAATATAAAGAGAAATAG +GAACTTGAATCAAGGAAATGATTTTAAAACGCAGTATTCTTAGTGGACTAGAGGAAAAAAATAATCTGAGCCAAGTAGAA +GACCTTTTCCCCTCCTACCCCTACTTTCTAAGTCACAGAGGCTTTTTGTTCCCCCAGACACTCTTGCAGATTAGTCCAGG +CAGAAACAGTTAGATGTCCCCAGTTAACCTCCTATTTGACACCACTGATTACCCCATTGATAGTCACACTTTGGGTTGTA +AGTGACTTTTTATTTATTTGTATTTTTGACTGCATTAAGAGGTCTCTAGTTTTTTATCTCTTGTTTCCCAAAACCTAATA +AGTAACTAATGCACAGAGCACATTGATTTGTATTTATTCTATTTTTAGACATAATTTATTAGCATGCATGAGCAAATTAA +GAAAAACAACAACAAATGAATGCATATATATGTATATGTATGTGTGTATATATACACACATATATATATATATTTTTTCT +TTTCTTACCAGAAGGTTTTAATCCAAATAAGGAGAAGATATGCTTAGAACCGAGGTAGAGTTTTCATCCATTCTGTCCTG +TAAGTATTTTGCATATTCTGGAGACGCAGGAAGAGATCCATCTACATATCCCAAAGCTGAATTATGGTAGACAAAACTCT +TCCACTTTTAGTGCATCAACTTCTTATTTGTGTAATAAGAAAATTGGGAAAACGATCTTCAATATGCTTACCAAGCTGTG +ATTCCAAATATTACGTAAATACACTTGCAAAGGAGGATGTTTTTAGTAGCAATTTGTACTGATGGTATGGGGCCAAGAGA +TATATCTTAGAGGGAGGGCTGAGGGTTTGAAGTCCAACTCCTAAGCCAGTGCCAGAAGAGCCAAGGACAGGTACGGCTGT +CATCACTTAGACCTCACCCTGTGGAGCCACACCCTAGGGTTGGCCAATCTACTCCCAGGAGCAGGGAGGGCAGGAGCCAG +GGCTGGGCATAAAAGTCAGGGCAGAGCCATCTATTGCTTACATTTGCTTCTGACACAACTGTGTTCACTAGCAACCTCAA +ACAGACACCATGGTGCATCTGACTCCTGAGGAGAAGTCTGCCGTTACTGCCCTGTGGGGCAAGGTGAACGTGGATGAAGT +TGGTGGTGAGGCCCTGGGCAGGTTGGTATCAAGGTTACAAGACAGGTTTAAGGAGACCAATAGAAACTGGGCATGTGGAG +ACAGAGAAGACTCTTGGGTTTCTGATAGGCACTGACTCTCTCTGCCTATTGGTCTATTTTCCCACCCTTAGGCTGCTGGT +GGTCTACCCTTGGACCCAGAGGTTCTTTGAGTCCTTTGGGGATCTGTCCACTCCTGATGCTGTTATGGGCAACCCTAAGG +TGAAGGCTCATGGCAAGAAAGTGCTCGGTGCCTTTAGTGATGGCCTGGCTCACCTGGACAACCTCAAGGGCACCTTTGCC +ACACTGAGTGAGCTGCACTGTGACAAGCTGCACGTGGATCCTGAGAACTTCAGGGTGAGTCTATGGGACGCTTGATGTTT +TCTTTCCCCTTCTTTTCTATGGTTAAGTTCATGTCATAGGAAGGGGATAAGTAACAGGGTACAGTTTAGAATGGGAAACA +GACGAATGATTGCATCAGTGTGGAAGTCTCAGGATCGTTTTAGTTTCTTTTATTTGCTGTTCATAACAATTGTTTTCTTT +TGTTTAATTCTTGCTTTCTTTTTTTTTCTTCTCCGCAATTTTTACTATTATACTTAATGCCTTAACATTGTGTATAACAA +AAGGAAATATCTCTGAGATACATTAAGTAACTTAAAAAAAAACTTTACACAGTCTGCCTAGTACATTACTATTTGGAATA +TATGTGTGCTTATTTGCATATTCATAATCTCCCTACTTTATTTTCTTTTATTTTTAATTGATACATAATCATTATACATA +TTTATGGGTTAAAGTGTAATGTTTTAATATGTGTACACATATTGACCAAATCAGGGTAATTTTGCATTTGTAATTTTAAA +AAATGCTTTCTTCTTTTAATATACTTTTTTGTTTATCTTATTTCTAATACTTTCCCTAATCTCTTTCTTTCAGGGCAATA +ATGATACAATGTATCATGCCTCTTTGCACCATTCTAAAGAATAACAGTGATAATTTCTGGGTTAAGGCAATAGCAATATC +TCTGCATATAAATATTTCTGCATATAAATTGTAACTGATGTAAGAGGTTTCATATTGCTAATAGCAGCTACAATCCAGCT +ACCATTCTGCTTTTATTTTATGGTTGGGATAAGGCTGGATTATTCTGAGTCCAAGCTAGGCCCTTTTGCTAATCATGTTC +ATACCTCTTATCTTCCTCCCACAGCTCCTGGGCAACGTGCTGGTCTGTGTGCTGGCCCATCACTTTGGCAAAGAATTCAC +CCCACCAGTGCAGGCTGCCTATCAGAAAGTGGTGGCTGGTGTGGCTAATGCCCTGGCCCACAAGTATCACTAAGCTCGCT +TTCTTGCTGTCCAATTTCTATTAAAGGTTCCTTTGTTCCCTAAGTCCAACTACTAAACTGGGGGATATTATGAAGGGCCT +TGAGCATCTGGATTCTGCCTAATAAAAAACATTTATTTTCATTGCAATGATGTATTTAAATTATTTCTGAATATTTTACT +AAAAAGGGAATGTGGGAGGTCAGTGCATTTAAAACATAAAGAAATGAAGAGCTAGTTCAAACCTTGGGAAAATACACTAT +ATCTTAAACTCCATGAAAGAAGGTGAGGCTGCAAACAGCTAATGCACATTGGCAACAGCCCCTGATGCATATGCCTTATT +CATCCCTCAGAAAAGGATTCAAGTAGAGGCTTGATTTGGAGGTTAAAGTTTTGCTATGCTGTATTTTACATTACTTATTG +TTTTAGCTGTCCTCATGAATGTCTTTTCACTACCCATTTGCTTATCCTGCATCTCTCAGCCTTGACTCCACTCAGTTCTC +TTGCTTAGAGATACCACCTTTCCCCTGAAGTGTTCCTTCCATGTTTTACGGCGAGATGGTTTCTCCTCGCCTGGCCACTC +AGCCTTAGTTGTCTCTGTTGTCTTATAGAGGTCTACTTGAAGAAGGAAAAACAGGGGTCATGGTTTGACTGTCCTGTGAG +CCCTTCTTCCCTGCCTCCCCCACTCACAGTGACCCGGAATCTGCAGTGCTAGTCTCCCGGAACTATCACTCTTTCACAGT +CTGCTTTGGAAGGACTGGGCTTAGTATGAAAAGTTAGGACTGAGAAGAATTTGAAAGGCGGCTTTTTGTAGCTTGATATT +CACTACTGTCTTATTACCCTGTCATAGGCCCACCCCAAATGGAAGTCCCATTCTTCCTCAGGATGTTTAAGATTAGCATT +CAGGAAGAGATCAGAGGTCTGCTGGCTCCCTTATCATGTCCCTTATGGTGCTTCTGGCTCTGCAGTTATTAGCATAGTGT +TACCATCAACCACCTTAACTTCATTTTTCTTATTCAATACCTAGGTAGGTAGATGCTAGATTCTGGAAATAAAATATGAG +TCTCAAGTGGTCCTTGTCCTCTCTCCCAGTCAAATTCTGAATCTAGTTGGCAAGATTCTGAAATCAAGGCATATAATCAG +TAATAAGTGATGATAGAAGGGTATATAGAAGAATTTTATTATATGAGAGGGTGAAACCCTCAAAATGAAATGAAATCAGA +CCCTTGTCTTACACCATAAACAAAAATAAATTTGAATGGGTTAAAGAATTAAACTAAGACCTAAAACCATAAAAATTTTT +AAAGAAATCAAAAGAAGAAAATTCTAATATTCACGTTGCAGCCGTTTTTTGAATTTGATATGAGAAGCAAAGGCAACAAA +AGGAAAAATAAAGAAGTGAGGCTACATCAAACTAAAAAATTTCCACACAAAAAACAAAACAATGAACAAATGAAAGGTGA +ACCATGAAATGGCATATTTGCAAACCAAATATTTCTTAAATATTTTGGTTAATATCCAAAATATATAAGAAACACAGATG +ATTCAATAACAAACAAAAAATTAAAAATAGGAAAATAAAAAAATTAAAAAGAAGAAAATCCTGCCATTTATGGCAGAATT +GATGAACCTGGAGGATGTAAAACTAAGAAAAATAAGCCTGACACAAAAAGACAAATACTACACAACCTTGCTCATATGTG +AAACATAAAAAAGTCACTCTCATGGAAACAGACAGTAGAGGTATGGTTTCCAGGGGTTGGGGGTGGGAGAATCAGGAAAC +TATTACTCAAAGGGTATAAAATTTCAGTTATGTGGGATGAATAAATTCTAGATATCTAATGTACAGCATCGTGACTGTAG +TTAATTGTACTGTAAGTATATTTAAAATTTGCAAAGAGAGTAGATTTTTTTTTTTTTTTAGATGGAGTTTTGCTCTTGTT +GTCCAGGCTGGAGTGCAATGGCAAGATCTTGGCTCACTGCAACCTCCGCCTCCTGGGTTCAAGCAAATCTCCTGCCTCAG +CCTCCCGAGTAGCTGGGATTACAGGCATGCGACACCATGCCCAGCTAATTTTGTATTTTTAGTAGAGACGGGGTTTCTCC +ATGTTGGTCAGGCTGATCCGCCTGCCTCGGCCACCCAAAGGGCTGGGATTACAGGCGTGAGCCACCGGGCCTGGCCGAGA +GTAGATCTTAAAAGCATTTACCACAAGAAAAAGGTAACTATGTGAGATAATGGGTATGTTAATTAGCTTGATTGTGGTAA +TCATTTCACAAGGTATACATATATTAAAACATCATGTTGTACACCTTAAATATATACAATTTTTATTTGTGAATGATACC +TCAATAAAGTTGAAGAATAATAAAAAAGAATAGACATCACATGAATTAAAAAACTAAAAAATAAAAAAATGCATCTTGAT +GATTAGAATTGCATTCTTGATTTTTCAGATACAAATATCCATTTGACTGTTTACTCTTTTCCAAAACAATACAATAAATT +TTAGCACTTTATCTTCATTTTCCCCTTCCCAATCTATAATTATATATATATATATTTTAGATATTTTGTATAGTTTTACT +CCCTAGATTTTCTAGTGTTATTATTAAATAGTGAAGAAATGTTTACACTTATGTACAAAATGTTTTGCATGCTTTTCTTC +ATTTCTAACATTCTCTCTAAGTTTATTCTATTTTTTTCTGATTATCCTTAATATTATCTCTTTCTGCTGGAAATACATTG +TTACTTTTGGTTTATCTAAAAATGGCTTCATTTTCTTCATTCTAAAATCATGTTAAATTAATACCACTCATGTGTAAGTA +AGATAGTGGAATAAATAGAAATCCAAAAACTAAATCTCACTAAAATATAATAATGTGATATATAAAAATATAGCTTTTAA +ATTTAGCTTGGAAATAAAAAACAAACAGTAATTGAACAACTATACTTTTTGAAAAGAGTAAAGTGAAATGCTTAACTGCA +TATACCACAATCGATTACACAATTAGGTGTGAAGGTAAAATTCAGTCACGAAAAAACTAGAATAAAAATATGGGAAGACA +TGTATATAATCTTAGAGATAACACTGTTATTTAATTATCAACCCAAAGTAGAAACTATCAAGGGAGAAATAAATTCAG diff --git a/nim-skills/genomic-intelligence-nim/assets/demo/promoter_tp53.fa b/nim-skills/genomic-intelligence-nim/assets/demo/promoter_tp53.fa new file mode 100644 index 0000000..d40e54e --- /dev/null +++ b/nim-skills/genomic-intelligence-nim/assets/demo/promoter_tp53.fa @@ -0,0 +1,431 @@ +>human|TP53|ENSG00000141510|chr17:7661779-7687546|GRCh38|strand:-1|gene-sense +GGATTGGGGTTTTCCCCTCCCATGTGCTCAAGACTGGCGCTAAAAGTTTTGAGCTTCTCA +AAAGTCTAGAGCCACCGTCCAGGGAGCAGGTAGCTGCTGGGCTCCGGGGACACTTTGCGT +TCGGGCTGGGAGCGTGCTTTCCACGACGGTGACACGCTTCCCTGGATTGGGTAAGCTCCT +GACTGAACTTGATGAGTCCTCTCTGAGTCACGGGCTCTCGGCTCCGTGTATTTTCAGCTC +GGGAAAATCGCTGGGGCTGGGGGTGGGGCAGTGGGGACTTAGCGAGTTTGGGGGTGAGTG +GGATGGAAGCTTGGCTAGAGGGATCATCATAGGAGTTGCATTGTTGGGAGACCTGGGTGT +AGATGATGGGGATGTTAGGACCATCCGAACTCAAAGTTGAACGCCTAGGCAGAGGAGTGG +AGCTTTGGGGAACCTTGAGCCGGCCTAAAGCGTACTTCTTTGCACATCCACCCGGTGCTG +GGCGTAGGGAATCCCTGAAATAAAAGATGCACAAAGCATTGAGGTCTGAGACTTTTGGAT +CTCGAAACATTGAGAACTCATAGCTGTATATTTTAGAGCCCATGGCATCCTAGTGAAAAC +TGGGGCTCCATTCCGAAATGATCATTTGGGGGTGATCCGGGGAGCCCAAGCTGCTAAGGT +CCCACAACTTCCGGACCTTTGTCCTTCCTGGAGCGATCTTTCCAGGCAGCCCCCGGCTCC +GCTAGATGGAGAAAATCCAATTGAAGGCTGTCAGTCGTGGAAGTGAGAAGTGCTAAACCA +GGGGTTTGCCCGCCAGGCCGAGGAGGACCGTCGCAATCTGAGAGGCCCGGCAGCCCTGTT +ATTGTTTGGCTCCACATTTACATTTCTGCCTCTTGCAGCAGCATTTCCGGTTTCTTTTTG +CCGGAGCAGCTCACTATTCACCCGATGAGAGGGGAGGAGAGAGAGAGAAAATGTCCTTTA +GGCCGGTTCCTCTTACTTGGCAGAGGGAGGCTGCTATTCTCCGCCTGCATTTCTTTTTCT +GGATTACTTAGTTATGGCCTTTGCAAAGGCAGGGGTATTTGTTTTGATGCAAACCTCAAT +CCCTCCCCTTCTTTGAATGGTGTGCCCCACCCCGCGGGTCGCCTGCAACCTAGGCGGACG +CTACCATGGCGTGAGACAGGGAGGGAAAGAAGTGTGCAGAAGGCAAGCCCGGAGGTATTT +TCAAGAATGAGTATATCTCATCTTCCCGGAGGAAAAAAAAAAAGAATGGGTACGTCTGAG +AATCAAATTTTGAAAGAGTGCAATGATGGGTCGTTTGATAATTTGTCGGAAAAACAATCT +ACCTGTTATCTAGCTTTGGGCTAGGCCATTCCAGTTCCAGACGCAGGCTGAACGTCGTGA +AGCGGAAGGGGCGGGCCCGCAGGCGTCCGTGTGGTCCTCCGTGCAGCCCTCCGGCCCGAG +CCGGTTCTTCCTGGTAGGAGGCGGAACTCGAATTCATTTCTCCCGCTGCCCCATCTCTTA +GCTCGCGGTTGTTTCATTCCGCAGTTTCTTCCCATGCACCTGCCGCGTACCGGCCACTTT +GTGCCGTACTTACGTCATCTTTTTCCTAAATCGAGGTGGCATTTACACACAGCGCCAGTG +CACACAGCAAGTGCACAGGAAGATGAGTTTTGGCCCCTAACCGCTCCGTGATGCCTACCA +AGTCACAGACCCTTTTCATCGTCCCAGAAACGTTTCATCACGTCTCTTCCCAGTCGATTC +CCGACCCCACCTTTATTTTGATCTCCATAACCATTTTGCCTGTTGGAGAACTTCATATAG +AATGGAATCAGGCTGGGCGCTGTGGCTCACGCCTGCACTTTGGGAGGCCGAGGCGGGCGG +ATTACTTGAGGATAGGAGTTCCAGACCAGCGTGGCCAACGTGGTGAATCCCCGTCTCTAC +TAAAAAATACAAAAATTAGCTGGGCGTGGTGGGTGCCTGTAATCCCAGCTATTCGGGAGG +GTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCAGAGGTTGCAGTGAGCCAAGATCGTGC +CACTACACTCCAGCCTGGGCGACAAGAACGAAACTCCGTCTCAAAAAAAAGGGGGGAATC +ATACATTATGTGCTCATTTTTGTCGGGCTTCTGTCCTTCAATGTACTGTCTGACATTCGT +TCATGTTGTATATATCAGTATTTTGCTCCTTTTCATTTAGTATAGTCCATCGATTGTATA +TCCGTCCTTTTGATGGCCTTTTGAGTTGTTTCCCATTTGCGGTTATGAAATAAAGCTGCT +ATAAACATTCTTGTACAATTCTTTTTGTGATCATATGTTTTCGTGTTTCTTGGAGAAATA +CTTAGGAGGGGAATTGCGAGTTTGGAAGTAAAAAGTAGCTGTATTTTGAACTTTTTCAGA +AGCTCTGAGTTTTCCAGAGCGGTTGTACCATTTTACACTCCAACTAGCAAGGTATGGGAG +TTATTATGGTTGTGCCACAGCCTTCCGGACATTAGGTATTGTCAGTCTTTCTAATGTGGT +ATATCCTTGTGGTTGTAATTTACAGTTCTCTATTGACTAAGGATGTTCAGCATTTTTTCA +TGTGCCTATTGGCCATTCGTATTTTGTTTGTAAAGTAGCTCTTCGAGTCTTTTACCTGTT +ATTTTGGTTTTTTGTTTGTTTTTATTGTTCAGTTGTGGGACTGCTTTATACATTCTGGAT +ACAAGTCCTTTATCAGATCCATGTGTCGTGAATGTTTTCTTCTGATCTGTTGCTTGCCTA +TTTGTTTGCTTTACAGAGTTTACAGTATCTTAAGAGGAGTGGATTTATCTTTTTTATGTT +CAGTATTTGCCTTGTCCTGTTTAGGACATCTTTTTTTTTTTTTTTAACCCCAGGGTCATG +AAGATATTATCTTACATTTTCTTTTAGGACCTTTATGGTTGTAAGTTTTACAGTAAGGTC +CTTGAGCCATTAATTAATTCTTAAAATTAATTGTTTATGGTGTGAGGTGTAGGAGTCAGT +CTCTGGTATCTTTCCTGTATGGAAATCCAGTTATTCTGTCTCCACTTGTTGAAATAGGCT +TCCTTTCTCTACTGAATGCTTTTAATTTTAATTATTTTACAGTTGGAGTATAGGGCTACC +ATTTTAGTGCTATTTTCTTTTTTTCTTTGTTAATTTTTGAGACAGGGACTCACACTGTTG +CCCAGGCTAGAGTACAATGGCACAATCAAGGCTTACTGCAGCCTCGAACCCCTGGGCTCA +AGCAGTCCTCTAGCAGCCTCACGAGTAGCTGGGATTACTCCACCACACCCAGCTAACTAT +TTTATTTTTTTGTATTGACAGGATCTCACTATGTTGCCCAGGCTGGTCTCAAACTGCTGG +CCTCAAGCTTTCATCCCATCTCGGCCTCCCAAAGTGCTGGGATTACAGGTGTGAGCCACC +ATGCCTGACCTCTTAGTGCTATTTTCTATTTATCTCCTCTGTTCTCTGCTCTCTTTAAAC +GTTGGAGGAAGAAACAGTACCCATCTTACACAAACTCTTCAGAAAACAGAGGAACAGACT +GGGCGCGGTGGCTCATACCTGTAATCTCAGCACTTTGGTACGCTGAGGCAGGGGATCATT +TGAGGTCGGGAGTTCGAGACCAGCCTGGCCAACACGGCGAAACCCCATCTCTACTAAAAA +TACAAAAAGTAGCTAGGCGTGGTGACACATACCTGTAATGCCAGTTACTCAGGAGGCTGA +GGCACAAGAATCCCTTGAACCTGGGAAGCGGAGGTTGCAGTGAGCCGAGATTGCGCCACT +GCACTCCAGCCTGGGCAACAGAGTGAGACCCTGTCTCAGAAAAAAAAAGAAAGAAAGAAA +AAATAGAGGAATATTTCCCAACTTGTTTTCGAAGCCAGCATAATCCTGGTACCAAAACCA +AACAAGGACATTATAAGAAAAGAAAATATAGACCAATATTCCTGTTAGCATAGACATGCA +ACAGCTAACCAATTTTAGCAAACCAAACCTGGTAATATAGAAAAAAGGATAAATAGGCCA +GTCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCTGAGGCAGGCAGATCACT +TGAGGTCAGGAGTTTGAGACCAGCCTGACCAACATGGTGAAACCCCGTTTCTAATAAAAA +TACAAAAATCAGGCTGGGCACGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCG +AGGTGGGCAGATCACGAGGTCAGGAGTTCAAGACCAGCCTGACCAATGTGGTGAAACGCC +ATCTCTACTAAAAATACAAAAATCAGCCGGTGTGGTGGCACCTGCCTGTAATCCCAGCTA +CTCAGGAGGCTGAGGCAGAATTGCTTGAACCCGGGAGGCAGAGGTTGCAGTGAGCCAAGA +TCGTGCCACTGCACTCCAGCCTGGGCGACAGAGCAAGACTTCATCTCAAAAAAAAAAAAA +AATTAGCTGGGCATGGTGGTGGGCACCTGAAATCCCAGCTACTCGGGAGTCTGAGGCAGG +AGAATCGCTTGAACCCAGGAGGCAGAAGTTGCACTGAGCTGGGATCACACCATTGCACTC +CAGCCTGGGCAACAGAGTGAGACTCCATCTCAAAAAAAGAAAAAGAAAAAGGATAAATAC +ATTCTAACCAAATAATGTTTATCTCATGATTGTAGCTGATTCAACATTCAAAAATTGGCC +TGGTGCAGTAGCTCAGGCCTGTAATCCCAACATTTTAGGAGGCTGAGGCAGGAAGATCTC +TTGAGCCCAGGATTTCAAGACCAGCCTGGGCAACATAGTCAGACTGGTCTTTACTGGGGG +GAAAAAAATCAGTCTGTGTAATTCACCACATTAACAAAGGGAAACATAAAAACCCTATGA +TCATTTCAACAGATGTAGCAAAAGCAGTTAATGATATTCAACACATATGCATGATTACAA +ACCAACCAACCTCCTAGCAAACTAGGGAAAGGAAACTTAACCTAGTTTGATAACAGGGCG +TCCACAGTCGGAGTTCCACTAGCAGCATACATAATGGTAGAAAACTCAGTGCTGCCGGGC +GCGGTGGCTCACGCCTGTAATGCCAGCACTTTGGGAGGCCTAGGCGGGCGGATCACGAGG +TCAGGAGATCGAGACTGTCCTGACTAGCATGCTGAAACCCCGTCTCTACTAAAAATACAA +AAACAAAAAATTAGCCGGGCATGGTGGCGGGCGCCTATAGTCCCAGCTACTCGGGAGGCT +GAGGCGAGAGAATGGCGTGAACCCGGGAGGCGGAGCTTGCAGAGCCTAGATCGTGCCACT +GCACTCCAGCCTGGGTGACAGAGTGAGACTTCGTCTCAAAAAAAAAAAAAAAAAAAAAAG +AAAAGAAAACTCAACGCTTTTTCCTCTAAGATCAGGAACTAGAAAAGGATTTGACTCTCA +CAACGTTGATACCATACTGGAGGTTTTAACCAGGCAAGAAAAAGAAATAATGAGGGCCGG +GTGCGGTGGCTCAGGCCTGTAATCCCAGCACTTTGGGAAGCCGAGACGGGTGGATCACGA +GGTCAGGAGATCGAGACCATCCTGGCTAACACGGTGAAACCCTGTCTCTACTAAATATAC +AAAAAATTAGCCGGGCGTAGTGGCGGGCGCCTGTAGTCCCAGCTACTCGGGAGGCTGAGG +CAGGAGAATGGCGTGAACTCAGGGGGCGGAGCTTGCAGTGAGCTGAGATCGAGCCACTGC +ACTCCAGCCTGGGCGACAGAGCAAGACTGTGTCTCAAAAAAAAAAAAAGAAAAAGAAATA +ATGATTAGTGGCCCGATGTCTCACGCCTATAATCCCAGCACTTTGGGAGGCCGAGGTGGG +CAGATCACCTGAGGTCTGGAGTTGGAGACCAGCCTGACAAAGATGGTGAAACCTCGTCTC +TATTAAAATATTAAAAAAATAGCCAGGCGTTGGCCGGGTACAGTGGCTCATGCCTGTAAT +CCCAGCACTTTGGGAGGCCGAGGTGGGTGGATCACCTGAGGTCAGGAGTTCAACACCAGC +CTGGCCAACATGGTGAAACCCCATCTCTACTAAAAATACAAAAATTAGCCGGGCGTAGTG +GCGGGCGCCTGTAATCCCAGCTACTTGGGAGGCTTAGGCAGGAGAATCGCTTGAACCTGG +GAGGCGGAGGTTGTAGTGAGCCGAGATTGCACCATTGCACTCCAGCCTGGGTGACAAAAG +CAAAAACTCCGTCTCAAAAAAAAAAGAATTAGCCAGGGGTAGTGGTGAACGCCTGTAGTC +CCAGCTACTCAGGAGGCAGAGGCAGGAGAATCACTTGAACCCAGGAGGCAGAGGTTGCAG +TGAGCCGAGATTGTCCCATTGCACTCCAGCCTAGGCGACAAGAGCAAAATTCCATGTCAA +AAAAAAAAAAAAAAAAGGAAAGAAAAAAAATAACGATTAGAAAGGAAGAAATAAAACACA +TTCACAGCCAGTATGATTCTATACATACATGTCCTAATGGGGCCAGGCGTGGTGGCTCAT +GCCTGTAATCCTAGCACTTTTAGGAGGCTGAGGCAGGTGGCTTCCCTGGGACCAGCCTGG +CCAACATGGTGAAACCCCAACTCTAATAAAAATACAAAAAATCAGCCAGGCGTGGTGACG +GGCACCTCTAATCCCAGCTACTCAGGAGGCTGAGGCAGGAGAATTGCTTGGACCTGGGAG +GCAGAGGTTGCAGTGAGCCGAGATCGCGCTATTGCACTCCAGCCTGGGCAACAAGAGTGA +AACTCCGGCAGGGTGTGGTGGCTTACGCCTGTAATCCCAGCACTTCGGGAGGCTGAGGCA +GGCCGATCACCTGAGGTCAGGAGTTTGAGACCAACCTAACATGGTGAAACCCCGTCTCTA +CTAAAAATACAAGAATTAGCTGGGTGTAGTGGTGGGCGCCTGTAATCCCAGCTACTTGGG +AGGCTGAGACAGAAGAATTGCTTGAACCCAGGAGGTGGAGGTTGCAGTGAGCTGAGATCA +TGCCATTGCACACCACGCCGGGCAACAGAGCGAGATTCCGTCTCAAAAAAAAAAAAAAAG +AGTGAAACTCTATCTCAAAAAAAAAAAAAAGTCCTAATGGAAAATCCATAAAAAGCTACC +AAAACTAATAAATAAATATAGCAGGGTTGCAGGTTACAGGGCAATATAGTTATCCCTCTA +TCTGTAGGGGCTTGGTTCTGGGACTCCTCACACACCAAACCCACAGATGTCTAAGTCCCA +TATATAAGACGGTATAGTATTTGGATTTAACCTACACATATCCTCCCATATAGTTTAAAT +TATCTCTAGATTACTTACATTACCCCCATACAATGAAAATGCTAATGTACATGCAAGTAT +GTATGTAAGTACTTGTACTATATTGTTTAGGGAATCACTGGACATATAGGCCTTCAAGAC +TGATACCAGCAGCCACTGTTAAGATTCTGGTCAGGCCTGCCCCTGTTTGGGGTCTCAGTT +GATCTCATTGCCTTCCCACCCAGCCAAGGGCACCTGCATTTCTCTTGGCTCCCTGGCCAT +TTGGAAGGCCTAGTTCAGCCTGGCACATTTGTATCCTGGCCCACTGATGCTGGTACCCCT +GGGAAGGTCCTGCTCTGAAAAACACGGAGATTTTAGTTGCTACTGAAGATTTGAGAGATA +AAGACAGGGAGACCTGTCTGTAGACCTGTGTCCCTCCAAGTGGGATTGAGACTTTGGGCC +CCCCATTTCAGGACAGCACCTCCTGGCCTGTTGACTGAATAGATCCCTGAAGGAGGTGTA +CTTGCATTAATGGAGTGGGGGTGGGAGCAGTACCACAGATCCGCACTAACAATCACACAG +TTCTCTCTAGAATAATAATATAGAACAAGTGAAATAGAACAATTGCAGAAAGAGCTAACC +TTTGTTGAGCTCTTACTGTGTGCCCAGCACTTTCCTCAACTCTACATTTCCCATAATACA +CAGAGTACTAGGTAGGCCAGGCTTGGTGGCTCACGCCTGTAATCCCAGCACTTTAGGAGG +CCAAGGGGGGTGGATCACCTGAGGTCGGGAGTTCAAGACCAGCCTGACCAACATGGTGAA +ACCCCGTCTCTACTAGAAGTACAAAATTAGCCAGGTGTGGTGGCACATGCTTGTAGTCCT +AGCTACTCAGCAGGCTGAGGCAGGAGAATCATTTGAATCCGGGAGGAGGTTGCAGTAAGC +GGAGATAGTGCCACTGTACTCCAGCCTGGGCAATAAGAGCTGAGACTCCGTCTCAAAATA +AAATAAAATAAAATAAAATAAAATAAAATAAAATAAAAAAAGAAAAGAGCCTGCCATTAA +AGGAGCTGTTTGGTAGGGGATGTTTTGTCAGTGCAAACAACAGAAAAGTGGGCTGGGCAC +AGTGGTTCATGCCTGTAATCCCAGCACTTTGGGAGGCCAAGGCGGGCGGATCACCTGAAG +TTGGGAGTTCAAGACCAGCCTGACCAATATGGAGAAACCCCGTCTCTACTAAAAATACAA +AATTAGCCGGGCGCAGTGGCGCATGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGG +AGAATCGCTTGAACCTGGGAGGCAGAGGTTGCGGTGAGCCGAGATCGCACCATTGCACTC +CAGCCTGGACGAGAGCAAAACTCTGTCTCAAAAAAAAAAAAAAACAGAAAAGTGTAACAA +ACACTTACAGTAGGCATGTTTCTTAGCAAATCTGATGACAAATTTGGCATAAAGAAAGAG +AGCATCCCTGAAAAAAAAAAAAAGAAAAAGAAAGAGAGCATCCTGCCTGGGCAACATAGT +GAAACCCTGCCTCTACAAAAAAACTCAAAAATTGGCCGGGTGCAGTGGCTCACACCTGTA +ATCCCAGCACTTTGGGAGTCGGAGGCGGGAGGATCACCTGAGGTCAGGAGTTCGAAACCA +GCCTGGCCAACATGGCAAAACCCCATCTCTACTAAAAATACAAAAAATTAATCAGGCGCA +TTGGTGGGCGCCTGTAATCCCAGCTACTCAGGAAGTTGAGGCAAGAGGATCGCTTGAATC +TGGGAGGTGGAGGTTACAGTGAGTCGAGATCACACCACTGCACTCTAGCCTGGGTGACAG +GGCGAGACTCCGTCTCCAAAAAAAAAAAGAAAAAGAAAAAGACTAAAAAATTAGCCAGGC +AGGCCTCTGTGGTCCCAGCTACTTGGGAGGCTGAGGCAGGAGAATCACTGAGCCCAGGAG +TCCGAGGCTGTAGTGAGCCATGATTGCACCACTGTACCCTAGCTTGGGCAACAAAGCAAG +ACCCTGCCTCAAAAGAAAAAAGAAAGAAAGAAAGAACATGGCGGGCCAGGCACAGTGGCT +CACACCTGTAATCCCAGCGCTTTGAGAGGCCGAGGCAGGTGGATCACAAGGTCAGGAGTT +CCACACCAGCCTGGCCAACATGGTGAAACCCTGTCTCTACTAAAAATACAAAAAATCAGC +CAGGCATGGTGGCAGGGGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATTG +CTTGAAACCAGAAGGCAGAGGTTGCAGTGAGCCTAGACTGCACCACTGCACTCCAGCCTG +GGCGAAAAGAGCCAAACTCCATCTCAAAAAACAAACAAAAAAACAAAACAAAAGAAAACA +TGGCAAAGCCTTTGAAAGCTTGTCTGGGAGAAGGTGCGATGATAGTTGCATAACTTCGTG +CAAGATGCTGGTCCACACAGGGGCTGCCCCTTGCTCTTTCTCGCTCTCTTAACCTCTCAT +ATAACAGGCTTGTGTGTTATTCACATTTATTGAGCCCAAGCAGGTGCAAGGCATTGTGAT +CTAATACTTTGGTCAGCAAGACAACAAGATAGATCACTGCCCTGCCCTTAGGAAGTGTAT +ATGCTATTAGAGGAAACAGATAAAATAAACAAGGAAAAGTATCAGACAATGTAAGTGCTA +TGAGAATGCAAATGAGGTGATGTGAATTAAAATAGGATGACTTAAAGTCTGCACGGGAAG +GAGCCTACCCCCATGTTCCTGGCTAGCCAAGGAACCACCAGTTGATTAGCAGAGAAGGGC +AGCCAGTCTAGCTAGAGCTTTTGGGGAAGAGGGAGTGGTTGTTAAGAGATGAGATTAAAG +AAGCCGAGACGGGCCATTCGTGAGGGGTTTGTAATGCAGGGCTGAGGAGTGTCCGAAGAG +AATGGGCAGGTGAGCGGTGAGACAGTTGTTCTTCCAGAAGCTTTGCAGTGAAAGGAATCA +AAGAAATGGAGCCGTGTATCAGGTGGGGAAGGGTGGGGGCCAAGGGGGTGTCCTTCCCCA +TACAGAGATTGCAGGCTGAGAATGACTATATCCTTGTTAACAGGAGGTGGGAGCAGGGCA +CGGTAGCTCACACCTGTAATCTTGGCACTTTAGGAGGCTGAGGCGGGCCGATCACCTGAA +GTAAGGAGTTCGAGACCAGCCTGGCCAACATGCAAAGCCCTGTCTCTACTAAAAATACAA +AAATTAGCTGGGTGTGGTGGTACTCGCCTGTAATCCCAGCTACTCGGGAGACTGAGGCAG +GAGAATGGCTTGAACCCGGAAGGTAGAGGTTGCAGTGAGCTGAGATCATGCCACTGTGCT +CCAGCCTAGGTGACAGAGAGAGACTCCATCTCAAAAAAAAAAAAAAAATACAGGAAGGGA +GTTGGGAATAGGGTGCACATTTAGGAAGTCTTGGGGATTTAGTGGTGGGAAGGTTGGAAG +TCCCTCTCTGATTGTCTTTTCCTCAAAGAAGTGCATGGCTGGTGAGGGGTGGGGCAGGAG +TGCTTGGGTTGTGGTGAAACATTGGAAGAGAGAATGTGAAGCAGCCATTCTTTTCCTGCT +CCACAGGAAGCCGAGCTGTCTCAGACACTGGCATGGTGTTGGGGGAGGGGGTTCCTTCTC +TGCAGGCCCAGGTGACCCAGGGTTGGAAGTGTCTCATGCTGGATCCCCACTTTTCCTCTT +GCAGCAGCCAGACTGCCTTCCGGGTCACTGCCATGGAGGAGCCGCAGTCAGATCCTAGCG +TCGAGCCCCCTCTGAGTCAGGAAACATTTTCAGACCTATGGAAACTGTGAGTGGATCCAT +TGGAAGGGCAGGCCCACCACCCCCACCCCAACCCCAGCCCCCTAGCAGAGACCTGTGGGA +AGCGAAAATTCCATGGGACTGACTTTCTGCTCTTGTCTTTCAGACTTCCTGAAAACAACG +TTCTGGTAAGGACAAGGGTTGGGCTGGGGACCTGGAGGGCTGGGGACCTGGAGGGCTGGG +GGGCTGGGGGGCTGAGGACCTGGTCCTCTGACTGCTCTTTTCACCCATCTACAGTCCCCC +TTGCCGTCCCAAGCAATGGATGATTTGATGCTGTCCCCGGACGATATTGAACAATGGTTC +ACTGAAGACCCAGGTCCAGATGAAGCTCCCAGAATGCCAGAGGCTGCTCCCCCCGTGGCC +CCTGCACCAGCAGCTCCTACACCGGCGGCCCCTGCACCAGCCCCCTCCTGGCCCCTGTCA +TCTTCTGTCCCTTCCCAGAAAACCTACCAGGGCAGCTACGGTTTCCGTCTGGGCTTCTTG +CATTCTGGGACAGCCAAGTCTGTGACTTGCACGGTCAGTTGCCCTGAGGGGCTGGCTTCC +ATGAGACTTCAATGCCTGGCCGTATCCCCCTGCATTTCTTTTGTTTGGAACTTTGGGATT +CCTCTTCACCCTTTGGCTTCCTGTCAGTGTTTTTTTATAGTTTACCCACTTAATGTGTGA +TCTCTGACTCCTGTCCCAAAGTTGAATATTCCCCCCTTGAATTTGGGCTTTTATCCATCC +CATCACACCCTCAGCATCTCTCCTGGGGATGCAGAACTTTTCTTTTTCTTCATCCACGTG +TATTCCTTGGCTTTTGAAAATAAGCTCCTGACCAGGCTTGGTGGCTCACACCTGCAATCC +CAGCACTCTCAAAGAGGCCAAGGCAGGCAGATCACCTGAGCCCAGGAGTTCAAGACCAGC +CTGGGTAACATGATGAAACCTCGTCTCTACAAAAAAATACAAAAAATTAGCCAGGCATGG +TGGTGCACACCTATAGTCCCAGCCACTTAGGAGGCTGAGGTGGGAAGATCACTTGAGGCC +AGGAGATGGAGGCTGCAGTGAGCTGTGATCACACCACTGTGCTCCAGCCTGAGTGACAGA +GCAAGACCCTATCTCAAAAAAAAAAAAAAAAAAGAAAAGCTCCTGAGGTGTAGACGCCAA +CTCTCTCTAGCTCGCTAGTGGGTTGCAGGAGGTGCTTACGCATGTTTGTTTCTTTGCTGC +CGTCTTCCAGTTGCTTTATCTGTTCACTTGTGCCCTGACTTTCAACTCTGTCTCCTTCCT +CTTCCTACAGTACTCCCCTGCCCTCAACAAGATGTTTTGCCAACTGGCCAAGACCTGCCC +TGTGCAGCTGTGGGTTGATTCCACACCCCCGCCCGGCACCCGCGTCCGCGCCATGGCCAT +CTACAAGCAGTCACAGCACATGACGGAGGTTGTGAGGCGCTGCCCCCACCATGAGCGCTG +CTCAGATAGCGATGGTGAGCAGCTGGGGCTGGAGAGACGACAGGGCTGGTTGCCCAGGGT +CCCCAGGCCTCTGATTCCTCACTGATTGCTCTTAGGTCTGGCCCCTCCTCAGCATCTTAT +CCGAGTGGAAGGAAATTTGCGTGTGGAGTATTTGGATGACAGAAACACTTTTCGACATAG +TGTGGTGGTGCCCTATGAGCCGCCTGAGGTCTGGTTTGCAACTGGGGTCTCTGGGAGGAG +GGGTTAAGGGTGGTTGTCAGTGGCCCTCCAGGTGAGCAGTAGGGGGGCTTTCTCCTGCTG +CTTATTTGACCTCCCTATAACCCCATGAGATGTGCAAAGTAAATGGGTTTAACTATTGCA +CAGTTGAAAAAACTGAAGCTTACAGAGGCTAAGGGCCTCCCCTGCTTGGCTGGGCGCAGT +GGCTCATGCCTGTAATCCCAGCACTTTGGGAGGCCAAGGCAGGCGGATCACGAGGTTGGG +AGATCGAGACCATCCTGGCTAACGGTGAAACCCCGTCTCTACTGAAAAATACAAAAAAAA +ATTAGCCGGGCGTGGTGCTGGGCACCTGTAGTCCCAGCTACTCGGGAGGCTGAGGAAGGA +GAATGGCGTGAACCTGGGCGGTGGAGCTTGCAGTGAGCTGAGATCACGCCACTGCACTCC +AGCCTGGGCGACAGAGCGAGATTCCATCTCAAAAAAAAAAAAAAAAGGCCTCCCCTGCTT +GCCACAGGTCTCCCCAAGGCGCACTGGCCTCATCTTGGGCCTGTGTTATCTCCTAGGTTG +GCTCTGACTGTACCACCATCCACTACAACTACATGTGTAACAGTTCCTGCATGGGCGGCA +TGAACCGGAGGCCCATCCTCACCATCATCACACTGGAAGACTCCAGGTCAGGAGCCACTT +GCCACCCTGCACACTGGCCTGCTGTGCCCCAGCCTCTGCTTGCCTCTGACCCCTGGGCCC +ACCTCTTACCGATTTCTTCCATACTACTACCCATCCACCTCTCATCACATCCCCGGCGGG +GAATCTCCTTACTGCTCCCACTCAGTTTTCTTTTCTCTGGCTTTGGGACCTCTTAACCTG +TGGCTTCTCCTCCACCTACCTGGAGCTGGAGCTTAGGCTCCAGAAAGGACAAGGGTGGTT +GGGAGTAGATGGAGCCTGGTTTTTTAAATGGGACAGGTAGGACCTGATTTCCTTACTGCC +TCTTGCTTCTCTTTTCCTATCCTGAGTAGTGGTAATCTACTGGGACGGAACAGCTTTGAG +GTGCGTGTTTGTGCCTGTCCTGGGAGAGACCGGCGCACAGAGGAAGAGAATCTCCGCAAG +AAAGGGGAGCCTCACCACGAGCTGCCCCCAGGGAGCACTAAGCGAGGTAAGCAAGCAGGA +CAAGAAGCGGTGGAGGAGACCAAGGGTGCAGTTATGCCTCAGATTCACTTTTATCACCTT +TCCTTGCCTCTTTCCTAGCACTGCCCAACAACACCAGCTCCTCTCCCCAGCCAAAGAAGA +AACCACTGGATGGAGAATATTTCACCCTTCAGGTACTAAGTCTTGGGACCTCTTATCAAG +TGGAAAGTTTCCAGTCTAACACTCAAAATGCCGTTTTCTTCTTGACTGTTTTACCTGCAA +TTGGGGCATTTGCCATCAGGGGGCAGTGATGCCTCAAAGACAATGGCTCCTGGTTGTAGC +TAACTAACTTCAGAACACCAACTTATACCATAATATATATTTTAAAGGACCAGACCAGCT +TTCAAAAAGAAAATTGTTAAAGAGAGCATGAAAATGGTTCTATGACTTTGCCTGATACAG +ATGCTACTTGACTTACGATGGTGTTACTTCCTGATAAACTCGTCGTAAGTTGAAAATATT +GTAAGTTGAAAATGGATTTAATACACCTAATCTAAGGAACATCATAGCTTAGCCTAGCCT +GCTTTTTTTTTTTTTTTTTTTGGAGACAGAGTCTCACTCTGTCACCCAGGCTGGAGTGCA +GTGGCGGGATCTCGGCTCACTGCAACCTCCGCCTTCTGGGTTCAAGCGATTCTCCTGCCT +CAGCCCACTGAGTAGCTGGGATTACAGGCACCTGCCCCGACGCCCAGCTAATTTTTTGTT +ATTTATTTATTTTTTTTTTTAGTAGAGATGAGGTTTCACCATGTTGGCCAGGCTAGTCTC +GAACTCCTGACCTTGTGATCTGCCTGCCTTGGCCTCCCAAAGTGCTGGGATTACAGGCGT +GAGCCACCGCACCCGGCCTGCCTAGCCTACTTTTATTTTATTTTTAATGGAGACAGCATC +TTGCTCTGTTGCCCAGGCTGGATTACAGTGATGTGATCATAGCTCATTATACCCTCCTGG +GCTCAAGCAATCCCCCTAACTCTGCCTCCCCAGTAGCTAGGACCACAGGCATACACCACC +ATACCCAGCTAATTTTTAAAATTTTTTGTAGATAGATAGAGTCTCACTATGTTGCCCAGG +CTGGTCTCTAGCCTACTTTTTTGAGACAAGGTCTTGCTCTGTCACCCAGGCTGGATAGAG +TGCAGTAGTGCAGTCACAGCTCACTGCAGCCTCCACCTCCCAGGCTCCATCCATCCTCCC +AGCTCAGCCTCCCAAGTTGCTTCAACTACAGGCCTGCACCACCATGCCTGGCTAATTTTT +ATTTATTTATTTTTATTTTATTTTATTTTATTTTTTTGAGACTCAGTCTCACTCTGTCGC +CCAGGCTGGAGTGCAGTGGCATGATCTCGGCTCACTGCAACCTCTGCCTCCTGGGTTCAA +GTGATTCTCCTGCCTCAGCCTCCCGAATAGCTAGGACTACAAGCGCCTGCTACCACGCCC +AGCTAATTTTTGTATTTTTAGTAGAGACAGGGTTTCACCATGTTGGCCAGGCTGGTCTCG +AACTTCTGACCATGTGATCCGCCCGCCTCGGCCTCCCAAAGTGCTGGGATTACAGGTGTG +AGCCACCACGCCCGGCTAATTTTTATTTATTTATTTAAAGACAGAGTCTCACTCTGTCAC +TCAGGCTAGAGTGCAGTGGCACCATCTCAGCTCACTGCAGCCTTGACCTCCCTGGGCTCC +GGTGATTTCACCCTCCCAAGTAGCTAGGACTACAGGCACATGCCACGACACCCAGCTAAT +TTTTTATTTTCTGTGAAGTCAAGGTCTTGCTACGTTGCCCATGCTGGTATCAAACCCCTG +GGCTCAATCAATCCTTCCACCTCAGCCTCCCCAAGTATTGGGGTTACAGGCATGAGCTAC +CACACTCAGCCCTAGCCTACTTGAAACGTGTTCAGAGCATTTAAGTTACCCTACAGTTGG +GCAAAGTCATCTAACACAAAGCCCTTTTTATAGTAATAAAATGTTGTATATCTCATGTGA +TTTATTGAATATTGTTACTGAAAGTGAGAAACAGCATGGTTGCATGAAAGGAGGCACAGT +CGAGCCAGGCACAGCCTGGGCGCAGAGCGAGACTCAAAAAAAGAAAAGGCCAGGCGCACT +GGCTCACGCCTGTAATCCCAGCATTTCGGGAGGCTGAGGCGGGTGGATCACCTGAGGTCA +GGAGTTCAAGACCAGCCTAGCCAACATGGTGAAACCCCGTCTCTACTAAAATACAAAAAT +TAACCGGGCGTGATGGCAGGTGCCTGTAATCCCAGCTACTTGGGAGGCTGAGGCAGGAGA +ATCGCTTGAACCAGGAGGCGGAGGTTGCAGGGAGCCAAGATGGCGCCACTGCACTCCAGC +CTGGGCGATAGAGTGAGACTCCGTCTCAGAAAAAAAAGAAAAGAAACGAGGCACAGTCGC +ATGCACATGTAGTCCCAGTTACTTGAGAGGCTAAGGCAGGAGGATCTCTTGAGCCCAAGA +GTTTGAGTCCAGCCTGAACAACATAGCAAGACATCATCTCTAAAATTTAAAAAAGGGCCG +GGCACAGTGGCTCACACCTGTAATCCCAGCACTTTGGGAGGTGGAGGTGGGTAGATCACC +TGACGTCAGGAGTTGGAAACCAGCCTGGCTAACATGGTGAAGCCCCATCTCTACTAAAAA +CACAAAAATTAGCCAGGTGTGGTAGCACACGCCTGTAGTCCCAGCTACTCGGGAGGCTGA +GGCACAAGAATCACTTGAACCCCAGAGGCGGAGATTGCAATCAGCCAAGATTGCACCATT +GCACTCCCGCCTGGGCAACAGAGTGAGACCCCATCTCAAAATAAATAAATAAATATTTTT +AAAAGTCAGCTGTATAGGTACTTGAAGTGCAGTTTCTACTAAATGCATGTTGCTTTTGTA +CCGTCATAAAGTCAAACAATTGTAACTTGAACCATCTTTTAACTCAGGTACTGTGTATAT +ACTTACTTCTCCCCCTCCTCTGTTGCTGCAGATCCGTGGGCGTGAGCGCTTCGAGATGTT +CCGAGAGCTGAATGAGGCCTTGGAACTCAAGGATGCCCAGGCTGGGAAGGAGCCAGGGGG +GAGCAGGGCTCACTCCAGGTGAGTGACCTCAGCCCCTTCCTGGCCCTACTCCCCTGCCTT +CCTAGGTTGGAAAGCCATAGGATTCCATTCTCATCCTGCCTTCATGGTCAAAGGCAGCTG +ACCCCATCTCATTGGGTCCCAGCCCTGCACAGACATTTTTTTAGTCTTCCTCCGGTTGAA +TCCTATAACCACATTCTTGCCTCAGTGTATCCACAGAACATCCAAACCCAGGGACGAGTG +TGGATACTTCTTTGCCATTCTCCGCAACTCCCAGCCCAGAGCTGGAGGGTCTCAAGGAGG +GGCCTAATAATTGTGTAATACTGAATACAGCCAGAGTTTCAGGTCATATACTCAGCCCTG +CCATGCACCGGCAGGTCCTAGGTGACCCCCGTCAAACTCAGTTTCCTTATATATAAAATG +GGGTAAGGGGGCCGGGCGCAGTGGCTCACGAATCCCACACTCTGGGAGGCCAAGGCGAGT +GGATCACCTGAGGTCGGGAGTTTGAGCCCAGCCTGACCAACATGGAGAAACCCCATCTCT +ACTAAAAATACAAAAGTAGCCGGGCGTGGTGATGCATGCCTGTAATCCCAGCTACCTACT +CGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCAGAGGTTGCGGTGAGCTGAG +ATCTCACCATTACACTCCAGCCTGGGCAACAAGAGTGAAACTCCGTCTCAAAAAAGATAA +ATAAAGTAAAATGGGGTAAGGGAAGATTACGAGACTAATACACACTAATACTCTGAGGTG +CTCAGTAAACATATTTGCATGGGGTGTGGCCACCATCTTGATTTGAATTCCCGTTGTCCC +AGCCTTAGGCCCTTCAAAGCATTGGTCAGGGAAAAGGGGCACAGACCCTCTCACTCATGT +GATGTCATCTCTCCTCCCTGCTTCTGTCTCCTACAGCCACCTGAAGTCCAAAAAGGGTCA +GTCTACCTCCCGCCATAAAAAACTCATGTTCAAGACAGAAGGGCCTGACTCAGACTGACA +TTCTCCACTTCTTGTTCCCCACTGACAGCCTCCCACCCCCATCTCTCCCTCCCCTGCCAT +TTTGGGTTTTGGGTCTTTGAACCCTTGCTTGCAATAGGTGTGCGTCAGAAGCACCCAGGA +CTTCCATTTGCTTTGTCCCGGGGCTCCACTGAACAAGTTGGCCTGCACTGGTGTTTTGTT +GTGGGGAGGAGGATGGGGAGTAGGACATACCAGCTTAGATTTTAAGGTTTTTACTGTGAG +GGATGTTTGGGAGATGTAAGAAATGTTCTTGCAGTTAAGGGTTAGTTTACAATCAGCCAC +ATTCTAGGTAGGGGCCCACTTCACCGTACTAACCAGGGAAGCTGTCCCTCACTGTTGAAT +TTTCTCTAACTTCAAGGCCCATATCTGTGAAATGCTGGCATTTGCACCTACCTCACAGAG +TGCATTGTGAGGGTTAATGAAATAATGTACATCTGGCCTTGAAACCACCTTTTATTACAT +GGGGTCTAGAACTTGACCCCCTTGAGGGTGCTTGTTCCCTCTCCCTGTTGGTCGGTGGGT +TGGTAGTTTCTACAGTTGGGCAGCTGGTTAGGTAGAGGGAGTTGTCAAGTCTCTGCTGGC +CCAGCCAAACCCTGTCTGACAACCTCTTGGTGAACCTTAGTACCTAAAAGGAAATCTCAC +CCCATCCCACACCCTGGAGGATTTCATCTCTTGTATATGATGATCTGGATCCACCAAGAC +TTGTTTTATGCTCAGGGTCAATTTCTTTTTTCTTTTTTTTTTTTTTTTTTCTTTTTCTTT +GAGACTGGGTCTCGCTTTGTTGCCCAGGCTGGAGTGGAGTGGCGTGATCTTGGCTTACTG +CAGCCTTTGCCTCCCCGGCTCGAGCAGTCCTGCCTCAGCCTCCGGAGTAGCTGGGACCAC +AGGTTCATGCCACCATGGCCAGCCAACTTTTGCATGTTTTGTAGAGATGGGGTCTCACAG +TGTTGCCCAGGCTGGTCTCAAACTCCTGGGCTCAGGCGATCCACCTGTCTCAGCCTCCCA +GAGTGCTGGGATTACAATTGTGAGCCACCACGTCCAGCTGGAAGGGTCAACATCTTTTAC +ATTCTGCAAGCACATCTGCATTTTCACCCCACCCTTCCCCTCCTTCTCCCTTTTTATATC +CCATTTTTATATCGATCTCTTATTTTACAATAAAACTTTGCTGCCACCTGTGTGTCTGAG +GGGTGAACGCCAGTGCAGGCTACTGGGGTCAGCAGGTGCAGGGGTGAGTGAGGAGGTGCT +GGGAAGCAGCCACCTGAGTCTGCAATGAGTGTGGGCTGGGGGGCCCAGTGCCCGGGTTCC +GGGAGGGGAACAAAGGCTGGAGACTGGGTCAGTCTGCGGGCTGCATGACAACAAGGGAGG +GGGTGGCTCCATTCATAACTCAGGAACCAACCGTCCCTCCTCCCCTCCGGCCACGGCTGG +CACAAGGTTCTCTCCCTCCCCTGCTTCTAGGACTGGGCTGCTTCCCCCTCGGCAGCCTCT +CACCAAGGATTACGGGATTTAAATGTCTGATTTAGCAAGGCTGAGCCTCCAGGGTGGCCA +TCTGCTCCATCAGAAAGTGGCAGGATACCTGGGTTCCCAAGGGGAACAGGGGTGGGTGCT +ACTGGATGGAGAGAGGCCAGTGGGAGGCCTGCTAGCCAGGGTCCCAGGAAAGTGGGGGCA +GCTAAGGTAAGAGTAGGGGTGTGGGGCTAGGTCCTTCCCAGCATCCCCTCATCCTGGGCC +TCATGCCAGGTAGCTGAATGAATTGAAGCTTTAAACTCTGCCAGGAAAACCTTTCAAAGG +GCTTCTTGGGATAGGGAGGAGAGTCGGGTTGAGGAGCTCAGTACTGCCTGCCCATGCTCC +TCAGGGCTGCTGGCTCCCAGGGAGGGGGGCTGGGAGCAGGCAGGCTCTTCCCCATCACCC +ACTGCTCTCTTGGAGCCAGTGCTTGAAGGGGCAGTCAGACATGGCTTGCCCTTCCTCCTC +CCTGGTGGTGGAGATGGGTGTTAGGGTCCAGTGGGTGCTACTGTCCAGGGGGGCTTCTGG +GGCCACCAGCCTGTCAGCTCATCAACCAGGCTGAAGGTGCAAGCAGGAGCCCCTTGCCTT +GCCCCAAGGATCCCAGACAGCTATGAAGCCACCAGCCTTCCTGACCTCAAGACCACCTTT +TTTTTTTCTCTTTCTTACTAGGGAATGCCAAACACTCTCCCCAGGAGATCCAGACCCGCC +TCTTTCAGAGACTTTTAACTTAAACATCTGTCCCTACCCAGCAGGCAAACTAGAGCTCCT +GAAGCTCAGTCCCTGTCCTTGCCTCTGTAGACAGGTCACCTTGATGAGCTTCCTTTTTTT +TTTTTTAATTTTTTTTTATTTTAGGCTTTATTGGGGCATAATTGATCCCCCAAAATTGCA +TACATTCAAGGTATGCAGTGTGATGATTTGATATGGGGGTATATTGTGAAACCATTACCA +CAATCAAATTAATCAGCACGTCCATCATCACACACAGTTACCATTTGTGTGTGTGCACGT +GTGTTCACCTACGACGAGGACACTTGGACCTACTCTGCAGATCTCAAGTAAACAGAAAAT +CTCCCTTTTTGACAACCATCCTCCACCCTTTCAATCCCAACCTTTTCCTAGATTATGTCC +CTAGCTCTGTTTTTATTTCTGCTGTGCTGCTTCAGATCCATTCTGACTCTGCCAAACCCT +TCTTTGTGAGCTGATAGATTGCTGGATTGAGAATTACAGCTGGGCGCGGTGGCTCACGCC +TGTAATCCCAACACTGTGGGAGGCCAAGGCCGGCGGATCACTTGAGGTCAGGAGTTGGAG +ACCAGCCTGACCAACAAGATGAAACCCCATCTCTACTAAAAATACAAAATTAGCTGGGCA +TGGTGGTGCACGCCTGTAATCTCATCTTCTTGGGAGGCTGAGGCAGGAGAATTGCTTGAA +CCCGGGAGGTGGAGGTTGCAGTGAGCCAAGATCCTGCCATTGCACTCCAGCCTGGGCAAC +AACAGTGAAGCTCCATCTCAAAACACACAAAAAAAAGAAGTACAAAGTCTGAGACTTCAG +GCCAGCTCTGCTACACTATATACTCTAACCTCTCTGGTCCTACTTGGTGACTTCTTTCCC +TCTGGTCGTGTTCAAGTTCCCGTCCCATCCAGTCAAGCAGGTACTCATTGGTACCTTACC +CTGTGCCAGGAGCTGTTCTAGGCCCTGGAAACCTATGGCAGACATGTTCCCTACCCTCCC +ACTCAAAGAGCCCAGGCCTTATCCTAATGAGATCTGAAATCAAATCTCCCAATTTCCTCA +TGGCTTCAGTCTAAACTTGTAATTCACAACCTTAAATCAATATGTTCTATTTTTTTATTT +AGAAAACATTTCCGGCCAGGCACGGTGGATCACACCTGTAATCCCAGCTACTCGGGAGGC +TGAGGCAGGAGAATCGCTTGAACCCAGGAGGCAGAGGGTTGCAGTGAGCCGAGATTGCGC +CATTGCACTCTAGCCTGGGCAACAGAGCAAGACTCCATCTCAAAAAAGAAAAAAAAATGG +AAGAAAAAAAAATTTCCCCCTCATTTTAGGAACACGAGGTCTCCAAATCTAAAATTCGTA +CTCTGAGGAGATTGAATAGCCTTAAATGCTTTCATCATTAAAAAGAAAAGAAAGGAACCT +GGTATGCATCCTAAAAATGAAAAATATACCTACCTGTAATCCCAGCACACAGCACATTGG +GAGGCTAAAGCAGGAGGATAACTTGAGGCCAGGAGTTTCAGATCAGCCTGGGCAACATAG +CAACACCCCATTTCTTTTTCTTTTCTTTTTTTTTTGGAGACACAGTCTCGCTCTGTTACT +CAGGCTGGAGTGCAGTGGCTCAATCTCAGCTCACTGCAAGCTCTGCCTCCCAGGTTCATG +CCATTCTCCTGCCTCAGCCTCCCGAGTAGCTGGGACTACAGGCGCCCGCCACCACGCCTG +GCTAATTTTTTGTATTTTTAGTAGAGACAGGGTTTCACCGTGTTAGCCAGGATGGTCTCG +ATCTCCTGACCTCGTGATCCGCCAGCCTTGGCCTCCTAAAGTACTGGGATTACAGGCGTG +AGCCACTGCGCCTGGCCACAACACCCCATTTCTATTTTAATAAAATAAAATACTGTGAAA +AACATTTACAATTTTTAAATTTTAATTTTAAAATTAAACTTATATTTATTCATTTGTGTG +TGTGGGTTTTTTTTTTTTTTTTTTTTTGCTTTTTTTTTGAGATGGAGTGTCACTCTGTCA +CCCAGGCTGGAGTGCAGTGGCGTGATCTCTGCCTCCCGGTTCAAGTGATTCTCCTGCCAT +AGCCTCCCAAGTAGCTGGGACTACAGGTACACGCCACCACGCCGGGTTAATTTTTGTATT +TTTAGTAGAGACAGGATTTCACTGTGTCGCCAGGCTAGCCTCGAACTCCTGACCTCAGGT +GATTCGCCCACCTTGGCCTCCCAAAGTGCTGTGATTACAAGCGTGAGCCACCGTGCCCAG +CCCAAAGTTGGTTTTAATAGCAGAAAATCTATCAACATAATTCAATATATTAAATTTAGA +AAGAAAAATTATCTATCATATCAACAGATACTGAAAGGAATTTGATTAAATTTCAGTAGC +CATTTCCTTAAAAAAGAAAACACTTTAACACAGTAATAGACTGATAATGGAATACCAATT +TTCCTAATAAGTTAAACATTAAGATAATTTCAATTAAGGTCAAGAGCTGGGCCAGGTGCA +GTGGCTCACACCTGTAATCCCAACACTTTGGAGGCCAAGGTGGGTGGATCACCTGAGGTC +AGGAGTGGAGACCAGCCTGGCTGACAATAGTGAAATCCTGCCTCTACTAAAAACACAAAA +AATTAGCTGGGCATGGTGGTGGGCACCTATAATCCCAGCTACTGGGAAGGCTGAGACAGG +AGAATTGCTTGAACCTGGGAGGCGGAGGTTGCAGTGAGCAAAGATCACACCATTGCACTC +CAGCCTGGGCGACAGAGCCAGAGTCAGTCTCAAAAAAAAAAAGAGGTGGCCACACCTATA +ATCCAAACATTTTGTGAGGCCAAGGCAGGAGAATTGCTTCAGGCCAAGAGTTGAACACCT +CGTCAACATAGCCAGACCTCTCTCTAGATAGATAGATAGATGATAGATAGAGAGATAGAT +AGATGATAGATAGAGAGATAGATAGATGATAGATAGATAGATAGATAGATAGATAGATAG +ATAGATAGATAGATAGATAGATAGATAATCTGGCCGGGTGTGGAGGCTCACGCCTGTAAT +CCCAGCACTTTGGGAGGCTGAGGCGGGCAGATCACGAGGACAAGAGATTGAAACCATCCT +GGCTAACAAGGTGAAACCCCGTCTCTACTAAAAATACAAAAAATTAGGCGGGTGTGGTGG +CACGCGCCTGTAGTCCTAGCTATTCAGGAGGCTGAGACAGGAGAATTGCTTGAATCCGAA +AGGCGGAGGTTGCAGCGAGCCGAGATCGTGCCACTGCACTCCAGCCTGGGTGACAGAGCA +AGACTCCATCTCAAAATAAATAAATAAATAATCAAGAACAGTATAAGGGGCTGTATGGTG +GCTCATGCCTGTGATCCCAGCACTTTGGGAGGCCAAGGTGGGAGGATCCCTTGAGACCAG +CCCAGGCAACAGAGAAAGACCCTGTCTCTATTTAAAAAAATTAAAAACTGGCCGGGCACG +GTGGCTCACGCCTGTAATTCCAGCGCTTGGGAGGCCAAGGCAGGCACATCAGGAGGTCAG +GAGTTCGAGACCAGCCTGGCCAACGTGGTGAAACCCCGTCTCTACTAAAAATACAAAAAG +TAGCTAGGCGTGGTGGCAGGCACCTGTAATCCCAGCTACTTGGGAGGCTGAGGCAGGAGA +ATCGCTTGAACCCAGGAGGCGGAGGTTGCAGTGGGCAAAGATCGTGCCATTGCACTCAGC +CTGGGTGACAGGGCAAGACTCCATCTCAAAATAAATAAACAAAGTAATTAATTAATTAAA +TTAAAAACTGTGGGGATATAGACTTACTCTGGTTTTATTTTTTCTTTTCTTTTCTTTTCT +TTTTTCTGAGACGGAGTCTCGCTCTGTTGCCCAGGCTGGAGTACAGTGGCGTGGTTTCTG +TTCTCTGCAACCTCCACCTCCCGGATTCAAGCGATTCTCTTGCCTCAGCCTCTTGAATAC +CTGGAATTACAGGTGCCTGCCACCACCCCCGGCTAATTTTTTGTATTTTTAGTAGAGACA +GGGTTTCACCATGTTGGCCAAGCTGGTCTCGAACTCCTGACCTCATGATCCACCCGCCTC +TGCCTCCCAAAGCACTGAGACTACAGGAGTGAGCCACTGTGCCCAGCCTACTCTGGTTTT +AGTGCATTCAAGAGGAACAAAAAAGGAAGAAAATCACTAGTAAATATACCTCTTTCTGGT +TAGAGTGGATGTTTGGAAATTATATATATATTATATTATATTATATATATTATATATATA +CACAAACACGTACATACATGCACACACATATATGCCTTTTTGATTATAGGATAGTATACC +AAAACTCAGAAATATTATGGAATTAACAGAATTTAGTAAGGCAGATAAGTAGTAGGTAGA +AAAATATTAATTTTATCTTCCAGCAGAAGCACTGTGAAAAATTAGACAACAAGAAAACAT +TCCATTCAAAATAATGACAATAAGGCCGGGCATGGTGGCTCACACCTGTAATCCCAGCAC +TTTGGGAGGCTGAGGCAGGAGGATCATCTGAGGTCAAGTTTGAGATCAGCCTGGCCAACA +TGGTGACACCCTGTCTCTACTGAAAATACAAAAATCAGCCAGCTATGGTAGTGTAAGCCT +GTAATTCCAGCTACTCGGGAGGTCGAAGCAGAAGAATCACTTGAACCCAGGAGGCAGAGA +TTGCAGTGAGCCAAGATCCTGCCAGTGCTTTCCAGCCTGGGCAACAGTGTGAGGCTCCAT +CTCAAAAAAAAAAAAAAAAAAAAGACAATAGCAATAAACATTAAGAAATGTGTAATAGGA +ATGGCACACACAAAGAAGGAATGGCACAGAGCCTGTATGCAGAAGACCACAAACCCTTAT +TTAACGACGTAAGCCAAGATCCAAAGAAAATGATAGATTCTCAGATGGGAAAACTAAAAA +AATAAGAAAAATCAATTATCTCGAGATAAATATAATATAATGCAATTTCAATTAGAATCC +CAAATTTTCATTGTGTGTGTGTGTGAGTTGGGTAAATTTATCATAAATGTATAGGAACGA +GTAAGTGTCACTAGTTGTTTAAATAAATACTGGATTTGGGCCAGGCATGGTGGCTCACGC +CTCTAATCCCAGCACTTTGGGAGACCGAGGCGGGCAGATCATGAGGTCAGGAGATCGAGA +CCATCTGGCCAACATAGTGAAAACTCGTCTCTACTAAAGATACAAAAAATTAGCTGGGCA +TGGTGGCACGTGCCTGTAGTTCCAGCTACTCTGGAGGCTGAGGCAGGAGAGTTGCTTGAA +CCCGGGAGGTGGAGGTTGCAATGAGCCGAGATCCTGTCACTGCACTCCACCCTGGCGACA +AAGTGAGACTCCGTCTCTCTCTCTCTCTTTAGGCCAAGGCAGGTGGATCACCTGAGGTCA +GGAGTTCAAGACAGCCTGGCCAACATAGCGAAATCCCATCTCTACTAAAAATACAAAAAT +TAGCCTGGCAGTGGTGGCCCACGCCTGTAATCCCAGCTACTAAGGGGGCTGAGGCAGGAG +GATCTCTTAACCAGGGAGGAGGAGGTTGCAGTGAGCAGAGATTGTGCCACTGCACTCCAG +CCTGTGCAACAGAGTGAGACTCTGTCTC diff --git a/nim-skills/genomic-intelligence-nim/assets/demo/splice_hbb.fa b/nim-skills/genomic-intelligence-nim/assets/demo/splice_hbb.fa new file mode 100644 index 0000000..fdbefab --- /dev/null +++ b/nim-skills/genomic-intelligence-nim/assets/demo/splice_hbb.fa @@ -0,0 +1,67 @@ +>human|HBB|chr11:5225464-5229395|GRCh38|strand:-1 +TTGGAAAAGCAACCCCTGCCTTGAAGCCAGGATGATGGTATCTGCAGCAGTTGCCAACAC +AAGAGAAGGATCCATAGTTCATCATTTAAAAAAGAAAACAAAATAGAAAAAGGAAAACTA +TTTCTGAGCATAAGAAGTTGTAGGGTAAGTCTTTAAGAAGGTGACAATTTCTGCCAATCA +GGATTTCAAAGCTCTTGCTTTGACAATTTTGGTCTTTCAGAATACTATAAATATAACCTA +TATTATAATTTCATAAAGTCTGTGCATTTTCTTTGACCCAGGATATTTGCAAAAGACATA +TTCAAACTTCCGCAGAACACTTTATTTCACATATACATGCCTCTTATATCAGGGATGTGA +AACAGGGTCTTGAAAACTGTCTAAATCTAAAACAATGCTAATGCAGGTTTAAATTTAATA +AAATAAAATCCAAAATCTAACAGCCAAGTCAAATCTGCATGTTTTAACATTTAAAATATT +TTAAAGACGTCTTTTCCCAGGATTCAACATGTGAAATCTTTTCTCAGGGATACACGTGTG +CCTAGATCCTCATTGCTTTAGTTTTTTACAGAGGAATGAATATAAAAAGAAAATACTTAA +ATTTTATCCCTCTTACCTCTATAATCATACATAGGCATAATTTTTTAACCTAGGCTCCAG +ATAGCCATAGAAGAACCAAACACTTTCTGCGTGTGTGAGAATAATCAGAGTGAGATTTTT +TCACAAGTACCTGATGAGGGTTGAGACAGGTAGAAAAAGTGAGAGATCTCTATTTATTTA +GCAATAATAGAGAAAGCATTTAAGAGAATAAAGCAATGGAAATAAGAAATTTGTAAATTT +CCTTCTGATAACTAGAAATAGAGGATCCAGTTTCTTTTGGTTAACCTAAATTTTATTTCA +TTTTATTGTTTTATTTTATTTTATTTTATTTTATTTTGTGTAATCGTAGTTTCAGAGTGT +TAGAGCTGAAAGGAAGAAGTAGGAGAAACATGCAAAGTAAAAGTATAACACTTTCCTTAC +TAAACCGACATGGGTTTCCAGGTAGGGGCAGGATTCAGGATGACTGACAGGGCCCTTAGG +GAACACTGAGACCCTACGCTGACCTCATAAATGCTTGCTACCTTTGCTGTTTTAATTACA +TCTTTTAATAGCAGGAAGCAGAACTCTGCACTTCAAAAGTTTTTCCTCACCTGAGGAGTT +AATTTAGTACAAGGGGAAAAAGTACAGGGGGATGGGAGAAAGGCGATCACGTTGGGAAGC +TATAGAGAAAGAAGAGTAAATTTTAGTAAAGGAGGTTTAAACAAACAAAATATAAAGAGA +AATAGGAACTTGAATCAAGGAAATGATTTTAAAACGCAGTATTCTTAGTGGACTAGAGGA +AAAAAATAATCTGAGCCAAGTAGAAGACCTTTTCCCCTCCTACCCCTACTTTCTAAGTCA +CAGAGGCTTTTTGTTCCCCCAGACACTCTTGCAGATTAGTCCAGGCAGAAACAGTTAGAT +GTCCCCAGTTAACCTCCTATTTGACACCACTGATTACCCCATTGATAGTCACACTTTGGG +TTGTAAGTGACTTTTTATTTATTTGTATTTTTGACTGCATTAAGAGGTCTCTAGTTTTTT +ATCTCTTGTTTCCCAAAACCTAATAAGTAACTAATGCACAGAGCACATTGATTTGTATTT +ATTCTATTTTTAGACATAATTTATTAGCATGCATGAGCAAATTAAGAAAAACAACAACAA +ATGAATGCATATATATGTATATGTATGTGTGTATATATACACACATATATATATATATTT +TTTCTTTTCTTACCAGAAGGTTTTAATCCAAATAAGGAGAAGATATGCTTAGAACCGAGG +TAGAGTTTTCATCCATTCTGTCCTGTAAGTATTTTGCATATTCTGGAGACGCAGGAAGAG +ATCCATCTACATATCCCAAAGCTGAATTATGGTAGACAAAACTCTTCCACTTTTAGTGCA +TCAACTTCTTATTTGTGTAATAAGAAAATTGGGAAAACGATCTTCAATATGCTTACCAAG +CTGTGATTCCAAATATTACGTAAATACACTTGCAAAGGAGGATGTTTTTAGTAGCAATTT +GTACTGATGGTATGGGGCCAAGAGATATATCTTAGAGGGAGGGCTGAGGGTTTGAAGTCC +AACTCCTAAGCCAGTGCCAGAAGAGCCAAGGACAGGTACGGCTGTCATCACTTAGACCTC +ACCCTGTGGAGCCACACCCTAGGGTTGGCCAATCTACTCCCAGGAGCAGGGAGGGCAGGA +GCCAGGGCTGGGCATAAAAGTCAGGGCAGAGCCATCTATTGCTTACATTTGCTTCTGACA +CAACTGTGTTCACTAGCAACCTCAAACAGACACCATGGTGCATCTGACTCCTGAGGAGAA +GTCTGCCGTTACTGCCCTGTGGGGCAAGGTGAACGTGGATGAAGTTGGTGGTGAGGCCCT +GGGCAGGTTGGTATCAAGGTTACAAGACAGGTTTAAGGAGACCAATAGAAACTGGGCATG +TGGAGACAGAGAAGACTCTTGGGTTTCTGATAGGCACTGACTCTCTCTGCCTATTGGTCT +ATTTTCCCACCCTTAGGCTGCTGGTGGTCTACCCTTGGACCCAGAGGTTCTTTGAGTCCT +TTGGGGATCTGTCCACTCCTGATGCTGTTATGGGCAACCCTAAGGTGAAGGCTCATGGCA +AGAAAGTGCTCGGTGCCTTTAGTGATGGCCTGGCTCACCTGGACAACCTCAAGGGCACCT +TTGCCACACTGAGTGAGCTGCACTGTGACAAGCTGCACGTGGATCCTGAGAACTTCAGGG +TGAGTCTATGGGACGCTTGATGTTTTCTTTCCCCTTCTTTTCTATGGTTAAGTTCATGTC +ATAGGAAGGGGATAAGTAACAGGGTACAGTTTAGAATGGGAAACAGACGAATGATTGCAT +CAGTGTGGAAGTCTCAGGATCGTTTTAGTTTCTTTTATTTGCTGTTCATAACAATTGTTT +TCTTTTGTTTAATTCTTGCTTTCTTTTTTTTTCTTCTCCGCAATTTTTACTATTATACTT +AATGCCTTAACATTGTGTATAACAAAAGGAAATATCTCTGAGATACATTAAGTAACTTAA +AAAAAAACTTTACACAGTCTGCCTAGTACATTACTATTTGGAATATATGTGTGCTTATTT +GCATATTCATAATCTCCCTACTTTATTTTCTTTTATTTTTAATTGATACATAATCATTAT +ACATATTTATGGGTTAAAGTGTAATGTTTTAATATGTGTACACATATTGACCAAATCAGG +GTAATTTTGCATTTGTAATTTTAAAAAATGCTTTCTTCTTTTAATATACTTTTTTGTTTA +TCTTATTTCTAATACTTTCCCTAATCTCTTTCTTTCAGGGCAATAATGATACAATGTATC +ATGCCTCTTTGCACCATTCTAAAGAATAACAGTGATAATTTCTGGGTTAAGGCAATAGCA +ATATCTCTGCATATAAATATTTCTGCATATAAATTGTAACTGATGTAAGAGGTTTCATAT +TGCTAATAGCAGCTACAATCCAGCTACCATTCTGCTTTTATTTTATGGTTGGGATAAGGC +TGGATTATTCTGAGTCCAAGCTAGGCCCTTTTGCTAATCATGTTCATACCTCTTATCTTC +CTCCCACAGCTCCTGGGCAACGTGCTGGTCTGTGTGCTGGCCCATCACTTTGGCAAAGAA +TTCACCCCACCAGTGCAGGCTGCCTATCAGAAAGTGGTGGCTGGTGTGGCTAATGCCCTG +GCCCACAAGTATCACTAAGCTCGCTTTCTTGCTGTCCAATTTCTATTAAAGGTTCCTTTG +TTCCCTAAGTCCAACTACTAAACTGGGGGATATTATGAAGGGCCTTGAGCATCTGGATTC +TGCCTAATAAAAAACATTTATTTTCATTGCAA diff --git a/nim-skills/genomic-intelligence-nim/evals/config.yml b/nim-skills/genomic-intelligence-nim/evals/config.yml new file mode 100644 index 0000000..5f85ee4 --- /dev/null +++ b/nim-skills/genomic-intelligence-nim/evals/config.yml @@ -0,0 +1,9 @@ +schema_version: 1 + +harbor: + task_source: evals_json + runtime_env: + - GI_API_KEY + +grading: + mode: aces_default diff --git a/nim-skills/genomic-intelligence-nim/evals/evals.json b/nim-skills/genomic-intelligence-nim/evals/evals.json new file mode 100644 index 0000000..368f39b --- /dev/null +++ b/nim-skills/genomic-intelligence-nim/evals/evals.json @@ -0,0 +1,96 @@ +{ + "skill_name": "genomic-intelligence-nim", + "evals": [ + { + "id": "1", + "prompt": "Predict promoter regions in assets/demo/promoter_tp53.fa using Genomic Intelligence, and tell me how many promoter windows it found. My GI_API_KEY is set.", + "expected_output": "Runs the provided gi_predict.py runner for the promoter task on the demo FASTA, then reports summary.promoter_windows / total_windows from the compact stdout JSON.", + "files": [ + "scripts/gi_predict.py", + "assets/demo/promoter_tp53.fa" + ], + "assertions": [ + { + "id": "uses-runner", + "description": "Uses the provided runner rather than hand-rolling HTTP", + "check": "Invokes 'scripts/gi_predict.py' with '--task promoter' (does not build a raw curl/requests call inline)" + }, + { + "id": "hosted-endpoint", + "description": "Targets the hosted Genomic Intelligence promoter endpoint", + "check": "The runner/client contains 'api.genomicintelligence.ai' and '/v1/tasks/{task}/predict' (task = promoter)" + }, + { + "id": "bearer-auth", + "description": "Authenticates with a Bearer key from GI_API_KEY", + "check": "Request sets 'Authorization: Bearer' from 'GI_API_KEY'; no key literal is hard-coded" + }, + { + "id": "reports-summary", + "description": "Reports the promoter-window headline", + "check": "Output reads 'promoter_windows' / 'total_windows' from the response summary" + } + ] + } + ], + "todo": "Deferred evals need network egress to api.genomicintelligence.ai and an Ensembl fetch (case 2) / async job polling (case 3); enable once the eval harness supports long-running third-party calls.", + "deferred_evals": [ + { + "id": "2", + "prompt": "Predict gene expression for HBB in K562 cells with Genomic Intelligence. I'll let you fetch the sequence.", + "expected_output": "Fetches the exact 9,198 bp TSS-centred window for HBB (gi_fetch --for-expression), then runs the expression task with a required cell-type description, reporting log(TPM+1).", + "files": [ + "scripts/gi_predict.py", + "scripts/gi_fetch.py" + ], + "assertions": [ + { + "id": "expression-window", + "description": "Obtains the exact 9,198 bp expression window", + "check": "Uses gi_fetch with '--for-expression' (or otherwise produces a 9,198 bp TSS-centred window) before predicting" + }, + { + "id": "description-required", + "description": "Passes the required cell-type context", + "check": "Runs expression with '--description' set to a cell type such as 'K562'" + }, + { + "id": "expression-endpoint", + "description": "Calls the expression task", + "check": "Runner is invoked with '--task expression'" + }, + { + "id": "reports-log-tpm", + "description": "Reports the expression prediction", + "check": "Output reads 'expression_log_tpm' (log TPM) from the response" + } + ] + }, + { + "id": "3", + "prompt": "Run de-novo gene annotation on assets/demo/annotation_tp53.fa with Genomic Intelligence and list the transcripts it predicts.", + "expected_output": "Runs the annotation task, which submits asynchronously and polls the job endpoint to completion, then reports the predicted transcripts.", + "files": [ + "scripts/gi_predict.py", + "assets/demo/annotation_tp53.fa" + ], + "assertions": [ + { + "id": "annotation-task", + "description": "Runs the annotation task via the runner", + "check": "Runner is invoked with '--task annotation'" + }, + { + "id": "async-submit-poll", + "description": "Uses the async submit/poll protocol", + "check": "Client submits with header 'Prefer: respond-async' and polls '/v1/tasks/jobs/'" + }, + { + "id": "reports-transcripts", + "description": "Reports predicted transcripts", + "check": "Output reads 'transcripts' / 'transcripts_found' from the response" + } + ] + } + ] +} diff --git a/nim-skills/genomic-intelligence-nim/evals/trigger_evals.json b/nim-skills/genomic-intelligence-nim/evals/trigger_evals.json new file mode 100644 index 0000000..073268c --- /dev/null +++ b/nim-skills/genomic-intelligence-nim/evals/trigger_evals.json @@ -0,0 +1,20 @@ +[ + {"query": "Predict promoter regions in this DNA sequence with Genomic Intelligence", "should_trigger": true}, + {"query": "Find splice donor and acceptor sites in this transcript", "should_trigger": true}, + {"query": "Score enhancer activity for this Drosophila regulatory sequence", "should_trigger": true}, + {"query": "Annotate chromatin state across tracks for this human region", "should_trigger": true}, + {"query": "Predict gene expression in K562 cells from this genomic window", "should_trigger": true}, + {"query": "Run de-novo gene / transcript annotation on this FASTA", "should_trigger": true}, + {"query": "Predict regulatory features of TP53 directly from sequence", "should_trigger": true}, + {"query": "What is the promoter/expression profile of HBB? Use the DNA language model API", "should_trigger": true}, + {"query": "Use the Genomic Intelligence hosted API to analyze this DNA", "should_trigger": true}, + {"query": "Fetch the TSS-centred window for a gene and predict its expression", "should_trigger": true}, + {"query": "Predict a protein structure from FASTA using OpenFold3", "should_trigger": false}, + {"query": "Generate a DNA continuation from this seed with Evo 2", "should_trigger": false}, + {"query": "Dock a ligand to a protein receptor and return binding poses", "should_trigger": false}, + {"query": "Design protein sequences for this backbone with ProteinMPNN", "should_trigger": false}, + {"query": "Calculate GC content of this DNA string with plain Python", "should_trigger": false}, + {"query": "Call variants from this BAM file and write a VCF", "should_trigger": false}, + {"query": "Cluster single-cell RNA-seq expression matrices", "should_trigger": false}, + {"query": "Summarize the promoter biology literature without calling an API", "should_trigger": false} +] diff --git a/nim-skills/genomic-intelligence-nim/references/api.md b/nim-skills/genomic-intelligence-nim/references/api.md new file mode 100644 index 0000000..0c0668f --- /dev/null +++ b/nim-skills/genomic-intelligence-nim/references/api.md @@ -0,0 +1,208 @@ +# API reference — the /v1 contract + +Genomic Intelligence exposes one versioned REST contract. Each task is its own +published operation with its own request schema; there is no shared request +model. Authoritative, live schema: + (human view: +). This file is a point-in-time +snapshot — if it disagrees with the OpenAPI doc, the OpenAPI doc wins. + +## Endpoints + +The document publishes eleven operations. The six predict paths are literal, one +per task — the URLs are byte-identical to what callers already send, so no client +URL construction changes: + +``` +POST https://api.genomicintelligence.ai/v1/tasks/promoter/predict # PromoterPredictRequest +POST https://api.genomicintelligence.ai/v1/tasks/splice/predict # SplicePredictRequest +POST https://api.genomicintelligence.ai/v1/tasks/enhancer/predict # EnhancerPredictRequest +POST https://api.genomicintelligence.ai/v1/tasks/chromatin/predict # ChromatinPredictRequest +POST https://api.genomicintelligence.ai/v1/tasks/annotation/predict # AnnotationPredictRequest +POST https://api.genomicintelligence.ai/v1/tasks/expression/predict # ExpressionPredictRequest +POST https://api.genomicintelligence.ai/v1/workflows/find-genes-and-predict-expression +GET https://api.genomicintelligence.ai/v1/tasks/jobs # list async jobs +GET https://api.genomicintelligence.ai/v1/tasks/jobs/{job_id} # poll an async job +GET https://api.genomicintelligence.ai/v1/tasks/{task}/models # model registry +GET https://api.genomicintelligence.ai/health # public +``` + +An unrecognised task segment is `404 not_found` (`"Unknown task: bogus"`), not a +`422`. + +- Base URL overridable via `GI_BASE_URL`. +- Auth: `Authorization: Bearer $GI_API_KEY` (partner key, prefix `gi_`) on every + `/v1/*` route, including `GET /v1/tasks/{task}/models`. +- `Content-Type: application/json`, `Accept: application/json`. + +`GET /v1/tasks/{task}/models` is **not** the `{data, meta}` envelope — it returns +a flat `{task, default_model, models: [{id, name, description, is_default, +bio_spec}]}`. + +## Request body + +Every request model is `additionalProperties: false`, as is every `options` +object, so an unknown key is a hard `422 validation_failed` with +`type: "extra_forbidden"` — never silently ignored. + +```json +{ + "sequence": "ACGT…", // required; A/C/G/T(/N); per-task minLength + "sequence_name": "TP53", // optional label echoed back (max 128 chars) + "model": "…", // optional; omit to use the task default + "options": { "threshold": 0.5 } // task-specific, closed; see below +} +``` + +`options` per task (all closed): + +| Task | `options` keys | +|---|---| +| promoter | `threshold` (0–1, default 0.5) | +| splice | `threshold` (0–1, default 0.5), `site_types` (subset of `["donor","acceptor"]`, default both) | +| enhancer | *(none)* | +| chromatin | `threshold` (0–1, default 0.5) | +| annotation | `batch_size` (1–128, default 8), `shift_coordinates`, `reverse_complement` (default true) | +| expression | `description` — **required**, and the only key | +| composite | `description`, `annotation_model`, `expression_model`, `batch_size`, `shift_coordinates` | + +`expression` additionally requires `options` itself and takes a fifth field: + +```json +{ + "sequence": "ACGT…", // required, 9,198–500,000 bp + "options": { "description": "K562 cells" }, // required + "tss_index": 12345, // required unless len == 9198 + "sequence_name": "HBB", + "model": "…" +} +``` + +Length bounds are per-task, published as `minLength`/`maxLength` on each request +schema and enforced before any model loads (see `references/tasks.md`): promoter +300, splice 100, enhancer 50, chromatin 200, annotation 1,000, expression 9,198, +composite 1,000 — all capped at 500,000 bp. Under the floor and over the cap are +both `422 validation_failed` at `loc ["body","sequence"]`; over-length is **not** +a `413`. The floor is admission control, not regime: a request above the floor +but below the model's `bio_spec.context_window_bp` is accepted and scored against +a padded window. + +Lengths and `tss_index` are measured on the **whitespace-stripped** sequence, so a +line-wrapped FASTA body pastes verbatim (a `>` header line still fails the +alphabet check). The runner validates length and `tss_index` bounds locally +before any call — those local constants are a mirror of the `minLength` +published on each task's request schema; the served schema wins. + +## `bio_spec` (from `GET /v1/tasks/{task}/models`) + +- `request_max_bp` — the enforced ceiling (500,000 for every model). +- `context_window_bp` — the model's own sliding window in bp; `null` for + annotation and expression. Promoter is 2,000 for the 2,000 bp models and 300 + for the 300 bp models; splice 15,000, enhancer 249, chromatin 1,000. Read the + value for the model you are actually using from + `GET /v1/tasks/{task}/models`. +- `trained_window_bp` — fixed receptive field; 9,198 for the expression model, + `null` for sliding-window models. + +There is no `strand_sensitive` flag. The splice model is strand-specific in +practice — feed transcript orientation. + +Expression responses echo the windowing: `meta.task_specific_counts.tss_index` / +`.scored_window`, and `data.input.tss_index` / `.scored_window` / +`.submitted_sequence_length`. Note `data.input.sequence_length` is the **scored** +length (always 9,198), not what you submitted. + +## Response envelope + +Success is `200` with a `{data, meta}` envelope. `data.summary` carries the +headline scalars; `data` also carries the per-item arrays (`regions`, `sites`, +`transcripts`) or `prediction` (expression). `meta` carries `model`, +`request_id`, and timing. Exact fields per task: `references/tasks.md`. + +## Async (annotation, and any predict operation) + +`Prefer` is a declared header parameter on all six predict operations and on the +composite — any of them can be run async, not just `annotation`: + +1. `POST …/tasks/annotation/predict` with header `Prefer: respond-async` → `202` + with `{data: {job_id, status: "accepted", links}, meta}` (the same + `{data, meta}` envelope as a sync `200`). The job id is also in the + `Content-Location` and `X-Job-Id` response headers. +2. Poll `GET …/tasks/jobs/{job_id}` — `202` while running (`{data: {job_id, + status, progress}, meta}`), `200` with the final `{data, meta}` when done. + +Async is JSON-only: a text `format` combined with `Prefer: respond-async` is +rejected. + +`scripts/gi_predict.py` handles the submit/poll loop (2 s interval, 30-min cap) +and streams progress to stderr — no extra flags. + +## Error envelope + +Non-2xx responses carry: + +```json +{ "error": { "code": "…", "message": "…", "request_id": "…", "details": … } } +``` + +`error.code` is a closed 21-value enum: `bad_request`, `unauthorized`, +`forbidden`, `not_found`, `conflict`, `job_expired`, `payload_too_large`, +`sync_too_large`, `unsupported_format`, `validation_failed`, +`too_many_requests`, `rate_limited`, `internal_error`, `timeout`, +`insufficient_memory`, `model_not_found`, `task_not_supported_by_model`, +`model_loading`, `service_unavailable`, `http_error`, `unknown`. The schema tells +clients to treat an unlisted value as a generic failure, not a parse error. + +Switch on `code` first, then read `details` — `details` is keyed on the sibling +`code` and matches the declared schema: `validation_failed` carries the +`ValidationFailedDetails` object `{errors: [{loc, msg, type}, …]}`. Read it +defensively and never make control flow depend on its shape. + +`error.request_id` mirrors the `X-Request-Id` response header, and both are set +on every response — error envelopes (including `413 sync_too_large`) and success +envelopes, where it lives at `meta.request_id`. Every response carries +`RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset` and +`RateLimit-Policy`; a `429` adds `Retry-After`. + +Common: `401/403` (auth), `422 validation_failed` (bad body/length/model/options +— including over-length sequence), `429` (rate limit), `413 payload_too_large` +(raw body over 16 MiB), `415 unsupported_format` (an unsupported `format` query +value — never a silent fallback to JSON), `504 timeout` (large sync +request on a cold GPU). More: `references/errors.md`. + +Per-task `format` values: promoter `json|bed|bedgraph`, splice `json|bed|gff3`, +enhancer `json|bedgraph`, chromatin `json|bed`, annotation `json|bed|gff3`, +expression JSON only. Text formats are synchronous-only. + +## Composite: find genes, then predict expression + +``` +POST /v1/workflows/find-genes-and-predict-expression +``` + +Request `FindGenesAndPredictExpressionRequest`: `sequence` 1,000–500,000 bp and +`options` are both required, and `options.description` (cell type / assay +context) is required too — enforced at runtime rather than marked `required` in +`FindGenesAndPredictExpressionOptions`, so a missing or empty value is a +`422 validation_failed` with the message *"options.description is required (cell +type / assay context)"*. Send it. + +It annotates the sequence, centres a 9,198 bp window on each discovered gene's +TSS (padding with `N` up to half the window rather than dropping an edge gene), +and returns an expression prediction per gene. `meta.task_specific_counts` is +`{genes_found, genes_predicted, genes_skipped}` with +`genes_predicted + genes_skipped == genes_found`; per-gene causes are in +`data.expression_predictions[].skip_reason`. + +Above **50,000 bp** the composite forces async: a synchronous request over that +size is `413 sync_too_large` with `error.details = {sequence_length, threshold}`. +Retry the same body with `Prefer: respond-async`. + +## Authority + +Everything above describes the contract the Genomic Intelligence API serves at +`api.genomicintelligence.ai`: the six literal predict operations, the typed +`options` objects, the per-task floors, the published composite, the `Prefer` +parameter, the `code` enum and the `bio_spec` fields. The served schema at + is authoritative — check it +if a detail here does not match. diff --git a/nim-skills/genomic-intelligence-nim/references/authentication.md b/nim-skills/genomic-intelligence-nim/references/authentication.md new file mode 100644 index 0000000..3861865 --- /dev/null +++ b/nim-skills/genomic-intelligence-nim/references/authentication.md @@ -0,0 +1,44 @@ +# Authentication + +Every `/v1/*` call requires a partner bearer key. The skill sends it as +`Authorization: Bearer `. Public routes that need no key: `/health`, +`/docs`, `/redoc`, `/v1/openapi.json`. + +## Setting the key + +```bash +export GI_API_KEY=gi_yourkeyhere +``` + +Resolution order in `scripts/gi_client.py`: + +1. Explicit `--api-key` CLI flag (highest precedence). +2. `GI_API_KEY` environment variable. +3. Otherwise: a `RuntimeError` with onboarding instructions and exit code 2. + +Keys are bearer tokens beginning with `gi_`. Request one at +**contact@genomicintelligence.ai**. + +## Base URL + +Default: `https://api.genomicintelligence.ai`. Override for staging or a local +service: + +```bash +export GI_BASE_URL=https://staging.example.internal +# or per-invocation: +python scripts/gi_predict.py --task promoter --demo --base-url http://localhost:8001 +``` + +## Partner tiers + +Keys are scoped to a partner tier with concurrency and rate limits. If you hit +`429`, you have exceeded your concurrency or per-minute cap — back off and +retry. Higher-throughput needs: ask Genomic Intelligence to raise your tier. + +## Security notes + +- Never commit a real key. Keep it in the environment or a secrets manager. +- The key authorizes billed inference. Treat it like a credential. +- The skill never writes the key into `report.md`, `result.json`, or the + reproducibility bundle — only the request ID and base URL are recorded. diff --git a/nim-skills/genomic-intelligence-nim/references/errors.md b/nim-skills/genomic-intelligence-nim/references/errors.md new file mode 100644 index 0000000..a9b6d3d --- /dev/null +++ b/nim-skills/genomic-intelligence-nim/references/errors.md @@ -0,0 +1,113 @@ +# Errors, Async Polling & Limits + +## Error envelope + +Non-2xx responses carry a JSON `{error}` envelope, surfaced by the skill as +`API error: [ ] (request_id=)` on stderr (exit +code 2). The shape: + +```json +{ + "error": { + "code": "validation_failed", + "message": "human-readable explanation", + "request_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "details": { "errors": [ { "loc": ["body", "sequence"], "msg": "…" } ] } + } +} +``` + +Always quote the `request_id` when reporting an issue to Genomic Intelligence. +It mirrors the `X-Request-Id` response header, and both are set on every +response: error envelopes carry `error.request_id`, success envelopes carry +`meta.request_id`. Reading the header as a fallback remains sound practice. + +`code` is a **closed 21-value enum**: `bad_request`, `unauthorized`, `forbidden`, +`not_found`, `conflict`, `job_expired`, `payload_too_large`, `sync_too_large`, +`unsupported_format`, `validation_failed`, `too_many_requests`, `rate_limited`, +`internal_error`, `timeout`, `insufficient_memory`, `model_not_found`, +`task_not_supported_by_model`, `model_loading`, `service_unavailable`, +`http_error`, `unknown`. The schema explicitly says to treat an unlisted value as +a generic failure, not a parse error. + +**Branch on `code`, never on `details` or `loc`.** `details` is keyed on the +sibling `code` (`ValidationFailedDetails`, `TaskNotSupportedByModelDetails`, +`ModelNotFoundDetails`, `SyncTooLargeDetails`, `GenericDetails`, or null). A +validation failure carries the declared `{errors: [{loc, msg, type}, …]}` +object — the FastAPI error array wrapped under `errors`. Read it defensively +and keep control flow off it. + +## Common status codes + +| Status | `code` | Meaning | Action | +|---|---|---|---| +| 400 | `bad_request` | Malformed request | Read the message | +| 401 / 403 | `unauthorized` / `forbidden` | Missing / bad / revoked key | Re-check `GI_API_KEY` (see authentication.md) | +| 404 | `not_found` | **Unknown task** or unknown job id | Check the `--task` value (an unrecognised task is a 404, not a 422); a job may have expired | +| 410 | `job_expired` | Async job result no longer retained | Re-submit | +| 413 | `payload_too_large` | Raw request body over **16 MiB**, rejected before parsing | Split the input — this is the body cap, not the sequence cap | +| 413 | `sync_too_large` | Composite workflow called synchronously above 50,000 bp | Retry with `Prefer: respond-async`; `details` = `{sequence_length, threshold}` | +| 415 | `unsupported_format` | Unsupported `format` query value | Use a format the task supports — there is no silent fallback to JSON | +| 422 | `validation_failed` | Sequence under the task floor **or over the 500,000 bp cap**, out-of-range/missing `tss_index`, missing `options.description`, unknown body or `options` key | Read the message; fix the body | +| 429 | `rate_limited` / `too_many_requests` | Concurrency / rate cap exceeded | Back off (honour `Retry-After`); request a higher tier | +| 500 | `internal_error` | Server-side failure | Retry; if persistent, report with `request_id` | +| 503 | `service_unavailable` / `model_loading` | Backend transiently down or a model is loading | Retry with backoff | +| 504 | `timeout` | Large sync request on a cold GPU | Retry, or use a smaller sequence | + +Note that a sequence **over** 500,000 bp is a `422 validation_failed` +(`"sequence is 520000 bp; the maximum is 500000 bp"`, `loc +["body","sequence"]`) — *not* a `413`. `413` means only the 16 MiB raw-body cap +or the composite's synchronous-delivery cap. + +The skill validates length, the `expression` `--description`, and the +`--tss-index` bounds **before** any network call, so those failures (exit +code 1) never reach the API. Server-side, every expression contract violation +(sequence below 9,198 bp, missing/out-of-range `tss_index`, missing +`options.description`, unknown body field) is a `422 validation_failed`. There +is no opt-out flag, header, or query parameter; nothing is padded or clamped. +The `tss_index` checks come from a whole-model validator and report at +`loc: ["body"]`, never `body.tss_index`. + +Every response — success or error — carries `RateLimit-Limit`, +`RateLimit-Remaining`, `RateLimit-Reset` and `RateLimit-Policy`; a `429` adds +`Retry-After`. + +## Async polling (`annotation`) + +The skill runs `annotation` asynchronously by default; the API accepts either +mode on every task (`Prefer: respond-async` is declared on all six predict +operations, and annotation returns `200` synchronously without it). The flow +inside `scripts/gi_predict.py`: + +1. `POST /v1/tasks/annotation/predict` with header `Prefer: respond-async` + → returns `202` with `data.job_id`. +2. Poll `GET /v1/tasks/jobs/{job_id}`: + - `202` → still running; `data.progress` is streamed to stderr; sleep and re-poll. + - `200` → terminal; the body is the final `{data, meta}` envelope. + - other → raised as a `GIError`. + +Defaults: poll every 2 s, give up after 30 min. Typical real latency is ~20 s +for a ~20 kb sequence (longer on a cold GPU). + +## Limits + +- **Max sequence length:** 500,000 bp for every task (over → `422`). +- **Minimum sequence length, per task:** promoter 300, splice 100, enhancer 50, + chromatin 200, annotation 1,000, expression 9,198 bp. Under → `422`. The floor + is admission control, not regime — see `references/tasks.md`. +- **Expression minimum:** 9,198 bp is also the width of the single TSS-centred + window the model scores. Above that width, `tss_index` is required. +- **Raw request body:** 16 MiB, enforced before parsing (`413 + payload_too_large`). +- **Composite synchronous delivery:** 50,000 bp (`413 sync_too_large` above it). +- **Single record per request:** the runner refuses a multi-record FASTA rather + than concatenating it into one chimeric sequence. Split the file and run per + record. It also refuses bases outside `ACGTN` — deleting an IUPAC ambiguity + code would shift every position after it and score a sequence you did not + submit. +- **Rate / concurrency:** per partner tier; `429` signals you have exceeded it. + +Authoritative limits are published as `minLength`/`maxLength` on each task's +request schema in the live OpenAPI document at +. Numbers repeated in this +skill are mirrors. diff --git a/nim-skills/genomic-intelligence-nim/references/sequence-acquisition.md b/nim-skills/genomic-intelligence-nim/references/sequence-acquisition.md new file mode 100644 index 0000000..51bb71f --- /dev/null +++ b/nim-skills/genomic-intelligence-nim/references/sequence-acquisition.md @@ -0,0 +1,89 @@ +# Sequence Acquisition (Ensembl) + +The skill can turn a **gene symbol** or a **genomic region** into reference +sequence, so users don't have to bring a FASTA. This is handled by +`scripts/gi_fetch.py` (CLI) and `scripts/gi_ensembl.py` (the Ensembl REST +helpers it calls). Acquisition is separate from prediction: `gi_fetch.py` writes +a FASTA, then `gi_predict.py --input` consumes it. + +**No API key** — Ensembl REST (`rest.ensembl.org`) is public. Only the +*prediction* step needs `GI_API_KEY`. + +## Modes + +```bash +# Full gene body (any task except expression) +python scripts/gi_fetch.py --gene TP53 --out tp53.fa + +# Coordinate range +python scripts/gi_fetch.py --region chr17:7,661,779-7,687,546 --out region.fa + +# 9,198 bp TSS-centred window (expression; or send a wider locus + --tss-index) +python scripts/gi_fetch.py --gene HBB --for-expression --out hbb_tss.fa +``` + +`--gene` and `--region` are mutually exclusive. The resolved FASTA path is +printed on **stdout**; a provenance line (length, Ensembl ID, region, strand) +goes to **stderr**. Chain it: + +```bash +FASTA=$(python scripts/gi_fetch.py --gene TP53 --out tp53.fa) +python scripts/gi_predict.py --task promoter --input "$FASTA" --output out/ +``` + +## TSS-centring (why `--for-expression` exists) + +The expression model always scores **exactly 9,198 bp centred on the +transcription start site (TSS)**. You may either hand it a pre-cut 9,198 bp +window (what `--for-expression` builds) or hand it up to 500,000 bp plus +`--tss-index` and let the server slice. Either way you must know where the TSS +is — the endpoint never discovers it, never pads, and never +reverse-complements. You cannot reliably build this from gene-body coordinates: +the gene's annotated start/end can sit far from the real TSS — HBB's gene end is +2,324 bp from its canonical TSS, ACTB's is 33,301 bp. Mis-centring tanks the +prediction. + +`--for-expression` resolves the gene's **canonical transcript** (Ensembl +`expand=1`), takes the TSS from it (transcript start on the + strand, end on the +− strand), and grabs 4,599 bp upstream + 4,598 bp downstream on the gene's +strand = 9,198 bp. It validates the returned length exactly. Because TSS +centring needs a transcript, `--for-expression` works only with `--gene`, never +`--region`. + +## Species & assembly + +- **Default: human, GRCh38** (Ensembl's current human assembly). +- Non-human: pass `--species ` — the Ensembl production name, + which is lowercase and underscored: `mus_musculus`, `drosophila_melanogaster`, + `saccharomyces_cerevisiae`. `mouse`, `Drosophila`, or `Drosophila melanogaster` + will fail with a 400; the error message says so. +- The **enhancer** default model (DeepSTARR) is *Drosophila* — match the species + to the model. See [tasks.md](tasks.md). + +## Strand & coordinates + +- `--region` defaults to `--strand 1` (plus). Pass `--strand -1` only for a + strand-sensitive task on a known minus-strand locus. Gene fetch uses the + gene's own strand automatically. +- Region strings are lenient: commas, `chr` prefix, en/em dashes, and `..` are + all accepted (e.g. `chr8:127,680,000..127,800,000`). A bare position with no + end defaults to a 1,000 bp window. +- `--flank-bp N` adds N bp on each side of a gene body or region (not used with + `--for-expression`). + +## When to skip acquisition + +Supply a FASTA directly to `gi_predict.py --input` when the sequence is **not** +reference genome — variant-bearing, edited, synthetic, or from a non-Ensembl +assembly. Acquisition only returns reference sequence for the requested +coordinates. + +## Limits + +Reference fetch is bounded by the task's own input cap (500,000 bp for every +task) and its floor (promoter 300, splice 100, enhancer 50, chromatin 200, +annotation 1,000, expression 9,198 bp). Fetch at least the model's +`context_window_bp` if you want the score to reflect real sequence rather than +padding — see `references/tasks.md`. Ensembl enforces its own per-request size +limits on `/sequence/region`; very large ranges may be rejected upstream — +fetch in pieces or narrow the region. diff --git a/nim-skills/genomic-intelligence-nim/references/tasks.md b/nim-skills/genomic-intelligence-nim/references/tasks.md new file mode 100644 index 0000000..78f1b90 --- /dev/null +++ b/nim-skills/genomic-intelligence-nim/references/tasks.md @@ -0,0 +1,182 @@ +# Tasks Reference + +The Genomic Intelligence `/v1` API exposes six DNA-sequence tasks. Each is its +own published operation — `POST /v1/tasks/promoter/predict`, +`/v1/tasks/splice/predict`, … — with its own request schema, its own minimum +length, and its own closed `options` object. There is no shared request model; +the schemas are per-task. Each returns a `{data, meta}` envelope. This skill's `scripts/gi_predict.py --task +` selects the endpoint; the table below documents what differs per task. + +Source of truth for bounds and models: the live OpenAPI doc at +, where each task's floor is +published as `minLength` on its own request schema, and the public task +reference at . The numbers below and in +`scripts/gi_predict.py` are **mirrors** — if they disagree with the served +schema, the served schema wins. + +## Summary + +| Task | Default architecture | Recommended mode | Accepted length | `context_window_bp` | Demo fixture | +|---|---|---|---|---|---| +| promoter | sliding-window promoter caller | sync | 300–500,000 bp | 2,000 bp | `promoter_tp53.fa` | +| splice | BigBird long-context | sync | 100–500,000 bp | 15,000 bp | `splice_hbb.fa` | +| enhancer | DeepSTARR (*Drosophila* S2) | sync | 50–500,000 bp | 249 bp | `enhancer_eve.fa` | +| chromatin | DeepSEA multi-track | sync | 200–500,000 bp | 1,000 bp | `chromatin_active_promoter_chr19.fa` | +| expression | TSS-window expression regressor | sync | **9,198–500,000 bp** | n/a (`trained_window_bp` 9,198) | `expression_hbb_k562.fa` | +| annotation | structure-aware gene finder | async | 1,000–500,000 bp | n/a | `annotation_tp53.fa` | + +`Recommended mode` is guidance, not a constraint — every task accepts both. Omit `Prefer` for a synchronous `200`; send `Prefer: respond-async` for a `202` plus `GET /v1/tasks/jobs/{job_id}`. Only the composite workflow enforces a mode, rejecting sync above 50,000 bp with `413 sync_too_large`. + +There are no per-model floors: a task's minimum is the strictest its models need, +and every model stays listed and loadable. + +**Floor ≠ regime.** The minimum is admission control, enforced at request +validation before any model loads. A request above the floor but shorter than the +selected model's `bio_spec.context_window_bp` is **accepted and scored** — against +a window padded out to the context window. Enhancer is the sharp case: the bound +is 50 bp but the enhancer context window is 249 bp, so 50–248 bp is scored +mostly on padding. Compare your length against `context_window_bp` (from +`GET /v1/tasks/{task}/models`) to know whether the model saw real sequence. +Longer-than-context input is fine — the scanner steps a prediction window at a +time and pads only the final partial window. + +Under the floor and over the 500,000 bp cap are both `422 validation_failed` at +`loc ["body","sequence"]` — over-length is **not** a `413`. All lengths are +measured after whitespace is stripped. + +To list the models available for a task and pass a non-default one, use +`--model `. The model registry is the single source of truth; do not invent +model IDs. + +## `options` per task + +Every `options` object is closed (`additionalProperties: false`); an unknown key +is a hard `422 validation_failed` (`type: "extra_forbidden"`, +`loc: ["body","options",""]`), never ignored. + +| Task | Keys | +|---|---| +| promoter | `threshold` (0–1, default 0.5) | +| splice | `threshold` (0–1, default 0.5), `site_types` (subset of `["donor","acceptor"]`, default both) | +| enhancer | *(none)* | +| chromatin | `threshold` (0–1, default 0.5) | +| annotation | `batch_size` (1–128, default 8), `shift_coordinates`, `reverse_complement` (default true) | +| expression | `description` — **required**, and the only key | + +`--description` therefore applies to `expression` only; the runner drops it (with +a warning) on any other task rather than letting the server 422. + +## promoter + +Predicts promoter regions over a sliding window. `data.summary` reports +`promoter_windows` / `total_windows`; `data.regions` lists windows with +`name`, `start`, `end`, `score`, and `strand`. Output also available as BED / +bedGraph via the API directly. + +Non-human models exist (Drosophila, yeast, Arabidopsis) — pass `--model`. The +default promoter model targets human/mammalian sequence. + +## splice + +Predicts splice **donor** and **acceptor** sites. `data.sites` lists each site +with `name`, `start`, `end`, `site_type` (donor/acceptor), `score`, and +`strand`. The default splice model uses a BigBird long-context architecture. Good demo: a +gene with known introns (the bundled `splice_hbb.fa` is HBB). + +`start` and `end` bound a tokenizer span, not the exon/intron junction. The span +is one variable-width token — 4–10 bp across the sequences measured so far — +reported with a `token_index`, and the junction lies somewhere inside it. Do not +derive a base position from the pair, and do not intersect it against reference +annotation as though it marked a boundary. + +## enhancer + +Scores enhancer activity. The default enhancer model (DeepSTARR) reports +**developmental** and **housekeeping** enhancer scores — +`summary.dev_score_max` / `summary.hk_score_max` per window. DeepSTARR is a +*Drosophila* model; the bundled demo (`enhancer_eve.fa`, the eve locus) reflects +that. Use the appropriate model for your organism. + +The 50 bp floor is the strictest gate any enhancer model needs. It is not +a biologically meaningful range: the model's `context_window_bp` is 249, so a +50–248 bp request is accepted and scored against a padded 249 bp window. Submit +at least 249 bp if you want the score to reflect real sequence. + +## chromatin + +Annotates chromatin state across a large panel of tracks (histone marks, DNase, +ATAC, TF binding) — the default chromatin model (DeepSEA architecture) covers hundreds of +features. `summary.total_annotations` is the headline; the full per-track matrix +is in `data`. Output also available as BED via the API. + +## expression + +Predicts gene expression as **log(TPM+1)** from a fixed window. Its published +operation is `POST /v1/tasks/expression/predict` with schema +`ExpressionPredictRequest`, which — unlike the other five — requires `options` as +well as `sequence`. Three requirements the skill enforces locally: + +1. **9,198–500,000 bp.** The model always scores exactly one 9,198 bp window + **centred on the TSS** (2 × 4,599) — `sequence[tss_index-4599 : + tss_index+4599]` — but the endpoint accepts up to 500 kb and slices for you. + Below 9,198 bp is rejected; nothing is padded or truncated. +2. **`tss_index`** (`--tss-index`) — the 0-based TSS offset into the + **whitespace-stripped** sequence. Required unless the sequence is exactly + 9,198 bp, where it defaults to 4,599 (the only legal value there). Bounds: + `4599 ≤ tss_index ≤ len(sequence) − 4599`. The endpoint does not find the TSS + for you and does not reverse-complement — submit gene-sense sequence. +3. **`--description`** — a cell-type / assay context string (e.g. `"K562 + cells"`), passed as `options.description`. Required, and the only key + `options` accepts on this task. + +The server reports both `tss_index` violations — "required unless exactly +9,198 bp" and the range check — from a whole-model validator, so they arrive at +`loc: ["body"]`, **never** `body.tss_index`. Match on +`error.code == "validation_failed"`; use the message for display only, and never +branch on `loc`. + +`data.prediction.expression_log_tpm` (and `expression_tpm`) hold the result. +`meta.task_specific_counts` carries `tss_index` and `scored_window` +(`[start, end]`, always 9,198 wide) — check it, because a `tss_index` that is in +range but wrong (e.g. counted over raw FASTA characters including newlines) +scores the wrong window and still returns `200`. `data.input.sequence_length` is +the **scored** 9,198; the length you submitted is +`data.input.submitted_sequence_length`. + +## annotation + +De-novo gene / transcript structure prediction — transcript intervals and +strand, no reference annotation needed. **The skill always runs it async** +(`Prefer: respond-async` is a declared header parameter on every predict +operation, so any task can be run this way; annotation is the one that needs it): +the skill submits with +`Prefer: respond-async`, receives a `job_id`, and polls +`GET /v1/tasks/jobs/{job_id}` until terminal (HTTP 200). Typical latency ~20 s +for ~20 kb; progress is streamed to stderr. `data.transcripts` lists each +predicted transcript with `name`, `start`, `end`, `strand`, and `score` (plus +structure fields: `length`, `tss_position`, `polya_position`, `transcript_type`, +`exons`, `introns`, `cds`). + +## Composite: find genes, then predict expression + +`POST /v1/workflows/find-genes-and-predict-expression` — "what genes are in this +region, and how are they expressed?". Request +`FindGenesAndPredictExpressionRequest`: `sequence` 1,000–500,000 bp and `options` +both required; send `options.description` (cell type / assay context) too — it is +enforced at runtime and a missing or empty value is a `422 validation_failed`. +Optional `annotation_model`, `expression_model`, `batch_size` (1–128, default 8), +`shift_coordinates`. + +It annotates the sequence, cuts a TSS-centred 9,198 bp window per discovered gene +(padding with `N` up to half the window rather than dropping an edge gene — the +direct expression route refuses to pad at all), and returns a prediction per +gene. `meta.task_specific_counts` = `{genes_found, genes_predicted, +genes_skipped}` with `genes_predicted + genes_skipped == genes_found`; per-gene +causes in `data.expression_predictions[].skip_reason`. + +Above **50,000 bp** it forces async: a synchronous request over that size is +`413 sync_too_large` with `error.details = {sequence_length, threshold}`. Retry +the same body with `Prefer: respond-async`. + +`scripts/gi_predict.py` does not wrap this workflow; call it directly (see +`references/api.md`). diff --git a/nim-skills/genomic-intelligence-nim/scripts/gi_client.py b/nim-skills/genomic-intelligence-nim/scripts/gi_client.py new file mode 100644 index 0000000..b8f79f4 --- /dev/null +++ b/nim-skills/genomic-intelligence-nim/scripts/gi_client.py @@ -0,0 +1,369 @@ +"""Client for the Genomic Intelligence API. + +Self-contained — this module has no dependencies beyond ``requests`` and is +imported by ``gi_predict.py`` (same directory). It wraps the hosted predict +contract for all six DNA-sequence tasks (promoter, splice, enhancer, +chromatin, expression, annotation). + +Each task is its own published operation — ``POST /v1/tasks/promoter/predict``, +``/v1/tasks/splice/predict``, and so on — with its own request schema, its own +``minLength``, and its own closed ``options`` object. The paths differ only in +the task segment, so one formatted URL covers all six; the request bodies do +not, which is why the per-task validation below is not shared. ``options`` is +``additionalProperties: false`` on every task, so an unrecognised key is a hard +``422 validation_failed`` rather than being ignored — never forward option keys +you have not confirmed against the live schema. + +Auth resolution order: +1. Explicit ``api_key=`` constructor arg (``--api-key`` on the CLI). +2. ``GI_API_KEY`` environment variable. + +If neither is supplied, ``resolve_api_key`` raises ``RuntimeError`` with +onboarding instructions. Request a partner key at +contact@genomicintelligence.ai, then ``export GI_API_KEY=gi_…``. + +Base URL: ``GI_BASE_URL`` env, default ``https://api.genomicintelligence.ai``. + +Contract reference: https://docs.genomicintelligence.ai +""" + +from __future__ import annotations + +import os +import time +from pathlib import Path +from typing import Any, Dict, Mapping, Optional, Tuple + +import requests + + +DEFAULT_BASE_URL = "https://api.genomicintelligence.ai" + +MISSING_KEY_MESSAGE = ( + "GI_API_KEY is not set. This skill calls the hosted Genomic " + "Intelligence API (https://api.genomicintelligence.ai) and requires a " + "partner bearer key.\n\n" + "Request a key at contact@genomicintelligence.ai, then:\n" + " export GI_API_KEY=gi_yourkeyhere\n\n" + "See references/authentication.md for details." +) + + +# IUPAC ambiguity codes. Listed so the parser can say *why* it is refusing: +# these are legitimate FASTA content the model cannot score, which is a +# different problem from a stray character and deserves a different hint. +_IUPAC_AMBIGUITY = "RYSWKMBDHV" + + +class FastaError(ValueError): + """Malformed FASTA input, rejected rather than silently repaired. + + Subclasses ``ValueError`` so a caller doing broad input validation still + catches it, while callers that want to distinguish input problems from + API problems can catch this specifically. + """ + + +class GIError(RuntimeError): + """Non-2xx response from the API. Mirrors the ``{error}`` envelope.""" + + def __init__( + self, + status: int, + body: Dict[str, Any], + headers: Optional[Mapping[str, str]] = None, + ): + err = (body or {}).get("error", {}) if isinstance(body, dict) else {} + self.status = status + self.code = err.get("code", "http_error") + self.message = err.get("message", "") + # Prefer the envelope's request_id; every error response carries it. + # Fall back to the X-Request-Id header for robustness (e.g. a non-JSON + # body from a proxy) — support tickets always need a correlation id. + self.request_id = err.get("request_id") or (headers or {}).get("X-Request-Id") + self.details = err.get("details") + rid = self.request_id or "unset" + super().__init__(f"[{status} {self.code}] {self.message} (request_id={rid})") + + +def resolve_api_key(explicit: Optional[str] = None) -> str: + """Apply the auth resolution order documented at module top. + + Raises ``RuntimeError`` with onboarding instructions if no key is found. + """ + if explicit: + return explicit + env = os.environ.get("GI_API_KEY") + if env: + return env + raise RuntimeError(MISSING_KEY_MESSAGE) + + +class Client: + """Thin synchronous client for the /v1/tasks//predict endpoints.""" + + def __init__( + self, + api_key: Optional[str] = None, + base_url: Optional[str] = None, + timeout: float = 300.0, + ) -> None: + self.api_key = resolve_api_key(api_key) + self.base_url = ( + base_url or os.environ.get("GI_BASE_URL") or DEFAULT_BASE_URL + ).rstrip("/") + self.timeout = timeout + self._session = requests.Session() + self._session.headers.update( + { + "Authorization": f"Bearer {self.api_key}", + "Content-Type": "application/json", + "Accept": "application/json", + "User-Agent": "BioNeMo-GI-Skill/0.1.0", + } + ) + + def _check(self, resp: requests.Response) -> Dict[str, Any]: + malformed = False + try: + body = resp.json() + except ValueError: + # http_error is a published enum value; the response arrived with a + # status and body, it just was not JSON. Client-origin errors carry + # no request_id, which distinguishes them from server codes. + body = {"error": {"code": "http_error", "message": resp.text[:200]}} + malformed = True + if not resp.ok: + raise GIError(resp.status_code, body, resp.headers) + if malformed: + # A 2xx whose body did not parse must not be returned as a result. + # The synthetic error envelope above is built for the failure path; + # returning it here would hand the caller {"error": ...} with ok=true. + raise GIError(resp.status_code, body, resp.headers) + return body + + @staticmethod + def _require_envelope(body: Any, resp: requests.Response) -> Dict[str, Any]: + """A prediction or job result carries a ``{data, meta}`` object. + + Only for those two. ``/health`` and ``GET /v1/tasks/{task}/models`` are + deliberately un-enveloped and must not be checked here. A 200 with an + empty, null or non-object body would otherwise reach the report writer + and fail there with an AttributeError or KeyError, which reads as a + client bug rather than a bad response. + + ``data`` must also be non-empty. All three call sites read content out + of it — a prediction payload, ``data.job_id``, a finished job's result — + so ``{"data": {}}`` is malformed for every one of them, and accepting it + wrote a zero-valued report and printed ``"ok": true`` with no prediction + in it. + """ + data = body.get("data") if isinstance(body, dict) else None + if not isinstance(data, dict) or not data: + raise GIError( + resp.status_code, + { + "error": { + "code": "http_error", + "message": ( + "expected a JSON object with a non-empty object " + f"'data' key, got {type(body).__name__} with data=" + + ("empty object" if isinstance(data, dict) else type(data).__name__) + ), + } + }, + resp.headers, + ) + return body + + def health(self) -> Dict[str, Any]: + r = self._session.get(f"{self.base_url}/health", timeout=self.timeout) + return self._check(r) + + def predict( + self, + task: str, + sequence: str, + sequence_name: str = "sequence", + model: Optional[str] = None, + options: Optional[Dict[str, Any]] = None, + tss_index: Optional[int] = None, + ) -> Dict[str, Any]: + body: Dict[str, Any] = {"sequence": sequence, "sequence_name": sequence_name} + if model is not None: + body["model"] = model + if options is not None: + body["options"] = options + # expression only: 0-based TSS offset into the whitespace-stripped + # sequence. Required by the API unless the sequence is exactly 9,198 bp. + if tss_index is not None: + body["tss_index"] = tss_index + r = self._session.post( + f"{self.base_url}/v1/tasks/{task}/predict", + json=body, + timeout=self.timeout, + ) + return self._require_envelope(self._check(r), r) + + def submit_async( + self, + task: str, + sequence: str, + sequence_name: str = "sequence", + model: Optional[str] = None, + options: Optional[Dict[str, Any]] = None, + ) -> str: + body: Dict[str, Any] = {"sequence": sequence, "sequence_name": sequence_name} + if model is not None: + body["model"] = model + if options is not None: + body["options"] = options + r = self._session.post( + f"{self.base_url}/v1/tasks/{task}/predict", + headers={"Prefer": "respond-async"}, + json=body, + timeout=self.timeout, + ) + body = self._require_envelope(self._check(r), r) + job_id = body["data"].get("job_id") + if not isinstance(job_id, str) or not job_id: + raise GIError( + r.status_code, + { + "error": { + "code": "http_error", + "message": "async submit returned no job_id in data", + } + }, + r.headers, + ) + return job_id + + def get_job(self, job_id: str) -> requests.Response: + return self._session.get( + f"{self.base_url}/v1/tasks/jobs/{job_id}", timeout=self.timeout + ) + + def wait_for_job( + self, + job_id: str, + poll_interval: float = 2.0, + max_wait: float = 30 * 60, + on_progress=None, + ) -> Dict[str, Any]: + deadline = time.monotonic() + max_wait + while True: + r = self.get_job(job_id) + if r.status_code == 200: + try: + body = r.json() + except ValueError: + raise GIError( + r.status_code, + { + "error": { + "code": "http_error", + "message": f"job {job_id} returned a non-JSON 200", + } + }, + r.headers, + ) from None + return self._require_envelope(body, r) + if r.status_code == 202: + if on_progress is not None: + try: + on_progress((r.json().get("data") or {}).get("progress") or {}) + except Exception: + pass + if time.monotonic() > deadline: + raise TimeoutError( + f"job {job_id} did not finish within {max_wait}s" + ) + time.sleep(poll_interval) + continue + try: + body = r.json() + except ValueError: + body = {"error": {"code": "http_error", "message": r.text[:200]}} + raise GIError(r.status_code, body, r.headers) + + +def read_fasta(path) -> Tuple[str, str]: + """Parse a single-record FASTA. Returns (sequence_name, sequence). + + Rejects malformed input rather than repairing it. Earlier versions + silently deleted every character outside ``ACGTN`` and concatenated a + multi-record file into one chimeric sequence under the first record's + name. Both are unrecoverable once they happen: deleting an IUPAC + ambiguity code shifts every base after it, so the model scores a + sequence the caller never supplied and returns a confident result with + nothing to indicate the substitution. + + Whitespace, blank lines and lowercase input are still handled — those + are formatting, not content. + + Raises: + FastaError: more than one record, a base outside ``ACGTN``, or + sequence appearing before the first header. + """ + name = None + record_names: list[str] = [] + seq_parts: list[str] = [] + offenders: Dict[str, int] = {} + with open(Path(path)) as fh: + for lineno, line in enumerate(fh, start=1): + line = line.strip() + if not line: + continue + if line.startswith(">"): + header = line[1:].split()[0] if line[1:].split() else "sequence" + record_names.append(header) + if name is None: + name = header + continue + if name is None: + raise FastaError( + f"{path}: sequence on line {lineno} before any '>' header. " + f"Those bases would be scored under the first record's name, " + f"and the coordinates returned would not describe what you " + f"submitted. Add a header, or remove the stray lines." + ) + # Whitespace anywhere in the line is formatting, not content: the + # API strips newlines, spaces and tabs before measuring length, so + # a space-grouped body (10-base blocks from a viewer or Sanger + # output) must parse here too. Stripping it moves nothing in + # coordinate space, which is what separates it from an ambiguity + # code we refuse to guess at. + upper = "".join(line.split()).upper() + for char in upper: + if char not in "ACGTN": + offenders.setdefault(char, lineno) + seq_parts.append(upper) + + if len(record_names) > 1: + shown = ", ".join(record_names[:3]) + more = f", … ({len(record_names)} total)" if len(record_names) > 3 else "" + raise FastaError( + f"{path}: expected a single FASTA record, found {len(record_names)} " + f"({shown}{more}). Concatenating them would submit a chimeric " + f"sequence under one name — split the file and submit one record " + f"per request." + ) + + if offenders: + detail = ", ".join( + f"{char!r} (first at line {lineno})" + for char, lineno in sorted(offenders.items(), key=lambda kv: kv[1])[:5] + ) + ambiguity = sorted(c for c in offenders if c in _IUPAC_AMBIGUITY) + hint = ( + " IUPAC ambiguity codes cannot be scored; resolve them to explicit " + "bases or submit a different region." + if ambiguity + else " Remove or resolve them before submitting." + ) + raise FastaError( + f"{path}: sequence contains characters outside ACGTN: {detail}.{hint}" + ) + + return name or "sequence", "".join(seq_parts) diff --git a/nim-skills/genomic-intelligence-nim/scripts/gi_ensembl.py b/nim-skills/genomic-intelligence-nim/scripts/gi_ensembl.py new file mode 100644 index 0000000..52bd8a6 --- /dev/null +++ b/nim-skills/genomic-intelligence-nim/scripts/gi_ensembl.py @@ -0,0 +1,290 @@ +"""Ensembl REST helpers: resolve a gene symbol or genomic region to reference sequence. + +Self-contained — the only dependency is ``requests``. It calls the public +Ensembl REST API (rest.ensembl.org); no API key is required, and no Genomic +Intelligence key is used here. + +Three flows used by the skill's acquisition CLI (``gi_fetch.py``): + - fetch_by_symbol(symbol) → full gene-body sequence + - fetch_region_by_coords("chr:start-end") → region sequence + - fetch_gene_window_for_expression(symbol) → exactly 9,198 bp centred on the TSS + +Base URL: ``GI_ENSEMBL_URL`` env, default ``https://rest.ensembl.org``. +""" + +from __future__ import annotations + +import os +import re +from dataclasses import dataclass +from typing import Optional, Tuple + +import requests + +# Expression scoring window: the fixed 9,198 bp the expression task scores, +# 4,599 bp either side of the TSS. Published as ``minLength`` on the expression +# request schema at https://api.genomicintelligence.ai/v1/openapi.json. Building +# the window here means the request needs no tss_index; longer submissions must +# supply one. +EXPRESSION_SEQUENCE_LENGTH = 9_198 + +DEFAULT_ENSEMBL_URL = "https://rest.ensembl.org" +_USER_AGENT = "BioNeMo-GI-Skill/0.1.0" + + +def ensembl_base_url() -> str: + return os.environ.get("GI_ENSEMBL_URL", DEFAULT_ENSEMBL_URL).rstrip("/") + + +class EnsemblError(RuntimeError): + pass + + +@dataclass +class GeneLocus: + ensembl_id: str + seq_region: str # e.g. "17" + start: int + end: int + strand: int # +1 or -1 + species: str + display_name: str + # Canonical-transcript boundaries (when resolved via expand=1). Gene-level + # start/end can sit far from the real TSS — HBB's gene end is 2,324 bp from + # its canonical TSS, ACTB's is 33,301 bp — so expression windowing must use + # the canonical transcript, not the gene body. + canonical_start: Optional[int] = None + canonical_end: Optional[int] = None + + @property + def tss(self) -> int: + """Transcription start site: transcript start on +strand, end on -strand. + + Requires the canonical transcript, and raises rather than falling back + to the gene body. A gene-body fallback puts ACTB's window 33,301 bp off + its real TSS. That window is still exactly 9,198 bp, so the + client-side size gate passes, no ``tss_index`` is sent, and the API + returns a confident score for the wrong locus — correctly sized, + wrongly centred, with no client-side tell. Refusing is the only honest + option. + + Raises: + EnsemblError: no canonical transcript was resolved for this gene. + """ + if self.canonical_start is None or self.canonical_end is None: + raise EnsemblError( + f"{self.display_name} ({self.ensembl_id}): no canonical transcript " + f"resolved, so the TSS is unknown. Expression windowing needs the " + f"canonical transcript — the gene body can sit tens of kb away " + f"(ACTB: 33,301 bp) and would score the wrong window at full " + f"confidence. Re-fetch with expand=1, or supply the window and " + f"tss_index explicitly." + ) + return self.canonical_start if self.strand >= 0 else self.canonical_end + + +def _get(path: str, *, headers: Optional[dict] = None, params: Optional[dict] = None, + timeout: float = 30.0) -> requests.Response: + """GET against the Ensembl REST base, mapping transport failures to EnsemblError.""" + base_headers = {"User-Agent": _USER_AGENT, "Accept": "application/json"} + if headers: + base_headers.update(headers) + url = f"{ensembl_base_url()}{path}" + try: + return requests.get(url, headers=base_headers, params=params, timeout=timeout) + except requests.RequestException as exc: + raise EnsemblError( + f"could not reach Ensembl at {ensembl_base_url()} ({type(exc).__name__})" + ) from exc + + +def lookup_symbol(symbol: str, species: str = "human", expand: bool = False) -> GeneLocus: + """Resolve a gene symbol to its genomic locus. + + With ``expand=True`` the gene's transcripts are pulled too, and the + canonical transcript's boundaries are recorded on the locus (used for + TSS-accurate expression windowing). + """ + r = _get( + f"/lookup/symbol/{species}/{symbol}", + params={"expand": 1 if expand else 0}, + ) + if r.status_code == 404: + raise EnsemblError(f"gene symbol {symbol!r} not found in {species}") + if not r.ok: + hint = "" + if r.status_code == 400: + # A 400 here is almost always an unrecognised species token: Ensembl + # wants the production name (lowercase, underscored), so 'drosophila' + # / 'Drosophila melanogaster' fail where 'drosophila_melanogaster' + # works. Point the caller at the canonical form. + hint = ( + " — check the species token: Ensembl expects a production name " + "(lowercase, underscored), e.g. 'drosophila_melanogaster', " + "'mus_musculus', not 'drosophila' or 'Drosophila melanogaster'" + ) + raise EnsemblError( + f"Ensembl lookup failed ({r.status_code}) for {symbol!r} " + f"in species {species!r}{hint}" + ) + d = r.json() + canonical_start: Optional[int] = None + canonical_end: Optional[int] = None + if expand: + transcripts = d.get("Transcript") or [] + canonical = next((t for t in transcripts if t.get("is_canonical") == 1), None) + if canonical is not None: + canonical_start = int(canonical["start"]) + canonical_end = int(canonical["end"]) + return GeneLocus( + ensembl_id=d["id"], + seq_region=str(d["seq_region_name"]), + start=int(d["start"]), + end=int(d["end"]), + strand=int(d.get("strand", 1)), + species=species, + display_name=d.get("display_name", symbol), + canonical_start=canonical_start, + canonical_end=canonical_end, + ) + + +def fetch_region(seq_region: str, start: int, end: int, species: str = "human", + strand: int = 1) -> str: + """Fetch raw nucleotide sequence for a 1-based inclusive region.""" + region = f"{seq_region}:{start}..{end}:{strand}" + r = _get(f"/sequence/region/{species}/{region}", headers={"Accept": "text/x-fasta"}) + if not r.ok: + raise EnsemblError(f"Ensembl sequence fetch failed ({r.status_code}) for {region}") + lines = [ln for ln in r.text.splitlines() if ln and not ln.startswith(">")] + return "".join(lines).upper() + + +# Coordinate string → (chrom, start, end). Deliberately lenient about the forms +# users paste: commas, en/em dashes, `..`, and an optional `chr` prefix. +_REGION_RE = re.compile(r"^(?:chr)?([A-Za-z0-9]+):(\d+)(?:-(\d+))?$", re.IGNORECASE) + + +def parse_region(text: str) -> Tuple[str, int, int]: + """Parse 'chr8:127,680,000-127,800,000' → ('8', 127680000, 127800000). + + Accepts commas, en/em-dashes and ``..`` (normalised to ``-``), an optional + ``chr`` prefix, and spaces around separators. A bare position (no end) + defaults to a 1,000 bp window. Raises EnsemblError if unparseable. + """ + normalized = ( + text.replace(",", "") + .replace("–", "-") # en dash + .replace("—", "-") # em dash + .replace("..", "-") + ) + normalized = re.sub(r"\s*-\s*", "-", normalized) + normalized = re.sub(r"\s*:\s*", ":", normalized).strip() + m = _REGION_RE.match(normalized) + if not m: + raise EnsemblError( + f"could not parse genomic region {text!r}; expected e.g. " + "'chr8:127,680,000-127,800,000'" + ) + chrom = m.group(1) + start = int(m.group(2)) + end = int(m.group(3)) if m.group(3) else start + 1000 + if start < 1: + raise EnsemblError(f"region start must be >= 1 (got {start})") + if end < start: + raise EnsemblError(f"region end ({end:,}) is before start ({start:,})") + return chrom, start, end + + +def fetch_region_by_coords(region: str, species: str = "human", strand: int = 1, + flank_bp: int = 0) -> Tuple[str, dict]: + """Coordinate string → reference sequence + meta. Plus strand by default.""" + chrom, raw_start, raw_end = parse_region(region) + start = max(1, raw_start - flank_bp) + end = raw_end + flank_bp + seq = fetch_region(chrom, start, end, species, strand=strand) + meta = { + "region": f"{chrom}:{start}-{end}", + "strand": strand, + "species": species, + "length": len(seq), + } + return seq, meta + + +def fetch_by_symbol(symbol: str, species: str = "human", flank_bp: int = 0) -> Tuple[str, dict]: + """Symbol → full gene-body sequence (optionally flanked). Returns (seq, meta).""" + locus = lookup_symbol(symbol, species) + start = max(1, locus.start - flank_bp) + end = locus.end + flank_bp + seq = fetch_region(locus.seq_region, start, end, species, strand=locus.strand) + meta = { + "ensembl_id": locus.ensembl_id, + "region": f"{locus.seq_region}:{start}-{end}", + "strand": locus.strand, + "species": species, + "gene": locus.display_name, + } + return seq, meta + + +def expression_window_bounds(tss: int, strand: int) -> Tuple[int, int]: + """Genomic bounds of the EXPRESSION_SEQUENCE_LENGTH window around a TSS. + + The API scores ``sequence[tss_index-4599 : tss_index+4599]`` and, for a + submission of exactly EXPRESSION_SEQUENCE_LENGTH bp, defaults ``tss_index`` + to 4,599 — the only legal value. So the TSS must land at offset 4,599 of + the sequence *as submitted*, and which genomic base that is depends on the + strand: Ensembl reverse-complements the region for ``strand=-1``, so the + sequence reads from ``end`` down to ``start`` and the TSS sits at + ``end - tss`` rather than ``tss - start``. Giving the extra base to the + high side on the minus strand puts both strands at 4,599. + + A one-base error here cannot be caught downstream: the window is still + exactly 9,198 bp, so the client-side size gate passes, no ``tss_index`` is + sent, and the API returns a confident score for a window shifted by one. + """ + half = EXPRESSION_SEQUENCE_LENGTH // 2 # 4599 + if strand == -1: + start, end = tss - half + 1, tss + half + else: + start, end = tss - half, tss + half - 1 + return start, end + + +def fetch_gene_window_for_expression(symbol: str, species: str = "human") -> Tuple[str, dict]: + """Symbol → exactly EXPRESSION_SEQUENCE_LENGTH bp centred on the TSS. + + The expression model demands a precise window. We take the TSS from the + gene's *canonical transcript* (expand=1) — gene-body boundaries can sit + thousands of bp from the real TSS (HBB: 2,324 bp; ACTB: 33,301 bp), which + would mis-centre the window and tank the prediction — and take the window + from ``expression_window_bounds``, which centres it the way the API reads + it on either strand. + """ + locus = lookup_symbol(symbol, species, expand=True) + tss = locus.tss + start, end = expression_window_bounds(tss, locus.strand) + if start < 1: + raise EnsemblError( + f"{symbol} TSS too close to chromosome start to extract a " + f"{EXPRESSION_SEQUENCE_LENGTH} bp window" + ) + seq = fetch_region(locus.seq_region, start, end, species, strand=locus.strand) + if len(seq) != EXPRESSION_SEQUENCE_LENGTH: + raise EnsemblError( + f"expected {EXPRESSION_SEQUENCE_LENGTH} bp, Ensembl returned {len(seq)}" + ) + meta = { + "ensembl_id": locus.ensembl_id, + "tss": tss, + # Always canonical: locus.tss raises rather than falling back to the + # gene body, so no other provenance is reachable. + "tss_source": "canonical-transcript", + "region": f"{locus.seq_region}:{start}-{end}", + "strand": locus.strand, + "species": species, + "gene": locus.display_name, + "window": "TSS-centred", + } + return seq, meta diff --git a/nim-skills/genomic-intelligence-nim/scripts/gi_fetch.py b/nim-skills/genomic-intelligence-nim/scripts/gi_fetch.py new file mode 100644 index 0000000..ebe28f3 --- /dev/null +++ b/nim-skills/genomic-intelligence-nim/scripts/gi_fetch.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +"""Resolve a gene or genomic region to a FASTA file, via Ensembl. + +This is the *acquisition* half of the skill. The agent calls it when the +user names a gene or locus instead of supplying a FASTA — it fetches reference +sequence from Ensembl (public, no API key) and writes a single-record FASTA that +``gi_predict.py --input`` then consumes. + +Modes (mutually exclusive): + --gene SYMBOL full gene-body sequence (e.g. TP53) + --region chr17:7.6M-7.7M sequence for a coordinate range + --gene SYMBOL --for-expression + EXACTLY 9,198 bp centred on the canonical TSS — + the only window the expression model accepts + +Examples: + python scripts/gi_fetch.py --gene TP53 --out tp53.fa + python scripts/gi_fetch.py --region chr17:7,661,779-7,687,546 --out region.fa + python scripts/gi_fetch.py --gene HBB --for-expression --out hbb_tss.fa + +On success the FASTA path is printed to stdout (so the agent can pipe it into +gi_predict.py); a one-line provenance summary goes to stderr. +""" + +from __future__ import annotations + +import argparse +import sys +from pathlib import Path + +SCRIPT_DIR = Path(__file__).resolve().parent +sys.path.insert(0, str(SCRIPT_DIR)) +from gi_ensembl import ( # noqa: E402 + EnsemblError, + fetch_by_symbol, + fetch_gene_window_for_expression, + fetch_region_by_coords, +) + + +def _parse_args() -> argparse.Namespace: + p = argparse.ArgumentParser( + description="Fetch reference sequence from Ensembl and write a FASTA." + ) + src = p.add_mutually_exclusive_group(required=True) + src.add_argument("--gene", type=str, help="Gene symbol, e.g. TP53.") + src.add_argument("--region", type=str, help="Coordinate range, e.g. chr17:7,661,779-7,687,546.") + p.add_argument( + "--for-expression", + action="store_true", + help="With --gene: return exactly 9,198 bp centred on the canonical TSS " + "(required by the expression task). Ignored with --region.", + ) + p.add_argument( + "--species", + type=str, + default="human", + help="Ensembl production name (default: human; e.g. mus_musculus, drosophila_melanogaster).", + ) + p.add_argument("--flank-bp", type=int, default=0, help="Extra bp on each side (gene/region only).") + p.add_argument("--strand", type=int, default=1, choices=(1, -1), help="Strand for --region (default 1).") + p.add_argument("--out", type=Path, required=True, help="Output FASTA path to write.") + return p.parse_args() + + +def main() -> int: + args = _parse_args() + try: + if args.region: + if args.for_expression: + print( + "[gi-fetch] --for-expression needs --gene (TSS centring requires a " + "gene's canonical transcript, not a raw region).", + file=sys.stderr, + ) + return 1 + seq, meta = fetch_region_by_coords( + args.region, species=args.species, strand=args.strand, flank_bp=args.flank_bp + ) + header = f"{meta['region']}|{args.species}|strand:{meta['strand']}" + elif args.for_expression: + seq, meta = fetch_gene_window_for_expression(args.gene, species=args.species) + header = ( + f"{meta['gene']}|{meta['ensembl_id']}|{meta['region']}|{args.species}|" + f"strand:{meta['strand']}|TSS:{meta['tss']}|{meta['tss_source']}" + ) + else: + seq, meta = fetch_by_symbol(args.gene, species=args.species, flank_bp=args.flank_bp) + header = ( + f"{meta['gene']}|{meta['ensembl_id']}|{meta['region']}|{args.species}|" + f"strand:{meta['strand']}" + ) + except EnsemblError as e: + print(f"[gi-fetch] {e}", file=sys.stderr) + return 2 + + args.out.parent.mkdir(parents=True, exist_ok=True) + wrapped = "\n".join(seq[i : i + 70] for i in range(0, len(seq), 70)) + args.out.write_text(f">{header}\n{wrapped}\n") + + print( + f"[gi-fetch] wrote {len(seq):,} bp → {args.out} ({header})", + file=sys.stderr, + ) + # stdout = just the path, so the agent can chain: FASTA=$(gi_fetch ...) + print(str(args.out)) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/nim-skills/genomic-intelligence-nim/scripts/gi_predict.py b/nim-skills/genomic-intelligence-nim/scripts/gi_predict.py new file mode 100644 index 0000000..edc6140 --- /dev/null +++ b/nim-skills/genomic-intelligence-nim/scripts/gi_predict.py @@ -0,0 +1,651 @@ +#!/usr/bin/env python3 +"""Unified CLI for the Genomic Intelligence DNA-sequence tasks. + +One entry point covers all six tasks exposed by the hosted API. Each is its own +published operation at ``/v1/tasks//predict``, with a separate request +schema per task: + + promoter · splice · enhancer · chromatin · expression · annotation + +It parses a single-record FASTA, calls the API, and writes ``report.md`` + +``result.json`` + ``reproducibility/`` to the output directory. Delivery is +synchronous except for ``annotation``, which defaults to async because it is +slow — the API accepts either mode on every task. + +Usage: + python scripts/gi_predict.py --task promoter --demo + python scripts/gi_predict.py --task splice --input my.fa --output out/ + python scripts/gi_predict.py --task expression --demo --description "K562 cells" + +Auth: set GI_API_KEY in the environment (see references/authentication.md). +""" + +from __future__ import annotations + +import argparse +import json +import os +import shlex +import sys +import time +from datetime import datetime, timezone +from pathlib import Path +from typing import Any, Dict, Optional + +# Self-contained: import the sibling client module regardless of CWD. +SCRIPT_DIR = Path(__file__).resolve().parent +sys.path.insert(0, str(SCRIPT_DIR)) +import requests # noqa: E402 (transport errors surface at the call boundary) + +from gi_client import Client, FastaError, GIError, read_fasta # noqa: E402 + +SKILL_DIR = SCRIPT_DIR.parent +DEMO_DIR = SKILL_DIR / "assets" / "demo" + +DISCLAIMER = ( + "Genomic Intelligence is a research tool. It is not a medical device and " + "does not provide clinical diagnoses. Consult a qualified professional " + "before making any medical decisions." +) + + +class TaskSpec: + """Per-task metadata: input bounds, default delivery mode, demo fixture.""" + + def __init__( + self, + min_bp: int, + max_bp: int, + async_default: bool, + demo: str, + window_bp: Optional[int] = None, + ) -> None: + self.min_bp = min_bp + self.max_bp = max_bp + # Delivery mode this runner picks by default. The API accepts BOTH + # modes on every task (Prefer: respond-async is a per-request header), + # so this is a latency choice, not a constraint. + self.async_default = async_default + self.demo = demo + # Fixed scoring-window width, if the task has one (expression: 9,198 bp). + # Anything longer than the window needs an explicit --tss-index. + self.window_bp = window_bp + + def validate(self, length: int) -> Optional[str]: + if length < self.min_bp: + return f"sequence too short: {length:,} bp < {self.min_bp:,} bp minimum" + if length > self.max_bp: + return f"sequence too long: {length:,} bp > {self.max_bp:,} bp maximum" + return None + + +# These bounds are a LOCAL MIRROR, not the authority. The authority is the +# `minLength`/`maxLength` published on each task's request schema in the live +# OpenAPI doc (https://api.genomicintelligence.ai/v1/openapi.json). Re-read it +# if a rejection here disagrees with the server. +# +# Each task has its own floor — the strictest its models need — enforced at +# request validation before any model loads. There are no per-model floors, so +# --model can never make a rejected length legal. The floor is admission control, NOT a statement about +# regime: a sequence above the floor but shorter than the selected model's +# `bio_spec.context_window_bp` is accepted and scored against a window padded out +# to the context window. Compare your length against `context_window_bp` (from +# GET /v1/tasks/{task}/models) to know whether the model saw real sequence or +# padding. Every task caps at 500,000 bp. Under-floor and over-max are both +# 422 validation_failed server-side — a 413 means the 16 MiB raw-body cap, never +# a long sequence. +PROMOTER_MIN_BP = 300 +SPLICE_MIN_BP = 100 +ENHANCER_MIN_BP = 50 +CHROMATIN_MIN_BP = 200 +ANNOTATION_MIN_BP = 1_000 +MAX_BP = 500_000 + +# expression's floor is also the width of the single window the model scores: +# sequence[tss_index-4599 : tss_index+4599]. Send a pre-cut 9,198 bp window, or +# send up to 500 kb plus --tss-index and let the server slice. +# +# The default path is deliberately stricter than the API: with no --tss-index, +# this client requires *exactly* 9,198 bp rather than merely at-or-above the +# floor. That is the tripwire — the server will happily score a 9,198 bp window +# cut from the wrong place and return a confident 200, and there is no +# client-side tell for a mis-centred window. Requiring the exact width keeps the +# caller visibly responsible for TSS-centring. --tss-index is the explicit +# opt-in that widens the accepted range to the full 9,198–500,000 bp the API +# allows and hands the cut to the server; it is range-checked below, and what +# was actually scored is echoed back from response meta rather than assumed. +EXPRESSION_WINDOW_BP = 9_198 +EXPRESSION_TSS_RADIUS = EXPRESSION_WINDOW_BP // 2 # 4,599 + +TASKS: Dict[str, TaskSpec] = { + "promoter": TaskSpec(PROMOTER_MIN_BP, MAX_BP, False, "promoter_tp53.fa"), + "splice": TaskSpec(SPLICE_MIN_BP, MAX_BP, False, "splice_hbb.fa"), + "enhancer": TaskSpec(ENHANCER_MIN_BP, MAX_BP, False, "enhancer_eve.fa"), + "chromatin": TaskSpec(CHROMATIN_MIN_BP, MAX_BP, False, "chromatin_active_promoter_chr19.fa"), + "expression": TaskSpec( + EXPRESSION_WINDOW_BP, MAX_BP, False, "expression_hbb_k562.fa", + window_bp=EXPRESSION_WINDOW_BP, + ), + "annotation": TaskSpec(ANNOTATION_MIN_BP, MAX_BP, True, "annotation_tp53.fa"), +} + + +def _parse_args() -> argparse.Namespace: + p = argparse.ArgumentParser( + description="Genomic Intelligence DNA-sequence prediction (one CLI, six tasks)." + ) + p.add_argument( + "--task", + required=True, + choices=sorted(TASKS), + help="Which prediction task to run.", + ) + p.add_argument("--input", type=Path, dest="input_file", help="Input FASTA (single record).") + p.add_argument("--output", type=Path, default=None, help="Output directory (default: /tmp/gi-).") + p.add_argument("--demo", action="store_true", help="Run with the bundled example FASTA for the task.") + p.add_argument("--model", type=str, default=None, help="Override the default model for the task.") + p.add_argument( + "--description", + type=str, + default=None, + help=( + "Cell type / assay context. REQUIRED by expression; not accepted by " + "any other task (their options objects are closed), so it is dropped " + "with a warning if passed elsewhere." + ), + ) + p.add_argument( + "--tss-index", + type=int, + default=None, + dest="tss_index", + help=( + "expression only: 0-based TSS offset into the sequence (whitespace " + "stripped). REQUIRED unless the sequence is exactly 9,198 bp. The " + "server scores sequence[tss_index-4599 : tss_index+4599]." + ), + ) + p.add_argument("--api-key", type=str, default=None, help="Override GI_API_KEY env.") + p.add_argument("--base-url", type=str, default=None, help="Override GI_BASE_URL (default: https://api.genomicintelligence.ai).") + return p.parse_args() + + +def _resolve_input(args: argparse.Namespace, spec: TaskSpec) -> Path: + # Running the demo has to be asked for. Falling back to it when --input is + # simply absent produces a full report, with a real request id and real + # scores, for a sequence the caller never supplied — and nothing in the + # output says so. + if not args.demo and args.input_file is None: + print( + "Error: no input. Pass --input , or --demo to run the " + f"bundled {spec.demo} fixture.", + file=sys.stderr, + ) + sys.exit(2) + if args.demo: + demo_path = DEMO_DIR / spec.demo + if not demo_path.exists(): + print(f"Error: bundled demo fixture missing at {demo_path}", file=sys.stderr) + sys.exit(1) + return demo_path + if not args.input_file.exists(): + print(f"Error: --input file not found: {args.input_file}", file=sys.stderr) + sys.exit(1) + return args.input_file + + +# Per-item arrays that belong in result.json, not in the compact stdout payload. +_BULKY_SUMMARY_KEYS = {"regions", "sites", "transcripts", "raw_summary"} + + +class ResponseShapeError(RuntimeError): + """A 2xx body whose nested fields contradict their documented types. + + Distinct from `GIError`, which covers what the API itself reported. This is + a well-formed envelope carrying a field the contract says is an object or an + array and that arrived as something else. + """ + + +def _as_obj(v: Any, field: str) -> Dict[str, Any]: + """Read a response field documented as an object. + + `_require_envelope` guarantees `data` is a non-empty object; it deliberately + does not police per-task fields nested inside it, because it is shared by + six tasks and must not encode any one task's schema. So the checking happens + here. + + Absent or null is legitimate — a task that has no `prediction` omits it — and + becomes `{}`. A field that is *present with the wrong type* is a malformed + response and is reported as one. + + Two failure modes have to be avoided here, and they pull in opposite + directions. The `x or {}` idiom this replaces handled null and absent but not + a truthy wrong type: a `"summary"` arriving as a string passed `or {}` + untouched and then raised AttributeError on `.get`, in the report writer, + which runs after main()'s try/except has closed — a traceback that reads as a + client bug. Substituting `{}` for it instead fixes the traceback and creates + something worse: a zero-valued report printed with `"ok": true`, so a bad + response is indistinguishable from a real prediction of nothing. Raise a + typed error that main() turns into the same diagnostic it gives any other + malformed response, and it is neither. + """ + if v is None: + return {} + if not isinstance(v, dict): + raise ResponseShapeError( + f"{field} should be an object, got {type(v).__name__}" + ) + return v + + +def _as_objs(v: Any, field: str) -> list: + """Same, for a field documented as an array of objects. + + A truthy non-list (a bare string) is iterable, so `or []` let it through and + the row loop iterated its characters; non-object elements fail the same way. + Neither is silently dropped — an array whose elements are the wrong type is a + malformed response, and a report missing rows it should have had is exactly + the silent wrong answer this is here to prevent. + """ + if v is None: + return [] + if not isinstance(v, list): + raise ResponseShapeError( + f"{field} should be an array, got {type(v).__name__}" + ) + for i, x in enumerate(v): + if not isinstance(x, dict): + raise ResponseShapeError( + f"{field}[{i}] should be an object, got {type(x).__name__}" + ) + return v + + +def _summarize(task: str, body: Dict[str, Any]) -> Dict[str, Any]: + """Pick the most useful headline numbers per task from `data`.""" + data = _as_obj(body.get("data"), "data") + summary = _as_obj(data.get("summary"), "data.summary") + out: Dict[str, Any] = {"task": task, "model": data.get("model")} + if task == "promoter": + out["promoter_windows"] = summary.get("promoter_windows") + out["total_windows"] = summary.get("total_windows") + out["regions"] = _as_objs(data.get("regions"), "data.regions") + elif task == "splice": + out["sites_found"] = summary.get("total_sites", summary.get("sites_found")) + out["donor_sites"] = summary.get("donor_sites") + out["acceptor_sites"] = summary.get("acceptor_sites") + out["sites"] = _as_objs(data.get("sites"), "data.sites") + elif task == "enhancer": + out["windows_processed"] = summary.get("total_windows", summary.get("windows_processed")) + out["dev_score_max"] = summary.get("dev_score_max") + out["hk_score_max"] = summary.get("hk_score_max") + elif task == "chromatin": + out["windows_processed"] = summary.get("total_windows", summary.get("windows_processed")) + out["total_annotations"] = summary.get("total_annotations") + elif task == "expression": + pred = _as_obj(data.get("prediction"), "data.prediction") + out["log_tpm"] = pred.get("expression_log_tpm") + out["tpm"] = pred.get("expression_tpm") + # Windowing provenance: an in-range but *wrong* tss_index scores the + # wrong 9,198 bp window and still returns 200, so surface what was + # actually scored rather than trusting the request. + counts = _as_obj( + _as_obj(body.get("meta"), "meta").get("task_specific_counts"), + "meta.task_specific_counts", + ) + out["tss_index"] = counts.get("tss_index") + out["scored_window"] = counts.get("scored_window") + elif task == "annotation": + out["transcripts_found"] = summary.get("total_transcripts", summary.get("transcripts_found")) + out["transcripts"] = _as_objs(data.get("transcripts"), "data.transcripts") + out["raw_summary"] = summary + return out + + +def _fmt(v: Any, spec: str = ".3f") -> str: + return format(v, spec) if isinstance(v, (int, float)) else str(v) + + +def _headline_lines(task: str, summary: Dict[str, Any]) -> list[str]: + lines: list[str] = [] + if task == "promoter": + lines.append( + f"- Promoter windows: **{summary.get('promoter_windows', 0)}** / " + f"{summary.get('total_windows', 0)} total" + ) + regions = _as_objs(summary.get("regions"), "data.regions") + if regions: + lines += ["", "| Name | Start | End | Score |", "|---|---|---|---|"] + for r in regions[:20]: + lines.append( + f"| {r.get('name', '-')} | {r.get('start', '-')} | " + f"{r.get('end', '-')} | {_fmt(r.get('score', '-'))} |" + ) + elif task == "splice": + lines.append( + f"- Splice sites found: **{summary.get('sites_found') or 0}** " + f"({summary.get('donor_sites') or 0} donor + {summary.get('acceptor_sites') or 0} acceptor)" + ) + sites = _as_objs(summary.get("sites"), "data.sites")[:20] + if sites: + lines += ["", "| Name | Start | Type | Score |", "|---|---|---|---|"] + for s in sites: + lines.append( + f"| {s.get('name', '-')} | {s.get('start', '-')} | " + f"{s.get('site_type', '-')} | {_fmt(s.get('score', '-'))} |" + ) + elif task == "enhancer": + lines.append(f"- Windows processed: **{summary.get('windows_processed') or 0}**") + dev, hk = summary.get("dev_score_max"), summary.get("hk_score_max") + if dev is not None: + lines.append(f"- Max developmental-enhancer score: **{_fmt(dev)}**") + if hk is not None: + lines.append(f"- Max housekeeping-enhancer score: **{_fmt(hk)}**") + elif task == "chromatin": + lines.append(f"- Windows processed: **{summary.get('windows_processed') or 0}**") + lines.append(f"- Total annotations across all tracks: **{summary.get('total_annotations') or 0}**") + elif task == "expression": + log_tpm, tpm = summary.get("log_tpm"), summary.get("tpm") + if log_tpm is not None: + tail = f" ≈ {tpm:.2f} TPM" if isinstance(tpm, (int, float)) else "" + lines.append(f"- Predicted expression: **{_fmt(log_tpm, '.4f')} log(TPM+1)**{tail}") + else: + lines.append("- See `result.json` for the full prediction payload.") + elif task == "annotation": + lines.append(f"- Transcripts found: **{summary.get('transcripts_found') or 0}**") + tx = _as_objs(summary.get("transcripts"), "data.transcripts")[:20] + if tx: + lines += ["", "| Name | Start | End | Strand | Score |", "|---|---|---|---|---|"] + for t in tx: + lines.append( + f"| {t.get('name', '-')} | {t.get('start', '-')} | " + f"{t.get('end', '-')} | {t.get('strand', '-')} | {_fmt(t.get('score', '-'))} |" + ) + return lines + + +def _repro_command( + task: str, + input_path: Path, + output_dir: Path, + model: Optional[str] = None, + description: Optional[str] = None, + tss_index: Optional[int] = None, +) -> str: + """Build the exact re-runnable invocation for reproducibility/command.sh. + + Emits --model, --description and --tss-index only when they were supplied, + so a replay reproduces the original call: expression requires --description + (no default) and --tss-index whenever the sequence is not exactly 9,198 bp, + and a non-default --model must survive. Uses python3 and shell-quotes every + value so paths/descriptions with spaces round-trip. + """ + parts = [ + "python3 scripts/gi_predict.py", + f"--task {task}", + f"--input {shlex.quote(str(input_path))}", + f"--output {shlex.quote(str(output_dir))}", + ] + if model: + parts.append(f"--model {shlex.quote(model)}") + if description is not None: + parts.append(f"--description {shlex.quote(description)}") + if tss_index is not None: + parts.append(f"--tss-index {tss_index}") + return " ".join(parts) + + +def _write_report( + task: str, + summary: Dict[str, Any], + body: Dict[str, Any], + output_dir: Path, + input_path: Path, + sequence_name: str, + sequence_length: int, + elapsed_ms: float, + model: Optional[str] = None, + description: Optional[str] = None, + tss_index: Optional[int] = None, +) -> None: + output_dir.mkdir(parents=True, exist_ok=True) + (output_dir / "result.json").write_text( + json.dumps({"summary": summary, "full_response": body}, indent=2) + ) + + meta = _as_obj(body.get("meta"), "meta") + report_model = summary.get("model") or "—" # effective model for the report + lines = [ + f"# Genomic Intelligence — {task} report", + "", + f"- **Sequence**: `{sequence_name}` ({sequence_length:,} bp)", + f"- **Input file**: `{input_path}`", + f"- **Model**: `{report_model}`", + f"- **Inference time**: {_fmt(meta.get('inference_time_ms', elapsed_ms), '.0f')} ms", + f"- **Request ID**: `{meta.get('request_id', '—')}`", + f"- **Generated**: {datetime.now(timezone.utc).isoformat(timespec='seconds')}", + "", + "## Headline result", + "", + *_headline_lines(task, summary), + "", + "## Reproducibility", + "", + "- `reproducibility/command.sh` — exact invocation", + "- `result.json` — full `{data, meta}` response from the API", + "", + "## API", + "", + f"`POST /v1/tasks/{task}/predict` on `https://api.genomicintelligence.ai` " + "— see .", + "", + "---", + "", + f"_{DISCLAIMER}_", + "", + ] + (output_dir / "report.md").write_text("\n".join(lines)) + + repro = output_dir / "reproducibility" + repro.mkdir(exist_ok=True) + cmd = _repro_command(task, input_path, output_dir, model, description, tss_index) + "\n" + (repro / "command.sh").write_text("#!/usr/bin/env bash\nset -euo pipefail\n" + cmd) + (repro / "command.sh").chmod(0o755) + (repro / "environment.json").write_text( + json.dumps( + { + "skill": "genomic-intelligence-nim", + "skill_version": "0.1.0", + "task": task, + "api_base_url": os.environ.get("GI_BASE_URL", "https://api.genomicintelligence.ai"), + "model": summary.get("model"), + "request_id": meta.get("request_id"), + "timestamp": datetime.now(timezone.utc).isoformat(timespec="seconds"), + }, + indent=2, + ) + ) + + +def main() -> int: + args = _parse_args() + task = args.task + spec = TASKS[task] + output_dir = args.output or Path(f"/tmp/gi-{task}") + + input_path = _resolve_input(args, spec) + try: + sequence_name, sequence = read_fasta(input_path) + except FastaError as e: + print(f"[gi-{task}] invalid input — {e}", file=sys.stderr) + return 1 + if not sequence: + print(f"Error: parsed an empty sequence from {input_path}", file=sys.stderr) + return 1 + + length_err = spec.validate(len(sequence)) + if length_err: + print(f"[gi-{task}] invalid input — {length_err}", file=sys.stderr) + if task == "expression": + print( + " The expression model scores exactly one 9,198 bp TSS-centred window " + "(TSS ± 4,599), so 9,198 bp is a hard floor. Send a pre-cut window, or a " + "longer locus plus --tss-index. See references/tasks.md#expression.", + file=sys.stderr, + ) + elif len(sequence) < spec.min_bp: + print( + f" {task} needs at least {spec.min_bp:,} bp. This floor is published as " + f"minLength on the endpoint's request schema; the server rejects a shorter " + f"sequence with 422 validation_failed. See references/tasks.md.", + file=sys.stderr, + ) + return 1 + + tss_index = args.tss_index + if task == "expression": + if tss_index is None: + # Default path: exact window only. See the EXPRESSION_WINDOW_BP note + # above for why this is stricter than the API's own floor. + if len(sequence) != EXPRESSION_WINDOW_BP: + print( + f"[gi-expression] --tss-index is required unless the sequence is " + f"exactly {EXPRESSION_WINDOW_BP:,} bp (got {len(sequence):,} bp). " + "It is the 0-based TSS offset into the sequence. " + "See references/tasks.md#expression.", + file=sys.stderr, + ) + return 1 + else: + lo, hi = EXPRESSION_TSS_RADIUS, len(sequence) - EXPRESSION_TSS_RADIUS + if not (lo <= tss_index <= hi): + print( + f"[gi-expression] --tss-index {tss_index:,} outside the allowed range " + f"[{lo:,}, {hi:,}] for a {len(sequence):,} bp sequence — the model needs " + f"a full ±{EXPRESSION_TSS_RADIUS:,} bp window around the TSS; submit more " + "flanking sequence.", + file=sys.stderr, + ) + return 1 + elif tss_index is not None: + print(f"[gi-{task}] --tss-index applies to expression only; ignoring it.", file=sys.stderr) + tss_index = None + + if task == "expression" and not args.description: + print( + "[gi-expression] --description is required (e.g. \"K562 cells\"). " + "It selects the expression context. See references/tasks.md#expression.", + file=sys.stderr, + ) + return 1 + + try: + client = Client(api_key=args.api_key, base_url=args.base_url) + except RuntimeError as e: + print(str(e), file=sys.stderr) + return 2 + # `options` is a closed (additionalProperties: false) object per task, and + # only ExpressionOptions declares `description`. Forwarding it on any other + # task is a hard 422 validation_failed (extra_forbidden), not a no-op — so + # drop it locally rather than letting the server reject the call. + options: Dict[str, Any] = {} + if args.description is not None: + if task == "expression": + options["description"] = args.description + else: + print( + f"[gi-{task}] --description applies to expression only; ignoring it " + f"({task} rejects unknown options keys with 422).", + file=sys.stderr, + ) + + print( + f"[gi-{task}] sequence_name={sequence_name} length={len(sequence):,} bp " + f"model={args.model or 'default'} mode={'async' if spec.async_default else 'sync'}", + file=sys.stderr, + ) + started = time.monotonic() + try: + if spec.async_default: + job_id = client.submit_async( + task, sequence=sequence, sequence_name=sequence_name, + model=args.model, options=options or None, + ) + print(f"[gi-{task}] submitted job_id={job_id}", file=sys.stderr) + + def _progress(p: Dict[str, Any]) -> None: + pct, msg = p.get("percent"), p.get("message", "") + if pct is not None: + print(f" {pct:>3}% {msg}", file=sys.stderr) + + body = client.wait_for_job(job_id, on_progress=_progress) + else: + body = client.predict( + task, sequence=sequence, sequence_name=sequence_name, + model=args.model, options=options or None, tss_index=tss_index, + ) + except GIError as e: + print(f"[gi-{task}] API error: {e}", file=sys.stderr) + return 2 + except requests.RequestException as e: + # Connection refused, DNS failure, TLS error, read timeout — routine + # for a hosted service and not the caller's bug. gi_ensembl already + # maps these to a diagnostic; do the same here rather than exiting + # with a traceback that reads like a client defect. + print(f"[gi-{task}] network error reaching the API: {type(e).__name__}: {e}", + file=sys.stderr) + return 2 + except TimeoutError as e: + # Raised by wait_for_job when a job outlives its poll deadline. + print(f"[gi-{task}] timed out waiting for the job: {e}", file=sys.stderr) + return 2 + except KeyError as e: + # A 2xx whose body is missing a field we index (e.g. data.job_id on an + # async submit). Malformed upstream response, not a usage error. + print(f"[gi-{task}] unexpected API response shape: missing {e}", file=sys.stderr) + return 2 + + elapsed_ms = (time.monotonic() - started) * 1000.0 + # The report writer runs outside the block above, so its own view of a + # malformed response needs its own handler. Without one a wrong-typed nested + # field either raised a traceback or — once the helpers coerced it — printed + # a zero-valued report with ok=true. Both are worse than exiting 2 with the + # field named. + try: + summary = _summarize(task, body) + meta = _as_obj(body.get("meta"), "meta") + _write_report( + task, summary, body, output_dir, input_path, sequence_name, len(sequence), + elapsed_ms, model=args.model, description=args.description, tss_index=tss_index, + ) + except ResponseShapeError as e: + print(f"[gi-{task}] unexpected API response shape: {e}", file=sys.stderr) + return 2 + print(f"[gi-{task}] OK — wrote {output_dir}/report.md ({elapsed_ms:.0f} ms wall)", file=sys.stderr) + + # stdout = a compact machine-readable summary so the agent gets the answer + # inline without reading a file. The bulky per-item arrays (regions / sites / + # transcripts) stay in result.json — only headline scalars go here. + headline = {k: v for k, v in summary.items() if k not in _BULKY_SUMMARY_KEYS} + stdout_payload = { + "ok": True, + "task": task, + "sequence_name": sequence_name, + "sequence_length_bp": len(sequence), + "model": summary.get("model"), + "request_id": meta.get("request_id"), + "inference_time_ms": meta.get("inference_time_ms"), + "result": headline, + "artifacts": { + "output_dir": str(output_dir), + "report": str(output_dir / "report.md"), + "result_json": str(output_dir / "result.json"), + }, + } + print(json.dumps(stdout_payload, indent=2)) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/nim-skills/genomic-intelligence-nim/tests/__init__.py b/nim-skills/genomic-intelligence-nim/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/nim-skills/genomic-intelligence-nim/tests/conftest.py b/nim-skills/genomic-intelligence-nim/tests/conftest.py new file mode 100644 index 0000000..a122f5a --- /dev/null +++ b/nim-skills/genomic-intelligence-nim/tests/conftest.py @@ -0,0 +1,2 @@ +def pytest_configure(config): + config.addinivalue_line("markers", "integration: live-API test; requires GI_API_KEY") diff --git a/nim-skills/genomic-intelligence-nim/tests/test_input_validation.py b/nim-skills/genomic-intelligence-nim/tests/test_input_validation.py new file mode 100644 index 0000000..a5041e6 --- /dev/null +++ b/nim-skills/genomic-intelligence-nim/tests/test_input_validation.py @@ -0,0 +1,478 @@ +"""The client refuses malformed input instead of silently repairing it. + +Each case here is a real defect that shipped: the parser used to delete every +character outside ACGTN and to concatenate multi-record files into one chimeric +sequence, and the Ensembl helper used to fall back to gene-body coordinates +when no canonical transcript was found. All three produced a confident, +wrong-but-well-formed result with nothing for the caller to notice. +""" + +from __future__ import annotations + +import argparse +import sys +from pathlib import Path + +import pytest + +SCRIPTS = Path(__file__).resolve().parent.parent / "scripts" +sys.path.insert(0, str(SCRIPTS)) + +import gi_client # noqa: E402 +import gi_predict # noqa: E402 +from gi_client import FastaError, read_fasta # noqa: E402 +from gi_ensembl import ( # noqa: E402 + EXPRESSION_SEQUENCE_LENGTH, + EnsemblError, + GeneLocus, + expression_window_bounds, +) + + +def _write(tmp_path: Path, content: str) -> Path: + path = tmp_path / "input.fa" + path.write_text(content) + return path + + +class TestReadFasta: + def test_accepts_a_clean_single_record(self, tmp_path): + path = _write(tmp_path, ">chr1 some description\nACGT\nacgt\n") + name, seq = read_fasta(path) + assert name == "chr1" + assert seq == "ACGTACGT", "lowercase and line breaks are formatting, not content" + + def test_rejects_iupac_ambiguity_codes(self, tmp_path): + # Deleting these shifts every downstream coordinate, so the model would + # score a sequence the caller never supplied. + path = _write(tmp_path, ">x\nACGTRYKM\n") + with pytest.raises(FastaError) as exc: + read_fasta(path) + assert "outside ACGTN" in str(exc.value) + assert "IUPAC" in str(exc.value), "the hint should name why these specifically cannot be scored" + + def test_rejects_non_iupac_junk_without_the_iupac_hint(self, tmp_path): + path = _write(tmp_path, ">x\nACGT--NN\n") + with pytest.raises(FastaError) as exc: + read_fasta(path) + assert "IUPAC" not in str(exc.value) + + def test_rejects_multi_record_input(self, tmp_path): + path = _write(tmp_path, ">a\nACGT\n>b\nTTTT\n") + with pytest.raises(FastaError) as exc: + read_fasta(path) + assert "single FASTA record" in str(exc.value) + assert "found 2" in str(exc.value) + + def test_error_names_the_offending_line(self, tmp_path): + path = _write(tmp_path, ">x\nACGT\nACGTR\n") + with pytest.raises(FastaError) as exc: + read_fasta(path) + assert "line 3" in str(exc.value) + + def test_fasta_error_is_a_value_error(self): + # Callers doing broad input validation still catch it. + assert issubclass(FastaError, ValueError) + + @pytest.mark.parametrize( + "fixture", sorted((SCRIPTS.parent / "assets" / "demo").glob("*.fa")), ids=lambda p: p.name + ) + def test_bundled_demo_fixtures_still_parse(self, fixture): + # A stricter parser that rejects our own demos would be useless. + name, seq = read_fasta(fixture) + assert name and seq + + +class TestCanonicalTss: + def _locus(self, **kw): + base = dict( + ensembl_id="ENSG0", seq_region="11", start=1000, end=2000, + strand=1, species="human", display_name="TEST", + ) + base.update(kw) + return GeneLocus(**base) + + def test_uses_canonical_start_on_plus_strand(self): + locus = self._locus(canonical_start=1500, canonical_end=1900) + assert locus.tss == 1500 + + def test_uses_canonical_end_on_minus_strand(self): + locus = self._locus(strand=-1, canonical_start=1500, canonical_end=1900) + assert locus.tss == 1900 + + def test_refuses_when_no_canonical_transcript(self): + # The old fallback returned the gene body here. That window is still + # exactly 9,198 bp, so the client-side size gate passes and the API + # returns a confident score for the wrong locus — ACTB's gene body sits + # 33,301 bp from its TSS. There is no client-side tell, so refusing is + # the only honest option. + locus = self._locus() + with pytest.raises(EnsemblError) as exc: + _ = locus.tss + assert "no canonical transcript" in str(exc.value) + + def test_refuses_when_only_one_boundary_is_known(self): + locus = self._locus(canonical_start=1500) + with pytest.raises(EnsemblError): + _ = locus.tss +class TestExpressionWindowCentring: + """The TSS must land at offset 4,599 on both strands. + + The API scores ``sequence[tss_index-4599 : tss_index+4599]`` and defaults + ``tss_index`` to 4,599 for a submission of exactly 9,198 bp. Ensembl + reverse-complements the region for ``strand=-1``, so a window built as if + the sequence always read low-to-high puts a minus-strand TSS at 4,598 — + still exactly 9,198 bp, so the size gate passes and the API scores a + window shifted by one with nothing for the caller to notice. + """ + + @pytest.mark.parametrize("strand", [1, -1]) + def test_window_is_exactly_the_expression_length(self, strand): + start, end = expression_window_bounds(1_000_000, strand) + assert end - start + 1 == EXPRESSION_SEQUENCE_LENGTH + + @pytest.mark.parametrize( + "strand,tss", + [(1, 1_000_000), (-1, 5_227_071)], # HBB's canonical TSS is on the minus strand + ) + def test_tss_lands_where_the_api_expects_it(self, strand, tss): + start, end = expression_window_bounds(tss, strand) + # Offset of the TSS in the sequence as Ensembl returns it: low-to-high + # on the plus strand, reverse-complemented on the minus strand. + offset = tss - start if strand == 1 else end - tss + assert offset == EXPRESSION_SEQUENCE_LENGTH // 2 +class TestDemoMustBeAskedFor: + """Omitting --input must not fall back to the bundled fixture. + + The fallback produced a complete run — real request id, real scores, a + written report — for a sequence the caller never supplied, and nothing in + the output distinguished it from a real one. + """ + + def _args(self, **kw): + defaults = {"demo": False, "input_file": None} + defaults.update(kw) + return argparse.Namespace(**defaults) + + def test_no_input_and_no_demo_exits(self): + spec = gi_predict.TASKS["promoter"] + with pytest.raises(SystemExit) as exc: + gi_predict._resolve_input(self._args(), spec) + assert exc.value.code == 2 + + def test_demo_flag_still_resolves_the_fixture(self): + spec = gi_predict.TASKS["promoter"] + path = gi_predict._resolve_input(self._args(demo=True), spec) + assert path.name == spec.demo +class TestSequenceBeforeFirstHeader: + """Bases before the first '>' must be refused, not absorbed. + + They were appended to the first record, so the API scored a sequence the + caller never named and returned coordinates for it. The multi-record check + does not see this: such a file has exactly one header. + """ + + def test_pre_header_sequence_is_rejected(self, tmp_path): + path = _write(tmp_path, "ACGTACGT\n>real_record\nGGGGCCCC\n") + with pytest.raises(FastaError) as exc: + read_fasta(path) + assert "before any" in str(exc.value) + + def test_a_normal_single_record_still_parses(self, tmp_path): + path = _write(tmp_path, ">real_record\nACGT\nGGGG\n") + name, seq = read_fasta(path) + assert (name, seq) == ("real_record", "ACGTGGGG") + + +class TestSuccessfulResponsesAreValidated: + """A 2xx is not automatically a result. + + A non-JSON 200 used to be turned into an error-shaped dict and returned as + a success, and an empty or non-object 200 reached the report writer and + failed there as an AttributeError. + """ + + class _Resp: + status_code = 200 + headers: dict = {} + ok = True + + def __init__(self, payload=None, text=""): + self._payload, self.text = payload, text + + def json(self): + if self._payload is None: + raise ValueError("not json") + return self._payload + + def test_non_json_200_raises_instead_of_returning_an_error_shape(self): + c = gi_client.Client.__new__(gi_client.Client) + with pytest.raises(gi_client.GIError): + c._check(self._Resp(text="gateway")) + + @pytest.mark.parametrize( + "payload", [{}, [], {"meta": {}}, "text", None, {"data": {}, "meta": {}}] + ) + def test_a_200_without_data_is_not_a_result(self, payload): + with pytest.raises(gi_client.GIError): + gi_client.Client._require_envelope(payload, self._Resp(payload)) + + def test_a_well_formed_envelope_passes_through(self): + body = {"data": {"summary": {}}, "meta": {}} + assert gi_client.Client._require_envelope(body, self._Resp(body)) is body +class TestWhitespaceIsFormattingNotContent: + """Whitespace is normalized; only content is refused. + + The API strips newlines, spaces and tabs before measuring length, so a + space-grouped body has to parse here too or the client is stricter than + the service it guards. + """ + + @pytest.mark.parametrize( + "body,expected", + [ + ("ACGT\nGGGG\n", "ACGTGGGG"), + ("ACGTACGTAC GTACGTACGT\n", "ACGTACGTACGTACGTACGT"), + ("ACGT\tACGT\n", "ACGTACGT"), + (" ACGT \n\n GGGG\n", "ACGTGGGG"), + ("acgtACGT\n", "ACGTACGT"), + ], + ) + def test_layout_is_normalized(self, tmp_path, body, expected): + assert read_fasta(_write(tmp_path, f">r\n{body}"))[1] == expected + + @pytest.mark.parametrize("body", ["ACGTRACGT\n", "ACGT R ACGT\n"]) + def test_ambiguity_codes_are_still_refused(self, tmp_path, body): + with pytest.raises(FastaError): + read_fasta(_write(tmp_path, f">r\n{body}")) +class TestSyncPredictIsValidatedToo: + """The sync path needs the same envelope check as the async one. + + An earlier patch wrapped only wait_for_job, so predict() still returned a + 200 with no data key straight to the report writer, which wrote an empty + report and reported ok=true. + """ + + class _Resp: + status_code, headers, ok = 200, {}, True + + def __init__(self, payload): + self._payload, self.text = payload, "" + + def json(self): + return self._payload + + @pytest.mark.parametrize( + "payload", + [ + {"meta": {}}, # no data key at all + {"data": None, "meta": {}}, # null data + {"data": "summary", "meta": {}}, # non-object data + {"data": [1, 2], "meta": {}}, # array data + {"data": {}, "meta": {"request_id": "req-1"}}, # object, but no result in it + ], + ) + def test_a_200_without_an_object_data_is_refused(self, payload): + with pytest.raises(gi_client.GIError): + gi_client.Client._require_envelope(payload, self._Resp(payload)) + + def test_predict_wraps_the_sync_path(self): + import inspect + + src = inspect.getsource(gi_client.Client.predict) + assert "_require_envelope" in src, ( + "predict() must validate the envelope; a patch that misses this " + "line leaves the sync path unguarded while the async one is fine" + ) +class TestAsyncSubmitIsValidatedToo: + """submit_async reads data.job_id, so it needs the envelope check as well. + + A 200 with no data key raised KeyError, which the CLI catches; a non-object + data raised TypeError, which it does not. Both are malformed responses, not + client bugs, and both should surface as GIError. + """ + + def test_submit_async_wraps_the_envelope(self): + import inspect + + src = inspect.getsource(gi_client.Client.submit_async) + assert "_require_envelope" in src + + def test_an_empty_data_never_reaches_the_job_id_read(self): + """`{"data": {}}` is refused by the envelope check itself. + + It used to pass, leaving `data.job_id` to catch it on this path only — + and nothing at all to catch it on the sync and job-result paths, which + wrote a zero-valued report and printed ok=true. + """ + + class _Resp: + status_code, headers, ok = 200, {}, True + text = "" + + def json(self): + return {"data": {}, "meta": {}} + + with pytest.raises(gi_client.GIError): + gi_client.Client._require_envelope(_Resp().json(), _Resp()) + + def test_a_data_without_job_id_is_still_a_gi_error(self): + """The job_id check stays: a non-empty `data` can still lack it.""" + body = {"data": {"status": "queued"}, "meta": {}} + + class _Resp: + status_code, headers, ok = 200, {}, True + text = "" + + def json(self): + return body + + checked = gi_client.Client._require_envelope(body, _Resp()) + assert checked["data"].get("job_id") is None + + +class TestNestedFieldsOfTheWrongType: + """`_require_envelope` passing is not a promise about what is inside `data`. + + The envelope check guarantees `data` is a non-empty object and stops there, + which is the right scope for it — it is shared by six tasks and must not + encode any one task's schema. So the report writer still meets whatever + `data.summary` or `meta` actually contains, and it runs *after* main()'s + first try/except has closed. + + Two wrong answers were available here and both were taken in turn. The + `x or {}` guards only covered null and absent, so a truthy wrong type + reached `.get` and raised AttributeError — a traceback that reads as a + client bug. Coercing it to `{}` instead produced a zero-valued report + printed with `"ok": true`, which is worse: a malformed response became + indistinguishable from a real prediction of nothing. The third option is a + typed refusal, which is what these pin. + + These call `_summarize` and `_write_report` rather than asserting on their + source, because the defect this pins is a missing call site and a source + grep is exactly what failed to catch the last one. + """ + + _MALFORMED = [ + {"data": {"summary": "all good"}}, # summary as a string + {"data": {"summary": ["a", "b"]}}, # summary as an array + {"data": {"summary": 0.94}}, # summary as a float + {"data": {"prediction": "high"}}, # prediction as a string + {"data": {"summary": {}}, "meta": "req-1"}, # meta as a string + {"data": {"summary": {}}, "meta": {"task_specific_counts": "n/a"}}, + {"data": {"summary": {}, "regions": "chr1"}}, # array field as a string + {"data": {"summary": {}, "sites": [1, 2, 3]}}, # non-object elements + {"data": {"summary": {}, "transcripts": "ENST1"}}, + ] + + @pytest.mark.parametrize("body", _MALFORMED) + @pytest.mark.parametrize( + "task", ["promoter", "splice", "enhancer", "chromatin", "expression", "annotation"] + ) + def test_a_wrong_typed_field_is_refused_not_coerced(self, task, body): + """Whichever task reads the offending field must refuse the body. + + A task that never reads it is entitled to succeed — `enhancer` does not + touch `data.transcripts` — so the assertion is on the failure mode, not + on every combination failing: either a typed refusal naming the field, + or a clean summary. Never an AttributeError, and never a summary built + out of a substituted empty value. + """ + try: + out = gi_predict._summarize(task, body) + except gi_predict.ResponseShapeError as e: + assert "should be an" in str(e) + return + assert isinstance(out, dict) + assert isinstance(out["raw_summary"], dict) + + @pytest.mark.parametrize( + "body,field", + [ + ({"data": {"summary": "all good"}}, "data.summary"), + ({"data": {"summary": ["a", "b"]}}, "data.summary"), + ({"data": {"summary": 0.94}}, "data.summary"), + ({"data": {"summary": {}, "regions": "chr1"}}, "data.regions"), + ({"data": {"summary": {}, "sites": [1, 2, 3]}}, "data.sites"), + ], + ) + def test_the_offending_field_is_named(self, body, field): + task = {"data.summary": "promoter", "data.regions": "promoter", + "data.sites": "splice"}[field] + with pytest.raises(gi_predict.ResponseShapeError) as exc: + gi_predict._summarize(task, body) + assert field in str(exc.value) + + @pytest.mark.parametrize("body", _MALFORMED) + @pytest.mark.parametrize("task", ["promoter", "splice", "expression", "annotation"]) + def test_the_report_never_half_writes(self, tmp_path, task, body): + """A refusal may happen; a traceback or a silent zero report may not.""" + try: + summary = gi_predict._summarize(task, body) + gi_predict._write_report( + task, summary, body, tmp_path, tmp_path / "in.fa", "seq", 9198, 12.0, + ) + except gi_predict.ResponseShapeError: + return + assert (tmp_path / "report.md").exists() + assert (tmp_path / "result.json").exists() + + def test_main_reports_it_instead_of_exiting_zero(self, tmp_path, monkeypatch, capsys): + """The whole point: exit 2 with a diagnostic, not 0 with `"ok": true`. + + Drives `main()` rather than grepping it for a handler, because the last + defect of this shape (GI-055) was a source-level edit that matched + nothing and still read as correct in review. + """ + fa = tmp_path / "in.fa" + fa.write_text(">seq\n" + "ACGT" * 100 + "\n") + + class _FakeClient: + def __init__(self, *a, **kw): + pass + + def predict(self, *a, **kw): + return {"data": {"summary": "all good"}, "meta": {}} + + monkeypatch.setattr(gi_predict, "Client", _FakeClient) + monkeypatch.setenv("GI_API_KEY", "partner-test-key") + monkeypatch.setattr( + sys, "argv", + ["gi_predict.py", "--task", "promoter", "--input", str(fa), + "--output", str(tmp_path / "out")], + ) + + assert gi_predict.main() == 2 + err = capsys.readouterr() + assert "unexpected API response shape" in err.err + assert "data.summary" in err.err + assert '"ok": true' not in err.out + assert not (tmp_path / "out" / "report.md").exists() + + def test_absent_and_null_are_still_legitimate(self): + """Only a *present, wrong-typed* field is malformed. + + A task with no `prediction` omits it; coercing that to `{}` is correct + and must not become a refusal, or every sparse-but-valid response breaks. + """ + body = {"data": {"summary": {"total_windows": 5}, "regions": None}, + "meta": None} + out = gi_predict._summarize("promoter", body) + assert out["regions"] == [] + assert out["raw_summary"] == {"total_windows": 5} + + def test_a_well_formed_body_still_reports_its_rows(self, tmp_path): + body = { + "data": { + "summary": {"promoter_windows": 2, "total_windows": 5}, + "regions": [{"name": "r1", "start": 10, "end": 20, "score": 0.9}], + }, + "meta": {"request_id": "req-1"}, + } + summary = gi_predict._summarize("promoter", body) + assert summary["regions"] == body["data"]["regions"] + gi_predict._write_report( + "promoter", summary, body, tmp_path, tmp_path / "in.fa", "seq", 9198, 12.0, + ) + report = (tmp_path / "report.md").read_text() + assert "req-1" in report and "r1" in report diff --git a/nim-skills/genomic-intelligence-nim/tests/test_reproducibility.py b/nim-skills/genomic-intelligence-nim/tests/test_reproducibility.py new file mode 100644 index 0000000..2291516 --- /dev/null +++ b/nim-skills/genomic-intelligence-nim/tests/test_reproducibility.py @@ -0,0 +1,102 @@ +"""Reproducibility replay guard for the Genomic Intelligence runner. + +Regression target: ``reproducibility/command.sh`` must reproduce the original +invocation. It historically dropped ``--description`` (required by expression, +no default) and a non-default ``--model``, so replaying an expression job hit +the runner's own validation gate and exited 1. + +All tests are offline (no network, no API key) except the one marked +``@pytest.mark.integration``, which replays ``command.sh`` against the live API +when ``GI_API_KEY`` is set. Run from the skill directory: ``pytest tests/``. +""" +from __future__ import annotations + +import os +import shlex +import subprocess +import sys +from pathlib import Path + +import pytest + +SKILL_DIR = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(SKILL_DIR / "scripts")) +import gi_predict # noqa: E402 + + +def _reparse(cmd: str): + """Tokenize a generated command and run it back through the runner's parser. + + This is the heart of the replay guard: if the generated command survives the + runner's own argument parsing with the required flags intact, a real replay + will pass the same pre-flight it originally passed. + """ + argv = shlex.split(cmd) + assert argv[:2] == ["python3", "scripts/gi_predict.py"], argv + saved = sys.argv + try: + sys.argv = ["gi_predict.py"] + argv[2:] + return gi_predict._parse_args() + finally: + sys.argv = saved + + +def test_expression_command_keeps_description(): + cmd = gi_predict._repro_command( + "expression", Path("in.fa"), Path("out"), model=None, description="K562 cells" + ) + ns = _reparse(cmd) + assert ns.task == "expression" + assert ns.description == "K562 cells", f"replay would fail the --description gate: {cmd}" + + +def test_command_keeps_nondefault_model(): + # Any non-default model id: the guard is that --model survives the replay, + # not that this particular id exists. + cmd = gi_predict._repro_command( + "promoter", Path("in.fa"), Path("out"), model="non-default-model", description=None + ) + assert _reparse(cmd).model == "non-default-model" + + +def test_command_omits_absent_flags(): + cmd = gi_predict._repro_command("promoter", Path("in.fa"), Path("out")) + assert "--description" not in cmd and "--model" not in cmd + + +def test_command_uses_python3_and_quotes_spaces(): + cmd = gi_predict._repro_command( + "expression", Path("a b.fa"), Path("out dir"), description="K562 cells" + ) + assert cmd.startswith("python3 ") + # spaces must be quoted so the replay tokenizes back to the same values + ns = _reparse(cmd) + assert ns.input_file == Path("a b.fa") + assert ns.output == Path("out dir") + assert ns.description == "K562 cells" + + +@pytest.mark.integration +def test_expression_bundle_replays_end_to_end(tmp_path): + """Full round-trip against the live API: run expression, then replay the + generated command.sh verbatim; both must exit 0. Requires GI_API_KEY.""" + if not os.environ.get("GI_API_KEY"): + pytest.skip("GI_API_KEY not set") + out = tmp_path / "expr" + first = subprocess.run( + [sys.executable, "scripts/gi_predict.py", "--task", "expression", "--demo", + "--description", "K562 cells", "--output", str(out)], + cwd=SKILL_DIR, capture_output=True, text=True, timeout=180, + ) + assert first.returncode == 0, first.stderr + command_sh = out / "reproducibility" / "command.sh" + body = command_sh.read_text() + # Ran without --model, so the default was used: the replay must NOT inject + # --model (regression guard for the _write_report model-shadowing bug). + assert "--description" in body, "expression replay must carry --description" + assert "--model" not in body, "must not inject --model when the user relied on the default" + replay = subprocess.run( + ["bash", str(command_sh)], cwd=SKILL_DIR, + capture_output=True, text=True, timeout=180, + ) + assert replay.returncode == 0, f"replay failed: {replay.stderr}" diff --git a/nim-skills/proteinmpnn-nim/evals/harbor/proteinmpnn-local-design/tests/__pycache__/grader.cpython-314.pyc b/nim-skills/proteinmpnn-nim/evals/harbor/proteinmpnn-local-design/tests/__pycache__/grader.cpython-314.pyc deleted file mode 100644 index 05274cc..0000000 Binary files a/nim-skills/proteinmpnn-nim/evals/harbor/proteinmpnn-local-design/tests/__pycache__/grader.cpython-314.pyc and /dev/null differ diff --git a/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/SKILL.md b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/SKILL.md new file mode 100644 index 0000000..d71451e --- /dev/null +++ b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/SKILL.md @@ -0,0 +1,249 @@ +--- +name: genomic-intelligence-nim +description: > + Predict regulatory features, gene structure, and expression directly from DNA sequence using Genomic Intelligence's hosted DNA language models. Six tasks over one hosted REST contract — promoter regions, splice donor/acceptor sites, enhancer activity, chromatin state, sequence-to-expression (log TPM), and de-novo gene/transcript annotation. Use for regulatory genomics, promoter/enhancer/splice/chromatin scanning, expression prediction, and gene annotation from a gene name, a genomic region, or a FASTA. Bearer auth; no local GPU or model weights. +license: Apache-2.0 AND CC-BY-4.0 +compatibility: "requests>=2.28" +allowed-tools: Bash, Read, Write, AskUserQuestion +--- + +# Genomic Intelligence NIM + +One skill, six DNA-sequence prediction tasks, served by the hosted Genomic +Intelligence API. Give it a **gene name**, a **genomic region**, or a **FASTA**; +it resolves a sequence, calls that task's own predict operation, and writes a +report + machine-readable JSON. Inference is remote — no model weights, GPU, or +heavyweight Python stack; the only dependency is `requests`. + +Load supplemental files only when needed: + +- `references/tasks.md`: per-task model, bounds, output fields, and biology. +- `references/api.md`: endpoints, request/response envelope, async, errors. +- `references/authentication.md`: `GI_API_KEY`, base URL, partner tiers. +- `references/sequence-acquisition.md`: gene/region → FASTA, TSS window, species. +- `references/errors.md`: error envelope, rate limits, async polling detail. + +> **Hosted, third-party service.** Genomic Intelligence is operated by Genomic +> Intelligence, not NVIDIA. The interface is the hosted-NIM shape (HTTPS + +> `Authorization: Bearer` + JSON). There is no local Docker mode. +> +> Research and development use. Not for clinical or diagnostic decisions. + +## The six tasks + +Each task is its own published operation — `POST /v1/tasks/promoter/predict`, +`/v1/tasks/splice/predict`, and so on — with its own request schema, its own +minimum length, and its own closed `options` object. The URLs are unchanged from +what callers already send. + +| Task | What it predicts | Recommended mode | Accepted length | `context_window_bp` | +|---|---|---|---|---| +| `promoter` | Promoter regions (sliding window) | sync | 300–500,000 bp | 2,000 bp (300 bp models exist) | +| `splice` | Splice donor/acceptor sites | sync | 100–500,000 bp | 15,000 bp | +| `enhancer` | Developmental & housekeeping enhancer activity | sync | 50–500,000 bp | 249 bp | +| `chromatin` | Chromatin state across hundreds of tracks | sync | 200–500,000 bp | 1,000 bp | +| `expression` | Expression as log(TPM+1) | sync | **9,198–500,000 bp** | n/a (`trained_window_bp` 9,198) | +| `annotation` | De-novo gene/transcript structure | async | 1,000–500,000 bp | n/a | + +`Recommended mode` is guidance, not a constraint — every task accepts both. Omit `Prefer` for a synchronous `200`; send `Prefer: respond-async` for a `202` plus `GET /v1/tasks/jobs/{job_id}`. Only the composite workflow enforces a mode, rejecting sync above 50,000 bp with `413 sync_too_large`. + +The minimum is **admission control, not regime**. A sequence above the floor but +shorter than the selected model's `bio_spec.context_window_bp` is *accepted and +scored* — against a window padded out to the context window. So a 100 bp enhancer +request succeeds, but the model saw ~150 bp of padding; compare your length +against `context_window_bp` (from `GET /v1/tasks/{task}/models`) to know whether +it scored real sequence. Longer-than-context input is fine: the scanner steps a +prediction window at a time and pads only the final partial window. All lengths +are measured **after whitespace is stripped**, so a line-wrapped FASTA body can be +pasted verbatim. Under the floor and over the cap are both +`422 validation_failed` — over-length is *not* a `413`. + +`expression` additionally needs a cell-type/assay context string +(`--description`, e.g. `"K562 cells"`). The model always scores exactly one +9,198 bp TSS-centred window, so 9,198 bp is a hard floor — but the endpoint +accepts up to 500,000 bp and will cut the window for you if you pass +`--tss-index` (the 0-based TSS offset into the sequence). `--tss-index` is +required for any expression sequence that is not exactly 9,198 bp. +`annotation` defaults to `Prefer: respond-async` and polls to completion; the +mode is the runner's choice, not an API constraint. +Details: `references/tasks.md`. + +`options` is closed (`additionalProperties: false`) on every task, and each task +declares different keys — `description` exists only on `expression`. An +unrecognised key is a hard `422 validation_failed`, never ignored, so never +forward an option you have not confirmed against the live schema. + +## Authentication + +This skill calls a hosted API and requires a partner bearer key (`gi_…`): + +```bash +export GI_API_KEY=gi_yourkeyhere +``` + +Request a key at **contact@genomicintelligence.ai**. Do not commit or hard-code a +key — it is resolved from the environment. Optional override: `GI_BASE_URL` +(default `https://api.genomicintelligence.ai`). See +`references/authentication.md`. + +## Install + +Python ≥3.8 and one package — no weights, no GPU: + +```bash +pip install requests +``` + +## Provided scripts + +This skill ships a small, self-contained (`requests`-only) runner rather than +inline snippets: the surface spans six tasks plus an async job (`annotation`) +and a windowing contract (`expression`) that do not inline cleanly. The runner +is the same client Genomic Intelligence's other integrations use. + +- **`scripts/gi_predict.py`** — one CLI, six tasks: FASTA → prediction → + `report.md` + `result.json` + `reproducibility/`, and a compact JSON summary on + stdout. Owns auth, length validation, the sync/async split, and error handling. +- **`scripts/gi_fetch.py`** — optional acquisition: gene symbol or region → + reference FASTA via Ensembl (public, no key), including TSS-centring for + `expression`. +- **`scripts/gi_client.py`**, **`scripts/gi_ensembl.py`** — the `/v1` client and + Ensembl helpers the two CLIs import. + +**Use the provided scripts — do not hand-roll `curl`, an Ensembl fetch, or an +inline HTTP client.** They own the length/async/expression contract. + +## Quick start + +Each task ships a real reference FASTA in `assets/demo/`: + +```bash +# Promoter scan of the TP53 locus (chr17, GRCh38) +python scripts/gi_predict.py --task promoter --demo --output out/promoter + +# Splice sites in HBB +python scripts/gi_predict.py --task splice --demo --output out/splice + +# Expression (needs a cell-type context; fixture is a 9,198 bp TSS window) +python scripts/gi_predict.py --task expression --demo --description "K562 cells" --output out/expr + +# De-novo annotation (async submit → poll, no extra flags) +python scripts/gi_predict.py --task annotation --demo --output out/annot +``` + +**By gene name** (fetch → predict, the common real case): + +```bash +FASTA=$(python scripts/gi_fetch.py --gene TP53 --out out/tp53.fa) +python scripts/gi_predict.py --task promoter --input "$FASTA" --output out/promoter + +# Expression of HBB in K562 — the exact 9,198 bp TSS window is built for you +FASTA=$(python scripts/gi_fetch.py --gene HBB --for-expression --out out/hbb.fa) +python scripts/gi_predict.py --task expression --input "$FASTA" --description "K562 cells" --output out/expr + +# Or hand over a whole locus and name the TSS; the server slices TSS +/- 4,599 bp. +# +# STRAND: expression scores whatever you send, in the orientation you send it. +# It never reverse-complements, and nothing in the request or the response +# reports strand -- a wrong-strand window returns a confident number, not an +# error. Always submit gene-sense sequence. --region returns the strand you ask +# for and defaults to --strand 1, so a minus-strand gene needs --strand -1 +# explicitly. HBB is minus-strand. +LOCUS=$(python scripts/gi_fetch.py --region chr11:5,220,000-5,240,000 --strand -1 \ + --out out/locus.fa) +# Offset of the TSS into the returned sequence, 0-based, whitespace stripped. +# Plus strand: TSS_INDEX = TSS - REGION_START +# Minus strand: TSS_INDEX = REGION_END - TSS (the sequence is reverse-complemented) +# Must satisfy 4599 <= TSS_INDEX <= len(sequence) - 4599. +# HBB 5' end on the minus strand is 5,229,395 (Ensembl, GRCh38). +TSS_INDEX=$(( 5240000 - 5229395 )) +python scripts/gi_predict.py --task expression --input "$LOCUS" --description "K562 cells" \ + --tss-index "$TSS_INDEX" --output out/expr +``` + +Prefer `--for-expression` when you have a gene symbol: it resolves the canonical +transcript and cuts the window for you, so there is no offset to get wrong. A +`--tss-index` that is in range but wrong is not an error — it scores the wrong +window and returns `200`. + +`gi_predict.py` prints a compact JSON summary to **stdout** (headline scalars +only; bulky per-item arrays stay in `result.json`). Progress/verification lines +go to **stderr**: + +``` +[gi-] OK — wrote out//report.md (NNN ms wall) +``` + +## Minimal inline call (no scripts) + +For the simplest sync tasks you can call the endpoint directly: + +```python +import os, requests + +base = os.environ.get("GI_BASE_URL", "https://api.genomicintelligence.ai").rstrip("/") +resp = requests.post( + f"{base}/v1/tasks/promoter/predict", + headers={"Authorization": f"Bearer {os.environ['GI_API_KEY']}", + "Content-Type": "application/json", + "User-Agent": "BioNeMo-GI-Skill/0.1.0"}, + json={"sequence": "ACGT...", "sequence_name": "example"}, + timeout=300, +) +resp.raise_for_status() +body = resp.json() # {"data": {...}, "meta": {...}} +print(body["data"]["summary"]) +``` + +Prefer the runner for `expression` (window/`tss_index` bounds + `description`) and +`annotation` (async) — those are error-prone to inline. + +## Standard workflow + +1. **Identify the task** from the request (map to one of the six above; if + ambiguous between promoter/enhancer/chromatin, ask — they are distinct models). +2. **Resolve the sequence.** If the user attached a FASTA, use it. If they named + a gene, `gi_fetch.py --gene ` (add `--for-expression` for expression). + If they gave a region, `gi_fetch.py --region `. Add + `--species ` for non-human (default human/GRCh38). +3. **Predict:** + ```bash + python scripts/gi_predict.py --task --input --output \ + [--model ] [--description ""] [--tss-index ] + # --description and --tss-index: expression only + ``` +4. **Read the result:** parse the stdout JSON for the headline; open + `/report.md` or `/result.json` for detail. + +## Validate and report + +Treat an invalid alphabet, an out-of-bounds length, a missing `expression` +window/description/`--tss-index`, or a non-2xx response as **hard failures** (the +runner exits non-zero and names the cause on stderr). Treat zero hits on a +sequence you expected to be feature-bearing as a **warning**. Record +`meta.model` and `meta.request_id` for audit. For `expression`, also check the +`scored_window` / `tss_index` echoed in the stdout summary: a `--tss-index` that +is in range but wrong is not an error, it just scores the wrong window. + +## Troubleshooting + +| Symptom (stderr) | Cause | Fix | +|---|---|---| +| `GI_API_KEY is not set` | No key | `export GI_API_KEY=gi_…` | +| `sequence too short: … < 9,198 bp minimum` | Expression sequence below the window size | Use `gi_fetch.py --gene X --for-expression` | +| `sequence too short: … bp minimum` (other tasks) | Below the task floor (promoter 300, splice 100, enhancer 50, chromatin 200, annotation 1,000) | Fetch more sequence; server-side this is a `422`, not a `413` | +| `API error: [413 payload_too_large]` | Raw request body over 16 MiB | Split the input; this is the body cap, not the sequence cap | +| `--tss-index is required unless the sequence is exactly 9,198 bp` | Longer locus, no TSS named | Add `--tss-index <0-based offset>` | +| `--tss-index … outside the allowed range` | TSS too close to an edge | Submit more flanking sequence | +| `--description is required` | expression w/o context | `--description "K562 cells"` | +| `API error: [401 …]` | Bad/revoked key | Re-check `GI_API_KEY` | +| `API error: [422 …]` | Body/model rejected | Check `--model` in `references/tasks.md` | +| `API error: [429 …]` | Rate limit | Back off; partner tiers have caps | +| `API error: [504 timeout]` | Large sync req, cold GPU | Retry or shorten | +| `parsed an empty sequence` | Empty/invalid FASTA | Check the file is a single ACGT record | +| `invalid input — …: sequence contains characters outside ACGTN` | IUPAC ambiguity codes or gap characters | Resolve them to explicit bases; the parser refuses rather than deleting them, because deleting shifts every downstream coordinate | +| `invalid input — …: expected a single FASTA record` | Multi-record FASTA | Split the file and submit one record per request | +| `network error reaching the API` | DNS/TLS/connection failure or read timeout | Transport-level, not a request problem; retry | +| `timed out waiting for the job` | Async job outlived the poll deadline | Retry, or shorten the input | + +More: `references/errors.md`. diff --git a/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/assets/demo/annotation_tp53.fa b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/assets/demo/annotation_tp53.fa new file mode 100644 index 0000000..d8cbe73 --- /dev/null +++ b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/assets/demo/annotation_tp53.fa @@ -0,0 +1,241 @@ +>human|TP53|chr17:7668402-7687550|GRCh38 +CACCCCTCAGACACACAGGTGGCAGCAAAGTTTTATTGTAAAATAAGAGATCGATATAAAAATGGGATATAAAAAGGGAG +AAGGAGGGGAAGGGTGGGGTGAAAATGCAGATGTGCTTGCAGAATGTAAAAGATGTTGACCCTTCCAGCTGGACGTGGTG +GCTCACAATTGTAATCCCAGCACTCTGGGAGGCTGAGACAGGTGGATCGCCTGAGCCCAGGAGTTTGAGACCAGCCTGGG +CAACACTGTGAGACCCCATCTCTACAAAACATGCAAAAGTTGGCTGGCCATGGTGGCATGAACCTGTGGTCCCAGCTACT +CCGGAGGCTGAGGCAGGACTGCTCGAGCCGGGGAGGCAAAGGCTGCAGTAAGCCAAGATCACGCCACTCCACTCCAGCCT +GGGCAACAAAGCGAGACCCAGTCTCAAAGAAAAAGAAAAAAAAAAAAAAAAAAGAAAAAAGAAATTGACCCTGAGCATAA +AACAAGTCTTGGTGGATCCAGATCATCATATACAAGAGATGAAATCCTCCAGGGTGTGGGATGGGGTGAGATTTCCTTTT +AGGTACTAAGGTTCACCAAGAGGTTGTCAGACAGGGTTTGGCTGGGCCAGCAGAGACTTGACAACTCCCTCTACCTAACC +AGCTGCCCAACTGTAGAAACTACCAACCCACCGACCAACAGGGAGAGGGAACAAGCACCCTCAAGGGGGTCAAGTTCTAG +ACCCCATGTAATAAAAGGTGGTTTCAAGGCCAGATGTACATTATTTCATTAACCCTCACAATGCACTCTGTGAGGTAGGT +GCAAATGCCAGCATTTCACAGATATGGGCCTTGAAGTTAGAGAAAATTCAACAGTGAGGGACAGCTTCCCTGGTTAGTAC +GGTGAAGTGGGCCCCTACCTAGAATGTGGCTGATTGTAAACTAACCCTTAACTGCAAGAACATTTCTTACATCTCCCAAA +CATCCCTCACAGTAAAAACCTTAAAATCTAAGCTGGTATGTCCTACTCCCCATCCTCCTCCCCACAACAAAACACCAGTG +CAGGCCAACTTGTTCAGTGGAGCCCCGGGACAAAGCAAATGGAAGTCCTGGGTGCTTCTGACGCACACCTATTGCAAGCA +AGGGTTCAAAGACCCAAAACCCAAAATGGCAGGGGAGGGAGAGATGGGGGTGGGAGGCTGTCAGTGGGGAACAAGAAGTG +GAGAATGTCAGTCTGAGTCAGGCCCTTCTGTCTTGAACATGAGTTTTTTATGGCGGGAGGTAGACTGACCCTTTTTGGAC +TTCAGGTGGCTGTAGGAGACAGAAGCAGGGAGGAGAGATGACATCACATGAGTGAGAGGGTCTGTGCCCCTTTTCCCTGA +CCAATGCTTTGAAGGGCCTAAGGCTGGGACAACGGGAATTCAAATCAAGATGGTGGCCACACCCCATGCAAATATGTTTA +CTGAGCACCTCAGAGTATTAGTGTGTATTAGTCTCGTAATCTTCCCTTACCCCATTTTACTTTATTTATCTTTTTTGAGA +CGGAGTTTCACTCTTGTTGCCCAGGCTGGAGTGTAATGGTGAGATCTCAGCTCACCGCAACCTCTGCCTCCCGGGTTCAA +GCGATTCTCCTGCCTCAGCCTCCCGAGTAGGTAGCTGGGATTACAGGCATGCATCACCACGCCCGGCTACTTTTGTATTT +TTAGTAGAGATGGGGTTTCTCCATGTTGGTCAGGCTGGGCTCAAACTCCCGACCTCAGGTGATCCACTCGCCTTGGCCTC +CCAGAGTGTGGGATTCGTGAGCCACTGCGCCCGGCCCCCTTACCCCATTTTATATATAAGGAAACTGAGTTTGACGGGGG +TCACCTAGGACCTGCCGGTGCATGGCAGGGCTGAGTATATGACCTGAAACTCTGGCTGTATTCAGTATTACACAATTATT +AGGCCCCTCCTTGAGACCCTCCAGCTCTGGGCTGGGAGTTGCGGAGAATGGCAAAGAAGTATCCACACTCGTCCCTGGGT +TTGGATGTTCTGTGGATACACTGAGGCAAGAATGTGGTTATAGGATTCAACCGGAGGAAGACTAAAAAAATGTCTGTGCA +GGGCTGGGACCCAATGAGATGGGGTCAGCTGCCTTTGACCATGAAGGCAGGATGAGAATGGAATCCTATGGCTTTCCAAC +CTAGGAAGGCAGGGGAGTAGGGCCAGGAAGGGGCTGAGGTCACTCACCTGGAGTGAGCCCTGCTCCCCCCTGGCTCCTTC +CCAGCCTGGGCATCCTTGAGTTCCAAGGCCTCATTCAGCTCTCGGAACATCTCGAAGCGCTCACGCCCACGGATCTGCAG +CAACAGAGGAGGGGGAGAAGTAAGTATATACACAGTACCTGAGTTAAAAGATGGTTCAAGTTACAATTGTTTGACTTTAT +GACGGTACAAAAGCAACATGCATTTAGTAGAAACTGCACTTCAAGTACCTATACAGCTGACTTTTAAAAATATTTATTTA +TTTATTTTGAGATGGGGTCTCACTCTGTTGCCCAGGCGGGAGTGCAATGGTGCAATCTTGGCTGATTGCAATCTCCGCCT +CTGGGGTTCAAGTGATTCTTGTGCCTCAGCCTCCCGAGTAGCTGGGACTACAGGCGTGTGCTACCACACCTGGCTAATTT +TTGTGTTTTTAGTAGAGATGGGGCTTCACCATGTTAGCCAGGCTGGTTTCCAACTCCTGACGTCAGGTGATCTACCCACC +TCCACCTCCCAAAGTGCTGGGATTACAGGTGTGAGCCACTGTGCCCGGCCCTTTTTTAAATTTTAGAGATGATGTCTTGC +TATGTTGTTCAGGCTGGACTCAAACTCTTGGGCTCAAGAGATCCTCCTGCCTTAGCCTCTCAAGTAACTGGGACTACATG +TGCATGCGACTGTGCCTCGTTTCTTTTCTTTTTTTTCTGAGACGGAGTCTCACTCTATCGCCCAGGCTGGAGTGCAGTGG +CGCCATCTTGGCTCCCTGCAACCTCCGCCTCCTGGTTCAAGCGATTCTCCTGCCTCAGCCTCCCAAGTAGCTGGGATTAC +AGGCACCTGCCATCACGCCCGGTTAATTTTTGTATTTTAGTAGAGACGGGGTTTCACCATGTTGGCTAGGCTGGTCTTGA +ACTCCTGACCTCAGGTGATCCACCCGCCTCAGCCTCCCGAAATGCTGGGATTACAGGCGTGAGCCAGTGCGCCTGGCCTT +TTCTTTTTTTGAGTCTCGCTCTGCGCCCAGGCTGTGCCTGGCTCGACTGTGCCTCCTTTCATGCAACCATGCTGTTTCTC +ACTTTCAGTAACAATATTCAATAAATCACATGAGATATACAACATTTTATTACTATAAAAAGGGCTTTGTGTTAGATGAC +TTTGCCCAACTGTAGGGTAACTTAAATGCTCTGAACACGTTTCAAGTAGGCTAGGGCTGAGTGTGGTAGCTCATGCCTGT +AACCCCAATACTTGGGGAGGCTGAGGTGGAAGGATTGATTGAGCCCAGGGGTTTGATACCAGCATGGGCAACGTAGCAAG +ACCTTGACTTCACAGAAAATAAAAAATTAGCTGGGTGTCGTGGCATGTGCCTGTAGTCCTAGCTACTTGGGAGGGTGAAA +TCACCGGAGCCCAGGGAGGTCAAGGCTGCAGTGAGCTGAGATGGTGCCACTGCACTCTAGCCTGAGTGACAGAGTGAGAC +TCTGTCTTTAAATAAATAAATAAAAATTAGCCGGGCGTGGTGGCTCACACCTGTAATCCCAGCACTTTGGGAGGCCGAGG +CGGGCGGATCACATGGTCAGAAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCTGTCTCTACTAAAAATACAAAAAT +TAGCTGGGCGTGGTAGCAGGCGCTTGTAGTCCTAGCTATTCGGGAGGCTGAGGCAGGAGAATCACTTGAACCCAGGAGGC +AGAGGTTGCAGTGAGCCGAGATCATGCCACTGCACTCCAGCCTGGGCGACAGAGTGAGACTGAGTCTCAAAAAAATAAAA +TAAAATAAAATAAAAATAAATAAATAAAAATTAGCCAGGCATGGTGGTGCAGGCCTGTAGTTGAAGCAACTTGGGAGGCT +GAGCTGGGAGGATGGATGGAGCCTGGGAGGTGGAGGCTGCAGTGAGCTGTGACTGCACTACTGCACTCTATCCAGCCTGG +GTGACAGAGCAAGACCTTGTCTCAAAAAAGTAGGCTAGAGACCAGCCTGGGCAACATAGTGAGACTCTATCTATCTACAA +AAAATTTTAAAAATTAGCTGGGTATGGTGGTGTATGCCTGTGGTCCTAGCTACTGGGGAGGCAGAGTTAGGGGGATTGCT +TGAGCCCAGGAGGGTATAATGAGCTATGATCACATCACTGTAATCCAGCCTGGGCAACAGAGCAAGATGCTGTCTCCATT +AAAAATAAAATAAAAGTAGGCTAGGCAGGCCGGGTGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCAAGGC +AGGCAGATCACAAGGTCAGGAGTTCGAGACTAGCCTGGCCAACATGGTGAAACCTCATCTCTACTAAAAAAAAAAATAAA +TAAATAACAAAAAATTAGCTGGGCGTCGGGGCAGGTGCCTGTAATCCCAGCTACTCAGTGGGCTGAGGCAGGAGAATCGC +TTGAACCCAGAAGGCGGAGGTTGCAGTGAGCCGAGATCCCGCCACTGCACTCCAGCCTGGGTGACAGAGTGAGACTCTGT +CTCCAAAAAAAAAAAAAAAAAAAGCAGGCTAGGCTAAGCTATGATGTTCCTTAGATTAGGTGTATTAAATCCATTTTCAA +CTTACAATATTTTCAACTTACGACGAGTTTATCAGGAAGTAACACCATCGTAAGTCAAGTAGCATCTGTATCAGGCAAAG +TCATAGAACCATTTTCATGCTCTCTTTAACAATTTTCTTTTTGAAAGCTGGTCTGGTCCTTTAAAATATATATTATGGTA +TAAGTTGGTGTTCTGAAGTTAGTTAGCTACAACCAGGAGCCATTGTCTTTGAGGCATCACTGCCCCCTGATGGCAAATGC +CCCAATTGCAGGTAAAACAGTCAAGAAGAAAACGGCATTTTGAGTGTTAGACTGGAAACTTTCCACTTGATAAGAGGTCC +CAAGACTTAGTACCTGAAGGGTGAAATATTCTCCATCCAGTGGTTTCTTCTTTGGCTGGGGAGAGGAGCTGGTGTTGTTG +GGCAGTGCTAGGAAAGAGGCAAGGAAAGGTGATAAAAGTGAATCTGAGGCATAACTGCACCCTTGGTCTCCTCCACCGCT +TCTTGTCCTGCTTGCTTACCTCGCTTAGTGCTCCCTGGGGGCAGCTCGTGGTGAGGCTCCCCTTTCTTGCGGAGATTCTC +TTCCTCTGTGCGCCGGTCTCTCCCAGGACAGGCACAAACACGCACCTCAAAGCTGTTCCGTCCCAGTAGATTACCACTAC +TCAGGATAGGAAAAGAGAAGCAAGAGGCAGTAAGGAAATCAGGTCCTACCTGTCCCATTTAAAAAACCAGGCTCCATCTA +CTCCCAACCACCCTTGTCCTTTCTGGAGCCTAAGCTCCAGCTCCAGGTAGGTGGAGGAGAAGCCACAGGTTAAGAGGTCC +CAAAGCCAGAGAAAAGAAAACTGAGTGGGAGCAGTAAGGAGATTCCCCGCCGGGGATGTGATGAGAGGTGGATGGGTAGT +AGTATGGAAGAAATCGGTAAGAGGTGGGCCCAGGGGTCAGAGGCAAGCAGAGGCTGGGGCACAGCAGGCCAGTGTGCAGG +GTGGCAAGTGGCTCCTGACCTGGAGTCTTCCAGTGTGATGATGGTGAGGATGGGCCTCCGGTTCATGCCGCCCATGCAGG +AACTGTTACACATGTAGTTGTAGTGGATGGTGGTACAGTCAGAGCCAACCTAGGAGATAACACAGGCCCAAGATGAGGCC +AGTGCGCCTTGGGGAGACCTGTGGCAAGCAGGGGAGGCCTTTTTTTTTTTTTTTTGAGATGGAATCTCGCTCTGTCGCCC +AGGCTGGAGTGCAGTGGCGTGATCTCAGCTCACTGCAAGCTCCACCGCCCAGGTTCACGCCATTCTCCTTCCTCAGCCTC +CCGAGTAGCTGGGACTACAGGTGCCCAGCACCACGCCCGGCTAATTTTTTTTTGTATTTTTCAGTAGAGACGGGGTTTCA +CCGTTAGCCAGGATGGTCTCGATCTCCCAACCTCGTGATCCGCCTGCCTTGGCCTCCCAAAGTGCTGGGATTACAGGCAT +GAGCCACTGCGCCCAGCCAAGCAGGGGAGGCCCTTAGCCTCTGTAAGCTTCAGTTTTTTCAACTGTGCAATAGTTAAACC +CATTTACTTTGCACATCTCATGGGGTTATAGGGAGGTCAAATAAGCAGCAGGAGAAAGCCCCCCTACTGCTCACCTGGAG +GGCCACTGACAACCACCCTTAACCCCTCCTCCCAGAGACCCCAGTTGCAAACCAGACCTCAGGCGGCTCATAGGGCACCA +CCACACTATGTCGAAAAGTGTTTCTGTCATCCAAATACTCCACACGCAAATTTCCTTCCACTCGGATAAGATGCTGAGGA +GGGGCCAGACCTAAGAGCAATCAGTGAGGAATCAGAGGCCTGGGGACCCTGGGCAACCAGCCCTGTCGTCTCTCCAGCCC +CAGCTGCTCACCATCGCTATCTGAGCAGCGCTCATGGTGGGGGCAGCGCCTCACAACCTCCGTCATGTGCTGTGACTGCT +TGTAGATGGCCATGGCGCGGACGCGGGTGCCGGGCGGGGGTGTGGAATCAACCCACAGCTGCACAGGGCAGGTCTTGGCC +AGTTGGCAAAACATCTTGTTGAGGGCAGGGGAGTACTGTAGGAAGAGGAAGGAGACAGAGTTGAAAGTCAGGGCACAAGT +GAACAGATAAAGCAACTGGAAGACGGCAGCAAAGAAACAAACATGCGTAAGCACCTCCTGCAACCCACTAGCGAGCTAGA +GAGAGTTGGCGTCTACACCTCAGGAGCTTTTCTTTTTTTTTTTTTTTTTTGAGATAGGGTCTTGCTCTGTCACTCAGGCT +GGAGCACAGTGGTGTGATCACAGCTCACTGCAGCCTCCATCTCCTGGCCTCAAGTGATCTTCCCACCTCAGCCTCCTAAG +TGGCTGGGACTATAGGTGTGCACCACCATGCCTGGCTAATTTTTTGTATTTTTTTGTAGAGACGAGGTTTCATCATGTTA +CCCAGGCTGGTCTTGAACTCCTGGGCTCAGGTGATCTGCCTGCCTTGGCCTCTTTGAGAGTGCTGGGATTGCAGGTGTGA +GCCACCAAGCCTGGTCAGGAGCTTATTTTCAAAAGCCAAGGAATACACGTGGATGAAGAAAAAGAAAAGTTCTGCATCCC +CAGGAGAGATGCTGAGGGTGTGATGGGATGGATAAAAGCCCAAATTCAAGGGGGGAATATTCAACTTTGGGACAGGAGTC +AGAGATCACACATTAAGTGGGTAAACTATAAAAAAACACTGACAGGAAGCCAAAGGGTGAAGAGGAATCCCAAAGTTCCA +AACAAAAGAAATGCAGGGGGATACGGCCAGGCATTGAAGTCTCATGGAAGCCAGCCCCTCAGGGCAACTGACCGTGCAAG +TCACAGACTTGGCTGTCCCAGAATGCAAGAAGCCCAGACGGAAACCGTAGCTGCCCTGGTAGGTTTTCTGGGAAGGGACA +GAAGATGACAGGGGCCAGGAGGGGGCTGGTGCAGGGGCCGCCGGTGTAGGAGCTGCTGGTGCAGGGGCCACGGGGGGAGC +AGCCTCTGGCATTCTGGGAGCTTCATCTGGACCTGGGTCTTCAGTGAACCATTGTTCAATATCGTCCGGGGACAGCATCA +AATCATCCATTGCTTGGGACGGCAAGGGGGACTGTAGATGGGTGAAAAGAGCAGTCAGAGGACCAGGTCCTCAGCCCCCC +AGCCCCCCAGCCCTCCAGGTCCCCAGCCCTCCAGGTCCCCAGCCCAACCCTTGTCCTTACCAGAACGTTGTTTTCAGGAA +GTCTGAAAGACAAGAGCAGAAAGTCAGTCCCATGGAATTTTCGCTTCCCACAGGTCTCTGCTAGGGGGCTGGGGTTGGGG +TGGGGGTGGTGGGCCTGCCCTTCCAATGGATCCACTCACAGTTTCCATAGGTCTGAAAATGTTTCCTGACTCAGAGGGGG +CTCGACGCTAGGATCTGACTGCGGCTCCTCCATGGCAGTGACCCGGAAGGCAGTCTGGCTGCTGCAAGAGGAAAAGTGGG +GATCCAGCATGAGACACTTCCAACCCTGGGTCACCTGGGCCTGCAGAGAAGGAACCCCCTCCCCCAACACCATGCCAGTG +TCTGAGACAGCTCGGCTTCCTGTGGAGCAGGAAAAGAATGGCTGCTTCACATTCTCTCTTCCAATGTTTCACCACAACCC +AAGCACTCCTGCCCCACCCCTCACCAGCCATGCACTTCTTTGAGGAAAAGACAATCAGAGAGGGACTTCCAACCTTCCCA +CCACTAAATCCCCAAGACTTCCTAAATGTGCACCCTATTCCCAACTCCCTTCCTGTATTTTTTTTTTTTTTTTGAGATGG +AGTCTCTCTCTGTCACCTAGGCTGGAGCACAGTGGCATGATCTCAGCTCACTGCAACCTCTACCTTCCGGGTTCAAGCCA +TTCTCCTGCCTCAGTCTCCCGAGTAGCTGGGATTACAGGCGAGTACCACCACACCCAGCTAATTTTTGTATTTTTAGTAG +AGACAGGGCTTTGCATGTTGGCCAGGCTGGTCTCGAACTCCTTACTTCAGGTGATCGGCCCGCCTCAGCCTCCTAAAGTG +CCAAGATTACAGGTGTGAGCTACCGTGCCCTGCTCCCACCTCCTGTTAACAAGGATATAGTCATTCTCAGCCTGCAATCT +CTGTATGGGGAAGGACACCCCCTTGGCCCCCACCCTTCCCCACCTGATACACGGCTCCATTTCTTTGATTCCTTTCACTG +CAAAGCTTCTGGAAGAACAACTGTCTCACCGCTCACCTGCCCATTCTCTTCGGACACTCCTCAGCCCTGCATTACAAACC +CCTCACGAATGGCCCGTCTCGGCTTCTTTAATCTCATCTCTTAACAACCACTCCCTCTTCCCCAAAAGCTCTAGCTAGAC +TGGCTGCCCTTCTCTGCTAATCAACTGGTGGTTCCTTGGCTAGCCAGGAACATGGGGGTAGGCTCCTTCCCGTGCAGACT +TTAAGTCATCCTATTTTAATTCACATCACCTCATTTGCATTCTCATAGCACTTACATTGTCTGATACTTTTCCTTGTTTA +TTTTATCTGTTTCCTCTAATAGCATATACACTTCCTAAGGGCAGGGCAGTGATCTATCTTGTTGTCTTGCTGACCAAAGT +ATTAGATCACAATGCCTTGCACCTGCTTGGGCTCAATAAATGTGAATAACACACAAGCCTGTTATATGAGAGGTTAAGAG +AGCGAGAAAGAGCAAGGGGCAGCCCCTGTGTGGACCAGCATCTTGCACGAAGTTATGCAACTATCATCGCACCTTCTCCC +AGACAAGCTTTCAAAGGCTTTGCCATGTTTTCTTTTGTTTTGTTTTTTTGTTTGTTTTTTGAGATGGAGTTTGGCTCTTT +TCGCCCAGGCTGGAGTGCAGTGGTGCAGTCTAGGCTCACTGCAACCTCTGCCTTCTGGTTTCAAGCAATTCTCCTGCCTC +AGCCTCCCGAGTAGCTGGGATTACAGGCCCCTGCCACCATGCCTGGCTGATTTTTTGTATTTTTAGTAGAGACAGGGTTT +CACCATGTTGGCCAGGCTGGTGTGGAACTCCTGACCTTGTGATCCACCTGCCTCGGCCTCTCAAAGCGCTGGGATTACAG +GTGTGAGCCACTGTGCCTGGCCCGCCATGTTCTTTCTTTCTTTCTTTTTTCTTTTGAGGCAGGGTCTTGCTTTGTTGCCC +AAGCTAGGGTACAGTGGTGCAATCATGGCTCACTACAGCCTCGGACTCCTGGGCTCAGTGATTCTCCTGCCTCAGCCTCC +CAAGTAGCTGGGACCACAGAGGCCTGCCTGGCTAATTTTTTAGTCTTTTTCTTTTTCTTTTTTTTTTTGGAGACGGAGTC +TCGCCCTGTCACCCAGGCTAGAGTGCAGTGGTGTGATCTCGACTCACTGTAACCTCCACCTCCCAGATTCAAGCGATCCT +CTTGCCTCAACTTCCTGAGTAGCTGGGATTACAGGCGCCCACCAATGCGCCTGATTAATTTTTTGTATTTTTAGTAGAGA +TGGGGTTTTGCCATGTTGGCCAGGCTGGTTTCGAACTCCTGACCTCAGGTGATCCTCCCGCCTCCGACTCCCAAAGTGCT +GGGATTACAGGTGTGAGCCACTGCACCCGGCCAATTTTTGAGTTTTTTTGTAGAGGCAGGGTTTCACTATGTTGCCCAGG +CAGGATGCTCTCTTTCTTTTTCTTTTTTTTTTTTTCAGGGATGCTCTCTTTCTTTATGCCAAATTTGTCATCAGATTTGC +TAAGAAACATGCCTACTGTAAGTGTTTGTTACACTTTTCTGTTTTTTTTTTTTTTTGAGACAGAGTTTTGCTCTCGTCCA +GGCTGGAGTGCAATGGTGCGATCTCGGCTCACCGCAACCTCTGCCTCCCAGGTTCAAGCGATTCTCCTGCCTCAGCCTCC +CGAGTAGCTGGGATTACAGGCATGCGCCACTGCGCCCGGCTAATTTTGTATTTTTAGTAGAGACGGGGTTTCTCCATATT +GGTCAGGCTGGTCTTGAACTCCCAACTTCAGGTGATCCGCCCGCCTTGGCCTCCCAAAGTGCTGGGATTACAGGCATGAA +CCACTGTGCCCAGCCCACTTTTCTGTTGTTTGCACTGACAAAACATCCCCTACCAAACAGCTCCTTTAATGGCAGGCTCT +TTTCTTTTTTTATTTTATTTTATTTTATTTTATTTTATTTTATTTTATTTTGAGACGGAGTCTCAGCTCTTATTGCCCAG +GCTGGAGTACAGTGGCACTATCTCCGCTTACTGCAACCTCCTCCCGGATTCAAATGATTCTCCTGCCTCAGCCTGCTGAG +TAGCTAGGACTACAAGCATGTGCCACCACACCTGGCTAATTTTGTACTTCTAGTAGAGACGGGGTTTCACCATGTTGGTC +AGGCTGGTCTTGAACTCCCGACCTCAGGTGATCCACCCCCCTTGGCCTCCTAAAGTGCTGGGATTACAGGCGTGAGCCAC +CAAGCCTGGCCTACCTAGTACTCTGTGTATTATGGGAAATGTAGAGTTGAGGAAAGTGCTGGGCACACAGTAAGAGCTCA +ACAAAGGTTAGCTCTTTCTGCAATTGTTCTATTTCACTTGTTCTATATTATTATTCTAGAGAGAACTGTGTGATTGTTAG +TGCGGATCTGTGGTACTGCTCCCACCCCCACTCCATTAATGCAAGTACACCTCCTTCAGGGATCTATTCAGTCAACAGGC +CAGGAGGTGCTGTCCTGAAATGGGGGGCCCAAAGTCTCAATCCCACTTGGAGGGACACAGGTCTACAGACAGGTCTCCCT +GTCTTTATCTCTCAAATCTTCAGTAGCAACTAAAATCTCCGTGTTTTTCAGAGCAGGACCTTCCCAGGGGTACCAGCATC +AGTGGGCCAGGATACAAATGTGCCAGGCTGAACTAGGCCTTCCAAATGGCCAGGGAGCCAAGAGAAATGCAGGTGCCCTT +GGCTGGGTGGGAAGGCAATGAGATCAACTGAGACCCCAAACAGGGGCAGGCCTGACCAGAATCTTAACAGTGGCTGCTGG +TATCAGTCTTGAAGGCCTATATGTCCAGTGATTCCCTAAACAATATAGTACAAGTACTTACATACATACTTGCATGTACA +TTAGCATTTTCATTGTATGGGGGTAATGTAAGTAATCTAGAGATAATTTAAACTATATGGGAGGATATGTGTAGGTTAAA +TCCAAATACTATACCGTCTTATATATGGGACTTAGACATCTGTGGGTTTGGTGTGTGAGGAGTCCCAGAACCAAGCCCCT +ACAGATAGAGGGATAACTATATTGCCCTGTAACCTGCAACCCTGCTATATTTATTTATTAGTTTTGGTAGCTTTTTATGG +ATTTTCCATTAGGACTTTTTTTTTTTTTTGAGATAGAGTTTCACTCTTTTTTTTTTTTTTTGAGACGGAATCTCGCTCTG +TTGCCCGGCGTGGTGTGCAATGGCATGATCTCAGCTCACTGCAACCTCCACCTCCTGGGTTCAAGCAATTCTTCTGTCTC +AGCCTCCCAAGTAGCTGGGATTACAGGCGCCCACCACTACACCCAGCTAATTCTTGTATTTTTAGTAGAGACGGGGTTTC +ACCATGTTAGGTTGGTCTCAAACTCCTGACCTCAGGTGATCGGCCTGCCTCAGCCTCCCGAAGTGCTGGGATTACAGGCG +TAAGCCACCACACCCTGCCGGAGTTTCACTCTTGTTGCCCAGGCTGGAGTGCAATAGCGCGATCTCGGCTCACTGCAACC +TCTGCCTCCCAGGTCCAAGCAATTCTCCTGCCTCAGCCTCCTGAGTAGCTGGGATTAGAGGTGCCCGTCACCACGCCTGG +CTGATTTTTTGTATTTTTATTAGAGTTGGGGTTTCACCATGTTGGCCAGGCTGGTCCCAGGGAAGCCACCTGCCTCAGCC +TCCTAAAAGTGCTAGGATTACAGGCATGAGCCACCACGCCTGGCCCCATTAGGACATGTATGTATAGAATCATACTGGCT +GTGAATGTGTTTTATTTCTTCCTTTCTAATCGTTATTTTTTTTCTTTCCTTTTTTTTTTTTTTTTTTGACATGGAATTTT +GCTCTTGTCGCCTAGGCTGGAGTGCAATGGGACAATCTCGGCTCACTGCAACCTCTGCCTCCTGGGTTCAAGTGATTCTC +CTGCCTCTGCCTCCTGAGTAGCTGGGACTACAGGCGTTCACCACTACCCCTGGCTAATTCTTTTTTTTTTGAGACGGAGT +TTTTGCTTTTGTCACCCAGGCTGGAGTGCAATGGTGCAATCTCGGCTCACTACAACCTCCGCCTCCCAGGTTCAAGCGAT +TCTCCTGCCTAAGCCTCCCAAGTAGCTGGGATTACAGGCGCCCGCCACTACGCCCGGCTAATTTTTGTATTTTTAGTAGA +GATGGGGTTTCACCATGTTGGCCAGGCTGGTGTTGAACTCCTGACCTCAGGTGATCCACCCACCTCGGCCTCCCAAAGTG +CTGGGATTACAGGCATGAGCCACTGTACCCGGCCAACGCCTGGCTATTTTTTTAATATTTTAATAGAGACGAGGTTTCAC +CATCTTTGTCAGGCTGGTCTCCAACTCCAGACCTCAGGTGATCTGCCCACCTCGGCCTCCCAAAGTGCTGGGATTATAGG +CGTGAGACATCGGGCCACTAATCATTATTTCTTTTTCTTTTTTTTTTTTTGAGACACAGTCTTGCTCTGTCGCCCAGGCT +GGAGTGCAGTGGCTCGATCTCAGCTCACTGCAAGCTCCGCCCCCTGAGTTCACGCCATTCTCCTGCCTCAGCCTCCCGAG +TAGCTGGGACTACAGGCGCCCGCCACTACGCCCGGCTAATTTTTTGTATATTTAGTAGAGACAGGGTTTCACCGTGTTAG +CCAGGATGGTCTCGATCTCCTGACCTCGTGATCCACCCGTCTCGGCTTCCCAAAGTGCTGGGATTACAGGCCTGAGCCAC +CGCACCCGGCCCTCATTATTTCTTTTTCTTGCCTGGTTAAAACCTCCAGTATGGTATCAACGTTGTGAGAGTCAAATCCT +TTTCTAGTTCCTGATCTTAGAGGAAAAAGCGTTGAGTTTTCTTTTCTTTTTTTTTTTTTTTTTTTTTTGAGACGAAGTCT +CACTCTGTCACCCAGGCTGGAGTGCAGTGGCACGATCTAGGCTCTGCAAGCTCCGCCTCCCGGGTTCACGCCATTCTCTC +GCCTCAGCCTCCCGAGTAGCTGGGACTATAGGCGCCCGCCACCATGCCCGGCTAATTTTTTGTTTTTGTATTTTTAGTAG +AGACGGGGTTTCAGCATGCTAGTCAGGACAGTCTCGATCTCCTGACCTCGTGATCCGCCCGCCTAGGCCTCCCAAAGTGC +TGGCATTACAGGCGTGAGCCACCGCGCCCGGCAGCACTGAGTTTTCTACCATTATGTATGCTGCTAGTGGAACTCCGACT +GTGGACGCCCTGTTATCAAACTAGGTTAAGTTTCCTTTCCCTAGTTTGCTAGGAGGTTGGTTGGTTTGTAATCATGCATA +TGTGTTGAATATCATTAACTGCTTTTGCTACATCTGTTGAAATGATCATAGGGTTTTTATGTTTCCCTTTGTTAATGTGG +TGAATTACACAGACTGATTTTTTTCCCCCCAGTAAAGACCAGTCTGACTATGTTGCCCAGGCTGGTCTTGAAATCCTGGG +CTCAAGAGATCTTCCTGCCTCAGCCTCCTAAAATGTTGGGATTACAGGCCTGAGCTACTGCACCAGGCCAATTTTTGAAT +GTTGAATCAGCTACAATCATGAGATAAACATTATTTGGTTAGAATGTATTTATCCTTTTTCTTTTTCTTTTTTTGAGATG +GAGTCTCACTCTGTTGCCCAGGCTGGAGTGCAATGGTGTGATCCCAGCTCAGTGCAACTTCTGCCTCCTGGGTTCAAGCG +ATTCTCCTGCCTCAGACTCCCGAGTAGCTGGGATTTCAGGTGCCCACCACCATGCCCAGCTAATTTTTTTTTTTTTTTGA +GATGAAGTCTTGCTCTGTCGCCCAGGCTGGAGTGCAGTGGCACGATCTTGGCTCACTGCAACCTCTGCCTCCCGGGTTCA +AGCAATTCTGCCTCAGCCTCCTGAGTAGCTGGGATTACAGGCAGGTGCCACCACACCGGCTGATTTTTGTATTTTTAGTA +GAGATGGCGTTTCACCACATTGGTCAGGCTGGTCTTGAACTCCTGACCTCGTGATCTGCCCACCTCGGCCTCCCAAAGTG +CTGGGATTACAGGCGTGAGCCACCGTGCCCAGCCTGATTTTTGTATTTTTATTAGAAACGGGGTTTCACCATGTTGGTCA +GGCTGGTCTCAAACTCCTGACCTCAAGTGATCTGCCTGCCTCAGCCTCCCAAAGTGCTGGGATTACAGGCGTGAGCCACC +GCGACTGGCCTATTTATCCTTTTTTCTATATTACCAGGTTTGGTTTGCTAAAATTGGTTAGCTGTTGCATGTCTATGCTA +ACAGGAATATTGGTCTATATTTTCTTTTCTTATAATGTCCTTGTTTGGTTTTGGTACCAGGATTATGCTGGCTTCGAAAA +CAAGTTGGGAAATATTCCTCTATTTTTTCTTTCTTTCTTTTTTTTTCTGAGACAGGGTCTCACTCTGTTGCCCAGGCTGG +AGTGCAGTGGCGCAATCTCGGCTCACTGCAACCTCCGCTTCCCAGGTTCAAGGGATTCTTGTGCCTCAGCCTCCTGAGTA +ACTGGCATTACAGGTATGTGTCACCACGCCTAGCTACTTTTTGTATTTTTAGTAGAGATGGGGTTTCGCCGTGTTGGCCA +GGCTGGTCTCGAACTCCCGACCTCAAATGATCCCCTGCCTCAGCGTACCAAAGTGCTGAGATTACAGGTATGAGCCACCG +CGCCCAGTCTGTTCCTCTGTTTTCTGAAGAGTTTGTGTAAGATGGGTACTGTTTCTTCCTCCAACGTTTAAAGAGAGCAG +AGAACAGAGGAGATAAATAGAAAATAGCACTAAGAGGTCAGGCATGGTGGCTCACACCTGTAATCCCAGCACTTTGGGAG +GCCGAGATGGGATGAAAGCTTGAGGCCAGCAGTTTGAGACCAGCCTGGGCAACATAGTGAGATCCTGTCAATACAAAAAA +ATAAAATAGTTAGCTGGGTGTGGTGGAGTAATCCCAGCTACTCGTGAGGCTGCTAGAGGACTGCTTGAGCCCAGGGGTTC +GAGGCTGCAGTAAGCCTTGATTGTGCCATTGTACTCTAGCCTGGGCAACAGTGTGAGTCCCTGTCTCAAAAATTAACAAA +GAAAAAAAGAAAATAGCACTAAAATGGTAGCCCTATACTCCAACTGTAAAATAATTAAAATTAAAAGCATTCAGTAGAGA +AAGGAAGCCTATTTCAACAAGTGGAGACAGAATAACTGGATTTCCATACAGGAAAGATACCAGAGACTGACTCCTACACC +TCACACCATAAACAATTAATTTTAAGAATTAATTAATGGCTCAAGGACCTTACTGTAAAACTTACAACCATAAAGGTCCT +AAAAGAAAATGTAAGATAATATCTTCATGACCCTGGGGTTAAAAAAAAAAAAAAAGATGTCCTAAACAGGACAAGGCAAA +TACTGAACATAAAAAAGATAAATCCACTCCTCTTAAGATACTGTAAACTCTGTAAAGCAAACAAATAGGCAAGCAACAGA +TCAGAAGAAAACATTCACGACACATGGATCTGATAAAGGACTTGTATCCAGAATGTATAAAGCAGTCCCACAACTGAACA +ATAAAAACAAACAAAAAACCAAAATAACAGGTAAAAGACTCGAAGAGCTACTTTACAAACAAAATACGAATGGCCAATAG +GCACATGAAAAAATGCTGAACATCCTTAGTCAATAGAGAACTGTAAATTACAACCACAAGGATATACCACATTAGAAAGA +CTGACAATACCTAATGTCCGGAAGGCTGTGGCACAACCATAATAACTCCCATACCTTGCTAGTTGGAGTGTAAAATGGTA +CAACCGCTCTGGAAAACTCAGAGCTTCTGAAAAAGTTCAAAATACAGCTACTTTTTACTTCCAAACTCGCAATTCCCCTC +CTAAGTATTTCTCCAAGAAACACGAAAACATATGATCACAAAAAGAATTGTACAAGAATGTTTATAGCAGCTTTATTTCA +TAACCGCAAATGGGAAACAACTCAAAAGGCCATCAAAAGGACGGATATACAATCGATGGACTATACTAAATGAAAAGGAG +CAAAATACTGATATATACAACATGAACGAATGTCAGACAGTACATTGAAGGACAGAAGCCCGACAAAAATGAGCACATAA +TGTATGATTCCCCCCTTTTTTTTGAGACGGAGTTTCGTTCTTGTCGCCCAGGCTGGAGTGTAGTGGCACGATCTTGGCTC +ACTGCAACCTCTGCCTCCCGGGTTCAAGCGATTCTCCTGCCTCACCCTCCCGAATAGCTGGGATTACAGGCACCCACCAC +GCCCAGCTAATTTTTGTATTTTTTAGTAGAGACGGGGATTCACCACGTTGGCCACGCTGGTCTGGAACTCCTATCCTCAA +GTAATCCGCCCGCCTCGGCCTCCCAAAGTGCAGGCGTGAGCCACAGCGCCCAGCCTGATTCCATTCTATATGAAGTTCTC +CAACAGGCAAAATGGTTATGGAGATCAAAATAAAGGTGGGGTCGGGAATCGACTGGGAAGAGACGTGATGAAACGTTTCT +GGGACGATGAAAAGGGTCTGTGACTTGGTAGGCATCACGGAGCGGTTAGGGGCCAAAACTCATCTTCCTGTGCACTTGCT +GTGTGCACTGGCGCTGTGTGTAAATGCCACCTCGATTTAGGAAAAAGATGACGTAAGTACGGCACAAAGTGGCCGGTACG +CGGCAGGTGCATGGGAAGAAACTGCGGAATGAAACAACCGCGAGCTAAGAGATGGGGCAGCGGGAGAAATGAATTCGAGT +TCCGCCTCCTACCAGGAAGAACCGGCTCGGGCCGGAGGGCTGCACGGAGGACCACACGGACGCCTGCGGGCCCGCCCCTT +CCGCTTCACGACGTTCAGCCTGCGTCTGGAACTGGAATGGCCTAGCCCAAAGCTAGATAACAGGTAGATTGTTTTTCCGA +CAAATTATCAAACGACCCATCATTGCACTCTTTCAAAATTTGATTCTCAGACGTACCCATTCTTTTTTTTTTTCCTCCGG +GAAGATGAGATATACTCATTCTTGAAAATACCTCCGGGCTTGCCTTCTGCACACTTCTTTCCCTCCCTGTCTCACGCCAT +GGTAGCGTCCGCCTAGGTTGCAGGCGACCCGCGGGGTGGGGCACACCATTCAAAGAAGGGGAGGGATTGAGGTTTGCATC +AAAACAAATACCCCTGCCTTTGCAAAGGCCATAACTAAGTAATCCAGAAAAAGAAATGCAGGCGGAGAATAGCAGCCTCC +CTCTGCCAAGTAAGAGGAACCGGCCTAAAGGACATTTTCTCTCTCTCTCCTCCCCTCTCATCGGGTGAATAGTGAGCTGC +TCCGGCAAAAAGAAACCGGAAATGCTGCTGCAAGAGGCAGAAATGTAAATGTGGAGCCAAACAATAACAGGGCTGCCGGG +CCTCTCAGATTGCGACGGTCCTCCTCGGCCTGGCGGGCAAACCCCTGGTTTAGCACTTCTCACTTCCACGACTGACAGCC +TTCAATTGGATTTTCTCCATCTAGCGGAGCCGGGGGCTGCCTGGAAAGATCGCTCCAGGAAGGACAAAGGTCCGGAAGTT +GTGGGACCTTAGCAGCTTGGGCTCCCCGGATCACCCCCAAATGATCATTTCGGAATGGAGCCCCAGTTTTCACTAGGATG +CCATGGGCTCTAAAATATACAGCTATGAGTTCTCAATGTTTCGAGATCCAAAAGTCTCAGACCTCAATGCTTTGTGCATC +TTTTATTTCAGGGATTCCCTACGCCCAGCACCGGGTGGATGTGCAAAGAAGTACGCTTTAGGCCGGCTCAAGGTTCCCCA +AAGCTCCACTCCTCTGCCTAGGCGTTCAACTTTGAGTTCGGATGGTCCTAACATCCCCATCATCTACACCCAGGTCTCCC +AACAATGCAACTCCTATGATGATCCCTCTAGCCAAGCTTCCATCCCACTCACCCCCAAACTCGCTAAGTCCCCACTGCCC +CACCCCCAGCCCCAGCGATTTTCCCGAGCTGAAAATACACGGAGCCGAGAGCCCGTGACTCAGAGAGGACTCATCAAGTT +CAGTCAGGAGCTTACCCAATCCAGGGAAGCGTGTCACCGTCGTGGAAAGCACGCTCCCAGCCCGAACGCAAAGTGTCCCC +GGAGCCCAGCAGCTACCTGCTCCCTGGACGGTGGCTCTAGACTTTTGAGAAGCTCAAAACTTTTAGCGCCAGTCTTGAGC +ACATGGGAGGGGAAAACCCCAATCCCATC diff --git a/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/assets/demo/chromatin_active_promoter_chr19.fa b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/assets/demo/chromatin_active_promoter_chr19.fa new file mode 100644 index 0000000..2b97a66 --- /dev/null +++ b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/assets/demo/chromatin_active_promoter_chr19.fa @@ -0,0 +1,502 @@ +>human|chr19:35546347-35586347|GRCh38 +GGACCTATCCTCTATCTCCCCGATTCCTGTAATTTTGCCACCATCTGGTGGTTCCTCTTCCAGATGGGCCCCCAGAACCT +GTCCTGCCCTCTTCCTACCCACGAACTTCCCACAAATCCCACGTGGTTTATCTTGATCCCCTCACCTTGAAGTGACCTCT +TTCTGCTTTCTGTTCTCAGATGCTGTTCTTGGCCACTTTCTTTCCCACCTGGGAAGGCGGCATCTATGACTTCATTGGGG +TGAGAGGGGCCAGGGAAGGGAAGGGAGTTCAGGAATGGGGCTCCCTGTCCCCCTGTGCTTACTTAAGCCTCAACCTGACC +CGCAGGAGTTCATGAAGGCCAGCGTGGATGTGGCAGACCTGATAGGTCTAAACCTTGTCATGTCCCGGAATGCCGGCAAG +GGAGAGTACAAGATCATGGTTGCTGCCCTGGGCTGGGCCACTGCTGAGCTTATTATGTCCCGGTGCGTACAGCAGCCTGG +AGCCCAGACCCCTGAGAAGGGACACCTGGGTTCCACGGGGGTGCTGGAGGGCAGGGGCTCAAAGCCTGGTGCTGAAGGTG +TCTGAGTACTGGAGAATCCCATCCTTTGCCTTCCTCAGCTGCATTCCCCTATGGGTCGGAGCCCGGGGCATTGAGTTTGA +CTGGAAGTACATCCAGATGAGCATAGACTCCAACATCAGTCTGGTAGGCAGTCGTGCTCTCCCACATACACATTTCTGCT +GGCGGCCATACTCCTCCCCAAGGCCTGGCCCCGACTTTCTGCCTCCCTCTAGGTCCATTACATCGTCGCGTCTGCTCAGG +TCTGGATGATAACACGCTATGATCTGTACCACACCTTCCGGCCAGCTGTCCTCCTGCTGATGTTCCTCAGTGTCTACAAG +GCCTTTGTTATGGAGTGAGTTGGGTGGGGTTTAGGGCTGGGTCCAAAGTGGGGTGGGTTATCTAGTCTCCCTTCCTTATT +GTGACATTTTCCTGCAGGACCTTCGTCCACCTCTGCTCGCTGGGCAGTTGGGCAGCTCTACTGGCCCGAGCAGTGGTAAC +GGGGCTGCTGGCCCTCAGCACTTTGGCCCTGTATGTCGCCGTTGTCAATGTGCACTCCTAGGCTTGGTGTCTCAGACATT +GATGTACCTTTTCCCTGCCTCACTCCAGGTTTTAGTGAAGTAAACAGTATTTGGAAAGTTGTTGCTGCCTCCATTTCTCT +CTCTTGGGAACTGTCTCCCAATACCGTGTCCACCTGGGTCTCAGAGGCCCTGGTTCTGTCTCAGGAGCCAGGTAGACAAG +CTGGAAGCTAGCCAGTCACTGACTTGTCCCATGTCTTGTTCCTCAGGCTCCTGGTTTGCCAGGAGTAGACAGAAGGTTTG +GATGATCTTTGAGCAGTGGCAGAGGCCAGGGCCCTCAGGGAACAGATGATAGAGGGGAGCTAGAATCCAAGAGAAGGCCC +TTGGGGGGCTCTTCCTCCTCACAGCCCCAACCTGGGCCTCCTCACATGGGCCCTTCCCGGGCTGGTTGCCTCTGAGGCTC +CTGGCCCCAGTGTCCCCCTCCAATCCATCCTCTGTATGGCAGCCAGGGGATCTATCTGAAACCCGTCTAACCAGGTCATC +CTCCCACTTGCAGCCACTTGCGGCCCCTTGTTACACAGTGGACAGTCCAATTGCTTGGCCTTTGGTTTTACCCAACCAGC +AAAACCAGCTTTTCTGAAACTGCTCCCTAGAATAATTTGTCCAGCCAGATTCTTAACATCGTTCAAGCCATGGGGTCAGC +ATGGGGCTGGGGGGAGAGTTGTGACTGTGAGCTCCTCAGTTCTATGGCCCTCAGATCAGATCCCAGCCTGAGCCTTCTCA +GGGTGGAGTGGAGAGACAGCTGCAGCCAAGAAGCAGAGCAGTGGGGCCCCCAGCTTGGACATTGTCCTGGATGCCCCCCT +CCACCCTCAGCCTTCCTTGGTCCACTGAAGCCGGCTCCCCGCCTTTCTCTGAGGGGGATGTGTCAGAAGTTTTGAATGTC +ATGTTTAAGCTCCACTTTGATGTACACCCTCCCCACTCAGGAAGATGCTCCCCATCCTTGGTGTTCCCAGTGGGGTACCC +CAGGGAGTAGGATAGCACCATTGCCCCTCCTCCTCCACCAGGCCGTTGAAGTTCCACCTGATTTTTTTTAAGCTTAGGCC +TAGGAAAGCTCACTATGACCATCTCGATGTTTCCAGAGGGAGCATTTGCCTTCAGACGGCAGCTGCCATCTAGGCCACTC +TTCCTCATTGTTTGGAGTAAAGACAGGGTCCATGGGTCTCTTGGGAGCTGGAAGTGATTGATCACCTTGACTTTGATGTA +GAAAGGAAGTAGATGGGGCAGTCTATCTGGGTGGACTTGTGACAAGGTCACTTTCTCCCACACTTCCATGCCCCACATAG +CTCTTCACACATATTGAGACAAGTGTAGGATGCAAAATTACCAACTGGAATAATCCCAGCTTACATGGGGTTCAGGGAGA +GAGACTGGAGTGGCTGGGCCTGAGTTGGCAGAGGACGGTGAAGCCTGGGCGGTTGGACTGTGGGGAGCCAGGCTTCAGGT +GATCGGGGTTATGATGGGAAAACCCCGGGTTATGGGGACCCAGAGGAGCCGCCTGGCCTGCCGTGGAAGCAGTGCGGGCT +CCCCTGAGCAGAGGACATGTGAGCTGAGACCTAAAGAATGAGTGATTGGGGCAAGGCAGAGGTAATGGTCTTGAGGTCAG +AGAGGGGGCCTGTTTTTCCCAAGGGAACATAACTGGTTGGAATGAGTTCAAGGGGACTTGTAAGACTACAGAAGCTGGCA +GGGGTCAGATCACAGGAGGCTTTGGGAACCAAGGAGAGCTCTAGGCAGAGGAAGGACAGGGTCAGATTTGGCTTTAGGAA +GCTCTGCATGGCTGTTGTCTGAAATGGGAAAGCTGAGGCCACGAGGCCAGGGCTGGACCAGGGCAGGGCTTTGTGGGGTT +AGGAGAGTGACTAGAAGCCAAGCCCAGGTGTTGGTGATCAGTGGCTGTGTGTATTTATTTACCGGCAGGGGGCGCTAACA +GGTAAGGGAGGAGCAAAGAGTTCCAGGATAACTGGAGTTTGGGGCTTGTAAGCCCGAGGGCTCACCACGTTTCCAAGGGA +GAAGGGACCACACTGATGGGACAGTGGGACTATCAGATTGGGTCAGTGCATGGGTACAGGCATGGGGGGTGATGAAGAGG +CCAGTGTAGGTCACCAACACTGGGGGAAGCTGGGGACAGAGGTGGGTCTCACTGCAGAAACAACTGAAGCCATGGGGGAC +TGGGGGGCTGTGGTCACAAGGAAGGAGAAGCCCAGGCCCTAGCCAGAGAAAGCCACATAAGAAAGAGGAAGAGAGGTGAA +CAGAGGGGCTGGGAAAGGAGCGGCAGAGATGGGAGGTAGGGGGATGGGGTTAACCTTTCAGGTGAGGGGTGTGAGGAGGG +GACCTCCAGGAGTTGTTCAGGAACTGGCCATTGCCGGGACAGAGGAGTAAGCAGGTAGGCTCAGAAAGGAGACACCGCTG +GGGGGTGGGGGATGCTTGATGTGGAAATGACTTGGGCCCCCTTGTGTCTGGGAGCCTCCAGGAGGGAGAAGGGTGCTGAG +AACAGAGAAAGCCCAGAGGTGGTAGGGGGTGCCTATCGCCACGGCCCAGCTTGCGTTTAGATGCCATGACCCTGGGCAAA +GGAATAGGAATCATTTCCCCAAGGCAGGTTTATTGAGGACCTACTATGTGCCAGGCTGTGTACTAGGCACTGGAGGTGCA +GCCCTGGATAAACAGCCCCCATGTACAGGACTGTGGGAGATCACAAGCACTAATGAGAGACGCTGAGGACAGTGCCTGGC +ACCAGATACAGCACAGTCAGCATGAGCTGGTTTTATTACCATCGCCCAGGACACAAGCGTGTCTTTAACGAAGGGCCCTC +AGGCAGCCGTCCAGCCTGGACTGGAGTCCTAAGAACAGAAACACCCTCCAGAAGCGGTCAGCTGTACTCCCTGTCAGAGC +CCCACCGCCACCACAGGTGGCGGCTTTCCCGAGGCCAGCCCAGAGGACTGCCCAGGCGCTGCTGCTCCAGGAGGTGCGAA +CCTTGGGAATGGGGGAGGGGAGTGGGCAGGTCCCTCCAAGTTTGGGGTGCCGTGGGCTACAGAAGCAGATACTGGTGGGG +CTGGGACTCTTGGTTGCTCAGATATCTTGGTGGCTGTCCAGAGGGTCCCACGAGCCCTGCCCCCACCTGCTGTGGCAGTT +GCAGGGATGCTTGAAGGCAGTCGTCCCTCTAATAGTAGAGTTCCTGGTCCCACCAGCCTGGGAGAGAGAGGGAGAAAGGA +GACTCAGTGCTGGGGGTGCCACTTAGGCAGGGCCAGGGGCTCAGAGGTCAGTGCTGGTTGCTATGGAGGGTCAAGGGCTT +AGAGGCCAAGTGTTGAGGATCAAAGGTGGGTGCAGCTGCTCAAACGTTTCAGTCCCCTGCCCCCAGGCTCCCAGTCTCCA +CACTCACTCCCTGGGCAACAGCGAACTCCAAGCTTCCGGATCTCATCATAGACGAAGATGAGGATGCCGTAGGGCAGGGG +GACCAGCCACCACTGGAACCTGAAGGCACATGGCAAGGTGAAGGCCATCCCAGGCCTGTGCCCTACAGCCCCCTCCCTGT +CCTTGCCCCTCACAGCCTCTCACCGAATGGGCATGAAGTTGAAGATGTTGGGCATGCCGGGGCAGTAGCACAGGAAGCAG +CCGATGCAGACCTGGAACACGATGGCGATCACCAGGATCTTATTCCTGGGGGTGGGCAGAATGGGACAGGCCATTAGGAA +TTGGGGACGTGATGGAAATCAGGATACTGTGGGTCAAAGTAGGTCAGATGTCAGCAGCAGCAGGGAGGTGTTCTACACAC +TGAACACTGGAGTGGCCCGGGCCTCTCAGCACCACCCCTTTAGTGAAATGTGGAGGGGGCCGTGGGGGGAGTTATTGGCC +AGTTAGAAAGGTTTTTTTGGCATGTCACCATCTGGCACTGGCACCCGCTGGCATTTGCCGGCTGTTCTAAACCACAGTCA +GGATCTGATGGGAGTTGGGACCAGGGGTTGGAGGGCAGGAAGAGGGCCAGCCAGGGACACCTGAAGAAGCCTTGCTGGAA +GGCAGAGAGACGGCGCGTCTTGCGGATGAGGACATCGGCGATCTGGCACACCTCAATGCTGATGAAGAACACGGTGTAGC +AGGTGTACTGCTGGTACAGGCGCTGCCCGAATGTCTGCAGGCCAGGGGCAAACGGAAACAGCCTGAGTCCAGCCTGAGTC +CCGGCGGAGAGCCTCTGCAGCCCACCGGGCATAGGGTCCCAGGGCCGTGAACCCCTAAATGCTCTCTCTGCCTTGCATCA +GAGTGTGGGGGTGGGGGGAAGGAGAGAGGCAGGGTCTGCCAGGTGTGCAGGACCCCAGAACGTGGGGCCCAGAAACCTCA +GCCTAGCAGAGCTTGGATGACAGTGGCCGGGAAAACTGTAGGCTGGGCCGACTGCTTTCATAGTGAAAGGGGGGAGGCAC +CCAAAAGAAAGGGCCTGGGAAAGGGGAATTGAAATCCTCAGATGGGATTTGCAAAGTCCAGTGACTCCAGGGGCCAGGCA +GATGGCAAAAATAGGTGAGGCAGTCACCGCGGGCCTTTGGGAAAAGAGGGCAGATGCCCCATTTAAAGGGGGCAGCCACT +ATGCAGCCCATTTCTTCAACACAGCTACTTTTTTTTTTGAGATGGAATCTCGCTCTGTCACCCAGGCTGGAGTGCAGTGG +TGCAATCTCAGCTCACTGCAACTTCCGAATCCCAGGTTCAGGAGATTCTCCTGCCTTAGCCTCCCAAGTAGCTGGAATTA +CAAGCATGTGCCACCATGCCTGGCAACACAACTCCTAATACCAGCTAACTGTGGGGATCACTGACTGCTCCAGGTGCCGA +GTGTTGCACGTGCATTAGCTCATTTAATCCGCTGGGTAACCCTAGGAGGTAAATCAGTGGTGCTGGAACTTGAACATGCA +TGAGAATCCCAGCAAGGGCTCCCAGAGGGCTGGGATAGAGCCTGAGAATTTGCACTGCAAACATGTTCCCAGGAGATGGT +GATGCTGCTGGTCTGGGAGCAAACTGAAAACCACTGAGGTAGACTTTGATATTCTCTCCACTTTGCAGGTGAGAAAACAG +GCACAGAGAGGCAAAGCAACCTGCCCAGGGTTACACAGCTTGTGAGAAGCAGTACCAGACCATAAGTCCAGGCAGTCTGG +CTTCAGTGTCTACCTTAACCATGATAGTAAATGTTTAAGGGAAGTATCCTCGTCTGTATGTAACTCTACAGGTGAACTAA +ACACATCTGCTGGCCAGATCTAGCCTCACTGTGCTCCAAACCTTCATTAAAATAGACCACATGTGCAAATACCCTGTGGG +AGTGATGCTGTAGGGAAAATAGCAGAATAGGGGCCTGGTTCCACATCTGAGATGATGCATTAGTGTCAGGGCAGAGACAC +CCATCTGCAGTTGCGATCATCAGAAGCAGGAGTTTGGAGCTGGAGATGGTGGGAAAGGCCCTTGCCCCCCCGAACTGGCA +GGGAGTCTGGGGGTCCGGGATGCTCTGGCTGAACTCAGTCACACGTGGAGGAACAAGTGGGCCGCACACAAGGCAAGTTG +CCCTGGGAAGGAGGGAGCCCAGGGATGGGATGGGGCGGGGCAGGGCTCACCCACTCCTGGCCGTAGCTGTCCTGCAGATC +TTGTAGGTGGTGGTCCTCCCACTGCGCCCGCAGCCCCACGCACAGCAGTGGGAACCAGCCCTCCTGGGCCATTGCCGTGA +AGTAGTCAGTGAAGCCAGCAAAGGACTGAATGGCACCTGGAGAGAGACAAGGGGACACAGGGAGACAGAGATGGACACAG +AGACAGGGACACAGGAAGAGAGGGACATGGAGAGACAGGGACACAGATACACAAAGGTCAAGGACACACAGACAGGGACA +CGGGAAGAGAGACAGGGACACAGATACACAAGGTCAAGGACACACAGAGCCAGGGACACAGAAACAGTGACAGAGACACA +GAGGCAGAGAGAGAAGCAGGGACAGAGAGACAGAGGCAGGGACAGAGAGAAAAAGACACATCCAGGACCCAAAAAGACAG +AAATAGGCAACAACTCAGAGACAGGGTCATGGAGAGCAAGGTCAGAGAGAGAGCAGGGACACAGCAGAACCAGAGAGACT +GTCACAAACAGAGAGGGACACGGAGGACAACGGAGACCCAGGACCAAATGCAGACACTGAGGTCCAGAACCAGCCGGAGC +CCAAGGCAGGGCCTGGGGCAGGCGAGCAGCCCAGCGCAGAGCCCCCCACCTCCAGGCTCCCCGGCCCACGGGCACCCACC +AATCTGGAAGTAGGAGTAGGCAGCCAGGGGCTCGTTGACCAATCTGTCACGCTTTGGGTTGCGTGGACGCAGGTGCATGA +TGTCACTCTCGGCCTTTTCATATGCCAGGGACACAGATGGGAACTGGCCAGGAGTGGAAGGAACTGGGACTGAGGGTTTG +GCTGGGCCCTTGTCCCCTCCACTTCGGGTCCCCCTTCCCCCACTTGTGAGCAGGAACAGGACTGAGGTTAGCAGGCAGGA +CCTGCAGGGACGGCACAGCCACACCAGCCTGGACAGCCTGGGCCCCACTGGCCCCTGGGTGTTGGCATCCTGGTGCCCAT +CTTCGTAAGAACATCTGCTTTTATCTTCTCCCTCTGTCTCCCTCCCTCTCTTTCTCTGTTCCTCACTCTGTTTCTCTGTC +CCTGTCTCCAGTGTAATTCTCTGTGACTGTATCTGCTCTTGTGTATTTCTGTGTCACTCCCTATCTCTGTGTATCTTCAG +TCTCTCAGTGTCGCCGTGCCCCTTCATCTGCTTGTGCCTAGAGTGGCAGCAGAGCATACTGACCACGACCACAGGCTGTG +GGACCTGTGACTGCTCAAGCTCAAGTCACAGCCGGGCCACTTATTTGCCCAGTGACCTGGGACAATCCACTCTACCTGTC +TATGCCTTGATGAGCTCATCAGTAAAGTACGGGTGATAGTAACAAAACTCATCTCATAGTGTGGTTATGAGTACAAAATT +AAGGAAATGATGGTGTGTCTGGCCCAGAGAAAGCACTGTGTAAATGTTACCGAATAAATACCTGTCAACACAGCTGCGCC +TCTGGCTGCATTCGTGACTGTATCTGTGGTTCTTTTCTGTCTGTCTGCCATGGGGTCTATCTGAGGGGGTTTATTTGTCC +GTGTCTGTTTAGGGGCACGTGTCTGTGATTCAAGTTTGGCCAGATACCTGTTCTGTTTCCATCTCTGTCTGAGTATCTGT +ATCTGAGTCATGTGTGTGCATGGGTCGGCCCGGGCCTACCTGAGTGTCCGCCTGTGTGTGCATGGCTGTATGTCCATTTA +GGTCTGTTCACCTGCCTGCCACCCATGTGTCCTGCACTGGCTGTCATTGCACACACAGGTCTTGTCTGTCACTCTGTGCC +CAAGAGTGTCTGTGGTGGTCTCTGTCCCTCCTGTCCATCTGCAAGCAAGTGTCTCTGGGCACCCTGTGGATGGGTACCCT +GGGCTGTGGACTTACAATGTCAGTGCAGAGTTCGATGAAGAGGATGGTGATGCACCCGAGGGGCAGGGGCACGCTGACGG +TGATGTAGATGAGGTAGGGTGTCAGCTCTGGGATGTTCTTGGTCAATGTGTAGGCAATAGACTTCTTCAGGTTGTCGAAG +ATCAGTCGACCTGTGGGGTAGGGTGGGCACCTCAGCCTCCTCACAGCCCTCTCCCTCCTGTGCCCACACTGCCTGCCCTC +CCCCTGGCGTGGCTCGGACCCTGCTCCACGCCTGTCACAATGGAGGCAAAGTTGTCATCCAGCAGGATCATGTCAGCTGC +ATTTTTGGCAGCATCTGAGCCAGCGATGCCCATGGCTACTCCGATGTCTGCCTTCTTCAGAGCTGGGGAGTCATTCACAC +CATCCCCCGTGACGGCCACAATCGCACCCTGCAGGCAGTGGGTGCAGGTGGTGGGTGGGTGGTCAGTGAGAGGCCGGTCC +AAGACCAGCCCCGCCTGTCTGCCCGCCTGCCCACCCTCATCAGCAGGGCTCACCAGCCGCTGGCAGCTCTCCACGATCAC +CAGCTTCTGCTGGGGGCTGGTGCGCGCAAACACCATCTCGGGGTGGGTGCGCAGGGCCTCGACCAGTTCCGATGGGTCCA +TGTCCTTCAGCTGCATGCCATTGATCACACAGGCACGGGCATCCCTGGGGAGGAGATGGGAGGACCTCGCTGGGACCTCG +GTCTGTGCCAGATGTGGGGAGAACCCCGGGGAGGTCTGGGGGGGCTTACTTGCGATTAACCTGGTCTACGGGCACACGGA +GGCGGGCAGCGATGTCCTCCACTGTCTCGCTGCCTTCCGAGATGATGCCCACACTGGCTGCAATGGCCTTGGCGGTGATG +GGGTGGTCACCCGTTACCATGATCACCTGTAGGGGGAACCAGTGGATCACTGACCCCTTCAGATCAGCCCAATCTCCCTG +TCCTCCCTGGGAGACATCTGCTGATACACGTGTTCATTTACTTGACCAAGCGTGACCACCTCCTACGTGCCTGGGATATA +GCAGAGACAAAAGAGACAAAAATCCCTGTCCTTGAGGAGCTCTAGTGAGGGTGACAGAAAATAAAGAAACGAATTATATA +GTGTGTTAGAAAGTGGAAAAAAACATATTAAGCTTGGTGAAGGAAACGGTGACTCGATTTTAAGTGTACCCAGGGAAGGG +CTTATTGAGAAGGTGATATCTGAGCAGAGACCTGTAAGAGGTGAGAGAGTCAGCCACGTGGAAAGCTGGGAGAAGAGTGC +TCCTGGCAGGGAGAACAGCAGGTGCAAAGGCCCTGAGGTGAGGACAGGCCCGAACTGTCGGAGGAACAGCAGGGAGGACA +CCATAACTGCAGAGGAGTGAACAAGGAAAGAGGGCTAACGAACAAGCTTAGAAAGGTAACAAGGCCCGATGTAACAGGGC +CGTAGAGACACGGGAAGGGCTTGGGCATTGCCTGCCTGGCCTTGGCTTTCCCACCCTTGTTTTGTGTTTGGGGAACCACT +CCTCTCCCCTACACCATCAGTTGCAGTGAGGTCCCTGGCTAAGATTGGGTAAGTAAGCCCAGACCTGGCCAATCAAGGTC +TTCCATCCCCCAGCCCCAGTGACTGGCTCAGGGATAAATGAGTGACCCAATCCAGTGCTACTGCAAAAGAGGCACTCTCT +TTTTGTGGGGTGGCTAAGCAGGTGGGAGTTAAGTTTGAGCTGCTGGAGCAGGGGTGTGTGTGTCCCCAAGTGAAGGGAGT +GCCTGCAAATAAAGCCAGCAAAGAGAACAGCAGAGCTGAGACATGGAGAAAGGAAAATTCCTAATGACACTGAGCTCCTG +GATCTGGCTAGACCTGAAGCTTAATTCCCCAGAATTTTTCAGTATCATAAACCAATGCATTTTGGTTCAAGCCAGTTTCA +GGTGGGTTTGTCATGGGGTTCTTCAACCCAAATCCTGCTGCCATCCTCTGTCCTCACTCCACTTGTTCCTCCCCACAGTG +GCATACCCGGATGCCTGCGGTGCGACACTTGAGCACAGCATCAGGGACGGTGGCCCGGGGTGGGTCAATCATGGATACAA +GTCCCGCAAAGCAGAGGCCGCTAGATGGAAAGTTCATGGCCTCTACGTCGAAGGCATAGCCAGGCGGGTAGTCCTTCTCA +TTCAGGTAGAGCTGGCAGAAGCCTGACCGGAAACGGGGAAGTCAGGGAAGAGCCCTGGGCACACCCTTTCTTAGCAGGGC +CAGGAAATGGGTAAAATAACCAGGCCCCTTGCACCAAACACCTATGGATGCCTGACCTTGTGCTGACCCCTTCACTCACA +TTATCTGAATCTACCCTCACAACCACCCTGTGAGGCCCATTCTCATCTTACAGATGAGGAAACTGAGGCTCAGAGAGGGG +ACATTTCTTGCCAGAGGTCACACAGCAAGTAAAAGGCCCAGAATTGGACCACAGATCTGCTTTCTAGGGTAGAGGCAGCG +AAGTTTAAGGCGTCAGGACAAAAATTGGGAGAGGTTAGAGGTCAGGATACGGATAAAAGTCCAGGTGAGGACTGGGGTCA +AGGTAGAAAGTGAGGACAGACAGGGGTCAGGACTGGTACAGGGAAAGTCAAGGGTGAGGCTGTGGACTGCGACAAATCAG +CCAGCAGCCAGGGATGAGGACGGTCAGGGCTGGGCCGGGAGTGGTGGGCAGGGTCTGTGCTAGCTCCTCCTCGCACCTGG +AGTCTCCTCCCCTGCCCAGGGGTCTCACCGAGCACGCGTTCGCCCAGGCCTCCCAGGCTGAGGTAGGCGGTCTGGAAGGC +CTCGCGCCACTGCTCGTCCAGCGGCAGCTCCTGGCCCTTGATAAGGATGGAGCTGCAGCGCTCCAGCACGCGCTCGGGGG +CGCCCTTCATCACCAGCAAGTGTCGCGGGTCCCGCGGGTCCTCCAGCGTATGGATGGACAGCTGTGGGCGGGGGGGAGAG +GCGAGGCTGTGGACGGGGGAACGGGGCGGGGCTGTGGACGAGGGAACGGGGCGGGGCTGAGGAGAGGGGCGGGGCCGAGA +GCTCGGTGCGGGCTCTGAGAGCTGCGGGGAAGGGTGAAGGTGGAAGATGGAGGCCTTGTGTGGAGGGGTCCTTGGTAGAA +GGTAAGCGTTAAGGCGGGGCTAGGTGCAGATTTGGAGTCCTGGACGCAGGGAATGAACAGAATTAGGGTGCGGAGTTGGG +CTGGGGGCGGATTTGGAGAGCGAGGTGCTCCCCATGGACAGTCCCGCCGAGGAGAAGCTGTGGGCGGGGCTGGGTGGTGG +GCGGGGCCTTGCCTCTGGTGGACGGGGCCATAGGCGGAGCGGGAGATGGGGTGGGGTTTGGCTGCGGAGAGAAGGGGCAA +GGAGCGAAGCCCCTCGTGGCCCGCTGATGTGGGTGTGGCCTGGGGCGGGGCCCGAGGTGGGCGGGCCCAGGCCGTGGGCG +GGGCCGGCTGCGCACCTGGAACTTGTTGGTGGAGTTGAAGGGTATCTCGCAGACTTTTGGGAAGCGGTCCCGGTAGCCCA +TGGCGTTGCCCAGCGTCAGCTCCGAGAACTTGAGCAGCGCCGTCTCCGATGCGTCTCCAATCACGATGCGCTGGGAGCGG +GGACCGGTGTCAGGGGCGAAGCCGGCTACACCAGCCTCCCGGGATTCCCTGGAGGCCCCCTGGCTCTCACCTTGGGCACA +GGCACTGCATCCTGGCCGGACTTGAAGGCGGCGCGGTTGCACAGGGTGAGCACCCGGCACAGCGCCCGCCACGTCTCCGA +GGACTGGTCAAACGTCTGCCCTGCAGACCAGGCGTCCAGGCTGGGTCCCGCACGGCGGCTCTCCCGGACCAGAACCGAGC +CCCCTCCTCCTAGGCTCATATCGCGGGCCCCCTCCCCAGACCTGGGTGGAATTGGGTTCCACCCAACCCTGAGGGACCCA +GCCCCCGGATGACCCTTCCCTCTAGACCCGGTAGCGAGTCTCCTTTGAGACCTGGAGCCGAGCCGCCCCGCCTTCGTACA +AAGCCCTCCCTACCTCCCTATCCCTCTTCAGGTCTCCACCATCCACCAGATCCTGCCCTGGCGCCTGTGCCCTCCCTCCC +CCACACCTGACTGGTCTTCCGTGGTGTCAGCTGTGTGGATGTGGTTGTCAAACCACAGATGGGACACAGTCATGCGGTTC +TGAGTGAGAGTCCCTGTCTTGTCCGAGCAGATCACCGAAGTGGAGCCCAATGTCTCCACCGCCTCCAGGTTCTTGACCAC +GCAGTTCTTACTGGCCAGGCGCTTGGCTGTCAGGGACAGGCAGACCTGGGGAAGGGGTGAGCACCGCAGGCTGGGGACCC +ACCCTGGCTTCCAGTCCTCTTCCCCGCGTCAAAGAACGGGGAAGGCTTTACCCCAGCCGCGGGGCTGCGTGTGCAACGTG +CTTCTGCAAACACCAGGTGTTTTCTTGGCCCCAGCTTTTGTTCAGCCAGTGCTTTGTTTGGGACTCCTTCCCCAGTCTGC +CCAGATACAGTAGCGAGGCCCCTCTCTGAGCTGTCCCAGCCGAGGTTCTGAAAATTCTCTTCACACAATCAAAATGAGAA +GAGGAACCTTCCCCGCACCTCCCTGGGGACTGGTCTGGAGCTGGGTGCTGACTGTGGAAGGTCTGGTGACATGCTGGCTG +CCTGCACAGGACATCAGCCTCTTCCAGTTAAGGACCCGGCCTCGTCATCTCTGCGCCCTCAATGTGTGGCACAGGACTTG +CCCCAGGGTTGCCTCGGGAAGGACTTGCTGAATGAGTGGATGATGGGAAGGCAGGAGAATGGATGGGAGCTAAGTGGACA +GATAGACAGGCAGGGAGGTGATGGGGGAAATGTGGAGGAAAGAACAGATGGTTGAGCAGGCCCCTCAGCTCCCTGCATCC +CCGCCTGCCCCCACTCACTGTGACAGTGGCCAGCAGCCCCTCAGGCACATAGGCCACCACGATGGCCATGAAGAAGACCA +TGGCCCGCAGGAAGGTGTAGCCAATGCACATGGCCACAATAAAAAATGTGGCACCGAAGAGAATGGCCAGGCCCGCGATG +ATGTCCACAAAATGCTCGATCTCGATAGCGATGGGTGTCTTCTCGTTTTCCACCCCCGACGCCAGCGATGCGATGCGCCC +AATGATGGTGCGGTCGCCCGTGTTCACCACCAGGCCCTGCACGGTGCCTGCAGGGGGGCCAAGGCGCGACTCAGGGATAG +GGGGCGGCAGTGGGGTGTGCACTGCCGTGTGAGCTGAAGGACAGCCAGTCACTGCCAGTGGAGGATGTGACCTGGGAGAG +GGTAGTGACAGTGGGAGACAGAGAAGCAGTGTGCCCTGGGGAGGTGGCAGTCACGGGGAGGTGGCAGTCATGCAGGAGAG +AGACAGGGAGGCTGAAGCCCCCTGTCCTAGAAGATAGCAGGAGAGAGGCCAGTGGAGGCAGCAGTTACTGGGCAGGCAGG +CGGGGGCTTCACAGACCCTCAAGGCACATGGTGGAGAAGAAGGCGATGTTGCGGGTCTCCAGAGGGCTCTCGTGCGTGCA +CTCGGGTGAGCGGGTCTGTGGCTCAGACTCCCCTGTCAGCGAGGAGTTGTCCACCTTGCAGCCCTGGGCCGCCAGGATGC +GGATGTCGGCGGGCACTCTGTCCCCACCTTTCATCTCCACCAGGTCGCCCACCACCAGTTGGTCAGCGTTGATCTGGAAT +TTGTCTCCATCGCGGATGACAGTGGCTTGCTGCGGGGCAGGGGCACCAAAGTTGAGGTGGACGGGGGTGGGGGTGGGAGC +TGCTGCATGTGGGGAGGTAAAGGATGAGGAGAGCTGGGACCCATGGGGAGAGATGGAGGCCACAGATGAGGGACAGGGGC +CTGATGGAAGGAGGCTACAGGAGCAGTTTGGAGTCTCTGGGATCTGGAGTGGCTGGGTGCTGGGGAACCCACCTGTGGCA +CAAGGTTCTTAAAGCTGGCGATGATGTTGGTGCTCTTGAATTCCTGGTAGTAGCCAAAGCAGCCGGTGACGACAACCACA +GCAATGAGAGCGATTGCCAGGTACAGCTGGGGACAGGGAAGGGGTGGGGTTATTCAGAGGGGCCGGAAGCTGCCTGCCTG +AGGCCACCGACCTGCTCCCTGGTGCCCTGGTTTTCTTGGTTTTCCCCATGCTTTTCCCCACCTACTAGGAAGCTCCATGC +ACCATTCCCTGTAGCCTTTAGCCCCCTCCCATGTCCCTCATGTCCCACACTCGCCTGCTCTGTCTTCTCAGTGTCTTGTG +TTTCTGTGTATATTTCATATCCTCTGGCCCTGAATCCTCAATCTTCCATGTCCCCATATCTGTGACTTGTGCCCCAAATC +ACTGTGTGCCTTGGTCTGACGTGTTCCCTTCTCCTGTGTCCCCAAGCCTTGCATTCCCCATGTCCACTGTCCCATGTCTC +CTCCTGTCCCCATGGCCTGTATTCCCCGTGTCTCCAGTTTCACATCCTGGGTTTTCCATAATCTGCTGCCACCCTCTGTG +TGTCCCCTGGGCCCCCTTGTCCCTGCATCCTCACATCCCCGTCCCTGCGTGTGCTGCAGACCTTGTACGTCATGGTCTGT +GTCCTCTGTGGCCACGGCCCCCATGTTCTAGCTCCCCAGTCACTGTGTCCCGTGTCTCCCATGTCTGTGTATCTCCGAGT +TCCCATCCTGTGTCCCATGGCCATGACTTTTCGAGCCTCTGTCCCATGTCACATGTCCCTGAGTCCCCATGTCCCTTGCC +CTCATCTCTGCCATATGTCCAATGTCCCTTGCCCTGCACCTCCATTTCCTCTCTGTCTCTGTGCTTCTATGTCTCATGTT +TTGTGTCTCTCTATGTGCTGTGTCCTGAGGCTTGGGTCCCTGCCCTGGTGTCCTATGTCTGAGTCCCAGGGTCTCCTATT +CCCCGTGCCTCAGTCTGTCCTGTGTCTGCACTGCATGTCCTCTGCTTCCAAGTCCCATCTCTTTTTTTTTTTTTTTTTTT +TTTTTTTAAGATGCAGTCTCGCTCTGTCGCCCAGGCTGGAGTGCAGTGGCATGGTCTTGGCTCACTGCAACATCCACCTC +CTGGGTTCAAATGATTCTCCTGCCTCAGCCTCCCAAGTAACTGGGATTACAGGTGCCCGCCACCATGTCTGGCTAATTTT +TGTAATTTTAGTAGAGATGGGGTTTTGCCATGTTGGCCAGGCTGGTCTCGAACTCCTGACCTCAGGTGATCTGCCCGCCT +CGGCCTCCCAAAGTGCTGGGATTACAGGCATGGGATTACACGCCCGGCCTCCAAATCCCATCTCTTACGTATCACATTGC +CATACTCTTTATGCCCCCACCTATGTCCCCATACCCAAGATTCACCCCAGTGTCCCTGTGTCCTGTGTCCTTGGGTCTGC +CCCATCCATATCCCAATGTCCTTCACCCTCCATGACCCCTGTCTTGAGACTGCCTTTCGTGTTCGTCTATCCCCATCCTT +CTCTGCCCCTCTTGTCCCAAGTCCCAGGCTCAGTGTCTTCCATCCCCAGGTCCCCATGTCCTGAGCCTGTCCCCACAACA +TGGCTCACATTGTCGTCGGTGGTGAGGTCCCCCTCACTAGCCTGGATGGCAAAGGCGATGAGGCAGATGGCGGCGGCAAC +CCACATGAGGCACTGCAGGCCCCCGGCCAGCTGCCTCGCGAACTTGACGTACTCTGGGGTGCCCCGTGGTGGCCGCAGTG +CGTTGGGCCCATCCCGCAGCAGCAGCTCAGCAGCCAGGCTCGCAGAGAGGCCCTGGGACAGAGGGGCAGGGCGAGGCGGT +CCTGGGGGCTTTCCTCCACATGCACACCCCGTGGAAAGCCCCCTGCTTCAGTTTCCCTTCTCCAGCTTGGTCTGTCCTGC +TCCTTCTCATCTCTTTGTCTCTTGGTTCCTGCCCCCACCCCATCTCTCCCTCCCTCCTTCATCTCTCCCTTTGTCTCTCT +CTCCTCGCTCCCCTTTCTATGTGTCTCTCTCCTGCTGCTCTCTCCTTATTTGTCTCTGCCTTCTCTGTCTTGCTGTCTCT +TTTCAGCTACTCCTCTCTCCGTCTCTGTGTGTCACCCTTTCTCTCTAATGCTTTCTCCCTTCTCTCCCTCTCCCTCTCTC +TCCCTCTCTCCATCTCCCTCCCTCTCTCCTCCCCTCCCTCCATCCTTTCCTCTCTCCCTCCCTCCCTCTTTCCTTTTCTG +CCTCCCTCCCTCTCTCTATTTCTCCATATCTTCCTCTCTTCATCTCTCCCTCTCTCTCCCTCTCTCCATCTCCCTCCTTC +CATCTCCCTCTCCCTCCCTCTCTCCTCCTCCCCTTTCTGTACGCTCCCAGTCTCCAGCTCACCTTGGTGGCACTGGTCTG +GTATTTCTGTTCCAGCTCCGCCACTGACAGCTGGTGGTCGTTCTGTGTGGTGGGGTGGGGCAGGGTGCTTGCTCTGGGCT +CTCCTGGCCCCCTCCCCGCCCACTGCCTGGTTCCCAGCCTACCCTCCAGCTCCCGCCCCTCCCCAGTCAGAGCTCACAAT +CTCCATCTCCTTCTTCATGTTCTCCAGCTTCTCCTTCCTCTTGCCACCCCCGCCACCCGCCTTCTTCTTCTTGCTCATCT +TGGCAGCCATGTCCCCGCCAGGGCCAGGACCCAGCTCCACCGAGTAGAGCTCATAGTTCTCCTGGGAATGGACAGGATGG +AGGGAGGGAGAACTCAGATTCCACTGCAACCCCTGTCCCCACTGCACCCCGGACCCCTGGGCCCCACTCACGGCCTTCCC +CATGGTGCCCGGTGCCTGTGCTCCCACCCAACAGAGCCTGGGCCCGGACCAGTCCTGATATACCCAGGGAGGGCGGTGGG +GTGGAGCAGAACCGCCCGACCACAGCTGATTACAGGCTCCTTGGGGTGACCTCTAGGCCTGGGCACCTCAGGCATTCCCT +CCTGGGCTGGCCCCCCCCTCCCCTGCCCAGATCCCCCCATGTCAACCTGCCCTGAGATCAGGGAGCCCCTGGGAACCGCT +GCAGCAACAAGAAGAGCAAACATAAGGTGCTCATTATGCGCTGGGCATTGTGCTGACGGCTTCCATGTGCTAACTTATTT +AACCTTCCAATAACCCTTTGAGGGGACAAGACCCTTTACAGATGGGGAAACTGAGGCATGGAGGGGTTGAGTCATTCTCC +TATGGCTCCATCACAGGCTAGGACATAATGGAGTGAGAACCAGATCAGCTGGCTGCAGAGTGTCCTCTTTCAGTCTAAAT +AGCACGTGCCTGATCAACTGGAAGAGGAGGGTGCTCCCACACTCTTTTTTTTTTTCTTTTTTTTTTTGAGATGGAGTGTC +ACTCTGACACCCAGGTTGGAGTGCAGTGGTGCAATCTCAGCTCGCTGCAACCTCCGCCTCCCGGGTTCAAGTGATTCTCC +TGCCTCAGCCTCTTGAGTAGCTGGGACTACAGGCACATGCCACCACACCCAGCTAATTTTTGTACTTTTAGTAGAGACGG +GGTTTCACCAGGTTGGCCAGGCTGGTCTCGAACTCCTGACCTCAGGTGATCCACCCGCCTTGGCCTCCCAAAGTGCTGGG +ATTACAGGCGTGAGCCACCATGCCCTGCCATGCTTACAACACTCTCTATGGTGGCTGAATGTTTACTGAACTCTGACTAT +TCACTATGGGCTGGGTGTGCTCTGTGTGTCACTCAAATGGTTTCCTTAGCATCTAACAGCAGCCCTGTAAAGCAATATTC +TCTTCTTAGCCCCCATCAGGGACTTTGCAGAAGATTTCAACGTCTAAGAATTAGGGACAGGGTTCCAGCCCGGGCTTGAC +CCCAGGGCTTGACCCCAGAGCCTAGCTCTTCTATGGCACCACCAATGGAAAAACCCAAACTTCACTGCAGCAGCACCACA +CACATGCCAGGCTCTGCACGTGTGACCTCACGGGTCTTTGCAAAAAACCCTGTGCAGGCAGAACTATCATTCCTGTTGTG +CAGAGGGGGAAACTGAGGCTCACAGGGACTCACATATCCAGCTACAGACTTTACAGCTCTCAACCATTCCCCGTCCCTAC +CCTAGATGGGGAAACCAAGGTGCAACATGGGGAAAGGATGTGTCCAAGGGTCTTGGCTTGATCTTGGTGTTGGGTGACAG +ATGCCAGGAGGCCTGGGGACTCCCTCCTCATCCCTCAGGTAGGGACCTCCATGGGCAGCAGCAGCGAGGGGCTGGGCCAG +TGGAATGGTGGGGACCTTGGTATGTTATGGGGTGAGCGTGTCTCCAGGAGCAGGTGAGCGTGCGCCTGATAAGATAACTG +CCCTGGCAATGTCCTTCTCTACTGGGGCAGGTCACCTGGGAAACTCAAAGCTGTTGGGCAGCCAGAGCTCCTCCCCGCAC +CCACACGGCTGGTCATCCCCAGCCTGCTAGAAGAAACCTCCAAAGAAGAGGTGGCGTGGGGCCACATGCTGTCCTTGTAG +TGAATCTACTCGCTCACTGGGCTCCCATCAGCTCTGAGCCATGTATGTCAAACAGCTTGTGTCACCCCCTGCTTACAACC +CTCCAAAGCCTTCCCTTCCCACTCAGAATAAACTCTGCACTCCTTCCTGCAACATCTGGCCCTGGTCACCTGTCCTGCCT +CTGTTGCTGCTAGGTTTCCAGGGGCACTGACCTCTCTGCTGCACTTTGCACAGACCAAGGTCGTTCCTGCTTCAGGACCT +TGGTACTTACAGTTCCTTCTGCCTGGGCCCCCTTCCTTAGATGTCTGCACTGGCTTTCTCACCTCTTTCAGGTCTCAGCT +CAAATGTCACCTCTTCAGAGAGTCCATTCTGTAATACTGCACACTGTGTTCTGTTCCTTTACCCCACTGTATTTACACCT +CCTACTCCAACCTGACACTGTGTTTTCTTTTTATTTGTTTGGTGTTCATTAGAAGGAGGACTGGGCCGGACACTGTGACT +CACATCTGTAATCCCAGCACTCTGGAAGGCTGAGGTGGACGGATCATGAGGTCAGGAGTTCGAGACCAGCCTGGTCAACA +TGGTGAAACCCTGTCTCTACTAAAAATACAAAAATTAGCTGGATGTGGTGGTGTGTGCCTGTAATCCCAGCTACCAGGGA +GGCCGAGGCAGGAGAATCACTTGAACCTGGGAGGTGAAAGTTGCAGTGAGCCGAGATCATGCCACTGCACTCCAGCCTGG +GTGACAGAGTGAGACTCTGTCTCAAAAAAAAAAAAAAAAAAAAATAGAAAAATAGATAGATAAGAGATGTTAGACAGATA +GATGGATACATAAATGATAGATGATTGATAGATTTTTTTTTTTTTTCTTTTTGAGACAGAGTCTTTCTCTGTCGCCCAGG +CTGTAGTGCAGTGTCATGATCTCGGCTCACTGCAACCTCTGTCTCCTGGATTCAAGCAATTCTTGTGCCTCAGCCTCCCA +AGCAGCTGGGATTACAGGCACCCGCCACCATGCCCAGCTAATTTCTGTATTTTTAGTAGAGACGGGGTTTCACCATGTTG +GCCAGGATGGTCTAGAACTCCTGGTCTCAAGTGATCCACCCGCCTTGGCCTCCCAAAGTGCTGGGATTACAGGTGTGAGC +CACCATGCCTGGCCAGAATTTTTTAAATTTTTGGCTGAATCATTTGGAAGCAAGTTGGAGACAGGATCCCCTTTATCCAT +ACTTAGGCATGTGTCTTCTATGAATAAGGACATTTTCTTACATAAGCACAGTCTAATAATCAAATTCAGGAAATTAACAT +TAATAAAACACTAGTAAATAATCTAGAGCAAGCTTGTCCAACCCATGGCCTGCAGGCCATATGTGGCCCAGGACTGCTTT +GAATGCTGCCCAATACAAATTCGTAGACTTTCTTAAAACATTATGAGATTTGTTTGCGATTTTTTTTTTTTTTAGCTCAT +CAGCTATTGATAGTGTTAATGTATTTTATGTGTGACCCAGGACAATTCTTCTTCCAATGTGGCCCAGGGAAGCCAAAAGA +TTGGACACCCATGATTTGGAGGATTTATTTGAATTTCCCCAGTTTTCCCACTAATGTCCTTTTCCTAGTCCAGGATCCAG +TCCGGGACCACACCTCACATTTTGGCTTCCTCCATTGCGTAACAATTCCTCCATCTTTCCTTGTCTTTCATGACCTGGGC +GTTTTGGAAGAGTATGATTCAGTGATTTTGTAGACTTCGCTCAGTGTGGCTTTGCTGCTGGTTCTTCGTGATTAAGTTCG +GGTTGTGTGCTGGCAGGAACACAGAAGGGATGTGTGTCCCCCCCATCCCCTGCCCCAGAGCATCATCGTGTCAGGCCATG +CCCCATTTTGGAGTCTGTAACTTGTCTAGAGCAGGGTGGACATGGTGGGGAGGGGTCTCAAGAGCCTTAGGGAGTGATCA +GGAAACCATTCTATGCCCTCTTTAATTTTTACTCAAGACAGTGCCACAGCCAGGGCTGGCACCCAGCTGCCCTGGTAGTT +AAAGTAATTGAAAGAAATACAGCCACTGCCTGAGCCCCCAGCACTTCCCCTAAGACTCCAGACCTCCCACACCCAACCCC +TTAAAGTGGATGCCTGTGCCAGTCCCAAGGCCTTGTCCTTTTTTTTTTTTTTTTTTTTTGAGACGGAGTCTGGCTCTGTC +ACCTAGGCTGAAGTGCAGTGGCACGATCTTGGCTTACTGCAACCTCCACCTCCTAGGTTCAAGCGATTCTCCTGCCTCAG +CCTCCCAAGTAGCTGGGACTACAGGCACGTACCACCATGCCCAGCTAATTTTTGTATTTTTAGTAGAGATGGGGTTTTGC +CATGTTGGCCAGGCTGGTCTCCAACTTCTTACCTCAAGTAATCTGCCCACTTCAGCCTCCCAAAATGCTGGGATAACAGG +TGTGAGCCACCACGCCCAGCCACCCTTGTCCATTCTGACCATCCCCTGCTCAGCTAGGTTCATGGGTAAATACTTTTACC +TACTCCCCTAATTTTGCAGTCTTCAGATGGTATGCAAGTTGTGTGTTTTTGTGGGGAAGGTCTTTGCTTGGGTCATTTAT +TATTTTACCGTGTGTCTAAGCAATACTAACCCCTGTCTCTGGGAGGTGAACACTGAGGCCTTAGTCCACAGGCAGGATCT +GGATAAGTTGGGAAGTGTTCCAAGTCTTTGGCACCCTGGAATGCAGCCTGGGTGGACCGCACGATGCCATTTCAGGCGGA +TGGACAACCCCTAGCACAACTCAGGGGCAAAGAAACTTCTCACAAGAATGTTGCAAGTGTGACATGGTCCACAGTAGTGC +AAGTTTCTCTGAGCCTGGGGCACACAGAACGTTGGAAACTTTTTTGAGAACAATTAGTGGTCAGAATTGAAAAAAATTTT +CTTTTCTTGAGAAAGAGGGGCAGAGGACAGACTATTAAATGGACGGTGGTCCAGGAGCAGTGGCTCATACCTGTAATCCC +AGCACTAGGAGGCTGAGGCGGGCATATCACTGGAGGTCAGGAGTTCGAGACCAGCCTGGCCAGCATGGTGAAACCCTGCC +TCTACTAAAAATACAAAAATCTGGCCGGGCGCAGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCAAGGTGGGTG +GATCACGAGGTCAGGAGTTCAAGACCAGCCTGACCAACAGGGTAAAACCCCGTCTCTACTAAAAATACAAAAATCAGCTG +GGCATGGTGGCGCATGCCTGTAATCCCAGCTACTCAGGAGGCTGAGGCAGAAGAATCGCTTGAACCTGGGAGGTGGAGGT +TGCAGTGAGCCAAGATTGCGCCATTGCACTCCAGCCTGGGTGACAGAGTGAGACTGCATCTCAAAAAAAATAAAAAATAA +AAAATAAAAAAATTTGCCAAGTGCAGTGGCGGGTGCCTGTAGTCCCAGCAACTCGGGAGGCTGAGGCAGGAGAACCTCTT +GAATCTGGGAGGCGCAGGTTGCAGTGAGCCAAGATTGCACCACTGCACTCCAGCCTGGGCGACAGAACAAAACTCCGTCT +CAAAAAAAAAAAAAAATGTGGATGGTGAACATTACTTGGGTTTGAAGGGGAATTTGAACAATAATGTCCTAGAATTGTAG +CCATACACACTCTTAGGAACCCAGCGCCCTGTAAACCTTGATGGGATCAGACCCCTGGCTATTCCTCCTCCTTCCCATGT +TCTACTTTTCCCTTTCATGCTTTGGGATCCTGCCCGCCCAGGCCTTCTGCTTTTCCAAAGGATCATTTCAAAACCTCAGC +ACATGCAATTTCCTCTACCTGAAGTGTGCACCCTACCTCCTCCCCATGTTTAACCACTCCTCACCTTTCCTGTTAAGCAC +AGATGATCCTTCCTCCGGGAAGCCTTGGACTACCCACCAGGCTGAGTCAGGCACCTCCCTGGGCTTCCACAGACCCCTGG +GGTCCCCATCACAGCCCCCACCTCCCTGCCTGTGCTTCCCCCATCCCAGTCCTGACGACTCTGACCCTATAATTCCTCCA +TCACAGCCCTGACCACTCTGGGCTGTCATGCTTTGGTGACATTTCATCTCCCCCTCCAGATCCATGAGGGCAGGACCCAG +GATGTCTCAGTCACCACTGTGTGCCCTGCATTGCCTGGCCCAGGCTGGCCTTGGGGAGTGTTTGCCAAGTGACAGGAGTC +ATTGTGACTGTGACTTTGGGTACAGTGTTTGCATCTCCCTGATAAGATAATGGGCCCAGCAATGCCCTTCACCTCCTGAG +CAGGTCACCCCGCCCCCTCGACCCTGGGAGGATGAAAGCAAGGGGCTGGGCATACAGGGACCTTGCTTCTCCCGCCTCTC +CAGGCCAGGTGCAGGGGCAGGTGGTCAGAATTGAAAAAATTGTCTAGGAGTCAAGGAGTTGGCCCCCAGCCCAGCTAAGC +TGGCAGCCAGGCCCCTGCGTTCTGTCCCTCTCCCATCTTTGTGCAGATTGGAAAGGTCAGCCATCCTGGGCTGGGTGCAG +AGCCCTTCACTTCTTCAGGAAATTTCCAGGCTGTACTTGCTCTGGGATTTTAAAAAATCATATTCAGGGCCGGGCGCAGT +GGCTCACACCTGTAATCCCAGCACTTTGTGAGGCCGAGGTGGGCAGATCACCTGAGATCAGGAGTTTGAGACCAGCCTGG +CCAATATGGTGAAACCCCGTCTCTGCTAAAAATACAAAAATCAGCCAGGCATGGTAGCGGGCGCCTGTAATCCCAGATAC +TCGGGAGGCTGAGGCAGGAGAATCACTGGAACCTGGGAGGTGGAGGTGGCAGTGAGCCGAGATTGTGCCACTGCACTCCA +GCCTGGGCAACAGAATGAGACTCCAGCTCAAAAAAAAAATAAAAATAAAAAAAATAAGGGTACTGGTGGAGAGACTTCCT +CCATGGGATGGGGCAAGGGCAGAGCGCTGATGTCCAAATCCAAGCTCAAAGATTCCCAGGGGAGCAAGGAGGCCTTGTGG +TTCCTAGAACTGTGAAGGGTACAGGGCGATTGCAAGAGGGCTTCTGGCTCGTGGGTCTCCTGCTTCTGGGCTGTCTGCTG +TCACAAGAACCTGCAGGATCATTAACAAGCTCAGAACGATGGCCCTGATGCTGAAGCCCAAAGCTAGCCTGGAATTTCTC +TTTGTGCTGACCTCGTTTTCCAAACGGCTCACTATCCGTGCCTATGGAGATGAAGACAGGCCGATGGGGGTGTTTTTCTA +AGAAGGGGTGAAATTGCAGAAGGGCCACCTGGAGTGAAACTGAAGGCCATGATCAGGGTTGGGGGAGGGAGGATTCAGCA +GAGGAGAGGAGGAGGCAATGCAGGAAGACAGTGCGGGGATCCTGGGCACGCCCAGGTAGGAGGGCATGCTGGGAGACCTT +GGGAAGAAGTGGGGTTGGCAGACTGTGAACAAAGTGGGGACAAGTCCCAGGGCAGCAGGAAGGGTGGGATGGAGAAGGGG +TTGGGGCTGGTATCTAGAGAAGAGGGGAAGCTTCCGAGGCATCCATGGAAAGGGGTATAAGGTGGGTCAGGGGATGTTTC +ACCATGGGGGACAGAGGGTCACTCTCCACCCACCAAGCAGGATGGCACAATGGCAAGTATTCCTAGCAGGAAGAAACAAA +AGATGAAAAAGATGCAGAGAGGCAGGCAGTCATTCACAGGAACACTTAAAGGCATTTTGTAGATGGTCATCTCCATGACT +TTGACCTCTGGGCTCCGGAGGTCAGTCACACAGGCATGAACCCTGGTGGAGGGAGGGGCAGGGTCTGCAGAAGGACTGAG +AAGGATCAGCAGATGCGAAGGAATGGGGACCAACAGAATCAATCCCATCAACAGGAATGTGCTGGGAACACTCAACCCAC +AGTGAACTTGGCCTTTGTTGTGGGAAAAGAACCCTGCAATGCCCCCACTGACCTCTATGATACCCTTGCTAGCCCTCAAA +GTATCCTGTTGGCTGCTTTTTTTTTTTTTTTTTTTGAGATGGAGTCTCACTCTGTCACCCAGGCTGGAGTGCAGCGGTGT +GATCTCAGCTCACAACAACCTCCACCTCCTAGGTTGGGATCCTCCCACCTCAGCTTCTTGAGTAGCTGGGATTACAAGCA +TGCACCACCATGCCCAGCTAATTTTTGTATTTTTAATAGAGGCAGGGTTTCACCATGTTGGCTAGGCTGGTCTCAAACTC +CTGACCTCAAGTAAATCACCCACCTTGGTCTCCCAAAGTACTGGGATTACAGGCATGAGCCACCGTGCCCAACCCCTGTT +GTCTTCTTCTGTCTCCCTAAACCCATCCCTGTTCTCCAGGAAGCACTACCTCTTGGTTTGCCAGTGCTTCCAGTTAGGTC +TGGTCCTAGCAAGTTATTCTGAACTCCCCTTTCAGTCTAAAAAATGCTCAGTTTGAATTTTAAATCCAGTCACTCATTGC +TTTAATCCAGTCACTCAATGGGGATAAGTTCTGAGAAATCCAGTCACTCAATGGGGATAAGTTCTGAGAAATGCATTGTT +AGGTGATGTCATCATTGTTTGAACATCATAGCTTGTACTCACACAAACCTAAATGGAGAGCCTACTGCACACCTAGGCTA +TATGGGATAGCCTTTTGCTCCTAGGCTACAAACCTGCACAGCACCTTACTGTATTGAATACTGTAGGCAACTTATAACAC +AATGGTAAGTATTTGTGTATCTAAACATATCTGAACATAGAAAAGATACAGTAAAAGTACAGTATAAAAGATAAAAGTGG +TATACATCTGATATGATTTGCTGTGTCCCCACCCAAATCTCATCTTGAATTGTACCTCATCTGCCTGAATCAGTGCTGAA +CCACTGAGGCTTAAAACAGTGCCTGGCACTGTTACCCAATAAATGTTCATTGAATATATGAGAGTCCCATGGTATCTGGC +AACGTGGACACACAGGAGATGTGAACATGTGGGCACACATACGTGCATGCAACAGATGAATCAGAAAGTGGAAACCTCCT +CATAAACTTTACTGCTCTTAAAAATGCTGATGGATGAAGGGGTGGGTTACAGATTAATAGCATCTCAAGGCAGAAGAATT +TTTCTTAGTACAGAACAAAATGGAGTCTCCTGTGTCTACTTCTTTCTACACAGACACAGTAACAATCTGATCTCTCTTTC +TTTTCCCCATATTTCCCCCTTTTCTTTTCAACAAAACCGCCATCGTCATCATGGCTCTTTCTCGATGGTCGCTGTCTCTT +CGGAGCTGTTGGGTACCCCTGCAGACTAACAACAGACAGAACAGGCACACAAGGATTAATATGAAATTTATAATTATAGT +ACTTCCGATGGTCTTAACCCAAGTGACAGGGTTAAGATTTACGAGGCCATCAGCAACTCCTGCAATTGCCTCAGTTCCTG +GCACCAAATTTAAATGGGCTTTTGATGTTTTGGAAATTTGTTCTTTTAATTTGGAAATGTCTAAAGTGAGATTATCTTCT +CTTCCCTGTAGATGGCGTCTAACCATGTCCCAGTGATGCTCAGACTCATTATAAACTTGGGGTGTAATACAAAAATCTGA +CGTATTCCAGTCACACTGTAACTGGAAACGATGTTCTAAGCTCATGAGCCTGTCTCCCATCCAAATGACAGTTTGTCTAA +GATCATTAATTTGGTTTGCCAATTTTTGATCAATACTAGATTGTGAATTCCACAATCTTGTAGAACTTTTTTGCCAATCA +TTAACAAAGTTTACTGACTGAACAGAAGAGTGCAATGCAACTCCTGCCACAGCAGCCATAGCTGTGACTGCAATTAATCC +CATAATCACTGTAATTAAAGTAAAAATGAATCTTTTGGATCTATTTAAAACGCCTTTTAATACTTCAGTCAAAATATGAA +CGGATGGTGAGGCCTCCCACAGTCGGTCCATGGACACAAGGATCCACACGCCTTCCCTTGCTCTCACCAGCAGAATACGG +TGTTGCCAATTAAAAGTTGAATCAATGCAAGTAAACAATCTGCAGTTTTCACAGGTTATAGTTTGAGAGTCTGGTTTAAT +AACTATATTTCCTATAACTAGCATATAAAGGGACTTTACACAGCTTTGTAAAGGAACTGTTAGACTGGAATTTAGGTCGA +TAGTATAAAATGGCTTATGATCTCGTTTCTAAAGTTTGATTTCCAGACCAAATTCTAATGTAGTATGAGGCTACACTAAG +CCTCCACCATTCTGGATGTTCAGGACCAGAAACAGGACTTATTATTTTTGGTCATGGGGTAGAGATTCCTTTTTCTCCCC +ATTTCCAAGGGTAGAAAGACTGTAATTTTTTATGCTTATGTTCGTCTAAACTTTCTATGAAGTCGCTATCAACAGTTGGA +CTCACTTGTGCACTGGGACACGACTGAGTTTGTCCTGTGCAATTGTGGTAGAATTGACCTCGAGGTGCCCAATCTATAAT +AGTTCGGAATTCACTGTTTTGTAATATCACTGCACTATTGGCCACACATTCTTCCCAAACTAAAGCTTCTACATTTTTTG +ATCCTTTGGAAATTTCCTCGGGGCAAGGTTTCCCTTTAGGTCTAAATTTTAATGATCTTTGATAAGAAAAGTCTTGTAAA +TAATTTACCTGTGGCCTGAGCGACATCCCGCTTACCATGTGATAAGTGAATCTTCTGATGGGACTGACAGTAGGTACTTC +TACCAACCAATTTTGGACTGCAGGCATTAAACATCCTGGTGCTCTCCCCAGGCAAATAGGAGGATAATGATACCCAATGA +AAATATTTATCATCATCCCTTCTTCCTCAGGTTTGGCAGGGCAACGATCATCTGTTGGACCAGGTACCCATACACTATCA +TTAACATATACTTCCATAGGATTATCCATCCATGTGACTGCCTGAATTAAGGGCGGGAAAGGCACATAGGCCCAGTAATT +ATAATTAGCTGCAGCTGCTCCTGCAGGCATAGGGAGACTTACCACCATTGATACAATCATCAAAGCTGCAAGCAGCATAC +TCTCTGGAGTTTGTGTCACCTTTGTGTTCTCTAGATATTTTGTAGCTAACTGTGTCAGCTTCTTTAATTGTGCCCAAGTC +AGCAGCTCTGCCTTCTTGGTGGATGGCAACTTCATCTGTTCTTCTGACATCACCATTCTGTTCATCTTCTGAGTCGATGG +TTCTCGATGACGGTGCTCGATTGCGGTGTCTCCGTCTCCGTGGAGGTGCTTTTCTTTGCATCTCTGATGGGTTCATTGTA +GAACTTCAAATGTCTAGTGGGTATCCAAACAGGACGCTGATTTTCTCCTGGTGAAACGCAAGCAAAACCTCTCCCCCACG +TTACCACCTTCCATATTTCCCATGTCTTATTTTTATTATCTTTCCACCAAATCAGTTTTCCTTCATGTGGGCTGTTCTTT +TTACCAGTAAGATGTTCTGCAGAAGTAGTAGTCTGATTTCTATAAATGTTTAAAAAATTTAAAGTATAGAGTGCTAGACT +AAGTTGCATCTGAGGAGTGGTACACTCCTTACTGTCTCCCCCTTCTTTTTGTTTAACTAATTGAGTTTTGAGTGTTCTAT +TAGTTCTTTCAACTATGGCCTGTCCTTGGGAATTATAGGGAATTCCTGTTGTATGTGTAATTTTCTTGGAAAGCTTTACT +ACAGTATCCTGGTCCATTGTTAGTTTTGATTTTTTCTGGAACTCCCATTACAGCAAAACAAGATAATAAATGTTTTTTAA +CATAAGAAGTACTTTCTCCTGTCTGGCAAGTTGCCCATATGAAATGTGAATAAGTATCAACTGTTACATGAACATATGAT +AATCTTCCGAATGAAGGTACATGTGTGACATCCATTTGCCATAACACATTAGGACACAGACCTCTGGGATTAACTCCTGC +CTCTTGAGTGGGCAGGTGTAGGACTTGACACTGGGTGCAATGTGGTACAGTATCTTTTGCCTGTTTCCATGTGACATCAA +ATTTGTTTTTTAATCCTGCTGCATTTACATGAGTTAAAGCATGAAGTTCTTGTGCTTTTATGAATGCAGATGATACCAGT +AAGTCAGCTTGTTAATTTGCTTTAGTCAAAGGCCCTAGTAAATTAGTGTGTGCTCGAATATGAGTAACATAAAATGGGAA +ATTTCTTTTTCTTACAGTTTGTTGTAATAAATTGAATAGCTGGTTTAACTGATCATCCATGCTATATTTGATTAGAGCTG +TCTCAACATCCCTTGTAGCCTGTACTACATATGCAGAATCTGATACAATATTGATAGGTTGATCAAAATCTTGTAACACT +GTAATGACTGCAACCAACTCTGCTCTTTGAGTCAATTGATATTGAGTTTTGATTACTCGCTCATCAGACCCCAGAAAAGG +TGTTGGTTGATATAGACAGCAGGACGGTGGCCATGGAAGTCAGAATCCACTAAGGAGTGTGTAACAACTCACCTGCCGAA +TCAACTAGCCCTGAAAATGGATGGCACTGGAGCATCAGGCCCATACCCGGCCATCGCCAGCAGTCAATGGAATGTGAGTG +CCTTAGAGGTCTTGGGGCCGAAACGATCTCAACCTATTCTCAAACTTTAAATGGGCAAGAAGCCCAGCTAGCTCAGTCGG +TAGAGCATAAGACTCTTAATCTCAGGGTTGTGGATTCGTGCCCCATGCTGGGTGCCAGATGAAGAGGTGAGTTGCCCCTA +AAAAGGATACCCTATTCCTTCTCTTTCTTAATTTCTTTTAGTCTCAATTGGGACTTTAATGCCATTTTCATTTTTTCCTA +GTCCCTTTCCTGGTATATATCCCATCTCAGTCATGATTTTTTGATTTGCGGGGCTGTATAATGGAGCAGGCATAGTGATT +TCCGCACCCCGTTGTTGTAATAAATCTCGACCCCACAGATTAAGAGGAATCGAAGTAGTCATTGGCTGAACAGTACTTTC +TTGATTATCTGACCCTAAACAATGTAAAATCTTAGTACTTTGATACACTTCTGAGGCTGTGCCTACGCCGATAAGTCCTA +TAACAGCCTTTGTTTAGGCCAATTTTTTGGCCACTGATTGAAAGCAATGATAGAGACATCTGCTCCAGTGTCTACTAACC +CTTCAAACTGTTTTCCTTGAATAATGGCCTTACACACAGGTCTGTTTTCTGAGACCTGACTTGCCCAATATGCAGCCTTT +CCTGTCGGATCAGTGCTTCCAAACTCTCCTATTCTTTTTATTTTACTATTTCCAATCTTAATATAAGGCAGGAGTAATAA +CTGAGCAATCCTGTCTCCTGGACAGGAGGGGGGACTTCCTCCAACACAGGCAGTCACTGATGTCCTAGTTCAAATGCTCC +AGGGGGAACAGTCCATTACTCACTGAGGGAGGCAGGCTTTCCTATTTTCTGTTTCAAGCTTTTGATCACAATGGAGCAAA +TGATCACAATGCAGCCCACCAGGATGCTGGTGATGCTGAATCCAAAAAGTAGTCTGGAATTGATCTGTGCTTTTCTCTGA +TTCCCAAGGAAGTTGGCTTCCTGGGTCTGTGGGGGTAAAGGCAAGCTGGTGTGAATGTTTCCAAGATGGGGAGGGGGAAG +AAAATCCCTGGGGAGAGGTGAAGAGGGCAAGATCAGGATGATGGCATGATGGAGGTCAGGGCAGGGCTCAGTCTCCCAGA +GGAAAAAGGAAGAAACCAGGGAGCCAGGATGAGGGAGTAGATCTAGGCAGAATACAGAGGGAGGAGCAGAGAGGTGGCCC +TGGAGAGGTGCAAGGGCAAGCAGATGGTGAAGAGGGAGGAGGCAAATCCCCGAGAAACACGAAGAGGTCCCTGGGGAGAA +GCAGGGCATCGGCAGGAAAGAGAAAGAAGATCTGGGGACAAAGAGAGTTGGTAGCTGGGTGGTGCCCATCCTGAGGGAGA +ACACCAGAGCGGGGATTGCCAGCAGGACGCAGAAGAAGGATACGACAGCGAGCAGTAAATAGTCTCTTTCTGGAGGACAC +AAGGGCATCAGGGTGACTGGTTGCATTGGGGCGAGCAGCTGCACTGGGATGAAGGGCTGCATGGGGATGAGTGGCTGCAT +CGGGGTGACCAGGGACTTCAAGTCCCTGGGGCTCATTGCAGTAGCCATAGGCCTTGGCCTGAGGAGGAGATGCTGGGTGA +GTGGAATCTGCAGACAGGGGTCATGGGGTCCGGGAAGGGGACATCTGAGTCCCCAAGCAACTGAGACCACCCAAACCAAC +CCCTCTGGCTAGAAAGGTACTGAAAGTTGACACTGTCCCAAGCCAGGGGACAGCGCTCCAAGCCAGAACCCACCACAGCC +CCGTGCTCCTGTAGAGTAGCCTTGCTGGCTCTGGAACACCCACGCCACCCTCCTCTAGCCTCTCACAACCCCCTCCCTGC +TCCCCTCTGGCTGTTGCACTTTCCTCCTCTAAACTCTCCCTACCCCTCCTTGCCCCGCAAACTCACACACCCCCACTGTT +TTCTCAGCCTCCCAAAGTGCTGGAATTACAGGCGTGAGCCACTGTGCCCGGCCTATTTATTTATTTTTTGTAGAGGTCTC +ACTATGTTGCCCAGTCTGGTCTTGAACTCCTGGCCTCAAGCAATCCTCCTGCCTCGGCCTCCCAAAATGTTGGGATTACA +GGCGTGAATCACTGTGCCCAGCCACTCTTCCTTTTTGTTGTTGTTGCCAGTTTGAACGCCTGATGGGTTCAATATATAGT +GTGTGGATCTCACATCCAGATTGAATATAAGTATAATGGGCAGATTTATCACTATTAGGAGGCTACTGTACATTAGGCCC +AGGCTGGCCCCTGAGGGCCACATGGTAACTAAGGCAGACAAGACCATAACTCAGATAGAATAATAGTCTCCAATCCCATC +CAGGTTGCTGTGAATGCCATTAATTCATTCCTTTTCATGGCTGAGTAGTATTCCACCATATATATATGTAAAATCAGTTT +CTTTATCCACTCATTCATTGATGGGCATCTGGGTTGGCTCCACATTTTTGCAATTGCAAATTGTGCTGCTATAAACATGT +GTGTACAAGTATCTTTTTCACATAATGACTTCTTTTCCTTTGAGTAGATACCCAGTAGTGGGATTGCTGGATCAAATGGT +AGTTCTACTTTTAGTTCTTTAAGGAATCTCCACACTGTTTTCCATAGTTATACTAGTTTACATTCCCACCAGCAATCAGG +GAAATGCAAATCAAAACCACAATGCGATACCACCTTATTCCTGCAAGAATGGCCATAATCAAAAACTCAAAAAATAATAG +ATATTGGCATGGATGGAGTGAACAGGGAACACTTTCCTTGAGTTTTAACTCCAGACTGGCTCCTTTGCTGTTTTTCTGGT +GGGGCTCTTACCACATTTCCCACAGCCATTGGCTGTGTGCTCCTGCATACTTGGATGCTCCTCTCCTGCCTGGGCCCCAC +CAGGACCCCTGAGATCCAGGAGCCGTGGCTTTTGGGGGCTGGTAGAGAAGAAACAGGGAGCTACGTGAAGGCCAATGGCA +GAAACCAAGAGAGTTTCAAGAAAGCAAGAAAGAGAAACCAGCTCTGAAAATAGCAAAAGGAACAACGGGAAAAAATAAAA +TAGTTTCATGTAACCTAAAAACAGACATTGAGTCTTCGAATCAAATTGAAAGCACTTGCTAGCGTCAGAGGCATGTGAAC +CAGAGCAACTCCATCTTGAATGGGGCTGAGTAAAATGAGGCTGACGTCTACTGGGCTGCATTCCCAGACAGCTAAGGCAT +TCTAAGTCACACGATGAGATAGGAGGTTGGCACAAAATACAGATCATAAAGACTTTGCTGATAAAACAGGTTACAGTAAA +GAAGCCAGCTAAAACCCACCAAAAACAAAAAGTGGCGATGAGAGTAACCTGTGGTCATCCCCACTGCTCATTATACACTA +ATTAGAATGCATTAGCATGCTAAGAGACACTCCCACCAGCGCCATGGCAGCTTACAAATGCCATGGCAACATCAGGAAGT +TACCCTATATGGTCTAAAAAGAGAAGACATGAGGCCAGGCACGGTGGCTCACGCCTGTAATCCCAGCACTTTGGAAGACC +AAGGCAGGCAGATAGGGTCACCTGAGGTCAGGAATTCCAGACCAGCCTGGGCAACATGGTGAAACCCTGTCTCTACTAAA +AACAGGAAAAATTAACTGGGCATGGTGGCACGCACCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCTCTTA +AACCCGGGAAGCAGAGGTTGCAGTGAGCCAAGATTGTGCCACTGCACTCCAGCCTAGGCAACAGAGCGAGACTCCATCTC +GAAAATTTAAAAATAAATAAATAAATAAAGGGGAATATCACAAGCAACGTATGGCAACAAATTAGACAGCTTAGATGACA +GCTAAAAATTCTTAGATGAATTACCAAAACCAACTCAAGAAGAAATAGAAAATCTGAATAGATGTATAACAAATAAAAAG +AGATTTGTAATTTAAAATACTCCCGCAAAGAAAAGCTCAGGCCTAGATGGTTTCATTGGTGAATTTCACAAAACATTAAA +GCATAAATAATATTAATTCTCTACAATCTGTTCCAAAATATGGAGAAAGGGAGCACTTTCCCAGCTTATTATATGAGGTC +AGTATTACCCTAATATCAAAGCCAGACAGTGGCATCTCAAGACAACTAGAGACCAATCTCTTTCATAAATATGATATAAA +AGTCATTGGTAAAATATCAGTAAGCCCAATACAGCAATATATAAAAAGGGTTATACACCATGATCAAGTGGGATTTACCC +CAGAAATGCAAGATTGGTTTAACTTCTGAAAATCAATTAATGTAACACACCATAGTAAAAGAATAAATTTTATTCATTTA +TTTTAAAGATGGGGTCTCACTCTGTCACCCAGGTGAGATTGCAGCTCACTGCAGCCTCAAACTCCTGGGCTCAAGCAATC +CTCCTGCCTTGGCCTCCCAGAGTGTTGGGATTACAGATGTGAGCCGTTGCACCTGCCCCTGTAAAAGAAGACACTTTTTT +TTTTTTTGAGATGGAGTCTCACTCTGTCGCCCAGGCTGGAGTGAAAGACTAATTTCCGGGTGGGCGCGGTGGCTCACGCC +TGTAATCCCAGCACTTTGGGAGGCCAAGGCAGGTGGATCACGAGGTCAGGAGATCGAGACCATCCTGGCTAACACGGTGA +AACCCCGTCTCTACTAAAAAATACAAAAAAATCAGCCAGGCGTGGTGGCGGGTGCCTGTAGTCCCAGCTACTCGGGAGGC +TGAGGCAGGAGAATGGCATGAACCCAGGAGGTGGAGCTTGCAGTGAGCCGAGATCATGCCACTGCACTCCAGCCTGGGCG +ACAGAGCAAGACTCTGTCTCAAAAAAAATAAATAAATAAAAGAAAAGAAAGACTAAATTCTTCCTGGCTAAGATAGGGAA +CAAAACAAGAATGTTCATTCTCACCACTTTTATTCAACAGTATAGTGGAGGTCCTAGCCAGTACAATTAGTCAATAAAAA +GAAATAAAAGGCATCTGGGTTGGAAAGGAAGAAGAAAAAACTGTTTTGACCCACAGTTGACTTAAATTTGCTTGTAGAAA +ACTCTAATGAATCCACAAATAACTACTAGAAGAAATGAGTCCAGCAATGCTGCACAAGATCAATATCCAAAAATCAATTG +TATGTCAATAAGCTACCAATGAACAATCAAAAAATGAAATTAATAAAATGAATTCATTCCCAATAGGATCAAAAAGAATA +ATAAATTCAGGCTGGGCGTGGTGACTTATGCCTGTAATCCCAGCACTTTGTTAGGCCGAGGCGGGCAGATCATCTGAAGT +CAGGACTTCAAGACGAGTTTGGGCAACATGGTGAAACCCTGTCTCTACTAAAAAAAAATACAAAAATCAGCTGGGCATGG +TGGCGGGTGCCTGTAATCCTAGCTACTTGGGAGGCTGAGGCAGGAGAATTGCTTGAACCTGGGAAGTGAAGGTTGCAGTG +AGCCACGATCGCACCACTGCACTCCAGCTTGGGTAACAGAGAGAGGCTATCTCAAAAAAAAAAAAAGAAGAAATTCAATT +AAAAAAAGTAATAAATTTAACAAAAATACAAGAGTTGTACAATAAAAACTATAAATCTTGGCTGGCGGCGTGATGGCTCA +CGCCTGTAATCCTAGCACTTTGGGAGGCCGAGGTGGGCGGATCATGAGGTCAGGAGATCGAGACCATCCTGGCTAACACG +GTGAAACCCCGTCTCTACTAAAAATACAAAAAAATTAGCCGGGCGTAGTGGCAGGCGCCTGTAGTCCCAGCTACTCGGGA +GGCTGAGGCAGGAGAATGGCATGAACCTGGGAGGCGGAGCTTGCAGTGAGCCGAGATCACGCCGCTGCACTCCAGCCTGG +GCAATAGAGTGAGACTCTGCCTCAAAAAAACAAACAAACAAGCAAACAAACAAACTATAAATCATTCCTTTAAAAATTTA +AAGATCTAAATAAATGGAGTGGCATCCCATGTTCATGGTTTGGAAAACTCAATGTTAGCAAAATGGCAGTACTCCCCTAA +TTGACCTAAAGAGTCAATGCAATCTTTATCGAAACCTCAACTGGCTTGTTTTTTTTTTTTTGCAGAATTTGACAAGTTGA +TCCTAAAAGTCATGTGGAAATGAAAAGGATGCAGAATAACCTAGCAATGTTGAAAAAGTAAAATAAAGTTGGAGGACTTC +CAGTTACCAATTTCAAAATTTATTCCTGATTACAAAGCTATAGGAATAAAGACAGTGTGGTACTGGCATAAGGATGGACG +TATAGCTCAATGAAGCAAAATTGAAAGTCCAGAAATAACTTTCATATTTATGGTCAAGTGATTTTTGACAAAAGTGGCAA +AACCATTCAATGTGAAAAGGAGATCCAACAAATGGTGCTAGAACAATTGGATCTCTCTCTCTCTCTCTTTTTTTTTTTTT +TTCTTTTTGGAGATGGAGCCTCGCTCTGTCACCCAGGCTGGAGTGCAGTGGCATGATCTCAGCTCACTGCAACCTCTGCC +TCCCGGGCTCAAGCAATTCTCCTGCCTCAGCATCCCAAGTAGCTTGGGGCTACAGGTAACCACCACCAAGCCCAGCTAAT +TTTTGTATTTTTAGTAGAGACAGGTTTCACCATGTTGGCCAGGCCAATCTTGAACTCCTGGCCATTATTTTCAAATGATT +CTCACATGCTCTGAACCCCACTCATTCATCATTATTATCTCTTTTTTTTTCTTGAGACAAGGTCTCCCTCTTGTCCAGGC +TGGAGTAGAGGGGTGCGACCATGGCTCACCACAGCCTCAAATTCCTGAGCAAAAGCAAACCTCCCATCACACCCTCCCAA +GCAGCTGAGAATACAGAAGCCTGCCCTCAGAGCCTGATATATATATATATCTGTCTAAAATATATACATTTTTTTTCTAG +ATACATGGTCCCACTGTATTGATCGGGCTAGGTTTCACTATCCTGGTCTCATGTGATCCTCTCCCCTTAGCCATTATCAC +CTTATAGAGTGCACATTTTGTTCCATGCACTGTTCAATACCACTTACATACATTAATTTAATCCTCACAATGAAACAAGA +GAGAAATAATTATTATTCTCATTTTAAAGATGAGAAAACTGGTACACAGAGAGGTTGCACAAGGTGATATTGCTAGTTAG +TGGCATGGTGGGGATCCAAACCTAGGTAGTCTAGTTCCAAGGTGTGTTTATTCAGCCCATGTTTACTGAGCACCTACTAC +TATGTGTTGGGCTCTGCTCTAGGTGTTAGAGCTATAGACAGTATAAAAGAACTACCTCGCCCCCATTAAGACATCTCTCC +TGGCTGGGCATGGTGGCTCATTCCTGTAATCCTAGCACTTTGGGAGGCCAAGGTGGGTGGATCACTTGAGGTCAGGAGTT +CAACACCAGCCTGGCCAACGTGGTGAAACCCCATATCTACTAAAAATACAAAAATAAGCCAAGCATGGTGGTGGGCACCT +GTAATCCCAGCTACTCGGGAGGCTCAGGCAGGAAAATCACTTAGAACCCAGGAGGTGGAGGTTTCAGTGAGCCAAGGTTG +CACCATTGCACTCCGCCCTGGGCAACAAGAGCAAAACTCCATCTCAAGAAAAAAAAAAAAAAAAGACATCTCTCCCATAT +CTCTCTCTTATTGAGACATGGTCTCTCTCACCTTTCCCACCAAAAATTTTTTTTAAAAAAACAAAACCCTTTCATCACTC +AGCCCCCTCAAGTTATGGATCTGGTTTTTGGTTTTTAATTCCAGAGACAAAAAAGGGGGCAGAACATAGACCCATGCTAC +CATCTTGACAGAGTCTCCATTTCAGTTCCATACGCTCAAGTCCCTCCTATCTTTAAAACAAACAAGCAAAATCATCATTC +TCACCCTTTTGCATCTACCCTAGTCCTGCCTTTCCTGGCTCTTCATAGGCAAACTTTTTTTTTTTTGAAACGGAGTCTCA +CTCTGTCACCCAGGCTGGAGTGCAGTGACACAATCTTGGTTCACTGCAAACTCCACCTCCCGGGTTCAAGCAATTTTCTG +CCTCAGCTTCCCAAGTAGCTGGGATTACAGGCACCCACCACCACATCTGGCTAAATTTTGTATTTTTAGTAGAGACAGGG +TTTCACCATCTTGGCCAGGCTTGTCTTGAACTCCTGACCTCGTGATCCACCCACCTTGGCCTCCCAAAGTGCTGGGATTA +CAGGCATGAGCCACTGCACCCAGCCATAGGCAAAATTCTTGAAGCAAATGTATACACTGTCTCCAATTCCACACCTCACA +CTCAATCCATGGCTCTCTGAAACCAGGTGTTGGCTTACAACATTCCAGAAACTGGAACCCATGTTCTTCTCAGCTGTGGC +ACTGCTCTCCTTACTCTTTCGCCTCCAAGCTCTTGGAAGAAATGTCTACATTGTCTATACTACCCCTTTCTTGCTCACTG +CCTGGATCCCTGCAATATGGCCACTGCTTCTGCTGATCCTCTGATACTGCCCTTACCAAAGGAGCTAATTCATTCCCGCC +TTCAGAGCTGGAGTACATGTCTCAGGGCTTGACACACTTGTCCATGTACTTGTTTCTAAGCCTGTCTTAGTTTGTTTGTT +CCACTATCCAAGGGACTTGCCAGCAACCAATACACATGCTTCATCTCACCCAACCTTCACTTCTGTCTTCCCCCACCTCG +GCGCCCCATGTTCTGTTGTCCTGGAACATCCACTTGGTCCTTTGAAAGGCTCTGAATCAATTTCCTCCATGAAGAATATC +CTGACTTCTGGATTGCATGTGTTGTCACTGTTCTTCTAACTACTTCTAGGTCCAGGACTGGTCTTAAATACCCAGGTTGT +GTCTTGGACTAGCCATACTAAGGTCTACATTTGTGGACTGCACAGTGTCACCTCCCACCCACCCCCAACTTCCTGAAAAG +TGGCTCCTCAAACCCTCTACTCCCCTAGAACCACCAGCTAAAGGAAGCATCAGCTAAAACCCCACAGTATTAGCATTCAT +TAAAAGCTCCTACCTGAATCAGCAATTACTATGGTATTTGAAAATGTTCTCTAATGGTGCATAACAAATTCCCCACAGGT +ATTAACTCACAGTTCTGTAGGTCAGAATGGCTGCTTCTCTGCTCAGAATCTACCAAAGCTAAAATTAAGGTGTCAGCTGG +GCTGGGTTCTCATGTGGAGCTCAGGATCCTCTTCCAAATGAATGTGATTACAGCAGGATTCACATCCTTGCTGTTGCAGT +ACTAAGGACCCCGTTTCCTTGCTGGCTGTCAGCTGGGAGCCATTCTCAGCCAGAGGCTGCCTGGATTCCTCCCTACGTGG +CCCTGGTGACATCTTCATGCCAGCAATGGAGACCTTTGCTTGCCTCCAATCCTCTCACACCTGAAACCTCTTTTGCCAGG +AAGACTCAGTCCTTTTCAAGAACTCACCTCATTAGGTGAGGCCCCCAGAGGATATTCTTCCTAACTTAAAGTTAACTGAT +TTCAGACCCTACCTACGTCTGCAAGACGCCTTCCCAGCAGCATACAGACTAGTACTTGACTGAGTGACTGGAGAAGGAGT +GTGTACACCAGGGAGCAGGAATCTTCGGGTCATCTCAGAGTTCTGCCTACCACATTGGGGATTTCCTGTTCCATCGTTCC +ATTAGGTTGTGTTCTACTTTAAGAAAGATCTTCCCCCTTTTCCCCTTCTTTCTCTATTCTTTCCTTTTTTCTTTCTGTCA +GCATAAACCAAGGAATTCTTTTTTTTTTTTTTTTTTGTCATTTTTTCTTGAGATGGAGTCTCACTCTGTCACCCAGGCTG +GAGTGTGCAGTGTGATCTCGAATCACTGCAACCTCCGCCTCCCGGGTTCAAACGATTCTCCTGCCTCATCCTCCCTAGTA +GCTGGGATTACGGGCGCCTGCCACCACACCCGGTTAATTTTTGTATTTTTAGTAGAGACGGGGTTTCACCACGTTGGTCA +GGCTAGTCTCGAACTCCTGATCGCAGGTAATCCGCCCGCTTTGGCCTCCCAAAGTGTTGGGATTACAGGCATGAGCCACT +GCGCCCAGCCGGAATTCATTTTTAATTCAAGACTTTACAATCCACTTCTGACACTACTCATTTTCCTCCCCTCAGTAAAT +CTCTTACTCTTACTGGGCGTGTCTAGTTTCTCTCTTCCACAACTCCTGGGAGACCCTCCTGACATCGCCCATCCGCGCAG +GCTGCAGAGAGAGATCAGTGGGAGGAGGAGGAGCCGAGAGCCACCACTTAGGCTTCCAACCAATCCCTACCAGGGTGGGC +GGAGCCCACTTCCTGATTGGCTGCACTCTCTGTGTCTTGGGGTGGGCGAGAACGGCGGGGCCACGCCCCCTAACTAGGCA +GCCAATCAGGACGTGGGGTGCTGGTTTCTCCATTGCGAAGCTTCAGCCTTTGATGGTTTGGGTCCTGGGAGTCTGGTTAG +TACAAGGGGAAGCCTAGTGGGTCTGGCGCTCCGTTTTCAAGACACTCGGAGTCCGTCTTTGAGGGGAAAGGTCATGGCCC +TGAAACCACCTTCTGCCACCCAGCCTGCTCCCAACGCGCCAGCTACCCCAGACGCCCCCCCTACCACAGGTGATCCAGGT +G diff --git a/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/assets/demo/enhancer_eve.fa b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/assets/demo/enhancer_eve.fa new file mode 100644 index 0000000..92dd9ca --- /dev/null +++ b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/assets/demo/enhancer_eve.fa @@ -0,0 +1,168 @@ +>drosophila|eve|chr2R:9972000-9982000|BDGP6|strand:1|gene-sense +CCTCGTGTTCTTCGATGTTCGGGAGCGCCAGCAAATGGGCGTCTACTTCGAAAGCCATGA +GGACGATATCACCTCGTTGCGGTTCCATGCCCAGAATCCAGACCTTCTGGCCACGGGCAG +TGTGGACGGGCTAGTAAACGTGTTCGATGTGAAGGAGCCGGACGAGGATGAAGCACTGCT +TAACACCTTCAACACGGAGAGCAGTGTGGCCCGGTTGGCGTGGCACAGAAACGTCTACGA +CAAGGACATCATATCCTGCGTCACGACCACTGGCGATTTCAAGAGCTACGAGTGCGAGGA +GGGCGACGAGGTGGCATCCTTCGAGCGGCCGGATGTTACGGCGGCAATAAGGAGAAAAAA +AGCCGCAAACTTCAACCTGATAAATGCCCACAATCAGGAAGACGGAGGAGTCTTTCTGTT +GGCGGGCACCAACTTCAACAAGGGGTTCGTTATTAACTGACTTTGGCCTAATACCCAAGC +AACTGAATAACCTTCTTTCAGCGAGATCCTGCGCTCCGTTAGCGTTACCTCGAAGAACAG +CCTCCAGCCATTGGCCAATTTCCAGGGAAACAAGCAGATCGTCAGAGATAGTCTTTTCGA +TTCAAAGAGAAGTTTACTTTTTACAGGCGGAGAATCCGGCATTGTTACGGTTTGGGCACA +GGACGCAAGTGGAACGGCGTTCAGCAGTGAAAAACTGAAGGCCAGGAAGGAGAAGAAATC +ACGCAAGCAAGCGCCATATTAAATGTGTATTTATGTTTTAAATAAAGGTTTTCTAAATAC +GAAATTTCAAATTTAATGGGTAGCAGCGTTGCCAGATAAAGGGACATGTTTACTGGCTGA +CAGGAGCTCACTGATGTGAGCGTATGTCTACGAGAGCTGCTTACGTAATTTGAGAAAGTG +TTGCATACTTTTGAGATTGCGTTTGAGCTACGTTACTTACATTTTTCCCACATGAGTCGG +GCATACACATATGTAAATAAAAATTGGGTTCACTGTATATAAAATAGAGTATAAATTATT +CATCTTATCTAACAAATATTAGAAATAATCAAAACACCTTATAAGTTTATCGAATTTACG +AATTTACGAATTTATTTCTACATATAAATGTTGACCGCCATTTGGACTCCACTTAACCGC +TTAATTTGCTTAAGTAACATAAGTAACTTTTAAGATTAATAGTATTGTATTTTGAATGGT +AAAAGTCCATCCTAATCTATTGAAAAAATCCGAAGGAACCAAAGTCATTCAAAGTTGATT +CAATTCACCCCCACCCTGCGCACTCAGTGCTCAATTTCTGCTCGCAGCAAGGTCTGCTGA +GTAAAAAATTAGCACATCCTCAGCGCAGATAAACGCATCCTTAAAACACCCCCTGGCCAT +CCCACAAGCTAGCCATCCATCCATCCGTTCATCCTGCATCCGCATCCCTTCGCACAGAGA +GCGGCCCAATTAGTGCTGCAGTAGGCATTTCGAATCCGTACCCACAAATCGGCTAATCCG +CTGCCACTGCCTGCCGGCCCAGAGTCCAAAAGTCCTGTGTGCAATGTCCAGATTTCCGAG +TCGCAGAGTCTAAATGTTGACAAATACAACACCTACGATACAAATAAAATTGCTACAGAA +GTACCACCTAAAAACGGGCACAACCGGAACGACTTAATTTTCTCAAATTTACAGAAACAG +ACGATGACATAGGAATGGGAATGGTGATGGTGATGGAAACACAGGAAGATATCTCGACGC +CGCCACTGAAGCTGTGGGTTTGCTCCTGCCGAGCGAATCCAACGCGAGTAGGGTCCCATT +CGGGGCCCGAGTAGCCAGAGTCCTGCAGCTCACTCGAAACCGCCACTCACCGTGGCTAAT +TGCCCATCAATAAAGGGCCCGGGCAGTGAGGAATTCCTCCGAAAGTCGGGTCCTCCGTTC +TCCAGCCGAAGATTTTTTCGAGCAACCAAAATATTATGGTGTGCCCCGCTGTTCTCGCAC +AGTCAGCGCGAATTTGCTGCGGTGAGTCGATGCTGTTTCGCAGGACCTTCTTCCATTTTC +GTCTCCCTCTTGCTCAGCCTGTCCCTGTTCCTCTGCAGTTCCCTATCTCCTGATGCCTGT +GCTCCTTTGGCGGCACTGTGTCCTGTCGTCGTTGTTTTCCTGTGATTTGACATGTCTGTT +AGCAGGATGCCTGACCCTGAGGCCGAGCCCTGGTCTCAGTGTCCACTGTTCCACTTTGAT +GTGATTCGTCAGTGCGGTGGACTACTGCTACTGCTCTCTTGCTGGACTGCGTCTTGAGTC +CTGTTCGGCTGCCCCCTCCCGTGACCTCTGACCCTGCACTCTGCGGCTTTCCAGCGGCGT +TTGTTGGCGAATCTGACCCCGAGCTCCTGCTGCTCCTTCGCTCCTTCGCTCCTTCTCCGC +ATCTCCGCTCTTTGGACTTCGTACGAATCAAAATTGGTCACAGCACCGAGTGAATTGCCC +CGGAGACCGCAATGCGCTGTATTTATAGTAAACGTGTCCGATTGATTTGGCCACCCGTGG +CGGCTCTGTCACAGATGCCTCAATTTGCATCTATCGAATGGTTTACATGGCTCTAAAAAG +GTACCTCGATGGGTTGGTCACAATGTGGTGGCCTCTCAACATTGCAAGGCTCTTACTTGT +GAATTATTAAGTTATTAACTGCTGCGATGTAAGTCATGGCAGTTTCTGTTTTCTTTATAG +GATATATATAGGAAGGATTAAAGGAGGCATGTACAATAATATGAGTATGATTTAGCTCAA +ATTCCAAATATGATAAAAGTACAAAGCATACGATAATATAATCAAATTACGCTGACAATC +ACGATAATGTTCTTGTAGTAGTATTTGTGTAATATTTATGTTTTTTTAAGATAAGAAACG +GTAATAAAATCCACGTAAGTGTAAAAAATGGATGCCCTAATCTATGCCATGATGTGTTCT +ACTTTCGAGATTTCGCCTCTGCCCTCATTGATGGTTTCCCGGGGCTACTTGGCCCAAAAA +TCCCGGCCGTCCAAAAAGACGATCCTTAAAAAAGAAACCGCTAATCATTGGGCCGCACAA +AGAGCGGACAATCGCTCACCTAATTATTTGGCCCGATTGTGAGGAGCGGACAGTCGGCTC +GTGGACGCTTTTTGTGGCCTCTTTTTGTTTCGACAAAAAGCGAGCCAATTTTTTTTCTTT +CTGGGCCACTTTGTTGCTCTTTTTATGAGTTTTTTCCATTGTCAGTTTTTCCGGGCCTGT +CTCGCAGCCCTCGATTCCCGCGATGCCTGCCCTACAAACCTCCTAATTACGGCAGTTAGT +CGTTGTCCGGGACAGGAGAGTATGCGGAAGGACATGCGTGAGTTTATTGCCCGCTCGAAT +TTCCACTAAAAATTGGGCCGAAAAAAAAACAACTAGGTAGGACTAGGAACTGCAAACTAG +CAAAGCGGACGCGCCTTTTTATTGGTGCACCTTCGGCGGAACCGCAGGATAACAGCAGTA +AAAGCGACGACGAGGACACAAGGATCCTCGAAATCGAGAGCGACCTCGCTGCATTAGAAA +ACTAGATCAGTTTTTTGTTTTGGCCGACCGATTTTTGTGCCCGGTGCTCTCTTTACGGTT +TATGGCCGCGTTCCCATTTCCCAGCTTCTTTGTTCCGGGCTCAGAAATCTGTATGGAATT +ATGGTATATGCAGATTTTTATGGGTCCCGGCGATCCGGTTCGCGGAACGGGAGTGTCCTG +CCGCGAGAGGTCCTCGCCGGCGATCCTTGTCGCCCGTATTAGGAAAGTAGATCACGTTTT +TTGTTCCCATTGTGCGCTTTTTTCGCTGCGCTAGTTTTTTTCCCCGAACCCAGCGAACTG +CTCTAATTTTTTAATTCTTCACGGCTTTTCATTGGGCTCCTGGAAAAACGCGGACAAGGT +TATAACGCTCTACTTACCTGCAATTGTGGCCATAACTCGCACTGCTCTCGTTTTTAAGAT +CCGTTTGTTTGTGTTTGTTTGTCCGCGATGGCATTCACGTTTTTACGAGCTCGTTCCTTC +GGGTCCAAAATTATGCCAGTTTGTTTTGTCTCTGGCAATTATTGGAAATTTCATTGGGTC +GATTTCGCTGCCTTCCTTGCTCTTCCCTTGAGAAAAGTGAATAGGTTGTGCCATAAAAAT +CGCTGCTCCTGAAGACCAAATGAAATGGATTTGTGTAAGCATTAAAAACGCGAGGCAAGC +CCCAAGATTCCTCCACTGCTTTTTTTATATTGCCCACTGCTAAATGCAGCTAATTCGTCG +ATTGTTTAAAAATTAAATTACTTATGTTGCCATTCATACATCCCCTCACATTTTATGGCC +ATTTGAGTGCGGGGTGCACAGTTCTGTCTTAAGTGGCGGATGGAAACCACCACATTTACT +CGAGGGATGATGTGCTCTAATATCTCCTCATCAAATGGGATGGTTTCTATGGAAAGGCAA +AATCGTTGTAAAGTGAGGCGGAGTTAAAAAATACCTTGTTATAGCCTTTTTAAAATAACA +CAAGATCGTTCGAATTGACTAGAAATATCAAAGTCTTTTTGTATTGAAGCGAGTGTAGTC +TCAATTTATGCTTAATTTTAAGAAATACATCTCTTTATTAGCCCCAAAATGAAACAAATG +GTCTACTAATTAAGCAAGTCAACAGAATTTTTATGCAATTATTCAAAATGAAATAATATA +TACATAAGATGTTTTTGGGAATCTGTCATGGGGTTTCTGAAATAGGTTTGCCAAACAAAT +TTTAAGTATAAATGTATACATATGTCAACTAATAAATTTAGCAAATAAAATGTACCTGCA +AGTATCTATAAATTTATTGGACCAATTTTGTGTAAAAAACTGAACTGGCACTCTTCCCAA +GAATGGGACTTCGAGGACTCCTTGCTGAATCACTTACTCAACCCATTCCAACTCATCCAA +TCCGCGCAATCATCATAAATTTTGGCCTTTTTGTTGTAATTGTTTTATGGCAGAAATTAC +TCAATCATCAAGCATAATTCCCTCGTTTTCGCCGTTTTATTGCCAATTTTTGCACTGCCT +TTGCCTTTTTCCCGCCCTTTCCTCAGCGTTTTGCGAATCTTTGCCGGCATTTCTATTGCG +CGGACAATCCGGCCAGTGTGTTGGCCATTTACTTGCCATGATGACGGGCATAATCAGCGA +GATCGGCGCTTTGTGAGTGCAGAATGTGCAATAAAGCGGCAACAATCGGCAGGGATTCGC +CTTCCCATATTCCGGGTATTGCCGGCCCGGGAAAATGCGAAAGTGTTTGCGGATCGAGAT +GGAAGATAGAGGATTGAGTATTGAAACGAGGAAGGTACTTCCGCCGGCGGACACTTTCGC +CTAACCAAGCCAATCCAACCCATCCCAATCCAATCCAACCCACCCGATCGCCATAAAGGG +TATTTACTGTCGCTGCCGCAGAGCCTCGCTTGACGACTTAACCCAAGCGGTCGTTTCGCG +TCCATTCTCCGGACGGAGTCAAAGACAAAGGCCGGCGGAGGTGGACAATAGGCAAGGTTG +TTGCTTGTGGGTAGGGTTTGAGCTATGAGCTATGAGCTGTGAGCTGTTAGCCCTGAACCC +CGAACCTCGAGAATTGAACCTTTCCCGGGGCAAGAAGGCTTGCATGTGGGCCTTTTCCAG +GTCGGCCAGTAGGTAGAGTTGTTGCGATGCGGCTATGCCGGGCGAGTTAATGCCAATGCA +AATTGCGGGCGCAATATAACCCAATAATTTGAAGTAACTGGCAGGAGCGAGGTATCCTTC +CTGGTTACCCGGTACTGCATAACAATGGAACCCGAACCGTAACTGGGACAGATCGAAAAG +CTGGCCTGGTTTCTCGCTGTGTGTGCCGTGTTAATCCGTTTGCCATCAGCGAGATTATTA +GTCAATTGCAGTTGCAGCGTTTCGCTTTCGTCCTCGTTTCACTTTCGAGTTAGACTTTAT +TGCAGCATCTTGAACAATCGTCGCAGTTTGGTAACACGCTGTGCCATACTTTCATTTAGA +CGGAATCGAGGGACCCTGGACTATAATCGCACAACGAGACCGGGTTGCGAAGTCAGGGCA +TTCCGCCGATCTAGCCATCGCCATCTTCTGCGGGCGTTTGTTTGTTTGTTTGCTGGGATT +AGCCAAGGGCTTGACTTGGAATCCAATCCCGATCCCTAGCCCGATCCCAATCCCAATCCC +AATCCCTTGTCCTTTTCATTAGAAAGTCATAAAAACACATAATAATGATGTCGAAGGGAT +TAGGGGCGCGCAGGTCCAGGCAACGCAATTAACGGACTAGCGAACTGGGTTATTTTTTTG +CGCCGACTTAGCCCTGATCCGCGAGCTTAACCCGTTTTGAGCCGGGCAGCAGGTAGTTGT +GGGTGGACCCCACGATTTTTTTGGCCAAACCTCCAAGCTAACTTGCGCAAGTGGCAAGTG +GCCGGTTTGCTGGCCCAAAAGAGGAGGCACTATCCCGGTCCTGGTACAGTTGGTACGCTG +GGAATGATTATATCATCATAATAAATGTTTTGCCCAACGAAACCGAAAACTTTTCAAATT +AAGTCCCGGCAACTGGGTTCCCATTTTCCATTTTCCATGTTCTGCGGGCAGGGGCGGCCA +TTATCTCGCTACAGCAGTTCCCAAATGGTTATGGCTGGACACCCCTGCCGCCGCTCCAAC +GGGGTGGATGAAGCCCCCAAAACCCGAAAGTCATGGCAGCCATGGCAGTGTGGGGCTGTT +AAACGTGCGGCATAATATTAAGACTTCATAAAAGCGCAAATAATTCGCTGGCAGGCGATC +GATAATACATACATACAAATATATAGTGGGATACACACACTCTCTGCCGGCAAACACACA +CCACCCGACCCGACTGAGCGGCATAATGCCATATCATTCTTGATGAAGCCGATAAAATCC +CATTATTAAGGGGGCCCGCCCGTCCCGCTCGCTCCTGCGGAGCAACCGCCTGCGGGCGGG +CGAGACAAAAGATTCGCTCATCCGCTATGAATACCAAATCGGAACTCTCTCTCTCTCCAG +CTCGGGAGTGCCATGGCCAGCATGGCCAGGACCTCCTCATGGTCCTGCCGAGCAGAGAAC +GCGGCTCCATCCCGCTGCTCCGGGTCCTGCTCCTCCGCTTTGTCCCGCCTCGTTATCGCC +GCTCAGCACCGAGAGCACAGCAGCGCATCCACTCTCAGCACCGCACGATTAGCACCGTTC +CGCTCAGGCTGTCCCGCTCGCACCTGCCTGGGTCGCTGCGATTGGCCGCTCCCAGCGACG +GCGGCCATTTGCCTGCAGAGCGCAGCGGTATAAAAGGGCGCGGGGTGGCTGAGAGCAGCA +CACTCGAGCTGTGACCGCCGCACAGTCAACAACTAACTGCCTTCGTTAATATCCTCTGAA +TAAGCCAACTTTGAATCACAAGACGCATACCAAACATGCACGGATACCGAACCTACAACA +TGGAGAGCCACCATGCCCATCACGACGCCAGTCCCGTGGACCAGAAGCCCCTGGTTGTGG +ACCTCTTGGCCACCCAGTACGGCAAGCCCCAGACACCGCCTCCCTCGCCAAATGGTAAGT +TTAAAGATAAAGCCGAGCAAACGTGACGAGTTACTTACACCCAATCTTTCCTCTGTCCAA +AACAGAATGCCTATCCAGTCCGGATAACTCCTTGAACGGCAGCCGCGGCTCGGAGATTCC +CGCCGACCCGTCGGTACGCCGCTATCGCACCGCCTTCACCCGTGACCAGCTGGGTCGCTT +GGAGAAGGAGTTCTACAAGGAGAACTACGTGTCCCGTCCCCGTCGCTGCGAACTGGCCGC +CCAGCTGAACCTCCCGGAGAGCACGATCAAGGTGTGGTTCCAGAACCGCCGCATGAAGGA +CAAGCGTCAGAGGATCGCCGTCGCCTGGCCCTACGCAGCCGTCTACTCCGATCCCGCCTT +CGCCGCCTCCATCCTCCAGGCCGCCGCCAACAGCGTGGGCATGCCCTATCCGCCCTACGC +CCCCGCTGCTGCCGCCGCTGCTGCCGCCGCCGCTGCCGTGGCCACCAATCCGATGATGGC +CACCGGAATGCCCCCGATGGGCATGCCCCAGATGCCCACAATGCAGATGCCCGGACACTC +GGGACATGCCGGCCATCCATCGCCCTACGGACAGTACCGCTACACGCCCTACCACATCCC +CGCCCGCCCGGCGCCGCCACATCCCGCTGGTCCTCATATGCATCATCCGCACATGATGGG +ATCCAGCGCAACGGGATCGTCGTACTCCGCCGGTGCCGCCGGCCTTTTGGGCGCTCTGCC +CTCCGCCACCTGCTATACCGGACTGGGTGTGGGTGTGCCCAAGACCCAGACGCCGCCGCT +GGATCTGCAGTCGTCGTCATCGCCGCACTCCTCCACGCTGTCGCTCTCGCCAGTGGGATC +CGATCACGCCAAGGTGTTCGACCGCAGTCCAGTGGCTCAATCCGCTCCATCAGTTCCTGC +TCCCGCTCCACTGACCACCACCAGCCCGCTGCCCGCTCCAGGCCTCCTGATGCCCAGTGC +CAAGCGGCCTGCCTCCGACATGTCGCCGCCGCCGACGACAACTGTGATTGCGGAGCCCAA +GCCGAAGCTCTTCAAGCCCTACAAGACTGAGGCGTAAGCCCGCGATCCACACACACTCTC +TCCCCCCCCCCCATGCTCCCCCAAAAGATTGTACAAACTAGTCTTAGTCAGCCTCATCTA +TTTATTCCCGAAGATTGTACAGATTGTAGAGTAGCTAATTGTAGTCATAATTAAGGCGCA +AAATCAAATTAAGAAATAAATGCGAAAATAACATTGAAAATTATACGACACACACTGTTT +ATTTGAACTACCTGGTACCTGGTATTATCTACATATATATCAACATATATTCATATACCT +GAGCTGAATCGCTTTTATGGACCTGGCTCTCTGGATTAGCTGTGGAGAAACCTGGCGGGA +CAGCTGCGCTGCTGACTGCTGACCTTCCCAGGGAAACTGTGGCAAATCGAACGGAAGAGC +GGCAGAGCGGCAGAGCCGCAGAGCGTGCCGAGTGTCCTTCGGAGTGGCCACCCACAATCA +GGAGTAAGGGTTCCCGATGCGGCACCGGCGGGGAAGGGAACAACATCCACGGGGGATTGG +CACAAGCCAATGGCTTATTAATGCGATCCGAACGGATCCAAGCGGGAGGGCCCATTGCAA +ATGCAGAGCCACCCCGCTGGGCGGCGACCTTGGCGAGGAGTTGGGCCCACTCCCTGCCAC +GCGTCAGGTTGCATTGCCAATGGAATCAATTTCGGTGTCCTCGGATTGCATTGGGCTCGG +CTAATTAATTGTCACCTCACCCGTTGAGTGACTTTTTTGTTTGTTGGTCCTGGCCTTATG +TTTGCTTTCTCTTCTCCATCGCCAGGCAATTGTCATAATTGAGAAATCATCGGAAGGACG +AGCCAGTGCGAGAAAGAGACAGAGAGAGAGAGAGAGAGGGCTGGTGGTAGAGTCCTTCTC +GAATCAAATCCCAATTTAATTACCGTGCAAAACATGTGTTTCAATTGCTCAGCCGTGGTG +TAAAGGGGTGTCAATTGCTCATAATCCCACAGCTAAAAAACGAGAGTCAACCCAAAGGCA +GGATAAACGATGGGTAAACGATGCGAGCGAGGCACAAGGATGTAGATACCAGCCACGGAT +GCTGATACACCCAGCCAGCCACCCCTGATCCCGGCTGTGTCTAATATCTAATTGAGCGCC +TGGCATCAAAACATGTTTTGACACTTGCCAGCCGAGCCAAATGGGCATAAAAAATGGTCC +TTGAAGTGTTATCCTTGCAGTTGGAGTGCCACGCACACACCCCTTTCCTTTGCTGGTGTT +TTGCGGAGTTTCGTCTCGTGTTTTCACTTGGGGGATGCGAACACACAAGTGGCAGCATTT +TATGATTTGATAATAAAGATACGCGCCACGGTTCCGCCCTGGGGAATTCCCCAGCAGATT +CGGATACAGTAAATTAAAAAATAGCCAAACAGGAAATCTCT diff --git a/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/assets/demo/expression_hbb_k562.fa b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/assets/demo/expression_hbb_k562.fa new file mode 100644 index 0000000..e870154 --- /dev/null +++ b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/assets/demo/expression_hbb_k562.fa @@ -0,0 +1,116 @@ +>human|HBB|TSS-centered|chr11:5222473-5231670|GRCh38|RC(gene-sense) +GTTAACTGTTTTGATCACAACATTTTGAATTGACTGGCAGCAGAAGCTCTTTTATATCCATGTGTTTTCCTTAAGTCATT +ATACATAGTAGGCACTGAGAACTCTTTATATCTGAATAAGATATTTAGGAACCACTGGTTTACATATCAGAAGCAGAGCT +ACTCAGGGCATTTTGGGGAAGATCACTTTCACATTCCTGAGCATAGGGAAGTTCTCATAAGAGTAAGATATTAAAAGGAG +ATACTTGTGTGGTATTCGAAAGACAGTAAGAGAGATTGTAGACCTTATGATCTTGATAGGGAAAACAAACTACATTCCTT +TCTCCAAAAGTCAAAAAAAAAGAGCAAATATAGCTTACTATACCTTCTATTCCTACACCATTAGAAGTAGTCAGTGAGTC +TAGGCAAGATGTTGGCCCTAAAAATCCAAATACCAGAGAATTCATGAGAACATCACCTGGATGGGACATGTGCCGAGCAC +ACACAATTACTATATGCTAGGCATTGCTATCTTCATATTGAAGATGAGGAGGTCAAGAGATGAAAAAAGACTTGGCACCT +TGTTGTTATATTAAAATTATTTGTTAGAGTAGAGCTTTTGTAAGAGTCTAGGAGTGTGGGAGCTAAATGATGATACACAT +GGACACAAAAAATAGATCAACAGACACCCAGGCCTACTTGAGGGTTGAGGGTGGGAAGAGGGAGACGATGAAAAAGAACC +TATTGGGTATTAAGTTCATCACTGAGTGATGAAATAATCTGTACATCAAGACCCAGTGATATGCAATTTACCTATATAAC +TTGTACATGTACCCCCAAATTTAAAATGAAAGTTAAAACAAAGTATAGGAATGGAATTAATTCCTCAAGATTTGGCTTTA +ATTTTATTTGATAATTTATCAAATGGTTGTTTTTCTTTTCTCACTATGGCGTTGCTTTATAAACTATGTTCAGTATGTCT +GAATGAAAGGGTGTGTGTGTGTGTGAAAGAGAGGGAGAGAGGAAGGGAAGAGAGGACGTAATAATGTGAATTTGAGTTCA +TGAAAATTTTTCAATAAAATAATTTAATGTCAGGAGAATTAAGCCTAATAGTCTCCTAAATCATCCATCTCTTGAGCTTC +AGAGCAGTCCTCTGAATTAATGCCTACATGTTTGTAAAGGGTGTTCAGACTGAAGCCAAGATTCTACCTCTAAAGAGATG +CAATCTCAAATTTATCTGAAGACTGTACCTCTGCTCTCCATAAATTGACACCATGGCCCACTTAATGAGGTTAAAAAAAA +GCTAATTCTGAATGAAAATCTGAGCCCAGTGGAGGAAATATTAATGAACAAGGTGCAGACTGAAATATAAATTTTTCTGT +AATAATTATGCATATACTTTAGCAAAGTTCTGTCTATGTTGACTTTATTGCTTTTTGGTAAGAAATACAACTTTTTAAAG +TGAACTAAACTATCCTATTTCCAAACTATTTTGTGTGTGTGCGGTTTGTTTCTATGGGTTCTGGTTTTCTTGGAGCATTT +TTATTTCATTTTAATTAATTAATTCTGAGAGCTGCTGAGTTGTGTTTACTGAGAGATTGTGTATCTGCGAGAGAAGTCTG +TAGCAAGTAGCTAGACTGTGCTTGACCTAGGAACATATACAGTAGATTGCTAAAATGTCTCACTTGGGGAATTTTAGACT +AAACAGTAGAGCATGTATAAAAATACTCTAGTCAAGTGCTGCTTTTGAAACAAATGATAAAACCACACTCCCATAGATGA +GTGTCATGATTTTCATGGAGGAAGTTAATATTCATCCTCTAAGTATACCCAGACTAGGGCCATTCTGATATAAAACATTA +GGACTTAAGAAAGATTAATAGACTGGAGTAAAGGAAATGGACCTCTGTCTCTCTCGCTGTCTCTTTTTTGAGGACTTGTG +TGTGTGTGTGTGTGTGTGTGTGTGTGTGTGTTGTGGTCAGTGGGGCTGGAATAAAAGTAGAATAGACCTGCACCTGCTGT +GGCATCCATTCACAGAGTAGAAGCAAGCTCACAATAGTGAAGATGTCAGTAAGCTTGAATAGTTTTTCAGGAACTTTGAA +TGCTGATTTAGATTTGAAACTGAGGCTCTGACCATAACCAAATTTGCACTATTTATTGCTTCTTGAAACTTATTTGCCTG +GTATGCCTGGGCTTTTGATGGTCTTAGTATAGCTTGCAGCCTTGTCCCTGCAGGGTATTATGGGTAATAGAAAGAAAAGT +CTGCGTTACACTCTAGTCACACTAAGTAACTACCATTGGAAAAGCAACCCCTGCCTTGAAGCCAGGATGATGGTATCTGC +AGCAGTTGCCAACACAAGAGAAGGATCCATAGTTCATCATTTAAAAAAGAAAACAAAATAGAAAAAGGAAAACTATTTCT +GAGCATAAGAAGTTGTAGGGTAAGTCTTTAAGAAGGTGACAATTTCTGCCAATCAGGATTTCAAAGCTCTTGCTTTGACA +ATTTTGGTCTTTCAGAATACTATAAATATAACCTATATTATAATTTCATAAAGTCTGTGCATTTTCTTTGACCCAGGATA +TTTGCAAAAGACATATTCAAACTTCCGCAGAACACTTTATTTCACATATACATGCCTCTTATATCAGGGATGTGAAACAG +GGTCTTGAAAACTGTCTAAATCTAAAACAATGCTAATGCAGGTTTAAATTTAATAAAATAAAATCCAAAATCTAACAGCC +AAGTCAAATCTGCATGTTTTAACATTTAAAATATTTTAAAGACGTCTTTTCCCAGGATTCAACATGTGAAATCTTTTCTC +AGGGATACACGTGTGCCTAGATCCTCATTGCTTTAGTTTTTTACAGAGGAATGAATATAAAAAGAAAATACTTAAATTTT +ATCCCTCTTACCTCTATAATCATACATAGGCATAATTTTTTAACCTAGGCTCCAGATAGCCATAGAAGAACCAAACACTT +TCTGCGTGTGTGAGAATAATCAGAGTGAGATTTTTTCACAAGTACCTGATGAGGGTTGAGACAGGTAGAAAAAGTGAGAG +ATCTCTATTTATTTAGCAATAATAGAGAAAGCATTTAAGAGAATAAAGCAATGGAAATAAGAAATTTGTAAATTTCCTTC +TGATAACTAGAAATAGAGGATCCAGTTTCTTTTGGTTAACCTAAATTTTATTTCATTTTATTGTTTTATTTTATTTTATT +TTATTTTATTTTGTGTAATCGTAGTTTCAGAGTGTTAGAGCTGAAAGGAAGAAGTAGGAGAAACATGCAAAGTAAAAGTA +TAACACTTTCCTTACTAAACCGACATGGGTTTCCAGGTAGGGGCAGGATTCAGGATGACTGACAGGGCCCTTAGGGAACA +CTGAGACCCTACGCTGACCTCATAAATGCTTGCTACCTTTGCTGTTTTAATTACATCTTTTAATAGCAGGAAGCAGAACT +CTGCACTTCAAAAGTTTTTCCTCACCTGAGGAGTTAATTTAGTACAAGGGGAAAAAGTACAGGGGGATGGGAGAAAGGCG +ATCACGTTGGGAAGCTATAGAGAAAGAAGAGTAAATTTTAGTAAAGGAGGTTTAAACAAACAAAATATAAAGAGAAATAG +GAACTTGAATCAAGGAAATGATTTTAAAACGCAGTATTCTTAGTGGACTAGAGGAAAAAAATAATCTGAGCCAAGTAGAA +GACCTTTTCCCCTCCTACCCCTACTTTCTAAGTCACAGAGGCTTTTTGTTCCCCCAGACACTCTTGCAGATTAGTCCAGG +CAGAAACAGTTAGATGTCCCCAGTTAACCTCCTATTTGACACCACTGATTACCCCATTGATAGTCACACTTTGGGTTGTA +AGTGACTTTTTATTTATTTGTATTTTTGACTGCATTAAGAGGTCTCTAGTTTTTTATCTCTTGTTTCCCAAAACCTAATA +AGTAACTAATGCACAGAGCACATTGATTTGTATTTATTCTATTTTTAGACATAATTTATTAGCATGCATGAGCAAATTAA +GAAAAACAACAACAAATGAATGCATATATATGTATATGTATGTGTGTATATATACACACATATATATATATATTTTTTCT +TTTCTTACCAGAAGGTTTTAATCCAAATAAGGAGAAGATATGCTTAGAACCGAGGTAGAGTTTTCATCCATTCTGTCCTG +TAAGTATTTTGCATATTCTGGAGACGCAGGAAGAGATCCATCTACATATCCCAAAGCTGAATTATGGTAGACAAAACTCT +TCCACTTTTAGTGCATCAACTTCTTATTTGTGTAATAAGAAAATTGGGAAAACGATCTTCAATATGCTTACCAAGCTGTG +ATTCCAAATATTACGTAAATACACTTGCAAAGGAGGATGTTTTTAGTAGCAATTTGTACTGATGGTATGGGGCCAAGAGA +TATATCTTAGAGGGAGGGCTGAGGGTTTGAAGTCCAACTCCTAAGCCAGTGCCAGAAGAGCCAAGGACAGGTACGGCTGT +CATCACTTAGACCTCACCCTGTGGAGCCACACCCTAGGGTTGGCCAATCTACTCCCAGGAGCAGGGAGGGCAGGAGCCAG +GGCTGGGCATAAAAGTCAGGGCAGAGCCATCTATTGCTTACATTTGCTTCTGACACAACTGTGTTCACTAGCAACCTCAA +ACAGACACCATGGTGCATCTGACTCCTGAGGAGAAGTCTGCCGTTACTGCCCTGTGGGGCAAGGTGAACGTGGATGAAGT +TGGTGGTGAGGCCCTGGGCAGGTTGGTATCAAGGTTACAAGACAGGTTTAAGGAGACCAATAGAAACTGGGCATGTGGAG +ACAGAGAAGACTCTTGGGTTTCTGATAGGCACTGACTCTCTCTGCCTATTGGTCTATTTTCCCACCCTTAGGCTGCTGGT +GGTCTACCCTTGGACCCAGAGGTTCTTTGAGTCCTTTGGGGATCTGTCCACTCCTGATGCTGTTATGGGCAACCCTAAGG +TGAAGGCTCATGGCAAGAAAGTGCTCGGTGCCTTTAGTGATGGCCTGGCTCACCTGGACAACCTCAAGGGCACCTTTGCC +ACACTGAGTGAGCTGCACTGTGACAAGCTGCACGTGGATCCTGAGAACTTCAGGGTGAGTCTATGGGACGCTTGATGTTT +TCTTTCCCCTTCTTTTCTATGGTTAAGTTCATGTCATAGGAAGGGGATAAGTAACAGGGTACAGTTTAGAATGGGAAACA +GACGAATGATTGCATCAGTGTGGAAGTCTCAGGATCGTTTTAGTTTCTTTTATTTGCTGTTCATAACAATTGTTTTCTTT +TGTTTAATTCTTGCTTTCTTTTTTTTTCTTCTCCGCAATTTTTACTATTATACTTAATGCCTTAACATTGTGTATAACAA +AAGGAAATATCTCTGAGATACATTAAGTAACTTAAAAAAAAACTTTACACAGTCTGCCTAGTACATTACTATTTGGAATA +TATGTGTGCTTATTTGCATATTCATAATCTCCCTACTTTATTTTCTTTTATTTTTAATTGATACATAATCATTATACATA +TTTATGGGTTAAAGTGTAATGTTTTAATATGTGTACACATATTGACCAAATCAGGGTAATTTTGCATTTGTAATTTTAAA +AAATGCTTTCTTCTTTTAATATACTTTTTTGTTTATCTTATTTCTAATACTTTCCCTAATCTCTTTCTTTCAGGGCAATA +ATGATACAATGTATCATGCCTCTTTGCACCATTCTAAAGAATAACAGTGATAATTTCTGGGTTAAGGCAATAGCAATATC +TCTGCATATAAATATTTCTGCATATAAATTGTAACTGATGTAAGAGGTTTCATATTGCTAATAGCAGCTACAATCCAGCT +ACCATTCTGCTTTTATTTTATGGTTGGGATAAGGCTGGATTATTCTGAGTCCAAGCTAGGCCCTTTTGCTAATCATGTTC +ATACCTCTTATCTTCCTCCCACAGCTCCTGGGCAACGTGCTGGTCTGTGTGCTGGCCCATCACTTTGGCAAAGAATTCAC +CCCACCAGTGCAGGCTGCCTATCAGAAAGTGGTGGCTGGTGTGGCTAATGCCCTGGCCCACAAGTATCACTAAGCTCGCT +TTCTTGCTGTCCAATTTCTATTAAAGGTTCCTTTGTTCCCTAAGTCCAACTACTAAACTGGGGGATATTATGAAGGGCCT +TGAGCATCTGGATTCTGCCTAATAAAAAACATTTATTTTCATTGCAATGATGTATTTAAATTATTTCTGAATATTTTACT +AAAAAGGGAATGTGGGAGGTCAGTGCATTTAAAACATAAAGAAATGAAGAGCTAGTTCAAACCTTGGGAAAATACACTAT +ATCTTAAACTCCATGAAAGAAGGTGAGGCTGCAAACAGCTAATGCACATTGGCAACAGCCCCTGATGCATATGCCTTATT +CATCCCTCAGAAAAGGATTCAAGTAGAGGCTTGATTTGGAGGTTAAAGTTTTGCTATGCTGTATTTTACATTACTTATTG +TTTTAGCTGTCCTCATGAATGTCTTTTCACTACCCATTTGCTTATCCTGCATCTCTCAGCCTTGACTCCACTCAGTTCTC +TTGCTTAGAGATACCACCTTTCCCCTGAAGTGTTCCTTCCATGTTTTACGGCGAGATGGTTTCTCCTCGCCTGGCCACTC +AGCCTTAGTTGTCTCTGTTGTCTTATAGAGGTCTACTTGAAGAAGGAAAAACAGGGGTCATGGTTTGACTGTCCTGTGAG +CCCTTCTTCCCTGCCTCCCCCACTCACAGTGACCCGGAATCTGCAGTGCTAGTCTCCCGGAACTATCACTCTTTCACAGT +CTGCTTTGGAAGGACTGGGCTTAGTATGAAAAGTTAGGACTGAGAAGAATTTGAAAGGCGGCTTTTTGTAGCTTGATATT +CACTACTGTCTTATTACCCTGTCATAGGCCCACCCCAAATGGAAGTCCCATTCTTCCTCAGGATGTTTAAGATTAGCATT +CAGGAAGAGATCAGAGGTCTGCTGGCTCCCTTATCATGTCCCTTATGGTGCTTCTGGCTCTGCAGTTATTAGCATAGTGT +TACCATCAACCACCTTAACTTCATTTTTCTTATTCAATACCTAGGTAGGTAGATGCTAGATTCTGGAAATAAAATATGAG +TCTCAAGTGGTCCTTGTCCTCTCTCCCAGTCAAATTCTGAATCTAGTTGGCAAGATTCTGAAATCAAGGCATATAATCAG +TAATAAGTGATGATAGAAGGGTATATAGAAGAATTTTATTATATGAGAGGGTGAAACCCTCAAAATGAAATGAAATCAGA +CCCTTGTCTTACACCATAAACAAAAATAAATTTGAATGGGTTAAAGAATTAAACTAAGACCTAAAACCATAAAAATTTTT +AAAGAAATCAAAAGAAGAAAATTCTAATATTCACGTTGCAGCCGTTTTTTGAATTTGATATGAGAAGCAAAGGCAACAAA +AGGAAAAATAAAGAAGTGAGGCTACATCAAACTAAAAAATTTCCACACAAAAAACAAAACAATGAACAAATGAAAGGTGA +ACCATGAAATGGCATATTTGCAAACCAAATATTTCTTAAATATTTTGGTTAATATCCAAAATATATAAGAAACACAGATG +ATTCAATAACAAACAAAAAATTAAAAATAGGAAAATAAAAAAATTAAAAAGAAGAAAATCCTGCCATTTATGGCAGAATT +GATGAACCTGGAGGATGTAAAACTAAGAAAAATAAGCCTGACACAAAAAGACAAATACTACACAACCTTGCTCATATGTG +AAACATAAAAAAGTCACTCTCATGGAAACAGACAGTAGAGGTATGGTTTCCAGGGGTTGGGGGTGGGAGAATCAGGAAAC +TATTACTCAAAGGGTATAAAATTTCAGTTATGTGGGATGAATAAATTCTAGATATCTAATGTACAGCATCGTGACTGTAG +TTAATTGTACTGTAAGTATATTTAAAATTTGCAAAGAGAGTAGATTTTTTTTTTTTTTTAGATGGAGTTTTGCTCTTGTT +GTCCAGGCTGGAGTGCAATGGCAAGATCTTGGCTCACTGCAACCTCCGCCTCCTGGGTTCAAGCAAATCTCCTGCCTCAG +CCTCCCGAGTAGCTGGGATTACAGGCATGCGACACCATGCCCAGCTAATTTTGTATTTTTAGTAGAGACGGGGTTTCTCC +ATGTTGGTCAGGCTGATCCGCCTGCCTCGGCCACCCAAAGGGCTGGGATTACAGGCGTGAGCCACCGGGCCTGGCCGAGA +GTAGATCTTAAAAGCATTTACCACAAGAAAAAGGTAACTATGTGAGATAATGGGTATGTTAATTAGCTTGATTGTGGTAA +TCATTTCACAAGGTATACATATATTAAAACATCATGTTGTACACCTTAAATATATACAATTTTTATTTGTGAATGATACC +TCAATAAAGTTGAAGAATAATAAAAAAGAATAGACATCACATGAATTAAAAAACTAAAAAATAAAAAAATGCATCTTGAT +GATTAGAATTGCATTCTTGATTTTTCAGATACAAATATCCATTTGACTGTTTACTCTTTTCCAAAACAATACAATAAATT +TTAGCACTTTATCTTCATTTTCCCCTTCCCAATCTATAATTATATATATATATATTTTAGATATTTTGTATAGTTTTACT +CCCTAGATTTTCTAGTGTTATTATTAAATAGTGAAGAAATGTTTACACTTATGTACAAAATGTTTTGCATGCTTTTCTTC +ATTTCTAACATTCTCTCTAAGTTTATTCTATTTTTTTCTGATTATCCTTAATATTATCTCTTTCTGCTGGAAATACATTG +TTACTTTTGGTTTATCTAAAAATGGCTTCATTTTCTTCATTCTAAAATCATGTTAAATTAATACCACTCATGTGTAAGTA +AGATAGTGGAATAAATAGAAATCCAAAAACTAAATCTCACTAAAATATAATAATGTGATATATAAAAATATAGCTTTTAA +ATTTAGCTTGGAAATAAAAAACAAACAGTAATTGAACAACTATACTTTTTGAAAAGAGTAAAGTGAAATGCTTAACTGCA +TATACCACAATCGATTACACAATTAGGTGTGAAGGTAAAATTCAGTCACGAAAAAACTAGAATAAAAATATGGGAAGACA +TGTATATAATCTTAGAGATAACACTGTTATTTAATTATCAACCCAAAGTAGAAACTATCAAGGGAGAAATAAATTCAG diff --git a/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/assets/demo/promoter_tp53.fa b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/assets/demo/promoter_tp53.fa new file mode 100644 index 0000000..d40e54e --- /dev/null +++ b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/assets/demo/promoter_tp53.fa @@ -0,0 +1,431 @@ +>human|TP53|ENSG00000141510|chr17:7661779-7687546|GRCh38|strand:-1|gene-sense +GGATTGGGGTTTTCCCCTCCCATGTGCTCAAGACTGGCGCTAAAAGTTTTGAGCTTCTCA +AAAGTCTAGAGCCACCGTCCAGGGAGCAGGTAGCTGCTGGGCTCCGGGGACACTTTGCGT +TCGGGCTGGGAGCGTGCTTTCCACGACGGTGACACGCTTCCCTGGATTGGGTAAGCTCCT +GACTGAACTTGATGAGTCCTCTCTGAGTCACGGGCTCTCGGCTCCGTGTATTTTCAGCTC +GGGAAAATCGCTGGGGCTGGGGGTGGGGCAGTGGGGACTTAGCGAGTTTGGGGGTGAGTG +GGATGGAAGCTTGGCTAGAGGGATCATCATAGGAGTTGCATTGTTGGGAGACCTGGGTGT +AGATGATGGGGATGTTAGGACCATCCGAACTCAAAGTTGAACGCCTAGGCAGAGGAGTGG +AGCTTTGGGGAACCTTGAGCCGGCCTAAAGCGTACTTCTTTGCACATCCACCCGGTGCTG +GGCGTAGGGAATCCCTGAAATAAAAGATGCACAAAGCATTGAGGTCTGAGACTTTTGGAT +CTCGAAACATTGAGAACTCATAGCTGTATATTTTAGAGCCCATGGCATCCTAGTGAAAAC +TGGGGCTCCATTCCGAAATGATCATTTGGGGGTGATCCGGGGAGCCCAAGCTGCTAAGGT +CCCACAACTTCCGGACCTTTGTCCTTCCTGGAGCGATCTTTCCAGGCAGCCCCCGGCTCC +GCTAGATGGAGAAAATCCAATTGAAGGCTGTCAGTCGTGGAAGTGAGAAGTGCTAAACCA +GGGGTTTGCCCGCCAGGCCGAGGAGGACCGTCGCAATCTGAGAGGCCCGGCAGCCCTGTT +ATTGTTTGGCTCCACATTTACATTTCTGCCTCTTGCAGCAGCATTTCCGGTTTCTTTTTG +CCGGAGCAGCTCACTATTCACCCGATGAGAGGGGAGGAGAGAGAGAGAAAATGTCCTTTA +GGCCGGTTCCTCTTACTTGGCAGAGGGAGGCTGCTATTCTCCGCCTGCATTTCTTTTTCT +GGATTACTTAGTTATGGCCTTTGCAAAGGCAGGGGTATTTGTTTTGATGCAAACCTCAAT +CCCTCCCCTTCTTTGAATGGTGTGCCCCACCCCGCGGGTCGCCTGCAACCTAGGCGGACG +CTACCATGGCGTGAGACAGGGAGGGAAAGAAGTGTGCAGAAGGCAAGCCCGGAGGTATTT +TCAAGAATGAGTATATCTCATCTTCCCGGAGGAAAAAAAAAAAGAATGGGTACGTCTGAG +AATCAAATTTTGAAAGAGTGCAATGATGGGTCGTTTGATAATTTGTCGGAAAAACAATCT +ACCTGTTATCTAGCTTTGGGCTAGGCCATTCCAGTTCCAGACGCAGGCTGAACGTCGTGA +AGCGGAAGGGGCGGGCCCGCAGGCGTCCGTGTGGTCCTCCGTGCAGCCCTCCGGCCCGAG +CCGGTTCTTCCTGGTAGGAGGCGGAACTCGAATTCATTTCTCCCGCTGCCCCATCTCTTA +GCTCGCGGTTGTTTCATTCCGCAGTTTCTTCCCATGCACCTGCCGCGTACCGGCCACTTT +GTGCCGTACTTACGTCATCTTTTTCCTAAATCGAGGTGGCATTTACACACAGCGCCAGTG +CACACAGCAAGTGCACAGGAAGATGAGTTTTGGCCCCTAACCGCTCCGTGATGCCTACCA +AGTCACAGACCCTTTTCATCGTCCCAGAAACGTTTCATCACGTCTCTTCCCAGTCGATTC +CCGACCCCACCTTTATTTTGATCTCCATAACCATTTTGCCTGTTGGAGAACTTCATATAG +AATGGAATCAGGCTGGGCGCTGTGGCTCACGCCTGCACTTTGGGAGGCCGAGGCGGGCGG +ATTACTTGAGGATAGGAGTTCCAGACCAGCGTGGCCAACGTGGTGAATCCCCGTCTCTAC +TAAAAAATACAAAAATTAGCTGGGCGTGGTGGGTGCCTGTAATCCCAGCTATTCGGGAGG +GTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCAGAGGTTGCAGTGAGCCAAGATCGTGC +CACTACACTCCAGCCTGGGCGACAAGAACGAAACTCCGTCTCAAAAAAAAGGGGGGAATC +ATACATTATGTGCTCATTTTTGTCGGGCTTCTGTCCTTCAATGTACTGTCTGACATTCGT +TCATGTTGTATATATCAGTATTTTGCTCCTTTTCATTTAGTATAGTCCATCGATTGTATA +TCCGTCCTTTTGATGGCCTTTTGAGTTGTTTCCCATTTGCGGTTATGAAATAAAGCTGCT +ATAAACATTCTTGTACAATTCTTTTTGTGATCATATGTTTTCGTGTTTCTTGGAGAAATA +CTTAGGAGGGGAATTGCGAGTTTGGAAGTAAAAAGTAGCTGTATTTTGAACTTTTTCAGA +AGCTCTGAGTTTTCCAGAGCGGTTGTACCATTTTACACTCCAACTAGCAAGGTATGGGAG +TTATTATGGTTGTGCCACAGCCTTCCGGACATTAGGTATTGTCAGTCTTTCTAATGTGGT +ATATCCTTGTGGTTGTAATTTACAGTTCTCTATTGACTAAGGATGTTCAGCATTTTTTCA +TGTGCCTATTGGCCATTCGTATTTTGTTTGTAAAGTAGCTCTTCGAGTCTTTTACCTGTT +ATTTTGGTTTTTTGTTTGTTTTTATTGTTCAGTTGTGGGACTGCTTTATACATTCTGGAT +ACAAGTCCTTTATCAGATCCATGTGTCGTGAATGTTTTCTTCTGATCTGTTGCTTGCCTA +TTTGTTTGCTTTACAGAGTTTACAGTATCTTAAGAGGAGTGGATTTATCTTTTTTATGTT +CAGTATTTGCCTTGTCCTGTTTAGGACATCTTTTTTTTTTTTTTTAACCCCAGGGTCATG +AAGATATTATCTTACATTTTCTTTTAGGACCTTTATGGTTGTAAGTTTTACAGTAAGGTC +CTTGAGCCATTAATTAATTCTTAAAATTAATTGTTTATGGTGTGAGGTGTAGGAGTCAGT +CTCTGGTATCTTTCCTGTATGGAAATCCAGTTATTCTGTCTCCACTTGTTGAAATAGGCT +TCCTTTCTCTACTGAATGCTTTTAATTTTAATTATTTTACAGTTGGAGTATAGGGCTACC +ATTTTAGTGCTATTTTCTTTTTTTCTTTGTTAATTTTTGAGACAGGGACTCACACTGTTG +CCCAGGCTAGAGTACAATGGCACAATCAAGGCTTACTGCAGCCTCGAACCCCTGGGCTCA +AGCAGTCCTCTAGCAGCCTCACGAGTAGCTGGGATTACTCCACCACACCCAGCTAACTAT +TTTATTTTTTTGTATTGACAGGATCTCACTATGTTGCCCAGGCTGGTCTCAAACTGCTGG +CCTCAAGCTTTCATCCCATCTCGGCCTCCCAAAGTGCTGGGATTACAGGTGTGAGCCACC +ATGCCTGACCTCTTAGTGCTATTTTCTATTTATCTCCTCTGTTCTCTGCTCTCTTTAAAC +GTTGGAGGAAGAAACAGTACCCATCTTACACAAACTCTTCAGAAAACAGAGGAACAGACT +GGGCGCGGTGGCTCATACCTGTAATCTCAGCACTTTGGTACGCTGAGGCAGGGGATCATT +TGAGGTCGGGAGTTCGAGACCAGCCTGGCCAACACGGCGAAACCCCATCTCTACTAAAAA +TACAAAAAGTAGCTAGGCGTGGTGACACATACCTGTAATGCCAGTTACTCAGGAGGCTGA +GGCACAAGAATCCCTTGAACCTGGGAAGCGGAGGTTGCAGTGAGCCGAGATTGCGCCACT +GCACTCCAGCCTGGGCAACAGAGTGAGACCCTGTCTCAGAAAAAAAAAGAAAGAAAGAAA +AAATAGAGGAATATTTCCCAACTTGTTTTCGAAGCCAGCATAATCCTGGTACCAAAACCA +AACAAGGACATTATAAGAAAAGAAAATATAGACCAATATTCCTGTTAGCATAGACATGCA +ACAGCTAACCAATTTTAGCAAACCAAACCTGGTAATATAGAAAAAAGGATAAATAGGCCA +GTCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCTGAGGCAGGCAGATCACT +TGAGGTCAGGAGTTTGAGACCAGCCTGACCAACATGGTGAAACCCCGTTTCTAATAAAAA +TACAAAAATCAGGCTGGGCACGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCG +AGGTGGGCAGATCACGAGGTCAGGAGTTCAAGACCAGCCTGACCAATGTGGTGAAACGCC +ATCTCTACTAAAAATACAAAAATCAGCCGGTGTGGTGGCACCTGCCTGTAATCCCAGCTA +CTCAGGAGGCTGAGGCAGAATTGCTTGAACCCGGGAGGCAGAGGTTGCAGTGAGCCAAGA +TCGTGCCACTGCACTCCAGCCTGGGCGACAGAGCAAGACTTCATCTCAAAAAAAAAAAAA +AATTAGCTGGGCATGGTGGTGGGCACCTGAAATCCCAGCTACTCGGGAGTCTGAGGCAGG +AGAATCGCTTGAACCCAGGAGGCAGAAGTTGCACTGAGCTGGGATCACACCATTGCACTC +CAGCCTGGGCAACAGAGTGAGACTCCATCTCAAAAAAAGAAAAAGAAAAAGGATAAATAC +ATTCTAACCAAATAATGTTTATCTCATGATTGTAGCTGATTCAACATTCAAAAATTGGCC +TGGTGCAGTAGCTCAGGCCTGTAATCCCAACATTTTAGGAGGCTGAGGCAGGAAGATCTC +TTGAGCCCAGGATTTCAAGACCAGCCTGGGCAACATAGTCAGACTGGTCTTTACTGGGGG +GAAAAAAATCAGTCTGTGTAATTCACCACATTAACAAAGGGAAACATAAAAACCCTATGA +TCATTTCAACAGATGTAGCAAAAGCAGTTAATGATATTCAACACATATGCATGATTACAA +ACCAACCAACCTCCTAGCAAACTAGGGAAAGGAAACTTAACCTAGTTTGATAACAGGGCG +TCCACAGTCGGAGTTCCACTAGCAGCATACATAATGGTAGAAAACTCAGTGCTGCCGGGC +GCGGTGGCTCACGCCTGTAATGCCAGCACTTTGGGAGGCCTAGGCGGGCGGATCACGAGG +TCAGGAGATCGAGACTGTCCTGACTAGCATGCTGAAACCCCGTCTCTACTAAAAATACAA +AAACAAAAAATTAGCCGGGCATGGTGGCGGGCGCCTATAGTCCCAGCTACTCGGGAGGCT +GAGGCGAGAGAATGGCGTGAACCCGGGAGGCGGAGCTTGCAGAGCCTAGATCGTGCCACT +GCACTCCAGCCTGGGTGACAGAGTGAGACTTCGTCTCAAAAAAAAAAAAAAAAAAAAAAG +AAAAGAAAACTCAACGCTTTTTCCTCTAAGATCAGGAACTAGAAAAGGATTTGACTCTCA +CAACGTTGATACCATACTGGAGGTTTTAACCAGGCAAGAAAAAGAAATAATGAGGGCCGG +GTGCGGTGGCTCAGGCCTGTAATCCCAGCACTTTGGGAAGCCGAGACGGGTGGATCACGA +GGTCAGGAGATCGAGACCATCCTGGCTAACACGGTGAAACCCTGTCTCTACTAAATATAC +AAAAAATTAGCCGGGCGTAGTGGCGGGCGCCTGTAGTCCCAGCTACTCGGGAGGCTGAGG +CAGGAGAATGGCGTGAACTCAGGGGGCGGAGCTTGCAGTGAGCTGAGATCGAGCCACTGC +ACTCCAGCCTGGGCGACAGAGCAAGACTGTGTCTCAAAAAAAAAAAAAGAAAAAGAAATA +ATGATTAGTGGCCCGATGTCTCACGCCTATAATCCCAGCACTTTGGGAGGCCGAGGTGGG +CAGATCACCTGAGGTCTGGAGTTGGAGACCAGCCTGACAAAGATGGTGAAACCTCGTCTC +TATTAAAATATTAAAAAAATAGCCAGGCGTTGGCCGGGTACAGTGGCTCATGCCTGTAAT +CCCAGCACTTTGGGAGGCCGAGGTGGGTGGATCACCTGAGGTCAGGAGTTCAACACCAGC +CTGGCCAACATGGTGAAACCCCATCTCTACTAAAAATACAAAAATTAGCCGGGCGTAGTG +GCGGGCGCCTGTAATCCCAGCTACTTGGGAGGCTTAGGCAGGAGAATCGCTTGAACCTGG +GAGGCGGAGGTTGTAGTGAGCCGAGATTGCACCATTGCACTCCAGCCTGGGTGACAAAAG +CAAAAACTCCGTCTCAAAAAAAAAAGAATTAGCCAGGGGTAGTGGTGAACGCCTGTAGTC +CCAGCTACTCAGGAGGCAGAGGCAGGAGAATCACTTGAACCCAGGAGGCAGAGGTTGCAG +TGAGCCGAGATTGTCCCATTGCACTCCAGCCTAGGCGACAAGAGCAAAATTCCATGTCAA +AAAAAAAAAAAAAAAAGGAAAGAAAAAAAATAACGATTAGAAAGGAAGAAATAAAACACA +TTCACAGCCAGTATGATTCTATACATACATGTCCTAATGGGGCCAGGCGTGGTGGCTCAT +GCCTGTAATCCTAGCACTTTTAGGAGGCTGAGGCAGGTGGCTTCCCTGGGACCAGCCTGG +CCAACATGGTGAAACCCCAACTCTAATAAAAATACAAAAAATCAGCCAGGCGTGGTGACG +GGCACCTCTAATCCCAGCTACTCAGGAGGCTGAGGCAGGAGAATTGCTTGGACCTGGGAG +GCAGAGGTTGCAGTGAGCCGAGATCGCGCTATTGCACTCCAGCCTGGGCAACAAGAGTGA +AACTCCGGCAGGGTGTGGTGGCTTACGCCTGTAATCCCAGCACTTCGGGAGGCTGAGGCA +GGCCGATCACCTGAGGTCAGGAGTTTGAGACCAACCTAACATGGTGAAACCCCGTCTCTA +CTAAAAATACAAGAATTAGCTGGGTGTAGTGGTGGGCGCCTGTAATCCCAGCTACTTGGG +AGGCTGAGACAGAAGAATTGCTTGAACCCAGGAGGTGGAGGTTGCAGTGAGCTGAGATCA +TGCCATTGCACACCACGCCGGGCAACAGAGCGAGATTCCGTCTCAAAAAAAAAAAAAAAG +AGTGAAACTCTATCTCAAAAAAAAAAAAAAGTCCTAATGGAAAATCCATAAAAAGCTACC +AAAACTAATAAATAAATATAGCAGGGTTGCAGGTTACAGGGCAATATAGTTATCCCTCTA +TCTGTAGGGGCTTGGTTCTGGGACTCCTCACACACCAAACCCACAGATGTCTAAGTCCCA +TATATAAGACGGTATAGTATTTGGATTTAACCTACACATATCCTCCCATATAGTTTAAAT +TATCTCTAGATTACTTACATTACCCCCATACAATGAAAATGCTAATGTACATGCAAGTAT +GTATGTAAGTACTTGTACTATATTGTTTAGGGAATCACTGGACATATAGGCCTTCAAGAC +TGATACCAGCAGCCACTGTTAAGATTCTGGTCAGGCCTGCCCCTGTTTGGGGTCTCAGTT +GATCTCATTGCCTTCCCACCCAGCCAAGGGCACCTGCATTTCTCTTGGCTCCCTGGCCAT +TTGGAAGGCCTAGTTCAGCCTGGCACATTTGTATCCTGGCCCACTGATGCTGGTACCCCT +GGGAAGGTCCTGCTCTGAAAAACACGGAGATTTTAGTTGCTACTGAAGATTTGAGAGATA +AAGACAGGGAGACCTGTCTGTAGACCTGTGTCCCTCCAAGTGGGATTGAGACTTTGGGCC +CCCCATTTCAGGACAGCACCTCCTGGCCTGTTGACTGAATAGATCCCTGAAGGAGGTGTA +CTTGCATTAATGGAGTGGGGGTGGGAGCAGTACCACAGATCCGCACTAACAATCACACAG +TTCTCTCTAGAATAATAATATAGAACAAGTGAAATAGAACAATTGCAGAAAGAGCTAACC +TTTGTTGAGCTCTTACTGTGTGCCCAGCACTTTCCTCAACTCTACATTTCCCATAATACA +CAGAGTACTAGGTAGGCCAGGCTTGGTGGCTCACGCCTGTAATCCCAGCACTTTAGGAGG +CCAAGGGGGGTGGATCACCTGAGGTCGGGAGTTCAAGACCAGCCTGACCAACATGGTGAA +ACCCCGTCTCTACTAGAAGTACAAAATTAGCCAGGTGTGGTGGCACATGCTTGTAGTCCT +AGCTACTCAGCAGGCTGAGGCAGGAGAATCATTTGAATCCGGGAGGAGGTTGCAGTAAGC +GGAGATAGTGCCACTGTACTCCAGCCTGGGCAATAAGAGCTGAGACTCCGTCTCAAAATA +AAATAAAATAAAATAAAATAAAATAAAATAAAATAAAAAAAGAAAAGAGCCTGCCATTAA +AGGAGCTGTTTGGTAGGGGATGTTTTGTCAGTGCAAACAACAGAAAAGTGGGCTGGGCAC +AGTGGTTCATGCCTGTAATCCCAGCACTTTGGGAGGCCAAGGCGGGCGGATCACCTGAAG +TTGGGAGTTCAAGACCAGCCTGACCAATATGGAGAAACCCCGTCTCTACTAAAAATACAA +AATTAGCCGGGCGCAGTGGCGCATGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGG +AGAATCGCTTGAACCTGGGAGGCAGAGGTTGCGGTGAGCCGAGATCGCACCATTGCACTC +CAGCCTGGACGAGAGCAAAACTCTGTCTCAAAAAAAAAAAAAAACAGAAAAGTGTAACAA +ACACTTACAGTAGGCATGTTTCTTAGCAAATCTGATGACAAATTTGGCATAAAGAAAGAG +AGCATCCCTGAAAAAAAAAAAAAGAAAAAGAAAGAGAGCATCCTGCCTGGGCAACATAGT +GAAACCCTGCCTCTACAAAAAAACTCAAAAATTGGCCGGGTGCAGTGGCTCACACCTGTA +ATCCCAGCACTTTGGGAGTCGGAGGCGGGAGGATCACCTGAGGTCAGGAGTTCGAAACCA +GCCTGGCCAACATGGCAAAACCCCATCTCTACTAAAAATACAAAAAATTAATCAGGCGCA +TTGGTGGGCGCCTGTAATCCCAGCTACTCAGGAAGTTGAGGCAAGAGGATCGCTTGAATC +TGGGAGGTGGAGGTTACAGTGAGTCGAGATCACACCACTGCACTCTAGCCTGGGTGACAG +GGCGAGACTCCGTCTCCAAAAAAAAAAAGAAAAAGAAAAAGACTAAAAAATTAGCCAGGC +AGGCCTCTGTGGTCCCAGCTACTTGGGAGGCTGAGGCAGGAGAATCACTGAGCCCAGGAG +TCCGAGGCTGTAGTGAGCCATGATTGCACCACTGTACCCTAGCTTGGGCAACAAAGCAAG +ACCCTGCCTCAAAAGAAAAAAGAAAGAAAGAAAGAACATGGCGGGCCAGGCACAGTGGCT +CACACCTGTAATCCCAGCGCTTTGAGAGGCCGAGGCAGGTGGATCACAAGGTCAGGAGTT +CCACACCAGCCTGGCCAACATGGTGAAACCCTGTCTCTACTAAAAATACAAAAAATCAGC +CAGGCATGGTGGCAGGGGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATTG +CTTGAAACCAGAAGGCAGAGGTTGCAGTGAGCCTAGACTGCACCACTGCACTCCAGCCTG +GGCGAAAAGAGCCAAACTCCATCTCAAAAAACAAACAAAAAAACAAAACAAAAGAAAACA +TGGCAAAGCCTTTGAAAGCTTGTCTGGGAGAAGGTGCGATGATAGTTGCATAACTTCGTG +CAAGATGCTGGTCCACACAGGGGCTGCCCCTTGCTCTTTCTCGCTCTCTTAACCTCTCAT +ATAACAGGCTTGTGTGTTATTCACATTTATTGAGCCCAAGCAGGTGCAAGGCATTGTGAT +CTAATACTTTGGTCAGCAAGACAACAAGATAGATCACTGCCCTGCCCTTAGGAAGTGTAT +ATGCTATTAGAGGAAACAGATAAAATAAACAAGGAAAAGTATCAGACAATGTAAGTGCTA +TGAGAATGCAAATGAGGTGATGTGAATTAAAATAGGATGACTTAAAGTCTGCACGGGAAG +GAGCCTACCCCCATGTTCCTGGCTAGCCAAGGAACCACCAGTTGATTAGCAGAGAAGGGC +AGCCAGTCTAGCTAGAGCTTTTGGGGAAGAGGGAGTGGTTGTTAAGAGATGAGATTAAAG +AAGCCGAGACGGGCCATTCGTGAGGGGTTTGTAATGCAGGGCTGAGGAGTGTCCGAAGAG +AATGGGCAGGTGAGCGGTGAGACAGTTGTTCTTCCAGAAGCTTTGCAGTGAAAGGAATCA +AAGAAATGGAGCCGTGTATCAGGTGGGGAAGGGTGGGGGCCAAGGGGGTGTCCTTCCCCA +TACAGAGATTGCAGGCTGAGAATGACTATATCCTTGTTAACAGGAGGTGGGAGCAGGGCA +CGGTAGCTCACACCTGTAATCTTGGCACTTTAGGAGGCTGAGGCGGGCCGATCACCTGAA +GTAAGGAGTTCGAGACCAGCCTGGCCAACATGCAAAGCCCTGTCTCTACTAAAAATACAA +AAATTAGCTGGGTGTGGTGGTACTCGCCTGTAATCCCAGCTACTCGGGAGACTGAGGCAG +GAGAATGGCTTGAACCCGGAAGGTAGAGGTTGCAGTGAGCTGAGATCATGCCACTGTGCT +CCAGCCTAGGTGACAGAGAGAGACTCCATCTCAAAAAAAAAAAAAAAATACAGGAAGGGA +GTTGGGAATAGGGTGCACATTTAGGAAGTCTTGGGGATTTAGTGGTGGGAAGGTTGGAAG +TCCCTCTCTGATTGTCTTTTCCTCAAAGAAGTGCATGGCTGGTGAGGGGTGGGGCAGGAG +TGCTTGGGTTGTGGTGAAACATTGGAAGAGAGAATGTGAAGCAGCCATTCTTTTCCTGCT +CCACAGGAAGCCGAGCTGTCTCAGACACTGGCATGGTGTTGGGGGAGGGGGTTCCTTCTC +TGCAGGCCCAGGTGACCCAGGGTTGGAAGTGTCTCATGCTGGATCCCCACTTTTCCTCTT +GCAGCAGCCAGACTGCCTTCCGGGTCACTGCCATGGAGGAGCCGCAGTCAGATCCTAGCG +TCGAGCCCCCTCTGAGTCAGGAAACATTTTCAGACCTATGGAAACTGTGAGTGGATCCAT +TGGAAGGGCAGGCCCACCACCCCCACCCCAACCCCAGCCCCCTAGCAGAGACCTGTGGGA +AGCGAAAATTCCATGGGACTGACTTTCTGCTCTTGTCTTTCAGACTTCCTGAAAACAACG +TTCTGGTAAGGACAAGGGTTGGGCTGGGGACCTGGAGGGCTGGGGACCTGGAGGGCTGGG +GGGCTGGGGGGCTGAGGACCTGGTCCTCTGACTGCTCTTTTCACCCATCTACAGTCCCCC +TTGCCGTCCCAAGCAATGGATGATTTGATGCTGTCCCCGGACGATATTGAACAATGGTTC +ACTGAAGACCCAGGTCCAGATGAAGCTCCCAGAATGCCAGAGGCTGCTCCCCCCGTGGCC +CCTGCACCAGCAGCTCCTACACCGGCGGCCCCTGCACCAGCCCCCTCCTGGCCCCTGTCA +TCTTCTGTCCCTTCCCAGAAAACCTACCAGGGCAGCTACGGTTTCCGTCTGGGCTTCTTG +CATTCTGGGACAGCCAAGTCTGTGACTTGCACGGTCAGTTGCCCTGAGGGGCTGGCTTCC +ATGAGACTTCAATGCCTGGCCGTATCCCCCTGCATTTCTTTTGTTTGGAACTTTGGGATT +CCTCTTCACCCTTTGGCTTCCTGTCAGTGTTTTTTTATAGTTTACCCACTTAATGTGTGA +TCTCTGACTCCTGTCCCAAAGTTGAATATTCCCCCCTTGAATTTGGGCTTTTATCCATCC +CATCACACCCTCAGCATCTCTCCTGGGGATGCAGAACTTTTCTTTTTCTTCATCCACGTG +TATTCCTTGGCTTTTGAAAATAAGCTCCTGACCAGGCTTGGTGGCTCACACCTGCAATCC +CAGCACTCTCAAAGAGGCCAAGGCAGGCAGATCACCTGAGCCCAGGAGTTCAAGACCAGC +CTGGGTAACATGATGAAACCTCGTCTCTACAAAAAAATACAAAAAATTAGCCAGGCATGG +TGGTGCACACCTATAGTCCCAGCCACTTAGGAGGCTGAGGTGGGAAGATCACTTGAGGCC +AGGAGATGGAGGCTGCAGTGAGCTGTGATCACACCACTGTGCTCCAGCCTGAGTGACAGA +GCAAGACCCTATCTCAAAAAAAAAAAAAAAAAAGAAAAGCTCCTGAGGTGTAGACGCCAA +CTCTCTCTAGCTCGCTAGTGGGTTGCAGGAGGTGCTTACGCATGTTTGTTTCTTTGCTGC +CGTCTTCCAGTTGCTTTATCTGTTCACTTGTGCCCTGACTTTCAACTCTGTCTCCTTCCT +CTTCCTACAGTACTCCCCTGCCCTCAACAAGATGTTTTGCCAACTGGCCAAGACCTGCCC +TGTGCAGCTGTGGGTTGATTCCACACCCCCGCCCGGCACCCGCGTCCGCGCCATGGCCAT +CTACAAGCAGTCACAGCACATGACGGAGGTTGTGAGGCGCTGCCCCCACCATGAGCGCTG +CTCAGATAGCGATGGTGAGCAGCTGGGGCTGGAGAGACGACAGGGCTGGTTGCCCAGGGT +CCCCAGGCCTCTGATTCCTCACTGATTGCTCTTAGGTCTGGCCCCTCCTCAGCATCTTAT +CCGAGTGGAAGGAAATTTGCGTGTGGAGTATTTGGATGACAGAAACACTTTTCGACATAG +TGTGGTGGTGCCCTATGAGCCGCCTGAGGTCTGGTTTGCAACTGGGGTCTCTGGGAGGAG +GGGTTAAGGGTGGTTGTCAGTGGCCCTCCAGGTGAGCAGTAGGGGGGCTTTCTCCTGCTG +CTTATTTGACCTCCCTATAACCCCATGAGATGTGCAAAGTAAATGGGTTTAACTATTGCA +CAGTTGAAAAAACTGAAGCTTACAGAGGCTAAGGGCCTCCCCTGCTTGGCTGGGCGCAGT +GGCTCATGCCTGTAATCCCAGCACTTTGGGAGGCCAAGGCAGGCGGATCACGAGGTTGGG +AGATCGAGACCATCCTGGCTAACGGTGAAACCCCGTCTCTACTGAAAAATACAAAAAAAA +ATTAGCCGGGCGTGGTGCTGGGCACCTGTAGTCCCAGCTACTCGGGAGGCTGAGGAAGGA +GAATGGCGTGAACCTGGGCGGTGGAGCTTGCAGTGAGCTGAGATCACGCCACTGCACTCC +AGCCTGGGCGACAGAGCGAGATTCCATCTCAAAAAAAAAAAAAAAAGGCCTCCCCTGCTT +GCCACAGGTCTCCCCAAGGCGCACTGGCCTCATCTTGGGCCTGTGTTATCTCCTAGGTTG +GCTCTGACTGTACCACCATCCACTACAACTACATGTGTAACAGTTCCTGCATGGGCGGCA +TGAACCGGAGGCCCATCCTCACCATCATCACACTGGAAGACTCCAGGTCAGGAGCCACTT +GCCACCCTGCACACTGGCCTGCTGTGCCCCAGCCTCTGCTTGCCTCTGACCCCTGGGCCC +ACCTCTTACCGATTTCTTCCATACTACTACCCATCCACCTCTCATCACATCCCCGGCGGG +GAATCTCCTTACTGCTCCCACTCAGTTTTCTTTTCTCTGGCTTTGGGACCTCTTAACCTG +TGGCTTCTCCTCCACCTACCTGGAGCTGGAGCTTAGGCTCCAGAAAGGACAAGGGTGGTT +GGGAGTAGATGGAGCCTGGTTTTTTAAATGGGACAGGTAGGACCTGATTTCCTTACTGCC +TCTTGCTTCTCTTTTCCTATCCTGAGTAGTGGTAATCTACTGGGACGGAACAGCTTTGAG +GTGCGTGTTTGTGCCTGTCCTGGGAGAGACCGGCGCACAGAGGAAGAGAATCTCCGCAAG +AAAGGGGAGCCTCACCACGAGCTGCCCCCAGGGAGCACTAAGCGAGGTAAGCAAGCAGGA +CAAGAAGCGGTGGAGGAGACCAAGGGTGCAGTTATGCCTCAGATTCACTTTTATCACCTT +TCCTTGCCTCTTTCCTAGCACTGCCCAACAACACCAGCTCCTCTCCCCAGCCAAAGAAGA +AACCACTGGATGGAGAATATTTCACCCTTCAGGTACTAAGTCTTGGGACCTCTTATCAAG +TGGAAAGTTTCCAGTCTAACACTCAAAATGCCGTTTTCTTCTTGACTGTTTTACCTGCAA +TTGGGGCATTTGCCATCAGGGGGCAGTGATGCCTCAAAGACAATGGCTCCTGGTTGTAGC +TAACTAACTTCAGAACACCAACTTATACCATAATATATATTTTAAAGGACCAGACCAGCT +TTCAAAAAGAAAATTGTTAAAGAGAGCATGAAAATGGTTCTATGACTTTGCCTGATACAG +ATGCTACTTGACTTACGATGGTGTTACTTCCTGATAAACTCGTCGTAAGTTGAAAATATT +GTAAGTTGAAAATGGATTTAATACACCTAATCTAAGGAACATCATAGCTTAGCCTAGCCT +GCTTTTTTTTTTTTTTTTTTTGGAGACAGAGTCTCACTCTGTCACCCAGGCTGGAGTGCA +GTGGCGGGATCTCGGCTCACTGCAACCTCCGCCTTCTGGGTTCAAGCGATTCTCCTGCCT +CAGCCCACTGAGTAGCTGGGATTACAGGCACCTGCCCCGACGCCCAGCTAATTTTTTGTT +ATTTATTTATTTTTTTTTTTAGTAGAGATGAGGTTTCACCATGTTGGCCAGGCTAGTCTC +GAACTCCTGACCTTGTGATCTGCCTGCCTTGGCCTCCCAAAGTGCTGGGATTACAGGCGT +GAGCCACCGCACCCGGCCTGCCTAGCCTACTTTTATTTTATTTTTAATGGAGACAGCATC +TTGCTCTGTTGCCCAGGCTGGATTACAGTGATGTGATCATAGCTCATTATACCCTCCTGG +GCTCAAGCAATCCCCCTAACTCTGCCTCCCCAGTAGCTAGGACCACAGGCATACACCACC +ATACCCAGCTAATTTTTAAAATTTTTTGTAGATAGATAGAGTCTCACTATGTTGCCCAGG +CTGGTCTCTAGCCTACTTTTTTGAGACAAGGTCTTGCTCTGTCACCCAGGCTGGATAGAG +TGCAGTAGTGCAGTCACAGCTCACTGCAGCCTCCACCTCCCAGGCTCCATCCATCCTCCC +AGCTCAGCCTCCCAAGTTGCTTCAACTACAGGCCTGCACCACCATGCCTGGCTAATTTTT +ATTTATTTATTTTTATTTTATTTTATTTTATTTTTTTGAGACTCAGTCTCACTCTGTCGC +CCAGGCTGGAGTGCAGTGGCATGATCTCGGCTCACTGCAACCTCTGCCTCCTGGGTTCAA +GTGATTCTCCTGCCTCAGCCTCCCGAATAGCTAGGACTACAAGCGCCTGCTACCACGCCC +AGCTAATTTTTGTATTTTTAGTAGAGACAGGGTTTCACCATGTTGGCCAGGCTGGTCTCG +AACTTCTGACCATGTGATCCGCCCGCCTCGGCCTCCCAAAGTGCTGGGATTACAGGTGTG +AGCCACCACGCCCGGCTAATTTTTATTTATTTATTTAAAGACAGAGTCTCACTCTGTCAC +TCAGGCTAGAGTGCAGTGGCACCATCTCAGCTCACTGCAGCCTTGACCTCCCTGGGCTCC +GGTGATTTCACCCTCCCAAGTAGCTAGGACTACAGGCACATGCCACGACACCCAGCTAAT +TTTTTATTTTCTGTGAAGTCAAGGTCTTGCTACGTTGCCCATGCTGGTATCAAACCCCTG +GGCTCAATCAATCCTTCCACCTCAGCCTCCCCAAGTATTGGGGTTACAGGCATGAGCTAC +CACACTCAGCCCTAGCCTACTTGAAACGTGTTCAGAGCATTTAAGTTACCCTACAGTTGG +GCAAAGTCATCTAACACAAAGCCCTTTTTATAGTAATAAAATGTTGTATATCTCATGTGA +TTTATTGAATATTGTTACTGAAAGTGAGAAACAGCATGGTTGCATGAAAGGAGGCACAGT +CGAGCCAGGCACAGCCTGGGCGCAGAGCGAGACTCAAAAAAAGAAAAGGCCAGGCGCACT +GGCTCACGCCTGTAATCCCAGCATTTCGGGAGGCTGAGGCGGGTGGATCACCTGAGGTCA +GGAGTTCAAGACCAGCCTAGCCAACATGGTGAAACCCCGTCTCTACTAAAATACAAAAAT +TAACCGGGCGTGATGGCAGGTGCCTGTAATCCCAGCTACTTGGGAGGCTGAGGCAGGAGA +ATCGCTTGAACCAGGAGGCGGAGGTTGCAGGGAGCCAAGATGGCGCCACTGCACTCCAGC +CTGGGCGATAGAGTGAGACTCCGTCTCAGAAAAAAAAGAAAAGAAACGAGGCACAGTCGC +ATGCACATGTAGTCCCAGTTACTTGAGAGGCTAAGGCAGGAGGATCTCTTGAGCCCAAGA +GTTTGAGTCCAGCCTGAACAACATAGCAAGACATCATCTCTAAAATTTAAAAAAGGGCCG +GGCACAGTGGCTCACACCTGTAATCCCAGCACTTTGGGAGGTGGAGGTGGGTAGATCACC +TGACGTCAGGAGTTGGAAACCAGCCTGGCTAACATGGTGAAGCCCCATCTCTACTAAAAA +CACAAAAATTAGCCAGGTGTGGTAGCACACGCCTGTAGTCCCAGCTACTCGGGAGGCTGA +GGCACAAGAATCACTTGAACCCCAGAGGCGGAGATTGCAATCAGCCAAGATTGCACCATT +GCACTCCCGCCTGGGCAACAGAGTGAGACCCCATCTCAAAATAAATAAATAAATATTTTT +AAAAGTCAGCTGTATAGGTACTTGAAGTGCAGTTTCTACTAAATGCATGTTGCTTTTGTA +CCGTCATAAAGTCAAACAATTGTAACTTGAACCATCTTTTAACTCAGGTACTGTGTATAT +ACTTACTTCTCCCCCTCCTCTGTTGCTGCAGATCCGTGGGCGTGAGCGCTTCGAGATGTT +CCGAGAGCTGAATGAGGCCTTGGAACTCAAGGATGCCCAGGCTGGGAAGGAGCCAGGGGG +GAGCAGGGCTCACTCCAGGTGAGTGACCTCAGCCCCTTCCTGGCCCTACTCCCCTGCCTT +CCTAGGTTGGAAAGCCATAGGATTCCATTCTCATCCTGCCTTCATGGTCAAAGGCAGCTG +ACCCCATCTCATTGGGTCCCAGCCCTGCACAGACATTTTTTTAGTCTTCCTCCGGTTGAA +TCCTATAACCACATTCTTGCCTCAGTGTATCCACAGAACATCCAAACCCAGGGACGAGTG +TGGATACTTCTTTGCCATTCTCCGCAACTCCCAGCCCAGAGCTGGAGGGTCTCAAGGAGG +GGCCTAATAATTGTGTAATACTGAATACAGCCAGAGTTTCAGGTCATATACTCAGCCCTG +CCATGCACCGGCAGGTCCTAGGTGACCCCCGTCAAACTCAGTTTCCTTATATATAAAATG +GGGTAAGGGGGCCGGGCGCAGTGGCTCACGAATCCCACACTCTGGGAGGCCAAGGCGAGT +GGATCACCTGAGGTCGGGAGTTTGAGCCCAGCCTGACCAACATGGAGAAACCCCATCTCT +ACTAAAAATACAAAAGTAGCCGGGCGTGGTGATGCATGCCTGTAATCCCAGCTACCTACT +CGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCAGAGGTTGCGGTGAGCTGAG +ATCTCACCATTACACTCCAGCCTGGGCAACAAGAGTGAAACTCCGTCTCAAAAAAGATAA +ATAAAGTAAAATGGGGTAAGGGAAGATTACGAGACTAATACACACTAATACTCTGAGGTG +CTCAGTAAACATATTTGCATGGGGTGTGGCCACCATCTTGATTTGAATTCCCGTTGTCCC +AGCCTTAGGCCCTTCAAAGCATTGGTCAGGGAAAAGGGGCACAGACCCTCTCACTCATGT +GATGTCATCTCTCCTCCCTGCTTCTGTCTCCTACAGCCACCTGAAGTCCAAAAAGGGTCA +GTCTACCTCCCGCCATAAAAAACTCATGTTCAAGACAGAAGGGCCTGACTCAGACTGACA +TTCTCCACTTCTTGTTCCCCACTGACAGCCTCCCACCCCCATCTCTCCCTCCCCTGCCAT +TTTGGGTTTTGGGTCTTTGAACCCTTGCTTGCAATAGGTGTGCGTCAGAAGCACCCAGGA +CTTCCATTTGCTTTGTCCCGGGGCTCCACTGAACAAGTTGGCCTGCACTGGTGTTTTGTT +GTGGGGAGGAGGATGGGGAGTAGGACATACCAGCTTAGATTTTAAGGTTTTTACTGTGAG +GGATGTTTGGGAGATGTAAGAAATGTTCTTGCAGTTAAGGGTTAGTTTACAATCAGCCAC +ATTCTAGGTAGGGGCCCACTTCACCGTACTAACCAGGGAAGCTGTCCCTCACTGTTGAAT +TTTCTCTAACTTCAAGGCCCATATCTGTGAAATGCTGGCATTTGCACCTACCTCACAGAG +TGCATTGTGAGGGTTAATGAAATAATGTACATCTGGCCTTGAAACCACCTTTTATTACAT +GGGGTCTAGAACTTGACCCCCTTGAGGGTGCTTGTTCCCTCTCCCTGTTGGTCGGTGGGT +TGGTAGTTTCTACAGTTGGGCAGCTGGTTAGGTAGAGGGAGTTGTCAAGTCTCTGCTGGC +CCAGCCAAACCCTGTCTGACAACCTCTTGGTGAACCTTAGTACCTAAAAGGAAATCTCAC +CCCATCCCACACCCTGGAGGATTTCATCTCTTGTATATGATGATCTGGATCCACCAAGAC +TTGTTTTATGCTCAGGGTCAATTTCTTTTTTCTTTTTTTTTTTTTTTTTTCTTTTTCTTT +GAGACTGGGTCTCGCTTTGTTGCCCAGGCTGGAGTGGAGTGGCGTGATCTTGGCTTACTG +CAGCCTTTGCCTCCCCGGCTCGAGCAGTCCTGCCTCAGCCTCCGGAGTAGCTGGGACCAC +AGGTTCATGCCACCATGGCCAGCCAACTTTTGCATGTTTTGTAGAGATGGGGTCTCACAG +TGTTGCCCAGGCTGGTCTCAAACTCCTGGGCTCAGGCGATCCACCTGTCTCAGCCTCCCA +GAGTGCTGGGATTACAATTGTGAGCCACCACGTCCAGCTGGAAGGGTCAACATCTTTTAC +ATTCTGCAAGCACATCTGCATTTTCACCCCACCCTTCCCCTCCTTCTCCCTTTTTATATC +CCATTTTTATATCGATCTCTTATTTTACAATAAAACTTTGCTGCCACCTGTGTGTCTGAG +GGGTGAACGCCAGTGCAGGCTACTGGGGTCAGCAGGTGCAGGGGTGAGTGAGGAGGTGCT +GGGAAGCAGCCACCTGAGTCTGCAATGAGTGTGGGCTGGGGGGCCCAGTGCCCGGGTTCC +GGGAGGGGAACAAAGGCTGGAGACTGGGTCAGTCTGCGGGCTGCATGACAACAAGGGAGG +GGGTGGCTCCATTCATAACTCAGGAACCAACCGTCCCTCCTCCCCTCCGGCCACGGCTGG +CACAAGGTTCTCTCCCTCCCCTGCTTCTAGGACTGGGCTGCTTCCCCCTCGGCAGCCTCT +CACCAAGGATTACGGGATTTAAATGTCTGATTTAGCAAGGCTGAGCCTCCAGGGTGGCCA +TCTGCTCCATCAGAAAGTGGCAGGATACCTGGGTTCCCAAGGGGAACAGGGGTGGGTGCT +ACTGGATGGAGAGAGGCCAGTGGGAGGCCTGCTAGCCAGGGTCCCAGGAAAGTGGGGGCA +GCTAAGGTAAGAGTAGGGGTGTGGGGCTAGGTCCTTCCCAGCATCCCCTCATCCTGGGCC +TCATGCCAGGTAGCTGAATGAATTGAAGCTTTAAACTCTGCCAGGAAAACCTTTCAAAGG +GCTTCTTGGGATAGGGAGGAGAGTCGGGTTGAGGAGCTCAGTACTGCCTGCCCATGCTCC +TCAGGGCTGCTGGCTCCCAGGGAGGGGGGCTGGGAGCAGGCAGGCTCTTCCCCATCACCC +ACTGCTCTCTTGGAGCCAGTGCTTGAAGGGGCAGTCAGACATGGCTTGCCCTTCCTCCTC +CCTGGTGGTGGAGATGGGTGTTAGGGTCCAGTGGGTGCTACTGTCCAGGGGGGCTTCTGG +GGCCACCAGCCTGTCAGCTCATCAACCAGGCTGAAGGTGCAAGCAGGAGCCCCTTGCCTT +GCCCCAAGGATCCCAGACAGCTATGAAGCCACCAGCCTTCCTGACCTCAAGACCACCTTT +TTTTTTTCTCTTTCTTACTAGGGAATGCCAAACACTCTCCCCAGGAGATCCAGACCCGCC +TCTTTCAGAGACTTTTAACTTAAACATCTGTCCCTACCCAGCAGGCAAACTAGAGCTCCT +GAAGCTCAGTCCCTGTCCTTGCCTCTGTAGACAGGTCACCTTGATGAGCTTCCTTTTTTT +TTTTTTAATTTTTTTTTATTTTAGGCTTTATTGGGGCATAATTGATCCCCCAAAATTGCA +TACATTCAAGGTATGCAGTGTGATGATTTGATATGGGGGTATATTGTGAAACCATTACCA +CAATCAAATTAATCAGCACGTCCATCATCACACACAGTTACCATTTGTGTGTGTGCACGT +GTGTTCACCTACGACGAGGACACTTGGACCTACTCTGCAGATCTCAAGTAAACAGAAAAT +CTCCCTTTTTGACAACCATCCTCCACCCTTTCAATCCCAACCTTTTCCTAGATTATGTCC +CTAGCTCTGTTTTTATTTCTGCTGTGCTGCTTCAGATCCATTCTGACTCTGCCAAACCCT +TCTTTGTGAGCTGATAGATTGCTGGATTGAGAATTACAGCTGGGCGCGGTGGCTCACGCC +TGTAATCCCAACACTGTGGGAGGCCAAGGCCGGCGGATCACTTGAGGTCAGGAGTTGGAG +ACCAGCCTGACCAACAAGATGAAACCCCATCTCTACTAAAAATACAAAATTAGCTGGGCA +TGGTGGTGCACGCCTGTAATCTCATCTTCTTGGGAGGCTGAGGCAGGAGAATTGCTTGAA +CCCGGGAGGTGGAGGTTGCAGTGAGCCAAGATCCTGCCATTGCACTCCAGCCTGGGCAAC +AACAGTGAAGCTCCATCTCAAAACACACAAAAAAAAGAAGTACAAAGTCTGAGACTTCAG +GCCAGCTCTGCTACACTATATACTCTAACCTCTCTGGTCCTACTTGGTGACTTCTTTCCC +TCTGGTCGTGTTCAAGTTCCCGTCCCATCCAGTCAAGCAGGTACTCATTGGTACCTTACC +CTGTGCCAGGAGCTGTTCTAGGCCCTGGAAACCTATGGCAGACATGTTCCCTACCCTCCC +ACTCAAAGAGCCCAGGCCTTATCCTAATGAGATCTGAAATCAAATCTCCCAATTTCCTCA +TGGCTTCAGTCTAAACTTGTAATTCACAACCTTAAATCAATATGTTCTATTTTTTTATTT +AGAAAACATTTCCGGCCAGGCACGGTGGATCACACCTGTAATCCCAGCTACTCGGGAGGC +TGAGGCAGGAGAATCGCTTGAACCCAGGAGGCAGAGGGTTGCAGTGAGCCGAGATTGCGC +CATTGCACTCTAGCCTGGGCAACAGAGCAAGACTCCATCTCAAAAAAGAAAAAAAAATGG +AAGAAAAAAAAATTTCCCCCTCATTTTAGGAACACGAGGTCTCCAAATCTAAAATTCGTA +CTCTGAGGAGATTGAATAGCCTTAAATGCTTTCATCATTAAAAAGAAAAGAAAGGAACCT +GGTATGCATCCTAAAAATGAAAAATATACCTACCTGTAATCCCAGCACACAGCACATTGG +GAGGCTAAAGCAGGAGGATAACTTGAGGCCAGGAGTTTCAGATCAGCCTGGGCAACATAG +CAACACCCCATTTCTTTTTCTTTTCTTTTTTTTTTGGAGACACAGTCTCGCTCTGTTACT +CAGGCTGGAGTGCAGTGGCTCAATCTCAGCTCACTGCAAGCTCTGCCTCCCAGGTTCATG +CCATTCTCCTGCCTCAGCCTCCCGAGTAGCTGGGACTACAGGCGCCCGCCACCACGCCTG +GCTAATTTTTTGTATTTTTAGTAGAGACAGGGTTTCACCGTGTTAGCCAGGATGGTCTCG +ATCTCCTGACCTCGTGATCCGCCAGCCTTGGCCTCCTAAAGTACTGGGATTACAGGCGTG +AGCCACTGCGCCTGGCCACAACACCCCATTTCTATTTTAATAAAATAAAATACTGTGAAA +AACATTTACAATTTTTAAATTTTAATTTTAAAATTAAACTTATATTTATTCATTTGTGTG +TGTGGGTTTTTTTTTTTTTTTTTTTTTGCTTTTTTTTTGAGATGGAGTGTCACTCTGTCA +CCCAGGCTGGAGTGCAGTGGCGTGATCTCTGCCTCCCGGTTCAAGTGATTCTCCTGCCAT +AGCCTCCCAAGTAGCTGGGACTACAGGTACACGCCACCACGCCGGGTTAATTTTTGTATT +TTTAGTAGAGACAGGATTTCACTGTGTCGCCAGGCTAGCCTCGAACTCCTGACCTCAGGT +GATTCGCCCACCTTGGCCTCCCAAAGTGCTGTGATTACAAGCGTGAGCCACCGTGCCCAG +CCCAAAGTTGGTTTTAATAGCAGAAAATCTATCAACATAATTCAATATATTAAATTTAGA +AAGAAAAATTATCTATCATATCAACAGATACTGAAAGGAATTTGATTAAATTTCAGTAGC +CATTTCCTTAAAAAAGAAAACACTTTAACACAGTAATAGACTGATAATGGAATACCAATT +TTCCTAATAAGTTAAACATTAAGATAATTTCAATTAAGGTCAAGAGCTGGGCCAGGTGCA +GTGGCTCACACCTGTAATCCCAACACTTTGGAGGCCAAGGTGGGTGGATCACCTGAGGTC +AGGAGTGGAGACCAGCCTGGCTGACAATAGTGAAATCCTGCCTCTACTAAAAACACAAAA +AATTAGCTGGGCATGGTGGTGGGCACCTATAATCCCAGCTACTGGGAAGGCTGAGACAGG +AGAATTGCTTGAACCTGGGAGGCGGAGGTTGCAGTGAGCAAAGATCACACCATTGCACTC +CAGCCTGGGCGACAGAGCCAGAGTCAGTCTCAAAAAAAAAAAGAGGTGGCCACACCTATA +ATCCAAACATTTTGTGAGGCCAAGGCAGGAGAATTGCTTCAGGCCAAGAGTTGAACACCT +CGTCAACATAGCCAGACCTCTCTCTAGATAGATAGATAGATGATAGATAGAGAGATAGAT +AGATGATAGATAGAGAGATAGATAGATGATAGATAGATAGATAGATAGATAGATAGATAG +ATAGATAGATAGATAGATAGATAGATAATCTGGCCGGGTGTGGAGGCTCACGCCTGTAAT +CCCAGCACTTTGGGAGGCTGAGGCGGGCAGATCACGAGGACAAGAGATTGAAACCATCCT +GGCTAACAAGGTGAAACCCCGTCTCTACTAAAAATACAAAAAATTAGGCGGGTGTGGTGG +CACGCGCCTGTAGTCCTAGCTATTCAGGAGGCTGAGACAGGAGAATTGCTTGAATCCGAA +AGGCGGAGGTTGCAGCGAGCCGAGATCGTGCCACTGCACTCCAGCCTGGGTGACAGAGCA +AGACTCCATCTCAAAATAAATAAATAAATAATCAAGAACAGTATAAGGGGCTGTATGGTG +GCTCATGCCTGTGATCCCAGCACTTTGGGAGGCCAAGGTGGGAGGATCCCTTGAGACCAG +CCCAGGCAACAGAGAAAGACCCTGTCTCTATTTAAAAAAATTAAAAACTGGCCGGGCACG +GTGGCTCACGCCTGTAATTCCAGCGCTTGGGAGGCCAAGGCAGGCACATCAGGAGGTCAG +GAGTTCGAGACCAGCCTGGCCAACGTGGTGAAACCCCGTCTCTACTAAAAATACAAAAAG +TAGCTAGGCGTGGTGGCAGGCACCTGTAATCCCAGCTACTTGGGAGGCTGAGGCAGGAGA +ATCGCTTGAACCCAGGAGGCGGAGGTTGCAGTGGGCAAAGATCGTGCCATTGCACTCAGC +CTGGGTGACAGGGCAAGACTCCATCTCAAAATAAATAAACAAAGTAATTAATTAATTAAA +TTAAAAACTGTGGGGATATAGACTTACTCTGGTTTTATTTTTTCTTTTCTTTTCTTTTCT +TTTTTCTGAGACGGAGTCTCGCTCTGTTGCCCAGGCTGGAGTACAGTGGCGTGGTTTCTG +TTCTCTGCAACCTCCACCTCCCGGATTCAAGCGATTCTCTTGCCTCAGCCTCTTGAATAC +CTGGAATTACAGGTGCCTGCCACCACCCCCGGCTAATTTTTTGTATTTTTAGTAGAGACA +GGGTTTCACCATGTTGGCCAAGCTGGTCTCGAACTCCTGACCTCATGATCCACCCGCCTC +TGCCTCCCAAAGCACTGAGACTACAGGAGTGAGCCACTGTGCCCAGCCTACTCTGGTTTT +AGTGCATTCAAGAGGAACAAAAAAGGAAGAAAATCACTAGTAAATATACCTCTTTCTGGT +TAGAGTGGATGTTTGGAAATTATATATATATTATATTATATTATATATATTATATATATA +CACAAACACGTACATACATGCACACACATATATGCCTTTTTGATTATAGGATAGTATACC +AAAACTCAGAAATATTATGGAATTAACAGAATTTAGTAAGGCAGATAAGTAGTAGGTAGA +AAAATATTAATTTTATCTTCCAGCAGAAGCACTGTGAAAAATTAGACAACAAGAAAACAT +TCCATTCAAAATAATGACAATAAGGCCGGGCATGGTGGCTCACACCTGTAATCCCAGCAC +TTTGGGAGGCTGAGGCAGGAGGATCATCTGAGGTCAAGTTTGAGATCAGCCTGGCCAACA +TGGTGACACCCTGTCTCTACTGAAAATACAAAAATCAGCCAGCTATGGTAGTGTAAGCCT +GTAATTCCAGCTACTCGGGAGGTCGAAGCAGAAGAATCACTTGAACCCAGGAGGCAGAGA +TTGCAGTGAGCCAAGATCCTGCCAGTGCTTTCCAGCCTGGGCAACAGTGTGAGGCTCCAT +CTCAAAAAAAAAAAAAAAAAAAAGACAATAGCAATAAACATTAAGAAATGTGTAATAGGA +ATGGCACACACAAAGAAGGAATGGCACAGAGCCTGTATGCAGAAGACCACAAACCCTTAT +TTAACGACGTAAGCCAAGATCCAAAGAAAATGATAGATTCTCAGATGGGAAAACTAAAAA +AATAAGAAAAATCAATTATCTCGAGATAAATATAATATAATGCAATTTCAATTAGAATCC +CAAATTTTCATTGTGTGTGTGTGTGAGTTGGGTAAATTTATCATAAATGTATAGGAACGA +GTAAGTGTCACTAGTTGTTTAAATAAATACTGGATTTGGGCCAGGCATGGTGGCTCACGC +CTCTAATCCCAGCACTTTGGGAGACCGAGGCGGGCAGATCATGAGGTCAGGAGATCGAGA +CCATCTGGCCAACATAGTGAAAACTCGTCTCTACTAAAGATACAAAAAATTAGCTGGGCA +TGGTGGCACGTGCCTGTAGTTCCAGCTACTCTGGAGGCTGAGGCAGGAGAGTTGCTTGAA +CCCGGGAGGTGGAGGTTGCAATGAGCCGAGATCCTGTCACTGCACTCCACCCTGGCGACA +AAGTGAGACTCCGTCTCTCTCTCTCTCTTTAGGCCAAGGCAGGTGGATCACCTGAGGTCA +GGAGTTCAAGACAGCCTGGCCAACATAGCGAAATCCCATCTCTACTAAAAATACAAAAAT +TAGCCTGGCAGTGGTGGCCCACGCCTGTAATCCCAGCTACTAAGGGGGCTGAGGCAGGAG +GATCTCTTAACCAGGGAGGAGGAGGTTGCAGTGAGCAGAGATTGTGCCACTGCACTCCAG +CCTGTGCAACAGAGTGAGACTCTGTCTC diff --git a/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/assets/demo/splice_hbb.fa b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/assets/demo/splice_hbb.fa new file mode 100644 index 0000000..fdbefab --- /dev/null +++ b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/assets/demo/splice_hbb.fa @@ -0,0 +1,67 @@ +>human|HBB|chr11:5225464-5229395|GRCh38|strand:-1 +TTGGAAAAGCAACCCCTGCCTTGAAGCCAGGATGATGGTATCTGCAGCAGTTGCCAACAC +AAGAGAAGGATCCATAGTTCATCATTTAAAAAAGAAAACAAAATAGAAAAAGGAAAACTA +TTTCTGAGCATAAGAAGTTGTAGGGTAAGTCTTTAAGAAGGTGACAATTTCTGCCAATCA +GGATTTCAAAGCTCTTGCTTTGACAATTTTGGTCTTTCAGAATACTATAAATATAACCTA +TATTATAATTTCATAAAGTCTGTGCATTTTCTTTGACCCAGGATATTTGCAAAAGACATA +TTCAAACTTCCGCAGAACACTTTATTTCACATATACATGCCTCTTATATCAGGGATGTGA +AACAGGGTCTTGAAAACTGTCTAAATCTAAAACAATGCTAATGCAGGTTTAAATTTAATA +AAATAAAATCCAAAATCTAACAGCCAAGTCAAATCTGCATGTTTTAACATTTAAAATATT +TTAAAGACGTCTTTTCCCAGGATTCAACATGTGAAATCTTTTCTCAGGGATACACGTGTG +CCTAGATCCTCATTGCTTTAGTTTTTTACAGAGGAATGAATATAAAAAGAAAATACTTAA +ATTTTATCCCTCTTACCTCTATAATCATACATAGGCATAATTTTTTAACCTAGGCTCCAG +ATAGCCATAGAAGAACCAAACACTTTCTGCGTGTGTGAGAATAATCAGAGTGAGATTTTT +TCACAAGTACCTGATGAGGGTTGAGACAGGTAGAAAAAGTGAGAGATCTCTATTTATTTA +GCAATAATAGAGAAAGCATTTAAGAGAATAAAGCAATGGAAATAAGAAATTTGTAAATTT +CCTTCTGATAACTAGAAATAGAGGATCCAGTTTCTTTTGGTTAACCTAAATTTTATTTCA +TTTTATTGTTTTATTTTATTTTATTTTATTTTATTTTGTGTAATCGTAGTTTCAGAGTGT +TAGAGCTGAAAGGAAGAAGTAGGAGAAACATGCAAAGTAAAAGTATAACACTTTCCTTAC +TAAACCGACATGGGTTTCCAGGTAGGGGCAGGATTCAGGATGACTGACAGGGCCCTTAGG +GAACACTGAGACCCTACGCTGACCTCATAAATGCTTGCTACCTTTGCTGTTTTAATTACA +TCTTTTAATAGCAGGAAGCAGAACTCTGCACTTCAAAAGTTTTTCCTCACCTGAGGAGTT +AATTTAGTACAAGGGGAAAAAGTACAGGGGGATGGGAGAAAGGCGATCACGTTGGGAAGC +TATAGAGAAAGAAGAGTAAATTTTAGTAAAGGAGGTTTAAACAAACAAAATATAAAGAGA +AATAGGAACTTGAATCAAGGAAATGATTTTAAAACGCAGTATTCTTAGTGGACTAGAGGA +AAAAAATAATCTGAGCCAAGTAGAAGACCTTTTCCCCTCCTACCCCTACTTTCTAAGTCA +CAGAGGCTTTTTGTTCCCCCAGACACTCTTGCAGATTAGTCCAGGCAGAAACAGTTAGAT +GTCCCCAGTTAACCTCCTATTTGACACCACTGATTACCCCATTGATAGTCACACTTTGGG +TTGTAAGTGACTTTTTATTTATTTGTATTTTTGACTGCATTAAGAGGTCTCTAGTTTTTT +ATCTCTTGTTTCCCAAAACCTAATAAGTAACTAATGCACAGAGCACATTGATTTGTATTT +ATTCTATTTTTAGACATAATTTATTAGCATGCATGAGCAAATTAAGAAAAACAACAACAA +ATGAATGCATATATATGTATATGTATGTGTGTATATATACACACATATATATATATATTT +TTTCTTTTCTTACCAGAAGGTTTTAATCCAAATAAGGAGAAGATATGCTTAGAACCGAGG +TAGAGTTTTCATCCATTCTGTCCTGTAAGTATTTTGCATATTCTGGAGACGCAGGAAGAG +ATCCATCTACATATCCCAAAGCTGAATTATGGTAGACAAAACTCTTCCACTTTTAGTGCA +TCAACTTCTTATTTGTGTAATAAGAAAATTGGGAAAACGATCTTCAATATGCTTACCAAG +CTGTGATTCCAAATATTACGTAAATACACTTGCAAAGGAGGATGTTTTTAGTAGCAATTT +GTACTGATGGTATGGGGCCAAGAGATATATCTTAGAGGGAGGGCTGAGGGTTTGAAGTCC +AACTCCTAAGCCAGTGCCAGAAGAGCCAAGGACAGGTACGGCTGTCATCACTTAGACCTC +ACCCTGTGGAGCCACACCCTAGGGTTGGCCAATCTACTCCCAGGAGCAGGGAGGGCAGGA +GCCAGGGCTGGGCATAAAAGTCAGGGCAGAGCCATCTATTGCTTACATTTGCTTCTGACA +CAACTGTGTTCACTAGCAACCTCAAACAGACACCATGGTGCATCTGACTCCTGAGGAGAA +GTCTGCCGTTACTGCCCTGTGGGGCAAGGTGAACGTGGATGAAGTTGGTGGTGAGGCCCT +GGGCAGGTTGGTATCAAGGTTACAAGACAGGTTTAAGGAGACCAATAGAAACTGGGCATG +TGGAGACAGAGAAGACTCTTGGGTTTCTGATAGGCACTGACTCTCTCTGCCTATTGGTCT +ATTTTCCCACCCTTAGGCTGCTGGTGGTCTACCCTTGGACCCAGAGGTTCTTTGAGTCCT +TTGGGGATCTGTCCACTCCTGATGCTGTTATGGGCAACCCTAAGGTGAAGGCTCATGGCA +AGAAAGTGCTCGGTGCCTTTAGTGATGGCCTGGCTCACCTGGACAACCTCAAGGGCACCT +TTGCCACACTGAGTGAGCTGCACTGTGACAAGCTGCACGTGGATCCTGAGAACTTCAGGG +TGAGTCTATGGGACGCTTGATGTTTTCTTTCCCCTTCTTTTCTATGGTTAAGTTCATGTC +ATAGGAAGGGGATAAGTAACAGGGTACAGTTTAGAATGGGAAACAGACGAATGATTGCAT +CAGTGTGGAAGTCTCAGGATCGTTTTAGTTTCTTTTATTTGCTGTTCATAACAATTGTTT +TCTTTTGTTTAATTCTTGCTTTCTTTTTTTTTCTTCTCCGCAATTTTTACTATTATACTT +AATGCCTTAACATTGTGTATAACAAAAGGAAATATCTCTGAGATACATTAAGTAACTTAA +AAAAAAACTTTACACAGTCTGCCTAGTACATTACTATTTGGAATATATGTGTGCTTATTT +GCATATTCATAATCTCCCTACTTTATTTTCTTTTATTTTTAATTGATACATAATCATTAT +ACATATTTATGGGTTAAAGTGTAATGTTTTAATATGTGTACACATATTGACCAAATCAGG +GTAATTTTGCATTTGTAATTTTAAAAAATGCTTTCTTCTTTTAATATACTTTTTTGTTTA +TCTTATTTCTAATACTTTCCCTAATCTCTTTCTTTCAGGGCAATAATGATACAATGTATC +ATGCCTCTTTGCACCATTCTAAAGAATAACAGTGATAATTTCTGGGTTAAGGCAATAGCA +ATATCTCTGCATATAAATATTTCTGCATATAAATTGTAACTGATGTAAGAGGTTTCATAT +TGCTAATAGCAGCTACAATCCAGCTACCATTCTGCTTTTATTTTATGGTTGGGATAAGGC +TGGATTATTCTGAGTCCAAGCTAGGCCCTTTTGCTAATCATGTTCATACCTCTTATCTTC +CTCCCACAGCTCCTGGGCAACGTGCTGGTCTGTGTGCTGGCCCATCACTTTGGCAAAGAA +TTCACCCCACCAGTGCAGGCTGCCTATCAGAAAGTGGTGGCTGGTGTGGCTAATGCCCTG +GCCCACAAGTATCACTAAGCTCGCTTTCTTGCTGTCCAATTTCTATTAAAGGTTCCTTTG +TTCCCTAAGTCCAACTACTAAACTGGGGGATATTATGAAGGGCCTTGAGCATCTGGATTC +TGCCTAATAAAAAACATTTATTTTCATTGCAA diff --git a/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/evals/config.yml b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/evals/config.yml new file mode 100644 index 0000000..5f85ee4 --- /dev/null +++ b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/evals/config.yml @@ -0,0 +1,9 @@ +schema_version: 1 + +harbor: + task_source: evals_json + runtime_env: + - GI_API_KEY + +grading: + mode: aces_default diff --git a/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/evals/evals.json b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/evals/evals.json new file mode 100644 index 0000000..368f39b --- /dev/null +++ b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/evals/evals.json @@ -0,0 +1,96 @@ +{ + "skill_name": "genomic-intelligence-nim", + "evals": [ + { + "id": "1", + "prompt": "Predict promoter regions in assets/demo/promoter_tp53.fa using Genomic Intelligence, and tell me how many promoter windows it found. My GI_API_KEY is set.", + "expected_output": "Runs the provided gi_predict.py runner for the promoter task on the demo FASTA, then reports summary.promoter_windows / total_windows from the compact stdout JSON.", + "files": [ + "scripts/gi_predict.py", + "assets/demo/promoter_tp53.fa" + ], + "assertions": [ + { + "id": "uses-runner", + "description": "Uses the provided runner rather than hand-rolling HTTP", + "check": "Invokes 'scripts/gi_predict.py' with '--task promoter' (does not build a raw curl/requests call inline)" + }, + { + "id": "hosted-endpoint", + "description": "Targets the hosted Genomic Intelligence promoter endpoint", + "check": "The runner/client contains 'api.genomicintelligence.ai' and '/v1/tasks/{task}/predict' (task = promoter)" + }, + { + "id": "bearer-auth", + "description": "Authenticates with a Bearer key from GI_API_KEY", + "check": "Request sets 'Authorization: Bearer' from 'GI_API_KEY'; no key literal is hard-coded" + }, + { + "id": "reports-summary", + "description": "Reports the promoter-window headline", + "check": "Output reads 'promoter_windows' / 'total_windows' from the response summary" + } + ] + } + ], + "todo": "Deferred evals need network egress to api.genomicintelligence.ai and an Ensembl fetch (case 2) / async job polling (case 3); enable once the eval harness supports long-running third-party calls.", + "deferred_evals": [ + { + "id": "2", + "prompt": "Predict gene expression for HBB in K562 cells with Genomic Intelligence. I'll let you fetch the sequence.", + "expected_output": "Fetches the exact 9,198 bp TSS-centred window for HBB (gi_fetch --for-expression), then runs the expression task with a required cell-type description, reporting log(TPM+1).", + "files": [ + "scripts/gi_predict.py", + "scripts/gi_fetch.py" + ], + "assertions": [ + { + "id": "expression-window", + "description": "Obtains the exact 9,198 bp expression window", + "check": "Uses gi_fetch with '--for-expression' (or otherwise produces a 9,198 bp TSS-centred window) before predicting" + }, + { + "id": "description-required", + "description": "Passes the required cell-type context", + "check": "Runs expression with '--description' set to a cell type such as 'K562'" + }, + { + "id": "expression-endpoint", + "description": "Calls the expression task", + "check": "Runner is invoked with '--task expression'" + }, + { + "id": "reports-log-tpm", + "description": "Reports the expression prediction", + "check": "Output reads 'expression_log_tpm' (log TPM) from the response" + } + ] + }, + { + "id": "3", + "prompt": "Run de-novo gene annotation on assets/demo/annotation_tp53.fa with Genomic Intelligence and list the transcripts it predicts.", + "expected_output": "Runs the annotation task, which submits asynchronously and polls the job endpoint to completion, then reports the predicted transcripts.", + "files": [ + "scripts/gi_predict.py", + "assets/demo/annotation_tp53.fa" + ], + "assertions": [ + { + "id": "annotation-task", + "description": "Runs the annotation task via the runner", + "check": "Runner is invoked with '--task annotation'" + }, + { + "id": "async-submit-poll", + "description": "Uses the async submit/poll protocol", + "check": "Client submits with header 'Prefer: respond-async' and polls '/v1/tasks/jobs/'" + }, + { + "id": "reports-transcripts", + "description": "Reports predicted transcripts", + "check": "Output reads 'transcripts' / 'transcripts_found' from the response" + } + ] + } + ] +} diff --git a/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/evals/trigger_evals.json b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/evals/trigger_evals.json new file mode 100644 index 0000000..073268c --- /dev/null +++ b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/evals/trigger_evals.json @@ -0,0 +1,20 @@ +[ + {"query": "Predict promoter regions in this DNA sequence with Genomic Intelligence", "should_trigger": true}, + {"query": "Find splice donor and acceptor sites in this transcript", "should_trigger": true}, + {"query": "Score enhancer activity for this Drosophila regulatory sequence", "should_trigger": true}, + {"query": "Annotate chromatin state across tracks for this human region", "should_trigger": true}, + {"query": "Predict gene expression in K562 cells from this genomic window", "should_trigger": true}, + {"query": "Run de-novo gene / transcript annotation on this FASTA", "should_trigger": true}, + {"query": "Predict regulatory features of TP53 directly from sequence", "should_trigger": true}, + {"query": "What is the promoter/expression profile of HBB? Use the DNA language model API", "should_trigger": true}, + {"query": "Use the Genomic Intelligence hosted API to analyze this DNA", "should_trigger": true}, + {"query": "Fetch the TSS-centred window for a gene and predict its expression", "should_trigger": true}, + {"query": "Predict a protein structure from FASTA using OpenFold3", "should_trigger": false}, + {"query": "Generate a DNA continuation from this seed with Evo 2", "should_trigger": false}, + {"query": "Dock a ligand to a protein receptor and return binding poses", "should_trigger": false}, + {"query": "Design protein sequences for this backbone with ProteinMPNN", "should_trigger": false}, + {"query": "Calculate GC content of this DNA string with plain Python", "should_trigger": false}, + {"query": "Call variants from this BAM file and write a VCF", "should_trigger": false}, + {"query": "Cluster single-cell RNA-seq expression matrices", "should_trigger": false}, + {"query": "Summarize the promoter biology literature without calling an API", "should_trigger": false} +] diff --git a/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/references/api.md b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/references/api.md new file mode 100644 index 0000000..0c0668f --- /dev/null +++ b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/references/api.md @@ -0,0 +1,208 @@ +# API reference — the /v1 contract + +Genomic Intelligence exposes one versioned REST contract. Each task is its own +published operation with its own request schema; there is no shared request +model. Authoritative, live schema: + (human view: +). This file is a point-in-time +snapshot — if it disagrees with the OpenAPI doc, the OpenAPI doc wins. + +## Endpoints + +The document publishes eleven operations. The six predict paths are literal, one +per task — the URLs are byte-identical to what callers already send, so no client +URL construction changes: + +``` +POST https://api.genomicintelligence.ai/v1/tasks/promoter/predict # PromoterPredictRequest +POST https://api.genomicintelligence.ai/v1/tasks/splice/predict # SplicePredictRequest +POST https://api.genomicintelligence.ai/v1/tasks/enhancer/predict # EnhancerPredictRequest +POST https://api.genomicintelligence.ai/v1/tasks/chromatin/predict # ChromatinPredictRequest +POST https://api.genomicintelligence.ai/v1/tasks/annotation/predict # AnnotationPredictRequest +POST https://api.genomicintelligence.ai/v1/tasks/expression/predict # ExpressionPredictRequest +POST https://api.genomicintelligence.ai/v1/workflows/find-genes-and-predict-expression +GET https://api.genomicintelligence.ai/v1/tasks/jobs # list async jobs +GET https://api.genomicintelligence.ai/v1/tasks/jobs/{job_id} # poll an async job +GET https://api.genomicintelligence.ai/v1/tasks/{task}/models # model registry +GET https://api.genomicintelligence.ai/health # public +``` + +An unrecognised task segment is `404 not_found` (`"Unknown task: bogus"`), not a +`422`. + +- Base URL overridable via `GI_BASE_URL`. +- Auth: `Authorization: Bearer $GI_API_KEY` (partner key, prefix `gi_`) on every + `/v1/*` route, including `GET /v1/tasks/{task}/models`. +- `Content-Type: application/json`, `Accept: application/json`. + +`GET /v1/tasks/{task}/models` is **not** the `{data, meta}` envelope — it returns +a flat `{task, default_model, models: [{id, name, description, is_default, +bio_spec}]}`. + +## Request body + +Every request model is `additionalProperties: false`, as is every `options` +object, so an unknown key is a hard `422 validation_failed` with +`type: "extra_forbidden"` — never silently ignored. + +```json +{ + "sequence": "ACGT…", // required; A/C/G/T(/N); per-task minLength + "sequence_name": "TP53", // optional label echoed back (max 128 chars) + "model": "…", // optional; omit to use the task default + "options": { "threshold": 0.5 } // task-specific, closed; see below +} +``` + +`options` per task (all closed): + +| Task | `options` keys | +|---|---| +| promoter | `threshold` (0–1, default 0.5) | +| splice | `threshold` (0–1, default 0.5), `site_types` (subset of `["donor","acceptor"]`, default both) | +| enhancer | *(none)* | +| chromatin | `threshold` (0–1, default 0.5) | +| annotation | `batch_size` (1–128, default 8), `shift_coordinates`, `reverse_complement` (default true) | +| expression | `description` — **required**, and the only key | +| composite | `description`, `annotation_model`, `expression_model`, `batch_size`, `shift_coordinates` | + +`expression` additionally requires `options` itself and takes a fifth field: + +```json +{ + "sequence": "ACGT…", // required, 9,198–500,000 bp + "options": { "description": "K562 cells" }, // required + "tss_index": 12345, // required unless len == 9198 + "sequence_name": "HBB", + "model": "…" +} +``` + +Length bounds are per-task, published as `minLength`/`maxLength` on each request +schema and enforced before any model loads (see `references/tasks.md`): promoter +300, splice 100, enhancer 50, chromatin 200, annotation 1,000, expression 9,198, +composite 1,000 — all capped at 500,000 bp. Under the floor and over the cap are +both `422 validation_failed` at `loc ["body","sequence"]`; over-length is **not** +a `413`. The floor is admission control, not regime: a request above the floor +but below the model's `bio_spec.context_window_bp` is accepted and scored against +a padded window. + +Lengths and `tss_index` are measured on the **whitespace-stripped** sequence, so a +line-wrapped FASTA body pastes verbatim (a `>` header line still fails the +alphabet check). The runner validates length and `tss_index` bounds locally +before any call — those local constants are a mirror of the `minLength` +published on each task's request schema; the served schema wins. + +## `bio_spec` (from `GET /v1/tasks/{task}/models`) + +- `request_max_bp` — the enforced ceiling (500,000 for every model). +- `context_window_bp` — the model's own sliding window in bp; `null` for + annotation and expression. Promoter is 2,000 for the 2,000 bp models and 300 + for the 300 bp models; splice 15,000, enhancer 249, chromatin 1,000. Read the + value for the model you are actually using from + `GET /v1/tasks/{task}/models`. +- `trained_window_bp` — fixed receptive field; 9,198 for the expression model, + `null` for sliding-window models. + +There is no `strand_sensitive` flag. The splice model is strand-specific in +practice — feed transcript orientation. + +Expression responses echo the windowing: `meta.task_specific_counts.tss_index` / +`.scored_window`, and `data.input.tss_index` / `.scored_window` / +`.submitted_sequence_length`. Note `data.input.sequence_length` is the **scored** +length (always 9,198), not what you submitted. + +## Response envelope + +Success is `200` with a `{data, meta}` envelope. `data.summary` carries the +headline scalars; `data` also carries the per-item arrays (`regions`, `sites`, +`transcripts`) or `prediction` (expression). `meta` carries `model`, +`request_id`, and timing. Exact fields per task: `references/tasks.md`. + +## Async (annotation, and any predict operation) + +`Prefer` is a declared header parameter on all six predict operations and on the +composite — any of them can be run async, not just `annotation`: + +1. `POST …/tasks/annotation/predict` with header `Prefer: respond-async` → `202` + with `{data: {job_id, status: "accepted", links}, meta}` (the same + `{data, meta}` envelope as a sync `200`). The job id is also in the + `Content-Location` and `X-Job-Id` response headers. +2. Poll `GET …/tasks/jobs/{job_id}` — `202` while running (`{data: {job_id, + status, progress}, meta}`), `200` with the final `{data, meta}` when done. + +Async is JSON-only: a text `format` combined with `Prefer: respond-async` is +rejected. + +`scripts/gi_predict.py` handles the submit/poll loop (2 s interval, 30-min cap) +and streams progress to stderr — no extra flags. + +## Error envelope + +Non-2xx responses carry: + +```json +{ "error": { "code": "…", "message": "…", "request_id": "…", "details": … } } +``` + +`error.code` is a closed 21-value enum: `bad_request`, `unauthorized`, +`forbidden`, `not_found`, `conflict`, `job_expired`, `payload_too_large`, +`sync_too_large`, `unsupported_format`, `validation_failed`, +`too_many_requests`, `rate_limited`, `internal_error`, `timeout`, +`insufficient_memory`, `model_not_found`, `task_not_supported_by_model`, +`model_loading`, `service_unavailable`, `http_error`, `unknown`. The schema tells +clients to treat an unlisted value as a generic failure, not a parse error. + +Switch on `code` first, then read `details` — `details` is keyed on the sibling +`code` and matches the declared schema: `validation_failed` carries the +`ValidationFailedDetails` object `{errors: [{loc, msg, type}, …]}`. Read it +defensively and never make control flow depend on its shape. + +`error.request_id` mirrors the `X-Request-Id` response header, and both are set +on every response — error envelopes (including `413 sync_too_large`) and success +envelopes, where it lives at `meta.request_id`. Every response carries +`RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset` and +`RateLimit-Policy`; a `429` adds `Retry-After`. + +Common: `401/403` (auth), `422 validation_failed` (bad body/length/model/options +— including over-length sequence), `429` (rate limit), `413 payload_too_large` +(raw body over 16 MiB), `415 unsupported_format` (an unsupported `format` query +value — never a silent fallback to JSON), `504 timeout` (large sync +request on a cold GPU). More: `references/errors.md`. + +Per-task `format` values: promoter `json|bed|bedgraph`, splice `json|bed|gff3`, +enhancer `json|bedgraph`, chromatin `json|bed`, annotation `json|bed|gff3`, +expression JSON only. Text formats are synchronous-only. + +## Composite: find genes, then predict expression + +``` +POST /v1/workflows/find-genes-and-predict-expression +``` + +Request `FindGenesAndPredictExpressionRequest`: `sequence` 1,000–500,000 bp and +`options` are both required, and `options.description` (cell type / assay +context) is required too — enforced at runtime rather than marked `required` in +`FindGenesAndPredictExpressionOptions`, so a missing or empty value is a +`422 validation_failed` with the message *"options.description is required (cell +type / assay context)"*. Send it. + +It annotates the sequence, centres a 9,198 bp window on each discovered gene's +TSS (padding with `N` up to half the window rather than dropping an edge gene), +and returns an expression prediction per gene. `meta.task_specific_counts` is +`{genes_found, genes_predicted, genes_skipped}` with +`genes_predicted + genes_skipped == genes_found`; per-gene causes are in +`data.expression_predictions[].skip_reason`. + +Above **50,000 bp** the composite forces async: a synchronous request over that +size is `413 sync_too_large` with `error.details = {sequence_length, threshold}`. +Retry the same body with `Prefer: respond-async`. + +## Authority + +Everything above describes the contract the Genomic Intelligence API serves at +`api.genomicintelligence.ai`: the six literal predict operations, the typed +`options` objects, the per-task floors, the published composite, the `Prefer` +parameter, the `code` enum and the `bio_spec` fields. The served schema at + is authoritative — check it +if a detail here does not match. diff --git a/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/references/authentication.md b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/references/authentication.md new file mode 100644 index 0000000..3861865 --- /dev/null +++ b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/references/authentication.md @@ -0,0 +1,44 @@ +# Authentication + +Every `/v1/*` call requires a partner bearer key. The skill sends it as +`Authorization: Bearer `. Public routes that need no key: `/health`, +`/docs`, `/redoc`, `/v1/openapi.json`. + +## Setting the key + +```bash +export GI_API_KEY=gi_yourkeyhere +``` + +Resolution order in `scripts/gi_client.py`: + +1. Explicit `--api-key` CLI flag (highest precedence). +2. `GI_API_KEY` environment variable. +3. Otherwise: a `RuntimeError` with onboarding instructions and exit code 2. + +Keys are bearer tokens beginning with `gi_`. Request one at +**contact@genomicintelligence.ai**. + +## Base URL + +Default: `https://api.genomicintelligence.ai`. Override for staging or a local +service: + +```bash +export GI_BASE_URL=https://staging.example.internal +# or per-invocation: +python scripts/gi_predict.py --task promoter --demo --base-url http://localhost:8001 +``` + +## Partner tiers + +Keys are scoped to a partner tier with concurrency and rate limits. If you hit +`429`, you have exceeded your concurrency or per-minute cap — back off and +retry. Higher-throughput needs: ask Genomic Intelligence to raise your tier. + +## Security notes + +- Never commit a real key. Keep it in the environment or a secrets manager. +- The key authorizes billed inference. Treat it like a credential. +- The skill never writes the key into `report.md`, `result.json`, or the + reproducibility bundle — only the request ID and base URL are recorded. diff --git a/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/references/errors.md b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/references/errors.md new file mode 100644 index 0000000..a9b6d3d --- /dev/null +++ b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/references/errors.md @@ -0,0 +1,113 @@ +# Errors, Async Polling & Limits + +## Error envelope + +Non-2xx responses carry a JSON `{error}` envelope, surfaced by the skill as +`API error: [ ] (request_id=)` on stderr (exit +code 2). The shape: + +```json +{ + "error": { + "code": "validation_failed", + "message": "human-readable explanation", + "request_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", + "details": { "errors": [ { "loc": ["body", "sequence"], "msg": "…" } ] } + } +} +``` + +Always quote the `request_id` when reporting an issue to Genomic Intelligence. +It mirrors the `X-Request-Id` response header, and both are set on every +response: error envelopes carry `error.request_id`, success envelopes carry +`meta.request_id`. Reading the header as a fallback remains sound practice. + +`code` is a **closed 21-value enum**: `bad_request`, `unauthorized`, `forbidden`, +`not_found`, `conflict`, `job_expired`, `payload_too_large`, `sync_too_large`, +`unsupported_format`, `validation_failed`, `too_many_requests`, `rate_limited`, +`internal_error`, `timeout`, `insufficient_memory`, `model_not_found`, +`task_not_supported_by_model`, `model_loading`, `service_unavailable`, +`http_error`, `unknown`. The schema explicitly says to treat an unlisted value as +a generic failure, not a parse error. + +**Branch on `code`, never on `details` or `loc`.** `details` is keyed on the +sibling `code` (`ValidationFailedDetails`, `TaskNotSupportedByModelDetails`, +`ModelNotFoundDetails`, `SyncTooLargeDetails`, `GenericDetails`, or null). A +validation failure carries the declared `{errors: [{loc, msg, type}, …]}` +object — the FastAPI error array wrapped under `errors`. Read it defensively +and keep control flow off it. + +## Common status codes + +| Status | `code` | Meaning | Action | +|---|---|---|---| +| 400 | `bad_request` | Malformed request | Read the message | +| 401 / 403 | `unauthorized` / `forbidden` | Missing / bad / revoked key | Re-check `GI_API_KEY` (see authentication.md) | +| 404 | `not_found` | **Unknown task** or unknown job id | Check the `--task` value (an unrecognised task is a 404, not a 422); a job may have expired | +| 410 | `job_expired` | Async job result no longer retained | Re-submit | +| 413 | `payload_too_large` | Raw request body over **16 MiB**, rejected before parsing | Split the input — this is the body cap, not the sequence cap | +| 413 | `sync_too_large` | Composite workflow called synchronously above 50,000 bp | Retry with `Prefer: respond-async`; `details` = `{sequence_length, threshold}` | +| 415 | `unsupported_format` | Unsupported `format` query value | Use a format the task supports — there is no silent fallback to JSON | +| 422 | `validation_failed` | Sequence under the task floor **or over the 500,000 bp cap**, out-of-range/missing `tss_index`, missing `options.description`, unknown body or `options` key | Read the message; fix the body | +| 429 | `rate_limited` / `too_many_requests` | Concurrency / rate cap exceeded | Back off (honour `Retry-After`); request a higher tier | +| 500 | `internal_error` | Server-side failure | Retry; if persistent, report with `request_id` | +| 503 | `service_unavailable` / `model_loading` | Backend transiently down or a model is loading | Retry with backoff | +| 504 | `timeout` | Large sync request on a cold GPU | Retry, or use a smaller sequence | + +Note that a sequence **over** 500,000 bp is a `422 validation_failed` +(`"sequence is 520000 bp; the maximum is 500000 bp"`, `loc +["body","sequence"]`) — *not* a `413`. `413` means only the 16 MiB raw-body cap +or the composite's synchronous-delivery cap. + +The skill validates length, the `expression` `--description`, and the +`--tss-index` bounds **before** any network call, so those failures (exit +code 1) never reach the API. Server-side, every expression contract violation +(sequence below 9,198 bp, missing/out-of-range `tss_index`, missing +`options.description`, unknown body field) is a `422 validation_failed`. There +is no opt-out flag, header, or query parameter; nothing is padded or clamped. +The `tss_index` checks come from a whole-model validator and report at +`loc: ["body"]`, never `body.tss_index`. + +Every response — success or error — carries `RateLimit-Limit`, +`RateLimit-Remaining`, `RateLimit-Reset` and `RateLimit-Policy`; a `429` adds +`Retry-After`. + +## Async polling (`annotation`) + +The skill runs `annotation` asynchronously by default; the API accepts either +mode on every task (`Prefer: respond-async` is declared on all six predict +operations, and annotation returns `200` synchronously without it). The flow +inside `scripts/gi_predict.py`: + +1. `POST /v1/tasks/annotation/predict` with header `Prefer: respond-async` + → returns `202` with `data.job_id`. +2. Poll `GET /v1/tasks/jobs/{job_id}`: + - `202` → still running; `data.progress` is streamed to stderr; sleep and re-poll. + - `200` → terminal; the body is the final `{data, meta}` envelope. + - other → raised as a `GIError`. + +Defaults: poll every 2 s, give up after 30 min. Typical real latency is ~20 s +for a ~20 kb sequence (longer on a cold GPU). + +## Limits + +- **Max sequence length:** 500,000 bp for every task (over → `422`). +- **Minimum sequence length, per task:** promoter 300, splice 100, enhancer 50, + chromatin 200, annotation 1,000, expression 9,198 bp. Under → `422`. The floor + is admission control, not regime — see `references/tasks.md`. +- **Expression minimum:** 9,198 bp is also the width of the single TSS-centred + window the model scores. Above that width, `tss_index` is required. +- **Raw request body:** 16 MiB, enforced before parsing (`413 + payload_too_large`). +- **Composite synchronous delivery:** 50,000 bp (`413 sync_too_large` above it). +- **Single record per request:** the runner refuses a multi-record FASTA rather + than concatenating it into one chimeric sequence. Split the file and run per + record. It also refuses bases outside `ACGTN` — deleting an IUPAC ambiguity + code would shift every position after it and score a sequence you did not + submit. +- **Rate / concurrency:** per partner tier; `429` signals you have exceeded it. + +Authoritative limits are published as `minLength`/`maxLength` on each task's +request schema in the live OpenAPI document at +. Numbers repeated in this +skill are mirrors. diff --git a/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/references/sequence-acquisition.md b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/references/sequence-acquisition.md new file mode 100644 index 0000000..51bb71f --- /dev/null +++ b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/references/sequence-acquisition.md @@ -0,0 +1,89 @@ +# Sequence Acquisition (Ensembl) + +The skill can turn a **gene symbol** or a **genomic region** into reference +sequence, so users don't have to bring a FASTA. This is handled by +`scripts/gi_fetch.py` (CLI) and `scripts/gi_ensembl.py` (the Ensembl REST +helpers it calls). Acquisition is separate from prediction: `gi_fetch.py` writes +a FASTA, then `gi_predict.py --input` consumes it. + +**No API key** — Ensembl REST (`rest.ensembl.org`) is public. Only the +*prediction* step needs `GI_API_KEY`. + +## Modes + +```bash +# Full gene body (any task except expression) +python scripts/gi_fetch.py --gene TP53 --out tp53.fa + +# Coordinate range +python scripts/gi_fetch.py --region chr17:7,661,779-7,687,546 --out region.fa + +# 9,198 bp TSS-centred window (expression; or send a wider locus + --tss-index) +python scripts/gi_fetch.py --gene HBB --for-expression --out hbb_tss.fa +``` + +`--gene` and `--region` are mutually exclusive. The resolved FASTA path is +printed on **stdout**; a provenance line (length, Ensembl ID, region, strand) +goes to **stderr**. Chain it: + +```bash +FASTA=$(python scripts/gi_fetch.py --gene TP53 --out tp53.fa) +python scripts/gi_predict.py --task promoter --input "$FASTA" --output out/ +``` + +## TSS-centring (why `--for-expression` exists) + +The expression model always scores **exactly 9,198 bp centred on the +transcription start site (TSS)**. You may either hand it a pre-cut 9,198 bp +window (what `--for-expression` builds) or hand it up to 500,000 bp plus +`--tss-index` and let the server slice. Either way you must know where the TSS +is — the endpoint never discovers it, never pads, and never +reverse-complements. You cannot reliably build this from gene-body coordinates: +the gene's annotated start/end can sit far from the real TSS — HBB's gene end is +2,324 bp from its canonical TSS, ACTB's is 33,301 bp. Mis-centring tanks the +prediction. + +`--for-expression` resolves the gene's **canonical transcript** (Ensembl +`expand=1`), takes the TSS from it (transcript start on the + strand, end on the +− strand), and grabs 4,599 bp upstream + 4,598 bp downstream on the gene's +strand = 9,198 bp. It validates the returned length exactly. Because TSS +centring needs a transcript, `--for-expression` works only with `--gene`, never +`--region`. + +## Species & assembly + +- **Default: human, GRCh38** (Ensembl's current human assembly). +- Non-human: pass `--species ` — the Ensembl production name, + which is lowercase and underscored: `mus_musculus`, `drosophila_melanogaster`, + `saccharomyces_cerevisiae`. `mouse`, `Drosophila`, or `Drosophila melanogaster` + will fail with a 400; the error message says so. +- The **enhancer** default model (DeepSTARR) is *Drosophila* — match the species + to the model. See [tasks.md](tasks.md). + +## Strand & coordinates + +- `--region` defaults to `--strand 1` (plus). Pass `--strand -1` only for a + strand-sensitive task on a known minus-strand locus. Gene fetch uses the + gene's own strand automatically. +- Region strings are lenient: commas, `chr` prefix, en/em dashes, and `..` are + all accepted (e.g. `chr8:127,680,000..127,800,000`). A bare position with no + end defaults to a 1,000 bp window. +- `--flank-bp N` adds N bp on each side of a gene body or region (not used with + `--for-expression`). + +## When to skip acquisition + +Supply a FASTA directly to `gi_predict.py --input` when the sequence is **not** +reference genome — variant-bearing, edited, synthetic, or from a non-Ensembl +assembly. Acquisition only returns reference sequence for the requested +coordinates. + +## Limits + +Reference fetch is bounded by the task's own input cap (500,000 bp for every +task) and its floor (promoter 300, splice 100, enhancer 50, chromatin 200, +annotation 1,000, expression 9,198 bp). Fetch at least the model's +`context_window_bp` if you want the score to reflect real sequence rather than +padding — see `references/tasks.md`. Ensembl enforces its own per-request size +limits on `/sequence/region`; very large ranges may be rejected upstream — +fetch in pieces or narrow the region. diff --git a/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/references/tasks.md b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/references/tasks.md new file mode 100644 index 0000000..78f1b90 --- /dev/null +++ b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/references/tasks.md @@ -0,0 +1,182 @@ +# Tasks Reference + +The Genomic Intelligence `/v1` API exposes six DNA-sequence tasks. Each is its +own published operation — `POST /v1/tasks/promoter/predict`, +`/v1/tasks/splice/predict`, … — with its own request schema, its own minimum +length, and its own closed `options` object. There is no shared request model; +the schemas are per-task. Each returns a `{data, meta}` envelope. This skill's `scripts/gi_predict.py --task +` selects the endpoint; the table below documents what differs per task. + +Source of truth for bounds and models: the live OpenAPI doc at +, where each task's floor is +published as `minLength` on its own request schema, and the public task +reference at . The numbers below and in +`scripts/gi_predict.py` are **mirrors** — if they disagree with the served +schema, the served schema wins. + +## Summary + +| Task | Default architecture | Recommended mode | Accepted length | `context_window_bp` | Demo fixture | +|---|---|---|---|---|---| +| promoter | sliding-window promoter caller | sync | 300–500,000 bp | 2,000 bp | `promoter_tp53.fa` | +| splice | BigBird long-context | sync | 100–500,000 bp | 15,000 bp | `splice_hbb.fa` | +| enhancer | DeepSTARR (*Drosophila* S2) | sync | 50–500,000 bp | 249 bp | `enhancer_eve.fa` | +| chromatin | DeepSEA multi-track | sync | 200–500,000 bp | 1,000 bp | `chromatin_active_promoter_chr19.fa` | +| expression | TSS-window expression regressor | sync | **9,198–500,000 bp** | n/a (`trained_window_bp` 9,198) | `expression_hbb_k562.fa` | +| annotation | structure-aware gene finder | async | 1,000–500,000 bp | n/a | `annotation_tp53.fa` | + +`Recommended mode` is guidance, not a constraint — every task accepts both. Omit `Prefer` for a synchronous `200`; send `Prefer: respond-async` for a `202` plus `GET /v1/tasks/jobs/{job_id}`. Only the composite workflow enforces a mode, rejecting sync above 50,000 bp with `413 sync_too_large`. + +There are no per-model floors: a task's minimum is the strictest its models need, +and every model stays listed and loadable. + +**Floor ≠ regime.** The minimum is admission control, enforced at request +validation before any model loads. A request above the floor but shorter than the +selected model's `bio_spec.context_window_bp` is **accepted and scored** — against +a window padded out to the context window. Enhancer is the sharp case: the bound +is 50 bp but the enhancer context window is 249 bp, so 50–248 bp is scored +mostly on padding. Compare your length against `context_window_bp` (from +`GET /v1/tasks/{task}/models`) to know whether the model saw real sequence. +Longer-than-context input is fine — the scanner steps a prediction window at a +time and pads only the final partial window. + +Under the floor and over the 500,000 bp cap are both `422 validation_failed` at +`loc ["body","sequence"]` — over-length is **not** a `413`. All lengths are +measured after whitespace is stripped. + +To list the models available for a task and pass a non-default one, use +`--model `. The model registry is the single source of truth; do not invent +model IDs. + +## `options` per task + +Every `options` object is closed (`additionalProperties: false`); an unknown key +is a hard `422 validation_failed` (`type: "extra_forbidden"`, +`loc: ["body","options",""]`), never ignored. + +| Task | Keys | +|---|---| +| promoter | `threshold` (0–1, default 0.5) | +| splice | `threshold` (0–1, default 0.5), `site_types` (subset of `["donor","acceptor"]`, default both) | +| enhancer | *(none)* | +| chromatin | `threshold` (0–1, default 0.5) | +| annotation | `batch_size` (1–128, default 8), `shift_coordinates`, `reverse_complement` (default true) | +| expression | `description` — **required**, and the only key | + +`--description` therefore applies to `expression` only; the runner drops it (with +a warning) on any other task rather than letting the server 422. + +## promoter + +Predicts promoter regions over a sliding window. `data.summary` reports +`promoter_windows` / `total_windows`; `data.regions` lists windows with +`name`, `start`, `end`, `score`, and `strand`. Output also available as BED / +bedGraph via the API directly. + +Non-human models exist (Drosophila, yeast, Arabidopsis) — pass `--model`. The +default promoter model targets human/mammalian sequence. + +## splice + +Predicts splice **donor** and **acceptor** sites. `data.sites` lists each site +with `name`, `start`, `end`, `site_type` (donor/acceptor), `score`, and +`strand`. The default splice model uses a BigBird long-context architecture. Good demo: a +gene with known introns (the bundled `splice_hbb.fa` is HBB). + +`start` and `end` bound a tokenizer span, not the exon/intron junction. The span +is one variable-width token — 4–10 bp across the sequences measured so far — +reported with a `token_index`, and the junction lies somewhere inside it. Do not +derive a base position from the pair, and do not intersect it against reference +annotation as though it marked a boundary. + +## enhancer + +Scores enhancer activity. The default enhancer model (DeepSTARR) reports +**developmental** and **housekeeping** enhancer scores — +`summary.dev_score_max` / `summary.hk_score_max` per window. DeepSTARR is a +*Drosophila* model; the bundled demo (`enhancer_eve.fa`, the eve locus) reflects +that. Use the appropriate model for your organism. + +The 50 bp floor is the strictest gate any enhancer model needs. It is not +a biologically meaningful range: the model's `context_window_bp` is 249, so a +50–248 bp request is accepted and scored against a padded 249 bp window. Submit +at least 249 bp if you want the score to reflect real sequence. + +## chromatin + +Annotates chromatin state across a large panel of tracks (histone marks, DNase, +ATAC, TF binding) — the default chromatin model (DeepSEA architecture) covers hundreds of +features. `summary.total_annotations` is the headline; the full per-track matrix +is in `data`. Output also available as BED via the API. + +## expression + +Predicts gene expression as **log(TPM+1)** from a fixed window. Its published +operation is `POST /v1/tasks/expression/predict` with schema +`ExpressionPredictRequest`, which — unlike the other five — requires `options` as +well as `sequence`. Three requirements the skill enforces locally: + +1. **9,198–500,000 bp.** The model always scores exactly one 9,198 bp window + **centred on the TSS** (2 × 4,599) — `sequence[tss_index-4599 : + tss_index+4599]` — but the endpoint accepts up to 500 kb and slices for you. + Below 9,198 bp is rejected; nothing is padded or truncated. +2. **`tss_index`** (`--tss-index`) — the 0-based TSS offset into the + **whitespace-stripped** sequence. Required unless the sequence is exactly + 9,198 bp, where it defaults to 4,599 (the only legal value there). Bounds: + `4599 ≤ tss_index ≤ len(sequence) − 4599`. The endpoint does not find the TSS + for you and does not reverse-complement — submit gene-sense sequence. +3. **`--description`** — a cell-type / assay context string (e.g. `"K562 + cells"`), passed as `options.description`. Required, and the only key + `options` accepts on this task. + +The server reports both `tss_index` violations — "required unless exactly +9,198 bp" and the range check — from a whole-model validator, so they arrive at +`loc: ["body"]`, **never** `body.tss_index`. Match on +`error.code == "validation_failed"`; use the message for display only, and never +branch on `loc`. + +`data.prediction.expression_log_tpm` (and `expression_tpm`) hold the result. +`meta.task_specific_counts` carries `tss_index` and `scored_window` +(`[start, end]`, always 9,198 wide) — check it, because a `tss_index` that is in +range but wrong (e.g. counted over raw FASTA characters including newlines) +scores the wrong window and still returns `200`. `data.input.sequence_length` is +the **scored** 9,198; the length you submitted is +`data.input.submitted_sequence_length`. + +## annotation + +De-novo gene / transcript structure prediction — transcript intervals and +strand, no reference annotation needed. **The skill always runs it async** +(`Prefer: respond-async` is a declared header parameter on every predict +operation, so any task can be run this way; annotation is the one that needs it): +the skill submits with +`Prefer: respond-async`, receives a `job_id`, and polls +`GET /v1/tasks/jobs/{job_id}` until terminal (HTTP 200). Typical latency ~20 s +for ~20 kb; progress is streamed to stderr. `data.transcripts` lists each +predicted transcript with `name`, `start`, `end`, `strand`, and `score` (plus +structure fields: `length`, `tss_position`, `polya_position`, `transcript_type`, +`exons`, `introns`, `cds`). + +## Composite: find genes, then predict expression + +`POST /v1/workflows/find-genes-and-predict-expression` — "what genes are in this +region, and how are they expressed?". Request +`FindGenesAndPredictExpressionRequest`: `sequence` 1,000–500,000 bp and `options` +both required; send `options.description` (cell type / assay context) too — it is +enforced at runtime and a missing or empty value is a `422 validation_failed`. +Optional `annotation_model`, `expression_model`, `batch_size` (1–128, default 8), +`shift_coordinates`. + +It annotates the sequence, cuts a TSS-centred 9,198 bp window per discovered gene +(padding with `N` up to half the window rather than dropping an edge gene — the +direct expression route refuses to pad at all), and returns a prediction per +gene. `meta.task_specific_counts` = `{genes_found, genes_predicted, +genes_skipped}` with `genes_predicted + genes_skipped == genes_found`; per-gene +causes in `data.expression_predictions[].skip_reason`. + +Above **50,000 bp** it forces async: a synchronous request over that size is +`413 sync_too_large` with `error.details = {sequence_length, threshold}`. Retry +the same body with `Prefer: respond-async`. + +`scripts/gi_predict.py` does not wrap this workflow; call it directly (see +`references/api.md`). diff --git a/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/scripts/gi_client.py b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/scripts/gi_client.py new file mode 100644 index 0000000..b8f79f4 --- /dev/null +++ b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/scripts/gi_client.py @@ -0,0 +1,369 @@ +"""Client for the Genomic Intelligence API. + +Self-contained — this module has no dependencies beyond ``requests`` and is +imported by ``gi_predict.py`` (same directory). It wraps the hosted predict +contract for all six DNA-sequence tasks (promoter, splice, enhancer, +chromatin, expression, annotation). + +Each task is its own published operation — ``POST /v1/tasks/promoter/predict``, +``/v1/tasks/splice/predict``, and so on — with its own request schema, its own +``minLength``, and its own closed ``options`` object. The paths differ only in +the task segment, so one formatted URL covers all six; the request bodies do +not, which is why the per-task validation below is not shared. ``options`` is +``additionalProperties: false`` on every task, so an unrecognised key is a hard +``422 validation_failed`` rather than being ignored — never forward option keys +you have not confirmed against the live schema. + +Auth resolution order: +1. Explicit ``api_key=`` constructor arg (``--api-key`` on the CLI). +2. ``GI_API_KEY`` environment variable. + +If neither is supplied, ``resolve_api_key`` raises ``RuntimeError`` with +onboarding instructions. Request a partner key at +contact@genomicintelligence.ai, then ``export GI_API_KEY=gi_…``. + +Base URL: ``GI_BASE_URL`` env, default ``https://api.genomicintelligence.ai``. + +Contract reference: https://docs.genomicintelligence.ai +""" + +from __future__ import annotations + +import os +import time +from pathlib import Path +from typing import Any, Dict, Mapping, Optional, Tuple + +import requests + + +DEFAULT_BASE_URL = "https://api.genomicintelligence.ai" + +MISSING_KEY_MESSAGE = ( + "GI_API_KEY is not set. This skill calls the hosted Genomic " + "Intelligence API (https://api.genomicintelligence.ai) and requires a " + "partner bearer key.\n\n" + "Request a key at contact@genomicintelligence.ai, then:\n" + " export GI_API_KEY=gi_yourkeyhere\n\n" + "See references/authentication.md for details." +) + + +# IUPAC ambiguity codes. Listed so the parser can say *why* it is refusing: +# these are legitimate FASTA content the model cannot score, which is a +# different problem from a stray character and deserves a different hint. +_IUPAC_AMBIGUITY = "RYSWKMBDHV" + + +class FastaError(ValueError): + """Malformed FASTA input, rejected rather than silently repaired. + + Subclasses ``ValueError`` so a caller doing broad input validation still + catches it, while callers that want to distinguish input problems from + API problems can catch this specifically. + """ + + +class GIError(RuntimeError): + """Non-2xx response from the API. Mirrors the ``{error}`` envelope.""" + + def __init__( + self, + status: int, + body: Dict[str, Any], + headers: Optional[Mapping[str, str]] = None, + ): + err = (body or {}).get("error", {}) if isinstance(body, dict) else {} + self.status = status + self.code = err.get("code", "http_error") + self.message = err.get("message", "") + # Prefer the envelope's request_id; every error response carries it. + # Fall back to the X-Request-Id header for robustness (e.g. a non-JSON + # body from a proxy) — support tickets always need a correlation id. + self.request_id = err.get("request_id") or (headers or {}).get("X-Request-Id") + self.details = err.get("details") + rid = self.request_id or "unset" + super().__init__(f"[{status} {self.code}] {self.message} (request_id={rid})") + + +def resolve_api_key(explicit: Optional[str] = None) -> str: + """Apply the auth resolution order documented at module top. + + Raises ``RuntimeError`` with onboarding instructions if no key is found. + """ + if explicit: + return explicit + env = os.environ.get("GI_API_KEY") + if env: + return env + raise RuntimeError(MISSING_KEY_MESSAGE) + + +class Client: + """Thin synchronous client for the /v1/tasks//predict endpoints.""" + + def __init__( + self, + api_key: Optional[str] = None, + base_url: Optional[str] = None, + timeout: float = 300.0, + ) -> None: + self.api_key = resolve_api_key(api_key) + self.base_url = ( + base_url or os.environ.get("GI_BASE_URL") or DEFAULT_BASE_URL + ).rstrip("/") + self.timeout = timeout + self._session = requests.Session() + self._session.headers.update( + { + "Authorization": f"Bearer {self.api_key}", + "Content-Type": "application/json", + "Accept": "application/json", + "User-Agent": "BioNeMo-GI-Skill/0.1.0", + } + ) + + def _check(self, resp: requests.Response) -> Dict[str, Any]: + malformed = False + try: + body = resp.json() + except ValueError: + # http_error is a published enum value; the response arrived with a + # status and body, it just was not JSON. Client-origin errors carry + # no request_id, which distinguishes them from server codes. + body = {"error": {"code": "http_error", "message": resp.text[:200]}} + malformed = True + if not resp.ok: + raise GIError(resp.status_code, body, resp.headers) + if malformed: + # A 2xx whose body did not parse must not be returned as a result. + # The synthetic error envelope above is built for the failure path; + # returning it here would hand the caller {"error": ...} with ok=true. + raise GIError(resp.status_code, body, resp.headers) + return body + + @staticmethod + def _require_envelope(body: Any, resp: requests.Response) -> Dict[str, Any]: + """A prediction or job result carries a ``{data, meta}`` object. + + Only for those two. ``/health`` and ``GET /v1/tasks/{task}/models`` are + deliberately un-enveloped and must not be checked here. A 200 with an + empty, null or non-object body would otherwise reach the report writer + and fail there with an AttributeError or KeyError, which reads as a + client bug rather than a bad response. + + ``data`` must also be non-empty. All three call sites read content out + of it — a prediction payload, ``data.job_id``, a finished job's result — + so ``{"data": {}}`` is malformed for every one of them, and accepting it + wrote a zero-valued report and printed ``"ok": true`` with no prediction + in it. + """ + data = body.get("data") if isinstance(body, dict) else None + if not isinstance(data, dict) or not data: + raise GIError( + resp.status_code, + { + "error": { + "code": "http_error", + "message": ( + "expected a JSON object with a non-empty object " + f"'data' key, got {type(body).__name__} with data=" + + ("empty object" if isinstance(data, dict) else type(data).__name__) + ), + } + }, + resp.headers, + ) + return body + + def health(self) -> Dict[str, Any]: + r = self._session.get(f"{self.base_url}/health", timeout=self.timeout) + return self._check(r) + + def predict( + self, + task: str, + sequence: str, + sequence_name: str = "sequence", + model: Optional[str] = None, + options: Optional[Dict[str, Any]] = None, + tss_index: Optional[int] = None, + ) -> Dict[str, Any]: + body: Dict[str, Any] = {"sequence": sequence, "sequence_name": sequence_name} + if model is not None: + body["model"] = model + if options is not None: + body["options"] = options + # expression only: 0-based TSS offset into the whitespace-stripped + # sequence. Required by the API unless the sequence is exactly 9,198 bp. + if tss_index is not None: + body["tss_index"] = tss_index + r = self._session.post( + f"{self.base_url}/v1/tasks/{task}/predict", + json=body, + timeout=self.timeout, + ) + return self._require_envelope(self._check(r), r) + + def submit_async( + self, + task: str, + sequence: str, + sequence_name: str = "sequence", + model: Optional[str] = None, + options: Optional[Dict[str, Any]] = None, + ) -> str: + body: Dict[str, Any] = {"sequence": sequence, "sequence_name": sequence_name} + if model is not None: + body["model"] = model + if options is not None: + body["options"] = options + r = self._session.post( + f"{self.base_url}/v1/tasks/{task}/predict", + headers={"Prefer": "respond-async"}, + json=body, + timeout=self.timeout, + ) + body = self._require_envelope(self._check(r), r) + job_id = body["data"].get("job_id") + if not isinstance(job_id, str) or not job_id: + raise GIError( + r.status_code, + { + "error": { + "code": "http_error", + "message": "async submit returned no job_id in data", + } + }, + r.headers, + ) + return job_id + + def get_job(self, job_id: str) -> requests.Response: + return self._session.get( + f"{self.base_url}/v1/tasks/jobs/{job_id}", timeout=self.timeout + ) + + def wait_for_job( + self, + job_id: str, + poll_interval: float = 2.0, + max_wait: float = 30 * 60, + on_progress=None, + ) -> Dict[str, Any]: + deadline = time.monotonic() + max_wait + while True: + r = self.get_job(job_id) + if r.status_code == 200: + try: + body = r.json() + except ValueError: + raise GIError( + r.status_code, + { + "error": { + "code": "http_error", + "message": f"job {job_id} returned a non-JSON 200", + } + }, + r.headers, + ) from None + return self._require_envelope(body, r) + if r.status_code == 202: + if on_progress is not None: + try: + on_progress((r.json().get("data") or {}).get("progress") or {}) + except Exception: + pass + if time.monotonic() > deadline: + raise TimeoutError( + f"job {job_id} did not finish within {max_wait}s" + ) + time.sleep(poll_interval) + continue + try: + body = r.json() + except ValueError: + body = {"error": {"code": "http_error", "message": r.text[:200]}} + raise GIError(r.status_code, body, r.headers) + + +def read_fasta(path) -> Tuple[str, str]: + """Parse a single-record FASTA. Returns (sequence_name, sequence). + + Rejects malformed input rather than repairing it. Earlier versions + silently deleted every character outside ``ACGTN`` and concatenated a + multi-record file into one chimeric sequence under the first record's + name. Both are unrecoverable once they happen: deleting an IUPAC + ambiguity code shifts every base after it, so the model scores a + sequence the caller never supplied and returns a confident result with + nothing to indicate the substitution. + + Whitespace, blank lines and lowercase input are still handled — those + are formatting, not content. + + Raises: + FastaError: more than one record, a base outside ``ACGTN``, or + sequence appearing before the first header. + """ + name = None + record_names: list[str] = [] + seq_parts: list[str] = [] + offenders: Dict[str, int] = {} + with open(Path(path)) as fh: + for lineno, line in enumerate(fh, start=1): + line = line.strip() + if not line: + continue + if line.startswith(">"): + header = line[1:].split()[0] if line[1:].split() else "sequence" + record_names.append(header) + if name is None: + name = header + continue + if name is None: + raise FastaError( + f"{path}: sequence on line {lineno} before any '>' header. " + f"Those bases would be scored under the first record's name, " + f"and the coordinates returned would not describe what you " + f"submitted. Add a header, or remove the stray lines." + ) + # Whitespace anywhere in the line is formatting, not content: the + # API strips newlines, spaces and tabs before measuring length, so + # a space-grouped body (10-base blocks from a viewer or Sanger + # output) must parse here too. Stripping it moves nothing in + # coordinate space, which is what separates it from an ambiguity + # code we refuse to guess at. + upper = "".join(line.split()).upper() + for char in upper: + if char not in "ACGTN": + offenders.setdefault(char, lineno) + seq_parts.append(upper) + + if len(record_names) > 1: + shown = ", ".join(record_names[:3]) + more = f", … ({len(record_names)} total)" if len(record_names) > 3 else "" + raise FastaError( + f"{path}: expected a single FASTA record, found {len(record_names)} " + f"({shown}{more}). Concatenating them would submit a chimeric " + f"sequence under one name — split the file and submit one record " + f"per request." + ) + + if offenders: + detail = ", ".join( + f"{char!r} (first at line {lineno})" + for char, lineno in sorted(offenders.items(), key=lambda kv: kv[1])[:5] + ) + ambiguity = sorted(c for c in offenders if c in _IUPAC_AMBIGUITY) + hint = ( + " IUPAC ambiguity codes cannot be scored; resolve them to explicit " + "bases or submit a different region." + if ambiguity + else " Remove or resolve them before submitting." + ) + raise FastaError( + f"{path}: sequence contains characters outside ACGTN: {detail}.{hint}" + ) + + return name or "sequence", "".join(seq_parts) diff --git a/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/scripts/gi_ensembl.py b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/scripts/gi_ensembl.py new file mode 100644 index 0000000..52bd8a6 --- /dev/null +++ b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/scripts/gi_ensembl.py @@ -0,0 +1,290 @@ +"""Ensembl REST helpers: resolve a gene symbol or genomic region to reference sequence. + +Self-contained — the only dependency is ``requests``. It calls the public +Ensembl REST API (rest.ensembl.org); no API key is required, and no Genomic +Intelligence key is used here. + +Three flows used by the skill's acquisition CLI (``gi_fetch.py``): + - fetch_by_symbol(symbol) → full gene-body sequence + - fetch_region_by_coords("chr:start-end") → region sequence + - fetch_gene_window_for_expression(symbol) → exactly 9,198 bp centred on the TSS + +Base URL: ``GI_ENSEMBL_URL`` env, default ``https://rest.ensembl.org``. +""" + +from __future__ import annotations + +import os +import re +from dataclasses import dataclass +from typing import Optional, Tuple + +import requests + +# Expression scoring window: the fixed 9,198 bp the expression task scores, +# 4,599 bp either side of the TSS. Published as ``minLength`` on the expression +# request schema at https://api.genomicintelligence.ai/v1/openapi.json. Building +# the window here means the request needs no tss_index; longer submissions must +# supply one. +EXPRESSION_SEQUENCE_LENGTH = 9_198 + +DEFAULT_ENSEMBL_URL = "https://rest.ensembl.org" +_USER_AGENT = "BioNeMo-GI-Skill/0.1.0" + + +def ensembl_base_url() -> str: + return os.environ.get("GI_ENSEMBL_URL", DEFAULT_ENSEMBL_URL).rstrip("/") + + +class EnsemblError(RuntimeError): + pass + + +@dataclass +class GeneLocus: + ensembl_id: str + seq_region: str # e.g. "17" + start: int + end: int + strand: int # +1 or -1 + species: str + display_name: str + # Canonical-transcript boundaries (when resolved via expand=1). Gene-level + # start/end can sit far from the real TSS — HBB's gene end is 2,324 bp from + # its canonical TSS, ACTB's is 33,301 bp — so expression windowing must use + # the canonical transcript, not the gene body. + canonical_start: Optional[int] = None + canonical_end: Optional[int] = None + + @property + def tss(self) -> int: + """Transcription start site: transcript start on +strand, end on -strand. + + Requires the canonical transcript, and raises rather than falling back + to the gene body. A gene-body fallback puts ACTB's window 33,301 bp off + its real TSS. That window is still exactly 9,198 bp, so the + client-side size gate passes, no ``tss_index`` is sent, and the API + returns a confident score for the wrong locus — correctly sized, + wrongly centred, with no client-side tell. Refusing is the only honest + option. + + Raises: + EnsemblError: no canonical transcript was resolved for this gene. + """ + if self.canonical_start is None or self.canonical_end is None: + raise EnsemblError( + f"{self.display_name} ({self.ensembl_id}): no canonical transcript " + f"resolved, so the TSS is unknown. Expression windowing needs the " + f"canonical transcript — the gene body can sit tens of kb away " + f"(ACTB: 33,301 bp) and would score the wrong window at full " + f"confidence. Re-fetch with expand=1, or supply the window and " + f"tss_index explicitly." + ) + return self.canonical_start if self.strand >= 0 else self.canonical_end + + +def _get(path: str, *, headers: Optional[dict] = None, params: Optional[dict] = None, + timeout: float = 30.0) -> requests.Response: + """GET against the Ensembl REST base, mapping transport failures to EnsemblError.""" + base_headers = {"User-Agent": _USER_AGENT, "Accept": "application/json"} + if headers: + base_headers.update(headers) + url = f"{ensembl_base_url()}{path}" + try: + return requests.get(url, headers=base_headers, params=params, timeout=timeout) + except requests.RequestException as exc: + raise EnsemblError( + f"could not reach Ensembl at {ensembl_base_url()} ({type(exc).__name__})" + ) from exc + + +def lookup_symbol(symbol: str, species: str = "human", expand: bool = False) -> GeneLocus: + """Resolve a gene symbol to its genomic locus. + + With ``expand=True`` the gene's transcripts are pulled too, and the + canonical transcript's boundaries are recorded on the locus (used for + TSS-accurate expression windowing). + """ + r = _get( + f"/lookup/symbol/{species}/{symbol}", + params={"expand": 1 if expand else 0}, + ) + if r.status_code == 404: + raise EnsemblError(f"gene symbol {symbol!r} not found in {species}") + if not r.ok: + hint = "" + if r.status_code == 400: + # A 400 here is almost always an unrecognised species token: Ensembl + # wants the production name (lowercase, underscored), so 'drosophila' + # / 'Drosophila melanogaster' fail where 'drosophila_melanogaster' + # works. Point the caller at the canonical form. + hint = ( + " — check the species token: Ensembl expects a production name " + "(lowercase, underscored), e.g. 'drosophila_melanogaster', " + "'mus_musculus', not 'drosophila' or 'Drosophila melanogaster'" + ) + raise EnsemblError( + f"Ensembl lookup failed ({r.status_code}) for {symbol!r} " + f"in species {species!r}{hint}" + ) + d = r.json() + canonical_start: Optional[int] = None + canonical_end: Optional[int] = None + if expand: + transcripts = d.get("Transcript") or [] + canonical = next((t for t in transcripts if t.get("is_canonical") == 1), None) + if canonical is not None: + canonical_start = int(canonical["start"]) + canonical_end = int(canonical["end"]) + return GeneLocus( + ensembl_id=d["id"], + seq_region=str(d["seq_region_name"]), + start=int(d["start"]), + end=int(d["end"]), + strand=int(d.get("strand", 1)), + species=species, + display_name=d.get("display_name", symbol), + canonical_start=canonical_start, + canonical_end=canonical_end, + ) + + +def fetch_region(seq_region: str, start: int, end: int, species: str = "human", + strand: int = 1) -> str: + """Fetch raw nucleotide sequence for a 1-based inclusive region.""" + region = f"{seq_region}:{start}..{end}:{strand}" + r = _get(f"/sequence/region/{species}/{region}", headers={"Accept": "text/x-fasta"}) + if not r.ok: + raise EnsemblError(f"Ensembl sequence fetch failed ({r.status_code}) for {region}") + lines = [ln for ln in r.text.splitlines() if ln and not ln.startswith(">")] + return "".join(lines).upper() + + +# Coordinate string → (chrom, start, end). Deliberately lenient about the forms +# users paste: commas, en/em dashes, `..`, and an optional `chr` prefix. +_REGION_RE = re.compile(r"^(?:chr)?([A-Za-z0-9]+):(\d+)(?:-(\d+))?$", re.IGNORECASE) + + +def parse_region(text: str) -> Tuple[str, int, int]: + """Parse 'chr8:127,680,000-127,800,000' → ('8', 127680000, 127800000). + + Accepts commas, en/em-dashes and ``..`` (normalised to ``-``), an optional + ``chr`` prefix, and spaces around separators. A bare position (no end) + defaults to a 1,000 bp window. Raises EnsemblError if unparseable. + """ + normalized = ( + text.replace(",", "") + .replace("–", "-") # en dash + .replace("—", "-") # em dash + .replace("..", "-") + ) + normalized = re.sub(r"\s*-\s*", "-", normalized) + normalized = re.sub(r"\s*:\s*", ":", normalized).strip() + m = _REGION_RE.match(normalized) + if not m: + raise EnsemblError( + f"could not parse genomic region {text!r}; expected e.g. " + "'chr8:127,680,000-127,800,000'" + ) + chrom = m.group(1) + start = int(m.group(2)) + end = int(m.group(3)) if m.group(3) else start + 1000 + if start < 1: + raise EnsemblError(f"region start must be >= 1 (got {start})") + if end < start: + raise EnsemblError(f"region end ({end:,}) is before start ({start:,})") + return chrom, start, end + + +def fetch_region_by_coords(region: str, species: str = "human", strand: int = 1, + flank_bp: int = 0) -> Tuple[str, dict]: + """Coordinate string → reference sequence + meta. Plus strand by default.""" + chrom, raw_start, raw_end = parse_region(region) + start = max(1, raw_start - flank_bp) + end = raw_end + flank_bp + seq = fetch_region(chrom, start, end, species, strand=strand) + meta = { + "region": f"{chrom}:{start}-{end}", + "strand": strand, + "species": species, + "length": len(seq), + } + return seq, meta + + +def fetch_by_symbol(symbol: str, species: str = "human", flank_bp: int = 0) -> Tuple[str, dict]: + """Symbol → full gene-body sequence (optionally flanked). Returns (seq, meta).""" + locus = lookup_symbol(symbol, species) + start = max(1, locus.start - flank_bp) + end = locus.end + flank_bp + seq = fetch_region(locus.seq_region, start, end, species, strand=locus.strand) + meta = { + "ensembl_id": locus.ensembl_id, + "region": f"{locus.seq_region}:{start}-{end}", + "strand": locus.strand, + "species": species, + "gene": locus.display_name, + } + return seq, meta + + +def expression_window_bounds(tss: int, strand: int) -> Tuple[int, int]: + """Genomic bounds of the EXPRESSION_SEQUENCE_LENGTH window around a TSS. + + The API scores ``sequence[tss_index-4599 : tss_index+4599]`` and, for a + submission of exactly EXPRESSION_SEQUENCE_LENGTH bp, defaults ``tss_index`` + to 4,599 — the only legal value. So the TSS must land at offset 4,599 of + the sequence *as submitted*, and which genomic base that is depends on the + strand: Ensembl reverse-complements the region for ``strand=-1``, so the + sequence reads from ``end`` down to ``start`` and the TSS sits at + ``end - tss`` rather than ``tss - start``. Giving the extra base to the + high side on the minus strand puts both strands at 4,599. + + A one-base error here cannot be caught downstream: the window is still + exactly 9,198 bp, so the client-side size gate passes, no ``tss_index`` is + sent, and the API returns a confident score for a window shifted by one. + """ + half = EXPRESSION_SEQUENCE_LENGTH // 2 # 4599 + if strand == -1: + start, end = tss - half + 1, tss + half + else: + start, end = tss - half, tss + half - 1 + return start, end + + +def fetch_gene_window_for_expression(symbol: str, species: str = "human") -> Tuple[str, dict]: + """Symbol → exactly EXPRESSION_SEQUENCE_LENGTH bp centred on the TSS. + + The expression model demands a precise window. We take the TSS from the + gene's *canonical transcript* (expand=1) — gene-body boundaries can sit + thousands of bp from the real TSS (HBB: 2,324 bp; ACTB: 33,301 bp), which + would mis-centre the window and tank the prediction — and take the window + from ``expression_window_bounds``, which centres it the way the API reads + it on either strand. + """ + locus = lookup_symbol(symbol, species, expand=True) + tss = locus.tss + start, end = expression_window_bounds(tss, locus.strand) + if start < 1: + raise EnsemblError( + f"{symbol} TSS too close to chromosome start to extract a " + f"{EXPRESSION_SEQUENCE_LENGTH} bp window" + ) + seq = fetch_region(locus.seq_region, start, end, species, strand=locus.strand) + if len(seq) != EXPRESSION_SEQUENCE_LENGTH: + raise EnsemblError( + f"expected {EXPRESSION_SEQUENCE_LENGTH} bp, Ensembl returned {len(seq)}" + ) + meta = { + "ensembl_id": locus.ensembl_id, + "tss": tss, + # Always canonical: locus.tss raises rather than falling back to the + # gene body, so no other provenance is reachable. + "tss_source": "canonical-transcript", + "region": f"{locus.seq_region}:{start}-{end}", + "strand": locus.strand, + "species": species, + "gene": locus.display_name, + "window": "TSS-centred", + } + return seq, meta diff --git a/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/scripts/gi_fetch.py b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/scripts/gi_fetch.py new file mode 100644 index 0000000..ebe28f3 --- /dev/null +++ b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/scripts/gi_fetch.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +"""Resolve a gene or genomic region to a FASTA file, via Ensembl. + +This is the *acquisition* half of the skill. The agent calls it when the +user names a gene or locus instead of supplying a FASTA — it fetches reference +sequence from Ensembl (public, no API key) and writes a single-record FASTA that +``gi_predict.py --input`` then consumes. + +Modes (mutually exclusive): + --gene SYMBOL full gene-body sequence (e.g. TP53) + --region chr17:7.6M-7.7M sequence for a coordinate range + --gene SYMBOL --for-expression + EXACTLY 9,198 bp centred on the canonical TSS — + the only window the expression model accepts + +Examples: + python scripts/gi_fetch.py --gene TP53 --out tp53.fa + python scripts/gi_fetch.py --region chr17:7,661,779-7,687,546 --out region.fa + python scripts/gi_fetch.py --gene HBB --for-expression --out hbb_tss.fa + +On success the FASTA path is printed to stdout (so the agent can pipe it into +gi_predict.py); a one-line provenance summary goes to stderr. +""" + +from __future__ import annotations + +import argparse +import sys +from pathlib import Path + +SCRIPT_DIR = Path(__file__).resolve().parent +sys.path.insert(0, str(SCRIPT_DIR)) +from gi_ensembl import ( # noqa: E402 + EnsemblError, + fetch_by_symbol, + fetch_gene_window_for_expression, + fetch_region_by_coords, +) + + +def _parse_args() -> argparse.Namespace: + p = argparse.ArgumentParser( + description="Fetch reference sequence from Ensembl and write a FASTA." + ) + src = p.add_mutually_exclusive_group(required=True) + src.add_argument("--gene", type=str, help="Gene symbol, e.g. TP53.") + src.add_argument("--region", type=str, help="Coordinate range, e.g. chr17:7,661,779-7,687,546.") + p.add_argument( + "--for-expression", + action="store_true", + help="With --gene: return exactly 9,198 bp centred on the canonical TSS " + "(required by the expression task). Ignored with --region.", + ) + p.add_argument( + "--species", + type=str, + default="human", + help="Ensembl production name (default: human; e.g. mus_musculus, drosophila_melanogaster).", + ) + p.add_argument("--flank-bp", type=int, default=0, help="Extra bp on each side (gene/region only).") + p.add_argument("--strand", type=int, default=1, choices=(1, -1), help="Strand for --region (default 1).") + p.add_argument("--out", type=Path, required=True, help="Output FASTA path to write.") + return p.parse_args() + + +def main() -> int: + args = _parse_args() + try: + if args.region: + if args.for_expression: + print( + "[gi-fetch] --for-expression needs --gene (TSS centring requires a " + "gene's canonical transcript, not a raw region).", + file=sys.stderr, + ) + return 1 + seq, meta = fetch_region_by_coords( + args.region, species=args.species, strand=args.strand, flank_bp=args.flank_bp + ) + header = f"{meta['region']}|{args.species}|strand:{meta['strand']}" + elif args.for_expression: + seq, meta = fetch_gene_window_for_expression(args.gene, species=args.species) + header = ( + f"{meta['gene']}|{meta['ensembl_id']}|{meta['region']}|{args.species}|" + f"strand:{meta['strand']}|TSS:{meta['tss']}|{meta['tss_source']}" + ) + else: + seq, meta = fetch_by_symbol(args.gene, species=args.species, flank_bp=args.flank_bp) + header = ( + f"{meta['gene']}|{meta['ensembl_id']}|{meta['region']}|{args.species}|" + f"strand:{meta['strand']}" + ) + except EnsemblError as e: + print(f"[gi-fetch] {e}", file=sys.stderr) + return 2 + + args.out.parent.mkdir(parents=True, exist_ok=True) + wrapped = "\n".join(seq[i : i + 70] for i in range(0, len(seq), 70)) + args.out.write_text(f">{header}\n{wrapped}\n") + + print( + f"[gi-fetch] wrote {len(seq):,} bp → {args.out} ({header})", + file=sys.stderr, + ) + # stdout = just the path, so the agent can chain: FASTA=$(gi_fetch ...) + print(str(args.out)) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/scripts/gi_predict.py b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/scripts/gi_predict.py new file mode 100644 index 0000000..edc6140 --- /dev/null +++ b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/scripts/gi_predict.py @@ -0,0 +1,651 @@ +#!/usr/bin/env python3 +"""Unified CLI for the Genomic Intelligence DNA-sequence tasks. + +One entry point covers all six tasks exposed by the hosted API. Each is its own +published operation at ``/v1/tasks//predict``, with a separate request +schema per task: + + promoter · splice · enhancer · chromatin · expression · annotation + +It parses a single-record FASTA, calls the API, and writes ``report.md`` + +``result.json`` + ``reproducibility/`` to the output directory. Delivery is +synchronous except for ``annotation``, which defaults to async because it is +slow — the API accepts either mode on every task. + +Usage: + python scripts/gi_predict.py --task promoter --demo + python scripts/gi_predict.py --task splice --input my.fa --output out/ + python scripts/gi_predict.py --task expression --demo --description "K562 cells" + +Auth: set GI_API_KEY in the environment (see references/authentication.md). +""" + +from __future__ import annotations + +import argparse +import json +import os +import shlex +import sys +import time +from datetime import datetime, timezone +from pathlib import Path +from typing import Any, Dict, Optional + +# Self-contained: import the sibling client module regardless of CWD. +SCRIPT_DIR = Path(__file__).resolve().parent +sys.path.insert(0, str(SCRIPT_DIR)) +import requests # noqa: E402 (transport errors surface at the call boundary) + +from gi_client import Client, FastaError, GIError, read_fasta # noqa: E402 + +SKILL_DIR = SCRIPT_DIR.parent +DEMO_DIR = SKILL_DIR / "assets" / "demo" + +DISCLAIMER = ( + "Genomic Intelligence is a research tool. It is not a medical device and " + "does not provide clinical diagnoses. Consult a qualified professional " + "before making any medical decisions." +) + + +class TaskSpec: + """Per-task metadata: input bounds, default delivery mode, demo fixture.""" + + def __init__( + self, + min_bp: int, + max_bp: int, + async_default: bool, + demo: str, + window_bp: Optional[int] = None, + ) -> None: + self.min_bp = min_bp + self.max_bp = max_bp + # Delivery mode this runner picks by default. The API accepts BOTH + # modes on every task (Prefer: respond-async is a per-request header), + # so this is a latency choice, not a constraint. + self.async_default = async_default + self.demo = demo + # Fixed scoring-window width, if the task has one (expression: 9,198 bp). + # Anything longer than the window needs an explicit --tss-index. + self.window_bp = window_bp + + def validate(self, length: int) -> Optional[str]: + if length < self.min_bp: + return f"sequence too short: {length:,} bp < {self.min_bp:,} bp minimum" + if length > self.max_bp: + return f"sequence too long: {length:,} bp > {self.max_bp:,} bp maximum" + return None + + +# These bounds are a LOCAL MIRROR, not the authority. The authority is the +# `minLength`/`maxLength` published on each task's request schema in the live +# OpenAPI doc (https://api.genomicintelligence.ai/v1/openapi.json). Re-read it +# if a rejection here disagrees with the server. +# +# Each task has its own floor — the strictest its models need — enforced at +# request validation before any model loads. There are no per-model floors, so +# --model can never make a rejected length legal. The floor is admission control, NOT a statement about +# regime: a sequence above the floor but shorter than the selected model's +# `bio_spec.context_window_bp` is accepted and scored against a window padded out +# to the context window. Compare your length against `context_window_bp` (from +# GET /v1/tasks/{task}/models) to know whether the model saw real sequence or +# padding. Every task caps at 500,000 bp. Under-floor and over-max are both +# 422 validation_failed server-side — a 413 means the 16 MiB raw-body cap, never +# a long sequence. +PROMOTER_MIN_BP = 300 +SPLICE_MIN_BP = 100 +ENHANCER_MIN_BP = 50 +CHROMATIN_MIN_BP = 200 +ANNOTATION_MIN_BP = 1_000 +MAX_BP = 500_000 + +# expression's floor is also the width of the single window the model scores: +# sequence[tss_index-4599 : tss_index+4599]. Send a pre-cut 9,198 bp window, or +# send up to 500 kb plus --tss-index and let the server slice. +# +# The default path is deliberately stricter than the API: with no --tss-index, +# this client requires *exactly* 9,198 bp rather than merely at-or-above the +# floor. That is the tripwire — the server will happily score a 9,198 bp window +# cut from the wrong place and return a confident 200, and there is no +# client-side tell for a mis-centred window. Requiring the exact width keeps the +# caller visibly responsible for TSS-centring. --tss-index is the explicit +# opt-in that widens the accepted range to the full 9,198–500,000 bp the API +# allows and hands the cut to the server; it is range-checked below, and what +# was actually scored is echoed back from response meta rather than assumed. +EXPRESSION_WINDOW_BP = 9_198 +EXPRESSION_TSS_RADIUS = EXPRESSION_WINDOW_BP // 2 # 4,599 + +TASKS: Dict[str, TaskSpec] = { + "promoter": TaskSpec(PROMOTER_MIN_BP, MAX_BP, False, "promoter_tp53.fa"), + "splice": TaskSpec(SPLICE_MIN_BP, MAX_BP, False, "splice_hbb.fa"), + "enhancer": TaskSpec(ENHANCER_MIN_BP, MAX_BP, False, "enhancer_eve.fa"), + "chromatin": TaskSpec(CHROMATIN_MIN_BP, MAX_BP, False, "chromatin_active_promoter_chr19.fa"), + "expression": TaskSpec( + EXPRESSION_WINDOW_BP, MAX_BP, False, "expression_hbb_k562.fa", + window_bp=EXPRESSION_WINDOW_BP, + ), + "annotation": TaskSpec(ANNOTATION_MIN_BP, MAX_BP, True, "annotation_tp53.fa"), +} + + +def _parse_args() -> argparse.Namespace: + p = argparse.ArgumentParser( + description="Genomic Intelligence DNA-sequence prediction (one CLI, six tasks)." + ) + p.add_argument( + "--task", + required=True, + choices=sorted(TASKS), + help="Which prediction task to run.", + ) + p.add_argument("--input", type=Path, dest="input_file", help="Input FASTA (single record).") + p.add_argument("--output", type=Path, default=None, help="Output directory (default: /tmp/gi-).") + p.add_argument("--demo", action="store_true", help="Run with the bundled example FASTA for the task.") + p.add_argument("--model", type=str, default=None, help="Override the default model for the task.") + p.add_argument( + "--description", + type=str, + default=None, + help=( + "Cell type / assay context. REQUIRED by expression; not accepted by " + "any other task (their options objects are closed), so it is dropped " + "with a warning if passed elsewhere." + ), + ) + p.add_argument( + "--tss-index", + type=int, + default=None, + dest="tss_index", + help=( + "expression only: 0-based TSS offset into the sequence (whitespace " + "stripped). REQUIRED unless the sequence is exactly 9,198 bp. The " + "server scores sequence[tss_index-4599 : tss_index+4599]." + ), + ) + p.add_argument("--api-key", type=str, default=None, help="Override GI_API_KEY env.") + p.add_argument("--base-url", type=str, default=None, help="Override GI_BASE_URL (default: https://api.genomicintelligence.ai).") + return p.parse_args() + + +def _resolve_input(args: argparse.Namespace, spec: TaskSpec) -> Path: + # Running the demo has to be asked for. Falling back to it when --input is + # simply absent produces a full report, with a real request id and real + # scores, for a sequence the caller never supplied — and nothing in the + # output says so. + if not args.demo and args.input_file is None: + print( + "Error: no input. Pass --input , or --demo to run the " + f"bundled {spec.demo} fixture.", + file=sys.stderr, + ) + sys.exit(2) + if args.demo: + demo_path = DEMO_DIR / spec.demo + if not demo_path.exists(): + print(f"Error: bundled demo fixture missing at {demo_path}", file=sys.stderr) + sys.exit(1) + return demo_path + if not args.input_file.exists(): + print(f"Error: --input file not found: {args.input_file}", file=sys.stderr) + sys.exit(1) + return args.input_file + + +# Per-item arrays that belong in result.json, not in the compact stdout payload. +_BULKY_SUMMARY_KEYS = {"regions", "sites", "transcripts", "raw_summary"} + + +class ResponseShapeError(RuntimeError): + """A 2xx body whose nested fields contradict their documented types. + + Distinct from `GIError`, which covers what the API itself reported. This is + a well-formed envelope carrying a field the contract says is an object or an + array and that arrived as something else. + """ + + +def _as_obj(v: Any, field: str) -> Dict[str, Any]: + """Read a response field documented as an object. + + `_require_envelope` guarantees `data` is a non-empty object; it deliberately + does not police per-task fields nested inside it, because it is shared by + six tasks and must not encode any one task's schema. So the checking happens + here. + + Absent or null is legitimate — a task that has no `prediction` omits it — and + becomes `{}`. A field that is *present with the wrong type* is a malformed + response and is reported as one. + + Two failure modes have to be avoided here, and they pull in opposite + directions. The `x or {}` idiom this replaces handled null and absent but not + a truthy wrong type: a `"summary"` arriving as a string passed `or {}` + untouched and then raised AttributeError on `.get`, in the report writer, + which runs after main()'s try/except has closed — a traceback that reads as a + client bug. Substituting `{}` for it instead fixes the traceback and creates + something worse: a zero-valued report printed with `"ok": true`, so a bad + response is indistinguishable from a real prediction of nothing. Raise a + typed error that main() turns into the same diagnostic it gives any other + malformed response, and it is neither. + """ + if v is None: + return {} + if not isinstance(v, dict): + raise ResponseShapeError( + f"{field} should be an object, got {type(v).__name__}" + ) + return v + + +def _as_objs(v: Any, field: str) -> list: + """Same, for a field documented as an array of objects. + + A truthy non-list (a bare string) is iterable, so `or []` let it through and + the row loop iterated its characters; non-object elements fail the same way. + Neither is silently dropped — an array whose elements are the wrong type is a + malformed response, and a report missing rows it should have had is exactly + the silent wrong answer this is here to prevent. + """ + if v is None: + return [] + if not isinstance(v, list): + raise ResponseShapeError( + f"{field} should be an array, got {type(v).__name__}" + ) + for i, x in enumerate(v): + if not isinstance(x, dict): + raise ResponseShapeError( + f"{field}[{i}] should be an object, got {type(x).__name__}" + ) + return v + + +def _summarize(task: str, body: Dict[str, Any]) -> Dict[str, Any]: + """Pick the most useful headline numbers per task from `data`.""" + data = _as_obj(body.get("data"), "data") + summary = _as_obj(data.get("summary"), "data.summary") + out: Dict[str, Any] = {"task": task, "model": data.get("model")} + if task == "promoter": + out["promoter_windows"] = summary.get("promoter_windows") + out["total_windows"] = summary.get("total_windows") + out["regions"] = _as_objs(data.get("regions"), "data.regions") + elif task == "splice": + out["sites_found"] = summary.get("total_sites", summary.get("sites_found")) + out["donor_sites"] = summary.get("donor_sites") + out["acceptor_sites"] = summary.get("acceptor_sites") + out["sites"] = _as_objs(data.get("sites"), "data.sites") + elif task == "enhancer": + out["windows_processed"] = summary.get("total_windows", summary.get("windows_processed")) + out["dev_score_max"] = summary.get("dev_score_max") + out["hk_score_max"] = summary.get("hk_score_max") + elif task == "chromatin": + out["windows_processed"] = summary.get("total_windows", summary.get("windows_processed")) + out["total_annotations"] = summary.get("total_annotations") + elif task == "expression": + pred = _as_obj(data.get("prediction"), "data.prediction") + out["log_tpm"] = pred.get("expression_log_tpm") + out["tpm"] = pred.get("expression_tpm") + # Windowing provenance: an in-range but *wrong* tss_index scores the + # wrong 9,198 bp window and still returns 200, so surface what was + # actually scored rather than trusting the request. + counts = _as_obj( + _as_obj(body.get("meta"), "meta").get("task_specific_counts"), + "meta.task_specific_counts", + ) + out["tss_index"] = counts.get("tss_index") + out["scored_window"] = counts.get("scored_window") + elif task == "annotation": + out["transcripts_found"] = summary.get("total_transcripts", summary.get("transcripts_found")) + out["transcripts"] = _as_objs(data.get("transcripts"), "data.transcripts") + out["raw_summary"] = summary + return out + + +def _fmt(v: Any, spec: str = ".3f") -> str: + return format(v, spec) if isinstance(v, (int, float)) else str(v) + + +def _headline_lines(task: str, summary: Dict[str, Any]) -> list[str]: + lines: list[str] = [] + if task == "promoter": + lines.append( + f"- Promoter windows: **{summary.get('promoter_windows', 0)}** / " + f"{summary.get('total_windows', 0)} total" + ) + regions = _as_objs(summary.get("regions"), "data.regions") + if regions: + lines += ["", "| Name | Start | End | Score |", "|---|---|---|---|"] + for r in regions[:20]: + lines.append( + f"| {r.get('name', '-')} | {r.get('start', '-')} | " + f"{r.get('end', '-')} | {_fmt(r.get('score', '-'))} |" + ) + elif task == "splice": + lines.append( + f"- Splice sites found: **{summary.get('sites_found') or 0}** " + f"({summary.get('donor_sites') or 0} donor + {summary.get('acceptor_sites') or 0} acceptor)" + ) + sites = _as_objs(summary.get("sites"), "data.sites")[:20] + if sites: + lines += ["", "| Name | Start | Type | Score |", "|---|---|---|---|"] + for s in sites: + lines.append( + f"| {s.get('name', '-')} | {s.get('start', '-')} | " + f"{s.get('site_type', '-')} | {_fmt(s.get('score', '-'))} |" + ) + elif task == "enhancer": + lines.append(f"- Windows processed: **{summary.get('windows_processed') or 0}**") + dev, hk = summary.get("dev_score_max"), summary.get("hk_score_max") + if dev is not None: + lines.append(f"- Max developmental-enhancer score: **{_fmt(dev)}**") + if hk is not None: + lines.append(f"- Max housekeeping-enhancer score: **{_fmt(hk)}**") + elif task == "chromatin": + lines.append(f"- Windows processed: **{summary.get('windows_processed') or 0}**") + lines.append(f"- Total annotations across all tracks: **{summary.get('total_annotations') or 0}**") + elif task == "expression": + log_tpm, tpm = summary.get("log_tpm"), summary.get("tpm") + if log_tpm is not None: + tail = f" ≈ {tpm:.2f} TPM" if isinstance(tpm, (int, float)) else "" + lines.append(f"- Predicted expression: **{_fmt(log_tpm, '.4f')} log(TPM+1)**{tail}") + else: + lines.append("- See `result.json` for the full prediction payload.") + elif task == "annotation": + lines.append(f"- Transcripts found: **{summary.get('transcripts_found') or 0}**") + tx = _as_objs(summary.get("transcripts"), "data.transcripts")[:20] + if tx: + lines += ["", "| Name | Start | End | Strand | Score |", "|---|---|---|---|---|"] + for t in tx: + lines.append( + f"| {t.get('name', '-')} | {t.get('start', '-')} | " + f"{t.get('end', '-')} | {t.get('strand', '-')} | {_fmt(t.get('score', '-'))} |" + ) + return lines + + +def _repro_command( + task: str, + input_path: Path, + output_dir: Path, + model: Optional[str] = None, + description: Optional[str] = None, + tss_index: Optional[int] = None, +) -> str: + """Build the exact re-runnable invocation for reproducibility/command.sh. + + Emits --model, --description and --tss-index only when they were supplied, + so a replay reproduces the original call: expression requires --description + (no default) and --tss-index whenever the sequence is not exactly 9,198 bp, + and a non-default --model must survive. Uses python3 and shell-quotes every + value so paths/descriptions with spaces round-trip. + """ + parts = [ + "python3 scripts/gi_predict.py", + f"--task {task}", + f"--input {shlex.quote(str(input_path))}", + f"--output {shlex.quote(str(output_dir))}", + ] + if model: + parts.append(f"--model {shlex.quote(model)}") + if description is not None: + parts.append(f"--description {shlex.quote(description)}") + if tss_index is not None: + parts.append(f"--tss-index {tss_index}") + return " ".join(parts) + + +def _write_report( + task: str, + summary: Dict[str, Any], + body: Dict[str, Any], + output_dir: Path, + input_path: Path, + sequence_name: str, + sequence_length: int, + elapsed_ms: float, + model: Optional[str] = None, + description: Optional[str] = None, + tss_index: Optional[int] = None, +) -> None: + output_dir.mkdir(parents=True, exist_ok=True) + (output_dir / "result.json").write_text( + json.dumps({"summary": summary, "full_response": body}, indent=2) + ) + + meta = _as_obj(body.get("meta"), "meta") + report_model = summary.get("model") or "—" # effective model for the report + lines = [ + f"# Genomic Intelligence — {task} report", + "", + f"- **Sequence**: `{sequence_name}` ({sequence_length:,} bp)", + f"- **Input file**: `{input_path}`", + f"- **Model**: `{report_model}`", + f"- **Inference time**: {_fmt(meta.get('inference_time_ms', elapsed_ms), '.0f')} ms", + f"- **Request ID**: `{meta.get('request_id', '—')}`", + f"- **Generated**: {datetime.now(timezone.utc).isoformat(timespec='seconds')}", + "", + "## Headline result", + "", + *_headline_lines(task, summary), + "", + "## Reproducibility", + "", + "- `reproducibility/command.sh` — exact invocation", + "- `result.json` — full `{data, meta}` response from the API", + "", + "## API", + "", + f"`POST /v1/tasks/{task}/predict` on `https://api.genomicintelligence.ai` " + "— see .", + "", + "---", + "", + f"_{DISCLAIMER}_", + "", + ] + (output_dir / "report.md").write_text("\n".join(lines)) + + repro = output_dir / "reproducibility" + repro.mkdir(exist_ok=True) + cmd = _repro_command(task, input_path, output_dir, model, description, tss_index) + "\n" + (repro / "command.sh").write_text("#!/usr/bin/env bash\nset -euo pipefail\n" + cmd) + (repro / "command.sh").chmod(0o755) + (repro / "environment.json").write_text( + json.dumps( + { + "skill": "genomic-intelligence-nim", + "skill_version": "0.1.0", + "task": task, + "api_base_url": os.environ.get("GI_BASE_URL", "https://api.genomicintelligence.ai"), + "model": summary.get("model"), + "request_id": meta.get("request_id"), + "timestamp": datetime.now(timezone.utc).isoformat(timespec="seconds"), + }, + indent=2, + ) + ) + + +def main() -> int: + args = _parse_args() + task = args.task + spec = TASKS[task] + output_dir = args.output or Path(f"/tmp/gi-{task}") + + input_path = _resolve_input(args, spec) + try: + sequence_name, sequence = read_fasta(input_path) + except FastaError as e: + print(f"[gi-{task}] invalid input — {e}", file=sys.stderr) + return 1 + if not sequence: + print(f"Error: parsed an empty sequence from {input_path}", file=sys.stderr) + return 1 + + length_err = spec.validate(len(sequence)) + if length_err: + print(f"[gi-{task}] invalid input — {length_err}", file=sys.stderr) + if task == "expression": + print( + " The expression model scores exactly one 9,198 bp TSS-centred window " + "(TSS ± 4,599), so 9,198 bp is a hard floor. Send a pre-cut window, or a " + "longer locus plus --tss-index. See references/tasks.md#expression.", + file=sys.stderr, + ) + elif len(sequence) < spec.min_bp: + print( + f" {task} needs at least {spec.min_bp:,} bp. This floor is published as " + f"minLength on the endpoint's request schema; the server rejects a shorter " + f"sequence with 422 validation_failed. See references/tasks.md.", + file=sys.stderr, + ) + return 1 + + tss_index = args.tss_index + if task == "expression": + if tss_index is None: + # Default path: exact window only. See the EXPRESSION_WINDOW_BP note + # above for why this is stricter than the API's own floor. + if len(sequence) != EXPRESSION_WINDOW_BP: + print( + f"[gi-expression] --tss-index is required unless the sequence is " + f"exactly {EXPRESSION_WINDOW_BP:,} bp (got {len(sequence):,} bp). " + "It is the 0-based TSS offset into the sequence. " + "See references/tasks.md#expression.", + file=sys.stderr, + ) + return 1 + else: + lo, hi = EXPRESSION_TSS_RADIUS, len(sequence) - EXPRESSION_TSS_RADIUS + if not (lo <= tss_index <= hi): + print( + f"[gi-expression] --tss-index {tss_index:,} outside the allowed range " + f"[{lo:,}, {hi:,}] for a {len(sequence):,} bp sequence — the model needs " + f"a full ±{EXPRESSION_TSS_RADIUS:,} bp window around the TSS; submit more " + "flanking sequence.", + file=sys.stderr, + ) + return 1 + elif tss_index is not None: + print(f"[gi-{task}] --tss-index applies to expression only; ignoring it.", file=sys.stderr) + tss_index = None + + if task == "expression" and not args.description: + print( + "[gi-expression] --description is required (e.g. \"K562 cells\"). " + "It selects the expression context. See references/tasks.md#expression.", + file=sys.stderr, + ) + return 1 + + try: + client = Client(api_key=args.api_key, base_url=args.base_url) + except RuntimeError as e: + print(str(e), file=sys.stderr) + return 2 + # `options` is a closed (additionalProperties: false) object per task, and + # only ExpressionOptions declares `description`. Forwarding it on any other + # task is a hard 422 validation_failed (extra_forbidden), not a no-op — so + # drop it locally rather than letting the server reject the call. + options: Dict[str, Any] = {} + if args.description is not None: + if task == "expression": + options["description"] = args.description + else: + print( + f"[gi-{task}] --description applies to expression only; ignoring it " + f"({task} rejects unknown options keys with 422).", + file=sys.stderr, + ) + + print( + f"[gi-{task}] sequence_name={sequence_name} length={len(sequence):,} bp " + f"model={args.model or 'default'} mode={'async' if spec.async_default else 'sync'}", + file=sys.stderr, + ) + started = time.monotonic() + try: + if spec.async_default: + job_id = client.submit_async( + task, sequence=sequence, sequence_name=sequence_name, + model=args.model, options=options or None, + ) + print(f"[gi-{task}] submitted job_id={job_id}", file=sys.stderr) + + def _progress(p: Dict[str, Any]) -> None: + pct, msg = p.get("percent"), p.get("message", "") + if pct is not None: + print(f" {pct:>3}% {msg}", file=sys.stderr) + + body = client.wait_for_job(job_id, on_progress=_progress) + else: + body = client.predict( + task, sequence=sequence, sequence_name=sequence_name, + model=args.model, options=options or None, tss_index=tss_index, + ) + except GIError as e: + print(f"[gi-{task}] API error: {e}", file=sys.stderr) + return 2 + except requests.RequestException as e: + # Connection refused, DNS failure, TLS error, read timeout — routine + # for a hosted service and not the caller's bug. gi_ensembl already + # maps these to a diagnostic; do the same here rather than exiting + # with a traceback that reads like a client defect. + print(f"[gi-{task}] network error reaching the API: {type(e).__name__}: {e}", + file=sys.stderr) + return 2 + except TimeoutError as e: + # Raised by wait_for_job when a job outlives its poll deadline. + print(f"[gi-{task}] timed out waiting for the job: {e}", file=sys.stderr) + return 2 + except KeyError as e: + # A 2xx whose body is missing a field we index (e.g. data.job_id on an + # async submit). Malformed upstream response, not a usage error. + print(f"[gi-{task}] unexpected API response shape: missing {e}", file=sys.stderr) + return 2 + + elapsed_ms = (time.monotonic() - started) * 1000.0 + # The report writer runs outside the block above, so its own view of a + # malformed response needs its own handler. Without one a wrong-typed nested + # field either raised a traceback or — once the helpers coerced it — printed + # a zero-valued report with ok=true. Both are worse than exiting 2 with the + # field named. + try: + summary = _summarize(task, body) + meta = _as_obj(body.get("meta"), "meta") + _write_report( + task, summary, body, output_dir, input_path, sequence_name, len(sequence), + elapsed_ms, model=args.model, description=args.description, tss_index=tss_index, + ) + except ResponseShapeError as e: + print(f"[gi-{task}] unexpected API response shape: {e}", file=sys.stderr) + return 2 + print(f"[gi-{task}] OK — wrote {output_dir}/report.md ({elapsed_ms:.0f} ms wall)", file=sys.stderr) + + # stdout = a compact machine-readable summary so the agent gets the answer + # inline without reading a file. The bulky per-item arrays (regions / sites / + # transcripts) stay in result.json — only headline scalars go here. + headline = {k: v for k, v in summary.items() if k not in _BULKY_SUMMARY_KEYS} + stdout_payload = { + "ok": True, + "task": task, + "sequence_name": sequence_name, + "sequence_length_bp": len(sequence), + "model": summary.get("model"), + "request_id": meta.get("request_id"), + "inference_time_ms": meta.get("inference_time_ms"), + "result": headline, + "artifacts": { + "output_dir": str(output_dir), + "report": str(output_dir / "report.md"), + "result_json": str(output_dir / "result.json"), + }, + } + print(json.dumps(stdout_payload, indent=2)) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/tests/__init__.py b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/tests/conftest.py b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/tests/conftest.py new file mode 100644 index 0000000..a122f5a --- /dev/null +++ b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/tests/conftest.py @@ -0,0 +1,2 @@ +def pytest_configure(config): + config.addinivalue_line("markers", "integration: live-API test; requires GI_API_KEY") diff --git a/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/tests/test_input_validation.py b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/tests/test_input_validation.py new file mode 100644 index 0000000..a5041e6 --- /dev/null +++ b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/tests/test_input_validation.py @@ -0,0 +1,478 @@ +"""The client refuses malformed input instead of silently repairing it. + +Each case here is a real defect that shipped: the parser used to delete every +character outside ACGTN and to concatenate multi-record files into one chimeric +sequence, and the Ensembl helper used to fall back to gene-body coordinates +when no canonical transcript was found. All three produced a confident, +wrong-but-well-formed result with nothing for the caller to notice. +""" + +from __future__ import annotations + +import argparse +import sys +from pathlib import Path + +import pytest + +SCRIPTS = Path(__file__).resolve().parent.parent / "scripts" +sys.path.insert(0, str(SCRIPTS)) + +import gi_client # noqa: E402 +import gi_predict # noqa: E402 +from gi_client import FastaError, read_fasta # noqa: E402 +from gi_ensembl import ( # noqa: E402 + EXPRESSION_SEQUENCE_LENGTH, + EnsemblError, + GeneLocus, + expression_window_bounds, +) + + +def _write(tmp_path: Path, content: str) -> Path: + path = tmp_path / "input.fa" + path.write_text(content) + return path + + +class TestReadFasta: + def test_accepts_a_clean_single_record(self, tmp_path): + path = _write(tmp_path, ">chr1 some description\nACGT\nacgt\n") + name, seq = read_fasta(path) + assert name == "chr1" + assert seq == "ACGTACGT", "lowercase and line breaks are formatting, not content" + + def test_rejects_iupac_ambiguity_codes(self, tmp_path): + # Deleting these shifts every downstream coordinate, so the model would + # score a sequence the caller never supplied. + path = _write(tmp_path, ">x\nACGTRYKM\n") + with pytest.raises(FastaError) as exc: + read_fasta(path) + assert "outside ACGTN" in str(exc.value) + assert "IUPAC" in str(exc.value), "the hint should name why these specifically cannot be scored" + + def test_rejects_non_iupac_junk_without_the_iupac_hint(self, tmp_path): + path = _write(tmp_path, ">x\nACGT--NN\n") + with pytest.raises(FastaError) as exc: + read_fasta(path) + assert "IUPAC" not in str(exc.value) + + def test_rejects_multi_record_input(self, tmp_path): + path = _write(tmp_path, ">a\nACGT\n>b\nTTTT\n") + with pytest.raises(FastaError) as exc: + read_fasta(path) + assert "single FASTA record" in str(exc.value) + assert "found 2" in str(exc.value) + + def test_error_names_the_offending_line(self, tmp_path): + path = _write(tmp_path, ">x\nACGT\nACGTR\n") + with pytest.raises(FastaError) as exc: + read_fasta(path) + assert "line 3" in str(exc.value) + + def test_fasta_error_is_a_value_error(self): + # Callers doing broad input validation still catch it. + assert issubclass(FastaError, ValueError) + + @pytest.mark.parametrize( + "fixture", sorted((SCRIPTS.parent / "assets" / "demo").glob("*.fa")), ids=lambda p: p.name + ) + def test_bundled_demo_fixtures_still_parse(self, fixture): + # A stricter parser that rejects our own demos would be useless. + name, seq = read_fasta(fixture) + assert name and seq + + +class TestCanonicalTss: + def _locus(self, **kw): + base = dict( + ensembl_id="ENSG0", seq_region="11", start=1000, end=2000, + strand=1, species="human", display_name="TEST", + ) + base.update(kw) + return GeneLocus(**base) + + def test_uses_canonical_start_on_plus_strand(self): + locus = self._locus(canonical_start=1500, canonical_end=1900) + assert locus.tss == 1500 + + def test_uses_canonical_end_on_minus_strand(self): + locus = self._locus(strand=-1, canonical_start=1500, canonical_end=1900) + assert locus.tss == 1900 + + def test_refuses_when_no_canonical_transcript(self): + # The old fallback returned the gene body here. That window is still + # exactly 9,198 bp, so the client-side size gate passes and the API + # returns a confident score for the wrong locus — ACTB's gene body sits + # 33,301 bp from its TSS. There is no client-side tell, so refusing is + # the only honest option. + locus = self._locus() + with pytest.raises(EnsemblError) as exc: + _ = locus.tss + assert "no canonical transcript" in str(exc.value) + + def test_refuses_when_only_one_boundary_is_known(self): + locus = self._locus(canonical_start=1500) + with pytest.raises(EnsemblError): + _ = locus.tss +class TestExpressionWindowCentring: + """The TSS must land at offset 4,599 on both strands. + + The API scores ``sequence[tss_index-4599 : tss_index+4599]`` and defaults + ``tss_index`` to 4,599 for a submission of exactly 9,198 bp. Ensembl + reverse-complements the region for ``strand=-1``, so a window built as if + the sequence always read low-to-high puts a minus-strand TSS at 4,598 — + still exactly 9,198 bp, so the size gate passes and the API scores a + window shifted by one with nothing for the caller to notice. + """ + + @pytest.mark.parametrize("strand", [1, -1]) + def test_window_is_exactly_the_expression_length(self, strand): + start, end = expression_window_bounds(1_000_000, strand) + assert end - start + 1 == EXPRESSION_SEQUENCE_LENGTH + + @pytest.mark.parametrize( + "strand,tss", + [(1, 1_000_000), (-1, 5_227_071)], # HBB's canonical TSS is on the minus strand + ) + def test_tss_lands_where_the_api_expects_it(self, strand, tss): + start, end = expression_window_bounds(tss, strand) + # Offset of the TSS in the sequence as Ensembl returns it: low-to-high + # on the plus strand, reverse-complemented on the minus strand. + offset = tss - start if strand == 1 else end - tss + assert offset == EXPRESSION_SEQUENCE_LENGTH // 2 +class TestDemoMustBeAskedFor: + """Omitting --input must not fall back to the bundled fixture. + + The fallback produced a complete run — real request id, real scores, a + written report — for a sequence the caller never supplied, and nothing in + the output distinguished it from a real one. + """ + + def _args(self, **kw): + defaults = {"demo": False, "input_file": None} + defaults.update(kw) + return argparse.Namespace(**defaults) + + def test_no_input_and_no_demo_exits(self): + spec = gi_predict.TASKS["promoter"] + with pytest.raises(SystemExit) as exc: + gi_predict._resolve_input(self._args(), spec) + assert exc.value.code == 2 + + def test_demo_flag_still_resolves_the_fixture(self): + spec = gi_predict.TASKS["promoter"] + path = gi_predict._resolve_input(self._args(demo=True), spec) + assert path.name == spec.demo +class TestSequenceBeforeFirstHeader: + """Bases before the first '>' must be refused, not absorbed. + + They were appended to the first record, so the API scored a sequence the + caller never named and returned coordinates for it. The multi-record check + does not see this: such a file has exactly one header. + """ + + def test_pre_header_sequence_is_rejected(self, tmp_path): + path = _write(tmp_path, "ACGTACGT\n>real_record\nGGGGCCCC\n") + with pytest.raises(FastaError) as exc: + read_fasta(path) + assert "before any" in str(exc.value) + + def test_a_normal_single_record_still_parses(self, tmp_path): + path = _write(tmp_path, ">real_record\nACGT\nGGGG\n") + name, seq = read_fasta(path) + assert (name, seq) == ("real_record", "ACGTGGGG") + + +class TestSuccessfulResponsesAreValidated: + """A 2xx is not automatically a result. + + A non-JSON 200 used to be turned into an error-shaped dict and returned as + a success, and an empty or non-object 200 reached the report writer and + failed there as an AttributeError. + """ + + class _Resp: + status_code = 200 + headers: dict = {} + ok = True + + def __init__(self, payload=None, text=""): + self._payload, self.text = payload, text + + def json(self): + if self._payload is None: + raise ValueError("not json") + return self._payload + + def test_non_json_200_raises_instead_of_returning_an_error_shape(self): + c = gi_client.Client.__new__(gi_client.Client) + with pytest.raises(gi_client.GIError): + c._check(self._Resp(text="gateway")) + + @pytest.mark.parametrize( + "payload", [{}, [], {"meta": {}}, "text", None, {"data": {}, "meta": {}}] + ) + def test_a_200_without_data_is_not_a_result(self, payload): + with pytest.raises(gi_client.GIError): + gi_client.Client._require_envelope(payload, self._Resp(payload)) + + def test_a_well_formed_envelope_passes_through(self): + body = {"data": {"summary": {}}, "meta": {}} + assert gi_client.Client._require_envelope(body, self._Resp(body)) is body +class TestWhitespaceIsFormattingNotContent: + """Whitespace is normalized; only content is refused. + + The API strips newlines, spaces and tabs before measuring length, so a + space-grouped body has to parse here too or the client is stricter than + the service it guards. + """ + + @pytest.mark.parametrize( + "body,expected", + [ + ("ACGT\nGGGG\n", "ACGTGGGG"), + ("ACGTACGTAC GTACGTACGT\n", "ACGTACGTACGTACGTACGT"), + ("ACGT\tACGT\n", "ACGTACGT"), + (" ACGT \n\n GGGG\n", "ACGTGGGG"), + ("acgtACGT\n", "ACGTACGT"), + ], + ) + def test_layout_is_normalized(self, tmp_path, body, expected): + assert read_fasta(_write(tmp_path, f">r\n{body}"))[1] == expected + + @pytest.mark.parametrize("body", ["ACGTRACGT\n", "ACGT R ACGT\n"]) + def test_ambiguity_codes_are_still_refused(self, tmp_path, body): + with pytest.raises(FastaError): + read_fasta(_write(tmp_path, f">r\n{body}")) +class TestSyncPredictIsValidatedToo: + """The sync path needs the same envelope check as the async one. + + An earlier patch wrapped only wait_for_job, so predict() still returned a + 200 with no data key straight to the report writer, which wrote an empty + report and reported ok=true. + """ + + class _Resp: + status_code, headers, ok = 200, {}, True + + def __init__(self, payload): + self._payload, self.text = payload, "" + + def json(self): + return self._payload + + @pytest.mark.parametrize( + "payload", + [ + {"meta": {}}, # no data key at all + {"data": None, "meta": {}}, # null data + {"data": "summary", "meta": {}}, # non-object data + {"data": [1, 2], "meta": {}}, # array data + {"data": {}, "meta": {"request_id": "req-1"}}, # object, but no result in it + ], + ) + def test_a_200_without_an_object_data_is_refused(self, payload): + with pytest.raises(gi_client.GIError): + gi_client.Client._require_envelope(payload, self._Resp(payload)) + + def test_predict_wraps_the_sync_path(self): + import inspect + + src = inspect.getsource(gi_client.Client.predict) + assert "_require_envelope" in src, ( + "predict() must validate the envelope; a patch that misses this " + "line leaves the sync path unguarded while the async one is fine" + ) +class TestAsyncSubmitIsValidatedToo: + """submit_async reads data.job_id, so it needs the envelope check as well. + + A 200 with no data key raised KeyError, which the CLI catches; a non-object + data raised TypeError, which it does not. Both are malformed responses, not + client bugs, and both should surface as GIError. + """ + + def test_submit_async_wraps_the_envelope(self): + import inspect + + src = inspect.getsource(gi_client.Client.submit_async) + assert "_require_envelope" in src + + def test_an_empty_data_never_reaches_the_job_id_read(self): + """`{"data": {}}` is refused by the envelope check itself. + + It used to pass, leaving `data.job_id` to catch it on this path only — + and nothing at all to catch it on the sync and job-result paths, which + wrote a zero-valued report and printed ok=true. + """ + + class _Resp: + status_code, headers, ok = 200, {}, True + text = "" + + def json(self): + return {"data": {}, "meta": {}} + + with pytest.raises(gi_client.GIError): + gi_client.Client._require_envelope(_Resp().json(), _Resp()) + + def test_a_data_without_job_id_is_still_a_gi_error(self): + """The job_id check stays: a non-empty `data` can still lack it.""" + body = {"data": {"status": "queued"}, "meta": {}} + + class _Resp: + status_code, headers, ok = 200, {}, True + text = "" + + def json(self): + return body + + checked = gi_client.Client._require_envelope(body, _Resp()) + assert checked["data"].get("job_id") is None + + +class TestNestedFieldsOfTheWrongType: + """`_require_envelope` passing is not a promise about what is inside `data`. + + The envelope check guarantees `data` is a non-empty object and stops there, + which is the right scope for it — it is shared by six tasks and must not + encode any one task's schema. So the report writer still meets whatever + `data.summary` or `meta` actually contains, and it runs *after* main()'s + first try/except has closed. + + Two wrong answers were available here and both were taken in turn. The + `x or {}` guards only covered null and absent, so a truthy wrong type + reached `.get` and raised AttributeError — a traceback that reads as a + client bug. Coercing it to `{}` instead produced a zero-valued report + printed with `"ok": true`, which is worse: a malformed response became + indistinguishable from a real prediction of nothing. The third option is a + typed refusal, which is what these pin. + + These call `_summarize` and `_write_report` rather than asserting on their + source, because the defect this pins is a missing call site and a source + grep is exactly what failed to catch the last one. + """ + + _MALFORMED = [ + {"data": {"summary": "all good"}}, # summary as a string + {"data": {"summary": ["a", "b"]}}, # summary as an array + {"data": {"summary": 0.94}}, # summary as a float + {"data": {"prediction": "high"}}, # prediction as a string + {"data": {"summary": {}}, "meta": "req-1"}, # meta as a string + {"data": {"summary": {}}, "meta": {"task_specific_counts": "n/a"}}, + {"data": {"summary": {}, "regions": "chr1"}}, # array field as a string + {"data": {"summary": {}, "sites": [1, 2, 3]}}, # non-object elements + {"data": {"summary": {}, "transcripts": "ENST1"}}, + ] + + @pytest.mark.parametrize("body", _MALFORMED) + @pytest.mark.parametrize( + "task", ["promoter", "splice", "enhancer", "chromatin", "expression", "annotation"] + ) + def test_a_wrong_typed_field_is_refused_not_coerced(self, task, body): + """Whichever task reads the offending field must refuse the body. + + A task that never reads it is entitled to succeed — `enhancer` does not + touch `data.transcripts` — so the assertion is on the failure mode, not + on every combination failing: either a typed refusal naming the field, + or a clean summary. Never an AttributeError, and never a summary built + out of a substituted empty value. + """ + try: + out = gi_predict._summarize(task, body) + except gi_predict.ResponseShapeError as e: + assert "should be an" in str(e) + return + assert isinstance(out, dict) + assert isinstance(out["raw_summary"], dict) + + @pytest.mark.parametrize( + "body,field", + [ + ({"data": {"summary": "all good"}}, "data.summary"), + ({"data": {"summary": ["a", "b"]}}, "data.summary"), + ({"data": {"summary": 0.94}}, "data.summary"), + ({"data": {"summary": {}, "regions": "chr1"}}, "data.regions"), + ({"data": {"summary": {}, "sites": [1, 2, 3]}}, "data.sites"), + ], + ) + def test_the_offending_field_is_named(self, body, field): + task = {"data.summary": "promoter", "data.regions": "promoter", + "data.sites": "splice"}[field] + with pytest.raises(gi_predict.ResponseShapeError) as exc: + gi_predict._summarize(task, body) + assert field in str(exc.value) + + @pytest.mark.parametrize("body", _MALFORMED) + @pytest.mark.parametrize("task", ["promoter", "splice", "expression", "annotation"]) + def test_the_report_never_half_writes(self, tmp_path, task, body): + """A refusal may happen; a traceback or a silent zero report may not.""" + try: + summary = gi_predict._summarize(task, body) + gi_predict._write_report( + task, summary, body, tmp_path, tmp_path / "in.fa", "seq", 9198, 12.0, + ) + except gi_predict.ResponseShapeError: + return + assert (tmp_path / "report.md").exists() + assert (tmp_path / "result.json").exists() + + def test_main_reports_it_instead_of_exiting_zero(self, tmp_path, monkeypatch, capsys): + """The whole point: exit 2 with a diagnostic, not 0 with `"ok": true`. + + Drives `main()` rather than grepping it for a handler, because the last + defect of this shape (GI-055) was a source-level edit that matched + nothing and still read as correct in review. + """ + fa = tmp_path / "in.fa" + fa.write_text(">seq\n" + "ACGT" * 100 + "\n") + + class _FakeClient: + def __init__(self, *a, **kw): + pass + + def predict(self, *a, **kw): + return {"data": {"summary": "all good"}, "meta": {}} + + monkeypatch.setattr(gi_predict, "Client", _FakeClient) + monkeypatch.setenv("GI_API_KEY", "partner-test-key") + monkeypatch.setattr( + sys, "argv", + ["gi_predict.py", "--task", "promoter", "--input", str(fa), + "--output", str(tmp_path / "out")], + ) + + assert gi_predict.main() == 2 + err = capsys.readouterr() + assert "unexpected API response shape" in err.err + assert "data.summary" in err.err + assert '"ok": true' not in err.out + assert not (tmp_path / "out" / "report.md").exists() + + def test_absent_and_null_are_still_legitimate(self): + """Only a *present, wrong-typed* field is malformed. + + A task with no `prediction` omits it; coercing that to `{}` is correct + and must not become a refusal, or every sparse-but-valid response breaks. + """ + body = {"data": {"summary": {"total_windows": 5}, "regions": None}, + "meta": None} + out = gi_predict._summarize("promoter", body) + assert out["regions"] == [] + assert out["raw_summary"] == {"total_windows": 5} + + def test_a_well_formed_body_still_reports_its_rows(self, tmp_path): + body = { + "data": { + "summary": {"promoter_windows": 2, "total_windows": 5}, + "regions": [{"name": "r1", "start": 10, "end": 20, "score": 0.9}], + }, + "meta": {"request_id": "req-1"}, + } + summary = gi_predict._summarize("promoter", body) + assert summary["regions"] == body["data"]["regions"] + gi_predict._write_report( + "promoter", summary, body, tmp_path, tmp_path / "in.fa", "seq", 9198, 12.0, + ) + report = (tmp_path / "report.md").read_text() + assert "req-1" in report and "r1" in report diff --git a/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/tests/test_reproducibility.py b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/tests/test_reproducibility.py new file mode 100644 index 0000000..2291516 --- /dev/null +++ b/plugins/bionemo-agent-toolkit/skills/genomic-intelligence-nim/tests/test_reproducibility.py @@ -0,0 +1,102 @@ +"""Reproducibility replay guard for the Genomic Intelligence runner. + +Regression target: ``reproducibility/command.sh`` must reproduce the original +invocation. It historically dropped ``--description`` (required by expression, +no default) and a non-default ``--model``, so replaying an expression job hit +the runner's own validation gate and exited 1. + +All tests are offline (no network, no API key) except the one marked +``@pytest.mark.integration``, which replays ``command.sh`` against the live API +when ``GI_API_KEY`` is set. Run from the skill directory: ``pytest tests/``. +""" +from __future__ import annotations + +import os +import shlex +import subprocess +import sys +from pathlib import Path + +import pytest + +SKILL_DIR = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(SKILL_DIR / "scripts")) +import gi_predict # noqa: E402 + + +def _reparse(cmd: str): + """Tokenize a generated command and run it back through the runner's parser. + + This is the heart of the replay guard: if the generated command survives the + runner's own argument parsing with the required flags intact, a real replay + will pass the same pre-flight it originally passed. + """ + argv = shlex.split(cmd) + assert argv[:2] == ["python3", "scripts/gi_predict.py"], argv + saved = sys.argv + try: + sys.argv = ["gi_predict.py"] + argv[2:] + return gi_predict._parse_args() + finally: + sys.argv = saved + + +def test_expression_command_keeps_description(): + cmd = gi_predict._repro_command( + "expression", Path("in.fa"), Path("out"), model=None, description="K562 cells" + ) + ns = _reparse(cmd) + assert ns.task == "expression" + assert ns.description == "K562 cells", f"replay would fail the --description gate: {cmd}" + + +def test_command_keeps_nondefault_model(): + # Any non-default model id: the guard is that --model survives the replay, + # not that this particular id exists. + cmd = gi_predict._repro_command( + "promoter", Path("in.fa"), Path("out"), model="non-default-model", description=None + ) + assert _reparse(cmd).model == "non-default-model" + + +def test_command_omits_absent_flags(): + cmd = gi_predict._repro_command("promoter", Path("in.fa"), Path("out")) + assert "--description" not in cmd and "--model" not in cmd + + +def test_command_uses_python3_and_quotes_spaces(): + cmd = gi_predict._repro_command( + "expression", Path("a b.fa"), Path("out dir"), description="K562 cells" + ) + assert cmd.startswith("python3 ") + # spaces must be quoted so the replay tokenizes back to the same values + ns = _reparse(cmd) + assert ns.input_file == Path("a b.fa") + assert ns.output == Path("out dir") + assert ns.description == "K562 cells" + + +@pytest.mark.integration +def test_expression_bundle_replays_end_to_end(tmp_path): + """Full round-trip against the live API: run expression, then replay the + generated command.sh verbatim; both must exit 0. Requires GI_API_KEY.""" + if not os.environ.get("GI_API_KEY"): + pytest.skip("GI_API_KEY not set") + out = tmp_path / "expr" + first = subprocess.run( + [sys.executable, "scripts/gi_predict.py", "--task", "expression", "--demo", + "--description", "K562 cells", "--output", str(out)], + cwd=SKILL_DIR, capture_output=True, text=True, timeout=180, + ) + assert first.returncode == 0, first.stderr + command_sh = out / "reproducibility" / "command.sh" + body = command_sh.read_text() + # Ran without --model, so the default was used: the replay must NOT inject + # --model (regression guard for the _write_report model-shadowing bug). + assert "--description" in body, "expression replay must carry --description" + assert "--model" not in body, "must not inject --model when the user relied on the default" + replay = subprocess.run( + ["bash", str(command_sh)], cwd=SKILL_DIR, + capture_output=True, text=True, timeout=180, + ) + assert replay.returncode == 0, f"replay failed: {replay.stderr}" diff --git a/skills.sh.json b/skills.sh.json index 0083e02..5a627f3 100644 --- a/skills.sh.json +++ b/skills.sh.json @@ -10,6 +10,7 @@ "drug-discovery-pipeline", "evo2-nim", "genmol-nim", + "genomic-intelligence-nim", "molmim-nim", "msa-search-nim", "msa-structure-prediction-pipeline",