agentgui 1.0.276 → 1.0.278

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.
Files changed (3) hide show
  1. package/lib/speech.js +11 -2
  2. package/package.json +1 -1
  3. package/server.js +89 -120
package/lib/speech.js CHANGED
@@ -134,11 +134,20 @@ function preloadTTS() {
134
134
  console.log('[TTS] pocket-tts functions not available');
135
135
  return;
136
136
  }
137
+ if (typeof serverTTS.isInstalled === 'function' && !serverTTS.isInstalled()) {
138
+ console.log('[TTS] pocket-tts not installed yet - will install on first use');
139
+ return;
140
+ }
141
+ const portableDataDir = process.env.PORTABLE_DATA_DIR;
142
+ const binaryPaths = portableDataDir ? [
143
+ path.join(portableDataDir, 'pocket-venv', 'Scripts', 'pocket-tts.exe'),
144
+ path.join(portableDataDir, 'pocket-venv', 'bin', 'pocket-tts'),
145
+ ] : undefined;
137
146
  const defaultVoice = serverTTS.findVoiceFile('custom_cleetus', EXTRA_VOICE_DIRS) || '/config/voices/cleetus.wav';
138
147
  const voicePath = fs.existsSync(defaultVoice) ? defaultVoice : null;
139
- serverTTS.start(voicePath, {}).then(ok => {
148
+ serverTTS.start(voicePath, binaryPaths ? { binaryPaths } : {}).then(ok => {
140
149
  if (ok) console.log('[TTS] pocket-tts sidecar started');
141
- else console.log('[TTS] pocket-tts failed to start');
150
+ else console.log('[TTS] pocket-tts not available - will use edge-tts fallback');
142
151
  }).catch(err => {
143
152
  console.error('[TTS] pocket-tts start error:', err.message);
144
153
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.276",
3
+ "version": "1.0.278",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
package/server.js CHANGED
@@ -8,6 +8,9 @@ import { fileURLToPath } from 'url';
8
8
  import { WebSocketServer } from 'ws';
9
9
  import { execSync, spawn } from 'child_process';
10
10
  import { createRequire } from 'module';
11
+ import express from 'express';
12
+ import Busboy from 'busboy';
13
+ import fsbrowse from 'fsbrowse';
11
14
  import { OAuth2Client } from 'google-auth-library';
12
15
  import { queries, dataDir } from './database.js';
13
16
  import { runClaudeWithStreaming } from './lib/claude-runner.js';
@@ -69,12 +72,9 @@ async function ensureModelsDownloaded() {
69
72
  }
70
73
 
71
74
  try {
72
- const { createRequire: cr } = await import('module');
73
- const r = cr(import.meta.url);
74
-
75
- const gmguiModels = path.join(dataDir, 'models');
76
- const sttDir = path.join(gmguiModels, 'onnx-community', 'whisper-base');
77
- const ttsDir = path.join(gmguiModels, 'tts');
75
+ const gmguiModels = path.join(dataDir, 'models');
76
+ const sttDir = path.join(gmguiModels, 'onnx-community', 'whisper-base');
77
+ const ttsDir = path.join(gmguiModels, 'tts');
78
78
 
79
79
  const sttOk = fs.existsSync(sttDir) && fs.readdirSync(sttDir).length > 0;
80
80
  const ttsOk = fs.existsSync(ttsDir) && fs.readdirSync(ttsDir).length > 0;
@@ -91,116 +91,89 @@ async function ensureModelsDownloaded() {
91
91
  const totalFiles = 16;
92
92
  let completedFiles = 0;
93
93
 
94
+ const require = createRequire(import.meta.url);
95
+
94
96
  if (!sttOk) {
95
- console.log('[MODELS] Downloading STT model via IPFS...');
96
97
  broadcastModelProgress({ started: true, done: false, downloading: true, type: 'stt', source: 'ipfs', completedFiles, totalFiles });
98
+ let sttDownloaded = false;
99
+
100
+ const ipfsCid = queries.getIpfsCidByModel('whisper-base', 'stt');
101
+ if (ipfsCid) {
102
+ console.log('[MODELS] Downloading STT from Lighthouse IPFS:', ipfsCid.cid);
103
+ fs.mkdirSync(sttDir, { recursive: true });
104
+ const sttUrl = `https://gateway.lighthouse.storage/ipfs/${ipfsCid.cid}/stt/onnx-community/whisper-base/onnx/`;
105
+ const sttFile = path.join(sttDir, 'whisper-onnx.tar');
106
+ try {
107
+ await IPFSDownloader.downloadWithProgress(sttUrl, sttFile, (progress) => {
108
+ broadcastModelProgress({ started: true, done: false, downloading: true, type: 'stt', source: 'lighthouse-ipfs', gateway: 'gateway.lighthouse.storage', ...progress, completedFiles, totalFiles });
109
+ });
110
+ console.log('[MODELS] STT model downloaded from Lighthouse IPFS');
111
+ sttDownloaded = true;
112
+ } catch (err) {
113
+ console.error('[MODELS] IPFS STT download failed:', err.message, '- falling back to HuggingFace');
114
+ }
115
+ } else {
116
+ console.warn('[MODELS] No STT IPFS CID registered - using HuggingFace directly');
117
+ }
97
118
 
98
- try {
99
- const ipfsCid = queries.getIpfsCidByModel('whisper-base', 'stt');
100
- if (!ipfsCid) {
101
- console.warn('[MODELS] STT IPFS CID not registered in database');
102
- console.warn('[MODELS] To enable STT: Pin whisper-base model to IPFS and register CID via: queries.recordIpfsCid(cid, "whisper-base", "stt", hash, gateway)');
103
- broadcastModelProgress({
104
- done: true,
105
- error: 'STT model CID not registered - speech will be unavailable. Register via IPFS.',
106
- type: 'stt',
107
- completedFiles,
108
- totalFiles
119
+ if (!sttDownloaded) {
120
+ console.log('[MODELS] Downloading STT model via HuggingFace...');
121
+ broadcastModelProgress({ started: true, done: false, downloading: true, type: 'stt', source: 'huggingface', completedFiles, totalFiles });
122
+ try {
123
+ const whisperModels = require('webtalk/whisper-models');
124
+ const modelsDir = path.join(dataDir, 'models');
125
+ fs.mkdirSync(modelsDir, { recursive: true });
126
+ await whisperModels.ensureModel('onnx-community/whisper-base', {
127
+ modelsDir,
128
+ whisperBaseUrl: 'https://huggingface.co/',
109
129
  });
110
- } else {
111
- console.log('[MODELS] Downloading STT from Lighthouse IPFS:', ipfsCid.cid);
112
- fs.mkdirSync(sttDir, { recursive: true });
113
-
114
- // Download from Lighthouse gateway: https://gateway.lighthouse.storage/ipfs/CID/stt/onnx-community/whisper-base/
115
- const lighthouseGateway = 'https://gateway.lighthouse.storage/ipfs';
116
- const sttUrl = `${lighthouseGateway}/${ipfsCid.cid}/stt/onnx-community/whisper-base/onnx/`;
117
- const sttFile = path.join(sttDir, 'whisper-onnx.tar');
118
-
119
- await IPFSDownloader.downloadWithProgress(
120
- sttUrl,
121
- sttFile,
122
- (progress) => {
123
- broadcastModelProgress({
124
- started: true,
125
- done: false,
126
- downloading: true,
127
- type: 'stt',
128
- source: 'lighthouse-ipfs',
129
- gateway: 'gateway.lighthouse.storage',
130
- ...progress,
131
- completedFiles,
132
- totalFiles
133
- });
134
- }
135
- );
136
- console.log('[MODELS] STT model downloaded successfully from Lighthouse IPFS');
130
+ console.log('[MODELS] STT model downloaded from HuggingFace');
131
+ } catch (hfErr) {
132
+ console.error('[MODELS] HuggingFace STT download failed:', hfErr.message);
133
+ broadcastModelProgress({ done: true, error: `STT download failed: ${hfErr.message}`, type: 'stt', completedFiles, totalFiles });
137
134
  }
138
- } catch (err) {
139
- console.error('[MODELS] IPFS STT download failed:', err.message);
140
- broadcastModelProgress({
141
- done: true,
142
- error: `IPFS STT download failed: ${err.message}`,
143
- type: 'stt',
144
- completedFiles,
145
- totalFiles
146
- });
147
135
  }
148
136
  completedFiles += 10;
149
137
  }
150
138
 
151
139
  if (!ttsOk) {
152
- console.log('[MODELS] Downloading TTS models via IPFS...');
153
140
  broadcastModelProgress({ started: true, done: false, downloading: true, type: 'tts', source: 'ipfs', completedFiles, totalFiles });
141
+ let ttsDownloaded = false;
142
+
143
+ const ipfsCid = queries.getIpfsCidByModel('tts', 'voice');
144
+ if (ipfsCid) {
145
+ console.log('[MODELS] Downloading TTS from Lighthouse IPFS:', ipfsCid.cid);
146
+ fs.mkdirSync(ttsDir, { recursive: true });
147
+ const ttsUrl = `https://gateway.lighthouse.storage/ipfs/${ipfsCid.cid}/tts/`;
148
+ const ttsFile = path.join(ttsDir, 'tts-models.tar');
149
+ try {
150
+ await IPFSDownloader.downloadWithProgress(ttsUrl, ttsFile, (progress) => {
151
+ broadcastModelProgress({ started: true, done: false, downloading: true, type: 'tts', source: 'lighthouse-ipfs', gateway: 'gateway.lighthouse.storage', ...progress, completedFiles, totalFiles });
152
+ });
153
+ console.log('[MODELS] TTS models downloaded from Lighthouse IPFS');
154
+ ttsDownloaded = true;
155
+ } catch (err) {
156
+ console.error('[MODELS] IPFS TTS download failed:', err.message, '- falling back to HuggingFace');
157
+ }
158
+ } else {
159
+ console.warn('[MODELS] No TTS IPFS CID registered - using HuggingFace directly');
160
+ }
154
161
 
155
- try {
156
- const ipfsCid = queries.getIpfsCidByModel('tts', 'voice');
157
- if (!ipfsCid) {
158
- console.warn('[MODELS] TTS IPFS CID not registered in database');
159
- console.warn('[MODELS] To enable TTS: Pin TTS models to IPFS and register CID via: queries.recordIpfsCid(cid, "tts", "voice", hash, gateway)');
160
- broadcastModelProgress({
161
- done: true,
162
- error: 'TTS model CID not registered - speech synthesis will be unavailable. Register via IPFS.',
163
- type: 'tts',
164
- completedFiles,
165
- totalFiles
162
+ if (!ttsDownloaded) {
163
+ console.log('[MODELS] Downloading TTS models via HuggingFace...');
164
+ broadcastModelProgress({ started: true, done: false, downloading: true, type: 'tts', source: 'huggingface', completedFiles, totalFiles });
165
+ try {
166
+ const ttsModels = require('webtalk/tts-models');
167
+ await ttsModels.ensureTTSModels({
168
+ ttsModelsDir: ttsDir,
169
+ ttsDir: path.join(dataDir, 'models', 'tts'),
170
+ ttsBaseUrl: 'https://huggingface.co/datasets/AnEntrypoint/sttttsmodels/resolve/main/tts/',
166
171
  });
167
- } else {
168
- console.log('[MODELS] Downloading TTS from Lighthouse IPFS:', ipfsCid.cid);
169
- fs.mkdirSync(ttsDir, { recursive: true });
170
-
171
- // Download from Lighthouse gateway: https://gateway.lighthouse.storage/ipfs/CID/tts/
172
- const lighthouseGateway = 'https://gateway.lighthouse.storage/ipfs';
173
- const ttsUrl = `${lighthouseGateway}/${ipfsCid.cid}/tts/`;
174
- const ttsFile = path.join(ttsDir, 'tts-models.tar');
175
-
176
- await IPFSDownloader.downloadWithProgress(
177
- ttsUrl,
178
- ttsFile,
179
- (progress) => {
180
- broadcastModelProgress({
181
- started: true,
182
- done: false,
183
- downloading: true,
184
- type: 'tts',
185
- source: 'lighthouse-ipfs',
186
- gateway: 'gateway.lighthouse.storage',
187
- ...progress,
188
- completedFiles,
189
- totalFiles
190
- });
191
- }
192
- );
193
- console.log('[MODELS] TTS models downloaded successfully from Lighthouse IPFS');
172
+ console.log('[MODELS] TTS models downloaded from HuggingFace');
173
+ } catch (hfErr) {
174
+ console.error('[MODELS] HuggingFace TTS download failed:', hfErr.message);
175
+ broadcastModelProgress({ done: true, error: `TTS download failed: ${hfErr.message}`, type: 'tts', completedFiles, totalFiles });
194
176
  }
195
- } catch (err) {
196
- console.error('[MODELS] IPFS TTS download failed:', err.message);
197
- broadcastModelProgress({
198
- done: true,
199
- error: `IPFS TTS download failed: ${err.message}`,
200
- type: 'tts',
201
- completedFiles,
202
- totalFiles
203
- });
204
177
  }
205
178
  completedFiles += 6;
206
179
  }
@@ -276,10 +249,6 @@ function pushTTSAudio(cacheKey, wav, conversationId, sessionId, voiceId) {
276
249
  });
277
250
  }
278
251
 
279
- const require = createRequire(import.meta.url);
280
- const express = require('express');
281
- const Busboy = require('busboy');
282
- const fsbrowse = require('fsbrowse');
283
252
 
284
253
  const SYSTEM_PROMPT = `Your output will be spoken aloud by a text-to-speech system. Write ONLY plain conversational sentences that sound natural when read aloud. Never use markdown, bold, italics, headers, bullet points, numbered lists, tables, or any formatting. Never use colons to introduce lists or options. Never use labels like "Option A" or "1." followed by a title. Instead of listing options, describe them conversationally in flowing sentences. For example, instead of "**Option 1**: Do X" say "One approach would be to do X." Keep sentences short and simple. Use transition words like "also", "another option", "or alternatively" to connect ideas. When mentioning file names, spell out the dot between the name and extension as the word "dot" so it is spoken clearly. For example, say "server dot js" instead of "server.js", "index dot html" instead of "index.html", and "package dot json" instead of "package.json". Write as if you are speaking to someone in a casual conversation.`;
285
254
 
@@ -296,16 +265,16 @@ const debugLog = (msg) => {
296
265
  console.error(`[${timestamp}] ${msg}`);
297
266
  };
298
267
 
299
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
300
- const PORT = process.env.PORT || 3000;
301
- const BASE_URL = (process.env.BASE_URL || '/gm').replace(/\/+$/, '');
302
- const watch = process.argv.includes('--no-watch') ? false : (process.argv.includes('--watch') || process.env.HOT_RELOAD !== 'false');
303
-
304
- const STARTUP_CWD = process.env.STARTUP_CWD || process.cwd();
305
- const staticDir = process.env.PORTABLE_EXE_DIR
306
- ? path.join(process.env.PORTABLE_EXE_DIR, 'static')
307
- : path.join(__dirname, 'static');
308
- if (!fs.existsSync(staticDir)) fs.mkdirSync(staticDir, { recursive: true });
268
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
269
+ const PORT = process.env.PORT || 3000;
270
+ const BASE_URL = (process.env.BASE_URL || '/gm').replace(/\/+$/, '');
271
+ const watch = process.argv.includes('--no-watch') ? false : (process.argv.includes('--watch') || process.env.HOT_RELOAD !== 'false');
272
+
273
+ const STARTUP_CWD = process.env.STARTUP_CWD || process.cwd();
274
+ const staticDir = process.env.PORTABLE_EXE_DIR
275
+ ? path.join(process.env.PORTABLE_EXE_DIR, 'static')
276
+ : path.join(__dirname, 'static');
277
+ if (!fs.existsSync(staticDir)) fs.mkdirSync(staticDir, { recursive: true });
309
278
 
310
279
  // Express sub-app for fsbrowse file browser and file upload
311
280
  const expressApp = express();
@@ -3697,21 +3666,21 @@ function onServerReady() {
3697
3666
 
3698
3667
  resumeInterruptedStreams().catch(err => console.error('[RESUME] Startup error:', err.message));
3699
3668
 
3700
- ensureModelsDownloaded().then(ok => {
3669
+ ensureModelsDownloaded().then(async ok => {
3701
3670
  if (ok) console.log('[MODELS] Speech models ready');
3702
3671
  else console.log('[MODELS] Speech model download failed');
3703
3672
  try {
3704
- const { getVoices } = require('./lib/speech.js');
3673
+ const { getVoices } = await getSpeech();
3705
3674
  const voices = getVoices();
3706
3675
  broadcastSync({ type: 'voice_list', voices });
3707
3676
  } catch (err) {
3708
3677
  debugLog('[VOICE] Failed to broadcast voices: ' + err.message);
3709
3678
  broadcastSync({ type: 'voice_list', voices: [] });
3710
3679
  }
3711
- }).catch(err => {
3680
+ }).catch(async err => {
3712
3681
  console.error('[MODELS] Download error:', err.message);
3713
3682
  try {
3714
- const { getVoices } = require('./lib/speech.js');
3683
+ const { getVoices } = await getSpeech();
3715
3684
  const voices = getVoices();
3716
3685
  broadcastSync({ type: 'voice_list', voices });
3717
3686
  } catch (err2) {