Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
cba10f7
Using Pathfinder package with local sqlite files
mohsenht Jan 20, 2026
5539e3d
Using Pathfinder package with mysql server
mohsenht Jan 21, 2026
cf94d6d
Settings for arax pathfinder
mohsenht Jan 21, 2026
4999dff
Black style errors
mohsenht Jan 21, 2026
767e51b
Black style errors
mohsenht Jan 21, 2026
540f220
New pathfinder package release update.
mohsenht Jan 23, 2026
cd4cf5a
Inject shepherd-arax provenance in all edge sources field
mohsenht Jan 24, 2026
ce1a5e6
Temporary faster pathfinder by decreasing parameters
mohsenht Feb 4, 2026
7cd663a
Merge branch 'main' of github.com:BioPack-team/shepherd into arax-pat…
mohsenht Feb 11, 2026
8ec57a9
Arax Pathfinder tested with 4 hops
mohsenht Feb 11, 2026
82852c4
Async Arax Pathfinder
mohsenht Feb 18, 2026
6b537e5
Pathfinder package updated
mohsenht Mar 3, 2026
9919306
Pathfinder package updated
mohsenht Mar 4, 2026
ab7049e
Merge branch 'main' of github.com:BioPack-team/shepherd into arax-pat…
mohsenht Mar 11, 2026
4771f90
resolved conflicts
mohsenht Mar 11, 2026
107614b
resolved conflicts
mohsenht Mar 11, 2026
268d226
resolved conflicts
mohsenht Mar 11, 2026
c6b79c3
Update to latest main code
maximusunc Mar 11, 2026
f98c6e9
Run black
maximusunc Mar 11, 2026
298aee5
PloverDB url updated to point to CI
mohsenht Mar 11, 2026
17d4187
Merge remote-tracking branch 'origin/arax-pathfinder' into arax-pathf…
mohsenht Mar 11, 2026
70758cb
PRUNE more
mohsenht Mar 11, 2026
b048766
Merge branch 'main' of github.com:BioPack-team/shepherd into arax-pat…
mohsenht Apr 30, 2026
60b7d4a
Merge branch 'main' of github.com:BioPack-team/shepherd into arax-pat…
mohsenht Jul 6, 2026
a7ab6be
Async call
mohsenht Jul 16, 2026
d344b95
Merge branch 'main' of github.com:BioPack-team/shepherd into arax-pat…
mohsenht Jul 16, 2026
ebff3e6
Merge branch 'main' of github.com:BioPack-team/shepherd into arax-pat…
mohsenht Jul 23, 2026
a3eb40d
ARAX Pathfinder Package 2.4.3. Adaptable with Retriever
mohsenht Jul 23, 2026
7bc9ba2
Auto download sqlite files for developers
mohsenht Jul 29, 2026
a3f35df
Merge branch 'main' of github.com:BioPack-team/shepherd into arax-pat…
mohsenht Jul 29, 2026
b823e96
redundant plover_url config removed.
mohsenht Jul 29, 2026
5195035
ssh path generalized for all developers
mohsenht Jul 29, 2026
c699d27
readme correction
mohsenht Jul 29, 2026
cbd9ad5
Fetch sqlite files over Https instead of scp
mohsenht Aug 5, 2026
7a0730f
arax_pathfinder_dbs git ignored
mohsenht Aug 5, 2026
d41090f
Run black
maximusunc Aug 5, 2026
ff282dc
Fix test import
maximusunc Aug 10, 2026
8c26f47
Move arax_pathfinder onto a process pool and run_task_lifecycle
claude Aug 10, 2026
78d17af
Add script for testing against pathfinder
maximusunc Aug 10, 2026
a782c5f
Set arax pathfinder resources
maximusunc Aug 10, 2026
e6ff46d
Merge pull request #137 from BioPack-team/claude/async-arax-pathfinde…
mohsenht Aug 12, 2026
6fbd183
Add arax pathfinder to release
maximusunc Aug 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ jobs:
- aragorn_pathfinder
- aragorn_score
- arax
- arax_pathfinder
- arax_rank
- bte
- bte_lookup
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ debug/
gandalf_mmap/
omnicorp_lmdb/
pathfinder_embeddings/
arax_pathfinder_dbs/

# Project specific
scripts/
Expand Down
40 changes: 26 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,45 @@ The main entrypoint is `./compose.yml` and will spin everything up.

If you want to add a new operation/worker, add a new service in `compose.yml` under `services`.

### Worker data (LMDB) downloads
### Worker data (LMDB / sqlite) downloads

A couple of workers read from large, read-only LMDB datasets that are too big to
A couple of workers read from large, read-only sqlite databases and LMDB datasets that are too big to
commit to git (they're gitignored and volume-mounted from the host):

- **`aragorn_omnicorp`** → `./omnicorp_lmdb/` (`curies.lmdb`, `shared_counts.lmdb`)
- **`score_paths`** → `./pathfinder_embeddings/` (a directory-style LMDB)
- **`arax_pathfinder`** → `./arax_pathfinder_dbs/` (`curie_ngd_v1.0_<tier-version>.sqlite`, `tier0-info-for-overlay_v1.0_<tier-version>.sqlite`)

So a new developer doesn't have to source these by hand, each worker can fetch
its dataset on first startup. Point it at a `.tar.gz` on an external server by
adding the matching variable to your root `.env` file:
So a new developer doesn't have to source these by hand, each worker can fetch its dataset on first
startup. Two download mechanisms are supported, depending on where the dataset lives:

**LMDB datasets (`aragorn_omnicorp`, `score_paths`)** are fetched as a `.tar.gz` from a plain HTTP(S)
URL and extracted in place. Add the matching variable to your root `.env` file:

```dotenv
OMNICORP_LMDB_URL=https://example.org/path/omnicorp_lmdb.tar.gz
PATHFINDER_EMBEDDINGS_URL=https://example.org/path/pathfinder_embeddings.tar.gz
```

On startup the worker checks whether its LMDB files already exist in the
volume-mounted directory. If they're missing and a URL is set, it downloads the
archive and extracts it into that directory — which lives on the host, so the
data persists across restarts and is only downloaded once. If the files are
already present, or no URL is configured, the download is skipped (production
mounts this data out of band, so it's unaffected).
The archive for each dataset should contain the expected files at its top level: `curies.lmdb` and
`shared_counts.lmdb` for omnicorp, `data.mdb` (and `lock.mdb`) for the embeddings.

**arax_pathfinder's sqlite databases** are served as plain files over HTTPS. The
filenames embed a Knowledge Graph version that changes periodically, so only one variable needs updating
when a new Knowledge Graph ships:

```dotenv
ARAX_PATHFINDER_TIER_VERSION=tier0-20260621
```

This requires an SSH key with access to that host, mounted read-only into the container
(`~/.ssh:/home/nru/.ssh:ro` in compose.yml).

The archive for each dataset should contain the expected files at its top level:
`curies.lmdb` and `shared_counts.lmdb` for omnicorp, `data.mdb` (and
`lock.mdb`) for the embeddings.
On startup, each worker checks whether its files already exist in the volume-mounted directory. If
they're missing and a source is configured (URL or scp path), it fetches them into that directory —
which lives on the host, so the data persists across restarts and is only downloaded once. If the
files are already present, or no source is configured, the download is skipped (production mounts
this data out of band, so it's unaffected).

### Worker

Expand Down
3 changes: 3 additions & 0 deletions compose.test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ services:
arax:
cpus: 1
mem_limit: 3g
arax_pathfinder:
cpus: 6
mem_limit: 22g
arax_rank:
cpus: 4
mem_limit: 10g
Expand Down
23 changes: 21 additions & 2 deletions compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ services:
- ./.env:/app/.env
# First run? Set PATHFINDER_EMBEDDINGS_URL in your .env to a .tar.gz and
# the worker downloads + extracts the embeddings LMDB into this mount on
# startup (see README "Worker data (LMDB) downloads").
# startup (see README "Worker data (LMDB / sqlite) downloads").
- ./pathfinder_embeddings:/app/pathfinder_embeddings

######### Example ARA
Expand Down Expand Up @@ -311,7 +311,7 @@ services:
- ./.env:/app/.env
# First run? Set OMNICORP_LMDB_URL in your .env to a .tar.gz and the worker
# downloads + extracts curies.lmdb / shared_counts.lmdb into this mount on
# startup (see README "Worker data (LMDB) downloads").
# startup (see README "Worker data (LMDB / sqlite) downloads").
- ./omnicorp_lmdb:/app/omnicorp_lmdb
aragorn_score:
container_name: aragorn_score
Expand Down Expand Up @@ -344,6 +344,25 @@ services:
volumes:
- ./logs:/app/logs
- ./.env:/app/.env
arax_pathfinder:
container_name: arax_pathfinder
build:
context: .
dockerfile: workers/arax_pathfinder/Dockerfile
restart: unless-stopped
depends_on:
shepherd_db:
condition: service_healthy
shepherd_broker:
condition: service_healthy
volumes:
- ./logs:/app/logs
- ./.env:/app/.env
# First run? The worker downloads its two sqlite dbs from
# kg2webhost.rtx.ai on startup, no credentials needed. Set
# ARAX_PATHFINDER_TIER_VERSION in your .env if you need a tier other than
# the default (see README "Worker data (LMDB / sqlite) downloads").
- ./arax_pathfinder_dbs:/app/arax_pathfinder_dbs

arax_rank:
container_name: arax_rank
Expand Down
140 changes: 140 additions & 0 deletions scripts/test_pathfinder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import asyncio
import json
import time
from datetime import datetime
from pathlib import Path

import httpx

target_urls = {
"aragorn-ci": "https://shepherd.ci.transltr.io/aragorn",
"arax-ci": "https://shepherd.ci.transltr.io/arax",
"aragorn-dev": "https://shepherd.renci.org/aragorn",
"arax-dev": "https://shepherd.renci.org/arax",
"aragorn-local": "http://localhost:5439/aragorn",
"arax-local": "http://localhost:5439/arax",
"bte-local": "http://localhost:5439/bte",
}

RESPONSES_DIR = "responses"


def generate_query(curie1: str, curie2: str) -> dict:
"""Given a curie, return a TRAPI message."""
parameters = {
# "timeout": 300,
# "tiers": [0],
}
return {
"message": {
"query_graph": {
"nodes": {
"on": {
"constraints": [],
"ids": [
curie1
],
},
"sn": {
"constraints": [],
"ids": [
curie2
],
}
},
"paths": {
"p0": {
"object": "on",
"subject": "sn",
"predicates": [
"biolink:related_to",
]
}
}
}
},
"parameters": parameters,
"log_level": "DEBUG",
}


async def single_lookup(curies: tuple[str, str], target: str):
"""Run a single query lookup synchronously."""
query = generate_query(curies[0], curies[1])
start_time = datetime.now()
try:
async with httpx.AsyncClient(timeout=600000) as client:
response = await client.post(
f"{target_urls[target]}/query",
json=query,
)
response.raise_for_status()
response_json = response.json()
results = (response_json.get("message") or {}).get("results") or []
num_results = len(results)
assert num_results == 1
num_analyses = len(results[0]["analyses"])
except Exception as e:
num_results = 0
num_analyses = 0
response_json = {
"Error": str(e),
}

stop_time = datetime.now()
print(f"{curies[0]}->{curies[1]} took {stop_time - start_time} seconds and gave {num_analyses} results")
out_dir = Path(RESPONSES_DIR) / "pathfinder" / target
out_dir.mkdir(parents=True, exist_ok=True)
response_path = out_dir / f"{('_').join(curies[0].split(':'))}_{('_').join(curies[1].split(':'))}_response.json"
with response_path.open("w", encoding="utf-8") as f:
json.dump(response_json, f, indent=2)


query_list = [
('MONDO:0021095', 'MONDO:0005105'),
('CHEBI:9139', 'MONDO:0004975'),
('CHEBI:5118', 'MONDO:0100233'),
('MONDO:0005180', 'MONDO:0005105'),
('MONDO:0019632', 'MONDO:0005340'),
('CHEBI:27881', 'NCBIGene:2739'),
('CHEBI:45783', 'MONDO:0004979'), # Imatinib -> Asthma
('GO:0006914', 'MONDO:0005265'),
('NCBIGene:3458', 'CHEBI:16828'),
('MONDO:0005532', 'MONDO:0005180'),
('CHEBI:15647', 'UNII:31YO63LBSN'),
('CHEBI:28364', 'MONDO:0005311'),
('NCBIGene:3458', 'MONDO:0100096'),
('NCBIGene:27240', 'MONDO:0100096'),
('CHEBI:3750', 'MONDO:0013209'),
('CHEBI:83766', 'MONDO:0008170'),
('CHEBI:45783', 'MONDO:0004784'),
('UNII:7SE5582Q2P', 'MONDO:0007037'),
('MONDO:0005011', 'MONDO:0005180'),
('CHEBI:15365', 'MONDO:0005575'),
('CHEBI:50924', 'MONDO:0007256'),
('CHEBI:45713', 'NCBIGene:2739'),
('NCBIGene:54716', 'MONDO:0100096'),
('CHEBI:7465', 'MONDO:0008218'),
# ('CHEBI:10033', 'MONDO:0004992'), # Warfarin -> Cancer, DON'T RUN
]


async def main():
"""Run the given query and time it."""
targets = ["arax-local"]
runs_per_target = 1

start = time.time()
queries = []
for curies in query_list:
queries.extend([
single_lookup(curies, target)
for target in targets
for _ in range(runs_per_target)
])
await asyncio.gather(*queries)
print(f"\nAll queries took {time.time() - start:.2f} seconds")


if __name__ == "__main__":
asyncio.run(main())
16 changes: 16 additions & 0 deletions shepherd_utils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,23 @@ class Settings(BaseSettings):
sync_kg_retrieval_url: str = "http://host.docker.internal:8080/query"
kg_rehydrate_url: str = "http://host.docker.internal:8080/rehydrate"
default_data_tier: int = 0

# ARAX configs
arax_url: str = "https://arax.ncats.io/shepherd/api/arax/v1.4/query"
arax_biolink_version: str = "4.2.5"
arax_blocked_list_url: str = (
"https://raw.githubusercontent.com/RTXteam/RTX/master/"
"code/ARAX/KnowledgeSources/general_concepts.json"
)

arax_pathfinder_dbs_dir: str = "arax_pathfinder_dbs"
arax_pathfinder_tier_version: str = "tier0-20260621"
arax_pathfinder_curie_ngd_sqlite_filename: str = "curie_ngd_v1.0_{version}.sqlite"
arax_pathfinder_tier0_overlay_sqlite_filename: str = (
"tier0-info-for-overlay_v1.0_{version}.sqlite"
)
arax_pathfinder_sqlite_base_url: str = "https://kg2webhost.rtx.ai/tier0"
# End of ARAX configs

pathfinder_redis_host: str = "host.docker.internal"
pathfinder_redis_port: int = 6383
Expand Down
Loading
Loading