@teamlearners/clawops 0.5.5 → 0.5.8
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 +581 -627
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts +99 -74
- package/dist/agent/index.d.ts +99 -74
- package/dist/agent/index.js +578 -628
- package/dist/agent/index.js.map +1 -1
- package/package.json +2 -2
package/dist/agent/index.cjs
CHANGED
|
@@ -1485,6 +1485,7 @@ var ClawOpsAgent = class {
|
|
|
1485
1485
|
}
|
|
1486
1486
|
/** Connect to the ClawOps platform and start listening for calls. */
|
|
1487
1487
|
async connect() {
|
|
1488
|
+
if (this._controlWs) return;
|
|
1488
1489
|
if (!this._apiKey) {
|
|
1489
1490
|
throw new chunk6IQN5RQD_cjs.AgentError("API key is required. Set CLAWOPS_API_KEY or pass apiKey option.");
|
|
1490
1491
|
}
|
|
@@ -1799,20 +1800,13 @@ var ClawOpsAgent = class {
|
|
|
1799
1800
|
}
|
|
1800
1801
|
};
|
|
1801
1802
|
|
|
1802
|
-
// src/agent/pipeline/
|
|
1803
|
-
var
|
|
1804
|
-
var HANG_UP_TOOL = {
|
|
1805
|
-
type: "function",
|
|
1803
|
+
// src/agent/pipeline/builtin-tool-schemas.ts
|
|
1804
|
+
var HANG_UP = {
|
|
1806
1805
|
name: "hang_up",
|
|
1807
1806
|
description: "End the phone call. Use when the conversation is finished or the caller says goodbye.",
|
|
1808
|
-
parameters: {
|
|
1809
|
-
type: "object",
|
|
1810
|
-
properties: {},
|
|
1811
|
-
required: []
|
|
1812
|
-
}
|
|
1807
|
+
parameters: { type: "object", properties: {} }
|
|
1813
1808
|
};
|
|
1814
|
-
var
|
|
1815
|
-
type: "function",
|
|
1809
|
+
var COLLECT_DTMF = {
|
|
1816
1810
|
name: "collect_dtmf",
|
|
1817
1811
|
description: "\uC0AC\uC6A9\uC790\uB85C\uBD80\uD130 DTMF(\uC804\uD654 \uD0A4\uD328\uB4DC) \uC785\uB825\uC744 \uC218\uC9D1\uD569\uB2C8\uB2E4. \uBC18\uB4DC\uC2DC \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBB34\uC5C7\uC744 \uC785\uB825\uD574\uC57C \uD558\uB294\uC9C0 \uC548\uB0B4\uD55C \uD6C4 \uD638\uCD9C\uD558\uC138\uC694.",
|
|
1818
1812
|
parameters: {
|
|
@@ -1825,8 +1819,7 @@ var COLLECT_DTMF_TOOL = {
|
|
|
1825
1819
|
required: ["max_digits"]
|
|
1826
1820
|
}
|
|
1827
1821
|
};
|
|
1828
|
-
var
|
|
1829
|
-
type: "function",
|
|
1822
|
+
var SEND_DTMF = {
|
|
1830
1823
|
name: "send_dtmf",
|
|
1831
1824
|
description: "DTMF \uC2E0\uD638\uB97C \uC804\uC1A1\uD569\uB2C8\uB2E4. ARS \uBA54\uB274 \uD0D0\uC0C9\uC774\uB098 \uB0B4\uC120\uBC88\uD638 \uC785\uB825 \uC2DC \uC0AC\uC6A9\uD569\uB2C8\uB2E4.",
|
|
1832
1825
|
parameters: {
|
|
@@ -1834,181 +1827,512 @@ var SEND_DTMF_TOOL = {
|
|
|
1834
1827
|
properties: {
|
|
1835
1828
|
digits: {
|
|
1836
1829
|
type: "string",
|
|
1837
|
-
description: "\uC804\uC1A1\uD560 \uBC88\uD638 (0-9, *, #). 'w'\uB294 500ms \uB300\uAE30, 'W'\uB294 1000ms \uB300\uAE30."
|
|
1830
|
+
description: "\uC804\uC1A1\uD560 \uBC88\uD638 (0-9, *, #). 'w'\uB294 500ms \uB300\uAE30, 'W'\uB294 1000ms \uB300\uAE30. \uC608: '1', '1234#', '1w2'"
|
|
1838
1831
|
}
|
|
1839
1832
|
},
|
|
1840
1833
|
required: ["digits"]
|
|
1841
1834
|
}
|
|
1842
1835
|
};
|
|
1843
|
-
var
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1836
|
+
var TOOL_MAP = /* @__PURE__ */ new Map([
|
|
1837
|
+
["hang_up" /* HANG_UP */, HANG_UP],
|
|
1838
|
+
["collect_dtmf" /* COLLECT_DTMF */, COLLECT_DTMF],
|
|
1839
|
+
["send_dtmf" /* SEND_DTMF */, SEND_DTMF]
|
|
1840
|
+
]);
|
|
1841
|
+
var BUILTIN_TOOL_NAMES = new Set(
|
|
1842
|
+
Array.from(TOOL_MAP.values()).map((s) => s.name)
|
|
1843
|
+
);
|
|
1844
|
+
function toChatCompletions(schema) {
|
|
1845
|
+
return {
|
|
1846
|
+
type: "function",
|
|
1847
|
+
function: {
|
|
1848
|
+
name: schema.name,
|
|
1849
|
+
description: schema.description,
|
|
1850
|
+
parameters: schema.parameters
|
|
1851
|
+
}
|
|
1852
|
+
};
|
|
1853
|
+
}
|
|
1854
|
+
function toRealtime(schema) {
|
|
1855
|
+
return {
|
|
1856
|
+
type: "function",
|
|
1857
|
+
name: schema.name,
|
|
1858
|
+
description: schema.description,
|
|
1859
|
+
parameters: schema.parameters
|
|
1860
|
+
};
|
|
1861
|
+
}
|
|
1862
|
+
function toGemini(schema) {
|
|
1863
|
+
return {
|
|
1864
|
+
name: schema.name,
|
|
1865
|
+
description: schema.description,
|
|
1866
|
+
parameters: schema.parameters
|
|
1867
|
+
};
|
|
1868
|
+
}
|
|
1869
|
+
var CONVERTERS = {
|
|
1870
|
+
chat: toChatCompletions,
|
|
1871
|
+
realtime: toRealtime,
|
|
1872
|
+
gemini: toGemini
|
|
1873
|
+
};
|
|
1874
|
+
function getBuiltinToolSchemas(builtinTools, fmt) {
|
|
1875
|
+
const converter = CONVERTERS[fmt];
|
|
1876
|
+
const result = [];
|
|
1877
|
+
for (const [toolEnum, schema] of TOOL_MAP) {
|
|
1878
|
+
if (builtinTools === null || builtinTools.has(toolEnum)) {
|
|
1879
|
+
result.push(converter(schema));
|
|
1880
|
+
}
|
|
1855
1881
|
}
|
|
1856
|
-
|
|
1857
|
-
|
|
1882
|
+
return result;
|
|
1883
|
+
}
|
|
1884
|
+
function isBuiltinTool(name) {
|
|
1885
|
+
return BUILTIN_TOOL_NAMES.has(name);
|
|
1886
|
+
}
|
|
1887
|
+
async function executeBuiltinTool(funcName, args, call) {
|
|
1888
|
+
if (funcName === "hang_up") {
|
|
1889
|
+
await call.hangup();
|
|
1890
|
+
return "";
|
|
1858
1891
|
}
|
|
1859
|
-
|
|
1860
|
-
|
|
1892
|
+
if (funcName === "collect_dtmf") {
|
|
1893
|
+
try {
|
|
1894
|
+
const result = await call.collectDtmf({
|
|
1895
|
+
maxDigits: args["max_digits"] ?? 4,
|
|
1896
|
+
finishOnKey: args["finish_on_key"] ?? "#",
|
|
1897
|
+
timeout: args["timeout"] ?? 5
|
|
1898
|
+
});
|
|
1899
|
+
return result || "(\uD0C0\uC784\uC544\uC6C3 - \uC785\uB825 \uC5C6\uC74C)";
|
|
1900
|
+
} catch (e) {
|
|
1901
|
+
return `Error: ${e}`;
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
if (funcName === "send_dtmf") {
|
|
1905
|
+
try {
|
|
1906
|
+
await call.sendDtmfSequence(args["digits"] ?? "");
|
|
1907
|
+
return "sent";
|
|
1908
|
+
} catch (e) {
|
|
1909
|
+
return `Error: ${e}`;
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
return null;
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
// src/agent/pipeline/pipeline-session.ts
|
|
1916
|
+
var PipelineSession = class {
|
|
1917
|
+
_stt;
|
|
1918
|
+
_llm;
|
|
1919
|
+
_tts;
|
|
1920
|
+
_systemPrompt;
|
|
1921
|
+
_greeting;
|
|
1922
|
+
_language;
|
|
1923
|
+
_temperature;
|
|
1924
|
+
_maxTokens;
|
|
1925
|
+
_sampleRate;
|
|
1926
|
+
_interruptOnSpeech;
|
|
1927
|
+
_callSession = null;
|
|
1861
1928
|
_tools = null;
|
|
1862
1929
|
_recorder = null;
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
this.
|
|
1874
|
-
this._systemPrompt = options.systemPrompt ?? "";
|
|
1875
|
-
this._model = options.model ?? "gpt-realtime-1.5";
|
|
1876
|
-
this._voice = options.voice ?? "marin";
|
|
1877
|
-
this._language = options.language ?? "ko";
|
|
1878
|
-
this._eagerness = options.eagerness ?? "high";
|
|
1930
|
+
_conversation = [];
|
|
1931
|
+
_audioBuffer = [];
|
|
1932
|
+
_running = false;
|
|
1933
|
+
_speaking = false;
|
|
1934
|
+
_builtinTools = null;
|
|
1935
|
+
_log = NOOP_LOGGER;
|
|
1936
|
+
constructor(options) {
|
|
1937
|
+
this._stt = options.stt;
|
|
1938
|
+
this._llm = options.llm;
|
|
1939
|
+
this._tts = options.tts;
|
|
1940
|
+
this._systemPrompt = options.systemPrompt;
|
|
1879
1941
|
this._greeting = options.greeting ?? true;
|
|
1942
|
+
this._language = options.language ?? "ko";
|
|
1943
|
+
this._temperature = options.temperature;
|
|
1944
|
+
this._maxTokens = options.maxTokens;
|
|
1945
|
+
this._sampleRate = options.sampleRate ?? 8e3;
|
|
1946
|
+
this._interruptOnSpeech = options.interruptOnSpeech ?? true;
|
|
1947
|
+
if (options.toolRegistry) this._tools = options.toolRegistry;
|
|
1948
|
+
if (options.recorder) this._recorder = options.recorder;
|
|
1880
1949
|
}
|
|
1881
|
-
/** Inject per-call ToolRegistry. */
|
|
1882
1950
|
setToolRegistry(registry) {
|
|
1883
1951
|
this._tools = registry;
|
|
1884
1952
|
}
|
|
1885
|
-
/** Inject per-call AudioRecorder. */
|
|
1886
1953
|
setRecorder(recorder) {
|
|
1887
1954
|
this._recorder = recorder;
|
|
1888
1955
|
}
|
|
1889
|
-
|
|
1890
|
-
this.
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
this.
|
|
1894
|
-
this.
|
|
1895
|
-
|
|
1896
|
-
this._audioRemainder = Buffer.alloc(0);
|
|
1897
|
-
if (!this._apiKey) {
|
|
1898
|
-
throw new Error("OpenAI API key is required. Set OPENAI_API_KEY or pass apiKey option.");
|
|
1956
|
+
setBuiltinTools(tools) {
|
|
1957
|
+
this._builtinTools = tools;
|
|
1958
|
+
}
|
|
1959
|
+
setLogger(logger) {
|
|
1960
|
+
this._log = logger;
|
|
1961
|
+
if ("setLogger" in this._stt && typeof this._stt.setLogger === "function") {
|
|
1962
|
+
this._stt.setLogger(logger);
|
|
1899
1963
|
}
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
this._send({ type: "response.create" });
|
|
1915
|
-
}
|
|
1916
|
-
resolve();
|
|
1917
|
-
});
|
|
1918
|
-
ws.on("message", (data) => {
|
|
1919
|
-
try {
|
|
1920
|
-
const msg = JSON.parse(data.toString());
|
|
1921
|
-
this._handleMessage(msg);
|
|
1922
|
-
} catch {
|
|
1923
|
-
}
|
|
1924
|
-
});
|
|
1925
|
-
ws.on("close", () => {
|
|
1926
|
-
this._closed = true;
|
|
1964
|
+
if ("setLogger" in this._tts && typeof this._tts.setLogger === "function") {
|
|
1965
|
+
this._tts.setLogger(logger);
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
async start(callSession, tools) {
|
|
1969
|
+
this._callSession = callSession;
|
|
1970
|
+
this._tools = tools ?? null;
|
|
1971
|
+
this._running = true;
|
|
1972
|
+
this._log.info("PipelineSession started");
|
|
1973
|
+
this._conversation = [];
|
|
1974
|
+
if (this._systemPrompt) {
|
|
1975
|
+
this._conversation.push({
|
|
1976
|
+
role: "system",
|
|
1977
|
+
content: this._systemPrompt
|
|
1927
1978
|
});
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
}
|
|
1932
|
-
this._log.error({ err }, "OpenAI Realtime WS error");
|
|
1979
|
+
}
|
|
1980
|
+
if (this._greeting) {
|
|
1981
|
+
this._generateGreeting().catch((err) => {
|
|
1982
|
+
this._log.error({ err }, "Greeting error");
|
|
1933
1983
|
});
|
|
1984
|
+
}
|
|
1985
|
+
this._runSttLoop().catch((err) => {
|
|
1986
|
+
this._log.error({ err }, "STT loop error");
|
|
1934
1987
|
});
|
|
1935
1988
|
}
|
|
1936
|
-
async feedDtmf(digits) {
|
|
1937
|
-
await this._waitForResponseDone();
|
|
1938
|
-
this._send({
|
|
1939
|
-
type: "conversation.item.create",
|
|
1940
|
-
item: {
|
|
1941
|
-
type: "message",
|
|
1942
|
-
role: "user",
|
|
1943
|
-
content: [{ type: "input_text", text: `[DTMF \uC785\uB825: ${digits}]` }]
|
|
1944
|
-
}
|
|
1945
|
-
});
|
|
1946
|
-
this._send({ type: "response.create" });
|
|
1947
|
-
}
|
|
1948
1989
|
feedAudio(audio) {
|
|
1949
|
-
if (this.
|
|
1950
|
-
this.
|
|
1951
|
-
type: "input_audio_buffer.append",
|
|
1952
|
-
audio: audio.toString("base64")
|
|
1953
|
-
});
|
|
1990
|
+
if (this._running) {
|
|
1991
|
+
this._audioBuffer.push(audio);
|
|
1954
1992
|
}
|
|
1955
1993
|
}
|
|
1994
|
+
async feedDtmf(digits) {
|
|
1995
|
+
this._conversation.push({
|
|
1996
|
+
role: "user",
|
|
1997
|
+
content: `[DTMF \uC785\uB825: ${digits}]`
|
|
1998
|
+
});
|
|
1999
|
+
await this._respond();
|
|
2000
|
+
}
|
|
1956
2001
|
async stop() {
|
|
1957
|
-
this.
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
2002
|
+
this._running = false;
|
|
2003
|
+
this._log.info("PipelineSession stopped");
|
|
2004
|
+
this._audioBuffer = [];
|
|
2005
|
+
}
|
|
2006
|
+
async _runSttLoop() {
|
|
2007
|
+
const audioStream = this._createAudioStream();
|
|
2008
|
+
for await (const event of this._stt.transcribe(audioStream, {
|
|
2009
|
+
sampleRate: this._sampleRate
|
|
2010
|
+
})) {
|
|
2011
|
+
if (!this._running) break;
|
|
2012
|
+
if (event.type === "interim" && this._speaking && this._interruptOnSpeech) {
|
|
2013
|
+
this._speaking = false;
|
|
2014
|
+
if (this._callSession) {
|
|
2015
|
+
this._callSession.clearAudio();
|
|
2016
|
+
}
|
|
2017
|
+
this._log.info('Barge-in: "%s"', event.transcript.substring(0, 30));
|
|
2018
|
+
}
|
|
2019
|
+
if (event.type === "final" && event.transcript.trim()) {
|
|
2020
|
+
this._log.info("STT: %s", event.transcript);
|
|
2021
|
+
await this._handleUserSpeech(event.transcript);
|
|
2022
|
+
}
|
|
1961
2023
|
}
|
|
1962
2024
|
}
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
this._send({
|
|
1973
|
-
type: "session.update",
|
|
1974
|
-
session: {
|
|
1975
|
-
modalities: ["text", "audio"],
|
|
1976
|
-
voice: this._voice,
|
|
1977
|
-
instructions: this._systemPrompt,
|
|
1978
|
-
input_audio_format: "g711_ulaw",
|
|
1979
|
-
output_audio_format: "g711_ulaw",
|
|
1980
|
-
input_audio_transcription: {
|
|
1981
|
-
model: "whisper-1",
|
|
1982
|
-
language: this._language
|
|
1983
|
-
},
|
|
1984
|
-
input_audio_noise_reduction: { type: "far_field" },
|
|
1985
|
-
turn_detection: {
|
|
1986
|
-
type: "semantic_vad",
|
|
1987
|
-
interrupt_response: true,
|
|
1988
|
-
eagerness: this._eagerness
|
|
1989
|
-
},
|
|
1990
|
-
tools: toolSchemas
|
|
2025
|
+
async *_createAudioStream() {
|
|
2026
|
+
while (this._running) {
|
|
2027
|
+
if (this._audioBuffer.length > 0) {
|
|
2028
|
+
const ulaw = this._audioBuffer.shift();
|
|
2029
|
+
const pcm8k = ulawToPcm16(ulaw);
|
|
2030
|
+
const pcm16k = resamplePcm16(pcm8k, 8e3, 16e3);
|
|
2031
|
+
yield pcm16k;
|
|
2032
|
+
} else {
|
|
2033
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
1991
2034
|
}
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
async _generateGreeting() {
|
|
2038
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
2039
|
+
await this._respond();
|
|
2040
|
+
}
|
|
2041
|
+
async _handleUserSpeech(transcript) {
|
|
2042
|
+
this._conversation.push({ role: "user", content: transcript });
|
|
2043
|
+
await this._respond();
|
|
2044
|
+
}
|
|
2045
|
+
_buildEffectiveTools() {
|
|
2046
|
+
const builtinSchemas = getBuiltinToolSchemas(this._builtinTools, "chat");
|
|
2047
|
+
const dtmfSchemas = builtinSchemas.filter((s) => {
|
|
2048
|
+
const name = s["function"]?.["name"];
|
|
2049
|
+
return name === "collect_dtmf" || name === "send_dtmf";
|
|
1992
2050
|
});
|
|
2051
|
+
if (dtmfSchemas.length === 0) return this._tools ?? void 0;
|
|
2052
|
+
const base = this._tools ? this._tools.fork() : new ToolRegistry();
|
|
2053
|
+
for (const schema of dtmfSchemas) {
|
|
2054
|
+
const fn = schema["function"];
|
|
2055
|
+
const params = fn["parameters"];
|
|
2056
|
+
base.register({
|
|
2057
|
+
name: fn["name"],
|
|
2058
|
+
description: fn["description"],
|
|
2059
|
+
parameters: params["properties"] ?? {},
|
|
2060
|
+
required: params["required"] ?? [],
|
|
2061
|
+
handler: async () => ""
|
|
2062
|
+
});
|
|
2063
|
+
}
|
|
2064
|
+
return base;
|
|
1993
2065
|
}
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
2066
|
+
async _respond() {
|
|
2067
|
+
let fullResponse = "";
|
|
2068
|
+
const textChunks = [];
|
|
2069
|
+
const effectiveTools = this._buildEffectiveTools();
|
|
2070
|
+
const llmStream = this._llm.generate(this._conversation, {
|
|
2071
|
+
tools: effectiveTools,
|
|
2072
|
+
temperature: this._temperature,
|
|
2073
|
+
maxTokens: this._maxTokens
|
|
2074
|
+
});
|
|
2075
|
+
for await (const chunk of llmStream) {
|
|
2076
|
+
if (!this._running) break;
|
|
2077
|
+
if (chunk.type === "text" && chunk.text) {
|
|
2078
|
+
textChunks.push(chunk.text);
|
|
2079
|
+
fullResponse += chunk.text;
|
|
2080
|
+
} else if (chunk.type === "tool_call" && chunk.toolCall) {
|
|
2081
|
+
await this._handleToolCall(chunk);
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
if (fullResponse.trim()) {
|
|
2085
|
+
this._log.info("Assistant: %s", fullResponse.substring(0, 100));
|
|
2086
|
+
this._conversation.push({ role: "assistant", content: fullResponse });
|
|
2087
|
+
await this._synthesizeAndSend(fullResponse);
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
async _handleToolCall(chunk) {
|
|
2091
|
+
if (!chunk.toolCall) return;
|
|
2092
|
+
const { id, name, arguments: argsStr } = chunk.toolCall;
|
|
2093
|
+
try {
|
|
2094
|
+
const args = JSON.parse(argsStr);
|
|
2095
|
+
if (BUILTIN_TOOL_NAMES.has(name) && this._callSession) {
|
|
2096
|
+
const result2 = await executeBuiltinTool(name, args, this._callSession);
|
|
2097
|
+
if (result2 !== null) {
|
|
2098
|
+
if (name === "hang_up") return;
|
|
2099
|
+
this._conversation.push({ role: "tool", content: result2, tool_call_id: id, name });
|
|
2100
|
+
await this._respond();
|
|
2101
|
+
return;
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
if (!this._tools) return;
|
|
2105
|
+
const result = await this._tools.call(name, args);
|
|
2106
|
+
this._conversation.push({
|
|
2107
|
+
role: "assistant",
|
|
2108
|
+
content: ""
|
|
2109
|
+
// Tool call info stored in the message flow
|
|
2110
|
+
});
|
|
2111
|
+
this._conversation.push({
|
|
2112
|
+
role: "tool",
|
|
2113
|
+
content: typeof result === "string" ? result : JSON.stringify(result),
|
|
2114
|
+
tool_call_id: id,
|
|
2115
|
+
name
|
|
2116
|
+
});
|
|
2117
|
+
const effectiveTools = this._buildEffectiveTools();
|
|
2118
|
+
let followUpText = "";
|
|
2119
|
+
const followUpStream = this._llm.generate(this._conversation, {
|
|
2120
|
+
tools: effectiveTools,
|
|
2121
|
+
temperature: this._temperature,
|
|
2122
|
+
maxTokens: this._maxTokens
|
|
2123
|
+
});
|
|
2124
|
+
for await (const followChunk of followUpStream) {
|
|
2125
|
+
if (!this._running) break;
|
|
2126
|
+
if (followChunk.type === "text" && followChunk.text) {
|
|
2127
|
+
followUpText += followChunk.text;
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
if (followUpText.trim()) {
|
|
2131
|
+
this._conversation.push({ role: "assistant", content: followUpText });
|
|
2132
|
+
await this._synthesizeAndSend(followUpText);
|
|
2133
|
+
}
|
|
2134
|
+
} catch (err) {
|
|
2135
|
+
this._log.error({ err }, "Tool call failed: %s", name);
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
async _synthesizeAndSend(text) {
|
|
2139
|
+
if (!this._callSession || !this._running) return;
|
|
2140
|
+
this._speaking = true;
|
|
2141
|
+
try {
|
|
2142
|
+
for await (const audioChunk of this._tts.synthesize(text, {
|
|
2143
|
+
sampleRate: this._sampleRate
|
|
2144
|
+
})) {
|
|
2145
|
+
if (!this._running || !this._speaking) break;
|
|
2146
|
+
if (this._recorder) {
|
|
2147
|
+
const pcm8k2 = this._sampleRate !== 8e3 ? resamplePcm16(audioChunk, this._sampleRate, 8e3) : audioChunk;
|
|
2148
|
+
this._recorder.writeOutbound(pcm8k2);
|
|
2149
|
+
}
|
|
2150
|
+
const pcm8k = resamplePcm16(audioChunk, this._sampleRate, 8e3);
|
|
2151
|
+
const ulaw = pcm16ToUlaw(pcm8k);
|
|
2152
|
+
for (let off = 0; off < ulaw.length; off += 160) {
|
|
2153
|
+
let chunk = ulaw.subarray(off, off + 160);
|
|
2154
|
+
if (chunk.length < 160) {
|
|
2155
|
+
chunk = Buffer.concat([chunk, Buffer.alloc(160 - chunk.length, 255)]);
|
|
2156
|
+
}
|
|
2157
|
+
this._callSession.sendAudio(chunk);
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
} catch (err) {
|
|
2161
|
+
this._log.error({ err }, "TTS error");
|
|
2162
|
+
} finally {
|
|
2163
|
+
this._speaking = false;
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
};
|
|
2167
|
+
|
|
2168
|
+
// src/agent/pipeline/realtime/openai-realtime.ts
|
|
2169
|
+
var OPENAI_REALTIME_URL = "wss://api.openai.com/v1/realtime?model=";
|
|
2170
|
+
var OpenAIRealtime = class {
|
|
2171
|
+
_apiKey;
|
|
2172
|
+
_systemPrompt;
|
|
2173
|
+
_model;
|
|
2174
|
+
_voice;
|
|
2175
|
+
_language;
|
|
2176
|
+
_turnDetection;
|
|
2177
|
+
_greeting;
|
|
2178
|
+
_log = NOOP_LOGGER;
|
|
2179
|
+
_builtinTools = null;
|
|
2180
|
+
setLogger(logger) {
|
|
2181
|
+
this._log = logger;
|
|
2182
|
+
}
|
|
2183
|
+
setBuiltinTools(tools) {
|
|
2184
|
+
this._builtinTools = tools;
|
|
2185
|
+
}
|
|
2186
|
+
_ws = null;
|
|
2187
|
+
_call = null;
|
|
2188
|
+
_tools = null;
|
|
2189
|
+
_recorder = null;
|
|
2190
|
+
_closed = false;
|
|
2191
|
+
// PlaybackState — 현재 재생 중인 응답 상태
|
|
2192
|
+
_playback = null;
|
|
2193
|
+
_latestMediaTs = 0;
|
|
2194
|
+
// Pending tool call tracking — 인터럽트 시 취소용
|
|
2195
|
+
_pendingToolCalls = /* @__PURE__ */ new Map();
|
|
2196
|
+
// Response state tracking — prevent sending response.create while one is active
|
|
2197
|
+
_responseInProgress = false;
|
|
2198
|
+
_onResponseDone = null;
|
|
2199
|
+
constructor(options = {}) {
|
|
2200
|
+
this._apiKey = options.apiKey ?? process.env["OPENAI_API_KEY"] ?? "";
|
|
2201
|
+
this._systemPrompt = options.systemPrompt ?? "";
|
|
2202
|
+
this._model = options.model ?? "gpt-realtime-1.5";
|
|
2203
|
+
this._voice = options.voice ?? "marin";
|
|
2204
|
+
this._language = options.language ?? "ko";
|
|
2205
|
+
this._turnDetection = options.turnDetection !== void 0 ? options.turnDetection : { type: "semantic_vad", eagerness: "medium", interrupt_response: true };
|
|
2206
|
+
this._greeting = options.greeting ?? true;
|
|
2207
|
+
}
|
|
2208
|
+
/** Inject per-call ToolRegistry. */
|
|
2209
|
+
setToolRegistry(registry) {
|
|
2210
|
+
this._tools = registry;
|
|
2211
|
+
}
|
|
2212
|
+
/** Inject per-call AudioRecorder. */
|
|
2213
|
+
setRecorder(recorder) {
|
|
2214
|
+
this._recorder = recorder;
|
|
2215
|
+
}
|
|
2216
|
+
async start(callSession, tools) {
|
|
2217
|
+
this._call = callSession;
|
|
2218
|
+
if (tools) this._tools = tools;
|
|
2219
|
+
this._closed = false;
|
|
2220
|
+
this._playback = null;
|
|
2221
|
+
this._latestMediaTs = 0;
|
|
2222
|
+
if (!this._apiKey) {
|
|
2223
|
+
throw new Error("OpenAI API key is required. Set OPENAI_API_KEY or pass apiKey option.");
|
|
2224
|
+
}
|
|
2225
|
+
const { WebSocket } = await import('ws');
|
|
2226
|
+
const url = `${OPENAI_REALTIME_URL}${this._model}`;
|
|
2227
|
+
this._ws = new WebSocket(url, {
|
|
2228
|
+
headers: {
|
|
2229
|
+
Authorization: `Bearer ${this._apiKey}`,
|
|
2230
|
+
"OpenAI-Beta": "realtime=v1"
|
|
2231
|
+
}
|
|
2232
|
+
});
|
|
2233
|
+
return new Promise((resolve, reject) => {
|
|
2234
|
+
const ws = this._ws;
|
|
2235
|
+
ws.on("open", () => {
|
|
2236
|
+
this._sendSessionUpdate();
|
|
2237
|
+
this._log.info("OpenAI Realtime connected");
|
|
2238
|
+
if (this._greeting) {
|
|
2239
|
+
this._send({ type: "response.create" });
|
|
2240
|
+
}
|
|
2241
|
+
resolve();
|
|
2242
|
+
});
|
|
2243
|
+
ws.on("message", (data) => {
|
|
2244
|
+
try {
|
|
2245
|
+
const msg = JSON.parse(data.toString());
|
|
2246
|
+
this._handleMessage(msg);
|
|
2247
|
+
} catch {
|
|
2248
|
+
}
|
|
2249
|
+
});
|
|
2250
|
+
ws.on("close", () => {
|
|
2251
|
+
this._closed = true;
|
|
2252
|
+
});
|
|
2253
|
+
ws.on("error", (err) => {
|
|
2254
|
+
if (!this._ws) {
|
|
2255
|
+
reject(err);
|
|
2256
|
+
}
|
|
2257
|
+
this._log.error({ err }, "OpenAI Realtime WS error");
|
|
2258
|
+
});
|
|
2259
|
+
});
|
|
2260
|
+
}
|
|
2261
|
+
async feedDtmf(digits) {
|
|
2262
|
+
await this._waitForResponseDone();
|
|
2263
|
+
this._send({
|
|
2264
|
+
type: "conversation.item.create",
|
|
2265
|
+
item: {
|
|
2266
|
+
type: "message",
|
|
2267
|
+
role: "user",
|
|
2268
|
+
content: [{ type: "input_text", text: `[DTMF \uC785\uB825: ${digits}]` }]
|
|
2269
|
+
}
|
|
2270
|
+
});
|
|
2271
|
+
this._send({ type: "response.create" });
|
|
2272
|
+
}
|
|
2273
|
+
feedAudio(audio) {
|
|
2274
|
+
this._latestMediaTs = Date.now();
|
|
2275
|
+
if (this._ws && this._ws.readyState === 1 && !this._closed) {
|
|
2276
|
+
this._send({
|
|
2277
|
+
type: "input_audio_buffer.append",
|
|
2278
|
+
audio: audio.toString("base64")
|
|
2279
|
+
});
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
async stop() {
|
|
2283
|
+
this._closed = true;
|
|
2284
|
+
for (const [, controller] of this._pendingToolCalls) {
|
|
2285
|
+
controller.abort();
|
|
2286
|
+
}
|
|
2287
|
+
this._pendingToolCalls.clear();
|
|
2288
|
+
if (this._ws) {
|
|
2289
|
+
this._ws.close();
|
|
2290
|
+
this._ws = null;
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
_sendSessionUpdate() {
|
|
2294
|
+
if (!this._ws || this._ws.readyState !== 1) return;
|
|
2295
|
+
const toolSchemas = this._tools ? this._tools.toOpenAITools().map((t) => ({ type: "function", ...t.function })) : [];
|
|
2296
|
+
toolSchemas.push(...getBuiltinToolSchemas(this._builtinTools, "realtime"));
|
|
2297
|
+
this._send({
|
|
2298
|
+
type: "session.update",
|
|
2299
|
+
session: {
|
|
2300
|
+
modalities: ["text", "audio"],
|
|
2301
|
+
voice: this._voice,
|
|
2302
|
+
instructions: this._systemPrompt,
|
|
2303
|
+
input_audio_format: "g711_ulaw",
|
|
2304
|
+
output_audio_format: "g711_ulaw",
|
|
2305
|
+
input_audio_transcription: {
|
|
2306
|
+
model: "whisper-1",
|
|
2307
|
+
language: this._language
|
|
2308
|
+
},
|
|
2309
|
+
input_audio_noise_reduction: { type: "far_field" },
|
|
2310
|
+
turn_detection: this._turnDetection,
|
|
2311
|
+
tools: toolSchemas
|
|
2312
|
+
}
|
|
2313
|
+
});
|
|
2314
|
+
}
|
|
2315
|
+
_handleMessage(msg) {
|
|
2316
|
+
const type = msg["type"];
|
|
2317
|
+
switch (type) {
|
|
1997
2318
|
case "response.audio.delta": {
|
|
1998
2319
|
this._handleAudioDelta(msg);
|
|
1999
2320
|
break;
|
|
2000
2321
|
}
|
|
2001
2322
|
case "response.audio.done": {
|
|
2002
|
-
if (this.
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
Buffer.
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2323
|
+
if (this._playback) {
|
|
2324
|
+
this._playback.generating = false;
|
|
2325
|
+
if (this._playback.audioRemainder.length > 0) {
|
|
2326
|
+
const padded = Buffer.concat([
|
|
2327
|
+
this._playback.audioRemainder,
|
|
2328
|
+
Buffer.alloc(160 - this._playback.audioRemainder.length, 255)
|
|
2329
|
+
]);
|
|
2330
|
+
if (this._call) {
|
|
2331
|
+
this._call.sendAudio(padded);
|
|
2332
|
+
}
|
|
2333
|
+
this._playback.sentChunks++;
|
|
2334
|
+
this._playback.audioRemainder = Buffer.alloc(0);
|
|
2009
2335
|
}
|
|
2010
|
-
this._sentAudioChunks++;
|
|
2011
|
-
this._audioRemainder = Buffer.alloc(0);
|
|
2012
2336
|
}
|
|
2013
2337
|
break;
|
|
2014
2338
|
}
|
|
@@ -2055,128 +2379,121 @@ var OpenAIRealtime = class {
|
|
|
2055
2379
|
}
|
|
2056
2380
|
}
|
|
2057
2381
|
_handleAudioDelta(msg) {
|
|
2058
|
-
if (this.
|
|
2059
|
-
this.
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2382
|
+
if (this._playback === null) {
|
|
2383
|
+
this._playback = {
|
|
2384
|
+
itemId: msg["item_id"] || "",
|
|
2385
|
+
startTs: this._latestMediaTs || Date.now(),
|
|
2386
|
+
sentChunks: 0,
|
|
2387
|
+
generating: true,
|
|
2388
|
+
audioRemainder: Buffer.alloc(0)
|
|
2389
|
+
};
|
|
2390
|
+
} else if (msg["item_id"]) {
|
|
2391
|
+
this._playback.itemId = msg["item_id"];
|
|
2064
2392
|
}
|
|
2393
|
+
const pb = this._playback;
|
|
2065
2394
|
const ulaw = Buffer.from(msg["delta"], "base64");
|
|
2066
2395
|
if (this._recorder) {
|
|
2067
2396
|
this._recorder.writeOutbound(ulawToPcm16(ulaw));
|
|
2068
2397
|
}
|
|
2069
|
-
const combined = Buffer.concat([
|
|
2398
|
+
const combined = Buffer.concat([pb.audioRemainder, ulaw]);
|
|
2070
2399
|
const chunkSize = 160;
|
|
2071
2400
|
const fullEnd = Math.floor(combined.length / chunkSize) * chunkSize;
|
|
2072
2401
|
for (let off = 0; off < fullEnd; off += chunkSize) {
|
|
2073
2402
|
if (this._call) {
|
|
2074
2403
|
this._call.sendAudio(combined.subarray(off, off + chunkSize));
|
|
2075
2404
|
}
|
|
2076
|
-
|
|
2405
|
+
pb.sentChunks++;
|
|
2077
2406
|
}
|
|
2078
|
-
|
|
2407
|
+
pb.audioRemainder = combined.subarray(fullEnd);
|
|
2079
2408
|
}
|
|
2080
2409
|
_handleTruncation() {
|
|
2081
|
-
|
|
2082
|
-
|
|
2410
|
+
for (const [, controller] of this._pendingToolCalls) {
|
|
2411
|
+
controller.abort();
|
|
2083
2412
|
}
|
|
2084
|
-
|
|
2085
|
-
this._send({
|
|
2086
|
-
type: "conversation.item.truncate",
|
|
2087
|
-
item_id: this._lastAssistantItem,
|
|
2088
|
-
content_index: 0,
|
|
2089
|
-
audio_end_ms: audioEndMs
|
|
2090
|
-
});
|
|
2413
|
+
this._pendingToolCalls.clear();
|
|
2091
2414
|
if (this._call) {
|
|
2092
2415
|
this._call.clearAudio();
|
|
2093
2416
|
}
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2417
|
+
const pb = this._playback;
|
|
2418
|
+
if (pb === null) {
|
|
2419
|
+
return;
|
|
2420
|
+
}
|
|
2421
|
+
const playedMs = Math.max(0, (this._latestMediaTs || Date.now()) - pb.startTs);
|
|
2422
|
+
this._log.info(
|
|
2423
|
+
"[Interrupt] item=%s played=%dms total=%dms",
|
|
2424
|
+
pb.itemId,
|
|
2425
|
+
playedMs,
|
|
2426
|
+
pb.sentChunks * 20
|
|
2427
|
+
);
|
|
2428
|
+
this._playback = null;
|
|
2098
2429
|
}
|
|
2099
2430
|
async _handleToolCall(item) {
|
|
2100
2431
|
const funcName = item["name"];
|
|
2101
2432
|
const callId = item["call_id"];
|
|
2102
2433
|
this._log.info("Tool call: %s", funcName);
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2434
|
+
const controller = new AbortController();
|
|
2435
|
+
this._pendingToolCalls.set(callId, controller);
|
|
2436
|
+
try {
|
|
2437
|
+
if (BUILTIN_TOOL_NAMES.has(funcName) && this._call) {
|
|
2438
|
+
const args = JSON.parse(item["arguments"] ?? "{}");
|
|
2439
|
+
const result2 = await executeBuiltinTool(funcName, args, this._call);
|
|
2440
|
+
if (result2 !== null) {
|
|
2441
|
+
if (funcName === "hang_up") return;
|
|
2442
|
+
if (controller.signal.aborted) return;
|
|
2443
|
+
await this._waitForResponseDone();
|
|
2444
|
+
this._send({
|
|
2445
|
+
type: "conversation.item.create",
|
|
2446
|
+
item: {
|
|
2447
|
+
type: "function_call_output",
|
|
2448
|
+
call_id: callId,
|
|
2449
|
+
output: result2
|
|
2450
|
+
}
|
|
2118
2451
|
});
|
|
2119
|
-
|
|
2120
|
-
|
|
2452
|
+
this._send({ type: "response.create" });
|
|
2453
|
+
return;
|
|
2121
2454
|
}
|
|
2455
|
+
}
|
|
2456
|
+
if (!this._tools || !this._tools.has(funcName)) {
|
|
2457
|
+
this._log.error("Unknown tool: %s", funcName);
|
|
2122
2458
|
await this._waitForResponseDone();
|
|
2123
2459
|
this._send({
|
|
2124
2460
|
type: "conversation.item.create",
|
|
2125
2461
|
item: {
|
|
2126
2462
|
type: "function_call_output",
|
|
2127
2463
|
call_id: callId,
|
|
2128
|
-
output:
|
|
2464
|
+
output: JSON.stringify({ error: `Unknown tool: ${funcName}` })
|
|
2129
2465
|
}
|
|
2130
2466
|
});
|
|
2131
2467
|
this._send({ type: "response.create" });
|
|
2468
|
+
return;
|
|
2132
2469
|
}
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
await this._call.sendDtmfSequence(args["digits"] ?? "");
|
|
2141
|
-
result2 = "sent";
|
|
2142
|
-
} catch (err) {
|
|
2143
|
-
result2 = `Error: ${err}`;
|
|
2144
|
-
}
|
|
2145
|
-
await this._waitForResponseDone();
|
|
2146
|
-
this._send({
|
|
2147
|
-
type: "conversation.item.create",
|
|
2148
|
-
item: {
|
|
2149
|
-
type: "function_call_output",
|
|
2150
|
-
call_id: callId,
|
|
2151
|
-
output: result2
|
|
2152
|
-
}
|
|
2153
|
-
});
|
|
2154
|
-
this._send({ type: "response.create" });
|
|
2470
|
+
let result;
|
|
2471
|
+
try {
|
|
2472
|
+
const args = JSON.parse(item["arguments"] ?? "{}");
|
|
2473
|
+
result = await this._tools.call(funcName, args);
|
|
2474
|
+
} catch (err) {
|
|
2475
|
+
this._log.error({ err }, "Tool call failed: %s", funcName);
|
|
2476
|
+
result = `Error: ${err}`;
|
|
2155
2477
|
}
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
this._log.error("Unknown tool: %s", funcName);
|
|
2160
|
-
return;
|
|
2161
|
-
}
|
|
2162
|
-
let result;
|
|
2163
|
-
try {
|
|
2164
|
-
const args = JSON.parse(item["arguments"] ?? "{}");
|
|
2165
|
-
result = await this._tools.call(funcName, args);
|
|
2166
|
-
} catch (err) {
|
|
2167
|
-
this._log.error({ err }, "Tool call failed: %s", funcName);
|
|
2168
|
-
result = `Error: ${err}`;
|
|
2169
|
-
}
|
|
2170
|
-
await this._waitForResponseDone();
|
|
2171
|
-
this._send({
|
|
2172
|
-
type: "conversation.item.create",
|
|
2173
|
-
item: {
|
|
2174
|
-
type: "function_call_output",
|
|
2175
|
-
call_id: callId,
|
|
2176
|
-
output: typeof result === "string" ? result : JSON.stringify(result)
|
|
2478
|
+
if (controller.signal.aborted) {
|
|
2479
|
+
this._log.info("Tool call cancelled (user interrupted): %s", funcName);
|
|
2480
|
+
return;
|
|
2177
2481
|
}
|
|
2178
|
-
|
|
2179
|
-
|
|
2482
|
+
const resultStr = typeof result === "string" ? result : JSON.stringify(result);
|
|
2483
|
+
await this._waitForResponseDone();
|
|
2484
|
+
this._send({
|
|
2485
|
+
type: "conversation.item.create",
|
|
2486
|
+
item: {
|
|
2487
|
+
type: "function_call_output",
|
|
2488
|
+
call_id: callId,
|
|
2489
|
+
output: resultStr
|
|
2490
|
+
}
|
|
2491
|
+
});
|
|
2492
|
+
this._log.info("[ToolResult] %s call_id=%s len=%d", funcName, callId, resultStr.length);
|
|
2493
|
+
this._send({ type: "response.create" });
|
|
2494
|
+
} finally {
|
|
2495
|
+
this._pendingToolCalls.delete(callId);
|
|
2496
|
+
}
|
|
2180
2497
|
}
|
|
2181
2498
|
_waitForResponseDone() {
|
|
2182
2499
|
if (!this._responseInProgress) return Promise.resolve();
|
|
@@ -2191,39 +2508,7 @@ var OpenAIRealtime = class {
|
|
|
2191
2508
|
}
|
|
2192
2509
|
};
|
|
2193
2510
|
|
|
2194
|
-
// src/agent/pipeline/gemini-realtime.ts
|
|
2195
|
-
var HANG_UP_TOOL2 = {
|
|
2196
|
-
name: "hang_up",
|
|
2197
|
-
description: "End the phone call. Use when the conversation is finished or the caller says goodbye.",
|
|
2198
|
-
parameters: { type: "object", properties: {} }
|
|
2199
|
-
};
|
|
2200
|
-
var COLLECT_DTMF_TOOL2 = {
|
|
2201
|
-
name: "collect_dtmf",
|
|
2202
|
-
description: "\uC0AC\uC6A9\uC790\uB85C\uBD80\uD130 DTMF(\uC804\uD654 \uD0A4\uD328\uB4DC) \uC785\uB825\uC744 \uC218\uC9D1\uD569\uB2C8\uB2E4. \uBC18\uB4DC\uC2DC \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBB34\uC5C7\uC744 \uC785\uB825\uD574\uC57C \uD558\uB294\uC9C0 \uC548\uB0B4\uD55C \uD6C4 \uD638\uCD9C\uD558\uC138\uC694.",
|
|
2203
|
-
parameters: {
|
|
2204
|
-
type: "object",
|
|
2205
|
-
properties: {
|
|
2206
|
-
max_digits: { type: "integer", description: "\uC218\uC9D1\uD560 \uCD5C\uB300 \uC790\uB9BF\uC218" },
|
|
2207
|
-
finish_on_key: { type: "string", description: "\uC785\uB825 \uC885\uB8CC \uD0A4 (\uAE30\uBCF8: #)" },
|
|
2208
|
-
timeout: { type: "integer", description: "\uC785\uB825 \uB300\uAE30 \uC2DC\uAC04(\uCD08, \uAE30\uBCF8: 5)" }
|
|
2209
|
-
},
|
|
2210
|
-
required: ["max_digits"]
|
|
2211
|
-
}
|
|
2212
|
-
};
|
|
2213
|
-
var SEND_DTMF_TOOL2 = {
|
|
2214
|
-
name: "send_dtmf",
|
|
2215
|
-
description: "DTMF \uC2E0\uD638\uB97C \uC804\uC1A1\uD569\uB2C8\uB2E4. ARS \uBA54\uB274 \uD0D0\uC0C9\uC774\uB098 \uB0B4\uC120\uBC88\uD638 \uC785\uB825 \uC2DC \uC0AC\uC6A9\uD569\uB2C8\uB2E4.",
|
|
2216
|
-
parameters: {
|
|
2217
|
-
type: "object",
|
|
2218
|
-
properties: {
|
|
2219
|
-
digits: {
|
|
2220
|
-
type: "string",
|
|
2221
|
-
description: "\uC804\uC1A1\uD560 \uBC88\uD638 (0-9, *, #). 'w'\uB294 500ms \uB300\uAE30, 'W'\uB294 1000ms \uB300\uAE30."
|
|
2222
|
-
}
|
|
2223
|
-
},
|
|
2224
|
-
required: ["digits"]
|
|
2225
|
-
}
|
|
2226
|
-
};
|
|
2511
|
+
// src/agent/pipeline/realtime/gemini-realtime.ts
|
|
2227
2512
|
function resolveRef(ref, defs) {
|
|
2228
2513
|
const parts = ref.replace(/^#\//, "").split("/");
|
|
2229
2514
|
let result = defs;
|
|
@@ -2433,9 +2718,7 @@ var GeminiRealtime = class {
|
|
|
2433
2718
|
t.function.parameters ?? { type: "object", properties: {} }
|
|
2434
2719
|
)
|
|
2435
2720
|
})) : [];
|
|
2436
|
-
|
|
2437
|
-
if (!this._builtinTools || this._builtinTools.has("collect_dtmf" /* COLLECT_DTMF */)) toolDefs.push(COLLECT_DTMF_TOOL2);
|
|
2438
|
-
if (!this._builtinTools || this._builtinTools.has("send_dtmf" /* SEND_DTMF */)) toolDefs.push(SEND_DTMF_TOOL2);
|
|
2721
|
+
toolDefs.push(...getBuiltinToolSchemas(this._builtinTools, "gemini"));
|
|
2439
2722
|
return toolDefs;
|
|
2440
2723
|
}
|
|
2441
2724
|
_handleMessage(msg) {
|
|
@@ -2523,51 +2806,17 @@ var GeminiRealtime = class {
|
|
|
2523
2806
|
const fcId = fc.id ?? "";
|
|
2524
2807
|
const args = fc.args ?? {};
|
|
2525
2808
|
this._log.info({ tool: name, args }, "Tool call: %s", name);
|
|
2526
|
-
if (name
|
|
2527
|
-
|
|
2528
|
-
if (
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
}
|
|
2533
|
-
if (name === "collect_dtmf") {
|
|
2534
|
-
if (this._call) {
|
|
2535
|
-
let result;
|
|
2536
|
-
try {
|
|
2537
|
-
this._log.info({ maxDigits: args["max_digits"] ?? 4, timeout: args["timeout"] ?? 5 }, "collect_dtmf: waiting for digits");
|
|
2538
|
-
result = await this._call.collectDtmf({
|
|
2539
|
-
maxDigits: args["max_digits"] ?? 4,
|
|
2540
|
-
finishOnKey: args["finish_on_key"] ?? "#",
|
|
2541
|
-
timeout: args["timeout"] ?? 5
|
|
2542
|
-
});
|
|
2543
|
-
this._log.info("DTMF collected: %s", result || "(empty)");
|
|
2544
|
-
} catch (err) {
|
|
2545
|
-
this._log.error({ err }, "collect_dtmf error");
|
|
2546
|
-
result = `Error: ${err}`;
|
|
2547
|
-
}
|
|
2548
|
-
responses.push({
|
|
2549
|
-
id: fcId,
|
|
2550
|
-
name,
|
|
2551
|
-
response: { result: result || "(\uD0C0\uC784\uC544\uC6C3 - \uC785\uB825 \uC5C6\uC74C)" }
|
|
2552
|
-
});
|
|
2553
|
-
}
|
|
2554
|
-
continue;
|
|
2555
|
-
}
|
|
2556
|
-
if (name === "send_dtmf") {
|
|
2557
|
-
if (this._call) {
|
|
2558
|
-
let result;
|
|
2559
|
-
try {
|
|
2560
|
-
this._log.info('send_dtmf: digits="%s"', args["digits"] ?? "");
|
|
2561
|
-
await this._call.sendDtmfSequence(args["digits"] ?? "");
|
|
2562
|
-
result = "sent";
|
|
2563
|
-
this._log.info("send_dtmf: sent");
|
|
2564
|
-
} catch (err) {
|
|
2565
|
-
this._log.error({ err }, "send_dtmf error");
|
|
2566
|
-
result = `Error: ${err}`;
|
|
2809
|
+
if (BUILTIN_TOOL_NAMES.has(name) && this._call) {
|
|
2810
|
+
const result = await executeBuiltinTool(name, args, this._call);
|
|
2811
|
+
if (result !== null) {
|
|
2812
|
+
if (name === "hang_up") {
|
|
2813
|
+
this._log.info("hang_up: ending call");
|
|
2814
|
+
return;
|
|
2567
2815
|
}
|
|
2816
|
+
this._log.info("Builtin tool result: %s -> %s", name, result);
|
|
2568
2817
|
responses.push({ id: fcId, name, response: { result } });
|
|
2818
|
+
continue;
|
|
2569
2819
|
}
|
|
2570
|
-
continue;
|
|
2571
2820
|
}
|
|
2572
2821
|
if (!this._tools || !this._tools.has(name)) {
|
|
2573
2822
|
this._log.error("Unknown tool: %s", name);
|
|
@@ -2602,306 +2851,7 @@ var GeminiRealtime = class {
|
|
|
2602
2851
|
}
|
|
2603
2852
|
};
|
|
2604
2853
|
|
|
2605
|
-
// src/agent/pipeline/
|
|
2606
|
-
var COLLECT_DTMF_TOOL3 = {
|
|
2607
|
-
function: {
|
|
2608
|
-
description: "\uC0AC\uC6A9\uC790\uB85C\uBD80\uD130 DTMF(\uC804\uD654 \uD0A4\uD328\uB4DC) \uC785\uB825\uC744 \uC218\uC9D1\uD569\uB2C8\uB2E4.",
|
|
2609
|
-
parameters: {
|
|
2610
|
-
properties: {
|
|
2611
|
-
max_digits: { type: "integer", description: "\uC218\uC9D1\uD560 \uCD5C\uB300 \uC790\uB9BF\uC218" },
|
|
2612
|
-
finish_on_key: { type: "string", description: "\uC785\uB825 \uC885\uB8CC \uD0A4 (\uAE30\uBCF8: #)" },
|
|
2613
|
-
timeout: { type: "integer", description: "\uC785\uB825 \uB300\uAE30 \uC2DC\uAC04(\uCD08, \uAE30\uBCF8: 5)" }
|
|
2614
|
-
},
|
|
2615
|
-
required: ["max_digits"]
|
|
2616
|
-
}
|
|
2617
|
-
}
|
|
2618
|
-
};
|
|
2619
|
-
var SEND_DTMF_TOOL3 = {
|
|
2620
|
-
function: {
|
|
2621
|
-
description: "DTMF \uC2E0\uD638\uB97C \uC804\uC1A1\uD569\uB2C8\uB2E4. ARS \uBA54\uB274 \uD0D0\uC0C9\uC774\uB098 \uB0B4\uC120\uBC88\uD638 \uC785\uB825 \uC2DC \uC0AC\uC6A9\uD569\uB2C8\uB2E4.",
|
|
2622
|
-
parameters: {
|
|
2623
|
-
properties: {
|
|
2624
|
-
digits: { type: "string", description: "\uC804\uC1A1\uD560 \uBC88\uD638 (0-9, *, #). 'w'\uB294 500ms \uB300\uAE30, 'W'\uB294 1000ms \uB300\uAE30." }
|
|
2625
|
-
},
|
|
2626
|
-
required: ["digits"]
|
|
2627
|
-
}
|
|
2628
|
-
}
|
|
2629
|
-
};
|
|
2630
|
-
var PipelineSession = class {
|
|
2631
|
-
_stt;
|
|
2632
|
-
_llm;
|
|
2633
|
-
_tts;
|
|
2634
|
-
_systemPrompt;
|
|
2635
|
-
_greeting;
|
|
2636
|
-
_language;
|
|
2637
|
-
_temperature;
|
|
2638
|
-
_maxTokens;
|
|
2639
|
-
_sampleRate;
|
|
2640
|
-
_interruptOnSpeech;
|
|
2641
|
-
_callSession = null;
|
|
2642
|
-
_tools = null;
|
|
2643
|
-
_recorder = null;
|
|
2644
|
-
_conversation = [];
|
|
2645
|
-
_audioBuffer = [];
|
|
2646
|
-
_running = false;
|
|
2647
|
-
_speaking = false;
|
|
2648
|
-
_builtinTools = null;
|
|
2649
|
-
_log = NOOP_LOGGER;
|
|
2650
|
-
constructor(options) {
|
|
2651
|
-
this._stt = options.stt;
|
|
2652
|
-
this._llm = options.llm;
|
|
2653
|
-
this._tts = options.tts;
|
|
2654
|
-
this._systemPrompt = options.systemPrompt;
|
|
2655
|
-
this._greeting = options.greeting ?? true;
|
|
2656
|
-
this._language = options.language ?? "ko";
|
|
2657
|
-
this._temperature = options.temperature;
|
|
2658
|
-
this._maxTokens = options.maxTokens;
|
|
2659
|
-
this._sampleRate = options.sampleRate ?? 8e3;
|
|
2660
|
-
this._interruptOnSpeech = options.interruptOnSpeech ?? true;
|
|
2661
|
-
if (options.toolRegistry) this._tools = options.toolRegistry;
|
|
2662
|
-
if (options.recorder) this._recorder = options.recorder;
|
|
2663
|
-
}
|
|
2664
|
-
setToolRegistry(registry) {
|
|
2665
|
-
this._tools = registry;
|
|
2666
|
-
}
|
|
2667
|
-
setRecorder(recorder) {
|
|
2668
|
-
this._recorder = recorder;
|
|
2669
|
-
}
|
|
2670
|
-
setBuiltinTools(tools) {
|
|
2671
|
-
this._builtinTools = tools;
|
|
2672
|
-
}
|
|
2673
|
-
setLogger(logger) {
|
|
2674
|
-
this._log = logger;
|
|
2675
|
-
if ("setLogger" in this._stt && typeof this._stt.setLogger === "function") {
|
|
2676
|
-
this._stt.setLogger(logger);
|
|
2677
|
-
}
|
|
2678
|
-
if ("setLogger" in this._tts && typeof this._tts.setLogger === "function") {
|
|
2679
|
-
this._tts.setLogger(logger);
|
|
2680
|
-
}
|
|
2681
|
-
}
|
|
2682
|
-
async start(callSession, tools) {
|
|
2683
|
-
this._callSession = callSession;
|
|
2684
|
-
this._tools = tools ?? null;
|
|
2685
|
-
this._running = true;
|
|
2686
|
-
this._log.info("PipelineSession started");
|
|
2687
|
-
this._conversation = [];
|
|
2688
|
-
if (this._systemPrompt) {
|
|
2689
|
-
this._conversation.push({
|
|
2690
|
-
role: "system",
|
|
2691
|
-
content: this._systemPrompt
|
|
2692
|
-
});
|
|
2693
|
-
}
|
|
2694
|
-
if (this._greeting) {
|
|
2695
|
-
this._generateGreeting().catch((err) => {
|
|
2696
|
-
this._log.error({ err }, "Greeting error");
|
|
2697
|
-
});
|
|
2698
|
-
}
|
|
2699
|
-
this._runSttLoop().catch((err) => {
|
|
2700
|
-
this._log.error({ err }, "STT loop error");
|
|
2701
|
-
});
|
|
2702
|
-
}
|
|
2703
|
-
feedAudio(audio) {
|
|
2704
|
-
if (this._running) {
|
|
2705
|
-
this._audioBuffer.push(audio);
|
|
2706
|
-
}
|
|
2707
|
-
}
|
|
2708
|
-
async feedDtmf(digits) {
|
|
2709
|
-
this._conversation.push({
|
|
2710
|
-
role: "user",
|
|
2711
|
-
content: `[DTMF \uC785\uB825: ${digits}]`
|
|
2712
|
-
});
|
|
2713
|
-
await this._respond();
|
|
2714
|
-
}
|
|
2715
|
-
async stop() {
|
|
2716
|
-
this._running = false;
|
|
2717
|
-
this._log.info("PipelineSession stopped");
|
|
2718
|
-
this._audioBuffer = [];
|
|
2719
|
-
}
|
|
2720
|
-
async _runSttLoop() {
|
|
2721
|
-
const audioStream = this._createAudioStream();
|
|
2722
|
-
for await (const event of this._stt.transcribe(audioStream, {
|
|
2723
|
-
sampleRate: this._sampleRate
|
|
2724
|
-
})) {
|
|
2725
|
-
if (!this._running) break;
|
|
2726
|
-
if (event.type === "interim" && this._speaking && this._interruptOnSpeech) {
|
|
2727
|
-
this._speaking = false;
|
|
2728
|
-
if (this._callSession) {
|
|
2729
|
-
this._callSession.clearAudio();
|
|
2730
|
-
}
|
|
2731
|
-
this._log.info('Barge-in: "%s"', event.transcript.substring(0, 30));
|
|
2732
|
-
}
|
|
2733
|
-
if (event.type === "final" && event.transcript.trim()) {
|
|
2734
|
-
this._log.info("STT: %s", event.transcript);
|
|
2735
|
-
await this._handleUserSpeech(event.transcript);
|
|
2736
|
-
}
|
|
2737
|
-
}
|
|
2738
|
-
}
|
|
2739
|
-
async *_createAudioStream() {
|
|
2740
|
-
while (this._running) {
|
|
2741
|
-
if (this._audioBuffer.length > 0) {
|
|
2742
|
-
const ulaw = this._audioBuffer.shift();
|
|
2743
|
-
const pcm8k = ulawToPcm16(ulaw);
|
|
2744
|
-
const pcm16k = resamplePcm16(pcm8k, 8e3, 16e3);
|
|
2745
|
-
yield pcm16k;
|
|
2746
|
-
} else {
|
|
2747
|
-
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
2748
|
-
}
|
|
2749
|
-
}
|
|
2750
|
-
}
|
|
2751
|
-
async _generateGreeting() {
|
|
2752
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
2753
|
-
await this._respond();
|
|
2754
|
-
}
|
|
2755
|
-
async _handleUserSpeech(transcript) {
|
|
2756
|
-
this._conversation.push({ role: "user", content: transcript });
|
|
2757
|
-
await this._respond();
|
|
2758
|
-
}
|
|
2759
|
-
_buildEffectiveTools() {
|
|
2760
|
-
const includeCollectDtmf = !this._builtinTools || this._builtinTools.has("collect_dtmf" /* COLLECT_DTMF */);
|
|
2761
|
-
const includeSendDtmf = !this._builtinTools || this._builtinTools.has("send_dtmf" /* SEND_DTMF */);
|
|
2762
|
-
if (!includeCollectDtmf && !includeSendDtmf) return this._tools ?? void 0;
|
|
2763
|
-
const base = this._tools ? this._tools.fork() : new ToolRegistry();
|
|
2764
|
-
if (includeCollectDtmf) {
|
|
2765
|
-
base.register({
|
|
2766
|
-
name: "collect_dtmf",
|
|
2767
|
-
description: COLLECT_DTMF_TOOL3.function.description,
|
|
2768
|
-
parameters: COLLECT_DTMF_TOOL3.function.parameters.properties,
|
|
2769
|
-
required: COLLECT_DTMF_TOOL3.function.parameters.required,
|
|
2770
|
-
handler: async () => ""
|
|
2771
|
-
});
|
|
2772
|
-
}
|
|
2773
|
-
if (includeSendDtmf) {
|
|
2774
|
-
base.register({
|
|
2775
|
-
name: "send_dtmf",
|
|
2776
|
-
description: SEND_DTMF_TOOL3.function.description,
|
|
2777
|
-
parameters: SEND_DTMF_TOOL3.function.parameters.properties,
|
|
2778
|
-
required: SEND_DTMF_TOOL3.function.parameters.required,
|
|
2779
|
-
handler: async () => ""
|
|
2780
|
-
});
|
|
2781
|
-
}
|
|
2782
|
-
return base;
|
|
2783
|
-
}
|
|
2784
|
-
async _respond() {
|
|
2785
|
-
let fullResponse = "";
|
|
2786
|
-
const textChunks = [];
|
|
2787
|
-
const effectiveTools = this._buildEffectiveTools();
|
|
2788
|
-
const llmStream = this._llm.generate(this._conversation, {
|
|
2789
|
-
tools: effectiveTools,
|
|
2790
|
-
temperature: this._temperature,
|
|
2791
|
-
maxTokens: this._maxTokens
|
|
2792
|
-
});
|
|
2793
|
-
for await (const chunk of llmStream) {
|
|
2794
|
-
if (!this._running) break;
|
|
2795
|
-
if (chunk.type === "text" && chunk.text) {
|
|
2796
|
-
textChunks.push(chunk.text);
|
|
2797
|
-
fullResponse += chunk.text;
|
|
2798
|
-
} else if (chunk.type === "tool_call" && chunk.toolCall) {
|
|
2799
|
-
await this._handleToolCall(chunk);
|
|
2800
|
-
}
|
|
2801
|
-
}
|
|
2802
|
-
if (fullResponse.trim()) {
|
|
2803
|
-
this._log.info("Assistant: %s", fullResponse.substring(0, 100));
|
|
2804
|
-
this._conversation.push({ role: "assistant", content: fullResponse });
|
|
2805
|
-
await this._synthesizeAndSend(fullResponse);
|
|
2806
|
-
}
|
|
2807
|
-
}
|
|
2808
|
-
async _handleToolCall(chunk) {
|
|
2809
|
-
if (!chunk.toolCall) return;
|
|
2810
|
-
const { id, name, arguments: argsStr } = chunk.toolCall;
|
|
2811
|
-
try {
|
|
2812
|
-
const args = JSON.parse(argsStr);
|
|
2813
|
-
if (name === "collect_dtmf" && this._callSession) {
|
|
2814
|
-
let result2;
|
|
2815
|
-
try {
|
|
2816
|
-
result2 = await this._callSession.collectDtmf({
|
|
2817
|
-
maxDigits: args["max_digits"] ?? 4,
|
|
2818
|
-
finishOnKey: args["finish_on_key"] ?? "#",
|
|
2819
|
-
timeout: args["timeout"] ?? 5
|
|
2820
|
-
});
|
|
2821
|
-
} catch (err) {
|
|
2822
|
-
result2 = `Error: ${err}`;
|
|
2823
|
-
}
|
|
2824
|
-
this._conversation.push({ role: "tool", content: result2 || "(\uD0C0\uC784\uC544\uC6C3 - \uC785\uB825 \uC5C6\uC74C)", tool_call_id: id, name });
|
|
2825
|
-
await this._respond();
|
|
2826
|
-
return;
|
|
2827
|
-
}
|
|
2828
|
-
if (name === "send_dtmf" && this._callSession) {
|
|
2829
|
-
let result2;
|
|
2830
|
-
try {
|
|
2831
|
-
await this._callSession.sendDtmfSequence(args["digits"] ?? "");
|
|
2832
|
-
result2 = "sent";
|
|
2833
|
-
} catch (err) {
|
|
2834
|
-
result2 = `Error: ${err}`;
|
|
2835
|
-
}
|
|
2836
|
-
this._conversation.push({ role: "tool", content: result2, tool_call_id: id, name });
|
|
2837
|
-
await this._respond();
|
|
2838
|
-
return;
|
|
2839
|
-
}
|
|
2840
|
-
if (!this._tools) return;
|
|
2841
|
-
const result = await this._tools.call(name, args);
|
|
2842
|
-
this._conversation.push({
|
|
2843
|
-
role: "assistant",
|
|
2844
|
-
content: ""
|
|
2845
|
-
// Tool call info stored in the message flow
|
|
2846
|
-
});
|
|
2847
|
-
this._conversation.push({
|
|
2848
|
-
role: "tool",
|
|
2849
|
-
content: typeof result === "string" ? result : JSON.stringify(result),
|
|
2850
|
-
tool_call_id: id,
|
|
2851
|
-
name
|
|
2852
|
-
});
|
|
2853
|
-
const effectiveTools = this._buildEffectiveTools();
|
|
2854
|
-
let followUpText = "";
|
|
2855
|
-
const followUpStream = this._llm.generate(this._conversation, {
|
|
2856
|
-
tools: effectiveTools,
|
|
2857
|
-
temperature: this._temperature,
|
|
2858
|
-
maxTokens: this._maxTokens
|
|
2859
|
-
});
|
|
2860
|
-
for await (const followChunk of followUpStream) {
|
|
2861
|
-
if (!this._running) break;
|
|
2862
|
-
if (followChunk.type === "text" && followChunk.text) {
|
|
2863
|
-
followUpText += followChunk.text;
|
|
2864
|
-
}
|
|
2865
|
-
}
|
|
2866
|
-
if (followUpText.trim()) {
|
|
2867
|
-
this._conversation.push({ role: "assistant", content: followUpText });
|
|
2868
|
-
await this._synthesizeAndSend(followUpText);
|
|
2869
|
-
}
|
|
2870
|
-
} catch (err) {
|
|
2871
|
-
this._log.error({ err }, "Tool call failed: %s", name);
|
|
2872
|
-
}
|
|
2873
|
-
}
|
|
2874
|
-
async _synthesizeAndSend(text) {
|
|
2875
|
-
if (!this._callSession || !this._running) return;
|
|
2876
|
-
this._speaking = true;
|
|
2877
|
-
try {
|
|
2878
|
-
for await (const audioChunk of this._tts.synthesize(text, {
|
|
2879
|
-
sampleRate: this._sampleRate
|
|
2880
|
-
})) {
|
|
2881
|
-
if (!this._running || !this._speaking) break;
|
|
2882
|
-
if (this._recorder) {
|
|
2883
|
-
const pcm8k2 = this._sampleRate !== 8e3 ? resamplePcm16(audioChunk, this._sampleRate, 8e3) : audioChunk;
|
|
2884
|
-
this._recorder.writeOutbound(pcm8k2);
|
|
2885
|
-
}
|
|
2886
|
-
const pcm8k = resamplePcm16(audioChunk, this._sampleRate, 8e3);
|
|
2887
|
-
const ulaw = pcm16ToUlaw(pcm8k);
|
|
2888
|
-
for (let off = 0; off < ulaw.length; off += 160) {
|
|
2889
|
-
let chunk = ulaw.subarray(off, off + 160);
|
|
2890
|
-
if (chunk.length < 160) {
|
|
2891
|
-
chunk = Buffer.concat([chunk, Buffer.alloc(160 - chunk.length, 255)]);
|
|
2892
|
-
}
|
|
2893
|
-
this._callSession.sendAudio(chunk);
|
|
2894
|
-
}
|
|
2895
|
-
}
|
|
2896
|
-
} catch (err) {
|
|
2897
|
-
this._log.error({ err }, "TTS error");
|
|
2898
|
-
} finally {
|
|
2899
|
-
this._speaking = false;
|
|
2900
|
-
}
|
|
2901
|
-
}
|
|
2902
|
-
};
|
|
2903
|
-
|
|
2904
|
-
// src/agent/pipeline/deepgram-stt.ts
|
|
2854
|
+
// src/agent/pipeline/stt/deepgram-stt.ts
|
|
2905
2855
|
var DeepgramSTT = class {
|
|
2906
2856
|
_options;
|
|
2907
2857
|
_log = NOOP_LOGGER;
|
|
@@ -3022,7 +2972,7 @@ var DeepgramSTT = class {
|
|
|
3022
2972
|
}
|
|
3023
2973
|
};
|
|
3024
2974
|
|
|
3025
|
-
// src/agent/pipeline/elevenlabs-tts.ts
|
|
2975
|
+
// src/agent/pipeline/tts/elevenlabs-tts.ts
|
|
3026
2976
|
var ElevenLabsTTS = class {
|
|
3027
2977
|
_options;
|
|
3028
2978
|
_log = NOOP_LOGGER;
|
|
@@ -3175,7 +3125,7 @@ var ElevenLabsTTS = class {
|
|
|
3175
3125
|
}
|
|
3176
3126
|
};
|
|
3177
3127
|
|
|
3178
|
-
// src/agent/pipeline/openai-llm.ts
|
|
3128
|
+
// src/agent/pipeline/llm/openai-llm.ts
|
|
3179
3129
|
var OpenAILLM = class {
|
|
3180
3130
|
_options;
|
|
3181
3131
|
constructor(options = {}) {
|
|
@@ -3261,7 +3211,7 @@ var OpenAILLM = class {
|
|
|
3261
3211
|
}
|
|
3262
3212
|
};
|
|
3263
3213
|
|
|
3264
|
-
// src/agent/pipeline/anthropic-llm.ts
|
|
3214
|
+
// src/agent/pipeline/llm/anthropic-llm.ts
|
|
3265
3215
|
var AnthropicLLM = class {
|
|
3266
3216
|
_options;
|
|
3267
3217
|
constructor(options = {}) {
|
|
@@ -3366,7 +3316,7 @@ var AnthropicLLM = class {
|
|
|
3366
3316
|
}
|
|
3367
3317
|
};
|
|
3368
3318
|
|
|
3369
|
-
// src/agent/pipeline/gemini-llm.ts
|
|
3319
|
+
// src/agent/pipeline/llm/gemini-llm.ts
|
|
3370
3320
|
var GeminiLLM = class {
|
|
3371
3321
|
_options;
|
|
3372
3322
|
constructor(options = {}) {
|
|
@@ -3450,7 +3400,7 @@ var GeminiLLM = class {
|
|
|
3450
3400
|
}
|
|
3451
3401
|
};
|
|
3452
3402
|
|
|
3453
|
-
// src/agent/pipeline/openai-compat-llm.ts
|
|
3403
|
+
// src/agent/pipeline/llm/openai-compat-llm.ts
|
|
3454
3404
|
var OpenAICompatLLM = class {
|
|
3455
3405
|
_options;
|
|
3456
3406
|
constructor(options) {
|
|
@@ -3533,7 +3483,7 @@ var OpenAICompatLLM = class {
|
|
|
3533
3483
|
}
|
|
3534
3484
|
};
|
|
3535
3485
|
|
|
3536
|
-
// src/agent/pipeline/ollama-llm.ts
|
|
3486
|
+
// src/agent/pipeline/llm/ollama-llm.ts
|
|
3537
3487
|
var OllamaLLM = class {
|
|
3538
3488
|
_inner;
|
|
3539
3489
|
constructor(options = {}) {
|
|
@@ -3551,7 +3501,7 @@ var OllamaLLM = class {
|
|
|
3551
3501
|
}
|
|
3552
3502
|
};
|
|
3553
3503
|
|
|
3554
|
-
// src/agent/pipeline/mistral-llm.ts
|
|
3504
|
+
// src/agent/pipeline/llm/mistral-llm.ts
|
|
3555
3505
|
var MistralLLM = class {
|
|
3556
3506
|
_inner;
|
|
3557
3507
|
constructor(options = {}) {
|
|
@@ -3568,7 +3518,7 @@ var MistralLLM = class {
|
|
|
3568
3518
|
}
|
|
3569
3519
|
};
|
|
3570
3520
|
|
|
3571
|
-
// src/agent/pipeline/groq-llm.ts
|
|
3521
|
+
// src/agent/pipeline/llm/groq-llm.ts
|
|
3572
3522
|
var GroqLLM = class {
|
|
3573
3523
|
_inner;
|
|
3574
3524
|
constructor(options = {}) {
|
|
@@ -3585,7 +3535,7 @@ var GroqLLM = class {
|
|
|
3585
3535
|
}
|
|
3586
3536
|
};
|
|
3587
3537
|
|
|
3588
|
-
// src/agent/pipeline/perplexity-llm.ts
|
|
3538
|
+
// src/agent/pipeline/llm/perplexity-llm.ts
|
|
3589
3539
|
var PerplexityLLM = class {
|
|
3590
3540
|
_inner;
|
|
3591
3541
|
constructor(options = {}) {
|
|
@@ -3602,7 +3552,7 @@ var PerplexityLLM = class {
|
|
|
3602
3552
|
}
|
|
3603
3553
|
};
|
|
3604
3554
|
|
|
3605
|
-
// src/agent/pipeline/together-llm.ts
|
|
3555
|
+
// src/agent/pipeline/llm/together-llm.ts
|
|
3606
3556
|
var TogetherLLM = class {
|
|
3607
3557
|
_inner;
|
|
3608
3558
|
constructor(options = {}) {
|
|
@@ -3619,7 +3569,7 @@ var TogetherLLM = class {
|
|
|
3619
3569
|
}
|
|
3620
3570
|
};
|
|
3621
3571
|
|
|
3622
|
-
// src/agent/pipeline/fireworks-llm.ts
|
|
3572
|
+
// src/agent/pipeline/llm/fireworks-llm.ts
|
|
3623
3573
|
var FireworksLLM = class {
|
|
3624
3574
|
_inner;
|
|
3625
3575
|
constructor(options = {}) {
|
|
@@ -3636,7 +3586,7 @@ var FireworksLLM = class {
|
|
|
3636
3586
|
}
|
|
3637
3587
|
};
|
|
3638
3588
|
|
|
3639
|
-
// src/agent/pipeline/deepseek-llm.ts
|
|
3589
|
+
// src/agent/pipeline/llm/deepseek-llm.ts
|
|
3640
3590
|
var DeepSeekLLM = class {
|
|
3641
3591
|
_inner;
|
|
3642
3592
|
constructor(options = {}) {
|
|
@@ -3653,7 +3603,7 @@ var DeepSeekLLM = class {
|
|
|
3653
3603
|
}
|
|
3654
3604
|
};
|
|
3655
3605
|
|
|
3656
|
-
// src/agent/pipeline/xai-llm.ts
|
|
3606
|
+
// src/agent/pipeline/llm/xai-llm.ts
|
|
3657
3607
|
var XaiLLM = class {
|
|
3658
3608
|
_inner;
|
|
3659
3609
|
constructor(options = {}) {
|
|
@@ -3691,6 +3641,7 @@ function mcpServerHTTP(options) {
|
|
|
3691
3641
|
|
|
3692
3642
|
exports.AnthropicLLM = AnthropicLLM;
|
|
3693
3643
|
exports.AudioRecorder = AudioRecorder;
|
|
3644
|
+
exports.BUILTIN_TOOL_NAMES = BUILTIN_TOOL_NAMES;
|
|
3694
3645
|
exports.BuiltinTool = BuiltinTool;
|
|
3695
3646
|
exports.CallSession = CallSession;
|
|
3696
3647
|
exports.ClawOpsAgent = ClawOpsAgent;
|
|
@@ -3715,8 +3666,11 @@ exports.ToolRegistry = ToolRegistry;
|
|
|
3715
3666
|
exports.XaiLLM = XaiLLM;
|
|
3716
3667
|
exports.createAgentLogger = createAgentLogger;
|
|
3717
3668
|
exports.createPipelineLogger = createPipelineLogger;
|
|
3669
|
+
exports.executeBuiltinTool = executeBuiltinTool;
|
|
3718
3670
|
exports.functionTool = functionTool;
|
|
3671
|
+
exports.getBuiltinToolSchemas = getBuiltinToolSchemas;
|
|
3719
3672
|
exports.getTracingConfig = getTracingConfig;
|
|
3673
|
+
exports.isBuiltinTool = isBuiltinTool;
|
|
3720
3674
|
exports.mcpServerHTTP = mcpServerHTTP;
|
|
3721
3675
|
exports.mcpServerStdio = mcpServerStdio;
|
|
3722
3676
|
exports.pcm16ToUlaw = pcm16ToUlaw;
|