File size: 3,958 Bytes
94004a2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import { API, apiJson, apiPost, apiFetch } from './client';

export interface MissingModel {
  repo_id: string;
  label: string;
}

export interface SetupStatus {
  models_ready: boolean;
  missing: MissingModel[];
  hf_cache_dir: string;
  disk_free_gb: number;
  min_free_gb: number;
  enough_disk: boolean;
}

export interface SetupProgressEvent {
  filename: string;
  downloaded: number;
  total: number;
  pct: number;
  phase: 'start' | 'progress' | 'done';
}

export async function setupStatus(): Promise<SetupStatus> {
  return apiJson<SetupStatus>('/setup/status');
}

export async function setupWarmup(): Promise<{ status: string }> {
  return apiPost<{ status: string }>('/setup/warmup');
}

export function setupDownloadStreamUrl(): string {
  return `${API}/setup/download-stream`;
}

// ── Model store ───────────────────────────────────────────────────────────

export interface KnownModel {
  repo_id: string;
  label: string;
  role: 'TTS' | 'ASR' | 'Diarisation' | string;
  size_gb: number;
  required: boolean;
  note?: string;
  installed: boolean;
  size_on_disk_bytes: number;
  nb_files: number;
}

export interface ModelList {
  models: KnownModel[];
  total_installed_bytes: number;
  hf_cache_dir: string;
}

export async function listModels(): Promise<ModelList> {
  return apiJson<ModelList>('/models');
}

export async function installModel(repo_id: string): Promise<{ status: string; repo_id: string }> {
  return apiPost('/models/install', { repo_id });
}

// ── Device-aware model recommendation ─────────────────────────────────────

export interface RecommendedModel {
  repo_id: string;
  label: string;
  role: string;
  size_gb: number;
  required: boolean;
  note: string | null;
  installed: boolean;
}

export interface Recommendations {
  device: {
    os: string;
    arch: string;
    is_mac_arm: boolean;
    is_mac_intel: boolean;
    is_linux: boolean;
    is_windows: boolean;
    has_cuda: boolean;
    label: string;
  };
  rationale: string;
  models: RecommendedModel[];
  download_gb_remaining: number;
  total_gb: number;
  all_installed: boolean;
}

export async function getRecommendations(): Promise<Recommendations> {
  return apiJson<Recommendations>('/setup/recommendations');
}

export async function deleteModel(repo_id: string): Promise<{ deleted: boolean; repo_id: string; freed_bytes: number }> {
  // HF repo_ids look like "owner/name" β€” encode each segment so special chars
  // are escaped but the literal "/" survives into FastAPI's `:path` converter.
  // encodeURIComponent on the whole string would turn "/" into "%2F", which
  // some ASGI middleware rejects as a path-traversal attempt.
  const path = repo_id.split('/').map(encodeURIComponent).join('/');
  const r = await apiFetch(`/models/${path}`, { method: 'DELETE' });
  return r.json();
}

// ── Pre-flight system check ───────────────────────────────────────────────

export type CheckStatus = 'pass' | 'warn' | 'fail';

export interface PreflightCheck {
  id: string;
  label: string;
  status: CheckStatus;
  detail: string;
  fix: string | null;
}

export interface PreflightDevice {
  os: string;
  arch: string;
  gpu_vendor: 'nvidia' | 'amd' | 'apple' | 'intel' | 'unknown' | 'none';
  gpu_backend: 'cuda' | 'rocm' | 'mps' | 'cpu';
  gpu_available: boolean;
  gpu_driver: string | null;
  gpu_device_name: string | null;
  ram_gb: number;
  disk_free_gb: number;
}

export interface PreflightReport {
  ok: boolean;
  has_warnings: boolean;
  checks: PreflightCheck[];
  device: PreflightDevice;
}

export async function preflight(): Promise<PreflightReport> {
  return apiJson<PreflightReport>('/setup/preflight');
}