File size: 3,363 Bytes
f6a5256
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/**
 * Typed fetch wrappers for the Next-Gen evaluation API (/api/nextgen).
 * Mirrors the style of services/rca.ts.
 */
import type {
  ArchitectureDocs,
  BehavioralAssessment,
  CatalogueEntry,
  CausalReplayResult,
  ComputeReport,
  CounterfactualScenario,
  DivergenceDemo,
  GauntletResult,
  NextgenStatus,
  SuiteWeights,
  TransformationStep,
  ValidityReport,
} from '../types/nextgen';

async function handle<T>(res: Response): Promise<T> {
  if (!res.ok) {
    let detail = '';
    try {
      const body = await res.json();
      detail = typeof body?.detail === 'string' ? body.detail : JSON.stringify(body);
    } catch {
      detail = await res.text().catch(() => '');
    }
    const err = new Error(detail || res.statusText) as Error & { status?: number };
    err.status = res.status;
    throw err;
  }
  return res.json() as Promise<T>;
}

const JSON_HEADERS = { 'Content-Type': 'application/json' };
const get = <T,>(url: string): Promise<T> => fetch(url).then((r) => handle<T>(r));
const post = <T,>(url: string, body?: unknown): Promise<T> =>
  fetch(url, {
    method: 'POST',
    headers: JSON_HEADERS,
    body: body === undefined ? undefined : JSON.stringify(body),
  }).then((r) => handle<T>(r));

export const getStatus = () => get<NextgenStatus>('/api/nextgen/status');

export const getCatalogue = () =>
  get<{ transformations: CatalogueEntry[] }>('/api/nextgen/counterfactuals/catalogue');

export const generateCounterfactuals = (body: {
  recipe: TransformationStep[];
  seed?: number;
  n_scenarios?: number;
  frames_per_sequence?: number;
}) => post<{ scenarios: CounterfactualScenario[] }>('/api/nextgen/counterfactuals/generate', body);

export const listCounterfactuals = () =>
  get<{ scenarios: CounterfactualScenario[] }>('/api/nextgen/counterfactuals');

export const validateCounterfactual = (scenarioId: string) =>
  post<ValidityReport>(`/api/nextgen/counterfactuals/${encodeURIComponent(scenarioId)}/validate`);

export const getSuiteWeights = () =>
  get<SuiteWeights>('/api/nextgen/counterfactuals/suite-weights');

export const runReplay = (body: {
  scenario_id: string;
  engine?: string;
  seed?: number;
  corrected?: boolean;
  faults?: Record<string, unknown>[];
}) => post<BehavioralAssessment>('/api/nextgen/simulation/replay', body);

export const runCausalReplay = (body: {
  scenario_id: string;
  engine?: string;
  seed?: number;
  faults?: Record<string, unknown>[];
}) => post<CausalReplayResult>('/api/nextgen/causal/replay', body);

export const getDivergenceDemo = () =>
  get<DivergenceDemo>('/api/nextgen/metrics/divergence-demo');

export const runGauntlet = (body: {
  candidate_version?: string;
  baseline_version?: string;
  effects?: Record<string, number>;
  budget_units?: number;
  batch_units?: number;
  seed?: number;
}) => post<GauntletResult>('/api/nextgen/gauntlet/run', body);

export const getGauntletResults = (runId: string) =>
  get<GauntletResult>(`/api/nextgen/gauntlet/${encodeURIComponent(runId)}/results`);

export const runComputeBenchmark = (body: { n_scenarios?: number; frames_per_sequence?: number }) =>
  post<ComputeReport>('/api/nextgen/compute/benchmark', body);

export const getComputeReport = () => get<ComputeReport>('/api/nextgen/compute/report');

export const getArchitectureDocs = () =>
  get<ArchitectureDocs>('/api/nextgen/architecture/docs');