oncodsl / web /lib /api.ts
govindbalki's picture
Upload folder using huggingface_hub
0fff343 verified
Raw
History Blame Contribute Delete
10.5 kB
// Typed API helpers + types matching the FastAPI Pydantic models.
export const API_URL =
process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:8000";
export type Target = "msi" | "tmb" | "none" | "hpv";
export type DatasetId = "coadread" | "hnsc";
export type Metric = "auroc" | "correlation" | "structure";
export type Direction = "neg" | "pos";
export interface ObjectiveSpec {
target: Target;
metric: Metric;
direction?: Direction;
}
export interface RunParams {
generations: number;
population: number;
genes_per_set: number;
max_sets: number;
lambda: number;
seed: number;
/**
* ``null`` => no prefilter; the engine searches the full ~20,000-column
* pool. Any positive int narrows to the top-N univariate features.
*/
prefilter_n: number | null;
permutations: number;
}
export interface Candidate {
id: string;
program_repr: string;
fitness: number;
parents: string[];
survived: boolean;
// v2-only
n_nodes?: number;
depth?: number;
gene_ids?: string[];
// v1-only legacy
feature_sets?: string[][];
n_genes?: number;
born?: boolean;
}
export interface GenerationEvent {
generation: number;
best_fitness: number;
median_fitness: number;
elitism: number;
candidates: Candidate[];
}
export interface Winning {
id: string;
program_repr: string;
gene_ids: string[];
cv_fitness: number;
holdout_auroc: number;
holdout_score: number;
permutation_p: number;
n_nodes?: number;
depth?: number;
feature_sets?: string[][]; // v1 legacy
// Iterative-unsupervised: full-cohort per-patient scores so a
// follow-up run can residualise against this axis.
full_scores?: (number | null)[];
full_sample_ids?: string[];
// Held-out per-patient scores, used by the post-hoc alignment step.
holdout_scores?: (number | null)[];
holdout_sample_ids?: string[];
}
export interface Baseline {
id: string;
program_repr: string;
gene_ids: string[];
holdout_auroc: number;
holdout_score: number;
feature_sets?: string[][]; // v1 legacy
}
export interface Posthoc {
n_holdout: number;
msi_auroc: number | null;
tmb_abs_spearman: number | null;
hpv_auroc?: number | null;
n_msi_held?: number;
n_tmb_held?: number;
n_hpv_held?: number;
}
export interface RunResult {
engine?: "v1" | "v2";
objective_spec: ObjectiveSpec;
fitness_label: string;
winning: Winning;
baseline?: Baseline;
permutation_summary: {
n_permutations: number;
null_kind?: string;
null_score_mean?: number;
null_score_p95?: number;
null_auroc_mean?: number;
null_auroc_p95?: number;
};
posthoc?: Posthoc;
}
export interface RunStatus {
id: string;
engine: "v1" | "v2";
objective_spec: ObjectiveSpec;
params: RunParams;
status: "running" | "done" | "error";
error: string | null;
n_generations_seen: number;
generations_persisted: number;
log: Array<{
generation: number;
best_fitness: number;
median_fitness: number;
elitism: number;
population_size?: number;
}>;
}
export interface PopulationResponse {
run_id: string;
engine: "v1" | "v2";
generation: number;
best_fitness: number;
median_fitness: number;
elitism: number;
population_size: number;
candidates: Candidate[];
}
export interface EvaluateRow {
id: string;
symbol: string;
matched: boolean;
// Single-gene rank diagnostic — present only for (dataset, target)
// pairs where one exists (HNSC/HPV, coadread/TMB).
rank?: number | null;
total?: number | null;
single_gene_metric?: number | null;
metric_kind?: "auroc" | "spearman" | null;
}
export interface EvaluateResponse {
revealed: EvaluateRow[];
overlap_count: number;
reference_set: string;
}
async function jsonFetch<T>(path: string, init?: RequestInit): Promise<T> {
const res = await fetch(`${API_URL}${path}`, {
headers: { "content-type": "application/json" },
...init,
});
if (!res.ok) {
const text = await res.text();
throw new Error(`${res.status} ${res.statusText}: ${text}`);
}
return (await res.json()) as T;
}
export function postRun(body: {
objective_spec: ObjectiveSpec;
params: RunParams;
engine?: "v1" | "v2";
dataset?: DatasetId;
residualize_against?: string[];
coherence?: boolean;
diversity?: boolean;
/** Per-run DSL injection-rate overrides. Flat dict whose keys are
* any of: ``split``, ``effect``, ``fitapply``, ``search``,
* ``scalar_share``. Missing keys keep their engine defaults; an
* empty / omitted dict reproduces the current behaviour byte-for-
* byte. ``search: 0`` is the replacement for the old
* ``enable_search: false`` toggle. */
rates_override?: Record<string, number>;
}): Promise<{ run_id: string }> {
return jsonFetch("/runs", {
method: "POST",
body: JSON.stringify(body),
});
}
export function getRunResult(runId: string): Promise<RunResult> {
return jsonFetch(`/runs/${runId}/result`);
}
export function getRunStatus(runId: string): Promise<RunStatus> {
return jsonFetch(`/runs/${runId}`);
}
export function getRunPopulation(
runId: string,
generation: number,
): Promise<PopulationResponse> {
return jsonFetch(`/runs/${runId}/population/${generation}`);
}
export function postEvaluate(body: {
gene_ids: string[];
reference_set: string;
dataset?: DatasetId;
target?: Target;
}): Promise<EvaluateResponse> {
return jsonFetch("/evaluate", {
method: "POST",
body: JSON.stringify(body),
});
}
export interface GeneRankRow {
symbol: string;
present: boolean;
corr: number | null;
rank: number | null;
percentile: number | null;
}
export interface TMBRankDiagnostic {
cohort: string;
n_samples: number;
n_genes: number;
mmr: GeneRankRow[];
immune: GeneRankRow[];
top_negative: GeneRankRow[];
}
export function getTMBRankDiagnostic(): Promise<TMBRankDiagnostic> {
return jsonFetch("/diagnostic/tmb-rank");
}
export interface HPVRankDiagnostic {
cohort: string;
seed: number;
n_samples: number;
n_pos: number;
n_neg: number;
n_genes: number;
p16: GeneRankRow[];
cell_cycle: GeneRankRow[];
top_separators: GeneRankRow[];
}
export function getHPVRankDiagnostic(): Promise<HPVRankDiagnostic> {
return jsonFetch("/diagnostic/hpv-rank");
}
export interface FullRankRow {
opaque_id: string;
score: number;
rank: number;
}
export interface ReferenceMark {
opaque_id: string;
symbol: string;
set_name: string;
rank: number;
score: number;
}
export interface FullRankDiagnostic {
dataset: DatasetId;
target: Target;
metric_kind: "auroc" | "spearman";
n_samples: number;
n_pos: number;
n_neg: number;
n_genes: number;
seed: number;
test_size: number;
ranks: FullRankRow[];
reference_marks: ReferenceMark[];
}
export function getFullRankDiagnostic(
dataset: DatasetId, target: Target,
): Promise<FullRankDiagnostic> {
const qs = new URLSearchParams({ dataset, target }).toString();
return jsonFetch(`/diagnostic/full-rank?${qs}`);
}
export interface ModulePerGene {
id: string;
single_gene_metric: number | null;
rank: number | null;
total: number | null;
}
export interface RankedModule {
gene_ids: string[];
size: number;
combined_holdout: number | null;
coherence: number | null;
/** The engine's own preference signal: the max GP fitness observed
* for any candidate carrying this gene-set in the persisted
* population. ``null`` for legacy / pre-Chunk-7 runs. */
gp_fitness?: number | null;
/** The actual program_repr of the candidate that earned
* ``gp_fitness`` (argmax over the persisted population for this
* gene-set). Opaque-safe (built from opaque IDs only). The Lab
* feeds this into the shared ProgramGraph renderer on row-expand. */
best_program_repr?: string | null;
/** Reference-set names this module's gene_ids intersect (bounded
* reveal of a small known set — e.g. ["p16","cell_cycle"]). */
ref_sets: string[];
per_gene: ModulePerGene[];
// Confounder-survival flags. Populated for HNSC/HPV only; null
// elsewhere or when the subgroup is too small to score honestly.
combined_holdout_oropharynx?: number | null;
n_holdout_oropharynx?: number;
n_pos_oropharynx?: number;
n_neg_oropharynx?: number;
survives_site?: boolean | null;
combined_holdout_highpurity?: number | null;
n_holdout_highpurity?: number;
n_pos_highpurity?: number;
n_neg_highpurity?: number;
survives_purity?: boolean | null;
}
export interface ModuleSubgroup {
kind: string;
n: number;
tolerance: number;
n_proxy_genes?: number;
}
export interface ModuleRanking {
run_id: string;
target: Target;
dataset: DatasetId;
metric_kind: "auroc" | "spearman";
coherence: boolean;
n_modules: number;
n_train: number;
n_test: number;
subgroups?: {
site?: ModuleSubgroup | null;
purity?: ModuleSubgroup | null;
};
modules: RankedModule[];
}
export function getRunModules(runId: string): Promise<ModuleRanking> {
return jsonFetch(`/runs/${runId}/modules`);
}
export interface OperatorUsageRow {
/** Human label for the DSL operator (e.g. "Select", "Fit/Apply"). */
name: string;
/** Total occurrences across every candidate in every generation. */
total_uses: number;
/** Number of candidate-instances containing this operator ≥ once. */
programs_using: number;
}
export interface OperatorUsage {
run_id: string;
n_generations: number;
n_candidates: number;
operators: OperatorUsageRow[];
}
export function getRunOperatorUsage(runId: string): Promise<OperatorUsage> {
return jsonFetch(`/runs/${runId}/operator-usage`);
}
export interface RevealResponse {
symbols: string[];
}
/** External-cohort transfer test payload (GSE65858 for HNSC/HPV). Gene
* NAMES in this payload are ONLY the winner's revealed symbols — same
* discipline as /evaluate. */
export interface TransferResult {
run_id: string;
cohort: string;
platform: string;
source: string;
n_cohort: number;
auroc: number | null;
p: number | null;
n: number;
n_pos: number;
n_neg: number;
n_found: number;
n_missing: number;
found_symbols: string[];
missing_symbols: string[];
}
export function getRunTransfer(runId: string): Promise<TransferResult> {
return jsonFetch(`/runs/${runId}/transfer`);
}
export function postReveal(gene_ids: string[]): Promise<RevealResponse> {
return jsonFetch("/reveal", {
method: "POST",
body: JSON.stringify({ gene_ids }),
});
}