@teamlearners/clawops 0.16.0 → 0.16.3
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/dist/agent/index.cjs +86 -46
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts +21 -7
- package/dist/agent/index.d.ts +21 -7
- package/dist/agent/index.js +79 -39
- package/dist/agent/index.js.map +1 -1
- package/dist/{chunk-D25IRY5V.cjs → chunk-4QQXMZ76.cjs} +3 -3
- package/dist/{chunk-D25IRY5V.cjs.map → chunk-4QQXMZ76.cjs.map} +1 -1
- package/dist/{chunk-N5EKKQVO.js → chunk-5IYJOP2D.js} +3 -3
- package/dist/{chunk-N5EKKQVO.js.map → chunk-5IYJOP2D.js.map} +1 -1
- package/dist/index.cjs +35 -35
- package/dist/index.js +2 -2
- package/package.json +1 -1
package/dist/agent/index.d.cts
CHANGED
|
@@ -320,7 +320,7 @@ interface Session {
|
|
|
320
320
|
/** Start the realtime session with the call session and tools. */
|
|
321
321
|
start(callSession: CallSession, tools?: ToolRegistry): Promise<void>;
|
|
322
322
|
/** Feed raw audio into the session. */
|
|
323
|
-
feedAudio(audio: Buffer): void;
|
|
323
|
+
feedAudio(audio: Buffer, timestamp?: number): void;
|
|
324
324
|
/** Feed DTMF digits into the LLM context and trigger a response. */
|
|
325
325
|
feedDtmf?(digits: string): Promise<void>;
|
|
326
326
|
/** Stop the session. */
|
|
@@ -430,6 +430,16 @@ interface ClawOpsAgentOptions {
|
|
|
430
430
|
logger?: Logger;
|
|
431
431
|
/** Tool 실행 관련 설정. */
|
|
432
432
|
toolConfig?: ToolConfig;
|
|
433
|
+
/**
|
|
434
|
+
* Gain applied to inbound audio (caller → AI). 1.0 = pass-through (default), 0 = mute, 2.0 = 2x amplify.
|
|
435
|
+
* AI/STT receive the gained audio, and recording captures it post-gain.
|
|
436
|
+
*/
|
|
437
|
+
rxGain?: number;
|
|
438
|
+
/**
|
|
439
|
+
* Gain applied to outbound audio (AI → caller). 1.0 = pass-through (default), 0 = mute, 2.0 = 2x amplify.
|
|
440
|
+
* The caller hears the gained audio, and recording captures it post-gain.
|
|
441
|
+
*/
|
|
442
|
+
txGain?: number;
|
|
433
443
|
}
|
|
434
444
|
declare class ClawOpsAgent {
|
|
435
445
|
private _apiKey;
|
|
@@ -454,7 +464,10 @@ declare class ClawOpsAgent {
|
|
|
454
464
|
private _pipelineLog;
|
|
455
465
|
private _isPipelineSession;
|
|
456
466
|
private _holdAudioChunks;
|
|
467
|
+
private _rxGain;
|
|
468
|
+
private _txGain;
|
|
457
469
|
constructor(options: ClawOpsAgentOptions);
|
|
470
|
+
private static _validateGain;
|
|
458
471
|
/**
|
|
459
472
|
* Register a function tool.
|
|
460
473
|
*
|
|
@@ -584,17 +597,18 @@ declare class AudioRecorder {
|
|
|
584
597
|
private _inWritten;
|
|
585
598
|
private _outWritten;
|
|
586
599
|
private _mixWritten;
|
|
587
|
-
private _startTime;
|
|
588
600
|
private _started;
|
|
601
|
+
private _baseTs;
|
|
602
|
+
private _outCursor;
|
|
589
603
|
private _log;
|
|
590
604
|
setLogger(logger: Logger): void;
|
|
591
605
|
constructor(recordingPath: string, callId: string);
|
|
592
606
|
start(): void;
|
|
593
|
-
private
|
|
607
|
+
private _timestampToBytes;
|
|
594
608
|
private _padSilence;
|
|
595
609
|
private _writeToMix;
|
|
596
|
-
writeInbound(pcm16_8k: Buffer): void;
|
|
597
|
-
writeOutbound(pcm16_8k: Buffer): void;
|
|
610
|
+
writeInbound(pcm16_8k: Buffer, mediaTsMs?: number): void;
|
|
611
|
+
writeOutbound(pcm16_8k: Buffer, mediaTsMs?: number): void;
|
|
598
612
|
stop(): void;
|
|
599
613
|
}
|
|
600
614
|
|
|
@@ -679,7 +693,7 @@ interface OpenAIRealtimeOptions {
|
|
|
679
693
|
apiKey?: string;
|
|
680
694
|
/** System prompt / instructions for the AI. */
|
|
681
695
|
systemPrompt?: string;
|
|
682
|
-
/** Model to use. Default: 'gpt-realtime-
|
|
696
|
+
/** Model to use. Default: 'gpt-realtime-2' */
|
|
683
697
|
model?: string;
|
|
684
698
|
/** Voice ID. Default: 'marin' */
|
|
685
699
|
voice?: string;
|
|
@@ -728,7 +742,7 @@ declare class OpenAIRealtime implements Session {
|
|
|
728
742
|
setHoldAudio(chunks: Buffer[]): void;
|
|
729
743
|
start(callSession: CallSession, tools?: ToolRegistry): Promise<void>;
|
|
730
744
|
feedDtmf(digits: string): Promise<void>;
|
|
731
|
-
feedAudio(audio: Buffer): void;
|
|
745
|
+
feedAudio(audio: Buffer, timestamp?: number): void;
|
|
732
746
|
stop(): Promise<void>;
|
|
733
747
|
private _sendSessionUpdate;
|
|
734
748
|
private _handleMessage;
|
package/dist/agent/index.d.ts
CHANGED
|
@@ -320,7 +320,7 @@ interface Session {
|
|
|
320
320
|
/** Start the realtime session with the call session and tools. */
|
|
321
321
|
start(callSession: CallSession, tools?: ToolRegistry): Promise<void>;
|
|
322
322
|
/** Feed raw audio into the session. */
|
|
323
|
-
feedAudio(audio: Buffer): void;
|
|
323
|
+
feedAudio(audio: Buffer, timestamp?: number): void;
|
|
324
324
|
/** Feed DTMF digits into the LLM context and trigger a response. */
|
|
325
325
|
feedDtmf?(digits: string): Promise<void>;
|
|
326
326
|
/** Stop the session. */
|
|
@@ -430,6 +430,16 @@ interface ClawOpsAgentOptions {
|
|
|
430
430
|
logger?: Logger;
|
|
431
431
|
/** Tool 실행 관련 설정. */
|
|
432
432
|
toolConfig?: ToolConfig;
|
|
433
|
+
/**
|
|
434
|
+
* Gain applied to inbound audio (caller → AI). 1.0 = pass-through (default), 0 = mute, 2.0 = 2x amplify.
|
|
435
|
+
* AI/STT receive the gained audio, and recording captures it post-gain.
|
|
436
|
+
*/
|
|
437
|
+
rxGain?: number;
|
|
438
|
+
/**
|
|
439
|
+
* Gain applied to outbound audio (AI → caller). 1.0 = pass-through (default), 0 = mute, 2.0 = 2x amplify.
|
|
440
|
+
* The caller hears the gained audio, and recording captures it post-gain.
|
|
441
|
+
*/
|
|
442
|
+
txGain?: number;
|
|
433
443
|
}
|
|
434
444
|
declare class ClawOpsAgent {
|
|
435
445
|
private _apiKey;
|
|
@@ -454,7 +464,10 @@ declare class ClawOpsAgent {
|
|
|
454
464
|
private _pipelineLog;
|
|
455
465
|
private _isPipelineSession;
|
|
456
466
|
private _holdAudioChunks;
|
|
467
|
+
private _rxGain;
|
|
468
|
+
private _txGain;
|
|
457
469
|
constructor(options: ClawOpsAgentOptions);
|
|
470
|
+
private static _validateGain;
|
|
458
471
|
/**
|
|
459
472
|
* Register a function tool.
|
|
460
473
|
*
|
|
@@ -584,17 +597,18 @@ declare class AudioRecorder {
|
|
|
584
597
|
private _inWritten;
|
|
585
598
|
private _outWritten;
|
|
586
599
|
private _mixWritten;
|
|
587
|
-
private _startTime;
|
|
588
600
|
private _started;
|
|
601
|
+
private _baseTs;
|
|
602
|
+
private _outCursor;
|
|
589
603
|
private _log;
|
|
590
604
|
setLogger(logger: Logger): void;
|
|
591
605
|
constructor(recordingPath: string, callId: string);
|
|
592
606
|
start(): void;
|
|
593
|
-
private
|
|
607
|
+
private _timestampToBytes;
|
|
594
608
|
private _padSilence;
|
|
595
609
|
private _writeToMix;
|
|
596
|
-
writeInbound(pcm16_8k: Buffer): void;
|
|
597
|
-
writeOutbound(pcm16_8k: Buffer): void;
|
|
610
|
+
writeInbound(pcm16_8k: Buffer, mediaTsMs?: number): void;
|
|
611
|
+
writeOutbound(pcm16_8k: Buffer, mediaTsMs?: number): void;
|
|
598
612
|
stop(): void;
|
|
599
613
|
}
|
|
600
614
|
|
|
@@ -679,7 +693,7 @@ interface OpenAIRealtimeOptions {
|
|
|
679
693
|
apiKey?: string;
|
|
680
694
|
/** System prompt / instructions for the AI. */
|
|
681
695
|
systemPrompt?: string;
|
|
682
|
-
/** Model to use. Default: 'gpt-realtime-
|
|
696
|
+
/** Model to use. Default: 'gpt-realtime-2' */
|
|
683
697
|
model?: string;
|
|
684
698
|
/** Voice ID. Default: 'marin' */
|
|
685
699
|
voice?: string;
|
|
@@ -728,7 +742,7 @@ declare class OpenAIRealtime implements Session {
|
|
|
728
742
|
setHoldAudio(chunks: Buffer[]): void;
|
|
729
743
|
start(callSession: CallSession, tools?: ToolRegistry): Promise<void>;
|
|
730
744
|
feedDtmf(digits: string): Promise<void>;
|
|
731
|
-
feedAudio(audio: Buffer): void;
|
|
745
|
+
feedAudio(audio: Buffer, timestamp?: number): void;
|
|
732
746
|
stop(): Promise<void>;
|
|
733
747
|
private _sendSessionUpdate;
|
|
734
748
|
private _handleMessage;
|
package/dist/agent/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_BASE_URL, AgentError, AgentConnectionError, VERSION } from '../chunk-
|
|
1
|
+
import { DEFAULT_BASE_URL, AgentError, AgentConnectionError, VERSION } from '../chunk-5IYJOP2D.js';
|
|
2
2
|
import pino from 'pino';
|
|
3
3
|
import * as fs from 'fs';
|
|
4
4
|
import * as path from 'path';
|
|
@@ -304,6 +304,27 @@ function ulawToPcm16(ulaw) {
|
|
|
304
304
|
}
|
|
305
305
|
return out;
|
|
306
306
|
}
|
|
307
|
+
function applyPcm16Gain(pcm, gain) {
|
|
308
|
+
if (!pcm.length) return Buffer.alloc(0);
|
|
309
|
+
if (gain === 1) return pcm;
|
|
310
|
+
if (!Number.isFinite(gain) || gain < 0) {
|
|
311
|
+
throw new Error("gain must be a finite number >= 0");
|
|
312
|
+
}
|
|
313
|
+
const nSamples = pcm.length >> 1;
|
|
314
|
+
const out = Buffer.alloc(nSamples * 2);
|
|
315
|
+
for (let i = 0; i < nSamples; i++) {
|
|
316
|
+
let value = Math.round(pcm.readInt16LE(i * 2) * gain);
|
|
317
|
+
if (value > 32767) value = 32767;
|
|
318
|
+
else if (value < -32768) value = -32768;
|
|
319
|
+
out.writeInt16LE(value, i * 2);
|
|
320
|
+
}
|
|
321
|
+
return out;
|
|
322
|
+
}
|
|
323
|
+
function applyUlawGain(ulaw, gain) {
|
|
324
|
+
if (!ulaw.length) return Buffer.alloc(0);
|
|
325
|
+
if (gain === 1) return ulaw;
|
|
326
|
+
return pcm16ToUlaw(applyPcm16Gain(ulawToPcm16(ulaw), gain));
|
|
327
|
+
}
|
|
307
328
|
function resamplePcm16(pcm, fromRate, toRate) {
|
|
308
329
|
if (fromRate === toRate || !pcm.length) return pcm;
|
|
309
330
|
const nSamples = pcm.length >> 1;
|
|
@@ -892,8 +913,9 @@ var AudioRecorder = class {
|
|
|
892
913
|
_inWritten = 0;
|
|
893
914
|
_outWritten = 0;
|
|
894
915
|
_mixWritten = 0;
|
|
895
|
-
_startTime = 0;
|
|
896
916
|
_started = false;
|
|
917
|
+
_baseTs = null;
|
|
918
|
+
_outCursor = 0;
|
|
897
919
|
_log = NOOP_LOGGER;
|
|
898
920
|
setLogger(logger) {
|
|
899
921
|
this._log = logger;
|
|
@@ -910,17 +932,18 @@ var AudioRecorder = class {
|
|
|
910
932
|
fs.writeSync(this._fdIn, header);
|
|
911
933
|
fs.writeSync(this._fdOut, header);
|
|
912
934
|
fs.writeSync(this._fdMix, header);
|
|
913
|
-
this._startTime = performance.now();
|
|
914
935
|
this._started = true;
|
|
915
936
|
this._log.info("Recording started: %s", this._dir);
|
|
916
937
|
}
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
938
|
+
_timestampToBytes(mediaTsMs) {
|
|
939
|
+
if (this._baseTs === null) {
|
|
940
|
+
this._baseTs = mediaTsMs;
|
|
941
|
+
}
|
|
942
|
+
const target = Math.floor((mediaTsMs - this._baseTs) * BYTES_PER_SECOND / 1e3);
|
|
943
|
+
return Math.max(0, target - target % 2);
|
|
920
944
|
}
|
|
921
|
-
_padSilence(fd, written) {
|
|
922
|
-
|
|
923
|
-
let gap = expected - written;
|
|
945
|
+
_padSilence(fd, written, target) {
|
|
946
|
+
let gap = target - written;
|
|
924
947
|
if (gap <= 0) return 0;
|
|
925
948
|
gap = gap - gap % 2;
|
|
926
949
|
if (gap > 0) {
|
|
@@ -930,9 +953,12 @@ var AudioRecorder = class {
|
|
|
930
953
|
}
|
|
931
954
|
_writeToMix(data, trackPos) {
|
|
932
955
|
if (this._fdMix === null) return;
|
|
956
|
+
data = data.subarray(0, data.length - data.length % 2);
|
|
957
|
+
if (data.length === 0) return;
|
|
933
958
|
const filePos = 44 + trackPos;
|
|
934
959
|
if (trackPos < this._mixWritten) {
|
|
935
|
-
|
|
960
|
+
let overlap = Math.min(data.length, this._mixWritten - trackPos);
|
|
961
|
+
overlap = overlap - overlap % 2;
|
|
936
962
|
const existing = Buffer.alloc(overlap);
|
|
937
963
|
fs.readSync(this._fdMix, existing, 0, overlap, filePos);
|
|
938
964
|
const mixed = mixSamples(existing, data.subarray(0, overlap));
|
|
@@ -955,10 +981,13 @@ var AudioRecorder = class {
|
|
|
955
981
|
this._mixWritten += data.length;
|
|
956
982
|
}
|
|
957
983
|
}
|
|
958
|
-
writeInbound(pcm16_8k) {
|
|
984
|
+
writeInbound(pcm16_8k, mediaTsMs = 0) {
|
|
959
985
|
if (!this._started || this._fdIn === null) return;
|
|
960
986
|
try {
|
|
961
|
-
|
|
987
|
+
pcm16_8k = pcm16_8k.subarray(0, pcm16_8k.length - pcm16_8k.length % 2);
|
|
988
|
+
if (pcm16_8k.length === 0) return;
|
|
989
|
+
const target = this._timestampToBytes(mediaTsMs);
|
|
990
|
+
const gap = this._padSilence(this._fdIn, this._inWritten, target);
|
|
962
991
|
this._inWritten += gap;
|
|
963
992
|
const posBefore = this._inWritten;
|
|
964
993
|
fs.writeSync(this._fdIn, pcm16_8k);
|
|
@@ -968,14 +997,21 @@ var AudioRecorder = class {
|
|
|
968
997
|
this._log.error({ err }, "Recording write error (inbound)");
|
|
969
998
|
}
|
|
970
999
|
}
|
|
971
|
-
writeOutbound(pcm16_8k) {
|
|
1000
|
+
writeOutbound(pcm16_8k, mediaTsMs) {
|
|
972
1001
|
if (!this._started || this._fdOut === null) return;
|
|
973
1002
|
try {
|
|
974
|
-
|
|
1003
|
+
pcm16_8k = pcm16_8k.subarray(0, pcm16_8k.length - pcm16_8k.length % 2);
|
|
1004
|
+
if (pcm16_8k.length === 0) return;
|
|
1005
|
+
let target = this._outCursor;
|
|
1006
|
+
if (mediaTsMs !== void 0) {
|
|
1007
|
+
target = Math.max(target, this._timestampToBytes(mediaTsMs));
|
|
1008
|
+
}
|
|
1009
|
+
const gap = this._padSilence(this._fdOut, this._outWritten, target);
|
|
975
1010
|
this._outWritten += gap;
|
|
976
1011
|
const posBefore = this._outWritten;
|
|
977
1012
|
fs.writeSync(this._fdOut, pcm16_8k);
|
|
978
1013
|
this._outWritten += pcm16_8k.length;
|
|
1014
|
+
this._outCursor = this._outWritten;
|
|
979
1015
|
this._writeToMix(pcm16_8k, posBefore);
|
|
980
1016
|
} catch (err) {
|
|
981
1017
|
this._log.error({ err }, "Recording write error (outbound)");
|
|
@@ -1671,7 +1707,7 @@ var ATTR_CALL_DIRECTION = "clawops.call.direction";
|
|
|
1671
1707
|
var ATTR_AGENT_ID = "clawops.agent.id";
|
|
1672
1708
|
|
|
1673
1709
|
// src/agent/agent.ts
|
|
1674
|
-
var ClawOpsAgent = class {
|
|
1710
|
+
var ClawOpsAgent = class _ClawOpsAgent {
|
|
1675
1711
|
_apiKey;
|
|
1676
1712
|
_accountId;
|
|
1677
1713
|
_baseUrl;
|
|
@@ -1694,6 +1730,8 @@ var ClawOpsAgent = class {
|
|
|
1694
1730
|
_pipelineLog;
|
|
1695
1731
|
_isPipelineSession = false;
|
|
1696
1732
|
_holdAudioChunks = null;
|
|
1733
|
+
_rxGain;
|
|
1734
|
+
_txGain;
|
|
1697
1735
|
constructor(options) {
|
|
1698
1736
|
this._apiKey = options.apiKey ?? process.env["CLAWOPS_API_KEY"] ?? "";
|
|
1699
1737
|
this._accountId = options.accountId ?? process.env["CLAWOPS_ACCOUNT_ID"] ?? "";
|
|
@@ -1705,6 +1743,8 @@ var ClawOpsAgent = class {
|
|
|
1705
1743
|
this._mcpServers = options.mcpServers ?? [];
|
|
1706
1744
|
this._builtinTools = resolveBuiltinTools(options.builtinTools ?? "all" /* ALL */);
|
|
1707
1745
|
this._passiveDtmfDebounceMs = options.passiveDtmfDebounceMs ?? 500;
|
|
1746
|
+
this._rxGain = _ClawOpsAgent._validateGain("rxGain", options.rxGain ?? 1);
|
|
1747
|
+
this._txGain = _ClawOpsAgent._validateGain("txGain", options.txGain ?? 1);
|
|
1708
1748
|
if (options.tracing) {
|
|
1709
1749
|
setTracingConfig(options.tracing);
|
|
1710
1750
|
}
|
|
@@ -1715,6 +1755,12 @@ var ClawOpsAgent = class {
|
|
|
1715
1755
|
this._holdAudioChunks = loadHoldAudio(options.toolConfig.holdAudio);
|
|
1716
1756
|
}
|
|
1717
1757
|
}
|
|
1758
|
+
static _validateGain(name, gain) {
|
|
1759
|
+
if (typeof gain !== "number" || !Number.isFinite(gain) || gain < 0) {
|
|
1760
|
+
throw new AgentError(`${name}=${gain} must be a finite number >= 0`);
|
|
1761
|
+
}
|
|
1762
|
+
return gain;
|
|
1763
|
+
}
|
|
1718
1764
|
/**
|
|
1719
1765
|
* Register a function tool.
|
|
1720
1766
|
*
|
|
@@ -1997,9 +2043,14 @@ var ClawOpsAgent = class {
|
|
|
1997
2043
|
}
|
|
1998
2044
|
const mediaWs = new MediaWebSocket();
|
|
1999
2045
|
mediaWs.setLogger(this._log);
|
|
2046
|
+
let latestMediaTs = 0;
|
|
2000
2047
|
session._bindTransport(
|
|
2001
2048
|
(audio) => {
|
|
2002
|
-
|
|
2049
|
+
const gained = applyUlawGain(audio, this._txGain);
|
|
2050
|
+
if (recorder) {
|
|
2051
|
+
recorder.writeOutbound(ulawToPcm16(gained), latestMediaTs);
|
|
2052
|
+
}
|
|
2053
|
+
mediaWs.sendAudio(gained.toString("base64"));
|
|
2003
2054
|
session.recordFirstResponse();
|
|
2004
2055
|
},
|
|
2005
2056
|
() => {
|
|
@@ -2036,12 +2087,14 @@ var ClawOpsAgent = class {
|
|
|
2036
2087
|
sessionHandler.setHoldAudio(this._holdAudioChunks);
|
|
2037
2088
|
}
|
|
2038
2089
|
this._callSessions.set(session.callId, sessionHandler);
|
|
2039
|
-
mediaWs.onAudio((ulawAudio,
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
}
|
|
2090
|
+
mediaWs.onAudio((ulawAudio, timestamp) => {
|
|
2091
|
+
latestMediaTs = timestamp;
|
|
2092
|
+
const gained = applyUlawGain(ulawAudio, this._rxGain);
|
|
2043
2093
|
if (recorder) {
|
|
2044
|
-
recorder.writeInbound(ulawToPcm16(
|
|
2094
|
+
recorder.writeInbound(ulawToPcm16(gained), timestamp);
|
|
2095
|
+
}
|
|
2096
|
+
if (sessionHandler) {
|
|
2097
|
+
sessionHandler.feedAudio(gained, timestamp);
|
|
2045
2098
|
}
|
|
2046
2099
|
});
|
|
2047
2100
|
mediaWs.onDtmf((digit) => {
|
|
@@ -2512,10 +2565,6 @@ var PipelineSession = class {
|
|
|
2512
2565
|
sampleRate: this._sampleRate
|
|
2513
2566
|
})) {
|
|
2514
2567
|
if (!this._running || !this._speaking) break;
|
|
2515
|
-
if (this._recorder) {
|
|
2516
|
-
const pcm8k2 = this._sampleRate !== 8e3 ? resamplePcm16(audioChunk, this._sampleRate, 8e3) : audioChunk;
|
|
2517
|
-
this._recorder.writeOutbound(pcm8k2);
|
|
2518
|
-
}
|
|
2519
2568
|
const pcm8k = resamplePcm16(audioChunk, this._sampleRate, 8e3);
|
|
2520
2569
|
const ulaw = pcm16ToUlaw(pcm8k);
|
|
2521
2570
|
for (let off = 0; off < ulaw.length; off += 160) {
|
|
@@ -2585,7 +2634,7 @@ var OpenAIRealtime = class {
|
|
|
2585
2634
|
constructor(options = {}) {
|
|
2586
2635
|
this._apiKey = options.apiKey ?? process.env["OPENAI_API_KEY"] ?? "";
|
|
2587
2636
|
this._systemPrompt = options.systemPrompt ?? "";
|
|
2588
|
-
this._model = options.model ?? "gpt-realtime-
|
|
2637
|
+
this._model = options.model ?? "gpt-realtime-2";
|
|
2589
2638
|
this._voice = options.voice ?? "marin";
|
|
2590
2639
|
this._language = options.language ?? "ko";
|
|
2591
2640
|
this._turnDetection = options.turnDetection !== void 0 ? options.turnDetection : { type: "semantic_vad", eagerness: "medium", interrupt_response: true };
|
|
@@ -2660,8 +2709,8 @@ var OpenAIRealtime = class {
|
|
|
2660
2709
|
});
|
|
2661
2710
|
this._send({ type: "response.create" });
|
|
2662
2711
|
}
|
|
2663
|
-
feedAudio(audio) {
|
|
2664
|
-
this._latestMediaTs =
|
|
2712
|
+
feedAudio(audio, timestamp) {
|
|
2713
|
+
this._latestMediaTs = timestamp ?? this._latestMediaTs;
|
|
2665
2714
|
if (this._ws && this._ws.readyState === 1 && !this._closed) {
|
|
2666
2715
|
this._send({
|
|
2667
2716
|
type: "input_audio_buffer.append",
|
|
@@ -2772,7 +2821,7 @@ var OpenAIRealtime = class {
|
|
|
2772
2821
|
if (this._playback === null) {
|
|
2773
2822
|
this._playback = {
|
|
2774
2823
|
itemId: msg["item_id"] || "",
|
|
2775
|
-
startTs: this._latestMediaTs
|
|
2824
|
+
startTs: this._latestMediaTs,
|
|
2776
2825
|
sentChunks: 0,
|
|
2777
2826
|
generating: true,
|
|
2778
2827
|
audioRemainder: Buffer.alloc(0)
|
|
@@ -2782,9 +2831,6 @@ var OpenAIRealtime = class {
|
|
|
2782
2831
|
}
|
|
2783
2832
|
const pb = this._playback;
|
|
2784
2833
|
const ulaw = Buffer.from(msg["delta"], "base64");
|
|
2785
|
-
if (this._recorder) {
|
|
2786
|
-
this._recorder.writeOutbound(ulawToPcm16(ulaw));
|
|
2787
|
-
}
|
|
2788
2834
|
const combined = Buffer.concat([pb.audioRemainder, ulaw]);
|
|
2789
2835
|
const chunkSize = 160;
|
|
2790
2836
|
const fullEnd = Math.floor(combined.length / chunkSize) * chunkSize;
|
|
@@ -2808,7 +2854,7 @@ var OpenAIRealtime = class {
|
|
|
2808
2854
|
if (pb === null) {
|
|
2809
2855
|
return;
|
|
2810
2856
|
}
|
|
2811
|
-
const playedMs = Math.max(0,
|
|
2857
|
+
const playedMs = Math.max(0, this._latestMediaTs - pb.startTs);
|
|
2812
2858
|
this._log.info(
|
|
2813
2859
|
"[Interrupt] item=%s played=%dms total=%dms",
|
|
2814
2860
|
pb.itemId,
|
|
@@ -3100,9 +3146,6 @@ var GeminiRealtime = class _GeminiRealtime {
|
|
|
3100
3146
|
feedAudio(audio) {
|
|
3101
3147
|
if (this._session && !this._closed) {
|
|
3102
3148
|
const pcm8k = ulawToPcm16(audio);
|
|
3103
|
-
if (this._recorder) {
|
|
3104
|
-
this._recorder.writeInbound(pcm8k);
|
|
3105
|
-
}
|
|
3106
3149
|
const pcm16k = resamplePcm16(pcm8k, 8e3, 16e3);
|
|
3107
3150
|
this._session.sendRealtimeInput({
|
|
3108
3151
|
audio: {
|
|
@@ -3200,9 +3243,6 @@ var GeminiRealtime = class _GeminiRealtime {
|
|
|
3200
3243
|
_handleAudioData(b64Data) {
|
|
3201
3244
|
if (!this._call) return;
|
|
3202
3245
|
const pcm24k = Buffer.from(b64Data, "base64");
|
|
3203
|
-
if (this._recorder) {
|
|
3204
|
-
this._recorder.writeOutbound(resamplePcm16(pcm24k, 24e3, 8e3));
|
|
3205
|
-
}
|
|
3206
3246
|
const pcm8k = resamplePcm16(pcm24k, 24e3, 8e3);
|
|
3207
3247
|
const ulaw = pcm16ToUlaw(pcm8k);
|
|
3208
3248
|
const combined = Buffer.concat([this._audioRemainder, ulaw]);
|