Spaces:
Paused
Paused
Download src/services/labeleval.ts from whassan/SensorflowStudio: direct link, hf CLI and curl.
- Browser
- Download file 9.88 kB
-
https://huggingface.co/spaces/whassan/SensorflowStudio/resolve/main/src/services/labeleval.ts
- Command line
-
hf download hf://spaces/whassan/SensorflowStudio/src/services/labeleval.ts
-
curl -L -o labeleval.ts https://huggingface.co/spaces/whassan/SensorflowStudio/resolve/main/src/services/labeleval.ts
9.88 kB
| /** | |
| * Typed fetch wrappers for the L4 Perception Label Evaluation API, | |
| * plus a useStream hook (SSE with polling fallback) and a usePoll helper. | |
| */ | |
| import { useEffect, useRef, useState, useCallback } from 'react'; | |
| import type { | |
| AnomalyConfig, | |
| Alert, | |
| AuditEvent, | |
| BenchmarkResponse, | |
| CopilotExplainRequest, | |
| CopilotExplainResponse, | |
| DatasetSummary, | |
| EvaluationRecord, | |
| FrameSummary, | |
| FunnelResponse, | |
| GateLine, | |
| HaystackPoint, | |
| ModelSummary, | |
| OverviewResponse, | |
| PipelineStateResponse, | |
| ProcessUnitsResponse, | |
| QualityGroupDetail, | |
| QualityGroupsResponse, | |
| QualityMetrics, | |
| QueueStatus, | |
| RareEvent, | |
| RegressionResponse, | |
| ReviewActionRequest, | |
| ReviewActionResponse, | |
| ReviewTask, | |
| StreamEvent, | |
| TrainingJobStatus, | |
| TrainRequest, | |
| TrainResponse, | |
| } from '../types/labeleval'; | |
| import type { MegaevalStatus } from '../types/megaeval'; | |
| import { getMegaevalStatus } from './megaeval'; | |
| async function handle<T>(res: Response): Promise<T> { | |
| if (!res.ok) { | |
| const body = await res.text().catch(() => ''); | |
| throw new Error(`API ${res.status}: ${body || res.statusText}`); | |
| } | |
| return res.json() as Promise<T>; | |
| } | |
| const JSON_HEADERS = { 'Content-Type': 'application/json' }; | |
| function get<T>(url: string): Promise<T> { | |
| return fetch(url).then((res) => handle<T>(res)); | |
| } | |
| function post<T>(url: string, body?: unknown): Promise<T> { | |
| return fetch(url, { | |
| method: 'POST', | |
| headers: JSON_HEADERS, | |
| body: body === undefined ? undefined : JSON.stringify(body), | |
| }).then((res) => handle<T>(res)); | |
| } | |
| // ---------------------------------------------------------------- responses not in the shared contract | |
| export interface PrecheckResponse { | |
| status: string; | |
| message: string; | |
| checks: GateLine[]; | |
| } | |
| export interface GenerateDatasetRequest { | |
| name?: string; | |
| num_sequences?: number; | |
| frames_per_sequence?: number; | |
| seed?: number; | |
| } | |
| export interface RunResponse { | |
| run_id: string; | |
| status: string; | |
| } | |
| export interface FrameResponse { | |
| frame: FrameSummary; | |
| prev: string | null; | |
| next: string | null; | |
| } | |
| // ---------------------------------------------------------------- endpoints | |
| export const getOverview = () => get<OverviewResponse>('/api/labeleval/overview'); | |
| export const getPipeline = () => get<PipelineStateResponse>('/api/labeleval/pipeline'); | |
| export const getFunnel = () => get<FunnelResponse>('/api/labeleval/funnel'); | |
| export const getQueueStatus = () => get<QueueStatus>('/api/queue/status'); | |
| export const getDatasets = () => get<{ datasets: DatasetSummary[] }>('/api/datasets'); | |
| export const getDataset = (id: string) => get<DatasetSummary>(`/api/datasets/${encodeURIComponent(id)}`); | |
| export const precheckDataset = (datasetId?: string) => | |
| post<PrecheckResponse>('/api/dataset/precheck', datasetId ? { dataset_id: datasetId } : {}); | |
| export const generateDataset = (body: GenerateDatasetRequest) => | |
| post<DatasetSummary>('/api/labeleval/datasets/generate', body); | |
| export const runPipeline = (datasetId: string, policyId?: string) => | |
| post<RunResponse>('/api/labeleval/run', { dataset_id: datasetId, ...(policyId ? { policy_id: policyId } : {}) }); | |
| export const getFrameIds = (datasetId: string) => | |
| get<{ frame_ids: string[] }>(`/api/labeleval/frames?dataset_id=${encodeURIComponent(datasetId)}`); | |
| export const getFrame = (frameId: string) => | |
| get<FrameResponse>(`/api/labeleval/frames/${encodeURIComponent(frameId)}`); | |
| export const getEvaluation = (annotationId: string) => | |
| get<EvaluationRecord>(`/api/labeleval/evaluations/${encodeURIComponent(annotationId)}`); | |
| export const getHaystack = (datasetId?: string | null) => | |
| get<{ points: HaystackPoint[] }>( | |
| datasetId ? `/api/labeleval/haystack?dataset_id=${encodeURIComponent(datasetId)}` : '/api/labeleval/haystack' | |
| ); | |
| export const getRareEvents = () => get<{ events: RareEvent[] }>('/api/labeleval/rare-events'); | |
| export const getQualityMetrics = (datasetId?: string | null) => | |
| get<QualityMetrics>( | |
| datasetId ? `/api/quality/metrics?dataset_id=${encodeURIComponent(datasetId)}` : '/api/quality/metrics' | |
| ); | |
| export const getQualityGroups = (datasetId?: string | null) => | |
| get<QualityGroupsResponse>( | |
| datasetId ? `/api/quality/groups?dataset_id=${encodeURIComponent(datasetId)}` : '/api/quality/groups' | |
| ); | |
| export const getQualityGroupDetail = (groupId: string) => | |
| get<QualityGroupDetail>(`/api/quality/groups/${encodeURIComponent(groupId)}`); | |
| export const getRegression = () => get<RegressionResponse>('/api/regression'); | |
| export const getReviewTasks = () => get<{ tasks: ReviewTask[] }>('/api/review/tasks'); | |
| export const getReviewTask = (id: string) => get<ReviewTask>(`/api/review/tasks/${encodeURIComponent(id)}`); | |
| export const postReviewAction = (id: string, body: ReviewActionRequest) => | |
| post<ReviewActionResponse>(`/api/review/tasks/${encodeURIComponent(id)}`, body); | |
| export const getModels = () => get<{ models: ModelSummary[] }>('/api/models'); | |
| export const postTrain = (body: TrainRequest) => post<TrainResponse>('/api/train', body); | |
| export const getTrainJobs = () => get<{ jobs: TrainingJobStatus[] }>('/api/train/jobs'); | |
| export const getTrainJob = (jobId: string) => | |
| get<TrainingJobStatus>(`/api/train/jobs/${encodeURIComponent(jobId)}`); | |
| export const getAnomalyConfig = () => get<AnomalyConfig>('/api/labeleval/config'); | |
| export const postAnomalyConfig = (body: AnomalyConfig) => post<AnomalyConfig>('/api/labeleval/config', body); | |
| export const runBenchmark = (datasetId?: string | null) => | |
| post<BenchmarkResponse>('/api/benchmark/techniques', datasetId ? { dataset_id: datasetId } : {}); | |
| export const getBenchmark = () => get<BenchmarkResponse>('/api/benchmark/techniques'); | |
| export const getProcessUnits = () => get<ProcessUnitsResponse>('/api/labeleval/process-units'); | |
| export const getAlerts = () => get<{ alerts: Alert[] }>('/api/labeleval/alerts'); | |
| export const getAudit = (limit = 200) => get<{ events: AuditEvent[] }>(`/api/labeleval/audit?limit=${limit}`); | |
| export const copilotExplain = (body: CopilotExplainRequest) => | |
| post<CopilotExplainResponse>('/api/copilot/explain', body); | |
| // ---------------------------------------------------------------- polling hook | |
| export interface PollState<T> { | |
| data: T | null; | |
| loading: boolean; | |
| error: string | null; | |
| refresh: () => void; | |
| } | |
| /** | |
| * Simple poll-based data fetching. Pass intervalMs = null to fetch once. | |
| * `deps` re-triggers the initial fetch (with loading state) when changed. | |
| */ | |
| export function usePoll<T>( | |
| fetcher: () => Promise<T>, | |
| intervalMs: number | null, | |
| deps: ReadonlyArray<unknown> = [] | |
| ): PollState<T> { | |
| const [data, setData] = useState<T | null>(null); | |
| const [loading, setLoading] = useState(true); | |
| const [error, setError] = useState<string | null>(null); | |
| const [tick, setTick] = useState(0); | |
| const fetcherRef = useRef(fetcher); | |
| fetcherRef.current = fetcher; | |
| useEffect(() => { | |
| let cancelled = false; | |
| let timer: ReturnType<typeof setTimeout> | null = null; | |
| setLoading(true); | |
| const run = async () => { | |
| try { | |
| const result = await fetcherRef.current(); | |
| if (!cancelled) { | |
| setData(result); | |
| setError(null); | |
| } | |
| } catch (err) { | |
| if (!cancelled) setError(err instanceof Error ? err.message : String(err)); | |
| } finally { | |
| if (!cancelled) { | |
| setLoading(false); | |
| if (intervalMs !== null) timer = setTimeout(run, intervalMs); | |
| } | |
| } | |
| }; | |
| void run(); | |
| return () => { | |
| cancelled = true; | |
| if (timer) clearTimeout(timer); | |
| }; | |
| // eslint-disable-next-line react-hooks/exhaustive-deps | |
| }, [intervalMs, tick, ...deps]); | |
| const refresh = useCallback(() => setTick((t) => t + 1), []); | |
| return { data, loading, error, refresh }; | |
| } | |
| // ---------------------------------------------------------------- SSE stream hook | |
| /** | |
| * Subscribes to /api/events/stream via EventSource. If the stream errors, | |
| * falls back to polling GET /api/labeleval/pipeline every 2s (synthesizing a | |
| * StreamEvent so consumers see a single shape). | |
| */ | |
| export function useStream(): StreamEvent | null { | |
| const [event, setEvent] = useState<StreamEvent | null>(null); | |
| useEffect(() => { | |
| let disposed = false; | |
| let es: EventSource | null = null; | |
| let pollTimer: ReturnType<typeof setInterval> | null = null; | |
| let lastAlerts = 0; | |
| const startPolling = () => { | |
| if (pollTimer || disposed) return; | |
| const poll = async () => { | |
| try { | |
| const pipeline = await getPipeline(); | |
| try { | |
| lastAlerts = (await getAlerts()).alerts.length; | |
| } catch { | |
| /* keep previous alert count */ | |
| } | |
| let megaeval: MegaevalStatus | undefined; | |
| try { | |
| megaeval = await getMegaevalStatus(); | |
| } catch { | |
| /* megaeval layer unavailable — leave the field absent */ | |
| } | |
| if (!disposed) { | |
| setEvent({ | |
| ts: new Date().toISOString(), | |
| pipeline, | |
| training: null, | |
| alerts_count: lastAlerts, | |
| ...(megaeval ? { megaeval } : {}), | |
| }); | |
| } | |
| } catch { | |
| /* backend offline — keep last known state */ | |
| } | |
| }; | |
| void poll(); | |
| pollTimer = setInterval(() => void poll(), 2000); | |
| }; | |
| try { | |
| es = new EventSource('/api/events/stream'); | |
| es.onmessage = (msg: MessageEvent<string>) => { | |
| try { | |
| setEvent(JSON.parse(msg.data) as StreamEvent); | |
| } catch { | |
| /* ignore malformed event */ | |
| } | |
| }; | |
| es.onerror = () => { | |
| if (es) { | |
| es.close(); | |
| es = null; | |
| } | |
| startPolling(); | |
| }; | |
| } catch { | |
| startPolling(); | |
| } | |
| return () => { | |
| disposed = true; | |
| if (es) es.close(); | |
| if (pollTimer) clearInterval(pollTimer); | |
| }; | |
| }, []); | |
| return event; | |
| } | |