@teamlearners/clawops 0.3.1 → 0.5.1

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.
@@ -464,7 +464,8 @@ declare class OpenAIRealtime implements Session {
464
464
  /**
465
465
  * Google Gemini Realtime API session (speech-to-speech).
466
466
  *
467
- * Matches Python SDK's GeminiRealtime implementation.
467
+ * Uses @google/genai SDK's live.connect() method.
468
+ * Matches Python SDK's GeminiRealtime implementation (v0.14.0).
468
469
  */
469
470
 
470
471
  interface GeminiRealtimeOptions {
@@ -480,8 +481,6 @@ interface GeminiRealtimeOptions {
480
481
  language?: string;
481
482
  /** Send initial greeting. Default: true */
482
483
  greeting?: boolean;
483
- /** Generation config overrides. */
484
- generationConfig?: Record<string, unknown>;
485
484
  }
486
485
  declare class GeminiRealtime implements Session {
487
486
  private _apiKey;
@@ -490,8 +489,7 @@ declare class GeminiRealtime implements Session {
490
489
  private _voice;
491
490
  private _language;
492
491
  private _greeting;
493
- private _generationConfig;
494
- private _ws;
492
+ private _session;
495
493
  private _call;
496
494
  private _tools;
497
495
  private _recorder;
@@ -506,10 +504,7 @@ declare class GeminiRealtime implements Session {
506
504
  start(callSession: CallSession, tools?: ToolRegistry): Promise<void>;
507
505
  feedAudio(audio: Buffer): void;
508
506
  stop(): Promise<void>;
509
- private _sendSetup;
510
- private _waitSetupComplete;
511
- private _sendGreeting;
512
- private _receiveLoop;
507
+ private _buildToolSchemas;
513
508
  private _handleMessage;
514
509
  private _handleAudioData;
515
510
  private _flushAudioRemainder;
@@ -464,7 +464,8 @@ declare class OpenAIRealtime implements Session {
464
464
  /**
465
465
  * Google Gemini Realtime API session (speech-to-speech).
466
466
  *
467
- * Matches Python SDK's GeminiRealtime implementation.
467
+ * Uses @google/genai SDK's live.connect() method.
468
+ * Matches Python SDK's GeminiRealtime implementation (v0.14.0).
468
469
  */
469
470
 
470
471
  interface GeminiRealtimeOptions {
@@ -480,8 +481,6 @@ interface GeminiRealtimeOptions {
480
481
  language?: string;
481
482
  /** Send initial greeting. Default: true */
482
483
  greeting?: boolean;
483
- /** Generation config overrides. */
484
- generationConfig?: Record<string, unknown>;
485
484
  }
486
485
  declare class GeminiRealtime implements Session {
487
486
  private _apiKey;
@@ -490,8 +489,7 @@ declare class GeminiRealtime implements Session {
490
489
  private _voice;
491
490
  private _language;
492
491
  private _greeting;
493
- private _generationConfig;
494
- private _ws;
492
+ private _session;
495
493
  private _call;
496
494
  private _tools;
497
495
  private _recorder;
@@ -506,10 +504,7 @@ declare class GeminiRealtime implements Session {
506
504
  start(callSession: CallSession, tools?: ToolRegistry): Promise<void>;
507
505
  feedAudio(audio: Buffer): void;
508
506
  stop(): Promise<void>;
509
- private _sendSetup;
510
- private _waitSetupComplete;
511
- private _sendGreeting;
512
- private _receiveLoop;
507
+ private _buildToolSchemas;
513
508
  private _handleMessage;
514
509
  private _handleAudioData;
515
510
  private _flushAudioRemainder;
@@ -1,4 +1,4 @@
1
- import { DEFAULT_BASE_URL, AgentError, AgentConnectionError } from '../chunk-UKAT24I6.js';
1
+ import { DEFAULT_BASE_URL, AgentError, AgentConnectionError } from '../chunk-7S2OEBS6.js';
2
2
  import * as fs from 'fs';
3
3
  import * as path from 'path';
4
4
 
@@ -1792,8 +1792,8 @@ var GeminiRealtime = class {
1792
1792
  _voice;
1793
1793
  _language;
1794
1794
  _greeting;
1795
- _generationConfig;
1796
- _ws = null;
1795
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1796
+ _session = null;
1797
1797
  _call = null;
1798
1798
  _tools = null;
1799
1799
  _recorder = null;
@@ -1807,7 +1807,6 @@ var GeminiRealtime = class {
1807
1807
  this._voice = options.voice ?? "Kore";
1808
1808
  this._language = options.language ?? "ko";
1809
1809
  this._greeting = options.greeting ?? true;
1810
- this._generationConfig = options.generationConfig;
1811
1810
  }
1812
1811
  /** Inject per-call ToolRegistry. */
1813
1812
  setToolRegistry(registry) {
@@ -1826,83 +1825,78 @@ var GeminiRealtime = class {
1826
1825
  if (!this._apiKey) {
1827
1826
  throw new Error("Google API key is required. Set GOOGLE_API_KEY or pass apiKey option.");
1828
1827
  }
1829
- const { WebSocket } = await import('ws');
1830
- const url = `wss://generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent?key=${this._apiKey}`;
1831
- this._ws = new WebSocket(url);
1832
- return new Promise((resolve, reject) => {
1833
- const ws = this._ws;
1834
- ws.on("open", () => {
1835
- this._sendSetup();
1836
- this._waitSetupComplete().then(() => {
1837
- if (this._greeting) {
1838
- this._sendGreeting();
1828
+ const { GoogleGenAI } = await import('@google/genai/node');
1829
+ const client = new GoogleGenAI({ apiKey: this._apiKey });
1830
+ const config = {
1831
+ responseModalities: ["AUDIO"],
1832
+ speechConfig: {
1833
+ voiceConfig: {
1834
+ prebuiltVoiceConfig: {
1835
+ voiceName: this._voice
1839
1836
  }
1840
- this._receiveLoop();
1841
- resolve();
1842
- }).catch(reject);
1843
- });
1844
- ws.on("close", () => {
1845
- this._closed = true;
1846
- });
1847
- ws.on("error", (err) => {
1848
- if (!this._ws) {
1849
- reject(err);
1850
1837
  }
1851
- console.error("[GeminiRealtime] WebSocket error:", err.message);
1852
- });
1838
+ },
1839
+ inputAudioTranscription: {},
1840
+ outputAudioTranscription: {}
1841
+ };
1842
+ if (this._systemPrompt) {
1843
+ config["systemInstruction"] = this._systemPrompt;
1844
+ }
1845
+ const toolSchemas = this._buildToolSchemas();
1846
+ if (toolSchemas.length > 0) {
1847
+ config["tools"] = [{ functionDeclarations: toolSchemas }];
1848
+ }
1849
+ this._session = await client.live.connect({
1850
+ model: this._model,
1851
+ config,
1852
+ callbacks: {
1853
+ onmessage: (msg) => this._handleMessage(msg),
1854
+ onerror: (err) => {
1855
+ console.error("[GeminiRealtime] SDK error:", err);
1856
+ },
1857
+ onclose: () => {
1858
+ this._closed = true;
1859
+ }
1860
+ }
1853
1861
  });
1862
+ if (this._greeting) {
1863
+ this._session.sendClientContent({
1864
+ turns: [
1865
+ {
1866
+ role: "user",
1867
+ parts: [{ text: "\uC778\uC0AC\uD574 \uC8FC\uC138\uC694." }]
1868
+ }
1869
+ ],
1870
+ turnComplete: true
1871
+ });
1872
+ }
1854
1873
  }
1855
1874
  feedAudio(audio) {
1856
- if (this._ws && this._ws.readyState === 1 && !this._closed) {
1875
+ if (this._session && !this._closed) {
1857
1876
  const pcm8k = ulawToPcm16(audio);
1877
+ if (this._recorder) {
1878
+ this._recorder.writeInbound(pcm8k);
1879
+ }
1858
1880
  const pcm16k = resamplePcm16(pcm8k, 8e3, 16e3);
1859
- this._ws.send(
1860
- JSON.stringify({
1861
- realtimeInput: {
1862
- mediaChunks: [
1863
- {
1864
- mimeType: "audio/pcm;rate=16000",
1865
- data: pcm16k.toString("base64")
1866
- }
1867
- ]
1868
- }
1869
- })
1870
- );
1881
+ this._session.sendRealtimeInput({
1882
+ audio: {
1883
+ data: Buffer.from(pcm16k).toString("base64"),
1884
+ mimeType: "audio/pcm;rate=16000"
1885
+ }
1886
+ });
1871
1887
  }
1872
1888
  }
1873
1889
  async stop() {
1874
1890
  this._closed = true;
1875
- if (this._ws) {
1876
- this._ws.close();
1877
- this._ws = null;
1878
- }
1879
- }
1880
- _sendSetup() {
1881
- if (!this._ws || this._ws.readyState !== 1) return;
1882
- const setupConfig = {
1883
- model: `models/${this._model}`,
1884
- generationConfig: {
1885
- responseModalities: ["AUDIO"],
1886
- speechConfig: {
1887
- voiceConfig: {
1888
- prebuiltVoiceConfig: {
1889
- voiceName: this._voice
1890
- }
1891
- }
1892
- },
1893
- ...this._generationConfig
1894
- },
1895
- realtimeInputConfig: {
1896
- automaticActivityDetection: {
1897
- disabled: false
1898
- }
1891
+ if (this._session) {
1892
+ try {
1893
+ this._session.close();
1894
+ } catch {
1899
1895
  }
1900
- };
1901
- if (this._systemPrompt) {
1902
- setupConfig["systemInstruction"] = {
1903
- parts: [{ text: this._systemPrompt }]
1904
- };
1896
+ this._session = null;
1905
1897
  }
1898
+ }
1899
+ _buildToolSchemas() {
1906
1900
  const toolDefs = this._tools ? this._tools.toOpenAITools().map((t) => ({
1907
1901
  name: t.function.name,
1908
1902
  description: t.function.description,
@@ -1911,107 +1905,46 @@ var GeminiRealtime = class {
1911
1905
  )
1912
1906
  })) : [];
1913
1907
  toolDefs.push(HANG_UP_TOOL2);
1914
- setupConfig["tools"] = [{ functionDeclarations: toolDefs }];
1915
- this._ws.send(JSON.stringify({ setup: setupConfig }));
1916
- }
1917
- _waitSetupComplete() {
1918
- return new Promise((resolve, reject) => {
1919
- if (!this._ws) {
1920
- reject(new Error("WebSocket not connected"));
1921
- return;
1922
- }
1923
- const onMessage = (data) => {
1924
- try {
1925
- const msg = JSON.parse(data.toString());
1926
- if ("setupComplete" in msg) {
1927
- this._ws?.removeListener("message", onMessage);
1928
- resolve();
1929
- }
1930
- } catch {
1931
- }
1932
- };
1933
- this._ws.on("message", onMessage);
1934
- });
1935
- }
1936
- _sendGreeting() {
1937
- if (!this._ws || this._ws.readyState !== 1) return;
1938
- this._ws.send(
1939
- JSON.stringify({
1940
- clientContent: {
1941
- turns: [
1942
- {
1943
- role: "user",
1944
- parts: [{ text: "\uC778\uC0AC\uD574 \uC8FC\uC138\uC694." }]
1945
- }
1946
- ],
1947
- turnComplete: true
1948
- }
1949
- })
1950
- );
1951
- }
1952
- _receiveLoop() {
1953
- if (!this._ws) return;
1954
- this._ws.on("message", (data) => {
1955
- try {
1956
- const msg = JSON.parse(data.toString());
1957
- this._handleMessage(msg);
1958
- } catch {
1959
- }
1960
- });
1908
+ return toolDefs;
1961
1909
  }
1962
1910
  _handleMessage(msg) {
1963
1911
  if (!this._call) return;
1964
- const serverContent = msg["serverContent"];
1912
+ const serverContent = msg.serverContent;
1965
1913
  if (serverContent) {
1966
- const modelTurn = serverContent["modelTurn"];
1914
+ const modelTurn = serverContent.modelTurn;
1967
1915
  if (modelTurn) {
1968
- const parts = modelTurn["parts"];
1969
- if (parts) {
1970
- for (const part of parts) {
1971
- const inlineData = part["inlineData"];
1972
- if (inlineData && inlineData["data"]) {
1973
- const mimeType = inlineData["mimeType"] ?? "";
1974
- if (mimeType.includes("audio")) {
1975
- this._handleAudioData(inlineData["data"]);
1976
- }
1977
- }
1978
- const text = part["text"];
1979
- if (text && this._call) {
1980
- this._call._emit("transcript", "assistant", text);
1916
+ for (const part of modelTurn.parts ?? []) {
1917
+ const inlineData = part.inlineData;
1918
+ if (inlineData?.data) {
1919
+ const mimeType = inlineData.mimeType ?? "";
1920
+ if (mimeType.includes("audio")) {
1921
+ this._handleAudioData(inlineData.data);
1981
1922
  }
1982
1923
  }
1983
1924
  }
1984
1925
  }
1985
- if (serverContent["turnComplete"]) {
1926
+ if (serverContent.turnComplete) {
1986
1927
  this._flushAudioRemainder();
1987
1928
  }
1988
- if (serverContent["interrupted"]) {
1929
+ if (serverContent.interrupted) {
1989
1930
  if (this._call) {
1990
1931
  this._call.clearAudio();
1991
1932
  }
1992
1933
  this._sentAudioChunks = 0;
1993
1934
  this._audioRemainder = Buffer.alloc(0);
1994
1935
  }
1995
- }
1996
- const inputTranscription = msg["inputTranscription"];
1997
- if (inputTranscription) {
1998
- const text = inputTranscription["text"];
1999
- if (text && this._call) {
2000
- this._call._emit("transcript", "user", text);
1936
+ const inputText = serverContent.inputTranscription?.text;
1937
+ if (inputText && this._call) {
1938
+ this._call._emit("transcript", "user", inputText);
2001
1939
  }
2002
- }
2003
- const outputTranscription = msg["outputTranscription"];
2004
- if (outputTranscription) {
2005
- const text = outputTranscription["text"];
2006
- if (text && this._call) {
2007
- this._call._emit("transcript", "assistant", text);
1940
+ const outputText = serverContent.outputTranscription?.text;
1941
+ if (outputText && this._call) {
1942
+ this._call._emit("transcript", "assistant", outputText);
2008
1943
  }
2009
1944
  }
2010
- const toolCall = msg["toolCall"];
2011
- if (toolCall) {
2012
- this._handleToolCall(toolCall);
1945
+ if (msg.toolCall) {
1946
+ this._handleToolCall(msg.toolCall);
2013
1947
  }
2014
- if (msg["toolCallCancellation"]) ;
2015
1948
  }
2016
1949
  _handleAudioData(b64Data) {
2017
1950
  if (!this._call) return;
@@ -2042,13 +1975,13 @@ var GeminiRealtime = class {
2042
1975
  }
2043
1976
  }
2044
1977
  async _handleToolCall(toolCall) {
2045
- const functionCalls = toolCall["functionCalls"];
1978
+ const functionCalls = toolCall.functionCalls;
2046
1979
  if (!functionCalls) return;
2047
1980
  const responses = [];
2048
1981
  for (const fc of functionCalls) {
2049
- const name = fc["name"];
2050
- const fcId = fc["id"] ?? "";
2051
- const args = fc["args"] ?? {};
1982
+ const name = fc.name ?? "";
1983
+ const fcId = fc.id ?? "";
1984
+ const args = fc.args ?? {};
2052
1985
  if (name === "hang_up") {
2053
1986
  if (this._call) {
2054
1987
  this._call.hangup();
@@ -2076,14 +2009,10 @@ var GeminiRealtime = class {
2076
2009
  });
2077
2010
  }
2078
2011
  }
2079
- if (this._ws && this._ws.readyState === 1) {
2080
- this._ws.send(
2081
- JSON.stringify({
2082
- toolResponse: {
2083
- functionResponses: responses
2084
- }
2085
- })
2086
- );
2012
+ if (this._session) {
2013
+ this._session.sendToolResponse({
2014
+ functionResponses: responses
2015
+ });
2087
2016
  }
2088
2017
  }
2089
2018
  };