Spaces:
Sleeping
Sleeping
| // 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 }), | |
| }); | |
| } | |