getpatter 0.6.3 → 0.6.4
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 +5 -4
- package/dist/{carrier-config-3WDQXP5J.mjs → carrier-config-7YGNRBPO.mjs} +17 -11
- package/dist/{chunk-R2T4JABZ.mjs → chunk-3VVATR6A.mjs} +8 -6
- package/dist/{chunk-Z6W5XFWS.mjs → chunk-7IIV3BY4.mjs} +981 -196
- package/dist/{chunk-CL2U3YET.mjs → chunk-BO227NTF.mjs} +271 -54
- package/dist/cli.js +63 -20
- package/dist/dashboard/ui.html +10 -10
- package/dist/index.d.mts +867 -187
- package/dist/index.d.ts +867 -187
- package/dist/index.js +1785 -517
- package/dist/index.mjs +501 -250
- package/dist/{openai-realtime-2-CNFARP25.mjs → openai-realtime-2-L5EKAAUH.mjs} +1 -1
- package/dist/{silero-vad-LNDFGIY7.mjs → silero-vad-RGF5HCIR.mjs} +1 -1
- package/dist/{test-mode-MDBQ4ECE.mjs → test-mode-4QLLWYVV.mjs} +2 -2
- package/package.json +2 -1
- package/src/dashboard/ui.html +10 -10
package/dist/index.mjs
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
OpenAILLMProvider,
|
|
20
20
|
PRICING_LAST_UPDATED,
|
|
21
21
|
PRICING_VERSION,
|
|
22
|
+
PatterConfigError,
|
|
22
23
|
PatterConnectionError,
|
|
23
24
|
PatterError,
|
|
24
25
|
PipelineHookExecutor,
|
|
@@ -52,9 +53,11 @@ import {
|
|
|
52
53
|
mergePricing,
|
|
53
54
|
mountApi,
|
|
54
55
|
mountDashboard,
|
|
56
|
+
openclawConsult,
|
|
57
|
+
openclawPostCallNotifier,
|
|
55
58
|
resolveLogRoot,
|
|
56
59
|
startSpan
|
|
57
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-7IIV3BY4.mjs";
|
|
58
61
|
import {
|
|
59
62
|
OpenAIRealtime2Adapter,
|
|
60
63
|
OpenAIRealtimeAdapter,
|
|
@@ -73,8 +76,9 @@ import {
|
|
|
73
76
|
pcm16ToMulaw,
|
|
74
77
|
resample16kTo8k,
|
|
75
78
|
resample24kTo16k,
|
|
76
|
-
resample8kTo16k
|
|
77
|
-
|
|
79
|
+
resample8kTo16k,
|
|
80
|
+
validateRealtimeTurnDetection
|
|
81
|
+
} from "./chunk-BO227NTF.mjs";
|
|
78
82
|
import {
|
|
79
83
|
MinWordsStrategy,
|
|
80
84
|
evaluateStrategies,
|
|
@@ -89,7 +93,7 @@ import {
|
|
|
89
93
|
} from "./chunk-6GR5MHHQ.mjs";
|
|
90
94
|
import {
|
|
91
95
|
SileroVAD
|
|
92
|
-
} from "./chunk-
|
|
96
|
+
} from "./chunk-3VVATR6A.mjs";
|
|
93
97
|
import {
|
|
94
98
|
__dirname,
|
|
95
99
|
__require,
|
|
@@ -111,6 +115,9 @@ var Realtime = class {
|
|
|
111
115
|
voice;
|
|
112
116
|
reasoningEffort;
|
|
113
117
|
inputAudioTranscriptionModel;
|
|
118
|
+
noiseReduction;
|
|
119
|
+
turnDetection;
|
|
120
|
+
gateResponseOnTranscript;
|
|
114
121
|
constructor(opts = {}) {
|
|
115
122
|
const key = opts.apiKey ?? process.env.OPENAI_API_KEY;
|
|
116
123
|
if (!key) {
|
|
@@ -118,11 +125,20 @@ var Realtime = class {
|
|
|
118
125
|
"OpenAI Realtime requires an apiKey. Pass { apiKey: 'sk-...' } or set OPENAI_API_KEY in the environment."
|
|
119
126
|
);
|
|
120
127
|
}
|
|
128
|
+
if (opts.noiseReduction !== void 0 && opts.noiseReduction !== "near_field" && opts.noiseReduction !== "far_field") {
|
|
129
|
+
throw new Error(
|
|
130
|
+
`noiseReduction must be 'near_field' or 'far_field', got ${JSON.stringify(opts.noiseReduction)}`
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
validateRealtimeTurnDetection(opts.turnDetection);
|
|
121
134
|
this.apiKey = key;
|
|
122
135
|
this.model = opts.model ?? "gpt-realtime-mini";
|
|
123
136
|
this.voice = opts.voice ?? "alloy";
|
|
124
137
|
this.reasoningEffort = opts.reasoningEffort;
|
|
125
138
|
this.inputAudioTranscriptionModel = opts.inputAudioTranscriptionModel;
|
|
139
|
+
this.noiseReduction = opts.noiseReduction;
|
|
140
|
+
this.turnDetection = opts.turnDetection;
|
|
141
|
+
this.gateResponseOnTranscript = opts.gateResponseOnTranscript;
|
|
126
142
|
}
|
|
127
143
|
};
|
|
128
144
|
|
|
@@ -135,6 +151,9 @@ var Realtime2 = class {
|
|
|
135
151
|
voice;
|
|
136
152
|
reasoningEffort;
|
|
137
153
|
inputAudioTranscriptionModel;
|
|
154
|
+
noiseReduction;
|
|
155
|
+
turnDetection;
|
|
156
|
+
gateResponseOnTranscript;
|
|
138
157
|
constructor(opts = {}) {
|
|
139
158
|
const key = opts.apiKey ?? process.env.OPENAI_API_KEY;
|
|
140
159
|
if (!key) {
|
|
@@ -142,11 +161,20 @@ var Realtime2 = class {
|
|
|
142
161
|
"OpenAI Realtime 2 requires an apiKey. Pass { apiKey: 'sk-...' } or set OPENAI_API_KEY in the environment."
|
|
143
162
|
);
|
|
144
163
|
}
|
|
164
|
+
if (opts.noiseReduction !== void 0 && opts.noiseReduction !== "near_field" && opts.noiseReduction !== "far_field") {
|
|
165
|
+
throw new Error(
|
|
166
|
+
`noiseReduction must be 'near_field' or 'far_field', got ${JSON.stringify(opts.noiseReduction)}`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
validateRealtimeTurnDetection(opts.turnDetection);
|
|
145
170
|
this.apiKey = key;
|
|
146
171
|
this.model = opts.model ?? "gpt-realtime-2";
|
|
147
172
|
this.voice = opts.voice ?? "alloy";
|
|
148
173
|
this.reasoningEffort = opts.reasoningEffort;
|
|
149
174
|
this.inputAudioTranscriptionModel = opts.inputAudioTranscriptionModel;
|
|
175
|
+
this.noiseReduction = opts.noiseReduction;
|
|
176
|
+
this.turnDetection = opts.turnDetection;
|
|
177
|
+
this.gateResponseOnTranscript = opts.gateResponseOnTranscript;
|
|
150
178
|
}
|
|
151
179
|
};
|
|
152
180
|
|
|
@@ -573,7 +601,7 @@ function resolvePersistRoot(persist) {
|
|
|
573
601
|
if (typeof persist === "string") return resolveLogRoot(persist);
|
|
574
602
|
const envRoot = resolveLogRoot();
|
|
575
603
|
if (envRoot !== null) return envRoot;
|
|
576
|
-
return
|
|
604
|
+
return null;
|
|
577
605
|
}
|
|
578
606
|
function closeParkedConnections(slot) {
|
|
579
607
|
if (slot.stt) {
|
|
@@ -857,7 +885,12 @@ var Patter = class {
|
|
|
857
885
|
...working,
|
|
858
886
|
provider: "openai_realtime",
|
|
859
887
|
model: working.model ?? engine.model,
|
|
860
|
-
voice: working.voice ?? engine.voice
|
|
888
|
+
voice: working.voice ?? engine.voice,
|
|
889
|
+
// Explicit agent() kwargs win over the engine marker value
|
|
890
|
+
// (same precedence as Python: explicit kwarg > engine > default).
|
|
891
|
+
openaiRealtimeNoiseReduction: working.openaiRealtimeNoiseReduction ?? engine.noiseReduction,
|
|
892
|
+
realtimeTurnDetection: working.realtimeTurnDetection ?? engine.turnDetection,
|
|
893
|
+
openaiRealtimeGateResponseOnTranscript: working.openaiRealtimeGateResponseOnTranscript ?? engine.gateResponseOnTranscript
|
|
861
894
|
};
|
|
862
895
|
if (!this.localConfig.openaiKey) {
|
|
863
896
|
this.localConfig = { ...this.localConfig, openaiKey: engine.apiKey };
|
|
@@ -882,6 +915,11 @@ var Patter = class {
|
|
|
882
915
|
throw new Error(`provider must be one of: ${valid.join(", ")}. Got: '${working.provider}'`);
|
|
883
916
|
}
|
|
884
917
|
}
|
|
918
|
+
if (working.consult && working.provider === "elevenlabs_convai") {
|
|
919
|
+
getLogger().warn(
|
|
920
|
+
"consult is set but provider is ElevenLabs ConvAI; the consult tool is only injected in Realtime and Pipeline modes and will be ignored for this agent."
|
|
921
|
+
);
|
|
922
|
+
}
|
|
885
923
|
if (working.llm !== void 0) {
|
|
886
924
|
const llm = working.llm;
|
|
887
925
|
if (!llm || typeof llm.stream !== "function") {
|
|
@@ -982,7 +1020,7 @@ var Patter = class {
|
|
|
982
1020
|
const telephonyProvider = carrier.kind;
|
|
983
1021
|
const wantsCarrierManagement = opts.manageWebhook !== false || wantsCloudflared;
|
|
984
1022
|
if (wantsCarrierManagement) {
|
|
985
|
-
const { autoConfigureCarrier } = await import("./carrier-config-
|
|
1023
|
+
const { autoConfigureCarrier } = await import("./carrier-config-7YGNRBPO.mjs");
|
|
986
1024
|
await autoConfigureCarrier({
|
|
987
1025
|
telephonyProvider,
|
|
988
1026
|
twilioSid: carrier.kind === "twilio" ? carrier.accountSid : void 0,
|
|
@@ -1020,7 +1058,8 @@ var Patter = class {
|
|
|
1020
1058
|
opts.onMetrics,
|
|
1021
1059
|
opts.pricing,
|
|
1022
1060
|
opts.dashboard ?? true,
|
|
1023
|
-
opts.dashboardToken ?? ""
|
|
1061
|
+
opts.dashboardToken ?? "",
|
|
1062
|
+
opts.allowInsecureDashboard ?? false
|
|
1024
1063
|
);
|
|
1025
1064
|
this.embeddedServer.popPrewarmAudio = this.popPrewarmAudio;
|
|
1026
1065
|
this.embeddedServer.popPrewarmedConnections = this.popPrewarmedConnections;
|
|
@@ -1039,7 +1078,7 @@ var Patter = class {
|
|
|
1039
1078
|
}
|
|
1040
1079
|
/** Run the agent in interactive terminal-test mode (no real telephony). */
|
|
1041
1080
|
async test(opts) {
|
|
1042
|
-
const { TestSession: TestSession2 } = await import("./test-mode-
|
|
1081
|
+
const { TestSession: TestSession2 } = await import("./test-mode-4QLLWYVV.mjs");
|
|
1043
1082
|
const session = new TestSession2();
|
|
1044
1083
|
await session.run({
|
|
1045
1084
|
agent: opts.agent,
|
|
@@ -1218,7 +1257,7 @@ var Patter = class {
|
|
|
1218
1257
|
}
|
|
1219
1258
|
if (wantsRealtimePark) {
|
|
1220
1259
|
tasks.push((async () => {
|
|
1221
|
-
const { OpenAIRealtime2Adapter: OpenAIRealtime2Adapter2 } = await import("./openai-realtime-2-
|
|
1260
|
+
const { OpenAIRealtime2Adapter: OpenAIRealtime2Adapter2 } = await import("./openai-realtime-2-L5EKAAUH.mjs");
|
|
1222
1261
|
const apiKey = process.env.OPENAI_API_KEY ?? "";
|
|
1223
1262
|
if (!apiKey) {
|
|
1224
1263
|
getLogger().debug(`Park OpenAI Realtime skipped for ${callId}: no OPENAI_API_KEY`);
|
|
@@ -1432,8 +1471,8 @@ var Patter = class {
|
|
|
1432
1471
|
if (!options.to) {
|
|
1433
1472
|
throw new Error("'to' phone number is required");
|
|
1434
1473
|
}
|
|
1435
|
-
if (
|
|
1436
|
-
throw new Error(
|
|
1474
|
+
if (!/^\+[1-9]\d{6,14}$/.test(options.to)) {
|
|
1475
|
+
throw new Error("'to' must be E.164 format (+<country><digits>). Got value with invalid format.");
|
|
1437
1476
|
}
|
|
1438
1477
|
if (options.wait && !this.embeddedServer) {
|
|
1439
1478
|
throw new PatterConnectionError(
|
|
@@ -1444,9 +1483,6 @@ var Patter = class {
|
|
|
1444
1483
|
let callId = "";
|
|
1445
1484
|
const effectiveRingTimeout = options.ringTimeout === void 0 ? 25 : options.ringTimeout;
|
|
1446
1485
|
const wantsAmd = options.machineDetection !== false || Boolean(options.voicemailMessage);
|
|
1447
|
-
if (this.embeddedServer) {
|
|
1448
|
-
this.embeddedServer.onMachineDetection = options.onMachineDetection;
|
|
1449
|
-
}
|
|
1450
1486
|
if (options.agent.prewarm !== false) {
|
|
1451
1487
|
this.spawnProviderWarmup(options.agent);
|
|
1452
1488
|
}
|
|
@@ -1491,6 +1527,12 @@ var Patter = class {
|
|
|
1491
1527
|
};
|
|
1492
1528
|
if (this.embeddedServer) {
|
|
1493
1529
|
this.embeddedServer.metricsStore.recordCallInitiated(initiatedPayload);
|
|
1530
|
+
if (options.onMachineDetection) {
|
|
1531
|
+
this.embeddedServer.onMachineDetectionByCallSid.set(
|
|
1532
|
+
telnyxCallId,
|
|
1533
|
+
options.onMachineDetection
|
|
1534
|
+
);
|
|
1535
|
+
}
|
|
1494
1536
|
}
|
|
1495
1537
|
try {
|
|
1496
1538
|
const { notifyDashboard: notifyDashboard2 } = await import("./persistence-LVIAHESK.mjs");
|
|
@@ -1556,6 +1598,12 @@ var Patter = class {
|
|
|
1556
1598
|
};
|
|
1557
1599
|
if (this.embeddedServer) {
|
|
1558
1600
|
this.embeddedServer.metricsStore.recordCallInitiated(initiatedPayload);
|
|
1601
|
+
if (options.onMachineDetection) {
|
|
1602
|
+
this.embeddedServer.onMachineDetectionByCallSid.set(
|
|
1603
|
+
plivoCallId,
|
|
1604
|
+
options.onMachineDetection
|
|
1605
|
+
);
|
|
1606
|
+
}
|
|
1559
1607
|
}
|
|
1560
1608
|
try {
|
|
1561
1609
|
const { notifyDashboard: notifyDashboard2 } = await import("./persistence-LVIAHESK.mjs");
|
|
@@ -1625,6 +1673,12 @@ var Patter = class {
|
|
|
1625
1673
|
};
|
|
1626
1674
|
if (this.embeddedServer) {
|
|
1627
1675
|
this.embeddedServer.metricsStore.recordCallInitiated(initiatedPayload);
|
|
1676
|
+
if (options.onMachineDetection) {
|
|
1677
|
+
this.embeddedServer.onMachineDetectionByCallSid.set(
|
|
1678
|
+
twilioCallSid,
|
|
1679
|
+
options.onMachineDetection
|
|
1680
|
+
);
|
|
1681
|
+
}
|
|
1628
1682
|
if (twilioNotificationsPath) {
|
|
1629
1683
|
getLogger().info(
|
|
1630
1684
|
`Outbound call ${twilioCallSid} placed. Twilio notifications: https://api.twilio.com${twilioNotificationsPath} (check here if the call drops with no audio).`
|
|
@@ -2105,8 +2159,8 @@ var FallbackLLMProvider = class {
|
|
|
2105
2159
|
* markers are filtered out so callers can concatenate the yielded strings
|
|
2106
2160
|
* directly.
|
|
2107
2161
|
*/
|
|
2108
|
-
async *completeStream(messages, tools) {
|
|
2109
|
-
for await (const chunk of this.stream(messages, tools)) {
|
|
2162
|
+
async *completeStream(messages, tools, opts) {
|
|
2163
|
+
for await (const chunk of this.stream(messages, tools, opts)) {
|
|
2110
2164
|
if (chunk.type === "text") {
|
|
2111
2165
|
yield chunk.content ?? "";
|
|
2112
2166
|
}
|
|
@@ -2116,14 +2170,15 @@ var FallbackLLMProvider = class {
|
|
|
2116
2170
|
// LLMProvider implementation
|
|
2117
2171
|
// -----------------------------------------------------------------------
|
|
2118
2172
|
/** Streaming entry point — yields chunks from the first provider that succeeds. */
|
|
2119
|
-
async *stream(messages, tools) {
|
|
2173
|
+
async *stream(messages, tools, opts) {
|
|
2120
2174
|
const errors = [];
|
|
2121
2175
|
const result = yield* this.tryProviders(
|
|
2122
2176
|
messages,
|
|
2123
2177
|
tools,
|
|
2124
2178
|
/* availableOnly */
|
|
2125
2179
|
true,
|
|
2126
|
-
errors
|
|
2180
|
+
errors,
|
|
2181
|
+
opts
|
|
2127
2182
|
);
|
|
2128
2183
|
if (result === "done") return;
|
|
2129
2184
|
getLogger().warn(
|
|
@@ -2134,7 +2189,8 @@ var FallbackLLMProvider = class {
|
|
|
2134
2189
|
tools,
|
|
2135
2190
|
/* availableOnly */
|
|
2136
2191
|
false,
|
|
2137
|
-
errors
|
|
2192
|
+
errors,
|
|
2193
|
+
opts
|
|
2138
2194
|
);
|
|
2139
2195
|
if (retryResult === "done") return;
|
|
2140
2196
|
throw new AllProvidersFailedError(
|
|
@@ -2144,7 +2200,7 @@ var FallbackLLMProvider = class {
|
|
|
2144
2200
|
// -----------------------------------------------------------------------
|
|
2145
2201
|
// Internals
|
|
2146
2202
|
// -----------------------------------------------------------------------
|
|
2147
|
-
async *tryProviders(messages, tools, availableOnly, errors) {
|
|
2203
|
+
async *tryProviders(messages, tools, availableOnly, errors, opts) {
|
|
2148
2204
|
for (let i = 0; i < this.providers.length; i++) {
|
|
2149
2205
|
if (availableOnly && !this.availability[i]) continue;
|
|
2150
2206
|
for (let attempt = 0; attempt < this.maxRetryPerProvider; attempt++) {
|
|
@@ -2153,7 +2209,7 @@ var FallbackLLMProvider = class {
|
|
|
2153
2209
|
`FallbackLLMProvider: trying provider ${i}${attempt > 0 ? ` (retry ${attempt})` : ""}`
|
|
2154
2210
|
);
|
|
2155
2211
|
let yieldedTokens = false;
|
|
2156
|
-
const gen = this.providers[i].stream(messages, tools);
|
|
2212
|
+
const gen = this.providers[i].stream(messages, tools, opts);
|
|
2157
2213
|
while (true) {
|
|
2158
2214
|
let iterResult;
|
|
2159
2215
|
try {
|
|
@@ -2273,6 +2329,11 @@ var PatterTool = class {
|
|
|
2273
2329
|
maxDurationSec;
|
|
2274
2330
|
recording;
|
|
2275
2331
|
started = false;
|
|
2332
|
+
/** Cached in-progress (or completed) start promise so concurrent execute()
|
|
2333
|
+
* callers all await the same boot sequence instead of each racing into
|
|
2334
|
+
* phone.serve(). Reset to null on failure so callers can retry after a
|
|
2335
|
+
* transient error. */
|
|
2336
|
+
startPromise = null;
|
|
2276
2337
|
constructor(opts) {
|
|
2277
2338
|
if (!opts.phone) {
|
|
2278
2339
|
throw new Error("PatterTool: `phone` (a Patter instance) is required.");
|
|
@@ -2324,8 +2385,21 @@ var PatterTool = class {
|
|
|
2324
2385
|
* `serve()` provides here. No `onCallEnd` callback is wired: the SDK's own
|
|
2325
2386
|
* per-callId completion registry resolves the result, so the user's
|
|
2326
2387
|
* `onCallEnd` slot is left free.
|
|
2388
|
+
*
|
|
2389
|
+
* Idempotent and concurrency-safe: concurrent callers all await the same
|
|
2390
|
+
* in-progress boot instead of each racing into `phone.serve()`.
|
|
2327
2391
|
*/
|
|
2328
2392
|
async start() {
|
|
2393
|
+
if (this.startPromise) return this.startPromise;
|
|
2394
|
+
this.startPromise = this._doStart();
|
|
2395
|
+
try {
|
|
2396
|
+
await this.startPromise;
|
|
2397
|
+
} catch (err) {
|
|
2398
|
+
this.startPromise = null;
|
|
2399
|
+
throw err;
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
async _doStart() {
|
|
2329
2403
|
if (this.started) return;
|
|
2330
2404
|
if (!this.agent) {
|
|
2331
2405
|
throw new Error(
|
|
@@ -2351,6 +2425,7 @@ var PatterTool = class {
|
|
|
2351
2425
|
}
|
|
2352
2426
|
}
|
|
2353
2427
|
this.started = false;
|
|
2428
|
+
this.startPromise = null;
|
|
2354
2429
|
}
|
|
2355
2430
|
// --- Execution ----------------------------------------------------------
|
|
2356
2431
|
/**
|
|
@@ -2715,7 +2790,8 @@ var UltravoxRealtimeAdapter = class {
|
|
|
2715
2790
|
"X-API-Key": this.apiKey,
|
|
2716
2791
|
"Content-Type": "application/json"
|
|
2717
2792
|
},
|
|
2718
|
-
body: JSON.stringify(body)
|
|
2793
|
+
body: JSON.stringify(body),
|
|
2794
|
+
signal: AbortSignal.timeout(15e3)
|
|
2719
2795
|
});
|
|
2720
2796
|
if (!resp.ok) {
|
|
2721
2797
|
const text = await resp.text().catch(() => "");
|
|
@@ -2726,12 +2802,36 @@ var UltravoxRealtimeAdapter = class {
|
|
|
2726
2802
|
this.ws = new WebSocket(call.joinUrl);
|
|
2727
2803
|
await new Promise((resolve, reject) => {
|
|
2728
2804
|
const ws = this.ws;
|
|
2805
|
+
let settled = false;
|
|
2806
|
+
const timer = setTimeout(() => {
|
|
2807
|
+
if (settled) return;
|
|
2808
|
+
settled = true;
|
|
2809
|
+
ws.off("open", onOpen);
|
|
2810
|
+
ws.off("error", onError);
|
|
2811
|
+
this.ws = null;
|
|
2812
|
+
try {
|
|
2813
|
+
ws.close();
|
|
2814
|
+
} catch {
|
|
2815
|
+
}
|
|
2816
|
+
reject(new Error("Ultravox WS connect timeout"));
|
|
2817
|
+
}, 15e3);
|
|
2729
2818
|
const onOpen = () => {
|
|
2819
|
+
if (settled) return;
|
|
2820
|
+
settled = true;
|
|
2821
|
+
clearTimeout(timer);
|
|
2730
2822
|
ws.off("error", onError);
|
|
2731
2823
|
resolve();
|
|
2732
2824
|
};
|
|
2733
2825
|
const onError = (err) => {
|
|
2826
|
+
if (settled) return;
|
|
2827
|
+
settled = true;
|
|
2828
|
+
clearTimeout(timer);
|
|
2734
2829
|
ws.off("open", onOpen);
|
|
2830
|
+
this.ws = null;
|
|
2831
|
+
try {
|
|
2832
|
+
ws.close();
|
|
2833
|
+
} catch {
|
|
2834
|
+
}
|
|
2735
2835
|
reject(err);
|
|
2736
2836
|
};
|
|
2737
2837
|
ws.once("open", onOpen);
|
|
@@ -3570,7 +3670,7 @@ var STT = class extends DeepgramSTT {
|
|
|
3570
3670
|
{
|
|
3571
3671
|
endpointingMs: opts.endpointingMs ?? 150,
|
|
3572
3672
|
utteranceEndMs: opts.utteranceEndMs === null ? null : opts.utteranceEndMs ?? 1e3,
|
|
3573
|
-
smartFormat: opts.smartFormat ??
|
|
3673
|
+
smartFormat: opts.smartFormat ?? false,
|
|
3574
3674
|
interimResults: opts.interimResults ?? true,
|
|
3575
3675
|
...opts.vadEvents !== void 0 ? { vadEvents: opts.vadEvents } : {}
|
|
3576
3676
|
}
|
|
@@ -3888,7 +3988,7 @@ var CartesiaSTT = class {
|
|
|
3888
3988
|
});
|
|
3889
3989
|
ws.once("error", (err) => {
|
|
3890
3990
|
clearTimeout(timer);
|
|
3891
|
-
reject(err);
|
|
3991
|
+
reject(new Error(`Cartesia STT park connect failed: ${describeWarmupError(err)}`));
|
|
3892
3992
|
});
|
|
3893
3993
|
});
|
|
3894
3994
|
return ws;
|
|
@@ -4243,7 +4343,7 @@ var SonioxSTT = class _SonioxSTT {
|
|
|
4243
4343
|
/** Stable pricing/dashboard key — read by stream-handler/metrics. */
|
|
4244
4344
|
static providerKey = "soniox";
|
|
4245
4345
|
ws = null;
|
|
4246
|
-
callbacks =
|
|
4346
|
+
callbacks = /* @__PURE__ */ new Set();
|
|
4247
4347
|
final = new TokenAccumulator();
|
|
4248
4348
|
keepaliveTimer = null;
|
|
4249
4349
|
apiKey;
|
|
@@ -4405,16 +4505,13 @@ var SonioxSTT = class _SonioxSTT {
|
|
|
4405
4505
|
if (audio.length === 0) return;
|
|
4406
4506
|
this.ws.send(audio);
|
|
4407
4507
|
}
|
|
4408
|
-
/** Register a transcript listener
|
|
4508
|
+
/** Register a transcript listener. */
|
|
4409
4509
|
onTranscript(callback) {
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
return;
|
|
4416
|
-
}
|
|
4417
|
-
this.callbacks.push(callback);
|
|
4510
|
+
this.callbacks.add(callback);
|
|
4511
|
+
}
|
|
4512
|
+
/** Unregister a previously registered transcript listener. */
|
|
4513
|
+
offTranscript(callback) {
|
|
4514
|
+
this.callbacks.delete(callback);
|
|
4418
4515
|
}
|
|
4419
4516
|
/** Send the empty-frame stream terminator and close the WebSocket. */
|
|
4420
4517
|
close() {
|
|
@@ -4495,12 +4592,6 @@ var VALID_DOMAINS = /* @__PURE__ */ new Set([
|
|
|
4495
4592
|
AssemblyAIDomain.GENERAL,
|
|
4496
4593
|
AssemblyAIDomain.MEDICAL_V1
|
|
4497
4594
|
]);
|
|
4498
|
-
var AssemblyAISTTNotConnectedError = class extends Error {
|
|
4499
|
-
constructor(message = "AssemblyAISTT is not connected") {
|
|
4500
|
-
super(message);
|
|
4501
|
-
this.name = "AssemblyAISTTNotConnectedError";
|
|
4502
|
-
}
|
|
4503
|
-
};
|
|
4504
4595
|
var AssemblyAISTT = class _AssemblyAISTT {
|
|
4505
4596
|
constructor(apiKey, options = {}) {
|
|
4506
4597
|
this.apiKey = apiKey;
|
|
@@ -4824,9 +4915,10 @@ var AssemblyAISTT = class _AssemblyAISTT {
|
|
|
4824
4915
|
*/
|
|
4825
4916
|
updateConfiguration(params) {
|
|
4826
4917
|
if (!this.ws || this.ws.readyState !== WebSocket4.OPEN) {
|
|
4827
|
-
|
|
4828
|
-
"AssemblyAISTT.updateConfiguration: WebSocket is not open"
|
|
4918
|
+
getLogger().debug(
|
|
4919
|
+
"AssemblyAISTT.updateConfiguration: WebSocket is not open \u2014 dropping update (call teardown)."
|
|
4829
4920
|
);
|
|
4921
|
+
return;
|
|
4830
4922
|
}
|
|
4831
4923
|
const payload = {
|
|
4832
4924
|
type: AssemblyAIClientFrame.UPDATE_CONFIGURATION
|
|
@@ -4848,9 +4940,10 @@ var AssemblyAISTT = class _AssemblyAISTT {
|
|
|
4848
4940
|
/** Force the server to finalize the current turn (for barge-in). */
|
|
4849
4941
|
forceEndpoint() {
|
|
4850
4942
|
if (!this.ws || this.ws.readyState !== WebSocket4.OPEN) {
|
|
4851
|
-
|
|
4852
|
-
"AssemblyAISTT.forceEndpoint: WebSocket is not open"
|
|
4943
|
+
getLogger().debug(
|
|
4944
|
+
"AssemblyAISTT.forceEndpoint: WebSocket is not open \u2014 dropping request (call teardown)."
|
|
4853
4945
|
);
|
|
4946
|
+
return;
|
|
4854
4947
|
}
|
|
4855
4948
|
this.ws.send(JSON.stringify({ type: AssemblyAIClientFrame.FORCE_ENDPOINT }));
|
|
4856
4949
|
}
|
|
@@ -4865,6 +4958,14 @@ var AssemblyAISTT = class _AssemblyAISTT {
|
|
|
4865
4958
|
async close() {
|
|
4866
4959
|
this.closing = true;
|
|
4867
4960
|
if (!this.ws) return;
|
|
4961
|
+
if (this.chunkBufferBytes > 0 && this.ws.readyState === WebSocket4.OPEN) {
|
|
4962
|
+
try {
|
|
4963
|
+
this.ws.send(Buffer.concat(this.chunkBuffer, this.chunkBufferBytes));
|
|
4964
|
+
} catch {
|
|
4965
|
+
}
|
|
4966
|
+
this.chunkBuffer = [];
|
|
4967
|
+
this.chunkBufferBytes = 0;
|
|
4968
|
+
}
|
|
4868
4969
|
try {
|
|
4869
4970
|
this.ws.send(JSON.stringify({ type: AssemblyAIClientFrame.TERMINATE }));
|
|
4870
4971
|
} catch {
|
|
@@ -6068,7 +6169,7 @@ var TTS3 = class extends OpenAITTS {
|
|
|
6068
6169
|
opts.model ?? "gpt-4o-mini-tts",
|
|
6069
6170
|
opts.instructions ?? null,
|
|
6070
6171
|
opts.speed ?? null,
|
|
6071
|
-
opts.antiAlias ??
|
|
6172
|
+
opts.antiAlias ?? true
|
|
6072
6173
|
);
|
|
6073
6174
|
}
|
|
6074
6175
|
};
|
|
@@ -6242,7 +6343,6 @@ init_esm_shims();
|
|
|
6242
6343
|
// src/providers/inworld-tts.ts
|
|
6243
6344
|
init_esm_shims();
|
|
6244
6345
|
var INWORLD_BASE_URL = "https://api.inworld.ai/tts/v1/voice:stream";
|
|
6245
|
-
var INWORLD_VOICES_URL = "https://api.inworld.ai/tts/v1/voices";
|
|
6246
6346
|
var InworldModel = {
|
|
6247
6347
|
TTS_2: "inworld-tts-2",
|
|
6248
6348
|
TTS_1_5_MAX: "inworld-tts-1.5-max",
|
|
@@ -6331,7 +6431,8 @@ var InworldTTS = class {
|
|
|
6331
6431
|
*/
|
|
6332
6432
|
async warmup() {
|
|
6333
6433
|
try {
|
|
6334
|
-
|
|
6434
|
+
const voicesUrl = new URL(this.baseUrl).origin + "/tts/v1/voices";
|
|
6435
|
+
await fetch(voicesUrl, {
|
|
6335
6436
|
method: "GET",
|
|
6336
6437
|
headers: {
|
|
6337
6438
|
Authorization: `Basic ${this.authToken}`
|
|
@@ -6588,58 +6689,87 @@ var AnthropicLLMProvider = class {
|
|
|
6588
6689
|
const toolIndexByBlock = /* @__PURE__ */ new Map();
|
|
6589
6690
|
const toolIdByBlock = /* @__PURE__ */ new Map();
|
|
6590
6691
|
let nextIndex = 0;
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
6600
|
-
const
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
continue;
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
id: toolId,
|
|
6619
|
-
name: toolName,
|
|
6620
|
-
arguments: ""
|
|
6621
|
-
};
|
|
6622
|
-
continue;
|
|
6623
|
-
}
|
|
6624
|
-
if (event.type === "content_block_delta") {
|
|
6625
|
-
if (event.delta?.type === "text_delta" && event.delta.text) {
|
|
6626
|
-
yield { type: "text", content: event.delta.text };
|
|
6692
|
+
let inputTokens = 0;
|
|
6693
|
+
let outputTokens = 0;
|
|
6694
|
+
let cacheReadTokens = 0;
|
|
6695
|
+
let cacheWriteTokens = 0;
|
|
6696
|
+
try {
|
|
6697
|
+
while (true) {
|
|
6698
|
+
const { done, value } = await reader.read();
|
|
6699
|
+
if (done) break;
|
|
6700
|
+
buffer += decoder.decode(value, { stream: true });
|
|
6701
|
+
const lines = buffer.split("\n");
|
|
6702
|
+
buffer = lines.pop() || "";
|
|
6703
|
+
for (const line of lines) {
|
|
6704
|
+
const trimmed = line.trim();
|
|
6705
|
+
if (!trimmed.startsWith("data: ")) continue;
|
|
6706
|
+
const data = trimmed.slice(6);
|
|
6707
|
+
if (!data || data === "[DONE]") continue;
|
|
6708
|
+
let event;
|
|
6709
|
+
try {
|
|
6710
|
+
event = JSON.parse(data);
|
|
6711
|
+
} catch {
|
|
6712
|
+
continue;
|
|
6713
|
+
}
|
|
6714
|
+
if (event.type === "message_start" && event.message?.usage) {
|
|
6715
|
+
const u = event.message.usage;
|
|
6716
|
+
if (u.input_tokens) inputTokens = u.input_tokens;
|
|
6717
|
+
if (u.cache_creation_input_tokens) cacheWriteTokens = u.cache_creation_input_tokens;
|
|
6718
|
+
if (u.cache_read_input_tokens) cacheReadTokens = u.cache_read_input_tokens;
|
|
6627
6719
|
continue;
|
|
6628
6720
|
}
|
|
6629
|
-
if (event.
|
|
6721
|
+
if (event.type === "message_delta" && event.usage?.output_tokens) {
|
|
6722
|
+
outputTokens = event.usage.output_tokens;
|
|
6723
|
+
continue;
|
|
6724
|
+
}
|
|
6725
|
+
if (event.type === "content_block_start" && event.content_block?.type === "tool_use") {
|
|
6630
6726
|
const blockIdx = event.index ?? 0;
|
|
6631
|
-
const
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6727
|
+
const toolId = event.content_block.id ?? "";
|
|
6728
|
+
const toolName = event.content_block.name ?? "";
|
|
6729
|
+
const patterIndex = nextIndex++;
|
|
6730
|
+
toolIndexByBlock.set(blockIdx, patterIndex);
|
|
6731
|
+
toolIdByBlock.set(blockIdx, toolId);
|
|
6732
|
+
yield {
|
|
6733
|
+
type: "tool_call",
|
|
6734
|
+
index: patterIndex,
|
|
6735
|
+
id: toolId,
|
|
6736
|
+
name: toolName,
|
|
6737
|
+
arguments: ""
|
|
6738
|
+
};
|
|
6739
|
+
continue;
|
|
6740
|
+
}
|
|
6741
|
+
if (event.type === "content_block_delta") {
|
|
6742
|
+
if (event.delta?.type === "text_delta" && event.delta.text) {
|
|
6743
|
+
yield { type: "text", content: event.delta.text };
|
|
6744
|
+
continue;
|
|
6745
|
+
}
|
|
6746
|
+
if (event.delta?.type === "input_json_delta" && event.delta.partial_json) {
|
|
6747
|
+
const blockIdx = event.index ?? 0;
|
|
6748
|
+
const patterIndex = toolIndexByBlock.get(blockIdx);
|
|
6749
|
+
if (patterIndex !== void 0) {
|
|
6750
|
+
yield {
|
|
6751
|
+
type: "tool_call",
|
|
6752
|
+
index: patterIndex,
|
|
6753
|
+
id: toolIdByBlock.get(blockIdx),
|
|
6754
|
+
arguments: event.delta.partial_json
|
|
6755
|
+
};
|
|
6756
|
+
}
|
|
6639
6757
|
}
|
|
6640
6758
|
}
|
|
6641
6759
|
}
|
|
6642
6760
|
}
|
|
6761
|
+
} finally {
|
|
6762
|
+
reader.cancel().catch(() => {
|
|
6763
|
+
});
|
|
6764
|
+
}
|
|
6765
|
+
if (inputTokens > 0 || outputTokens > 0 || cacheReadTokens > 0 || cacheWriteTokens > 0) {
|
|
6766
|
+
yield {
|
|
6767
|
+
type: "usage",
|
|
6768
|
+
inputTokens,
|
|
6769
|
+
outputTokens,
|
|
6770
|
+
cacheReadInputTokens: cacheReadTokens,
|
|
6771
|
+
cacheWriteInputTokens: cacheWriteTokens
|
|
6772
|
+
};
|
|
6643
6773
|
}
|
|
6644
6774
|
yield { type: "done" };
|
|
6645
6775
|
}
|
|
@@ -6699,16 +6829,17 @@ function toAnthropicMessages(messages) {
|
|
|
6699
6829
|
}
|
|
6700
6830
|
if (role === "tool") {
|
|
6701
6831
|
const contentStr = typeof rawMsg.content === "string" ? rawMsg.content : JSON.stringify(rawMsg.content);
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6832
|
+
const toolResultBlock = {
|
|
6833
|
+
type: "tool_result",
|
|
6834
|
+
tool_use_id: rawMsg.tool_call_id ?? "",
|
|
6835
|
+
content: contentStr
|
|
6836
|
+
};
|
|
6837
|
+
const prev = out.length > 0 ? out[out.length - 1] : void 0;
|
|
6838
|
+
if (prev && prev.role === "user" && Array.isArray(prev.content) && prev.content.length > 0 && prev.content.every((b) => b["type"] === "tool_result")) {
|
|
6839
|
+
prev.content.push(toolResultBlock);
|
|
6840
|
+
} else {
|
|
6841
|
+
out.push({ role: "user", content: [toolResultBlock] });
|
|
6842
|
+
}
|
|
6712
6843
|
continue;
|
|
6713
6844
|
}
|
|
6714
6845
|
}
|
|
@@ -6848,50 +6979,55 @@ async function* parseOpenAISseStream(response) {
|
|
|
6848
6979
|
if (!reader) return;
|
|
6849
6980
|
const decoder = new TextDecoder();
|
|
6850
6981
|
let buffer = "";
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
const
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
type: "usage",
|
|
6873
|
-
inputTokens: usage.prompt_tokens,
|
|
6874
|
-
outputTokens: usage.completion_tokens,
|
|
6875
|
-
cacheReadInputTokens: cached
|
|
6876
|
-
};
|
|
6877
|
-
}
|
|
6878
|
-
const delta = chunk.choices?.[0]?.delta;
|
|
6879
|
-
if (!delta) continue;
|
|
6880
|
-
if (delta.content) {
|
|
6881
|
-
yield { type: "text", content: delta.content };
|
|
6882
|
-
}
|
|
6883
|
-
if (delta.tool_calls) {
|
|
6884
|
-
for (const tc of delta.tool_calls) {
|
|
6982
|
+
try {
|
|
6983
|
+
while (true) {
|
|
6984
|
+
const { done, value } = await reader.read();
|
|
6985
|
+
if (done) break;
|
|
6986
|
+
buffer += decoder.decode(value, { stream: true });
|
|
6987
|
+
const lines = buffer.split("\n");
|
|
6988
|
+
buffer = lines.pop() || "";
|
|
6989
|
+
for (const line of lines) {
|
|
6990
|
+
const trimmed = line.trim();
|
|
6991
|
+
if (!trimmed || !trimmed.startsWith("data: ")) continue;
|
|
6992
|
+
const data = trimmed.slice(6);
|
|
6993
|
+
if (data === "[DONE]") continue;
|
|
6994
|
+
let chunk;
|
|
6995
|
+
try {
|
|
6996
|
+
chunk = JSON.parse(data);
|
|
6997
|
+
} catch {
|
|
6998
|
+
continue;
|
|
6999
|
+
}
|
|
7000
|
+
const usage = chunk.usage ?? chunk.x_groq?.usage;
|
|
7001
|
+
if (usage) {
|
|
7002
|
+
const cached = chunk.usage?.prompt_tokens_details?.cached_tokens ?? 0;
|
|
6885
7003
|
yield {
|
|
6886
|
-
type: "
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
arguments: tc.function?.arguments
|
|
7004
|
+
type: "usage",
|
|
7005
|
+
inputTokens: usage.prompt_tokens,
|
|
7006
|
+
outputTokens: usage.completion_tokens,
|
|
7007
|
+
cacheReadInputTokens: cached
|
|
6891
7008
|
};
|
|
6892
7009
|
}
|
|
7010
|
+
const delta = chunk.choices?.[0]?.delta;
|
|
7011
|
+
if (!delta) continue;
|
|
7012
|
+
if (delta.content) {
|
|
7013
|
+
yield { type: "text", content: delta.content };
|
|
7014
|
+
}
|
|
7015
|
+
if (delta.tool_calls) {
|
|
7016
|
+
for (const tc of delta.tool_calls) {
|
|
7017
|
+
yield {
|
|
7018
|
+
type: "tool_call",
|
|
7019
|
+
index: tc.index,
|
|
7020
|
+
id: tc.id,
|
|
7021
|
+
name: tc.function?.name,
|
|
7022
|
+
arguments: tc.function?.arguments
|
|
7023
|
+
};
|
|
7024
|
+
}
|
|
7025
|
+
}
|
|
6893
7026
|
}
|
|
6894
7027
|
}
|
|
7028
|
+
} finally {
|
|
7029
|
+
reader.cancel().catch(() => {
|
|
7030
|
+
});
|
|
6895
7031
|
}
|
|
6896
7032
|
}
|
|
6897
7033
|
|
|
@@ -7056,11 +7192,21 @@ var CerebrasLLMProvider = class {
|
|
|
7056
7192
|
}
|
|
7057
7193
|
const advisoryMs = parseRateLimitResetMs(response.headers);
|
|
7058
7194
|
const exponentialMs = RETRY_BACKOFF_BASE_MS * Math.pow(2, attempt);
|
|
7059
|
-
const delayMs = Math.max(advisoryMs, exponentialMs);
|
|
7195
|
+
const delayMs = Math.min(5e3, Math.max(advisoryMs, exponentialMs));
|
|
7060
7196
|
getLogger().warn(
|
|
7061
7197
|
`Cerebras API ${response.status} (attempt ${attempt + 1}/${maxAttempts}); retrying after ${delayMs}ms`
|
|
7062
7198
|
);
|
|
7063
|
-
await new Promise((
|
|
7199
|
+
await new Promise((resolve, reject) => {
|
|
7200
|
+
const t = setTimeout(resolve, delayMs);
|
|
7201
|
+
opts?.signal?.addEventListener(
|
|
7202
|
+
"abort",
|
|
7203
|
+
() => {
|
|
7204
|
+
clearTimeout(t);
|
|
7205
|
+
reject(opts.signal.reason);
|
|
7206
|
+
},
|
|
7207
|
+
{ once: true }
|
|
7208
|
+
);
|
|
7209
|
+
});
|
|
7064
7210
|
}
|
|
7065
7211
|
throw new PatterError(`Cerebras API error ${lastStatus}: ${lastErrText || "request failed"}`);
|
|
7066
7212
|
}
|
|
@@ -7221,47 +7367,52 @@ var GoogleLLMProvider = class {
|
|
|
7221
7367
|
let buffer = "";
|
|
7222
7368
|
let nextIndex = 0;
|
|
7223
7369
|
let lastUsage;
|
|
7224
|
-
|
|
7225
|
-
|
|
7226
|
-
|
|
7227
|
-
|
|
7228
|
-
|
|
7229
|
-
|
|
7230
|
-
|
|
7231
|
-
const
|
|
7232
|
-
|
|
7233
|
-
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
|
|
7239
|
-
|
|
7240
|
-
}
|
|
7241
|
-
if (payload.usageMetadata) {
|
|
7242
|
-
lastUsage = payload.usageMetadata;
|
|
7243
|
-
}
|
|
7244
|
-
const candidate = payload.candidates?.[0];
|
|
7245
|
-
const parts = candidate?.content?.parts ?? [];
|
|
7246
|
-
for (const part of parts) {
|
|
7247
|
-
if (part.functionCall) {
|
|
7248
|
-
const args = part.functionCall.args ?? {};
|
|
7249
|
-
const callId = part.functionCall.id ?? `gemini_call_${nextIndex}`;
|
|
7250
|
-
yield {
|
|
7251
|
-
type: "tool_call",
|
|
7252
|
-
index: nextIndex,
|
|
7253
|
-
id: callId,
|
|
7254
|
-
name: part.functionCall.name ?? "",
|
|
7255
|
-
arguments: JSON.stringify(args)
|
|
7256
|
-
};
|
|
7257
|
-
nextIndex++;
|
|
7370
|
+
try {
|
|
7371
|
+
while (true) {
|
|
7372
|
+
const { done, value } = await reader.read();
|
|
7373
|
+
if (done) break;
|
|
7374
|
+
buffer += decoder.decode(value, { stream: true });
|
|
7375
|
+
const lines = buffer.split("\n");
|
|
7376
|
+
buffer = lines.pop() || "";
|
|
7377
|
+
for (const line of lines) {
|
|
7378
|
+
const trimmed = line.trim();
|
|
7379
|
+
if (!trimmed.startsWith("data: ")) continue;
|
|
7380
|
+
const data = trimmed.slice(6);
|
|
7381
|
+
if (!data) continue;
|
|
7382
|
+
let payload;
|
|
7383
|
+
try {
|
|
7384
|
+
payload = JSON.parse(data);
|
|
7385
|
+
} catch {
|
|
7258
7386
|
continue;
|
|
7259
7387
|
}
|
|
7260
|
-
if (
|
|
7261
|
-
|
|
7388
|
+
if (payload.usageMetadata) {
|
|
7389
|
+
lastUsage = payload.usageMetadata;
|
|
7390
|
+
}
|
|
7391
|
+
const candidate = payload.candidates?.[0];
|
|
7392
|
+
const parts = candidate?.content?.parts ?? [];
|
|
7393
|
+
for (const part of parts) {
|
|
7394
|
+
if (part.functionCall) {
|
|
7395
|
+
const args = part.functionCall.args ?? {};
|
|
7396
|
+
const callId = part.functionCall.id ?? `gemini_call_${nextIndex}`;
|
|
7397
|
+
yield {
|
|
7398
|
+
type: "tool_call",
|
|
7399
|
+
index: nextIndex,
|
|
7400
|
+
id: callId,
|
|
7401
|
+
name: part.functionCall.name ?? "",
|
|
7402
|
+
arguments: JSON.stringify(args)
|
|
7403
|
+
};
|
|
7404
|
+
nextIndex++;
|
|
7405
|
+
continue;
|
|
7406
|
+
}
|
|
7407
|
+
if (part.text) {
|
|
7408
|
+
yield { type: "text", content: part.text };
|
|
7409
|
+
}
|
|
7262
7410
|
}
|
|
7263
7411
|
}
|
|
7264
7412
|
}
|
|
7413
|
+
} finally {
|
|
7414
|
+
reader.cancel().catch(() => {
|
|
7415
|
+
});
|
|
7265
7416
|
}
|
|
7266
7417
|
if (lastUsage) {
|
|
7267
7418
|
yield {
|
|
@@ -7355,7 +7506,17 @@ function toGeminiContents(messages) {
|
|
|
7355
7506
|
continue;
|
|
7356
7507
|
}
|
|
7357
7508
|
}
|
|
7358
|
-
|
|
7509
|
+
const merged = [];
|
|
7510
|
+
for (const entry of contents) {
|
|
7511
|
+
const prev = merged[merged.length - 1];
|
|
7512
|
+
const isFunctionResponseOnly = (c) => c.role === "user" && c.parts.every((p) => p.functionResponse !== void 0);
|
|
7513
|
+
if (prev && isFunctionResponseOnly(prev) && isFunctionResponseOnly(entry)) {
|
|
7514
|
+
prev.parts.push(...entry.parts);
|
|
7515
|
+
} else {
|
|
7516
|
+
merged.push(entry);
|
|
7517
|
+
}
|
|
7518
|
+
}
|
|
7519
|
+
return { systemInstruction: systemParts.join("\n\n"), contents: merged };
|
|
7359
7520
|
}
|
|
7360
7521
|
|
|
7361
7522
|
// src/llm/google.ts
|
|
@@ -7409,6 +7570,57 @@ function float32ToPcm16(samples) {
|
|
|
7409
7570
|
}
|
|
7410
7571
|
return out;
|
|
7411
7572
|
}
|
|
7573
|
+
var ArbitraryResampler = class {
|
|
7574
|
+
srcRate;
|
|
7575
|
+
dstRate;
|
|
7576
|
+
phase = 0;
|
|
7577
|
+
// fractional position into the current chunk
|
|
7578
|
+
lastSample = 0;
|
|
7579
|
+
// last input sample from the previous chunk
|
|
7580
|
+
hasHistory = false;
|
|
7581
|
+
constructor(srcRate, dstRate) {
|
|
7582
|
+
this.srcRate = srcRate;
|
|
7583
|
+
this.dstRate = dstRate;
|
|
7584
|
+
}
|
|
7585
|
+
/** Process a chunk of PCM16-LE mono audio and return resampled PCM16-LE. */
|
|
7586
|
+
process(pcm) {
|
|
7587
|
+
const sampleCount = Math.floor(pcm.length / 2);
|
|
7588
|
+
if (sampleCount === 0) return Buffer.alloc(0);
|
|
7589
|
+
const step = this.srcRate / this.dstRate;
|
|
7590
|
+
const outArr = [];
|
|
7591
|
+
let phase = this.phase;
|
|
7592
|
+
while (true) {
|
|
7593
|
+
const idx = Math.floor(phase);
|
|
7594
|
+
if (idx >= sampleCount) break;
|
|
7595
|
+
const frac = phase - idx;
|
|
7596
|
+
let s0;
|
|
7597
|
+
let s1;
|
|
7598
|
+
if (idx < 0) {
|
|
7599
|
+
s0 = this.hasHistory ? this.lastSample : 0;
|
|
7600
|
+
s1 = pcm.readInt16LE(0);
|
|
7601
|
+
} else {
|
|
7602
|
+
s0 = pcm.readInt16LE(idx * 2);
|
|
7603
|
+
s1 = idx + 1 < sampleCount ? pcm.readInt16LE((idx + 1) * 2) : s0;
|
|
7604
|
+
}
|
|
7605
|
+
const interp = Math.round(s0 + (s1 - s0) * frac);
|
|
7606
|
+
outArr.push(Math.max(-32768, Math.min(32767, interp)));
|
|
7607
|
+
phase += step;
|
|
7608
|
+
}
|
|
7609
|
+
this.lastSample = pcm.readInt16LE((sampleCount - 1) * 2);
|
|
7610
|
+
this.hasHistory = true;
|
|
7611
|
+
this.phase = phase - sampleCount;
|
|
7612
|
+
const out = Buffer.alloc(outArr.length * 2);
|
|
7613
|
+
for (let j = 0; j < outArr.length; j++) out.writeInt16LE(outArr[j], j * 2);
|
|
7614
|
+
return out;
|
|
7615
|
+
}
|
|
7616
|
+
/** Flush any buffered state and reset. Returns any remaining tail output. */
|
|
7617
|
+
flush() {
|
|
7618
|
+
this.phase = 0;
|
|
7619
|
+
this.lastSample = 0;
|
|
7620
|
+
this.hasHistory = false;
|
|
7621
|
+
return Buffer.alloc(0);
|
|
7622
|
+
}
|
|
7623
|
+
};
|
|
7412
7624
|
var DeepFilterNetFilter = class {
|
|
7413
7625
|
modelPath;
|
|
7414
7626
|
silenceWarnings;
|
|
@@ -7416,8 +7628,9 @@ var DeepFilterNetFilter = class {
|
|
|
7416
7628
|
ort = null;
|
|
7417
7629
|
warned = false;
|
|
7418
7630
|
closed = false;
|
|
7419
|
-
//
|
|
7631
|
+
// Stateful resamplers for src_sr↔48k conversions so chunk-boundary
|
|
7420
7632
|
// samples are not discarded. Lazy-created and torn down on rate change.
|
|
7633
|
+
// Uses ArbitraryResampler which supports any integer rate pair.
|
|
7421
7634
|
_resamplerSrcRate = null;
|
|
7422
7635
|
_upsamplerInst = null;
|
|
7423
7636
|
_downsamplerInst = null;
|
|
@@ -7475,8 +7688,8 @@ var DeepFilterNetFilter = class {
|
|
|
7475
7688
|
try {
|
|
7476
7689
|
if (this._resamplerSrcRate !== sampleRate) {
|
|
7477
7690
|
this._resamplerSrcRate = sampleRate;
|
|
7478
|
-
this._upsamplerInst = new
|
|
7479
|
-
this._downsamplerInst = new
|
|
7691
|
+
this._upsamplerInst = new ArbitraryResampler(sampleRate, DEEPFILTERNET_SR);
|
|
7692
|
+
this._downsamplerInst = new ArbitraryResampler(DEEPFILTERNET_SR, sampleRate);
|
|
7480
7693
|
}
|
|
7481
7694
|
const samples = pcm16ToFloat32(pcmChunk);
|
|
7482
7695
|
const pcm16Up = this._upsamplerInst.process(float32ToPcm16(new Float32Array(samples)));
|
|
@@ -7636,6 +7849,17 @@ var Tool = class {
|
|
|
7636
7849
|
parameters;
|
|
7637
7850
|
handler;
|
|
7638
7851
|
webhookUrl;
|
|
7852
|
+
reassurance;
|
|
7853
|
+
/**
|
|
7854
|
+
* Per-tool execution timeout in milliseconds. `undefined` uses the
|
|
7855
|
+
* executor default (10 000 ms). Mirrors Python `timeout_s`.
|
|
7856
|
+
*/
|
|
7857
|
+
timeoutMs;
|
|
7858
|
+
/**
|
|
7859
|
+
* Enable OpenAI strict mode for this tool's function schema. Off by
|
|
7860
|
+
* default. Mirrors Python `strict` on `Tool`.
|
|
7861
|
+
*/
|
|
7862
|
+
strict;
|
|
7639
7863
|
constructor(opts) {
|
|
7640
7864
|
if (!opts.name) {
|
|
7641
7865
|
throw new Error("Tool requires a non-empty name.");
|
|
@@ -7653,6 +7877,9 @@ var Tool = class {
|
|
|
7653
7877
|
this.parameters = opts.parameters ?? { type: "object", properties: {} };
|
|
7654
7878
|
if (hasHandler) this.handler = opts.handler;
|
|
7655
7879
|
if (hasWebhook) this.webhookUrl = opts.webhookUrl;
|
|
7880
|
+
if (opts.reassurance !== void 0) this.reassurance = opts.reassurance;
|
|
7881
|
+
if (opts.timeoutMs !== void 0) this.timeoutMs = opts.timeoutMs;
|
|
7882
|
+
if (opts.strict !== void 0) this.strict = opts.strict;
|
|
7656
7883
|
}
|
|
7657
7884
|
};
|
|
7658
7885
|
function tool(opts) {
|
|
@@ -7811,7 +8038,6 @@ var ChatContext = class _ChatContext {
|
|
|
7811
8038
|
// src/services/ivr.ts
|
|
7812
8039
|
init_esm_shims();
|
|
7813
8040
|
var DTMF_EVENTS = [
|
|
7814
|
-
"0",
|
|
7815
8041
|
"1",
|
|
7816
8042
|
"2",
|
|
7817
8043
|
"3",
|
|
@@ -7821,6 +8047,7 @@ var DTMF_EVENTS = [
|
|
|
7821
8047
|
"7",
|
|
7822
8048
|
"8",
|
|
7823
8049
|
"9",
|
|
8050
|
+
"0",
|
|
7824
8051
|
"*",
|
|
7825
8052
|
"#",
|
|
7826
8053
|
"A",
|
|
@@ -8497,18 +8724,24 @@ var TelnyxAdapter = class {
|
|
|
8497
8724
|
"/number_orders",
|
|
8498
8725
|
orderBody
|
|
8499
8726
|
);
|
|
8500
|
-
const orderId = order.data?.id
|
|
8727
|
+
const orderId = order.data?.id;
|
|
8728
|
+
if (!orderId) throw new Error("TelnyxAdapter: /number_orders returned no order id");
|
|
8501
8729
|
return { phoneNumber: chosen, orderId };
|
|
8502
8730
|
}
|
|
8503
8731
|
/** Attach a number to a Call Control Application. */
|
|
8504
8732
|
async configureNumber(phoneNumber, opts) {
|
|
8505
8733
|
if (!phoneNumber) throw new Error("TelnyxAdapter: phoneNumber is required");
|
|
8506
8734
|
if (!opts.connectionId) throw new Error("TelnyxAdapter: connectionId is required");
|
|
8507
|
-
|
|
8508
|
-
|
|
8509
|
-
|
|
8510
|
-
|
|
8511
|
-
|
|
8735
|
+
try {
|
|
8736
|
+
await this.request(
|
|
8737
|
+
"PATCH",
|
|
8738
|
+
`/phone_numbers/${encodeURIComponent(phoneNumber)}/voice`,
|
|
8739
|
+
{ connection_id: opts.connectionId, tech_prefix_enabled: false }
|
|
8740
|
+
);
|
|
8741
|
+
} catch (err) {
|
|
8742
|
+
const status = err instanceof Error ? err.message.replace(/\+\d{7,15}/g, "[REDACTED]") : String(err);
|
|
8743
|
+
throw new Error(`TelnyxAdapter: configureNumber failed: ${status}`);
|
|
8744
|
+
}
|
|
8512
8745
|
}
|
|
8513
8746
|
/**
|
|
8514
8747
|
* Place an outbound call on the Call Control Application.
|
|
@@ -8612,7 +8845,7 @@ var TelnyxSTT = class {
|
|
|
8612
8845
|
/** Stable pricing/dashboard key — read by stream-handler/metrics. */
|
|
8613
8846
|
static providerKey = "telnyx_stt";
|
|
8614
8847
|
ws = null;
|
|
8615
|
-
callbacks =
|
|
8848
|
+
callbacks = /* @__PURE__ */ new Set();
|
|
8616
8849
|
headerSent = false;
|
|
8617
8850
|
/** Open the streaming WebSocket and arm message handlers. */
|
|
8618
8851
|
async connect() {
|
|
@@ -8668,14 +8901,13 @@ var TelnyxSTT = class {
|
|
|
8668
8901
|
}
|
|
8669
8902
|
this.ws.send(audio);
|
|
8670
8903
|
}
|
|
8671
|
-
/** Register a transcript listener
|
|
8904
|
+
/** Register a transcript listener. */
|
|
8672
8905
|
onTranscript(callback) {
|
|
8673
|
-
|
|
8674
|
-
|
|
8675
|
-
|
|
8676
|
-
|
|
8677
|
-
|
|
8678
|
-
this.callbacks.push(callback);
|
|
8906
|
+
this.callbacks.add(callback);
|
|
8907
|
+
}
|
|
8908
|
+
/** Unregister a previously-registered transcript listener. */
|
|
8909
|
+
offTranscript(callback) {
|
|
8910
|
+
this.callbacks.delete(callback);
|
|
8679
8911
|
}
|
|
8680
8912
|
/** Close the streaming WebSocket. */
|
|
8681
8913
|
close() {
|
|
@@ -8686,6 +8918,7 @@ var TelnyxSTT = class {
|
|
|
8686
8918
|
}
|
|
8687
8919
|
this.ws = null;
|
|
8688
8920
|
}
|
|
8921
|
+
this.headerSent = false;
|
|
8689
8922
|
}
|
|
8690
8923
|
};
|
|
8691
8924
|
|
|
@@ -8706,6 +8939,7 @@ var TelnyxTTSSampleRate = {
|
|
|
8706
8939
|
HZ_24000: 24e3
|
|
8707
8940
|
};
|
|
8708
8941
|
var DEFAULT_VOICE = TelnyxTTSVoice.NATURAL_HD_ASTRA;
|
|
8942
|
+
var FRAME_TIMEOUT_MS2 = 3e4;
|
|
8709
8943
|
var TelnyxTTS = class {
|
|
8710
8944
|
constructor(apiKey, voice = DEFAULT_VOICE, baseUrl = TELNYX_TTS_WS_URL) {
|
|
8711
8945
|
this.apiKey = apiKey;
|
|
@@ -8733,69 +8967,83 @@ var TelnyxTTS = class {
|
|
|
8733
8967
|
*/
|
|
8734
8968
|
async *synthesizeStream(text) {
|
|
8735
8969
|
const url = `${this.baseUrl}?voice=${encodeURIComponent(this.voice)}`;
|
|
8736
|
-
|
|
8737
|
-
|
|
8738
|
-
|
|
8739
|
-
|
|
8740
|
-
|
|
8741
|
-
|
|
8742
|
-
|
|
8743
|
-
|
|
8970
|
+
let ws = null;
|
|
8971
|
+
try {
|
|
8972
|
+
let push2 = function(item) {
|
|
8973
|
+
const w = waiters.shift();
|
|
8974
|
+
if (w) {
|
|
8975
|
+
w(item);
|
|
8976
|
+
} else {
|
|
8977
|
+
queue.push(item);
|
|
8978
|
+
}
|
|
8979
|
+
};
|
|
8980
|
+
var push = push2;
|
|
8981
|
+
ws = new WebSocket8(url, {
|
|
8982
|
+
headers: { Authorization: `Bearer ${this.apiKey}` }
|
|
8744
8983
|
});
|
|
8745
|
-
|
|
8746
|
-
|
|
8747
|
-
|
|
8984
|
+
await new Promise((resolve, reject) => {
|
|
8985
|
+
const timer = setTimeout(() => reject(new Error("Telnyx TTS connect timeout")), 1e4);
|
|
8986
|
+
ws.once("open", () => {
|
|
8987
|
+
clearTimeout(timer);
|
|
8988
|
+
resolve();
|
|
8989
|
+
});
|
|
8990
|
+
ws.once("error", (err) => {
|
|
8991
|
+
clearTimeout(timer);
|
|
8992
|
+
reject(err);
|
|
8993
|
+
});
|
|
8748
8994
|
});
|
|
8749
|
-
|
|
8750
|
-
|
|
8751
|
-
|
|
8752
|
-
|
|
8753
|
-
|
|
8754
|
-
|
|
8755
|
-
|
|
8756
|
-
|
|
8757
|
-
|
|
8758
|
-
}
|
|
8759
|
-
}
|
|
8760
|
-
ws.on("message", (raw) => {
|
|
8761
|
-
let data;
|
|
8762
|
-
try {
|
|
8763
|
-
data = JSON.parse(raw.toString());
|
|
8764
|
-
} catch {
|
|
8765
|
-
getLogger().warn("TelnyxTTS: received invalid JSON");
|
|
8766
|
-
return;
|
|
8767
|
-
}
|
|
8768
|
-
const audioB64 = data.audio;
|
|
8769
|
-
if (!audioB64) return;
|
|
8770
|
-
try {
|
|
8771
|
-
const audioBytes = Buffer.from(audioB64, "base64");
|
|
8772
|
-
if (audioBytes.length > 0) {
|
|
8773
|
-
push(audioBytes);
|
|
8995
|
+
const queue = [];
|
|
8996
|
+
const waiters = [];
|
|
8997
|
+
ws.on("message", (raw) => {
|
|
8998
|
+
let data;
|
|
8999
|
+
try {
|
|
9000
|
+
data = JSON.parse(raw.toString());
|
|
9001
|
+
} catch {
|
|
9002
|
+
getLogger().warn("TelnyxTTS: received invalid JSON");
|
|
9003
|
+
return;
|
|
8774
9004
|
}
|
|
8775
|
-
|
|
8776
|
-
|
|
8777
|
-
|
|
8778
|
-
|
|
8779
|
-
|
|
8780
|
-
|
|
8781
|
-
|
|
8782
|
-
|
|
8783
|
-
|
|
8784
|
-
|
|
8785
|
-
|
|
8786
|
-
|
|
8787
|
-
|
|
9005
|
+
const audioB64 = data.audio;
|
|
9006
|
+
if (!audioB64) return;
|
|
9007
|
+
try {
|
|
9008
|
+
const audioBytes = Buffer.from(audioB64, "base64");
|
|
9009
|
+
if (audioBytes.length > 0) {
|
|
9010
|
+
push2(audioBytes);
|
|
9011
|
+
}
|
|
9012
|
+
} catch {
|
|
9013
|
+
}
|
|
9014
|
+
});
|
|
9015
|
+
ws.on("close", () => {
|
|
9016
|
+
push2(null);
|
|
9017
|
+
});
|
|
9018
|
+
ws.on("error", (err) => {
|
|
9019
|
+
push2({ error: err instanceof Error ? err : new Error(String(err)) });
|
|
9020
|
+
});
|
|
9021
|
+
ws.send(JSON.stringify({ text: " " }));
|
|
9022
|
+
ws.send(JSON.stringify({ text }));
|
|
9023
|
+
ws.send(JSON.stringify({ text: "" }));
|
|
8788
9024
|
while (true) {
|
|
8789
|
-
|
|
9025
|
+
let frameTimer;
|
|
9026
|
+
const item = queue.length > 0 ? queue.shift() : await Promise.race([
|
|
9027
|
+
new Promise((resolve) => waiters.push(resolve)),
|
|
9028
|
+
new Promise((_, reject) => {
|
|
9029
|
+
frameTimer = setTimeout(
|
|
9030
|
+
() => reject(new Error("Telnyx TTS frame timeout")),
|
|
9031
|
+
FRAME_TIMEOUT_MS2
|
|
9032
|
+
);
|
|
9033
|
+
})
|
|
9034
|
+
]).finally(() => {
|
|
9035
|
+
if (frameTimer !== void 0) clearTimeout(frameTimer);
|
|
9036
|
+
});
|
|
8790
9037
|
if (item === null) return;
|
|
8791
9038
|
if (typeof item === "object" && "error" in item) throw item.error;
|
|
8792
9039
|
yield item;
|
|
8793
9040
|
}
|
|
8794
9041
|
} finally {
|
|
8795
9042
|
try {
|
|
8796
|
-
ws
|
|
9043
|
+
ws?.close();
|
|
8797
9044
|
} catch {
|
|
8798
9045
|
}
|
|
9046
|
+
ws?.removeAllListeners();
|
|
8799
9047
|
}
|
|
8800
9048
|
}
|
|
8801
9049
|
};
|
|
@@ -8867,6 +9115,7 @@ export {
|
|
|
8867
9115
|
PRICING_VERSION,
|
|
8868
9116
|
PartialStreamError,
|
|
8869
9117
|
Patter,
|
|
9118
|
+
PatterConfigError,
|
|
8870
9119
|
PatterConnectionError,
|
|
8871
9120
|
PatterError,
|
|
8872
9121
|
PatterTool,
|
|
@@ -8954,6 +9203,8 @@ export {
|
|
|
8954
9203
|
mulawToPcm16,
|
|
8955
9204
|
notifyDashboard,
|
|
8956
9205
|
openaiTts,
|
|
9206
|
+
openclawConsult,
|
|
9207
|
+
openclawPostCallNotifier,
|
|
8957
9208
|
pcm16ToMulaw,
|
|
8958
9209
|
resample16kTo8k,
|
|
8959
9210
|
resample24kTo16k,
|