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.
- package/lib/speech.js +11 -2
- package/package.json +1 -1
- 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
|
|
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
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
|
|
73
|
-
const
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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 } =
|
|
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 } =
|
|
3683
|
+
const { getVoices } = await getSpeech();
|
|
3715
3684
|
const voices = getVoices();
|
|
3716
3685
|
broadcastSync({ type: 'voice_list', voices });
|
|
3717
3686
|
} catch (err2) {
|