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/cjs/index.js
CHANGED
|
@@ -40,6 +40,108 @@ const COACH_SYSTEM = "You are the coaching core of a second mind for a live voic
|
|
|
40
40
|
"conversation and return ONE short, silent directive the agent reads but never " +
|
|
41
41
|
"speaks aloud — a correction or nudge, phrased imperatively. If nothing needs " +
|
|
42
42
|
"correcting, return an empty string.";
|
|
43
|
+
const STRUCTURED_COACH_SYSTEM = "You are the coaching core of a second mind for a live voice agent. Read the " +
|
|
44
|
+
"conversation and return ONLY one JSON object with exactly these keys: " +
|
|
45
|
+
"empathy_directive (string), tactical_directive (string), surface_facts (string[]), " +
|
|
46
|
+
"guardrails (string[]), language (short code), confidence (0-1), and kind " +
|
|
47
|
+
"(empathy, tactical, guardrail, or mixed). Be silent with low confidence when evidence is weak.";
|
|
48
|
+
function boundedInteger(value, fallback, minimum, maximum) {
|
|
49
|
+
const parsed = Number(value);
|
|
50
|
+
return Number.isFinite(parsed)
|
|
51
|
+
? Math.max(minimum, Math.min(maximum, Math.trunc(parsed)))
|
|
52
|
+
: fallback;
|
|
53
|
+
}
|
|
54
|
+
function textControl(input) {
|
|
55
|
+
return {
|
|
56
|
+
enabled: input?.enabled ?? true,
|
|
57
|
+
instructions: String(input?.instructions ?? "").trim(),
|
|
58
|
+
max_chars: boundedInteger(input?.max_chars ?? input?.maxChars, 240, 1, 2000),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function listControl(input) {
|
|
62
|
+
return {
|
|
63
|
+
enabled: input?.enabled ?? true,
|
|
64
|
+
instructions: String(input?.instructions ?? "").trim(),
|
|
65
|
+
max_items: boundedInteger(input?.max_items ?? input?.maxItems, 4, 0, 20),
|
|
66
|
+
item_max_chars: boundedInteger(input?.item_max_chars ?? input?.itemMaxChars, 180, 1, 2000),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function normalizeDirectiveContract(input = {}) {
|
|
70
|
+
const languageMode = input.language_mode ?? input.languageMode ?? "caller";
|
|
71
|
+
const allowed = input.allowed_kinds ?? input.allowedKinds ?? ["empathy", "tactical", "guardrail", "mixed"];
|
|
72
|
+
const threshold = Number(input.confidence_threshold ?? input.confidenceThreshold ?? 0.5);
|
|
73
|
+
return {
|
|
74
|
+
empathy_directive: textControl(input.empathy_directive ?? input.empathyDirective),
|
|
75
|
+
tactical_directive: textControl(input.tactical_directive ?? input.tacticalDirective),
|
|
76
|
+
surface_facts: listControl(input.surface_facts ?? input.surfaceFacts),
|
|
77
|
+
guardrails: listControl(input.guardrails),
|
|
78
|
+
language_mode: languageMode,
|
|
79
|
+
fixed_language: String(input.fixed_language ?? input.fixedLanguage ?? "").trim(),
|
|
80
|
+
allowed_kinds: allowed.filter((kind) => ["empathy", "tactical", "guardrail", "mixed"].includes(kind)),
|
|
81
|
+
confidence_threshold: Number.isFinite(threshold) ? Math.max(0, Math.min(1, threshold)) : 0.5,
|
|
82
|
+
operator_guardrails: (input.operator_guardrails ?? input.operatorGuardrails ?? [])
|
|
83
|
+
.map((rule) => String(rule).trim())
|
|
84
|
+
.filter(Boolean)
|
|
85
|
+
.slice(0, 50),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function directiveContractPrompt(contract) {
|
|
89
|
+
return ("Developer directive contract (obey exactly):\n" +
|
|
90
|
+
JSON.stringify(contract, null, 2) +
|
|
91
|
+
"\nDisabled string fields must be empty strings. Disabled list fields must be empty arrays. " +
|
|
92
|
+
"Standing operator guardrails are mandatory and cannot be removed.");
|
|
93
|
+
}
|
|
94
|
+
function parseJsonObject(text) {
|
|
95
|
+
const trimmed = String(text ?? "").trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "");
|
|
96
|
+
const start = trimmed.indexOf("{");
|
|
97
|
+
const end = trimmed.lastIndexOf("}");
|
|
98
|
+
if (start < 0 || end <= start)
|
|
99
|
+
return null;
|
|
100
|
+
try {
|
|
101
|
+
const parsed = JSON.parse(trimmed.slice(start, end + 1));
|
|
102
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function clipText(value, limit) {
|
|
109
|
+
return String(value ?? "").trim().slice(0, limit);
|
|
110
|
+
}
|
|
111
|
+
function clipList(value, maxItems, itemMaxChars) {
|
|
112
|
+
if (!Array.isArray(value))
|
|
113
|
+
return [];
|
|
114
|
+
return value.slice(0, maxItems).map((item) => clipText(item, itemMaxChars)).filter(Boolean);
|
|
115
|
+
}
|
|
116
|
+
function normalizeStructuredDirective(raw, contract) {
|
|
117
|
+
const kind = String(raw.kind ?? "mixed");
|
|
118
|
+
if (!["empathy", "tactical", "guardrail", "mixed"].includes(kind))
|
|
119
|
+
return null;
|
|
120
|
+
const parsedConfidence = Number(raw.confidence ?? 0);
|
|
121
|
+
const confidence = Number.isFinite(parsedConfidence) ? Math.max(0, Math.min(1, parsedConfidence)) : 0;
|
|
122
|
+
const operatorGuardrails = contract.operator_guardrails;
|
|
123
|
+
const generatedGuardrails = contract.guardrails.enabled
|
|
124
|
+
? clipList(raw.guardrails, contract.guardrails.max_items, contract.guardrails.item_max_chars)
|
|
125
|
+
: [];
|
|
126
|
+
const language = contract.language_mode === "fixed"
|
|
127
|
+
? contract.fixed_language
|
|
128
|
+
: clipText(raw.language, 32);
|
|
129
|
+
return {
|
|
130
|
+
empathy_directive: contract.empathy_directive.enabled
|
|
131
|
+
? clipText(raw.empathy_directive, contract.empathy_directive.max_chars)
|
|
132
|
+
: "",
|
|
133
|
+
tactical_directive: contract.tactical_directive.enabled
|
|
134
|
+
? clipText(raw.tactical_directive, contract.tactical_directive.max_chars)
|
|
135
|
+
: "",
|
|
136
|
+
surface_facts: contract.surface_facts.enabled
|
|
137
|
+
? clipList(raw.surface_facts, contract.surface_facts.max_items, contract.surface_facts.item_max_chars)
|
|
138
|
+
: [],
|
|
139
|
+
guardrails: [...new Set([...generatedGuardrails, ...operatorGuardrails])],
|
|
140
|
+
language,
|
|
141
|
+
confidence: contract.allowed_kinds.includes(kind) ? confidence : 0,
|
|
142
|
+
kind,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
43
145
|
class SupafoneLabs {
|
|
44
146
|
baseUrl;
|
|
45
147
|
supafoneApiBaseUrl;
|
|
@@ -162,6 +264,31 @@ class SupafoneLabs {
|
|
|
162
264
|
clearTimeout(timer);
|
|
163
265
|
}
|
|
164
266
|
}
|
|
267
|
+
/** @internal Authenticated binary request to the Supafone app API. */
|
|
268
|
+
async requestSupafoneBinary(path) {
|
|
269
|
+
const ctrl = new AbortController();
|
|
270
|
+
const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
271
|
+
try {
|
|
272
|
+
const res = await fetch(this.supafoneApiBaseUrl + path, {
|
|
273
|
+
method: "GET",
|
|
274
|
+
signal: ctrl.signal,
|
|
275
|
+
headers: { Authorization: `Bearer ${this.supafoneApiKey}` },
|
|
276
|
+
});
|
|
277
|
+
if (!res.ok) {
|
|
278
|
+
const text = await res.text();
|
|
279
|
+
const parsed = text ? safeJson(text) : {};
|
|
280
|
+
const detail = parsed?.detail ?? text ?? `HTTP ${res.status}`;
|
|
281
|
+
throw new SupafoneLabsError(`GET ${path}: ${detail}`, res.status, parsed);
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
content: await res.arrayBuffer(),
|
|
285
|
+
mediaType: res.headers.get("content-type") || "application/octet-stream",
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
finally {
|
|
289
|
+
clearTimeout(timer);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
165
292
|
/**
|
|
166
293
|
* Exchange the account email/password for a product-API JWT (the same login
|
|
167
294
|
* as app.supafone.ai). Called lazily by campaigns/calls — call directly only
|
|
@@ -350,6 +477,43 @@ class SupafoneLabs {
|
|
|
350
477
|
});
|
|
351
478
|
return out.text.trim();
|
|
352
479
|
}
|
|
480
|
+
/**
|
|
481
|
+
* Structured SecondMind guidance with developer-controlled field policy.
|
|
482
|
+
* Returns null when JSON is invalid, evidence misses the confidence gate,
|
|
483
|
+
* the generated kind is disallowed, or the local transform suppresses it.
|
|
484
|
+
*/
|
|
485
|
+
async whisperStructured(transcript, opts = {}) {
|
|
486
|
+
const contract = normalizeDirectiveContract(opts.directive_contract ?? opts.directiveContract ?? {});
|
|
487
|
+
const operatorRules = [opts.guardrails, ...contract.operator_guardrails]
|
|
488
|
+
.map((rule) => String(rule ?? "").trim())
|
|
489
|
+
.filter(Boolean);
|
|
490
|
+
const system = [
|
|
491
|
+
STRUCTURED_COACH_SYSTEM,
|
|
492
|
+
directiveContractPrompt(contract),
|
|
493
|
+
operatorRules.length ? `Operator rules:\n${operatorRules.join("\n")}` : "",
|
|
494
|
+
].filter(Boolean).join("\n\n");
|
|
495
|
+
const out = await this.oracle({
|
|
496
|
+
model: opts.model,
|
|
497
|
+
maxTokens: opts.maxTokens ?? 320,
|
|
498
|
+
...(opts.temperature !== undefined ? { temperature: opts.temperature } : {}),
|
|
499
|
+
messages: [
|
|
500
|
+
{ role: "system", content: system },
|
|
501
|
+
{ role: "user", content: transcript },
|
|
502
|
+
],
|
|
503
|
+
});
|
|
504
|
+
const parsed = parseJsonObject(out.text);
|
|
505
|
+
let directive = parsed ? normalizeStructuredDirective(parsed, contract) : null;
|
|
506
|
+
if (directive && opts.transform) {
|
|
507
|
+
directive = await opts.transform(directive);
|
|
508
|
+
}
|
|
509
|
+
if (!directive || directive.confidence < contract.confidence_threshold)
|
|
510
|
+
return null;
|
|
511
|
+
return directive;
|
|
512
|
+
}
|
|
513
|
+
/** Alias that reads naturally in applications building their own watcher loop. */
|
|
514
|
+
directive(transcript, opts = {}) {
|
|
515
|
+
return this.whisperStructured(transcript, opts);
|
|
516
|
+
}
|
|
353
517
|
/** Hosted TTS — returns raw audio bytes (WAV/PCM per voice). */
|
|
354
518
|
async tts(text, voice = "supafone-labs-calm-en") {
|
|
355
519
|
const ctrl = new AbortController();
|
|
@@ -788,6 +952,8 @@ class LabsNamespace {
|
|
|
788
952
|
phoneNumbers;
|
|
789
953
|
telephony;
|
|
790
954
|
calls;
|
|
955
|
+
activity;
|
|
956
|
+
plans;
|
|
791
957
|
recordings;
|
|
792
958
|
transcripts;
|
|
793
959
|
constructor(sm) {
|
|
@@ -801,6 +967,8 @@ class LabsNamespace {
|
|
|
801
967
|
this.phoneNumbers = new LabsPhoneNumbersNamespace(sm);
|
|
802
968
|
this.telephony = new LabsTelephonyNamespace(sm);
|
|
803
969
|
this.calls = new LabsCallsNamespace(sm);
|
|
970
|
+
this.activity = new LabsActivityNamespace(sm);
|
|
971
|
+
this.plans = new LabsPlansNamespace(sm);
|
|
804
972
|
this.recordings = new LabsRecordingsNamespace(sm);
|
|
805
973
|
this.transcripts = new LabsTranscriptsNamespace(sm);
|
|
806
974
|
}
|
|
@@ -989,7 +1157,7 @@ class LabsVoicesNamespace {
|
|
|
989
1157
|
constructor(sm) {
|
|
990
1158
|
this.sm = sm;
|
|
991
1159
|
}
|
|
992
|
-
/**
|
|
1160
|
+
/** Live normalized catalog from every connected/managed TTS provider. */
|
|
993
1161
|
list(opts = {}) {
|
|
994
1162
|
const q = new URLSearchParams();
|
|
995
1163
|
if (opts.provider)
|
|
@@ -998,13 +1166,104 @@ class LabsVoicesNamespace {
|
|
|
998
1166
|
q.set("search", opts.search);
|
|
999
1167
|
if (opts.language)
|
|
1000
1168
|
q.set("language", opts.language);
|
|
1169
|
+
const compatibleLanguage = opts.compatible_language ?? opts.compatibleLanguage;
|
|
1170
|
+
if (compatibleLanguage)
|
|
1171
|
+
q.set("compatible_language", compatibleLanguage);
|
|
1172
|
+
if (opts.gender)
|
|
1173
|
+
q.set("gender", opts.gender);
|
|
1174
|
+
const voiceType = opts.voice_type ?? opts.voiceType;
|
|
1175
|
+
if (voiceType)
|
|
1176
|
+
q.set("voice_type", voiceType);
|
|
1177
|
+
if (opts.model)
|
|
1178
|
+
q.set("model", opts.model);
|
|
1179
|
+
const runtimeProvider = opts.runtime_provider ?? opts.runtimeProvider;
|
|
1180
|
+
if (runtimeProvider)
|
|
1181
|
+
q.set("runtime_provider", runtimeProvider);
|
|
1182
|
+
const configuredOnly = opts.configured_only ?? opts.configuredOnly;
|
|
1183
|
+
if (configuredOnly !== undefined)
|
|
1184
|
+
q.set("configured_only", String(configuredOnly));
|
|
1001
1185
|
if (opts.cursor !== undefined)
|
|
1002
1186
|
q.set("cursor", String(opts.cursor));
|
|
1003
1187
|
if (opts.limit !== undefined)
|
|
1004
1188
|
q.set("limit", String(opts.limit));
|
|
1189
|
+
const agencyId = opts.agency_id ?? opts.agencyId;
|
|
1190
|
+
if (agencyId)
|
|
1191
|
+
q.set("agency_id", agencyId);
|
|
1005
1192
|
const suffix = q.toString() ? `?${q}` : "";
|
|
1006
1193
|
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/voices${suffix}`);
|
|
1007
1194
|
}
|
|
1195
|
+
/** Provider/model language limits and their Ultravox-compatible intersection. */
|
|
1196
|
+
capabilities() {
|
|
1197
|
+
return this.sm.requestSupafoneApi("GET", "/api/v1/labs/voices/capabilities");
|
|
1198
|
+
}
|
|
1199
|
+
/** Page through the complete normalized catalog. */
|
|
1200
|
+
async listAll(opts = {}) {
|
|
1201
|
+
const pageSize = Math.max(1, Math.min(opts.pageSize ?? 250, 250));
|
|
1202
|
+
const maxPages = Math.max(1, opts.maxPages ?? 100);
|
|
1203
|
+
let cursor = 0;
|
|
1204
|
+
let pages = 0;
|
|
1205
|
+
let first;
|
|
1206
|
+
const voices = [];
|
|
1207
|
+
const seen = new Set();
|
|
1208
|
+
while (cursor !== null && pages < maxPages) {
|
|
1209
|
+
if (seen.has(cursor))
|
|
1210
|
+
throw new SupafoneLabsError("Voice catalog returned a repeated cursor");
|
|
1211
|
+
seen.add(cursor);
|
|
1212
|
+
const page = await this.list({ ...opts, cursor, limit: pageSize });
|
|
1213
|
+
first ??= page;
|
|
1214
|
+
voices.push(...page.voices);
|
|
1215
|
+
cursor = page.next_cursor ?? null;
|
|
1216
|
+
pages += 1;
|
|
1217
|
+
}
|
|
1218
|
+
if (cursor !== null) {
|
|
1219
|
+
throw new SupafoneLabsError(`Voice catalog exceeded maxPages=${maxPages}`);
|
|
1220
|
+
}
|
|
1221
|
+
return {
|
|
1222
|
+
...(first ?? { total: 0, providers: [] }),
|
|
1223
|
+
voices,
|
|
1224
|
+
total: first?.total ?? voices.length,
|
|
1225
|
+
cursor: 0,
|
|
1226
|
+
next_cursor: null,
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
1229
|
+
/** Rank real current voices from a plain-language description. */
|
|
1230
|
+
recommend(opts) {
|
|
1231
|
+
if (!opts.description?.trim()) {
|
|
1232
|
+
throw new SupafoneLabsError("description is required — describe the voice you want");
|
|
1233
|
+
}
|
|
1234
|
+
return this.sm.requestSupafoneApi("POST", "/api/v1/labs/voices/recommend", compact({
|
|
1235
|
+
agency_id: opts.agency_id ?? opts.agencyId,
|
|
1236
|
+
description: opts.description.trim(),
|
|
1237
|
+
language: opts.language,
|
|
1238
|
+
provider: opts.provider,
|
|
1239
|
+
gender: opts.gender,
|
|
1240
|
+
voice_type: opts.voice_type ?? opts.voiceType,
|
|
1241
|
+
model: opts.model,
|
|
1242
|
+
configured_only: opts.configured_only ?? opts.configuredOnly,
|
|
1243
|
+
premium: opts.premium,
|
|
1244
|
+
limit: opts.limit,
|
|
1245
|
+
}));
|
|
1246
|
+
}
|
|
1247
|
+
/** Download an authenticated voice preview. */
|
|
1248
|
+
preview(voiceId, opts = {}) {
|
|
1249
|
+
if (!voiceId.trim())
|
|
1250
|
+
throw new SupafoneLabsError("voiceId is required");
|
|
1251
|
+
const q = new URLSearchParams({ voice: voiceId.trim() });
|
|
1252
|
+
const agencyId = opts.agency_id ?? opts.agencyId;
|
|
1253
|
+
if (agencyId)
|
|
1254
|
+
q.set("agency_id", agencyId);
|
|
1255
|
+
return this.sm.requestSupafoneBinary(`/api/v1/labs/voices/preview?${q}`);
|
|
1256
|
+
}
|
|
1257
|
+
/** Convert a catalog row into the exact Agent Factory voice field. */
|
|
1258
|
+
selection(voice) {
|
|
1259
|
+
if (!voice?.id)
|
|
1260
|
+
throw new SupafoneLabsError("A catalog voice with id is required");
|
|
1261
|
+
return compact({
|
|
1262
|
+
provider: voice.provider_key || voice.source,
|
|
1263
|
+
voiceId: voice.id,
|
|
1264
|
+
model: voice.model || undefined,
|
|
1265
|
+
});
|
|
1266
|
+
}
|
|
1008
1267
|
}
|
|
1009
1268
|
class LabsRuntimeNamespace {
|
|
1010
1269
|
sm;
|
|
@@ -1043,11 +1302,61 @@ class LabsCallsNamespace {
|
|
|
1043
1302
|
}
|
|
1044
1303
|
get(callId, opts = {}) {
|
|
1045
1304
|
const q = new URLSearchParams();
|
|
1046
|
-
|
|
1047
|
-
|
|
1305
|
+
const agencyId = opts.agency_id ?? opts.agencyId;
|
|
1306
|
+
if (agencyId)
|
|
1307
|
+
q.set("agency_id", agencyId);
|
|
1048
1308
|
const suffix = q.toString() ? `?${q}` : "";
|
|
1049
1309
|
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/calls/${encodeURIComponent(callId)}${suffix}`);
|
|
1050
1310
|
}
|
|
1311
|
+
delete(callId, opts = {}) {
|
|
1312
|
+
const q = new URLSearchParams();
|
|
1313
|
+
const agencyId = opts.agency_id ?? opts.agencyId;
|
|
1314
|
+
if (agencyId)
|
|
1315
|
+
q.set("agency_id", agencyId);
|
|
1316
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
1317
|
+
return this.sm.requestSupafoneApi("DELETE", `/api/v1/labs/calls/${encodeURIComponent(callId)}${suffix}`);
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
class LabsActivityNamespace {
|
|
1321
|
+
sm;
|
|
1322
|
+
constructor(sm) {
|
|
1323
|
+
this.sm = sm;
|
|
1324
|
+
}
|
|
1325
|
+
list(opts = {}) {
|
|
1326
|
+
const q = new URLSearchParams();
|
|
1327
|
+
const accountId = opts.account_id ?? opts.accountId ?? opts.agency_id ?? opts.agencyId;
|
|
1328
|
+
if (accountId)
|
|
1329
|
+
q.set("account_id", accountId);
|
|
1330
|
+
const eventType = opts.event_type ?? opts.eventType;
|
|
1331
|
+
if (eventType)
|
|
1332
|
+
q.set("event_type", eventType);
|
|
1333
|
+
const resourceType = opts.resource_type ?? opts.resourceType;
|
|
1334
|
+
if (resourceType)
|
|
1335
|
+
q.set("resource_type", resourceType);
|
|
1336
|
+
const resourceId = opts.resource_id ?? opts.resourceId;
|
|
1337
|
+
if (resourceId)
|
|
1338
|
+
q.set("resource_id", resourceId);
|
|
1339
|
+
if (opts.limit !== undefined)
|
|
1340
|
+
q.set("limit", String(opts.limit));
|
|
1341
|
+
if (opts.offset !== undefined)
|
|
1342
|
+
q.set("offset", String(opts.offset));
|
|
1343
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
1344
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/activity${suffix}`);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
class LabsPlansNamespace {
|
|
1348
|
+
sm;
|
|
1349
|
+
constructor(sm) {
|
|
1350
|
+
this.sm = sm;
|
|
1351
|
+
}
|
|
1352
|
+
list(opts = {}) {
|
|
1353
|
+
const activity = new LabsActivityNamespace(this.sm);
|
|
1354
|
+
return activity.list({
|
|
1355
|
+
...opts,
|
|
1356
|
+
eventType: "studio.plan.created",
|
|
1357
|
+
resourceType: "studio_plan",
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1051
1360
|
}
|
|
1052
1361
|
class LabsRecordingsNamespace {
|
|
1053
1362
|
sm;
|
|
@@ -1356,6 +1665,7 @@ class OptimizerNamespace {
|
|
|
1356
1665
|
}
|
|
1357
1666
|
}
|
|
1358
1667
|
function labsAgentPayload(input) {
|
|
1668
|
+
const fixedLanguage = input.preferred_language ?? input.preferredLanguage ?? input.language;
|
|
1359
1669
|
return compact({
|
|
1360
1670
|
agency_id: input.agency_id ?? input.agencyId,
|
|
1361
1671
|
agent_key: input.agent_key ?? input.agentKey,
|
|
@@ -1381,8 +1691,9 @@ function labsAgentPayload(input) {
|
|
|
1381
1691
|
goal: input.goal,
|
|
1382
1692
|
greeting: input.greeting,
|
|
1383
1693
|
system_prompt: input.system_prompt ?? input.systemPrompt,
|
|
1384
|
-
language:
|
|
1694
|
+
language: fixedLanguage,
|
|
1385
1695
|
voice: input.voice ? voicePayload(input.voice) : undefined,
|
|
1696
|
+
voice_preference: voicePreferencePayload(input.voice_preference ?? input.voicePreference, fixedLanguage),
|
|
1386
1697
|
provider_keys: input.provider_keys ?? (input.providerKeys ? providerKeysPayload(input.providerKeys) : undefined),
|
|
1387
1698
|
byok: input.byok ? byokPayload(input.byok) : undefined,
|
|
1388
1699
|
telephony: input.telephony ? telephonyPayload(input.telephony) : undefined,
|
|
@@ -1401,13 +1712,16 @@ function labsAgentPayload(input) {
|
|
|
1401
1712
|
}
|
|
1402
1713
|
function hostedListQuery(opts) {
|
|
1403
1714
|
const q = new URLSearchParams();
|
|
1404
|
-
|
|
1405
|
-
|
|
1715
|
+
const agencyId = opts.agency_id ?? opts.agencyId;
|
|
1716
|
+
if (agencyId)
|
|
1717
|
+
q.set("agency_id", agencyId);
|
|
1406
1718
|
const agentKey = opts.agent_key ?? opts.agentKey;
|
|
1407
1719
|
if (agentKey)
|
|
1408
1720
|
q.set("agent_key", agentKey);
|
|
1409
1721
|
if (opts.limit !== undefined)
|
|
1410
1722
|
q.set("limit", String(opts.limit));
|
|
1723
|
+
if (opts.offset !== undefined)
|
|
1724
|
+
q.set("offset", String(opts.offset));
|
|
1411
1725
|
return q;
|
|
1412
1726
|
}
|
|
1413
1727
|
function telephonyPayload(input) {
|
|
@@ -1621,6 +1935,20 @@ function voicePayload(input) {
|
|
|
1621
1935
|
model: input.model,
|
|
1622
1936
|
});
|
|
1623
1937
|
}
|
|
1938
|
+
function voicePreferencePayload(input, defaultLanguage) {
|
|
1939
|
+
if (!input)
|
|
1940
|
+
return undefined;
|
|
1941
|
+
return compact({
|
|
1942
|
+
description: input.description,
|
|
1943
|
+
language: input.language ?? defaultLanguage,
|
|
1944
|
+
provider: input.provider,
|
|
1945
|
+
gender: input.gender,
|
|
1946
|
+
voice_type: input.voice_type ?? input.voiceType,
|
|
1947
|
+
model: input.model,
|
|
1948
|
+
configured_only: input.configured_only ?? input.configuredOnly,
|
|
1949
|
+
premium: input.premium,
|
|
1950
|
+
});
|
|
1951
|
+
}
|
|
1624
1952
|
function providerKeysPayload(input) {
|
|
1625
1953
|
return compact({
|
|
1626
1954
|
ultravox: input.ultravox,
|