supafone-labs 0.4.12 → 0.4.13
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/README.md +30 -0
- package/dist/cjs/index.d.ts +330 -3
- package/dist/cjs/index.js +334 -6
- package/dist/index.d.ts +330 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +334 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +679 -8
package/src/index.ts
CHANGED
|
@@ -93,6 +93,64 @@ export interface WhisperOptions {
|
|
|
93
93
|
temperature?: number;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
export type SecondMindDirectiveKind = "empathy" | "tactical" | "guardrail" | "mixed";
|
|
97
|
+
|
|
98
|
+
export interface DirectiveTextControl {
|
|
99
|
+
enabled?: boolean;
|
|
100
|
+
instructions?: string;
|
|
101
|
+
maxChars?: number;
|
|
102
|
+
max_chars?: number;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface DirectiveListControl {
|
|
106
|
+
enabled?: boolean;
|
|
107
|
+
instructions?: string;
|
|
108
|
+
maxItems?: number;
|
|
109
|
+
max_items?: number;
|
|
110
|
+
itemMaxChars?: number;
|
|
111
|
+
item_max_chars?: number;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Serializable controls for each field generated by SecondMind. */
|
|
115
|
+
export interface DirectiveContract {
|
|
116
|
+
empathyDirective?: DirectiveTextControl;
|
|
117
|
+
empathy_directive?: DirectiveTextControl;
|
|
118
|
+
tacticalDirective?: DirectiveTextControl;
|
|
119
|
+
tactical_directive?: DirectiveTextControl;
|
|
120
|
+
surfaceFacts?: DirectiveListControl;
|
|
121
|
+
surface_facts?: DirectiveListControl;
|
|
122
|
+
guardrails?: DirectiveListControl;
|
|
123
|
+
languageMode?: "caller" | "model" | "fixed";
|
|
124
|
+
language_mode?: "caller" | "model" | "fixed";
|
|
125
|
+
fixedLanguage?: string;
|
|
126
|
+
fixed_language?: string;
|
|
127
|
+
allowedKinds?: SecondMindDirectiveKind[];
|
|
128
|
+
allowed_kinds?: SecondMindDirectiveKind[];
|
|
129
|
+
confidenceThreshold?: number;
|
|
130
|
+
confidence_threshold?: number;
|
|
131
|
+
operatorGuardrails?: string[];
|
|
132
|
+
operator_guardrails?: string[];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface SecondMindDirective {
|
|
136
|
+
empathy_directive: string;
|
|
137
|
+
tactical_directive: string;
|
|
138
|
+
surface_facts: string[];
|
|
139
|
+
guardrails: string[];
|
|
140
|
+
language: string;
|
|
141
|
+
confidence: number;
|
|
142
|
+
kind: SecondMindDirectiveKind;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface StructuredWhisperOptions extends WhisperOptions {
|
|
146
|
+
directiveContract?: DirectiveContract;
|
|
147
|
+
directive_contract?: DirectiveContract;
|
|
148
|
+
/** Local final say: revise or suppress the generated directive before use. */
|
|
149
|
+
transform?: (
|
|
150
|
+
directive: SecondMindDirective,
|
|
151
|
+
) => SecondMindDirective | null | Promise<SecondMindDirective | null>;
|
|
152
|
+
}
|
|
153
|
+
|
|
96
154
|
export interface Balance {
|
|
97
155
|
plan: string;
|
|
98
156
|
seconds_remaining: number;
|
|
@@ -177,6 +235,115 @@ export interface LabsVoiceSelection {
|
|
|
177
235
|
model?: string;
|
|
178
236
|
}
|
|
179
237
|
|
|
238
|
+
export type LabsVoiceGender = "female" | "male" | "neutral";
|
|
239
|
+
|
|
240
|
+
export interface LabsVoiceLanguage {
|
|
241
|
+
code: string;
|
|
242
|
+
locale: string;
|
|
243
|
+
name: string;
|
|
244
|
+
native_name: string;
|
|
245
|
+
aliases?: string[];
|
|
246
|
+
routing_supported?: boolean;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export interface LabsVoiceLanguageSupport {
|
|
250
|
+
provider: string;
|
|
251
|
+
model?: string | null;
|
|
252
|
+
known: boolean;
|
|
253
|
+
language_codes: string[];
|
|
254
|
+
/** Number of language codes Supafone can enumerate from provider docs. */
|
|
255
|
+
enumerated_language_count: number;
|
|
256
|
+
generally_available_language_codes: string[];
|
|
257
|
+
experimental_language_codes: string[];
|
|
258
|
+
documented_language_count: number;
|
|
259
|
+
documented_language_count_is_minimum: boolean;
|
|
260
|
+
support_tier: string;
|
|
261
|
+
cross_lingual: boolean | null;
|
|
262
|
+
documentation_url?: string | null;
|
|
263
|
+
verified_at: string;
|
|
264
|
+
native_language_codes: string[];
|
|
265
|
+
ultravox_routing_language_codes: string[];
|
|
266
|
+
primary_language_support_tier: "generally_available" | "experimental" | "unknown";
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export interface LabsVoiceProviderTraits {
|
|
270
|
+
countries: string[];
|
|
271
|
+
accents: string[];
|
|
272
|
+
age_groups: string[];
|
|
273
|
+
categories: string[];
|
|
274
|
+
use_cases: string[];
|
|
275
|
+
descriptors: string[];
|
|
276
|
+
native_locales: string[];
|
|
277
|
+
created_at?: string | number | null;
|
|
278
|
+
is_public?: boolean | null;
|
|
279
|
+
is_owner?: boolean | null;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export interface LabsVoiceCatalogItem {
|
|
283
|
+
/** Stable runtime reference, including provider prefix when required. */
|
|
284
|
+
id: string;
|
|
285
|
+
voice_id: string;
|
|
286
|
+
/** Human-readable provider display name. */
|
|
287
|
+
provider: string;
|
|
288
|
+
/** Canonical SDK/runtime provider key. */
|
|
289
|
+
provider_key: string;
|
|
290
|
+
/** Runtime adapter used to place the voice on an Ultravox call. */
|
|
291
|
+
runtime_provider_key: string;
|
|
292
|
+
/** Actual TTS engine behind the voice (can differ for Ultravox built-ins). */
|
|
293
|
+
synthesis_provider_key: string;
|
|
294
|
+
/** Raw identifier used by the provider API. */
|
|
295
|
+
provider_voice_id: string;
|
|
296
|
+
name: string;
|
|
297
|
+
label: string;
|
|
298
|
+
description: string;
|
|
299
|
+
style: string;
|
|
300
|
+
model?: string | null;
|
|
301
|
+
language: string;
|
|
302
|
+
language_code: string;
|
|
303
|
+
language_locale: string;
|
|
304
|
+
language_name: string;
|
|
305
|
+
native_language_name: string;
|
|
306
|
+
languages: LabsVoiceLanguage[];
|
|
307
|
+
native_language_codes: string[];
|
|
308
|
+
model_language_codes: string[];
|
|
309
|
+
runtime_supported_language_codes: string[];
|
|
310
|
+
routing_supported: boolean;
|
|
311
|
+
language_support: LabsVoiceLanguageSupport;
|
|
312
|
+
gender: LabsVoiceGender;
|
|
313
|
+
accent: string;
|
|
314
|
+
age: string;
|
|
315
|
+
provider_traits: LabsVoiceProviderTraits;
|
|
316
|
+
voice_types: string[];
|
|
317
|
+
primary_voice_type: string;
|
|
318
|
+
tags: string[];
|
|
319
|
+
/** Sanitized provider-native fields retained for forward-compatible filtering. */
|
|
320
|
+
provider_metadata: Record<string, unknown>;
|
|
321
|
+
provider_metadata_fields: string[];
|
|
322
|
+
preview_url?: string | null;
|
|
323
|
+
preview_available: boolean;
|
|
324
|
+
source: string;
|
|
325
|
+
ownership?: string | null;
|
|
326
|
+
configured: boolean;
|
|
327
|
+
recommended: boolean;
|
|
328
|
+
premium?: boolean;
|
|
329
|
+
is_custom: boolean;
|
|
330
|
+
runtime: { provider: string; voice_id: string; model?: string };
|
|
331
|
+
[extra: string]: unknown;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export interface LabsVoicePreference {
|
|
335
|
+
description: string;
|
|
336
|
+
language?: string;
|
|
337
|
+
provider?: string;
|
|
338
|
+
gender?: LabsVoiceGender;
|
|
339
|
+
voiceType?: string;
|
|
340
|
+
voice_type?: string;
|
|
341
|
+
model?: string;
|
|
342
|
+
configuredOnly?: boolean;
|
|
343
|
+
configured_only?: boolean;
|
|
344
|
+
premium?: boolean;
|
|
345
|
+
}
|
|
346
|
+
|
|
180
347
|
export interface LabsProviderKeys {
|
|
181
348
|
/** Agent runtime/platform providers. */
|
|
182
349
|
ultravox?: string;
|
|
@@ -536,8 +703,14 @@ export interface CreateLabsAgentRequest {
|
|
|
536
703
|
greeting?: string;
|
|
537
704
|
systemPrompt?: string;
|
|
538
705
|
system_prompt?: string;
|
|
706
|
+
/** Fixed language for the full call. Does not enable mid-call switching. */
|
|
539
707
|
language?: string;
|
|
708
|
+
preferredLanguage?: string;
|
|
709
|
+
preferred_language?: string;
|
|
540
710
|
voice?: LabsVoiceSelection;
|
|
711
|
+
/** Resolve a real current catalog voice from this plain-language preference. */
|
|
712
|
+
voicePreference?: LabsVoicePreference;
|
|
713
|
+
voice_preference?: LabsVoicePreference;
|
|
541
714
|
providerKeys?: LabsProviderKeys;
|
|
542
715
|
provider_keys?: LabsProviderKeys;
|
|
543
716
|
byok?: LabsProviderKeys | LabsByokConfig;
|
|
@@ -605,17 +778,106 @@ export interface LabsToolListResponse {
|
|
|
605
778
|
export interface LabsVoiceListOptions {
|
|
606
779
|
provider?: string;
|
|
607
780
|
search?: string;
|
|
781
|
+
/** Native/accent language advertised for the individual voice. */
|
|
608
782
|
language?: string;
|
|
783
|
+
/** Language that the provider model and Ultravox can both run live. */
|
|
784
|
+
compatibleLanguage?: string;
|
|
785
|
+
compatible_language?: string;
|
|
786
|
+
gender?: LabsVoiceGender;
|
|
787
|
+
voiceType?: string;
|
|
788
|
+
voice_type?: string;
|
|
789
|
+
model?: string;
|
|
790
|
+
runtimeProvider?: string;
|
|
791
|
+
runtime_provider?: string;
|
|
792
|
+
configuredOnly?: boolean;
|
|
793
|
+
configured_only?: boolean;
|
|
609
794
|
cursor?: number;
|
|
610
795
|
limit?: number;
|
|
796
|
+
agencyId?: string;
|
|
797
|
+
agency_id?: string;
|
|
611
798
|
}
|
|
612
799
|
|
|
613
800
|
export interface LabsVoiceListResponse {
|
|
614
|
-
|
|
801
|
+
account_id?: string;
|
|
802
|
+
voices: LabsVoiceCatalogItem[];
|
|
615
803
|
total: number;
|
|
616
|
-
|
|
804
|
+
cursor?: number;
|
|
805
|
+
next_cursor?: number | null;
|
|
806
|
+
providers: Array<{
|
|
807
|
+
id: string;
|
|
808
|
+
label: string;
|
|
809
|
+
configured: boolean;
|
|
810
|
+
voice_count: number;
|
|
811
|
+
error?: string | null;
|
|
812
|
+
}>;
|
|
617
813
|
provider_accounts?: Record<string, unknown>;
|
|
618
814
|
errors?: Record<string, string>;
|
|
815
|
+
catalog?: {
|
|
816
|
+
dynamic: boolean;
|
|
817
|
+
source: string;
|
|
818
|
+
normalization_schema_version: number;
|
|
819
|
+
cache_ttl_seconds: number;
|
|
820
|
+
capabilities_url?: string;
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
export interface LabsVoiceModelCapability {
|
|
825
|
+
provider: string;
|
|
826
|
+
model?: string | null;
|
|
827
|
+
known: boolean;
|
|
828
|
+
language_codes: string[];
|
|
829
|
+
enumerated_language_count: number;
|
|
830
|
+
generally_available_language_codes: string[];
|
|
831
|
+
experimental_language_codes: string[];
|
|
832
|
+
documented_language_count: number;
|
|
833
|
+
documented_language_count_is_minimum: boolean;
|
|
834
|
+
support_tier: string;
|
|
835
|
+
cross_lingual: boolean | null;
|
|
836
|
+
documentation_url?: string | null;
|
|
837
|
+
verified_at: string;
|
|
838
|
+
ultravox_routing_language_codes: string[];
|
|
839
|
+
ultravox_routing_language_count: number;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
export interface LabsVoiceProviderCapability {
|
|
843
|
+
provider: string;
|
|
844
|
+
label: string;
|
|
845
|
+
ultravox_integration: "native" | "named_external" | string;
|
|
846
|
+
default_model?: string | null;
|
|
847
|
+
models: LabsVoiceModelCapability[];
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
export interface LabsVoiceCapabilitiesResponse {
|
|
851
|
+
runtime: "ultravox" | string;
|
|
852
|
+
runtime_spoken_languages: LabsVoiceLanguage[];
|
|
853
|
+
runtime_spoken_language_count: number;
|
|
854
|
+
providers: LabsVoiceProviderCapability[];
|
|
855
|
+
selection_rule: Record<string, string>;
|
|
856
|
+
normalization_schema_version: number;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
export interface LabsVoiceRecommendOptions extends LabsVoicePreference {
|
|
860
|
+
limit?: number;
|
|
861
|
+
agencyId?: string;
|
|
862
|
+
agency_id?: string;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
export interface LabsVoiceRecommendation {
|
|
866
|
+
voice: LabsVoiceCatalogItem;
|
|
867
|
+
score: number;
|
|
868
|
+
reasons: string[];
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
export interface LabsVoiceRecommendResponse {
|
|
872
|
+
account_id?: string;
|
|
873
|
+
description: string;
|
|
874
|
+
matches: LabsVoiceRecommendation[];
|
|
875
|
+
total_considered: number;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
export interface LabsVoicePreview {
|
|
879
|
+
content: ArrayBuffer;
|
|
880
|
+
mediaType: string;
|
|
619
881
|
}
|
|
620
882
|
|
|
621
883
|
export interface LabsRuntimeResponse {
|
|
@@ -629,9 +891,11 @@ export interface LabsRuntimeResponse {
|
|
|
629
891
|
|
|
630
892
|
export interface LabsCallListOptions {
|
|
631
893
|
agencyId?: string;
|
|
894
|
+
agency_id?: string;
|
|
632
895
|
agentKey?: string;
|
|
633
896
|
agent_key?: string;
|
|
634
897
|
limit?: number;
|
|
898
|
+
offset?: number;
|
|
635
899
|
}
|
|
636
900
|
|
|
637
901
|
export interface LabsCallArtifact {
|
|
@@ -642,7 +906,11 @@ export interface LabsCallArtifact {
|
|
|
642
906
|
started_at?: string;
|
|
643
907
|
duration_seconds?: number;
|
|
644
908
|
recording_url?: string;
|
|
909
|
+
recording_download_url?: string;
|
|
910
|
+
recording_archived?: boolean;
|
|
645
911
|
transcript_url?: string;
|
|
912
|
+
watcher_events?: LabsDeveloperActivityEvent[];
|
|
913
|
+
watcher_event_count?: number;
|
|
646
914
|
[extra: string]: unknown;
|
|
647
915
|
}
|
|
648
916
|
|
|
@@ -671,6 +939,40 @@ export interface LabsTranscriptListResponse {
|
|
|
671
939
|
[extra: string]: unknown;
|
|
672
940
|
}
|
|
673
941
|
|
|
942
|
+
export interface LabsDeveloperActivityEvent {
|
|
943
|
+
id: string;
|
|
944
|
+
account_id: string;
|
|
945
|
+
event_type: string;
|
|
946
|
+
resource_type: string;
|
|
947
|
+
resource_id: string;
|
|
948
|
+
source?: string;
|
|
949
|
+
detail?: Record<string, unknown>;
|
|
950
|
+
created_at: string;
|
|
951
|
+
[extra: string]: unknown;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
export interface LabsActivityListOptions {
|
|
955
|
+
accountId?: string;
|
|
956
|
+
account_id?: string;
|
|
957
|
+
agencyId?: string;
|
|
958
|
+
agency_id?: string;
|
|
959
|
+
eventType?: string;
|
|
960
|
+
event_type?: string;
|
|
961
|
+
resourceType?: string;
|
|
962
|
+
resource_type?: string;
|
|
963
|
+
resourceId?: string;
|
|
964
|
+
resource_id?: string;
|
|
965
|
+
limit?: number;
|
|
966
|
+
offset?: number;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
export interface LabsActivityListResponse {
|
|
970
|
+
events: LabsDeveloperActivityEvent[];
|
|
971
|
+
count: number;
|
|
972
|
+
next_offset?: number | null;
|
|
973
|
+
account_id?: string;
|
|
974
|
+
}
|
|
975
|
+
|
|
674
976
|
export interface LabsPhoneNumberSearchOptions {
|
|
675
977
|
agencyId?: string;
|
|
676
978
|
agency_id?: string;
|
|
@@ -1136,6 +1438,140 @@ const COACH_SYSTEM =
|
|
|
1136
1438
|
"speaks aloud — a correction or nudge, phrased imperatively. If nothing needs " +
|
|
1137
1439
|
"correcting, return an empty string.";
|
|
1138
1440
|
|
|
1441
|
+
const STRUCTURED_COACH_SYSTEM =
|
|
1442
|
+
"You are the coaching core of a second mind for a live voice agent. Read the " +
|
|
1443
|
+
"conversation and return ONLY one JSON object with exactly these keys: " +
|
|
1444
|
+
"empathy_directive (string), tactical_directive (string), surface_facts (string[]), " +
|
|
1445
|
+
"guardrails (string[]), language (short code), confidence (0-1), and kind " +
|
|
1446
|
+
"(empathy, tactical, guardrail, or mixed). Be silent with low confidence when evidence is weak.";
|
|
1447
|
+
|
|
1448
|
+
interface NormalizedDirectiveContract {
|
|
1449
|
+
empathy_directive: Required<Pick<DirectiveTextControl, "enabled" | "instructions">> & { max_chars: number };
|
|
1450
|
+
tactical_directive: Required<Pick<DirectiveTextControl, "enabled" | "instructions">> & { max_chars: number };
|
|
1451
|
+
surface_facts: Required<Pick<DirectiveListControl, "enabled" | "instructions">> & {
|
|
1452
|
+
max_items: number;
|
|
1453
|
+
item_max_chars: number;
|
|
1454
|
+
};
|
|
1455
|
+
guardrails: Required<Pick<DirectiveListControl, "enabled" | "instructions">> & {
|
|
1456
|
+
max_items: number;
|
|
1457
|
+
item_max_chars: number;
|
|
1458
|
+
};
|
|
1459
|
+
language_mode: "caller" | "model" | "fixed";
|
|
1460
|
+
fixed_language: string;
|
|
1461
|
+
allowed_kinds: SecondMindDirectiveKind[];
|
|
1462
|
+
confidence_threshold: number;
|
|
1463
|
+
operator_guardrails: string[];
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
function boundedInteger(value: unknown, fallback: number, minimum: number, maximum: number): number {
|
|
1467
|
+
const parsed = Number(value);
|
|
1468
|
+
return Number.isFinite(parsed)
|
|
1469
|
+
? Math.max(minimum, Math.min(maximum, Math.trunc(parsed)))
|
|
1470
|
+
: fallback;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
function textControl(input: DirectiveTextControl | undefined): NormalizedDirectiveContract["empathy_directive"] {
|
|
1474
|
+
return {
|
|
1475
|
+
enabled: input?.enabled ?? true,
|
|
1476
|
+
instructions: String(input?.instructions ?? "").trim(),
|
|
1477
|
+
max_chars: boundedInteger(input?.max_chars ?? input?.maxChars, 240, 1, 2000),
|
|
1478
|
+
};
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
function listControl(input: DirectiveListControl | undefined): NormalizedDirectiveContract["surface_facts"] {
|
|
1482
|
+
return {
|
|
1483
|
+
enabled: input?.enabled ?? true,
|
|
1484
|
+
instructions: String(input?.instructions ?? "").trim(),
|
|
1485
|
+
max_items: boundedInteger(input?.max_items ?? input?.maxItems, 4, 0, 20),
|
|
1486
|
+
item_max_chars: boundedInteger(input?.item_max_chars ?? input?.itemMaxChars, 180, 1, 2000),
|
|
1487
|
+
};
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
function normalizeDirectiveContract(input: DirectiveContract = {}): NormalizedDirectiveContract {
|
|
1491
|
+
const languageMode = input.language_mode ?? input.languageMode ?? "caller";
|
|
1492
|
+
const allowed = input.allowed_kinds ?? input.allowedKinds ?? ["empathy", "tactical", "guardrail", "mixed"];
|
|
1493
|
+
const threshold = Number(input.confidence_threshold ?? input.confidenceThreshold ?? 0.5);
|
|
1494
|
+
return {
|
|
1495
|
+
empathy_directive: textControl(input.empathy_directive ?? input.empathyDirective),
|
|
1496
|
+
tactical_directive: textControl(input.tactical_directive ?? input.tacticalDirective),
|
|
1497
|
+
surface_facts: listControl(input.surface_facts ?? input.surfaceFacts),
|
|
1498
|
+
guardrails: listControl(input.guardrails),
|
|
1499
|
+
language_mode: languageMode,
|
|
1500
|
+
fixed_language: String(input.fixed_language ?? input.fixedLanguage ?? "").trim(),
|
|
1501
|
+
allowed_kinds: allowed.filter((kind): kind is SecondMindDirectiveKind =>
|
|
1502
|
+
["empathy", "tactical", "guardrail", "mixed"].includes(kind),
|
|
1503
|
+
),
|
|
1504
|
+
confidence_threshold: Number.isFinite(threshold) ? Math.max(0, Math.min(1, threshold)) : 0.5,
|
|
1505
|
+
operator_guardrails: (input.operator_guardrails ?? input.operatorGuardrails ?? [])
|
|
1506
|
+
.map((rule) => String(rule).trim())
|
|
1507
|
+
.filter(Boolean)
|
|
1508
|
+
.slice(0, 50),
|
|
1509
|
+
};
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
function directiveContractPrompt(contract: NormalizedDirectiveContract): string {
|
|
1513
|
+
return (
|
|
1514
|
+
"Developer directive contract (obey exactly):\n" +
|
|
1515
|
+
JSON.stringify(contract, null, 2) +
|
|
1516
|
+
"\nDisabled string fields must be empty strings. Disabled list fields must be empty arrays. " +
|
|
1517
|
+
"Standing operator guardrails are mandatory and cannot be removed."
|
|
1518
|
+
);
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
function parseJsonObject(text: string): Record<string, unknown> | null {
|
|
1522
|
+
const trimmed = String(text ?? "").trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "");
|
|
1523
|
+
const start = trimmed.indexOf("{");
|
|
1524
|
+
const end = trimmed.lastIndexOf("}");
|
|
1525
|
+
if (start < 0 || end <= start) return null;
|
|
1526
|
+
try {
|
|
1527
|
+
const parsed = JSON.parse(trimmed.slice(start, end + 1));
|
|
1528
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
1529
|
+
} catch {
|
|
1530
|
+
return null;
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
function clipText(value: unknown, limit: number): string {
|
|
1535
|
+
return String(value ?? "").trim().slice(0, limit);
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
function clipList(value: unknown, maxItems: number, itemMaxChars: number): string[] {
|
|
1539
|
+
if (!Array.isArray(value)) return [];
|
|
1540
|
+
return value.slice(0, maxItems).map((item) => clipText(item, itemMaxChars)).filter(Boolean);
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
function normalizeStructuredDirective(
|
|
1544
|
+
raw: Record<string, unknown>,
|
|
1545
|
+
contract: NormalizedDirectiveContract,
|
|
1546
|
+
): SecondMindDirective | null {
|
|
1547
|
+
const kind = String(raw.kind ?? "mixed") as SecondMindDirectiveKind;
|
|
1548
|
+
if (!["empathy", "tactical", "guardrail", "mixed"].includes(kind)) return null;
|
|
1549
|
+
const parsedConfidence = Number(raw.confidence ?? 0);
|
|
1550
|
+
const confidence = Number.isFinite(parsedConfidence) ? Math.max(0, Math.min(1, parsedConfidence)) : 0;
|
|
1551
|
+
const operatorGuardrails = contract.operator_guardrails;
|
|
1552
|
+
const generatedGuardrails = contract.guardrails.enabled
|
|
1553
|
+
? clipList(raw.guardrails, contract.guardrails.max_items, contract.guardrails.item_max_chars)
|
|
1554
|
+
: [];
|
|
1555
|
+
const language = contract.language_mode === "fixed"
|
|
1556
|
+
? contract.fixed_language
|
|
1557
|
+
: clipText(raw.language, 32);
|
|
1558
|
+
return {
|
|
1559
|
+
empathy_directive: contract.empathy_directive.enabled
|
|
1560
|
+
? clipText(raw.empathy_directive, contract.empathy_directive.max_chars)
|
|
1561
|
+
: "",
|
|
1562
|
+
tactical_directive: contract.tactical_directive.enabled
|
|
1563
|
+
? clipText(raw.tactical_directive, contract.tactical_directive.max_chars)
|
|
1564
|
+
: "",
|
|
1565
|
+
surface_facts: contract.surface_facts.enabled
|
|
1566
|
+
? clipList(raw.surface_facts, contract.surface_facts.max_items, contract.surface_facts.item_max_chars)
|
|
1567
|
+
: [],
|
|
1568
|
+
guardrails: [...new Set([...generatedGuardrails, ...operatorGuardrails])],
|
|
1569
|
+
language,
|
|
1570
|
+
confidence: contract.allowed_kinds.includes(kind) ? confidence : 0,
|
|
1571
|
+
kind,
|
|
1572
|
+
};
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1139
1575
|
export class SupafoneLabs {
|
|
1140
1576
|
readonly baseUrl: string;
|
|
1141
1577
|
readonly supafoneApiBaseUrl: string;
|
|
@@ -1266,6 +1702,31 @@ export class SupafoneLabs {
|
|
|
1266
1702
|
}
|
|
1267
1703
|
}
|
|
1268
1704
|
|
|
1705
|
+
/** @internal Authenticated binary request to the Supafone app API. */
|
|
1706
|
+
async requestSupafoneBinary(path: string): Promise<LabsVoicePreview> {
|
|
1707
|
+
const ctrl = new AbortController();
|
|
1708
|
+
const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
1709
|
+
try {
|
|
1710
|
+
const res = await fetch(this.supafoneApiBaseUrl + path, {
|
|
1711
|
+
method: "GET",
|
|
1712
|
+
signal: ctrl.signal,
|
|
1713
|
+
headers: { Authorization: `Bearer ${this.supafoneApiKey}` },
|
|
1714
|
+
});
|
|
1715
|
+
if (!res.ok) {
|
|
1716
|
+
const text = await res.text();
|
|
1717
|
+
const parsed = text ? safeJson(text) : {};
|
|
1718
|
+
const detail = (parsed as { detail?: string })?.detail ?? text ?? `HTTP ${res.status}`;
|
|
1719
|
+
throw new SupafoneLabsError(`GET ${path}: ${detail}`, res.status, parsed);
|
|
1720
|
+
}
|
|
1721
|
+
return {
|
|
1722
|
+
content: await res.arrayBuffer(),
|
|
1723
|
+
mediaType: res.headers.get("content-type") || "application/octet-stream",
|
|
1724
|
+
};
|
|
1725
|
+
} finally {
|
|
1726
|
+
clearTimeout(timer);
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1269
1730
|
/**
|
|
1270
1731
|
* Exchange the account email/password for a product-API JWT (the same login
|
|
1271
1732
|
* as app.supafone.ai). Called lazily by campaigns/calls — call directly only
|
|
@@ -1477,6 +1938,52 @@ export class SupafoneLabs {
|
|
|
1477
1938
|
return out.text.trim();
|
|
1478
1939
|
}
|
|
1479
1940
|
|
|
1941
|
+
/**
|
|
1942
|
+
* Structured SecondMind guidance with developer-controlled field policy.
|
|
1943
|
+
* Returns null when JSON is invalid, evidence misses the confidence gate,
|
|
1944
|
+
* the generated kind is disallowed, or the local transform suppresses it.
|
|
1945
|
+
*/
|
|
1946
|
+
async whisperStructured(
|
|
1947
|
+
transcript: string,
|
|
1948
|
+
opts: StructuredWhisperOptions = {},
|
|
1949
|
+
): Promise<SecondMindDirective | null> {
|
|
1950
|
+
const contract = normalizeDirectiveContract(
|
|
1951
|
+
opts.directive_contract ?? opts.directiveContract ?? {},
|
|
1952
|
+
);
|
|
1953
|
+
const operatorRules = [opts.guardrails, ...contract.operator_guardrails]
|
|
1954
|
+
.map((rule) => String(rule ?? "").trim())
|
|
1955
|
+
.filter(Boolean);
|
|
1956
|
+
const system = [
|
|
1957
|
+
STRUCTURED_COACH_SYSTEM,
|
|
1958
|
+
directiveContractPrompt(contract),
|
|
1959
|
+
operatorRules.length ? `Operator rules:\n${operatorRules.join("\n")}` : "",
|
|
1960
|
+
].filter(Boolean).join("\n\n");
|
|
1961
|
+
const out = await this.oracle({
|
|
1962
|
+
model: opts.model,
|
|
1963
|
+
maxTokens: opts.maxTokens ?? 320,
|
|
1964
|
+
...(opts.temperature !== undefined ? { temperature: opts.temperature } : {}),
|
|
1965
|
+
messages: [
|
|
1966
|
+
{ role: "system", content: system },
|
|
1967
|
+
{ role: "user", content: transcript },
|
|
1968
|
+
],
|
|
1969
|
+
});
|
|
1970
|
+
const parsed = parseJsonObject(out.text);
|
|
1971
|
+
let directive = parsed ? normalizeStructuredDirective(parsed, contract) : null;
|
|
1972
|
+
if (directive && opts.transform) {
|
|
1973
|
+
directive = await opts.transform(directive);
|
|
1974
|
+
}
|
|
1975
|
+
if (!directive || directive.confidence < contract.confidence_threshold) return null;
|
|
1976
|
+
return directive;
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
/** Alias that reads naturally in applications building their own watcher loop. */
|
|
1980
|
+
directive(
|
|
1981
|
+
transcript: string,
|
|
1982
|
+
opts: StructuredWhisperOptions = {},
|
|
1983
|
+
): Promise<SecondMindDirective | null> {
|
|
1984
|
+
return this.whisperStructured(transcript, opts);
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1480
1987
|
/** Hosted TTS — returns raw audio bytes (WAV/PCM per voice). */
|
|
1481
1988
|
async tts(text: string, voice = "supafone-labs-calm-en"): Promise<Uint8Array> {
|
|
1482
1989
|
const ctrl = new AbortController();
|
|
@@ -2147,6 +2654,8 @@ class LabsNamespace {
|
|
|
2147
2654
|
readonly phoneNumbers: LabsPhoneNumbersNamespace;
|
|
2148
2655
|
readonly telephony: LabsTelephonyNamespace;
|
|
2149
2656
|
readonly calls: LabsCallsNamespace;
|
|
2657
|
+
readonly activity: LabsActivityNamespace;
|
|
2658
|
+
readonly plans: LabsPlansNamespace;
|
|
2150
2659
|
readonly recordings: LabsRecordingsNamespace;
|
|
2151
2660
|
readonly transcripts: LabsTranscriptsNamespace;
|
|
2152
2661
|
|
|
@@ -2160,6 +2669,8 @@ class LabsNamespace {
|
|
|
2160
2669
|
this.phoneNumbers = new LabsPhoneNumbersNamespace(sm);
|
|
2161
2670
|
this.telephony = new LabsTelephonyNamespace(sm);
|
|
2162
2671
|
this.calls = new LabsCallsNamespace(sm);
|
|
2672
|
+
this.activity = new LabsActivityNamespace(sm);
|
|
2673
|
+
this.plans = new LabsPlansNamespace(sm);
|
|
2163
2674
|
this.recordings = new LabsRecordingsNamespace(sm);
|
|
2164
2675
|
this.transcripts = new LabsTranscriptsNamespace(sm);
|
|
2165
2676
|
}
|
|
@@ -2401,17 +2912,111 @@ class LabsToolsNamespace {
|
|
|
2401
2912
|
class LabsVoicesNamespace {
|
|
2402
2913
|
constructor(private sm: SupafoneLabs) {}
|
|
2403
2914
|
|
|
2404
|
-
/**
|
|
2915
|
+
/** Live normalized catalog from every connected/managed TTS provider. */
|
|
2405
2916
|
list(opts: LabsVoiceListOptions = {}): Promise<LabsVoiceListResponse> {
|
|
2406
2917
|
const q = new URLSearchParams();
|
|
2407
2918
|
if (opts.provider) q.set("provider", opts.provider);
|
|
2408
2919
|
if (opts.search) q.set("search", opts.search);
|
|
2409
2920
|
if (opts.language) q.set("language", opts.language);
|
|
2921
|
+
const compatibleLanguage = opts.compatible_language ?? opts.compatibleLanguage;
|
|
2922
|
+
if (compatibleLanguage) q.set("compatible_language", compatibleLanguage);
|
|
2923
|
+
if (opts.gender) q.set("gender", opts.gender);
|
|
2924
|
+
const voiceType = opts.voice_type ?? opts.voiceType;
|
|
2925
|
+
if (voiceType) q.set("voice_type", voiceType);
|
|
2926
|
+
if (opts.model) q.set("model", opts.model);
|
|
2927
|
+
const runtimeProvider = opts.runtime_provider ?? opts.runtimeProvider;
|
|
2928
|
+
if (runtimeProvider) q.set("runtime_provider", runtimeProvider);
|
|
2929
|
+
const configuredOnly = opts.configured_only ?? opts.configuredOnly;
|
|
2930
|
+
if (configuredOnly !== undefined) q.set("configured_only", String(configuredOnly));
|
|
2410
2931
|
if (opts.cursor !== undefined) q.set("cursor", String(opts.cursor));
|
|
2411
2932
|
if (opts.limit !== undefined) q.set("limit", String(opts.limit));
|
|
2933
|
+
const agencyId = opts.agency_id ?? opts.agencyId;
|
|
2934
|
+
if (agencyId) q.set("agency_id", agencyId);
|
|
2412
2935
|
const suffix = q.toString() ? `?${q}` : "";
|
|
2413
2936
|
return this.sm.requestSupafoneApi<LabsVoiceListResponse>("GET", `/api/v1/labs/voices${suffix}`);
|
|
2414
2937
|
}
|
|
2938
|
+
|
|
2939
|
+
/** Provider/model language limits and their Ultravox-compatible intersection. */
|
|
2940
|
+
capabilities(): Promise<LabsVoiceCapabilitiesResponse> {
|
|
2941
|
+
return this.sm.requestSupafoneApi<LabsVoiceCapabilitiesResponse>(
|
|
2942
|
+
"GET",
|
|
2943
|
+
"/api/v1/labs/voices/capabilities",
|
|
2944
|
+
);
|
|
2945
|
+
}
|
|
2946
|
+
|
|
2947
|
+
/** Page through the complete normalized catalog. */
|
|
2948
|
+
async listAll(
|
|
2949
|
+
opts: Omit<LabsVoiceListOptions, "cursor" | "limit"> & { pageSize?: number; maxPages?: number } = {},
|
|
2950
|
+
): Promise<LabsVoiceListResponse> {
|
|
2951
|
+
const pageSize = Math.max(1, Math.min(opts.pageSize ?? 250, 250));
|
|
2952
|
+
const maxPages = Math.max(1, opts.maxPages ?? 100);
|
|
2953
|
+
let cursor: number | null = 0;
|
|
2954
|
+
let pages = 0;
|
|
2955
|
+
let first: LabsVoiceListResponse | undefined;
|
|
2956
|
+
const voices: LabsVoiceCatalogItem[] = [];
|
|
2957
|
+
const seen = new Set<number>();
|
|
2958
|
+
while (cursor !== null && pages < maxPages) {
|
|
2959
|
+
if (seen.has(cursor)) throw new SupafoneLabsError("Voice catalog returned a repeated cursor");
|
|
2960
|
+
seen.add(cursor);
|
|
2961
|
+
const page = await this.list({ ...opts, cursor, limit: pageSize });
|
|
2962
|
+
first ??= page;
|
|
2963
|
+
voices.push(...page.voices);
|
|
2964
|
+
cursor = page.next_cursor ?? null;
|
|
2965
|
+
pages += 1;
|
|
2966
|
+
}
|
|
2967
|
+
if (cursor !== null) {
|
|
2968
|
+
throw new SupafoneLabsError(`Voice catalog exceeded maxPages=${maxPages}`);
|
|
2969
|
+
}
|
|
2970
|
+
return {
|
|
2971
|
+
...(first ?? { total: 0, providers: [] }),
|
|
2972
|
+
voices,
|
|
2973
|
+
total: first?.total ?? voices.length,
|
|
2974
|
+
cursor: 0,
|
|
2975
|
+
next_cursor: null,
|
|
2976
|
+
};
|
|
2977
|
+
}
|
|
2978
|
+
|
|
2979
|
+
/** Rank real current voices from a plain-language description. */
|
|
2980
|
+
recommend(opts: LabsVoiceRecommendOptions): Promise<LabsVoiceRecommendResponse> {
|
|
2981
|
+
if (!opts.description?.trim()) {
|
|
2982
|
+
throw new SupafoneLabsError("description is required — describe the voice you want");
|
|
2983
|
+
}
|
|
2984
|
+
return this.sm.requestSupafoneApi<LabsVoiceRecommendResponse>(
|
|
2985
|
+
"POST",
|
|
2986
|
+
"/api/v1/labs/voices/recommend",
|
|
2987
|
+
compact({
|
|
2988
|
+
agency_id: opts.agency_id ?? opts.agencyId,
|
|
2989
|
+
description: opts.description.trim(),
|
|
2990
|
+
language: opts.language,
|
|
2991
|
+
provider: opts.provider,
|
|
2992
|
+
gender: opts.gender,
|
|
2993
|
+
voice_type: opts.voice_type ?? opts.voiceType,
|
|
2994
|
+
model: opts.model,
|
|
2995
|
+
configured_only: opts.configured_only ?? opts.configuredOnly,
|
|
2996
|
+
premium: opts.premium,
|
|
2997
|
+
limit: opts.limit,
|
|
2998
|
+
}),
|
|
2999
|
+
);
|
|
3000
|
+
}
|
|
3001
|
+
|
|
3002
|
+
/** Download an authenticated voice preview. */
|
|
3003
|
+
preview(voiceId: string, opts: { agencyId?: string; agency_id?: string } = {}): Promise<LabsVoicePreview> {
|
|
3004
|
+
if (!voiceId.trim()) throw new SupafoneLabsError("voiceId is required");
|
|
3005
|
+
const q = new URLSearchParams({ voice: voiceId.trim() });
|
|
3006
|
+
const agencyId = opts.agency_id ?? opts.agencyId;
|
|
3007
|
+
if (agencyId) q.set("agency_id", agencyId);
|
|
3008
|
+
return this.sm.requestSupafoneBinary(`/api/v1/labs/voices/preview?${q}`);
|
|
3009
|
+
}
|
|
3010
|
+
|
|
3011
|
+
/** Convert a catalog row into the exact Agent Factory voice field. */
|
|
3012
|
+
selection(voice: LabsVoiceCatalogItem): LabsVoiceSelection {
|
|
3013
|
+
if (!voice?.id) throw new SupafoneLabsError("A catalog voice with id is required");
|
|
3014
|
+
return compact({
|
|
3015
|
+
provider: voice.provider_key || voice.source,
|
|
3016
|
+
voiceId: voice.id,
|
|
3017
|
+
model: voice.model || undefined,
|
|
3018
|
+
}) as LabsVoiceSelection;
|
|
3019
|
+
}
|
|
2415
3020
|
}
|
|
2416
3021
|
|
|
2417
3022
|
class LabsRuntimeNamespace {
|
|
@@ -2452,11 +3057,53 @@ class LabsCallsNamespace {
|
|
|
2452
3057
|
return this.sm.requestSupafoneApi<LabsCallListResponse>("GET", `/api/v1/labs/calls${suffix}`);
|
|
2453
3058
|
}
|
|
2454
3059
|
|
|
2455
|
-
get(callId: string, opts: { agencyId?: string } = {}): Promise<
|
|
3060
|
+
get(callId: string, opts: { agencyId?: string; agency_id?: string } = {}): Promise<{ call: LabsCallArtifact }> {
|
|
2456
3061
|
const q = new URLSearchParams();
|
|
2457
|
-
|
|
3062
|
+
const agencyId = opts.agency_id ?? opts.agencyId;
|
|
3063
|
+
if (agencyId) q.set("agency_id", agencyId);
|
|
3064
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
3065
|
+
return this.sm.requestSupafoneApi<{ call: LabsCallArtifact }>("GET", `/api/v1/labs/calls/${encodeURIComponent(callId)}${suffix}`);
|
|
3066
|
+
}
|
|
3067
|
+
|
|
3068
|
+
delete(callId: string, opts: { agencyId?: string; agency_id?: string } = {}): Promise<Record<string, unknown>> {
|
|
3069
|
+
const q = new URLSearchParams();
|
|
3070
|
+
const agencyId = opts.agency_id ?? opts.agencyId;
|
|
3071
|
+
if (agencyId) q.set("agency_id", agencyId);
|
|
2458
3072
|
const suffix = q.toString() ? `?${q}` : "";
|
|
2459
|
-
return this.sm.requestSupafoneApi("
|
|
3073
|
+
return this.sm.requestSupafoneApi("DELETE", `/api/v1/labs/calls/${encodeURIComponent(callId)}${suffix}`);
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
|
|
3077
|
+
class LabsActivityNamespace {
|
|
3078
|
+
constructor(private sm: SupafoneLabs) {}
|
|
3079
|
+
|
|
3080
|
+
list(opts: LabsActivityListOptions = {}): Promise<LabsActivityListResponse> {
|
|
3081
|
+
const q = new URLSearchParams();
|
|
3082
|
+
const accountId = opts.account_id ?? opts.accountId ?? opts.agency_id ?? opts.agencyId;
|
|
3083
|
+
if (accountId) q.set("account_id", accountId);
|
|
3084
|
+
const eventType = opts.event_type ?? opts.eventType;
|
|
3085
|
+
if (eventType) q.set("event_type", eventType);
|
|
3086
|
+
const resourceType = opts.resource_type ?? opts.resourceType;
|
|
3087
|
+
if (resourceType) q.set("resource_type", resourceType);
|
|
3088
|
+
const resourceId = opts.resource_id ?? opts.resourceId;
|
|
3089
|
+
if (resourceId) q.set("resource_id", resourceId);
|
|
3090
|
+
if (opts.limit !== undefined) q.set("limit", String(opts.limit));
|
|
3091
|
+
if (opts.offset !== undefined) q.set("offset", String(opts.offset));
|
|
3092
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
3093
|
+
return this.sm.requestSupafoneApi<LabsActivityListResponse>("GET", `/api/v1/labs/activity${suffix}`);
|
|
3094
|
+
}
|
|
3095
|
+
}
|
|
3096
|
+
|
|
3097
|
+
class LabsPlansNamespace {
|
|
3098
|
+
constructor(private sm: SupafoneLabs) {}
|
|
3099
|
+
|
|
3100
|
+
list(opts: Omit<LabsActivityListOptions, "eventType" | "event_type" | "resourceType" | "resource_type"> = {}): Promise<LabsActivityListResponse> {
|
|
3101
|
+
const activity = new LabsActivityNamespace(this.sm);
|
|
3102
|
+
return activity.list({
|
|
3103
|
+
...opts,
|
|
3104
|
+
eventType: "studio.plan.created",
|
|
3105
|
+
resourceType: "studio_plan",
|
|
3106
|
+
});
|
|
2460
3107
|
}
|
|
2461
3108
|
}
|
|
2462
3109
|
|
|
@@ -2820,6 +3467,7 @@ class OptimizerNamespace {
|
|
|
2820
3467
|
}
|
|
2821
3468
|
|
|
2822
3469
|
function labsAgentPayload(input: CreateLabsAgentRequest): Record<string, unknown> {
|
|
3470
|
+
const fixedLanguage = input.preferred_language ?? input.preferredLanguage ?? input.language;
|
|
2823
3471
|
return compact({
|
|
2824
3472
|
agency_id: input.agency_id ?? input.agencyId,
|
|
2825
3473
|
agent_key: input.agent_key ?? input.agentKey,
|
|
@@ -2845,8 +3493,12 @@ function labsAgentPayload(input: CreateLabsAgentRequest): Record<string, unknown
|
|
|
2845
3493
|
goal: input.goal,
|
|
2846
3494
|
greeting: input.greeting,
|
|
2847
3495
|
system_prompt: input.system_prompt ?? input.systemPrompt,
|
|
2848
|
-
language:
|
|
3496
|
+
language: fixedLanguage,
|
|
2849
3497
|
voice: input.voice ? voicePayload(input.voice) : undefined,
|
|
3498
|
+
voice_preference: voicePreferencePayload(
|
|
3499
|
+
input.voice_preference ?? input.voicePreference,
|
|
3500
|
+
fixedLanguage,
|
|
3501
|
+
),
|
|
2850
3502
|
provider_keys: input.provider_keys ?? (input.providerKeys ? providerKeysPayload(input.providerKeys) : undefined),
|
|
2851
3503
|
byok: input.byok ? byokPayload(input.byok) : undefined,
|
|
2852
3504
|
telephony: input.telephony ? telephonyPayload(input.telephony) : undefined,
|
|
@@ -2866,10 +3518,12 @@ function labsAgentPayload(input: CreateLabsAgentRequest): Record<string, unknown
|
|
|
2866
3518
|
|
|
2867
3519
|
function hostedListQuery(opts: LabsCallListOptions): URLSearchParams {
|
|
2868
3520
|
const q = new URLSearchParams();
|
|
2869
|
-
|
|
3521
|
+
const agencyId = opts.agency_id ?? opts.agencyId;
|
|
3522
|
+
if (agencyId) q.set("agency_id", agencyId);
|
|
2870
3523
|
const agentKey = opts.agent_key ?? opts.agentKey;
|
|
2871
3524
|
if (agentKey) q.set("agent_key", agentKey);
|
|
2872
3525
|
if (opts.limit !== undefined) q.set("limit", String(opts.limit));
|
|
3526
|
+
if (opts.offset !== undefined) q.set("offset", String(opts.offset));
|
|
2873
3527
|
return q;
|
|
2874
3528
|
}
|
|
2875
3529
|
|
|
@@ -3107,6 +3761,23 @@ function voicePayload(input: LabsVoiceSelection): Record<string, unknown> {
|
|
|
3107
3761
|
});
|
|
3108
3762
|
}
|
|
3109
3763
|
|
|
3764
|
+
function voicePreferencePayload(
|
|
3765
|
+
input?: LabsVoicePreference,
|
|
3766
|
+
defaultLanguage?: string,
|
|
3767
|
+
): Record<string, unknown> | undefined {
|
|
3768
|
+
if (!input) return undefined;
|
|
3769
|
+
return compact({
|
|
3770
|
+
description: input.description,
|
|
3771
|
+
language: input.language ?? defaultLanguage,
|
|
3772
|
+
provider: input.provider,
|
|
3773
|
+
gender: input.gender,
|
|
3774
|
+
voice_type: input.voice_type ?? input.voiceType,
|
|
3775
|
+
model: input.model,
|
|
3776
|
+
configured_only: input.configured_only ?? input.configuredOnly,
|
|
3777
|
+
premium: input.premium,
|
|
3778
|
+
});
|
|
3779
|
+
}
|
|
3780
|
+
|
|
3110
3781
|
function providerKeysPayload(input: LabsProviderKeys): Record<string, unknown> {
|
|
3111
3782
|
return compact({
|
|
3112
3783
|
ultravox: input.ultravox,
|