supafone-labs 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts CHANGED
@@ -287,16 +287,38 @@ export interface LabsVoiceProviderTraits {
287
287
  is_owner?: boolean | null;
288
288
  }
289
289
 
290
+ /** Stable provider presentation metadata returned by the hosted catalog. */
291
+ export interface LabsVoiceProviderBrand {
292
+ /** Canonical provider key used for filtering and grouping. */
293
+ key: string;
294
+ /** Human-readable provider/company name. */
295
+ name: string;
296
+ /** Stable local asset slug when Supafone has an official provider mark. */
297
+ logo_slug?: string | null;
298
+ /** Absolute, browser-ready provider logo URL. */
299
+ logo_url?: string | null;
300
+ /** Provider's official product or company website. */
301
+ website_url?: string | null;
302
+ }
303
+
290
304
  export interface LabsVoiceCatalogItem {
291
305
  /** Stable runtime reference, including provider prefix when required. */
292
306
  id: string;
293
307
  voice_id: string;
294
308
  /** Human-readable provider display name. */
295
309
  provider: string;
310
+ /** Canonical brand display name for the synthesis provider. */
311
+ provider_label?: string;
312
+ /** Browser-ready official provider mark, or null for an unknown provider. */
313
+ provider_logo_url?: string | null;
314
+ /** Branding for the provider that renders the voice. */
315
+ provider_brand?: LabsVoiceProviderBrand;
296
316
  /** Canonical SDK/runtime provider key. */
297
317
  provider_key: string;
298
318
  /** Runtime adapter used to place the voice on an Ultravox call. */
299
319
  runtime_provider_key: string;
320
+ /** Branding for the realtime runtime, which can differ from synthesis. */
321
+ runtime_provider_brand?: LabsVoiceProviderBrand;
300
322
  /** Actual TTS engine behind the voice (can differ for Ultravox built-ins). */
301
323
  synthesis_provider_key: string;
302
324
  /** Raw identifier used by the provider API. */
@@ -980,6 +1002,30 @@ export interface LabsAgentLifecycleResponse {
980
1002
  [extra: string]: unknown;
981
1003
  }
982
1004
 
1005
+ export interface LabsKnowledgeSyncRequest {
1006
+ websiteUrl?: string;
1007
+ website_url?: string;
1008
+ url?: string;
1009
+ }
1010
+
1011
+ export interface LabsKnowledgeSyncResponse {
1012
+ agent: LabsAgentResponse;
1013
+ chunks_indexed?: number;
1014
+ scraped_ok?: boolean;
1015
+ last_error?: string | null;
1016
+ website_detached?: boolean;
1017
+ }
1018
+
1019
+ export interface LabsKnowledgeChatTurn {
1020
+ role: "user" | "assistant";
1021
+ content: string;
1022
+ }
1023
+
1024
+ export interface LabsKnowledgeChatResponse {
1025
+ reply: string;
1026
+ sources: string[];
1027
+ }
1028
+
983
1029
  export interface LabsCapabilitiesResponse {
984
1030
  product: string;
985
1031
  api_namespace: string;
@@ -1034,7 +1080,10 @@ export interface LabsVoiceListResponse {
1034
1080
  configured: boolean;
1035
1081
  voice_count: number;
1036
1082
  error?: string | null;
1083
+ brand?: LabsVoiceProviderBrand;
1037
1084
  }>;
1085
+ /** Distinct synthesis-provider brands represented in the complete result. */
1086
+ provider_brands?: LabsVoiceProviderBrand[];
1038
1087
  provider_accounts?: Record<string, unknown>;
1039
1088
  errors?: Record<string, string>;
1040
1089
  catalog?: {
@@ -1942,6 +1991,37 @@ export class SupafoneLabs {
1942
1991
  }
1943
1992
  }
1944
1993
 
1994
+ /** @internal Multipart upload to the Supafone product API with the Labs key. */
1995
+ async requestSupafoneUpload<T>(
1996
+ path: string,
1997
+ file: Uint8Array | ArrayBuffer | Blob,
1998
+ filename: string,
1999
+ ): Promise<T> {
2000
+ const ctrl = new AbortController();
2001
+ const timer = setTimeout(() => ctrl.abort(), Math.max(this.timeoutMs, 120_000));
2002
+ try {
2003
+ const safeFilename = filename.replace(/["\\\r\n]/g, "").slice(0, 120) || "document.txt";
2004
+ const blob = file instanceof Blob ? file : new Blob([file as BlobPart]);
2005
+ const form = new FormData();
2006
+ form.append("file", blob, safeFilename);
2007
+ const res = await fetch(this.supafoneApiBaseUrl + path, {
2008
+ method: "POST",
2009
+ signal: ctrl.signal,
2010
+ headers: { Authorization: `Bearer ${this.supafoneApiKey}` },
2011
+ body: form,
2012
+ });
2013
+ const text = await res.text();
2014
+ const parsed = text ? safeJson(text) : {};
2015
+ if (!res.ok) {
2016
+ const detail = (parsed as { detail?: string })?.detail ?? text ?? `HTTP ${res.status}`;
2017
+ throw new SupafoneLabsError(`POST ${path}: ${detail}`, res.status, parsed);
2018
+ }
2019
+ return parsed as T;
2020
+ } finally {
2021
+ clearTimeout(timer);
2022
+ }
2023
+ }
2024
+
1945
2025
  /** @internal Authenticated binary request to the Supafone app API. */
1946
2026
  async requestSupafoneBinary(path: string): Promise<LabsVoicePreview> {
1947
2027
  const ctrl = new AbortController();
@@ -3153,6 +3233,26 @@ class LabsAgentsNamespace {
3153
3233
  );
3154
3234
  }
3155
3235
 
3236
+ /** Scrape a website and rebuild this agent's managed knowledge corpus. */
3237
+ syncKnowledge(
3238
+ agentId: string,
3239
+ input: LabsKnowledgeSyncRequest = {},
3240
+ ): Promise<LabsKnowledgeSyncResponse> {
3241
+ return this.sm.requestSupafoneApi<LabsKnowledgeSyncResponse>(
3242
+ "POST",
3243
+ `/api/v1/agents/${encodeURIComponent(agentId)}/sync-knowledge`,
3244
+ { url: input.url ?? input.website_url ?? input.websiteUrl ?? "" },
3245
+ );
3246
+ }
3247
+
3248
+ /** Remove website knowledge while preserving uploaded documents and notes. */
3249
+ detachWebsiteKnowledge(agentId: string): Promise<LabsKnowledgeSyncResponse> {
3250
+ return this.sm.requestSupafoneApi<LabsKnowledgeSyncResponse>(
3251
+ "DELETE",
3252
+ `/api/v1/agents/${encodeURIComponent(agentId)}/knowledge/website`,
3253
+ );
3254
+ }
3255
+
3156
3256
  /** Ask the managed server whether this agent is ready to activate. */
3157
3257
  readiness(agentKey: string, opts: GetLabsAgentOptions = {}): Promise<LabsAgentReadiness> {
3158
3258
  const q = new URLSearchParams();
@@ -3192,6 +3292,56 @@ class LabsAgentsNamespace {
3192
3292
  );
3193
3293
  }
3194
3294
 
3295
+ /** Start the native provider-neutral browser voice session for this agent. */
3296
+ startWebRtcCall(agentId: string): Promise<WebRtcCallResult> {
3297
+ return this.sm.startWebRtcCall({ agentId });
3298
+ }
3299
+
3300
+ /** Rebuild retrieval data from the agent's already saved knowledge sources. */
3301
+ reindexKnowledge(agentId: string): Promise<Record<string, unknown>> {
3302
+ return this.sm.requestSupafoneApi(
3303
+ "POST",
3304
+ `/api/v1/agents/${encodeURIComponent(agentId)}/knowledge/reindex`,
3305
+ );
3306
+ }
3307
+
3308
+ /** Upload and index a txt, md, csv, or PDF knowledge document. */
3309
+ uploadKnowledgeDocument(
3310
+ agentId: string,
3311
+ file: Uint8Array | ArrayBuffer | Blob,
3312
+ filename: string,
3313
+ ): Promise<{ agent: LabsAgentResponse }> {
3314
+ return this.sm.requestSupafoneUpload(
3315
+ `/api/v1/agents/${encodeURIComponent(agentId)}/knowledge/upload`,
3316
+ file,
3317
+ filename,
3318
+ );
3319
+ }
3320
+
3321
+ /** Delete one document and rebuild the agent's managed corpus. */
3322
+ deleteKnowledgeDocument(
3323
+ agentId: string,
3324
+ documentId: string,
3325
+ ): Promise<{ agent: LabsAgentResponse }> {
3326
+ return this.sm.requestSupafoneApi(
3327
+ "DELETE",
3328
+ `/api/v1/agents/${encodeURIComponent(agentId)}/knowledge/docs/${encodeURIComponent(documentId)}`,
3329
+ );
3330
+ }
3331
+
3332
+ /** Ask a grounded question against the same corpus used during calls. */
3333
+ chatKnowledge(
3334
+ agentId: string,
3335
+ message: string,
3336
+ history: LabsKnowledgeChatTurn[] = [],
3337
+ ): Promise<LabsKnowledgeChatResponse> {
3338
+ return this.sm.requestSupafoneApi(
3339
+ "POST",
3340
+ `/api/v1/agents/${encodeURIComponent(agentId)}/knowledge-chat`,
3341
+ { message, history },
3342
+ );
3343
+ }
3344
+
3195
3345
  /** Delete an agent. Optionally ask the backend to release assigned numbers. */
3196
3346
  delete(agentKey: string, opts: DeleteLabsAgentOptions = {}): Promise<DeleteLabsAgentResponse> {
3197
3347
  const q = new URLSearchParams();
@@ -4206,6 +4356,9 @@ function languageProfilesPayload(
4206
4356
  input?: LabsLanguageVoiceProfile[],
4207
4357
  ): Record<string, unknown>[] | undefined {
4208
4358
  if (!Array.isArray(input)) return undefined;
4359
+ if (input.length > 4) {
4360
+ throw new SupafoneLabsError("languageProfiles supports at most four profiles");
4361
+ }
4209
4362
  const profiles = input.map((profile) => compact({
4210
4363
  language: profile.language,
4211
4364
  language_hint: profile.language_hint ?? profile.languageHint,