getpatter 0.6.3 → 0.6.5
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-CL2U3YET.mjs → chunk-BO227NTF.mjs} +271 -54
- package/dist/{chunk-Z6W5XFWS.mjs → chunk-CRPJLVHB.mjs} +992 -197
- package/dist/cli.js +63 -20
- package/dist/dashboard/ui.html +10 -10
- package/dist/index.d.mts +1250 -192
- package/dist/index.d.ts +1250 -192
- package/dist/index.js +2062 -518
- package/dist/index.mjs +759 -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-HGHI2AUV.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-CRPJLVHB.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-HGHI2AUV.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
|
|
@@ -7378,6 +7539,260 @@ var LLM5 = class extends GoogleLLMProvider {
|
|
|
7378
7539
|
}
|
|
7379
7540
|
};
|
|
7380
7541
|
|
|
7542
|
+
// src/llm/openai-compatible.ts
|
|
7543
|
+
init_esm_shims();
|
|
7544
|
+
var DEFAULT_TIMEOUT_S = 60;
|
|
7545
|
+
var OpenAICompatibleLLMProvider = class {
|
|
7546
|
+
/**
|
|
7547
|
+
* Stable pricing/dashboard key — read by stream-handler/metrics. Typed as
|
|
7548
|
+
* ``string`` (not the narrowed literal) so the Hermes / OpenClaw presets can
|
|
7549
|
+
* override it with their own key while still extending this class.
|
|
7550
|
+
*/
|
|
7551
|
+
static providerKey = "openai_compatible";
|
|
7552
|
+
/** Resolved bearer; undefined for keyless gateways. */
|
|
7553
|
+
apiKey;
|
|
7554
|
+
model;
|
|
7555
|
+
baseUrl;
|
|
7556
|
+
timeoutMs;
|
|
7557
|
+
extraHeaders;
|
|
7558
|
+
sessionUserPrefix;
|
|
7559
|
+
sessionIdHeader;
|
|
7560
|
+
sessionIdPrefix;
|
|
7561
|
+
sessionKeyHeader;
|
|
7562
|
+
sessionKey;
|
|
7563
|
+
temperature;
|
|
7564
|
+
maxTokens;
|
|
7565
|
+
responseFormat;
|
|
7566
|
+
parallelToolCalls;
|
|
7567
|
+
toolChoice;
|
|
7568
|
+
seed;
|
|
7569
|
+
topP;
|
|
7570
|
+
frequencyPenalty;
|
|
7571
|
+
presencePenalty;
|
|
7572
|
+
stop;
|
|
7573
|
+
constructor(options) {
|
|
7574
|
+
if (!options.baseUrl) {
|
|
7575
|
+
throw new Error(
|
|
7576
|
+
'OpenAICompatibleLLMProvider requires a baseUrl (e.g. "http://127.0.0.1:11434/v1").'
|
|
7577
|
+
);
|
|
7578
|
+
}
|
|
7579
|
+
if (!options.model) {
|
|
7580
|
+
throw new Error("OpenAICompatibleLLMProvider requires a model.");
|
|
7581
|
+
}
|
|
7582
|
+
this.apiKey = options.apiKey ?? (options.apiKeyEnv ? process.env[options.apiKeyEnv] : void 0);
|
|
7583
|
+
this.model = options.model;
|
|
7584
|
+
this.baseUrl = options.baseUrl;
|
|
7585
|
+
this.timeoutMs = (options.timeout ?? DEFAULT_TIMEOUT_S) * 1e3;
|
|
7586
|
+
this.extraHeaders = options.extraHeaders;
|
|
7587
|
+
this.sessionUserPrefix = options.sessionUserPrefix;
|
|
7588
|
+
this.sessionIdHeader = options.sessionIdHeader;
|
|
7589
|
+
this.sessionIdPrefix = options.sessionIdPrefix;
|
|
7590
|
+
this.sessionKeyHeader = options.sessionKeyHeader;
|
|
7591
|
+
this.sessionKey = options.sessionKey;
|
|
7592
|
+
this.temperature = options.temperature;
|
|
7593
|
+
this.maxTokens = options.maxTokens;
|
|
7594
|
+
this.responseFormat = options.responseFormat;
|
|
7595
|
+
this.parallelToolCalls = options.parallelToolCalls;
|
|
7596
|
+
this.toolChoice = options.toolChoice;
|
|
7597
|
+
this.seed = options.seed;
|
|
7598
|
+
this.topP = options.topP;
|
|
7599
|
+
this.frequencyPenalty = options.frequencyPenalty;
|
|
7600
|
+
this.presencePenalty = options.presencePenalty;
|
|
7601
|
+
this.stop = options.stop;
|
|
7602
|
+
}
|
|
7603
|
+
/**
|
|
7604
|
+
* Assemble the request headers. ``User-Agent`` is set first so any
|
|
7605
|
+
* ``extraHeaders`` (and the per-call session headers) layer on top without
|
|
7606
|
+
* silently dropping the SDK attribution, and the ``Authorization`` header is
|
|
7607
|
+
* only added when a key is present (keyless gateways omit it).
|
|
7608
|
+
*
|
|
7609
|
+
* The two session headers are emitted INDEPENDENTLY, each gated on its own
|
|
7610
|
+
* config (decoupled from ``sessionUserPrefix`` and from each other):
|
|
7611
|
+
* - ``sessionIdHeader`` (+ ``callId``) → ``` `${sessionIdPrefix}${callId}` ```
|
|
7612
|
+
* - ``sessionKeyHeader`` (+ ``sessionKey``) → the static ``sessionKey`` value.
|
|
7613
|
+
* ``sessionKey`` is a credential-grade memory scope and is never logged.
|
|
7614
|
+
*/
|
|
7615
|
+
buildHeaders(callId) {
|
|
7616
|
+
const headers = {
|
|
7617
|
+
"Content-Type": "application/json",
|
|
7618
|
+
"User-Agent": `getpatter/${VERSION}`,
|
|
7619
|
+
...this.extraHeaders ?? {}
|
|
7620
|
+
};
|
|
7621
|
+
if (this.apiKey) {
|
|
7622
|
+
headers.Authorization = `Bearer ${this.apiKey}`;
|
|
7623
|
+
}
|
|
7624
|
+
if (this.sessionIdHeader && callId) {
|
|
7625
|
+
headers[this.sessionIdHeader] = `${this.sessionIdPrefix ?? ""}${callId}`;
|
|
7626
|
+
}
|
|
7627
|
+
if (this.sessionKeyHeader && this.sessionKey) {
|
|
7628
|
+
headers[this.sessionKeyHeader] = this.sessionKey;
|
|
7629
|
+
}
|
|
7630
|
+
return headers;
|
|
7631
|
+
}
|
|
7632
|
+
/**
|
|
7633
|
+
* Pre-call DNS / TLS warmup for the configured endpoint. Best-effort:
|
|
7634
|
+
* 5 s timeout, all exceptions swallowed at debug level. The ``Authorization``
|
|
7635
|
+
* header is only sent when a key is present so the operator-grade bearer is
|
|
7636
|
+
* never echoed for keyless gateways (and the key is never logged).
|
|
7637
|
+
*/
|
|
7638
|
+
async warmup() {
|
|
7639
|
+
try {
|
|
7640
|
+
const headers = {};
|
|
7641
|
+
if (this.apiKey) headers.Authorization = `Bearer ${this.apiKey}`;
|
|
7642
|
+
await fetch(`${this.baseUrl}/models`, {
|
|
7643
|
+
method: "GET",
|
|
7644
|
+
headers,
|
|
7645
|
+
signal: AbortSignal.timeout(5e3)
|
|
7646
|
+
});
|
|
7647
|
+
} catch (err) {
|
|
7648
|
+
getLogger().debug(
|
|
7649
|
+
`OpenAI-compatible LLM warmup failed (best-effort): ${String(err)}`
|
|
7650
|
+
);
|
|
7651
|
+
}
|
|
7652
|
+
}
|
|
7653
|
+
/**
|
|
7654
|
+
* Build the request body. Mirrors the base OpenAI provider's sampling-kwarg
|
|
7655
|
+
* assembly and additionally sets ``user`` for session continuity when
|
|
7656
|
+
* ``sessionUserPrefix`` is set AND a ``callId`` is available — so the default
|
|
7657
|
+
* (prefix unset) behaviour is byte-identical to the base provider.
|
|
7658
|
+
*/
|
|
7659
|
+
buildBody(messages, tools, callId) {
|
|
7660
|
+
const body = {
|
|
7661
|
+
model: this.model,
|
|
7662
|
+
messages,
|
|
7663
|
+
stream: true,
|
|
7664
|
+
stream_options: { include_usage: true }
|
|
7665
|
+
};
|
|
7666
|
+
if (this.temperature !== void 0) body.temperature = this.temperature;
|
|
7667
|
+
if (this.maxTokens !== void 0) body.max_completion_tokens = this.maxTokens;
|
|
7668
|
+
if (this.responseFormat !== void 0) body.response_format = this.responseFormat;
|
|
7669
|
+
if (this.parallelToolCalls !== void 0) body.parallel_tool_calls = this.parallelToolCalls;
|
|
7670
|
+
if (this.toolChoice !== void 0) body.tool_choice = this.toolChoice;
|
|
7671
|
+
if (this.seed !== void 0) body.seed = this.seed;
|
|
7672
|
+
if (this.topP !== void 0) body.top_p = this.topP;
|
|
7673
|
+
if (this.frequencyPenalty !== void 0) body.frequency_penalty = this.frequencyPenalty;
|
|
7674
|
+
if (this.presencePenalty !== void 0) body.presence_penalty = this.presencePenalty;
|
|
7675
|
+
if (this.stop !== void 0) body.stop = this.stop;
|
|
7676
|
+
if (tools) body.tools = tools;
|
|
7677
|
+
if (this.sessionUserPrefix !== void 0 && callId) {
|
|
7678
|
+
body.user = `${this.sessionUserPrefix}${callId}`;
|
|
7679
|
+
}
|
|
7680
|
+
return body;
|
|
7681
|
+
}
|
|
7682
|
+
/** Stream Patter-format LLM chunks from the configured chat completions API. */
|
|
7683
|
+
async *stream(messages, tools, opts) {
|
|
7684
|
+
const callId = opts?.callId;
|
|
7685
|
+
const body = this.buildBody(messages, tools, callId);
|
|
7686
|
+
const response = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
7687
|
+
method: "POST",
|
|
7688
|
+
headers: this.buildHeaders(callId),
|
|
7689
|
+
body: JSON.stringify(body),
|
|
7690
|
+
signal: mergeAbortSignals(opts?.signal, AbortSignal.timeout(this.timeoutMs))
|
|
7691
|
+
});
|
|
7692
|
+
if (!response.ok) {
|
|
7693
|
+
const errText = await response.text();
|
|
7694
|
+
getLogger().error(
|
|
7695
|
+
`OpenAI-compatible API error: ${response.status} ${errText}`
|
|
7696
|
+
);
|
|
7697
|
+
throw new PatterConnectionError(
|
|
7698
|
+
`LLM API returned ${response.status}: ${errText.slice(0, 200)}`
|
|
7699
|
+
);
|
|
7700
|
+
}
|
|
7701
|
+
yield* parseOpenAISseStream(response);
|
|
7702
|
+
}
|
|
7703
|
+
};
|
|
7704
|
+
var LLM6 = class extends OpenAICompatibleLLMProvider {
|
|
7705
|
+
static providerKey = "openai_compatible";
|
|
7706
|
+
};
|
|
7707
|
+
|
|
7708
|
+
// src/llm/hermes.ts
|
|
7709
|
+
init_esm_shims();
|
|
7710
|
+
var BASE_URL = "http://127.0.0.1:8642/v1";
|
|
7711
|
+
var DEFAULT_MODEL5 = "hermes-agent";
|
|
7712
|
+
var API_KEY_ENV = "API_SERVER_KEY";
|
|
7713
|
+
var MODEL_ENV = "API_SERVER_MODEL_NAME";
|
|
7714
|
+
var SESSION_USER_PREFIX = "patter-call-";
|
|
7715
|
+
var SESSION_ID_HEADER = "X-Hermes-Session-Id";
|
|
7716
|
+
var SESSION_ID_PREFIX = "patter-call-";
|
|
7717
|
+
var SESSION_KEY_HEADER = "X-Hermes-Session-Key";
|
|
7718
|
+
var DEFAULT_TIMEOUT_S2 = 120;
|
|
7719
|
+
var LLM7 = class extends OpenAICompatibleLLMProvider {
|
|
7720
|
+
static providerKey = "hermes";
|
|
7721
|
+
constructor(opts = {}) {
|
|
7722
|
+
const model = opts.model ?? process.env[MODEL_ENV] ?? DEFAULT_MODEL5;
|
|
7723
|
+
const options = {
|
|
7724
|
+
apiKey: opts.apiKey,
|
|
7725
|
+
apiKeyEnv: API_KEY_ENV,
|
|
7726
|
+
baseUrl: opts.baseUrl ?? BASE_URL,
|
|
7727
|
+
model,
|
|
7728
|
+
timeout: opts.timeout ?? DEFAULT_TIMEOUT_S2,
|
|
7729
|
+
sessionUserPrefix: SESSION_USER_PREFIX,
|
|
7730
|
+
sessionIdHeader: SESSION_ID_HEADER,
|
|
7731
|
+
sessionIdPrefix: SESSION_ID_PREFIX,
|
|
7732
|
+
sessionKeyHeader: SESSION_KEY_HEADER,
|
|
7733
|
+
sessionKey: opts.sessionKey,
|
|
7734
|
+
extraHeaders: opts.extraHeaders,
|
|
7735
|
+
temperature: opts.temperature,
|
|
7736
|
+
maxTokens: opts.maxTokens,
|
|
7737
|
+
responseFormat: opts.responseFormat,
|
|
7738
|
+
parallelToolCalls: opts.parallelToolCalls,
|
|
7739
|
+
toolChoice: opts.toolChoice,
|
|
7740
|
+
seed: opts.seed,
|
|
7741
|
+
topP: opts.topP,
|
|
7742
|
+
frequencyPenalty: opts.frequencyPenalty,
|
|
7743
|
+
presencePenalty: opts.presencePenalty,
|
|
7744
|
+
stop: opts.stop
|
|
7745
|
+
};
|
|
7746
|
+
super(options);
|
|
7747
|
+
}
|
|
7748
|
+
};
|
|
7749
|
+
|
|
7750
|
+
// src/llm/openclaw.ts
|
|
7751
|
+
init_esm_shims();
|
|
7752
|
+
var BASE_URL2 = "http://127.0.0.1:18789/v1";
|
|
7753
|
+
var API_KEY_ENV2 = "OPENCLAW_API_KEY";
|
|
7754
|
+
var SESSION_HEADER = "x-openclaw-session-key";
|
|
7755
|
+
var SESSION_USER_PREFIX2 = "patter-call-";
|
|
7756
|
+
var DEFAULT_TIMEOUT_S3 = 120;
|
|
7757
|
+
var OPENCLAW_AGENT_RE = /^[A-Za-z0-9._:/-]+$/;
|
|
7758
|
+
var LLM8 = class extends OpenAICompatibleLLMProvider {
|
|
7759
|
+
static providerKey = "openclaw";
|
|
7760
|
+
constructor(opts) {
|
|
7761
|
+
const agent = opts?.agent;
|
|
7762
|
+
if (!agent || !OPENCLAW_AGENT_RE.test(agent)) {
|
|
7763
|
+
throw new Error(
|
|
7764
|
+
`Invalid OpenClaw agent id: ${JSON.stringify(agent)}. Allowed characters: letters, digits, dot, underscore, colon, slash, dash.`
|
|
7765
|
+
);
|
|
7766
|
+
}
|
|
7767
|
+
const model = agent.includes("/") || agent.includes(":") ? agent : `openclaw/${agent}`;
|
|
7768
|
+
const options = {
|
|
7769
|
+
apiKey: opts.apiKey,
|
|
7770
|
+
apiKeyEnv: API_KEY_ENV2,
|
|
7771
|
+
baseUrl: opts.baseUrl ?? BASE_URL2,
|
|
7772
|
+
model,
|
|
7773
|
+
timeout: opts.timeout ?? DEFAULT_TIMEOUT_S3,
|
|
7774
|
+
sessionUserPrefix: SESSION_USER_PREFIX2,
|
|
7775
|
+
// Wire-identical to the prior behaviour: header value is the raw call id
|
|
7776
|
+
// (empty prefix), and OpenClaw's gateway also derives the session from
|
|
7777
|
+
// the ``user`` field above. No separate memory-scope header.
|
|
7778
|
+
sessionIdHeader: SESSION_HEADER,
|
|
7779
|
+
sessionIdPrefix: "",
|
|
7780
|
+
extraHeaders: opts.extraHeaders,
|
|
7781
|
+
temperature: opts.temperature,
|
|
7782
|
+
maxTokens: opts.maxTokens,
|
|
7783
|
+
responseFormat: opts.responseFormat,
|
|
7784
|
+
parallelToolCalls: opts.parallelToolCalls,
|
|
7785
|
+
toolChoice: opts.toolChoice,
|
|
7786
|
+
seed: opts.seed,
|
|
7787
|
+
topP: opts.topP,
|
|
7788
|
+
frequencyPenalty: opts.frequencyPenalty,
|
|
7789
|
+
presencePenalty: opts.presencePenalty,
|
|
7790
|
+
stop: opts.stop
|
|
7791
|
+
};
|
|
7792
|
+
super(options);
|
|
7793
|
+
}
|
|
7794
|
+
};
|
|
7795
|
+
|
|
7381
7796
|
// src/providers/deepfilternet-filter.ts
|
|
7382
7797
|
init_esm_shims();
|
|
7383
7798
|
function log() {
|
|
@@ -7409,6 +7824,57 @@ function float32ToPcm16(samples) {
|
|
|
7409
7824
|
}
|
|
7410
7825
|
return out;
|
|
7411
7826
|
}
|
|
7827
|
+
var ArbitraryResampler = class {
|
|
7828
|
+
srcRate;
|
|
7829
|
+
dstRate;
|
|
7830
|
+
phase = 0;
|
|
7831
|
+
// fractional position into the current chunk
|
|
7832
|
+
lastSample = 0;
|
|
7833
|
+
// last input sample from the previous chunk
|
|
7834
|
+
hasHistory = false;
|
|
7835
|
+
constructor(srcRate, dstRate) {
|
|
7836
|
+
this.srcRate = srcRate;
|
|
7837
|
+
this.dstRate = dstRate;
|
|
7838
|
+
}
|
|
7839
|
+
/** Process a chunk of PCM16-LE mono audio and return resampled PCM16-LE. */
|
|
7840
|
+
process(pcm) {
|
|
7841
|
+
const sampleCount = Math.floor(pcm.length / 2);
|
|
7842
|
+
if (sampleCount === 0) return Buffer.alloc(0);
|
|
7843
|
+
const step = this.srcRate / this.dstRate;
|
|
7844
|
+
const outArr = [];
|
|
7845
|
+
let phase = this.phase;
|
|
7846
|
+
while (true) {
|
|
7847
|
+
const idx = Math.floor(phase);
|
|
7848
|
+
if (idx >= sampleCount) break;
|
|
7849
|
+
const frac = phase - idx;
|
|
7850
|
+
let s0;
|
|
7851
|
+
let s1;
|
|
7852
|
+
if (idx < 0) {
|
|
7853
|
+
s0 = this.hasHistory ? this.lastSample : 0;
|
|
7854
|
+
s1 = pcm.readInt16LE(0);
|
|
7855
|
+
} else {
|
|
7856
|
+
s0 = pcm.readInt16LE(idx * 2);
|
|
7857
|
+
s1 = idx + 1 < sampleCount ? pcm.readInt16LE((idx + 1) * 2) : s0;
|
|
7858
|
+
}
|
|
7859
|
+
const interp = Math.round(s0 + (s1 - s0) * frac);
|
|
7860
|
+
outArr.push(Math.max(-32768, Math.min(32767, interp)));
|
|
7861
|
+
phase += step;
|
|
7862
|
+
}
|
|
7863
|
+
this.lastSample = pcm.readInt16LE((sampleCount - 1) * 2);
|
|
7864
|
+
this.hasHistory = true;
|
|
7865
|
+
this.phase = phase - sampleCount;
|
|
7866
|
+
const out = Buffer.alloc(outArr.length * 2);
|
|
7867
|
+
for (let j = 0; j < outArr.length; j++) out.writeInt16LE(outArr[j], j * 2);
|
|
7868
|
+
return out;
|
|
7869
|
+
}
|
|
7870
|
+
/** Flush any buffered state and reset. Returns any remaining tail output. */
|
|
7871
|
+
flush() {
|
|
7872
|
+
this.phase = 0;
|
|
7873
|
+
this.lastSample = 0;
|
|
7874
|
+
this.hasHistory = false;
|
|
7875
|
+
return Buffer.alloc(0);
|
|
7876
|
+
}
|
|
7877
|
+
};
|
|
7412
7878
|
var DeepFilterNetFilter = class {
|
|
7413
7879
|
modelPath;
|
|
7414
7880
|
silenceWarnings;
|
|
@@ -7416,8 +7882,9 @@ var DeepFilterNetFilter = class {
|
|
|
7416
7882
|
ort = null;
|
|
7417
7883
|
warned = false;
|
|
7418
7884
|
closed = false;
|
|
7419
|
-
//
|
|
7885
|
+
// Stateful resamplers for src_sr↔48k conversions so chunk-boundary
|
|
7420
7886
|
// samples are not discarded. Lazy-created and torn down on rate change.
|
|
7887
|
+
// Uses ArbitraryResampler which supports any integer rate pair.
|
|
7421
7888
|
_resamplerSrcRate = null;
|
|
7422
7889
|
_upsamplerInst = null;
|
|
7423
7890
|
_downsamplerInst = null;
|
|
@@ -7475,8 +7942,8 @@ var DeepFilterNetFilter = class {
|
|
|
7475
7942
|
try {
|
|
7476
7943
|
if (this._resamplerSrcRate !== sampleRate) {
|
|
7477
7944
|
this._resamplerSrcRate = sampleRate;
|
|
7478
|
-
this._upsamplerInst = new
|
|
7479
|
-
this._downsamplerInst = new
|
|
7945
|
+
this._upsamplerInst = new ArbitraryResampler(sampleRate, DEEPFILTERNET_SR);
|
|
7946
|
+
this._downsamplerInst = new ArbitraryResampler(DEEPFILTERNET_SR, sampleRate);
|
|
7480
7947
|
}
|
|
7481
7948
|
const samples = pcm16ToFloat32(pcmChunk);
|
|
7482
7949
|
const pcm16Up = this._upsamplerInst.process(float32ToPcm16(new Float32Array(samples)));
|
|
@@ -7636,6 +8103,17 @@ var Tool = class {
|
|
|
7636
8103
|
parameters;
|
|
7637
8104
|
handler;
|
|
7638
8105
|
webhookUrl;
|
|
8106
|
+
reassurance;
|
|
8107
|
+
/**
|
|
8108
|
+
* Per-tool execution timeout in milliseconds. `undefined` uses the
|
|
8109
|
+
* executor default (10 000 ms). Mirrors Python `timeout_s`.
|
|
8110
|
+
*/
|
|
8111
|
+
timeoutMs;
|
|
8112
|
+
/**
|
|
8113
|
+
* Enable OpenAI strict mode for this tool's function schema. Off by
|
|
8114
|
+
* default. Mirrors Python `strict` on `Tool`.
|
|
8115
|
+
*/
|
|
8116
|
+
strict;
|
|
7639
8117
|
constructor(opts) {
|
|
7640
8118
|
if (!opts.name) {
|
|
7641
8119
|
throw new Error("Tool requires a non-empty name.");
|
|
@@ -7653,6 +8131,9 @@ var Tool = class {
|
|
|
7653
8131
|
this.parameters = opts.parameters ?? { type: "object", properties: {} };
|
|
7654
8132
|
if (hasHandler) this.handler = opts.handler;
|
|
7655
8133
|
if (hasWebhook) this.webhookUrl = opts.webhookUrl;
|
|
8134
|
+
if (opts.reassurance !== void 0) this.reassurance = opts.reassurance;
|
|
8135
|
+
if (opts.timeoutMs !== void 0) this.timeoutMs = opts.timeoutMs;
|
|
8136
|
+
if (opts.strict !== void 0) this.strict = opts.strict;
|
|
7656
8137
|
}
|
|
7657
8138
|
};
|
|
7658
8139
|
function tool(opts) {
|
|
@@ -7811,7 +8292,6 @@ var ChatContext = class _ChatContext {
|
|
|
7811
8292
|
// src/services/ivr.ts
|
|
7812
8293
|
init_esm_shims();
|
|
7813
8294
|
var DTMF_EVENTS = [
|
|
7814
|
-
"0",
|
|
7815
8295
|
"1",
|
|
7816
8296
|
"2",
|
|
7817
8297
|
"3",
|
|
@@ -7821,6 +8301,7 @@ var DTMF_EVENTS = [
|
|
|
7821
8301
|
"7",
|
|
7822
8302
|
"8",
|
|
7823
8303
|
"9",
|
|
8304
|
+
"0",
|
|
7824
8305
|
"*",
|
|
7825
8306
|
"#",
|
|
7826
8307
|
"A",
|
|
@@ -8497,18 +8978,24 @@ var TelnyxAdapter = class {
|
|
|
8497
8978
|
"/number_orders",
|
|
8498
8979
|
orderBody
|
|
8499
8980
|
);
|
|
8500
|
-
const orderId = order.data?.id
|
|
8981
|
+
const orderId = order.data?.id;
|
|
8982
|
+
if (!orderId) throw new Error("TelnyxAdapter: /number_orders returned no order id");
|
|
8501
8983
|
return { phoneNumber: chosen, orderId };
|
|
8502
8984
|
}
|
|
8503
8985
|
/** Attach a number to a Call Control Application. */
|
|
8504
8986
|
async configureNumber(phoneNumber, opts) {
|
|
8505
8987
|
if (!phoneNumber) throw new Error("TelnyxAdapter: phoneNumber is required");
|
|
8506
8988
|
if (!opts.connectionId) throw new Error("TelnyxAdapter: connectionId is required");
|
|
8507
|
-
|
|
8508
|
-
|
|
8509
|
-
|
|
8510
|
-
|
|
8511
|
-
|
|
8989
|
+
try {
|
|
8990
|
+
await this.request(
|
|
8991
|
+
"PATCH",
|
|
8992
|
+
`/phone_numbers/${encodeURIComponent(phoneNumber)}/voice`,
|
|
8993
|
+
{ connection_id: opts.connectionId, tech_prefix_enabled: false }
|
|
8994
|
+
);
|
|
8995
|
+
} catch (err) {
|
|
8996
|
+
const status = err instanceof Error ? err.message.replace(/\+\d{7,15}/g, "[REDACTED]") : String(err);
|
|
8997
|
+
throw new Error(`TelnyxAdapter: configureNumber failed: ${status}`);
|
|
8998
|
+
}
|
|
8512
8999
|
}
|
|
8513
9000
|
/**
|
|
8514
9001
|
* Place an outbound call on the Call Control Application.
|
|
@@ -8612,7 +9099,7 @@ var TelnyxSTT = class {
|
|
|
8612
9099
|
/** Stable pricing/dashboard key — read by stream-handler/metrics. */
|
|
8613
9100
|
static providerKey = "telnyx_stt";
|
|
8614
9101
|
ws = null;
|
|
8615
|
-
callbacks =
|
|
9102
|
+
callbacks = /* @__PURE__ */ new Set();
|
|
8616
9103
|
headerSent = false;
|
|
8617
9104
|
/** Open the streaming WebSocket and arm message handlers. */
|
|
8618
9105
|
async connect() {
|
|
@@ -8668,14 +9155,13 @@ var TelnyxSTT = class {
|
|
|
8668
9155
|
}
|
|
8669
9156
|
this.ws.send(audio);
|
|
8670
9157
|
}
|
|
8671
|
-
/** Register a transcript listener
|
|
9158
|
+
/** Register a transcript listener. */
|
|
8672
9159
|
onTranscript(callback) {
|
|
8673
|
-
|
|
8674
|
-
|
|
8675
|
-
|
|
8676
|
-
|
|
8677
|
-
|
|
8678
|
-
this.callbacks.push(callback);
|
|
9160
|
+
this.callbacks.add(callback);
|
|
9161
|
+
}
|
|
9162
|
+
/** Unregister a previously-registered transcript listener. */
|
|
9163
|
+
offTranscript(callback) {
|
|
9164
|
+
this.callbacks.delete(callback);
|
|
8679
9165
|
}
|
|
8680
9166
|
/** Close the streaming WebSocket. */
|
|
8681
9167
|
close() {
|
|
@@ -8686,6 +9172,7 @@ var TelnyxSTT = class {
|
|
|
8686
9172
|
}
|
|
8687
9173
|
this.ws = null;
|
|
8688
9174
|
}
|
|
9175
|
+
this.headerSent = false;
|
|
8689
9176
|
}
|
|
8690
9177
|
};
|
|
8691
9178
|
|
|
@@ -8706,6 +9193,7 @@ var TelnyxTTSSampleRate = {
|
|
|
8706
9193
|
HZ_24000: 24e3
|
|
8707
9194
|
};
|
|
8708
9195
|
var DEFAULT_VOICE = TelnyxTTSVoice.NATURAL_HD_ASTRA;
|
|
9196
|
+
var FRAME_TIMEOUT_MS2 = 3e4;
|
|
8709
9197
|
var TelnyxTTS = class {
|
|
8710
9198
|
constructor(apiKey, voice = DEFAULT_VOICE, baseUrl = TELNYX_TTS_WS_URL) {
|
|
8711
9199
|
this.apiKey = apiKey;
|
|
@@ -8733,69 +9221,83 @@ var TelnyxTTS = class {
|
|
|
8733
9221
|
*/
|
|
8734
9222
|
async *synthesizeStream(text) {
|
|
8735
9223
|
const url = `${this.baseUrl}?voice=${encodeURIComponent(this.voice)}`;
|
|
8736
|
-
|
|
8737
|
-
|
|
8738
|
-
|
|
8739
|
-
|
|
8740
|
-
|
|
8741
|
-
|
|
8742
|
-
|
|
8743
|
-
|
|
9224
|
+
let ws = null;
|
|
9225
|
+
try {
|
|
9226
|
+
let push2 = function(item) {
|
|
9227
|
+
const w = waiters.shift();
|
|
9228
|
+
if (w) {
|
|
9229
|
+
w(item);
|
|
9230
|
+
} else {
|
|
9231
|
+
queue.push(item);
|
|
9232
|
+
}
|
|
9233
|
+
};
|
|
9234
|
+
var push = push2;
|
|
9235
|
+
ws = new WebSocket8(url, {
|
|
9236
|
+
headers: { Authorization: `Bearer ${this.apiKey}` }
|
|
8744
9237
|
});
|
|
8745
|
-
|
|
8746
|
-
|
|
8747
|
-
|
|
9238
|
+
await new Promise((resolve, reject) => {
|
|
9239
|
+
const timer = setTimeout(() => reject(new Error("Telnyx TTS connect timeout")), 1e4);
|
|
9240
|
+
ws.once("open", () => {
|
|
9241
|
+
clearTimeout(timer);
|
|
9242
|
+
resolve();
|
|
9243
|
+
});
|
|
9244
|
+
ws.once("error", (err) => {
|
|
9245
|
+
clearTimeout(timer);
|
|
9246
|
+
reject(err);
|
|
9247
|
+
});
|
|
8748
9248
|
});
|
|
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);
|
|
9249
|
+
const queue = [];
|
|
9250
|
+
const waiters = [];
|
|
9251
|
+
ws.on("message", (raw) => {
|
|
9252
|
+
let data;
|
|
9253
|
+
try {
|
|
9254
|
+
data = JSON.parse(raw.toString());
|
|
9255
|
+
} catch {
|
|
9256
|
+
getLogger().warn("TelnyxTTS: received invalid JSON");
|
|
9257
|
+
return;
|
|
8774
9258
|
}
|
|
8775
|
-
|
|
8776
|
-
|
|
8777
|
-
|
|
8778
|
-
|
|
8779
|
-
|
|
8780
|
-
|
|
8781
|
-
|
|
8782
|
-
|
|
8783
|
-
|
|
8784
|
-
|
|
8785
|
-
|
|
8786
|
-
|
|
8787
|
-
|
|
9259
|
+
const audioB64 = data.audio;
|
|
9260
|
+
if (!audioB64) return;
|
|
9261
|
+
try {
|
|
9262
|
+
const audioBytes = Buffer.from(audioB64, "base64");
|
|
9263
|
+
if (audioBytes.length > 0) {
|
|
9264
|
+
push2(audioBytes);
|
|
9265
|
+
}
|
|
9266
|
+
} catch {
|
|
9267
|
+
}
|
|
9268
|
+
});
|
|
9269
|
+
ws.on("close", () => {
|
|
9270
|
+
push2(null);
|
|
9271
|
+
});
|
|
9272
|
+
ws.on("error", (err) => {
|
|
9273
|
+
push2({ error: err instanceof Error ? err : new Error(String(err)) });
|
|
9274
|
+
});
|
|
9275
|
+
ws.send(JSON.stringify({ text: " " }));
|
|
9276
|
+
ws.send(JSON.stringify({ text }));
|
|
9277
|
+
ws.send(JSON.stringify({ text: "" }));
|
|
8788
9278
|
while (true) {
|
|
8789
|
-
|
|
9279
|
+
let frameTimer;
|
|
9280
|
+
const item = queue.length > 0 ? queue.shift() : await Promise.race([
|
|
9281
|
+
new Promise((resolve) => waiters.push(resolve)),
|
|
9282
|
+
new Promise((_, reject) => {
|
|
9283
|
+
frameTimer = setTimeout(
|
|
9284
|
+
() => reject(new Error("Telnyx TTS frame timeout")),
|
|
9285
|
+
FRAME_TIMEOUT_MS2
|
|
9286
|
+
);
|
|
9287
|
+
})
|
|
9288
|
+
]).finally(() => {
|
|
9289
|
+
if (frameTimer !== void 0) clearTimeout(frameTimer);
|
|
9290
|
+
});
|
|
8790
9291
|
if (item === null) return;
|
|
8791
9292
|
if (typeof item === "object" && "error" in item) throw item.error;
|
|
8792
9293
|
yield item;
|
|
8793
9294
|
}
|
|
8794
9295
|
} finally {
|
|
8795
9296
|
try {
|
|
8796
|
-
ws
|
|
9297
|
+
ws?.close();
|
|
8797
9298
|
} catch {
|
|
8798
9299
|
}
|
|
9300
|
+
ws?.removeAllListeners();
|
|
8799
9301
|
}
|
|
8800
9302
|
}
|
|
8801
9303
|
};
|
|
@@ -8840,6 +9342,7 @@ export {
|
|
|
8840
9342
|
LLM5 as GoogleLLM,
|
|
8841
9343
|
LLM3 as GroqLLM,
|
|
8842
9344
|
Guardrail,
|
|
9345
|
+
LLM7 as HermesLLM,
|
|
8843
9346
|
IVRActivity,
|
|
8844
9347
|
TTS7 as InworldTTS,
|
|
8845
9348
|
KrispFrameDuration,
|
|
@@ -8850,6 +9353,8 @@ export {
|
|
|
8850
9353
|
MetricsStore,
|
|
8851
9354
|
MinWordsStrategy,
|
|
8852
9355
|
Ngrok,
|
|
9356
|
+
LLM6 as OpenAICompatibleLLM,
|
|
9357
|
+
OpenAICompatibleLLMProvider,
|
|
8853
9358
|
LLM as OpenAILLM,
|
|
8854
9359
|
OpenAILLMProvider,
|
|
8855
9360
|
Realtime as OpenAIRealtime,
|
|
@@ -8863,10 +9368,12 @@ export {
|
|
|
8863
9368
|
STT3 as OpenAITranscribeSTT,
|
|
8864
9369
|
OpenAITranscriptionModel,
|
|
8865
9370
|
OpenAIVoice,
|
|
9371
|
+
LLM8 as OpenClawLLM,
|
|
8866
9372
|
PRICING_LAST_UPDATED,
|
|
8867
9373
|
PRICING_VERSION,
|
|
8868
9374
|
PartialStreamError,
|
|
8869
9375
|
Patter,
|
|
9376
|
+
PatterConfigError,
|
|
8870
9377
|
PatterConnectionError,
|
|
8871
9378
|
PatterError,
|
|
8872
9379
|
PatterTool,
|
|
@@ -8954,6 +9461,8 @@ export {
|
|
|
8954
9461
|
mulawToPcm16,
|
|
8955
9462
|
notifyDashboard,
|
|
8956
9463
|
openaiTts,
|
|
9464
|
+
openclawConsult,
|
|
9465
|
+
openclawPostCallNotifier,
|
|
8957
9466
|
pcm16ToMulaw,
|
|
8958
9467
|
resample16kTo8k,
|
|
8959
9468
|
resample24kTo16k,
|