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/cli.js
CHANGED
|
@@ -185,14 +185,49 @@ var MetricsStore = class extends import_events.EventEmitter {
|
|
|
185
185
|
} else {
|
|
186
186
|
for (let i = this.calls.length - 1; i >= 0; i--) {
|
|
187
187
|
if (this.calls[i].call_id === callId) {
|
|
188
|
-
this.calls[i].status
|
|
189
|
-
Object.assign(this.calls[i], extra);
|
|
188
|
+
this.calls[i] = { ...this.calls[i], status, ...extra };
|
|
190
189
|
break;
|
|
191
190
|
}
|
|
192
191
|
}
|
|
193
192
|
}
|
|
194
193
|
this.publish("call_status", { call_id: callId, status, ...extra });
|
|
195
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* Record a single transcript line (user/assistant) as it becomes known.
|
|
197
|
+
*
|
|
198
|
+
* FIX-5 (issue #154): the live forward path for the dashboard transcript.
|
|
199
|
+
* The Realtime stream handler calls this the moment each line is known — the
|
|
200
|
+
* user line right after the hallucination filter accepts it, the assistant
|
|
201
|
+
* line when its turn flushes — keyed by the monotonic ``turnIndex`` reserved
|
|
202
|
+
* at turn-open (``reserveTurnIndex``). Each line is appended to the active
|
|
203
|
+
* call's ``transcript`` array and broadcast over SSE as a ``transcript_line``
|
|
204
|
+
* event so the dashboard can render lines as they arrive and re-sort by
|
|
205
|
+
* ``(turnIndex, user<assistant)`` — making a late-arriving user line land
|
|
206
|
+
* ABOVE its agent line. ``recordTurn`` de-dups against the lines pushed here
|
|
207
|
+
* by ``(turnIndex, role)`` so the metrics path never double-pushes the same
|
|
208
|
+
* text. Parity with Python ``record_transcript_line``.
|
|
209
|
+
*/
|
|
210
|
+
recordTranscriptLine(data) {
|
|
211
|
+
const callId = data.call_id || "";
|
|
212
|
+
const { role, text, turnIndex } = data;
|
|
213
|
+
if (!callId || role !== "user" && role !== "assistant" || !text) return;
|
|
214
|
+
const active = this.activeCalls.get(callId);
|
|
215
|
+
if (active) {
|
|
216
|
+
if (!active.transcript) active.transcript = [];
|
|
217
|
+
active.transcript.push({
|
|
218
|
+
role,
|
|
219
|
+
text,
|
|
220
|
+
timestamp: Date.now() / 1e3,
|
|
221
|
+
turnIndex
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
this.publish("transcript_line", {
|
|
225
|
+
call_id: callId,
|
|
226
|
+
turnIndex,
|
|
227
|
+
role,
|
|
228
|
+
text
|
|
229
|
+
});
|
|
230
|
+
}
|
|
196
231
|
/** Append a single conversation turn to an active call and broadcast it via SSE. */
|
|
197
232
|
recordTurn(data) {
|
|
198
233
|
const callId = data.call_id || "";
|
|
@@ -207,14 +242,19 @@ var MetricsStore = class extends import_events.EventEmitter {
|
|
|
207
242
|
const userText = typeof turnRecord.user_text === "string" ? turnRecord.user_text : "";
|
|
208
243
|
const agentText = typeof turnRecord.agent_text === "string" ? turnRecord.agent_text : "";
|
|
209
244
|
const ts = typeof turnRecord.timestamp === "number" ? turnRecord.timestamp : Date.now() / 1e3;
|
|
210
|
-
|
|
211
|
-
|
|
245
|
+
const turnIndex = typeof turnRecord.turn_index === "number" ? turnRecord.turn_index : void 0;
|
|
246
|
+
const alreadyLive = (role) => turnIndex !== void 0 && (active.transcript ?? []).some(
|
|
247
|
+
(e) => e.turnIndex === turnIndex && e.role === role
|
|
248
|
+
);
|
|
249
|
+
if (userText.length > 0 && !alreadyLive("user")) {
|
|
250
|
+
active.transcript.push({ role: "user", text: userText, timestamp: ts, turnIndex });
|
|
212
251
|
}
|
|
213
|
-
if (agentText.length > 0 && agentText !== "[interrupted]") {
|
|
252
|
+
if (agentText.length > 0 && agentText !== "[interrupted]" && !alreadyLive("assistant")) {
|
|
214
253
|
active.transcript.push({
|
|
215
254
|
role: "assistant",
|
|
216
255
|
text: agentText,
|
|
217
|
-
timestamp: ts
|
|
256
|
+
timestamp: ts,
|
|
257
|
+
turnIndex
|
|
218
258
|
});
|
|
219
259
|
}
|
|
220
260
|
}
|
|
@@ -287,7 +327,7 @@ var MetricsStore = class extends import_events.EventEmitter {
|
|
|
287
327
|
getCall(callId) {
|
|
288
328
|
if (this.deletedCallIds.has(callId)) return null;
|
|
289
329
|
for (let i = this.calls.length - 1; i >= 0; i--) {
|
|
290
|
-
if (this.calls[i].call_id === callId) return this.calls[i];
|
|
330
|
+
if (this.calls[i].call_id === callId) return { ...this.calls[i] };
|
|
291
331
|
}
|
|
292
332
|
return null;
|
|
293
333
|
}
|
|
@@ -329,7 +369,9 @@ var MetricsStore = class extends import_events.EventEmitter {
|
|
|
329
369
|
}
|
|
330
370
|
if (accepted.length === 0) return [];
|
|
331
371
|
accepted.sort();
|
|
332
|
-
this.persistDeletedIds()
|
|
372
|
+
this.persistDeletedIds().catch(
|
|
373
|
+
(err) => getLogger().debug(`MetricsStore.deleteCalls: persistDeletedIds failed: ${String(err)}`)
|
|
374
|
+
);
|
|
333
375
|
this.publish("calls_deleted", { call_ids: accepted });
|
|
334
376
|
return accepted;
|
|
335
377
|
}
|
|
@@ -341,19 +383,19 @@ var MetricsStore = class extends import_events.EventEmitter {
|
|
|
341
383
|
getDeletedCallIds() {
|
|
342
384
|
return Array.from(this.deletedCallIds).sort();
|
|
343
385
|
}
|
|
344
|
-
/** Atomically persist the deleted-ids set to disk. Best-effort. */
|
|
345
|
-
persistDeletedIds() {
|
|
386
|
+
/** Atomically persist the deleted-ids set to disk. Best-effort async. */
|
|
387
|
+
async persistDeletedIds() {
|
|
346
388
|
if (this.deletedIdsPath === null) return;
|
|
347
389
|
try {
|
|
348
390
|
const dir = path2.dirname(this.deletedIdsPath);
|
|
349
|
-
fs2.
|
|
391
|
+
await fs2.promises.mkdir(dir, { recursive: true });
|
|
350
392
|
const tmp = this.deletedIdsPath + ".tmp";
|
|
351
393
|
const payload = {
|
|
352
394
|
version: 1,
|
|
353
395
|
deleted_call_ids: Array.from(this.deletedCallIds).sort()
|
|
354
396
|
};
|
|
355
|
-
fs2.
|
|
356
|
-
fs2.
|
|
397
|
+
await fs2.promises.writeFile(tmp, JSON.stringify(payload, null, 2), "utf8");
|
|
398
|
+
await fs2.promises.rename(tmp, this.deletedIdsPath);
|
|
357
399
|
} catch (err) {
|
|
358
400
|
getLogger().debug(
|
|
359
401
|
`MetricsStore.persistDeletedIds: ${String(err)}`
|
|
@@ -362,7 +404,8 @@ var MetricsStore = class extends import_events.EventEmitter {
|
|
|
362
404
|
}
|
|
363
405
|
/** Look up an active call by id (returns undefined if not active or unknown). */
|
|
364
406
|
getActive(callId) {
|
|
365
|
-
|
|
407
|
+
const rec = this.activeCalls.get(callId);
|
|
408
|
+
return rec !== void 0 ? { ...rec } : void 0;
|
|
366
409
|
}
|
|
367
410
|
/** Return all currently active (not yet ended) calls. */
|
|
368
411
|
getActiveCalls() {
|
|
@@ -607,8 +650,8 @@ function loadTranscriptJsonl(filePath) {
|
|
|
607
650
|
} catch {
|
|
608
651
|
continue;
|
|
609
652
|
}
|
|
610
|
-
const tsIso = typeof row.ts === "string" ? Date.parse(row.ts) : NaN;
|
|
611
|
-
const tsNumeric = typeof row.timestamp === "number" ? row.timestamp
|
|
653
|
+
const tsIso = typeof row.ts === "string" ? Date.parse(row.ts) / 1e3 : NaN;
|
|
654
|
+
const tsNumeric = typeof row.timestamp === "number" ? row.timestamp : NaN;
|
|
612
655
|
const timestamp = Number.isFinite(tsIso) ? tsIso : Number.isFinite(tsNumeric) ? tsNumeric : 0;
|
|
613
656
|
const userText = typeof row.user_text === "string" ? row.user_text : "";
|
|
614
657
|
const agentText = typeof row.agent_text === "string" ? row.agent_text : "";
|
|
@@ -759,8 +802,8 @@ function mountDashboard(app, store, token = "") {
|
|
|
759
802
|
res.type("text/html").send(DASHBOARD_HTML);
|
|
760
803
|
});
|
|
761
804
|
app.get("/api/dashboard/calls", auth, (req, res) => {
|
|
762
|
-
const limit = Math.min(parseInt(req.query.limit || "50", 10) || 50, 1e3);
|
|
763
|
-
const offset = parseInt(req.query.offset || "0", 10) || 0;
|
|
805
|
+
const limit = Math.min(Math.max(0, parseInt(req.query.limit || "50", 10) || 50), 1e3);
|
|
806
|
+
const offset = Math.max(0, parseInt(req.query.offset || "0", 10) || 0);
|
|
764
807
|
res.json(store.getCalls(limit, offset));
|
|
765
808
|
});
|
|
766
809
|
app.get("/api/dashboard/calls/:callId", auth, (req, res) => {
|
|
@@ -850,8 +893,8 @@ data: ${data}
|
|
|
850
893
|
function mountApi(app, store, token = "") {
|
|
851
894
|
const auth = makeAuthMiddleware(token);
|
|
852
895
|
app.get("/api/v1/calls", auth, (req, res) => {
|
|
853
|
-
const limit = Math.min(parseInt(req.query.limit || "50", 10) || 50, 1e3);
|
|
854
|
-
const offset = parseInt(req.query.offset || "0", 10) || 0;
|
|
896
|
+
const limit = Math.min(Math.max(0, parseInt(req.query.limit || "50", 10) || 50), 1e3);
|
|
897
|
+
const offset = Math.max(0, parseInt(req.query.offset || "0", 10) || 0);
|
|
855
898
|
const calls = store.getCalls(limit, offset);
|
|
856
899
|
res.json({
|
|
857
900
|
data: calls,
|