File size: 1,700 Bytes
7c447a5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
// src/services/api.ts
import type { JobConfig } from '../types';

export type PipelinePlots = {
  original_sentiment: string;
  counterfactual_sentiment: string;
};

export type PipelineResultsDTO = {
  data_loaded: number;
  model_loaded: string;

  generation_file: string;
  generation_samples: number;

  counterfactual_file: string;
  counterfactual_added: number;
  counterfactual_total: number;

  sampling_method: string;
  sentiment_subset_file: string;
  sentiment_subset_size: number;

  cf_sentiment_subset_file: string;
  cf_sentiment_subset_size: number;

  // 後端還會給 stereotype 的欄位,但前端不需要可不宣告
  config_used: import('../types').JobConfig;
  metrics: import('../types').JobMetrics & {
    finalMeanDiff: number;
    cfFinalMeanDiff: number;
  };
  plots: PipelinePlots;
};

export type PipelineResponseDTO = {
  status: 'success' | 'error';
  message: string;
  timestamp: string;
  results: PipelineResultsDTO;
};

const BASE = import.meta.env.VITE_API_BASE ?? '/api';

async function runPipeline(config: any) {
  const r = await fetch(`${BASE}/pipeline`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ config }),
  });
  if (!r.ok) {
    const text = await r.text();
    throw new Error(`Pipeline failed (${r.status}): ${text}`);
  }
  return r.json();
}

async function checkHealth() {
  const r = await fetch(`${BASE}/health`);
  return r.json();
}

function resolvePath(p?: string) {
  if (!p) return '';
  if (p.startsWith('http')) return p;
  const path = p.startsWith('/') ? p : `/${p}`;
  return `${BASE}${path}`;
}

export const MLBiasAPI = { runPipeline, checkHealth, resolvePath };