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/dist/index.js
CHANGED
|
@@ -35,6 +35,108 @@ const COACH_SYSTEM = "You are the coaching core of a second mind for a live voic
|
|
|
35
35
|
"conversation and return ONE short, silent directive the agent reads but never " +
|
|
36
36
|
"speaks aloud — a correction or nudge, phrased imperatively. If nothing needs " +
|
|
37
37
|
"correcting, return an empty string.";
|
|
38
|
+
const STRUCTURED_COACH_SYSTEM = "You are the coaching core of a second mind for a live voice agent. Read the " +
|
|
39
|
+
"conversation and return ONLY one JSON object with exactly these keys: " +
|
|
40
|
+
"empathy_directive (string), tactical_directive (string), surface_facts (string[]), " +
|
|
41
|
+
"guardrails (string[]), language (short code), confidence (0-1), and kind " +
|
|
42
|
+
"(empathy, tactical, guardrail, or mixed). Be silent with low confidence when evidence is weak.";
|
|
43
|
+
function boundedInteger(value, fallback, minimum, maximum) {
|
|
44
|
+
const parsed = Number(value);
|
|
45
|
+
return Number.isFinite(parsed)
|
|
46
|
+
? Math.max(minimum, Math.min(maximum, Math.trunc(parsed)))
|
|
47
|
+
: fallback;
|
|
48
|
+
}
|
|
49
|
+
function textControl(input) {
|
|
50
|
+
return {
|
|
51
|
+
enabled: input?.enabled ?? true,
|
|
52
|
+
instructions: String(input?.instructions ?? "").trim(),
|
|
53
|
+
max_chars: boundedInteger(input?.max_chars ?? input?.maxChars, 240, 1, 2000),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function listControl(input) {
|
|
57
|
+
return {
|
|
58
|
+
enabled: input?.enabled ?? true,
|
|
59
|
+
instructions: String(input?.instructions ?? "").trim(),
|
|
60
|
+
max_items: boundedInteger(input?.max_items ?? input?.maxItems, 4, 0, 20),
|
|
61
|
+
item_max_chars: boundedInteger(input?.item_max_chars ?? input?.itemMaxChars, 180, 1, 2000),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function normalizeDirectiveContract(input = {}) {
|
|
65
|
+
const languageMode = input.language_mode ?? input.languageMode ?? "caller";
|
|
66
|
+
const allowed = input.allowed_kinds ?? input.allowedKinds ?? ["empathy", "tactical", "guardrail", "mixed"];
|
|
67
|
+
const threshold = Number(input.confidence_threshold ?? input.confidenceThreshold ?? 0.5);
|
|
68
|
+
return {
|
|
69
|
+
empathy_directive: textControl(input.empathy_directive ?? input.empathyDirective),
|
|
70
|
+
tactical_directive: textControl(input.tactical_directive ?? input.tacticalDirective),
|
|
71
|
+
surface_facts: listControl(input.surface_facts ?? input.surfaceFacts),
|
|
72
|
+
guardrails: listControl(input.guardrails),
|
|
73
|
+
language_mode: languageMode,
|
|
74
|
+
fixed_language: String(input.fixed_language ?? input.fixedLanguage ?? "").trim(),
|
|
75
|
+
allowed_kinds: allowed.filter((kind) => ["empathy", "tactical", "guardrail", "mixed"].includes(kind)),
|
|
76
|
+
confidence_threshold: Number.isFinite(threshold) ? Math.max(0, Math.min(1, threshold)) : 0.5,
|
|
77
|
+
operator_guardrails: (input.operator_guardrails ?? input.operatorGuardrails ?? [])
|
|
78
|
+
.map((rule) => String(rule).trim())
|
|
79
|
+
.filter(Boolean)
|
|
80
|
+
.slice(0, 50),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function directiveContractPrompt(contract) {
|
|
84
|
+
return ("Developer directive contract (obey exactly):\n" +
|
|
85
|
+
JSON.stringify(contract, null, 2) +
|
|
86
|
+
"\nDisabled string fields must be empty strings. Disabled list fields must be empty arrays. " +
|
|
87
|
+
"Standing operator guardrails are mandatory and cannot be removed.");
|
|
88
|
+
}
|
|
89
|
+
function parseJsonObject(text) {
|
|
90
|
+
const trimmed = String(text ?? "").trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "");
|
|
91
|
+
const start = trimmed.indexOf("{");
|
|
92
|
+
const end = trimmed.lastIndexOf("}");
|
|
93
|
+
if (start < 0 || end <= start)
|
|
94
|
+
return null;
|
|
95
|
+
try {
|
|
96
|
+
const parsed = JSON.parse(trimmed.slice(start, end + 1));
|
|
97
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function clipText(value, limit) {
|
|
104
|
+
return String(value ?? "").trim().slice(0, limit);
|
|
105
|
+
}
|
|
106
|
+
function clipList(value, maxItems, itemMaxChars) {
|
|
107
|
+
if (!Array.isArray(value))
|
|
108
|
+
return [];
|
|
109
|
+
return value.slice(0, maxItems).map((item) => clipText(item, itemMaxChars)).filter(Boolean);
|
|
110
|
+
}
|
|
111
|
+
function normalizeStructuredDirective(raw, contract) {
|
|
112
|
+
const kind = String(raw.kind ?? "mixed");
|
|
113
|
+
if (!["empathy", "tactical", "guardrail", "mixed"].includes(kind))
|
|
114
|
+
return null;
|
|
115
|
+
const parsedConfidence = Number(raw.confidence ?? 0);
|
|
116
|
+
const confidence = Number.isFinite(parsedConfidence) ? Math.max(0, Math.min(1, parsedConfidence)) : 0;
|
|
117
|
+
const operatorGuardrails = contract.operator_guardrails;
|
|
118
|
+
const generatedGuardrails = contract.guardrails.enabled
|
|
119
|
+
? clipList(raw.guardrails, contract.guardrails.max_items, contract.guardrails.item_max_chars)
|
|
120
|
+
: [];
|
|
121
|
+
const language = contract.language_mode === "fixed"
|
|
122
|
+
? contract.fixed_language
|
|
123
|
+
: clipText(raw.language, 32);
|
|
124
|
+
return {
|
|
125
|
+
empathy_directive: contract.empathy_directive.enabled
|
|
126
|
+
? clipText(raw.empathy_directive, contract.empathy_directive.max_chars)
|
|
127
|
+
: "",
|
|
128
|
+
tactical_directive: contract.tactical_directive.enabled
|
|
129
|
+
? clipText(raw.tactical_directive, contract.tactical_directive.max_chars)
|
|
130
|
+
: "",
|
|
131
|
+
surface_facts: contract.surface_facts.enabled
|
|
132
|
+
? clipList(raw.surface_facts, contract.surface_facts.max_items, contract.surface_facts.item_max_chars)
|
|
133
|
+
: [],
|
|
134
|
+
guardrails: [...new Set([...generatedGuardrails, ...operatorGuardrails])],
|
|
135
|
+
language,
|
|
136
|
+
confidence: contract.allowed_kinds.includes(kind) ? confidence : 0,
|
|
137
|
+
kind,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
38
140
|
export class SupafoneLabs {
|
|
39
141
|
baseUrl;
|
|
40
142
|
supafoneApiBaseUrl;
|
|
@@ -157,6 +259,31 @@ export class SupafoneLabs {
|
|
|
157
259
|
clearTimeout(timer);
|
|
158
260
|
}
|
|
159
261
|
}
|
|
262
|
+
/** @internal Authenticated binary request to the Supafone app API. */
|
|
263
|
+
async requestSupafoneBinary(path) {
|
|
264
|
+
const ctrl = new AbortController();
|
|
265
|
+
const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
266
|
+
try {
|
|
267
|
+
const res = await fetch(this.supafoneApiBaseUrl + path, {
|
|
268
|
+
method: "GET",
|
|
269
|
+
signal: ctrl.signal,
|
|
270
|
+
headers: { Authorization: `Bearer ${this.supafoneApiKey}` },
|
|
271
|
+
});
|
|
272
|
+
if (!res.ok) {
|
|
273
|
+
const text = await res.text();
|
|
274
|
+
const parsed = text ? safeJson(text) : {};
|
|
275
|
+
const detail = parsed?.detail ?? text ?? `HTTP ${res.status}`;
|
|
276
|
+
throw new SupafoneLabsError(`GET ${path}: ${detail}`, res.status, parsed);
|
|
277
|
+
}
|
|
278
|
+
return {
|
|
279
|
+
content: await res.arrayBuffer(),
|
|
280
|
+
mediaType: res.headers.get("content-type") || "application/octet-stream",
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
finally {
|
|
284
|
+
clearTimeout(timer);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
160
287
|
/**
|
|
161
288
|
* Exchange the account email/password for a product-API JWT (the same login
|
|
162
289
|
* as app.supafone.ai). Called lazily by campaigns/calls — call directly only
|
|
@@ -345,6 +472,43 @@ export class SupafoneLabs {
|
|
|
345
472
|
});
|
|
346
473
|
return out.text.trim();
|
|
347
474
|
}
|
|
475
|
+
/**
|
|
476
|
+
* Structured SecondMind guidance with developer-controlled field policy.
|
|
477
|
+
* Returns null when JSON is invalid, evidence misses the confidence gate,
|
|
478
|
+
* the generated kind is disallowed, or the local transform suppresses it.
|
|
479
|
+
*/
|
|
480
|
+
async whisperStructured(transcript, opts = {}) {
|
|
481
|
+
const contract = normalizeDirectiveContract(opts.directive_contract ?? opts.directiveContract ?? {});
|
|
482
|
+
const operatorRules = [opts.guardrails, ...contract.operator_guardrails]
|
|
483
|
+
.map((rule) => String(rule ?? "").trim())
|
|
484
|
+
.filter(Boolean);
|
|
485
|
+
const system = [
|
|
486
|
+
STRUCTURED_COACH_SYSTEM,
|
|
487
|
+
directiveContractPrompt(contract),
|
|
488
|
+
operatorRules.length ? `Operator rules:\n${operatorRules.join("\n")}` : "",
|
|
489
|
+
].filter(Boolean).join("\n\n");
|
|
490
|
+
const out = await this.oracle({
|
|
491
|
+
model: opts.model,
|
|
492
|
+
maxTokens: opts.maxTokens ?? 320,
|
|
493
|
+
...(opts.temperature !== undefined ? { temperature: opts.temperature } : {}),
|
|
494
|
+
messages: [
|
|
495
|
+
{ role: "system", content: system },
|
|
496
|
+
{ role: "user", content: transcript },
|
|
497
|
+
],
|
|
498
|
+
});
|
|
499
|
+
const parsed = parseJsonObject(out.text);
|
|
500
|
+
let directive = parsed ? normalizeStructuredDirective(parsed, contract) : null;
|
|
501
|
+
if (directive && opts.transform) {
|
|
502
|
+
directive = await opts.transform(directive);
|
|
503
|
+
}
|
|
504
|
+
if (!directive || directive.confidence < contract.confidence_threshold)
|
|
505
|
+
return null;
|
|
506
|
+
return directive;
|
|
507
|
+
}
|
|
508
|
+
/** Alias that reads naturally in applications building their own watcher loop. */
|
|
509
|
+
directive(transcript, opts = {}) {
|
|
510
|
+
return this.whisperStructured(transcript, opts);
|
|
511
|
+
}
|
|
348
512
|
/** Hosted TTS — returns raw audio bytes (WAV/PCM per voice). */
|
|
349
513
|
async tts(text, voice = "supafone-labs-calm-en") {
|
|
350
514
|
const ctrl = new AbortController();
|
|
@@ -781,6 +945,8 @@ class LabsNamespace {
|
|
|
781
945
|
phoneNumbers;
|
|
782
946
|
telephony;
|
|
783
947
|
calls;
|
|
948
|
+
activity;
|
|
949
|
+
plans;
|
|
784
950
|
recordings;
|
|
785
951
|
transcripts;
|
|
786
952
|
constructor(sm) {
|
|
@@ -794,6 +960,8 @@ class LabsNamespace {
|
|
|
794
960
|
this.phoneNumbers = new LabsPhoneNumbersNamespace(sm);
|
|
795
961
|
this.telephony = new LabsTelephonyNamespace(sm);
|
|
796
962
|
this.calls = new LabsCallsNamespace(sm);
|
|
963
|
+
this.activity = new LabsActivityNamespace(sm);
|
|
964
|
+
this.plans = new LabsPlansNamespace(sm);
|
|
797
965
|
this.recordings = new LabsRecordingsNamespace(sm);
|
|
798
966
|
this.transcripts = new LabsTranscriptsNamespace(sm);
|
|
799
967
|
}
|
|
@@ -982,7 +1150,7 @@ class LabsVoicesNamespace {
|
|
|
982
1150
|
constructor(sm) {
|
|
983
1151
|
this.sm = sm;
|
|
984
1152
|
}
|
|
985
|
-
/**
|
|
1153
|
+
/** Live normalized catalog from every connected/managed TTS provider. */
|
|
986
1154
|
list(opts = {}) {
|
|
987
1155
|
const q = new URLSearchParams();
|
|
988
1156
|
if (opts.provider)
|
|
@@ -991,13 +1159,104 @@ class LabsVoicesNamespace {
|
|
|
991
1159
|
q.set("search", opts.search);
|
|
992
1160
|
if (opts.language)
|
|
993
1161
|
q.set("language", opts.language);
|
|
1162
|
+
const compatibleLanguage = opts.compatible_language ?? opts.compatibleLanguage;
|
|
1163
|
+
if (compatibleLanguage)
|
|
1164
|
+
q.set("compatible_language", compatibleLanguage);
|
|
1165
|
+
if (opts.gender)
|
|
1166
|
+
q.set("gender", opts.gender);
|
|
1167
|
+
const voiceType = opts.voice_type ?? opts.voiceType;
|
|
1168
|
+
if (voiceType)
|
|
1169
|
+
q.set("voice_type", voiceType);
|
|
1170
|
+
if (opts.model)
|
|
1171
|
+
q.set("model", opts.model);
|
|
1172
|
+
const runtimeProvider = opts.runtime_provider ?? opts.runtimeProvider;
|
|
1173
|
+
if (runtimeProvider)
|
|
1174
|
+
q.set("runtime_provider", runtimeProvider);
|
|
1175
|
+
const configuredOnly = opts.configured_only ?? opts.configuredOnly;
|
|
1176
|
+
if (configuredOnly !== undefined)
|
|
1177
|
+
q.set("configured_only", String(configuredOnly));
|
|
994
1178
|
if (opts.cursor !== undefined)
|
|
995
1179
|
q.set("cursor", String(opts.cursor));
|
|
996
1180
|
if (opts.limit !== undefined)
|
|
997
1181
|
q.set("limit", String(opts.limit));
|
|
1182
|
+
const agencyId = opts.agency_id ?? opts.agencyId;
|
|
1183
|
+
if (agencyId)
|
|
1184
|
+
q.set("agency_id", agencyId);
|
|
998
1185
|
const suffix = q.toString() ? `?${q}` : "";
|
|
999
1186
|
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/voices${suffix}`);
|
|
1000
1187
|
}
|
|
1188
|
+
/** Provider/model language limits and their Ultravox-compatible intersection. */
|
|
1189
|
+
capabilities() {
|
|
1190
|
+
return this.sm.requestSupafoneApi("GET", "/api/v1/labs/voices/capabilities");
|
|
1191
|
+
}
|
|
1192
|
+
/** Page through the complete normalized catalog. */
|
|
1193
|
+
async listAll(opts = {}) {
|
|
1194
|
+
const pageSize = Math.max(1, Math.min(opts.pageSize ?? 250, 250));
|
|
1195
|
+
const maxPages = Math.max(1, opts.maxPages ?? 100);
|
|
1196
|
+
let cursor = 0;
|
|
1197
|
+
let pages = 0;
|
|
1198
|
+
let first;
|
|
1199
|
+
const voices = [];
|
|
1200
|
+
const seen = new Set();
|
|
1201
|
+
while (cursor !== null && pages < maxPages) {
|
|
1202
|
+
if (seen.has(cursor))
|
|
1203
|
+
throw new SupafoneLabsError("Voice catalog returned a repeated cursor");
|
|
1204
|
+
seen.add(cursor);
|
|
1205
|
+
const page = await this.list({ ...opts, cursor, limit: pageSize });
|
|
1206
|
+
first ??= page;
|
|
1207
|
+
voices.push(...page.voices);
|
|
1208
|
+
cursor = page.next_cursor ?? null;
|
|
1209
|
+
pages += 1;
|
|
1210
|
+
}
|
|
1211
|
+
if (cursor !== null) {
|
|
1212
|
+
throw new SupafoneLabsError(`Voice catalog exceeded maxPages=${maxPages}`);
|
|
1213
|
+
}
|
|
1214
|
+
return {
|
|
1215
|
+
...(first ?? { total: 0, providers: [] }),
|
|
1216
|
+
voices,
|
|
1217
|
+
total: first?.total ?? voices.length,
|
|
1218
|
+
cursor: 0,
|
|
1219
|
+
next_cursor: null,
|
|
1220
|
+
};
|
|
1221
|
+
}
|
|
1222
|
+
/** Rank real current voices from a plain-language description. */
|
|
1223
|
+
recommend(opts) {
|
|
1224
|
+
if (!opts.description?.trim()) {
|
|
1225
|
+
throw new SupafoneLabsError("description is required — describe the voice you want");
|
|
1226
|
+
}
|
|
1227
|
+
return this.sm.requestSupafoneApi("POST", "/api/v1/labs/voices/recommend", compact({
|
|
1228
|
+
agency_id: opts.agency_id ?? opts.agencyId,
|
|
1229
|
+
description: opts.description.trim(),
|
|
1230
|
+
language: opts.language,
|
|
1231
|
+
provider: opts.provider,
|
|
1232
|
+
gender: opts.gender,
|
|
1233
|
+
voice_type: opts.voice_type ?? opts.voiceType,
|
|
1234
|
+
model: opts.model,
|
|
1235
|
+
configured_only: opts.configured_only ?? opts.configuredOnly,
|
|
1236
|
+
premium: opts.premium,
|
|
1237
|
+
limit: opts.limit,
|
|
1238
|
+
}));
|
|
1239
|
+
}
|
|
1240
|
+
/** Download an authenticated voice preview. */
|
|
1241
|
+
preview(voiceId, opts = {}) {
|
|
1242
|
+
if (!voiceId.trim())
|
|
1243
|
+
throw new SupafoneLabsError("voiceId is required");
|
|
1244
|
+
const q = new URLSearchParams({ voice: voiceId.trim() });
|
|
1245
|
+
const agencyId = opts.agency_id ?? opts.agencyId;
|
|
1246
|
+
if (agencyId)
|
|
1247
|
+
q.set("agency_id", agencyId);
|
|
1248
|
+
return this.sm.requestSupafoneBinary(`/api/v1/labs/voices/preview?${q}`);
|
|
1249
|
+
}
|
|
1250
|
+
/** Convert a catalog row into the exact Agent Factory voice field. */
|
|
1251
|
+
selection(voice) {
|
|
1252
|
+
if (!voice?.id)
|
|
1253
|
+
throw new SupafoneLabsError("A catalog voice with id is required");
|
|
1254
|
+
return compact({
|
|
1255
|
+
provider: voice.provider_key || voice.source,
|
|
1256
|
+
voiceId: voice.id,
|
|
1257
|
+
model: voice.model || undefined,
|
|
1258
|
+
});
|
|
1259
|
+
}
|
|
1001
1260
|
}
|
|
1002
1261
|
class LabsRuntimeNamespace {
|
|
1003
1262
|
sm;
|
|
@@ -1036,11 +1295,61 @@ class LabsCallsNamespace {
|
|
|
1036
1295
|
}
|
|
1037
1296
|
get(callId, opts = {}) {
|
|
1038
1297
|
const q = new URLSearchParams();
|
|
1039
|
-
|
|
1040
|
-
|
|
1298
|
+
const agencyId = opts.agency_id ?? opts.agencyId;
|
|
1299
|
+
if (agencyId)
|
|
1300
|
+
q.set("agency_id", agencyId);
|
|
1041
1301
|
const suffix = q.toString() ? `?${q}` : "";
|
|
1042
1302
|
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/calls/${encodeURIComponent(callId)}${suffix}`);
|
|
1043
1303
|
}
|
|
1304
|
+
delete(callId, opts = {}) {
|
|
1305
|
+
const q = new URLSearchParams();
|
|
1306
|
+
const agencyId = opts.agency_id ?? opts.agencyId;
|
|
1307
|
+
if (agencyId)
|
|
1308
|
+
q.set("agency_id", agencyId);
|
|
1309
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
1310
|
+
return this.sm.requestSupafoneApi("DELETE", `/api/v1/labs/calls/${encodeURIComponent(callId)}${suffix}`);
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
class LabsActivityNamespace {
|
|
1314
|
+
sm;
|
|
1315
|
+
constructor(sm) {
|
|
1316
|
+
this.sm = sm;
|
|
1317
|
+
}
|
|
1318
|
+
list(opts = {}) {
|
|
1319
|
+
const q = new URLSearchParams();
|
|
1320
|
+
const accountId = opts.account_id ?? opts.accountId ?? opts.agency_id ?? opts.agencyId;
|
|
1321
|
+
if (accountId)
|
|
1322
|
+
q.set("account_id", accountId);
|
|
1323
|
+
const eventType = opts.event_type ?? opts.eventType;
|
|
1324
|
+
if (eventType)
|
|
1325
|
+
q.set("event_type", eventType);
|
|
1326
|
+
const resourceType = opts.resource_type ?? opts.resourceType;
|
|
1327
|
+
if (resourceType)
|
|
1328
|
+
q.set("resource_type", resourceType);
|
|
1329
|
+
const resourceId = opts.resource_id ?? opts.resourceId;
|
|
1330
|
+
if (resourceId)
|
|
1331
|
+
q.set("resource_id", resourceId);
|
|
1332
|
+
if (opts.limit !== undefined)
|
|
1333
|
+
q.set("limit", String(opts.limit));
|
|
1334
|
+
if (opts.offset !== undefined)
|
|
1335
|
+
q.set("offset", String(opts.offset));
|
|
1336
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
1337
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/activity${suffix}`);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
class LabsPlansNamespace {
|
|
1341
|
+
sm;
|
|
1342
|
+
constructor(sm) {
|
|
1343
|
+
this.sm = sm;
|
|
1344
|
+
}
|
|
1345
|
+
list(opts = {}) {
|
|
1346
|
+
const activity = new LabsActivityNamespace(this.sm);
|
|
1347
|
+
return activity.list({
|
|
1348
|
+
...opts,
|
|
1349
|
+
eventType: "studio.plan.created",
|
|
1350
|
+
resourceType: "studio_plan",
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1044
1353
|
}
|
|
1045
1354
|
class LabsRecordingsNamespace {
|
|
1046
1355
|
sm;
|
|
@@ -1349,6 +1658,7 @@ class OptimizerNamespace {
|
|
|
1349
1658
|
}
|
|
1350
1659
|
}
|
|
1351
1660
|
function labsAgentPayload(input) {
|
|
1661
|
+
const fixedLanguage = input.preferred_language ?? input.preferredLanguage ?? input.language;
|
|
1352
1662
|
return compact({
|
|
1353
1663
|
agency_id: input.agency_id ?? input.agencyId,
|
|
1354
1664
|
agent_key: input.agent_key ?? input.agentKey,
|
|
@@ -1374,8 +1684,9 @@ function labsAgentPayload(input) {
|
|
|
1374
1684
|
goal: input.goal,
|
|
1375
1685
|
greeting: input.greeting,
|
|
1376
1686
|
system_prompt: input.system_prompt ?? input.systemPrompt,
|
|
1377
|
-
language:
|
|
1687
|
+
language: fixedLanguage,
|
|
1378
1688
|
voice: input.voice ? voicePayload(input.voice) : undefined,
|
|
1689
|
+
voice_preference: voicePreferencePayload(input.voice_preference ?? input.voicePreference, fixedLanguage),
|
|
1379
1690
|
provider_keys: input.provider_keys ?? (input.providerKeys ? providerKeysPayload(input.providerKeys) : undefined),
|
|
1380
1691
|
byok: input.byok ? byokPayload(input.byok) : undefined,
|
|
1381
1692
|
telephony: input.telephony ? telephonyPayload(input.telephony) : undefined,
|
|
@@ -1394,13 +1705,16 @@ function labsAgentPayload(input) {
|
|
|
1394
1705
|
}
|
|
1395
1706
|
function hostedListQuery(opts) {
|
|
1396
1707
|
const q = new URLSearchParams();
|
|
1397
|
-
|
|
1398
|
-
|
|
1708
|
+
const agencyId = opts.agency_id ?? opts.agencyId;
|
|
1709
|
+
if (agencyId)
|
|
1710
|
+
q.set("agency_id", agencyId);
|
|
1399
1711
|
const agentKey = opts.agent_key ?? opts.agentKey;
|
|
1400
1712
|
if (agentKey)
|
|
1401
1713
|
q.set("agent_key", agentKey);
|
|
1402
1714
|
if (opts.limit !== undefined)
|
|
1403
1715
|
q.set("limit", String(opts.limit));
|
|
1716
|
+
if (opts.offset !== undefined)
|
|
1717
|
+
q.set("offset", String(opts.offset));
|
|
1404
1718
|
return q;
|
|
1405
1719
|
}
|
|
1406
1720
|
function telephonyPayload(input) {
|
|
@@ -1614,6 +1928,20 @@ function voicePayload(input) {
|
|
|
1614
1928
|
model: input.model,
|
|
1615
1929
|
});
|
|
1616
1930
|
}
|
|
1931
|
+
function voicePreferencePayload(input, defaultLanguage) {
|
|
1932
|
+
if (!input)
|
|
1933
|
+
return undefined;
|
|
1934
|
+
return compact({
|
|
1935
|
+
description: input.description,
|
|
1936
|
+
language: input.language ?? defaultLanguage,
|
|
1937
|
+
provider: input.provider,
|
|
1938
|
+
gender: input.gender,
|
|
1939
|
+
voice_type: input.voice_type ?? input.voiceType,
|
|
1940
|
+
model: input.model,
|
|
1941
|
+
configured_only: input.configured_only ?? input.configuredOnly,
|
|
1942
|
+
premium: input.premium,
|
|
1943
|
+
});
|
|
1944
|
+
}
|
|
1617
1945
|
function providerKeysPayload(input) {
|
|
1618
1946
|
return compact({
|
|
1619
1947
|
ultravox: input.ultravox,
|