supafone-labs 0.4.10 → 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 +37 -0
- package/dist/cjs/index.d.ts +453 -10
- package/dist/cjs/index.js +469 -9
- package/dist/index.d.ts +453 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +469 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +914 -18
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;
|
|
@@ -144,14 +202,32 @@ export interface LabsCallStage {
|
|
|
144
202
|
key?: string;
|
|
145
203
|
id?: string;
|
|
146
204
|
name: string;
|
|
205
|
+
/** Plain-English job description used by the hosted call-plan generator. */
|
|
206
|
+
description?: string;
|
|
147
207
|
goal?: string;
|
|
148
208
|
instructions?: string;
|
|
149
209
|
exitCriteria?: string[];
|
|
150
210
|
exit_criteria?: string[];
|
|
151
211
|
tools?: string[];
|
|
212
|
+
temperature?: number;
|
|
213
|
+
nextStages?: string[];
|
|
214
|
+
next_stages?: string[];
|
|
152
215
|
metadata?: Record<string, unknown>;
|
|
153
216
|
}
|
|
154
217
|
|
|
218
|
+
export type LabsStageGeneration = "oracle" | "template" | "off";
|
|
219
|
+
|
|
220
|
+
export interface LabsCallPlan {
|
|
221
|
+
version: "supafone_call_plan_v1" | string;
|
|
222
|
+
summary: string;
|
|
223
|
+
base_system_prompt: string;
|
|
224
|
+
call_stages: LabsCallStage[];
|
|
225
|
+
generated_by: "supafone_hosted_haiku" | "deterministic_template" | "deterministic_fallback" | "developer" | string;
|
|
226
|
+
model: string;
|
|
227
|
+
fallback: boolean;
|
|
228
|
+
warnings?: string[];
|
|
229
|
+
}
|
|
230
|
+
|
|
155
231
|
export interface LabsVoiceSelection {
|
|
156
232
|
provider?: string;
|
|
157
233
|
voiceId?: string;
|
|
@@ -159,6 +235,115 @@ export interface LabsVoiceSelection {
|
|
|
159
235
|
model?: string;
|
|
160
236
|
}
|
|
161
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
|
+
|
|
162
347
|
export interface LabsProviderKeys {
|
|
163
348
|
/** Agent runtime/platform providers. */
|
|
164
349
|
ultravox?: string;
|
|
@@ -481,6 +666,8 @@ export interface CreateLabsAgentRequest {
|
|
|
481
666
|
agentStyle?: LabsAgentStyle;
|
|
482
667
|
agent_style?: LabsAgentStyle;
|
|
483
668
|
name: string;
|
|
669
|
+
/** Plain-language job description used by the hosted call-plan generator. */
|
|
670
|
+
description?: string;
|
|
484
671
|
assistantName?: string;
|
|
485
672
|
assistant_name?: string;
|
|
486
673
|
businessName?: string;
|
|
@@ -500,18 +687,30 @@ export interface CreateLabsAgentRequest {
|
|
|
500
687
|
preset_key?: string;
|
|
501
688
|
runtimeMode?: LabsRuntimeMode;
|
|
502
689
|
runtime_mode?: LabsRuntimeMode;
|
|
503
|
-
/**
|
|
504
|
-
callStages?: boolean | LabsCallStage[];
|
|
505
|
-
call_stages?: boolean | LabsCallStage[];
|
|
690
|
+
/** Defaults to Supafone's hosted planner; model credentials remain server-side. */
|
|
691
|
+
callStages?: boolean | LabsStageGeneration | LabsCallStage[];
|
|
692
|
+
call_stages?: boolean | LabsStageGeneration | LabsCallStage[];
|
|
506
693
|
stages?: LabsCallStage[];
|
|
507
694
|
autoCallStages?: boolean;
|
|
508
695
|
auto_call_stages?: boolean;
|
|
696
|
+
stageGeneration?: LabsStageGeneration;
|
|
697
|
+
stage_generation?: LabsStageGeneration;
|
|
698
|
+
stageCount?: number;
|
|
699
|
+
stage_count?: number;
|
|
700
|
+
stageDetail?: "compact" | "standard" | "detailed";
|
|
701
|
+
stage_detail?: "compact" | "standard" | "detailed";
|
|
509
702
|
goal?: string;
|
|
510
703
|
greeting?: string;
|
|
511
704
|
systemPrompt?: string;
|
|
512
705
|
system_prompt?: string;
|
|
706
|
+
/** Fixed language for the full call. Does not enable mid-call switching. */
|
|
513
707
|
language?: string;
|
|
708
|
+
preferredLanguage?: string;
|
|
709
|
+
preferred_language?: string;
|
|
514
710
|
voice?: LabsVoiceSelection;
|
|
711
|
+
/** Resolve a real current catalog voice from this plain-language preference. */
|
|
712
|
+
voicePreference?: LabsVoicePreference;
|
|
713
|
+
voice_preference?: LabsVoicePreference;
|
|
515
714
|
providerKeys?: LabsProviderKeys;
|
|
516
715
|
provider_keys?: LabsProviderKeys;
|
|
517
716
|
byok?: LabsProviderKeys | LabsByokConfig;
|
|
@@ -578,21 +777,125 @@ export interface LabsToolListResponse {
|
|
|
578
777
|
|
|
579
778
|
export interface LabsVoiceListOptions {
|
|
580
779
|
provider?: string;
|
|
780
|
+
search?: string;
|
|
781
|
+
/** Native/accent language advertised for the individual voice. */
|
|
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;
|
|
794
|
+
cursor?: number;
|
|
795
|
+
limit?: number;
|
|
796
|
+
agencyId?: string;
|
|
797
|
+
agency_id?: string;
|
|
581
798
|
}
|
|
582
799
|
|
|
583
800
|
export interface LabsVoiceListResponse {
|
|
584
|
-
|
|
801
|
+
account_id?: string;
|
|
802
|
+
voices: LabsVoiceCatalogItem[];
|
|
585
803
|
total: number;
|
|
586
|
-
|
|
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
|
+
}>;
|
|
587
813
|
provider_accounts?: Record<string, unknown>;
|
|
588
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;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
export interface LabsRuntimeResponse {
|
|
884
|
+
account_id: string;
|
|
885
|
+
provider: "ultravox" | string;
|
|
886
|
+
managed: boolean;
|
|
887
|
+
byok_connected: boolean;
|
|
888
|
+
base_url?: string;
|
|
889
|
+
updated_at?: string;
|
|
589
890
|
}
|
|
590
891
|
|
|
591
892
|
export interface LabsCallListOptions {
|
|
592
893
|
agencyId?: string;
|
|
894
|
+
agency_id?: string;
|
|
593
895
|
agentKey?: string;
|
|
594
896
|
agent_key?: string;
|
|
595
897
|
limit?: number;
|
|
898
|
+
offset?: number;
|
|
596
899
|
}
|
|
597
900
|
|
|
598
901
|
export interface LabsCallArtifact {
|
|
@@ -603,7 +906,11 @@ export interface LabsCallArtifact {
|
|
|
603
906
|
started_at?: string;
|
|
604
907
|
duration_seconds?: number;
|
|
605
908
|
recording_url?: string;
|
|
909
|
+
recording_download_url?: string;
|
|
910
|
+
recording_archived?: boolean;
|
|
606
911
|
transcript_url?: string;
|
|
912
|
+
watcher_events?: LabsDeveloperActivityEvent[];
|
|
913
|
+
watcher_event_count?: number;
|
|
607
914
|
[extra: string]: unknown;
|
|
608
915
|
}
|
|
609
916
|
|
|
@@ -632,6 +939,40 @@ export interface LabsTranscriptListResponse {
|
|
|
632
939
|
[extra: string]: unknown;
|
|
633
940
|
}
|
|
634
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
|
+
|
|
635
976
|
export interface LabsPhoneNumberSearchOptions {
|
|
636
977
|
agencyId?: string;
|
|
637
978
|
agency_id?: string;
|
|
@@ -718,6 +1059,8 @@ export interface LabsPhoneNumberProvisionRequest {
|
|
|
718
1059
|
numberPool?: string;
|
|
719
1060
|
number_pool?: string;
|
|
720
1061
|
premium?: boolean;
|
|
1062
|
+
billingCheckoutSessionId?: string;
|
|
1063
|
+
billing_checkout_session_id?: string;
|
|
721
1064
|
style?: LabsAgentStyle;
|
|
722
1065
|
agentStyle?: LabsAgentStyle;
|
|
723
1066
|
agent_style?: LabsAgentStyle;
|
|
@@ -779,7 +1122,7 @@ export interface CreateLabsAgentWithNumberRequest extends CreateLabsAgentRequest
|
|
|
779
1122
|
}
|
|
780
1123
|
|
|
781
1124
|
export interface CreateLabsAgentWithNumberResponse extends CreateLabsAgentResponse {
|
|
782
|
-
number?: LabsPhoneNumberProvisionResponse;
|
|
1125
|
+
number?: LabsPhoneNumberProvisionResponse | LabsBillingCheckoutResponse;
|
|
783
1126
|
}
|
|
784
1127
|
|
|
785
1128
|
export interface LabsAgentResponse {
|
|
@@ -805,6 +1148,7 @@ export interface CreateLabsAgentResponse {
|
|
|
805
1148
|
snippet?: string;
|
|
806
1149
|
[extra: string]: unknown;
|
|
807
1150
|
};
|
|
1151
|
+
call_plan?: LabsCallPlan;
|
|
808
1152
|
[extra: string]: unknown;
|
|
809
1153
|
}
|
|
810
1154
|
|
|
@@ -1094,6 +1438,140 @@ const COACH_SYSTEM =
|
|
|
1094
1438
|
"speaks aloud — a correction or nudge, phrased imperatively. If nothing needs " +
|
|
1095
1439
|
"correcting, return an empty string.";
|
|
1096
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
|
+
|
|
1097
1575
|
export class SupafoneLabs {
|
|
1098
1576
|
readonly baseUrl: string;
|
|
1099
1577
|
readonly supafoneApiBaseUrl: string;
|
|
@@ -1153,6 +1631,15 @@ export class SupafoneLabs {
|
|
|
1153
1631
|
this.campaigns = new CampaignsNamespace(this);
|
|
1154
1632
|
}
|
|
1155
1633
|
|
|
1634
|
+
/** Build a complete hosted plan with the same Supafone key used to create agents. */
|
|
1635
|
+
generateCallStages(input: CreateLabsAgentRequest & { description?: string }): Promise<LabsCallPlan> {
|
|
1636
|
+
return this.labs.agents.plan(input);
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
generate_call_stages(input: CreateLabsAgentRequest & { description?: string }): Promise<LabsCallPlan> {
|
|
1640
|
+
return this.generateCallStages(input);
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1156
1643
|
/** True once login() (or a passed sessionToken) is in effect. */
|
|
1157
1644
|
get isLoggedIn(): boolean {
|
|
1158
1645
|
return !!this.sessionToken;
|
|
@@ -1215,6 +1702,31 @@ export class SupafoneLabs {
|
|
|
1215
1702
|
}
|
|
1216
1703
|
}
|
|
1217
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
|
+
|
|
1218
1730
|
/**
|
|
1219
1731
|
* Exchange the account email/password for a product-API JWT (the same login
|
|
1220
1732
|
* as app.supafone.ai). Called lazily by campaigns/calls — call directly only
|
|
@@ -1426,6 +1938,52 @@ export class SupafoneLabs {
|
|
|
1426
1938
|
return out.text.trim();
|
|
1427
1939
|
}
|
|
1428
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
|
+
|
|
1429
1987
|
/** Hosted TTS — returns raw audio bytes (WAV/PCM per voice). */
|
|
1430
1988
|
async tts(text: string, voice = "supafone-labs-calm-en"): Promise<Uint8Array> {
|
|
1431
1989
|
const ctrl = new AbortController();
|
|
@@ -2088,23 +2646,31 @@ class CampaignsNamespace {
|
|
|
2088
2646
|
|
|
2089
2647
|
class LabsNamespace {
|
|
2090
2648
|
readonly agents: LabsAgentsNamespace;
|
|
2649
|
+
readonly billing: LabsBillingNamespace;
|
|
2091
2650
|
readonly presets: LabsPresetsNamespace;
|
|
2092
2651
|
readonly tools: LabsToolsNamespace;
|
|
2093
2652
|
readonly voices: LabsVoicesNamespace;
|
|
2653
|
+
readonly runtime: LabsRuntimeNamespace;
|
|
2094
2654
|
readonly phoneNumbers: LabsPhoneNumbersNamespace;
|
|
2095
2655
|
readonly telephony: LabsTelephonyNamespace;
|
|
2096
2656
|
readonly calls: LabsCallsNamespace;
|
|
2657
|
+
readonly activity: LabsActivityNamespace;
|
|
2658
|
+
readonly plans: LabsPlansNamespace;
|
|
2097
2659
|
readonly recordings: LabsRecordingsNamespace;
|
|
2098
2660
|
readonly transcripts: LabsTranscriptsNamespace;
|
|
2099
2661
|
|
|
2100
2662
|
constructor(private sm: SupafoneLabs) {
|
|
2101
2663
|
this.agents = new LabsAgentsNamespace(sm);
|
|
2664
|
+
this.billing = new LabsBillingNamespace(sm);
|
|
2102
2665
|
this.presets = new LabsPresetsNamespace(sm);
|
|
2103
2666
|
this.tools = new LabsToolsNamespace(sm);
|
|
2104
2667
|
this.voices = new LabsVoicesNamespace(sm);
|
|
2668
|
+
this.runtime = new LabsRuntimeNamespace(sm);
|
|
2105
2669
|
this.phoneNumbers = new LabsPhoneNumbersNamespace(sm);
|
|
2106
2670
|
this.telephony = new LabsTelephonyNamespace(sm);
|
|
2107
2671
|
this.calls = new LabsCallsNamespace(sm);
|
|
2672
|
+
this.activity = new LabsActivityNamespace(sm);
|
|
2673
|
+
this.plans = new LabsPlansNamespace(sm);
|
|
2108
2674
|
this.recordings = new LabsRecordingsNamespace(sm);
|
|
2109
2675
|
this.transcripts = new LabsTranscriptsNamespace(sm);
|
|
2110
2676
|
}
|
|
@@ -2115,6 +2681,73 @@ class LabsNamespace {
|
|
|
2115
2681
|
}
|
|
2116
2682
|
}
|
|
2117
2683
|
|
|
2684
|
+
export type LabsBillingCheckoutKind = "plan" | "credits" | "number_addon";
|
|
2685
|
+
|
|
2686
|
+
export interface LabsBillingCheckoutInput {
|
|
2687
|
+
kind?: LabsBillingCheckoutKind;
|
|
2688
|
+
planKey?: "developer" | "growth" | "scale" | string;
|
|
2689
|
+
plan_key?: string;
|
|
2690
|
+
numberStrategy?: "dedicated" | "premium" | string;
|
|
2691
|
+
number_strategy?: string;
|
|
2692
|
+
phoneNumber?: string;
|
|
2693
|
+
phone_number?: string;
|
|
2694
|
+
quantity?: number;
|
|
2695
|
+
successUrl?: string;
|
|
2696
|
+
success_url?: string;
|
|
2697
|
+
cancelUrl?: string;
|
|
2698
|
+
cancel_url?: string;
|
|
2699
|
+
}
|
|
2700
|
+
|
|
2701
|
+
export interface LabsBillingCheckoutResponse {
|
|
2702
|
+
status: "requires_payment" | "pending" | "paid" | string;
|
|
2703
|
+
checkout_session_id: string;
|
|
2704
|
+
checkout_url?: string;
|
|
2705
|
+
kind: LabsBillingCheckoutKind | string;
|
|
2706
|
+
plan_key?: string | null;
|
|
2707
|
+
number_strategy?: string | null;
|
|
2708
|
+
phone_number?: string | null;
|
|
2709
|
+
stripe_subscription_id?: string | null;
|
|
2710
|
+
ready_to_provision?: boolean;
|
|
2711
|
+
next_step?: string;
|
|
2712
|
+
}
|
|
2713
|
+
|
|
2714
|
+
class LabsBillingNamespace {
|
|
2715
|
+
constructor(private sm: SupafoneLabs) {}
|
|
2716
|
+
|
|
2717
|
+
/** Start hosted Stripe Checkout. MCP callers should render checkout_url as a link. */
|
|
2718
|
+
checkout(input: LabsBillingCheckoutInput = {}): Promise<LabsBillingCheckoutResponse> {
|
|
2719
|
+
return this.sm.request("POST", "/v1/billing/checkout", compact({
|
|
2720
|
+
kind: input.kind ?? "plan",
|
|
2721
|
+
plan_key: input.plan_key ?? input.planKey,
|
|
2722
|
+
number_strategy: input.number_strategy ?? input.numberStrategy,
|
|
2723
|
+
phone_number: input.phone_number ?? input.phoneNumber,
|
|
2724
|
+
quantity: input.quantity,
|
|
2725
|
+
success_url: input.success_url ?? input.successUrl,
|
|
2726
|
+
cancel_url: input.cancel_url ?? input.cancelUrl,
|
|
2727
|
+
}));
|
|
2728
|
+
}
|
|
2729
|
+
|
|
2730
|
+
status(checkoutSessionId: string): Promise<LabsBillingCheckoutResponse> {
|
|
2731
|
+
if (!checkoutSessionId?.trim()) throw new SupafoneLabsError("checkoutSessionId is required");
|
|
2732
|
+
return this.sm.request(
|
|
2733
|
+
"GET",
|
|
2734
|
+
`/v1/billing/checkout/${encodeURIComponent(checkoutSessionId)}`,
|
|
2735
|
+
);
|
|
2736
|
+
}
|
|
2737
|
+
|
|
2738
|
+
portal(): Promise<{ url: string }> {
|
|
2739
|
+
return this.sm.request("POST", "/v1/billing/portal", {});
|
|
2740
|
+
}
|
|
2741
|
+
|
|
2742
|
+
createCheckout(input: LabsBillingCheckoutInput = {}): Promise<LabsBillingCheckoutResponse> {
|
|
2743
|
+
return this.checkout(input);
|
|
2744
|
+
}
|
|
2745
|
+
|
|
2746
|
+
getCheckout(checkoutSessionId: string): Promise<LabsBillingCheckoutResponse> {
|
|
2747
|
+
return this.status(checkoutSessionId);
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
|
|
2118
2751
|
class LabsAgentsNamespace {
|
|
2119
2752
|
constructor(private sm: SupafoneLabs) {}
|
|
2120
2753
|
|
|
@@ -2127,6 +2760,19 @@ class LabsAgentsNamespace {
|
|
|
2127
2760
|
);
|
|
2128
2761
|
}
|
|
2129
2762
|
|
|
2763
|
+
/** Preview the exact validated call-plan contract the Supafone runtime executes. */
|
|
2764
|
+
plan(input: CreateLabsAgentRequest & { description?: string }): Promise<LabsCallPlan> {
|
|
2765
|
+
return this.sm.requestSupafoneApi<LabsCallPlan>(
|
|
2766
|
+
"POST",
|
|
2767
|
+
"/api/v1/labs/agent-plans",
|
|
2768
|
+
stagePlanPayload(input),
|
|
2769
|
+
);
|
|
2770
|
+
}
|
|
2771
|
+
|
|
2772
|
+
generateCallStages(input: CreateLabsAgentRequest & { description?: string }): Promise<LabsCallPlan> {
|
|
2773
|
+
return this.plan(input);
|
|
2774
|
+
}
|
|
2775
|
+
|
|
2130
2776
|
/** Default the agent onto the client's Voice Watcher setting (live supervision
|
|
2131
2777
|
* + QA + scoring) unless the caller set it explicitly; mirror into
|
|
2132
2778
|
* labs.voice_watcher when a labs block exists. Never overwrites a caller value. */
|
|
@@ -2266,13 +2912,140 @@ class LabsToolsNamespace {
|
|
|
2266
2912
|
class LabsVoicesNamespace {
|
|
2267
2913
|
constructor(private sm: SupafoneLabs) {}
|
|
2268
2914
|
|
|
2269
|
-
/**
|
|
2915
|
+
/** Live normalized catalog from every connected/managed TTS provider. */
|
|
2270
2916
|
list(opts: LabsVoiceListOptions = {}): Promise<LabsVoiceListResponse> {
|
|
2271
2917
|
const q = new URLSearchParams();
|
|
2272
2918
|
if (opts.provider) q.set("provider", opts.provider);
|
|
2919
|
+
if (opts.search) q.set("search", opts.search);
|
|
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));
|
|
2931
|
+
if (opts.cursor !== undefined) q.set("cursor", String(opts.cursor));
|
|
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);
|
|
2273
2935
|
const suffix = q.toString() ? `?${q}` : "";
|
|
2274
2936
|
return this.sm.requestSupafoneApi<LabsVoiceListResponse>("GET", `/api/v1/labs/voices${suffix}`);
|
|
2275
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
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
|
|
3022
|
+
class LabsRuntimeNamespace {
|
|
3023
|
+
constructor(private sm: SupafoneLabs) {}
|
|
3024
|
+
|
|
3025
|
+
get(opts: { agencyId?: string; agency_id?: string } = {}): Promise<LabsRuntimeResponse> {
|
|
3026
|
+
const q = new URLSearchParams();
|
|
3027
|
+
const agencyId = opts.agency_id ?? opts.agencyId;
|
|
3028
|
+
if (agencyId) q.set("agency_id", agencyId);
|
|
3029
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
3030
|
+
return this.sm.requestSupafoneApi<LabsRuntimeResponse>("GET", `/api/v1/labs/runtime${suffix}`);
|
|
3031
|
+
}
|
|
3032
|
+
|
|
3033
|
+
configure(input: {
|
|
3034
|
+
agencyId?: string;
|
|
3035
|
+
agency_id?: string;
|
|
3036
|
+
provider?: "ultravox" | string;
|
|
3037
|
+
credentials?: { apiKey?: string; api_key?: string; baseUrl?: string; base_url?: string };
|
|
3038
|
+
}): Promise<LabsRuntimeResponse> {
|
|
3039
|
+
const credentials = input.credentials ?? {};
|
|
3040
|
+
return this.sm.requestSupafoneApi<LabsRuntimeResponse>("PUT", "/api/v1/labs/runtime", compact({
|
|
3041
|
+
agency_id: input.agency_id ?? input.agencyId,
|
|
3042
|
+
provider: input.provider ?? "ultravox",
|
|
3043
|
+
credentials: compact({
|
|
3044
|
+
api_key: credentials.api_key ?? credentials.apiKey,
|
|
3045
|
+
base_url: credentials.base_url ?? credentials.baseUrl,
|
|
3046
|
+
}),
|
|
3047
|
+
}));
|
|
3048
|
+
}
|
|
2276
3049
|
}
|
|
2277
3050
|
|
|
2278
3051
|
class LabsCallsNamespace {
|
|
@@ -2284,11 +3057,53 @@ class LabsCallsNamespace {
|
|
|
2284
3057
|
return this.sm.requestSupafoneApi<LabsCallListResponse>("GET", `/api/v1/labs/calls${suffix}`);
|
|
2285
3058
|
}
|
|
2286
3059
|
|
|
2287
|
-
get(callId: string, opts: { agencyId?: string } = {}): Promise<
|
|
3060
|
+
get(callId: string, opts: { agencyId?: string; agency_id?: string } = {}): Promise<{ call: LabsCallArtifact }> {
|
|
2288
3061
|
const q = new URLSearchParams();
|
|
2289
|
-
|
|
3062
|
+
const agencyId = opts.agency_id ?? opts.agencyId;
|
|
3063
|
+
if (agencyId) q.set("agency_id", agencyId);
|
|
2290
3064
|
const suffix = q.toString() ? `?${q}` : "";
|
|
2291
|
-
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/calls/${encodeURIComponent(callId)}${suffix}`);
|
|
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);
|
|
3072
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
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
|
+
});
|
|
2292
3107
|
}
|
|
2293
3108
|
}
|
|
2294
3109
|
|
|
@@ -2362,13 +3177,32 @@ class LabsPhoneNumbersNamespace {
|
|
|
2362
3177
|
);
|
|
2363
3178
|
}
|
|
2364
3179
|
|
|
2365
|
-
/**
|
|
2366
|
-
|
|
3180
|
+
/**
|
|
3181
|
+
* Buy a managed number. Paid strategies return a hosted Checkout link first;
|
|
3182
|
+
* call again with billingCheckoutSessionId after Checkout reports paid.
|
|
3183
|
+
*/
|
|
3184
|
+
buy(
|
|
3185
|
+
input: LabsPhoneNumberProvisionRequest,
|
|
3186
|
+
): Promise<LabsPhoneNumberProvisionResponse | LabsBillingCheckoutResponse> {
|
|
3187
|
+
const strategy = input.number_strategy ?? input.numberStrategy ?? (input.premium ? "premium" : "default_pool");
|
|
3188
|
+
const checkoutSessionId = input.billing_checkout_session_id ?? input.billingCheckoutSessionId;
|
|
3189
|
+
if ((strategy === "dedicated" || strategy === "premium") && !checkoutSessionId) {
|
|
3190
|
+
const phoneNumber = input.phone_number ?? input.phoneNumber;
|
|
3191
|
+
if (!phoneNumber) {
|
|
3192
|
+
return Promise.reject(new SupafoneLabsError("phoneNumber is required before starting number Checkout"));
|
|
3193
|
+
}
|
|
3194
|
+
return this.sm.labs.billing.checkout({
|
|
3195
|
+
kind: "number_addon",
|
|
3196
|
+
numberStrategy: strategy,
|
|
3197
|
+
phoneNumber,
|
|
3198
|
+
});
|
|
3199
|
+
}
|
|
2367
3200
|
return this.sm.requestSupafoneApi<LabsPhoneNumberProvisionResponse>(
|
|
2368
3201
|
"POST",
|
|
2369
3202
|
"/api/v1/labs/phone-numbers",
|
|
2370
3203
|
phoneNumberProvisionPayload({
|
|
2371
3204
|
...input,
|
|
3205
|
+
numberStrategy: strategy,
|
|
2372
3206
|
telephony: input.telephony ?? { mode: "supafone_managed", provider: "supafone" },
|
|
2373
3207
|
}),
|
|
2374
3208
|
);
|
|
@@ -2423,7 +3257,9 @@ class LabsPhoneNumbersNamespace {
|
|
|
2423
3257
|
* Search if needed, buy the first matching Supafone-managed number, and assign
|
|
2424
3258
|
* it to the supplied agent. This is the zero-Twilio-account happy path.
|
|
2425
3259
|
*/
|
|
2426
|
-
async buyAndAssign(
|
|
3260
|
+
async buyAndAssign(
|
|
3261
|
+
input: LabsPhoneNumberBuyAndAssignRequest,
|
|
3262
|
+
): Promise<LabsPhoneNumberProvisionResponse | LabsBillingCheckoutResponse> {
|
|
2427
3263
|
let phoneNumber = input.phoneNumber ?? input.phone_number ?? "";
|
|
2428
3264
|
if (!phoneNumber) {
|
|
2429
3265
|
const found = await this.search({
|
|
@@ -2631,12 +3467,14 @@ class OptimizerNamespace {
|
|
|
2631
3467
|
}
|
|
2632
3468
|
|
|
2633
3469
|
function labsAgentPayload(input: CreateLabsAgentRequest): Record<string, unknown> {
|
|
3470
|
+
const fixedLanguage = input.preferred_language ?? input.preferredLanguage ?? input.language;
|
|
2634
3471
|
return compact({
|
|
2635
3472
|
agency_id: input.agency_id ?? input.agencyId,
|
|
2636
3473
|
agent_key: input.agent_key ?? input.agentKey,
|
|
2637
3474
|
agent_type: input.agent_type ?? input.agentType,
|
|
2638
3475
|
style: input.agent_style ?? input.agentStyle ?? input.style,
|
|
2639
3476
|
name: input.name,
|
|
3477
|
+
description: input.description,
|
|
2640
3478
|
assistant_name: input.assistant_name ?? input.assistantName,
|
|
2641
3479
|
business_name: input.business_name ?? input.businessName,
|
|
2642
3480
|
industry: input.industry,
|
|
@@ -2649,11 +3487,18 @@ function labsAgentPayload(input: CreateLabsAgentRequest): Record<string, unknown
|
|
|
2649
3487
|
preset_key: input.preset_key ?? input.presetKey,
|
|
2650
3488
|
runtime_mode: input.runtime_mode ?? input.runtimeMode,
|
|
2651
3489
|
call_stages: callStagesPayload(input),
|
|
3490
|
+
stage_generation: input.stage_generation ?? input.stageGeneration,
|
|
3491
|
+
stage_count: input.stage_count ?? input.stageCount,
|
|
3492
|
+
stage_detail: input.stage_detail ?? input.stageDetail,
|
|
2652
3493
|
goal: input.goal,
|
|
2653
3494
|
greeting: input.greeting,
|
|
2654
3495
|
system_prompt: input.system_prompt ?? input.systemPrompt,
|
|
2655
|
-
language:
|
|
3496
|
+
language: fixedLanguage,
|
|
2656
3497
|
voice: input.voice ? voicePayload(input.voice) : undefined,
|
|
3498
|
+
voice_preference: voicePreferencePayload(
|
|
3499
|
+
input.voice_preference ?? input.voicePreference,
|
|
3500
|
+
fixedLanguage,
|
|
3501
|
+
),
|
|
2657
3502
|
provider_keys: input.provider_keys ?? (input.providerKeys ? providerKeysPayload(input.providerKeys) : undefined),
|
|
2658
3503
|
byok: input.byok ? byokPayload(input.byok) : undefined,
|
|
2659
3504
|
telephony: input.telephony ? telephonyPayload(input.telephony) : undefined,
|
|
@@ -2673,10 +3518,12 @@ function labsAgentPayload(input: CreateLabsAgentRequest): Record<string, unknown
|
|
|
2673
3518
|
|
|
2674
3519
|
function hostedListQuery(opts: LabsCallListOptions): URLSearchParams {
|
|
2675
3520
|
const q = new URLSearchParams();
|
|
2676
|
-
|
|
3521
|
+
const agencyId = opts.agency_id ?? opts.agencyId;
|
|
3522
|
+
if (agencyId) q.set("agency_id", agencyId);
|
|
2677
3523
|
const agentKey = opts.agent_key ?? opts.agentKey;
|
|
2678
3524
|
if (agentKey) q.set("agent_key", agentKey);
|
|
2679
3525
|
if (opts.limit !== undefined) q.set("limit", String(opts.limit));
|
|
3526
|
+
if (opts.offset !== undefined) q.set("offset", String(opts.offset));
|
|
2680
3527
|
return q;
|
|
2681
3528
|
}
|
|
2682
3529
|
|
|
@@ -2752,6 +3599,7 @@ function phoneNumberProvisionPayload(input: LabsPhoneNumberProvisionRequest): Re
|
|
|
2752
3599
|
number_strategy: input.number_strategy ?? input.numberStrategy,
|
|
2753
3600
|
number_pool: input.number_pool ?? input.numberPool,
|
|
2754
3601
|
premium: input.premium,
|
|
3602
|
+
billing_checkout_session_id: input.billing_checkout_session_id ?? input.billingCheckoutSessionId,
|
|
2755
3603
|
style: input.agent_style ?? input.agentStyle ?? input.style,
|
|
2756
3604
|
direction: input.direction,
|
|
2757
3605
|
telephony: input.telephony ? telephonyPayload(input.telephony) : undefined,
|
|
@@ -2786,12 +3634,39 @@ function phoneNumberReleasePayload(input: LabsPhoneNumberReleaseRequest): Record
|
|
|
2786
3634
|
});
|
|
2787
3635
|
}
|
|
2788
3636
|
|
|
2789
|
-
function callStagesPayload(
|
|
3637
|
+
function callStagesPayload(
|
|
3638
|
+
input: CreateLabsAgentRequest,
|
|
3639
|
+
): Record<string, unknown>[] | LabsStageGeneration | false | undefined {
|
|
2790
3640
|
const explicit = input.call_stages ?? input.callStages ?? input.stages;
|
|
2791
3641
|
const auto = input.auto_call_stages ?? input.autoCallStages;
|
|
2792
3642
|
if (Array.isArray(explicit)) return explicit.map(callStagePayload);
|
|
2793
|
-
if (explicit === false || auto === false) return
|
|
2794
|
-
|
|
3643
|
+
if (explicit === false || auto === false) return false;
|
|
3644
|
+
if (explicit === "oracle" || explicit === "template" || explicit === "off") return explicit;
|
|
3645
|
+
// Omitted means the private Supafone API generates and compiles the plan.
|
|
3646
|
+
return undefined;
|
|
3647
|
+
}
|
|
3648
|
+
|
|
3649
|
+
function stagePlanPayload(
|
|
3650
|
+
input: CreateLabsAgentRequest & { description?: string },
|
|
3651
|
+
): Record<string, unknown> {
|
|
3652
|
+
return compact({
|
|
3653
|
+
agency_id: input.agency_id ?? input.agencyId,
|
|
3654
|
+
description: input.description,
|
|
3655
|
+
name: input.name,
|
|
3656
|
+
assistant_name: input.assistant_name ?? input.assistantName,
|
|
3657
|
+
business_name: input.business_name ?? input.businessName,
|
|
3658
|
+
industry: input.industry,
|
|
3659
|
+
direction: input.direction ?? input.agent_style ?? input.agentStyle ?? input.style,
|
|
3660
|
+
goal: input.goal,
|
|
3661
|
+
system_prompt: input.system_prompt ?? input.systemPrompt,
|
|
3662
|
+
tools: input.tools ? toolsPayload(input.tools) : undefined,
|
|
3663
|
+
call_stages: Array.isArray(input.call_stages ?? input.callStages ?? input.stages)
|
|
3664
|
+
? (input.call_stages ?? input.callStages ?? input.stages)
|
|
3665
|
+
: undefined,
|
|
3666
|
+
stage_generation: input.stage_generation ?? input.stageGeneration,
|
|
3667
|
+
stage_count: input.stage_count ?? input.stageCount,
|
|
3668
|
+
stage_detail: input.stage_detail ?? input.stageDetail,
|
|
3669
|
+
});
|
|
2795
3670
|
}
|
|
2796
3671
|
|
|
2797
3672
|
function callStagePayload(input: LabsCallStage): Record<string, unknown> {
|
|
@@ -2802,11 +3677,15 @@ function callStagePayload(input: LabsCallStage): Record<string, unknown> {
|
|
|
2802
3677
|
instructions: input.instructions,
|
|
2803
3678
|
exit_criteria: input.exit_criteria ?? input.exitCriteria,
|
|
2804
3679
|
tools: input.tools,
|
|
3680
|
+
temperature: input.temperature,
|
|
3681
|
+
next_stages: input.next_stages ?? input.nextStages,
|
|
2805
3682
|
metadata: input.metadata,
|
|
2806
3683
|
});
|
|
2807
3684
|
}
|
|
2808
3685
|
|
|
2809
3686
|
export function generateCallStages(input: CreateLabsAgentRequest): LabsCallStage[] {
|
|
3687
|
+
// Offline compatibility template. New code should use
|
|
3688
|
+
// client.generateCallStages(...) for the hosted, executable plan.
|
|
2810
3689
|
const direction = String(input.direction ?? input.agent_style ?? input.agentStyle ?? input.style ?? "inbound").toLowerCase();
|
|
2811
3690
|
const haystack = [
|
|
2812
3691
|
input.name,
|
|
@@ -2882,6 +3761,23 @@ function voicePayload(input: LabsVoiceSelection): Record<string, unknown> {
|
|
|
2882
3761
|
});
|
|
2883
3762
|
}
|
|
2884
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
|
+
|
|
2885
3781
|
function providerKeysPayload(input: LabsProviderKeys): Record<string, unknown> {
|
|
2886
3782
|
return compact({
|
|
2887
3783
|
ultravox: input.ultravox,
|