Initial release: rarelens platform skeleton (AGPL-3.0)
ci / api (push) Failing after 10s
ci / terraform (push) Failing after 11s
ci / web (push) Failing after 35s
ci / pipeline (push) Failing after 2m29s
ci / images (api) (push) Skipped
ci / images (ml) (push) Skipped
ci / images (pipeline) (push) Skipped
ci / images (web) (push) Skipped

End-to-end variant interpretation platform for rare genetic disease research:
SvelteKit UI, FastAPI + PostgreSQL API, Nextflow/Ensembl VEP pipeline,
LightGBM pathogenicity scoring with MLflow, K8s/ArgoCD/GCP infrastructure.
Public test data only; no clinical claims.
This commit is contained in:
2026-09-11 16:55:35 +01:00
commit 5463f489a3
74 changed files with 4597 additions and 0 deletions
View File
+31
View File
@@ -0,0 +1,31 @@
"""Event publishing.
GCP: publish a JSON message to Pub/Sub; an Argo Events sensor turns it into an Argo Workflow.
Local: run Nextflow directly in a background task so `docker compose up` gives a working demo.
"""
import asyncio
import json
import uuid
from app.config import settings
async def publish_vcf_uploaded(job_id: uuid.UUID, vcf_uri: str) -> None:
message = {"job_id": str(job_id), "vcf_uri": vcf_uri}
if settings.pubsub_topic:
from google.cloud import pubsub_v1 # optional dependency, installed in the GCP image
publisher = pubsub_v1.PublisherClient()
publisher.publish(settings.pubsub_topic, json.dumps(message).encode())
return
cmd = [
"nextflow", "run", "/pipeline/main.nf", "-profile", "docker",
"--vcf", vcf_uri, "--job_id", str(job_id), "--db_url", settings.database_url,
]
try:
await asyncio.create_subprocess_exec(*cmd)
except FileNotFoundError:
# Nextflow is not installed in this environment (no Java toolchain yet);
# leave the job queued instead of crashing the request.
print(f"[rarelens] nextflow not found; cannot start pipeline for job {job_id}", flush=True)
+45
View File
@@ -0,0 +1,45 @@
import uuid
import mlflow
import pandas as pd
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.config import settings
from app.models import Prediction, Variant
_model = None
def load_model():
global _model
if _model is None:
mlflow.set_tracking_uri(settings.mlflow_tracking_uri)
_model = mlflow.pyfunc.load_model(f"models:/{settings.model_name}/{settings.model_stage}")
return _model
def featurise(variants: list[Variant]) -> pd.DataFrame:
# Mirror ml/rarelens_ml/features.py exactly; shared package later.
return pd.DataFrame(
{
"impact": [v.impact for v in variants],
"consequence": [v.consequence for v in variants],
"gnomad_af": [v.gnomad_af if v.gnomad_af is not None else 0.0 for v in variants],
"cadd_phred": [v.annotations.get("CADD_PHRED") for v in variants],
"am_pathogenicity": [v.annotations.get("am_pathogenicity") for v in variants],
}
)
async def score_job(job_id: uuid.UUID, session: AsyncSession) -> int:
variants = (await session.scalars(select(Variant).where(Variant.job_id == job_id))).all()
if not variants:
return 0
model = load_model()
scores = model.predict(featurise(variants))
for v, s in zip(variants, scores):
session.add(Prediction(variant_id=v.id, model_name=settings.model_name,
model_version=settings.model_stage, score=float(s)))
await session.commit()
return len(variants)