File size: 3,156 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
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import React, { createContext, useContext, useMemo, useState } from 'react';
import type { JobConfig, JobResult } from '../types';
import { MLBiasAPI } from '../services/api';

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

type PipelineResultsDTO = {
  generation_file: string;
  sentiment_subset_file: string;
  cf_sentiment_subset_file: string;
  metrics: {
    finalMeanDiff: number;
    cfFinalMeanDiff: number;
    reductionPct?: number;
    stableCoverage?: number;
  };
  plots: PipelinePlots;
  finetuned_model_zip?: string;
  finetuned_model_dir?: string;
  run_config_files?: {
    json?: string;     
    markdown?: string; 
  };
};

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

type Extras = {
  datasetLimit: number
};

type Ctx = {
  result: JobResult | null;
  resp?: PipelineResponseDTO;
  loading: boolean;
  error?: string;
  start: (cfg: JobConfig, extras: Extras) => Promise<void>;
  url: (p?: string) => string;
};

const JobRunnerContext = createContext<Ctx | undefined>(undefined);

export function JobRunnerProvider({ children }: { children: React.ReactNode }) {
  const [result, setResult] = useState<JobResult | null>(null);
  const [resp, setResp] = useState<PipelineResponseDTO | undefined>();
  const [loading, setLoading] = useState(false);
  const [error, setErr] = useState<string | undefined>();

  const start: Ctx['start'] = async (cfg, extras) => {
    setLoading(true);
    setErr(undefined);
    setResp(undefined);

    const now = new Date().toISOString();
    setResult({
      id: crypto.randomUUID(),
      status: 'running',
      progress: 0,
      config: cfg,
      createdAt: now,
      updatedAt: now,
    });

    try {
      const cfgToSend = {
        ...cfg,
        datasetLimit: extras.datasetLimit
      } as unknown as JobConfig; 

      const r = await MLBiasAPI.runPipeline(cfgToSend as any); 

      setResp(r);

      const done = new Date().toISOString();
      setResult((prev) => ({
        ...(prev as JobResult),
        status: 'completed',
        progress: 100,
        updatedAt: done,
        completedAt: done,
        metrics: {
          finalMeanDiff: r.results.metrics.finalMeanDiff,
          reductionPct: r.results.metrics.reductionPct ?? 0,
          stableCoverage: r.results.metrics.stableCoverage ?? 100,
        },
      }));
    } catch (e: any) {
      setErr(e.message || String(e));
      setResult((prev) =>
        prev
          ? { ...prev, status: 'failed', progress: 100, updatedAt: new Date().toISOString() }
          : prev
      );
    } finally {
      setLoading(false);
    }
  };

  const url = MLBiasAPI.resolvePath;

  const value = useMemo<Ctx>(
    () => ({ result, resp, loading, error, start, url }),
    [result, resp, loading, error]
  );

  return <JobRunnerContext.Provider value={value}>{children}</JobRunnerContext.Provider>;
}

export function useJobRunner() {
  const ctx = useContext(JobRunnerContext);
  if (!ctx) throw new Error('useJobRunner must be used within JobRunnerProvider');
  return ctx;
}