File size: 2,350 Bytes
96f2542
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
export type Lang = { code: string; label: string };

export type ParamSpec = {
  name: string;
  label: string;
  type: "float" | "int" | "bool" | "enum";
  default: number | string | boolean;
  min?: number;
  max?: number;
  step?: number;
  choices?: string[];
  help?: string;
};

export type ModelInfo = {
  id: string;
  label: string;
  description: string;
  languages: Lang[];
  paralinguistic_tags: string[];
  supports_voice_clone: boolean;
  params: ParamSpec[];
};

export type ActiveStatus = {
  id: string | null;
  status: "idle" | "loading" | "loaded" | "error";
  last_error: string | null;
};

export async function listModels(): Promise<ModelInfo[]> {
  const r = await fetch("/api/models");
  if (!r.ok) throw new Error(`listModels: ${r.status}`);
  return r.json();
}

export async function getActiveModel(): Promise<ActiveStatus> {
  const r = await fetch("/api/models/active");
  if (!r.ok) throw new Error(`getActiveModel: ${r.status}`);
  return r.json();
}

export async function activateModel(id: string): Promise<void> {
  const r = await fetch(`/api/models/${encodeURIComponent(id)}/activate`, { method: "POST" });
  if (!r.ok) {
    const err = await r.json().catch(() => ({}));
    throw new Error(err?.error?.code ?? `activateModel: ${r.status}`);
  }
}

export type GenerateInput = {
  modelId: string;
  text: string;
  language?: string;
  params: Record<string, unknown>;
  reference?: Blob;
};

export async function generate(input: GenerateInput): Promise<Blob> {
  const fd = new FormData();
  fd.set("text", input.text);
  fd.set("model_id", input.modelId);
  fd.set("params", JSON.stringify(input.params ?? {}));
  if (input.language) fd.set("language", input.language);
  if (input.reference) fd.set("reference_wav", input.reference, "ref.wav");
  const r = await fetch("/api/generate", { method: "POST", body: fd });
  if (!r.ok) {
    const err = await r.json().catch(() => ({}));
    throw new Error(err?.error?.code ?? `generate: ${r.status}`);
  }
  return r.blob();
}

export function streamActiveEvents(
  onEvent: (e: { id: string | null; status: string; error?: string }) => void,
) {
  const es = new EventSource("/api/models/active/events");
  es.onmessage = (m) => {
    try {
      onEvent(JSON.parse(m.data));
    } catch {
      /* ignore malformed */
    }
  };
  return () => es.close();
}