@aj-archipelago/cortex 1.3.7 → 1.3.9
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/README.md +578 -80
- package/helper-apps/cortex-file-handler/blobHandler.js +27 -8
- package/helper-apps/cortex-file-handler/index.js +20 -2
- package/helper-apps/cortex-realtime-voice-server/client/src/chat/Chat.tsx +51 -11
- package/helper-apps/cortex-realtime-voice-server/src/SocketServer.ts +220 -183
- package/helper-apps/cortex-realtime-voice-server/src/Tools.ts +18 -34
- package/helper-apps/cortex-realtime-voice-server/src/cortex/utils.ts +29 -15
- package/helper-apps/cortex-realtime-voice-server/src/realtime/client.ts +47 -1
- package/helper-apps/cortex-realtime-voice-server/src/utils/prompt.ts +2 -11
- package/package.json +1 -1
- package/pathways/system/entity/memory/sys_search_memory.js +2 -1
- package/pathways/system/entity/sys_entity_start.js +6 -7
- package/pathways/system/entity/sys_generator_voice_sample.js +2 -2
- package/pathways/translate_gpt4_omni.js +20 -0
- package/pathways/translate_subtitle.js +326 -135
- package/pathways/translate_subtitle_helper.js +4 -16
- package/server/plugins/azureVideoTranslatePlugin.js +27 -15
- package/server/plugins/claude3VertexPlugin.js +10 -17
- package/server/plugins/gemini15VisionPlugin.js +16 -3
- package/server/plugins/modelPlugin.js +27 -0
- package/server/plugins/openAiVisionPlugin.js +26 -8
- package/tests/multimodal_conversion.test.js +88 -12
- package/tests/translate_srt.test.js +66 -14
|
@@ -191,7 +191,7 @@ async function deleteBlob(requestId) {
|
|
|
191
191
|
return result;
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
-
async function uploadBlob(context, req, saveToLocal = false, useGoogle = false, filePath=null) {
|
|
194
|
+
async function uploadBlob(context, req, saveToLocal = false, useGoogle = false, filePath=null, hash=null) {
|
|
195
195
|
return new Promise((resolve, reject) => {
|
|
196
196
|
try {
|
|
197
197
|
let requestId = uuidv4();
|
|
@@ -201,7 +201,7 @@ async function uploadBlob(context, req, saveToLocal = false, useGoogle = false,
|
|
|
201
201
|
if (filePath) {
|
|
202
202
|
const file = fs.createReadStream(filePath);
|
|
203
203
|
const filename = path.basename(filePath);
|
|
204
|
-
uploadFile(context, requestId, body, saveToLocal, useGoogle, file, filename, resolve)
|
|
204
|
+
uploadFile(context, requestId, body, saveToLocal, useGoogle, file, filename, resolve, hash);
|
|
205
205
|
} else {
|
|
206
206
|
// Otherwise, continue working with form-data
|
|
207
207
|
const busboy = Busboy({ headers: req.headers });
|
|
@@ -215,7 +215,7 @@ async function uploadBlob(context, req, saveToLocal = false, useGoogle = false,
|
|
|
215
215
|
});
|
|
216
216
|
|
|
217
217
|
busboy.on("file", async (fieldname, file, filename) => {
|
|
218
|
-
uploadFile(context, requestId, body, saveToLocal, useGoogle, file, filename?.filename || filename, resolve)
|
|
218
|
+
uploadFile(context, requestId, body, saveToLocal, useGoogle, file, filename?.filename || filename, resolve, hash);
|
|
219
219
|
});
|
|
220
220
|
|
|
221
221
|
busboy.on("error", (error) => {
|
|
@@ -240,7 +240,7 @@ async function uploadBlob(context, req, saveToLocal = false, useGoogle = false,
|
|
|
240
240
|
});
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
-
async function uploadFile(context, requestId, body, saveToLocal, useGoogle, file, filename, resolve) {
|
|
243
|
+
async function uploadFile(context, requestId, body, saveToLocal, useGoogle, file, filename, resolve, hash=null) {
|
|
244
244
|
// do not use Google if the file is not an image or video
|
|
245
245
|
const ext = path.extname(filename).toLowerCase();
|
|
246
246
|
const canUseGoogle = IMAGE_EXTENSIONS.includes(ext) || VIDEO_EXTENSIONS.includes(ext) || AUDIO_EXTENSIONS.includes(ext);
|
|
@@ -306,16 +306,29 @@ async function uploadFile(context, requestId, body, saveToLocal, useGoogle, file
|
|
|
306
306
|
const { url } = body;
|
|
307
307
|
const gcsFile = gcs.bucket(GCS_BUCKETNAME).file(encodedFilename);
|
|
308
308
|
const writeStream = gcsFile.createWriteStream();
|
|
309
|
-
|
|
309
|
+
|
|
310
310
|
const response = await axios({
|
|
311
311
|
method: "get",
|
|
312
312
|
url: url,
|
|
313
313
|
responseType: "stream",
|
|
314
314
|
});
|
|
315
|
-
|
|
316
|
-
//
|
|
315
|
+
|
|
316
|
+
// // Get the total file size from the response headers
|
|
317
|
+
// const totalSize = Number(response.headers["content-length"]);
|
|
318
|
+
// let downloadedSize = 0;
|
|
319
|
+
|
|
320
|
+
// // Listen to the 'data' event to track the progress
|
|
321
|
+
// response.data.on("data", (chunk) => {
|
|
322
|
+
// downloadedSize += chunk.length;
|
|
323
|
+
|
|
324
|
+
// // Calculate and display the progress
|
|
325
|
+
// const progress = (downloadedSize / totalSize) * 100;
|
|
326
|
+
// console.log(`Progress gsc of ${encodedFilename}: ${progress.toFixed(2)}%`);
|
|
327
|
+
// });
|
|
328
|
+
|
|
329
|
+
// Pipe the Axios response stream directly into the GCS Write Stream
|
|
317
330
|
response.data.pipe(writeStream);
|
|
318
|
-
|
|
331
|
+
|
|
319
332
|
await new Promise((resolve, reject) => {
|
|
320
333
|
writeStream.on("finish", resolve);
|
|
321
334
|
writeStream.on("error", reject);
|
|
@@ -324,6 +337,12 @@ async function uploadFile(context, requestId, body, saveToLocal, useGoogle, file
|
|
|
324
337
|
body.gcs = `gs://${GCS_BUCKETNAME}/${encodedFilename}`;
|
|
325
338
|
}
|
|
326
339
|
|
|
340
|
+
if(!body.filename) {
|
|
341
|
+
body.filename = filename;
|
|
342
|
+
}
|
|
343
|
+
if(hash && !body.hash) {
|
|
344
|
+
body.hash = hash;
|
|
345
|
+
}
|
|
327
346
|
resolve(body); // Resolve the promise
|
|
328
347
|
}
|
|
329
348
|
|
|
@@ -120,7 +120,7 @@ async function main(context, req) {
|
|
|
120
120
|
return;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
const { uri, requestId, save, hash, checkHash, fetch, load, restore } = req.body?.params || req.query;
|
|
123
|
+
const { uri, requestId, save, hash, checkHash, clearHash, fetch, load, restore } = req.body?.params || req.query;
|
|
124
124
|
|
|
125
125
|
const filepond = fetch || restore || load;
|
|
126
126
|
if (req.method.toLowerCase() === `get` && filepond) {
|
|
@@ -163,6 +163,24 @@ async function main(context, req) {
|
|
|
163
163
|
return;
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
+
if(hash && clearHash){
|
|
167
|
+
try {
|
|
168
|
+
const hashValue = await getFileStoreMap(hash);
|
|
169
|
+
await removeFromFileStoreMap(hash);
|
|
170
|
+
context.res = {
|
|
171
|
+
status: 200,
|
|
172
|
+
body: hashValue ? `Hash ${hash} removed` : `Hash ${hash} not found`
|
|
173
|
+
};
|
|
174
|
+
} catch (error) {
|
|
175
|
+
context.res = {
|
|
176
|
+
status: 500,
|
|
177
|
+
body: `Error occurred during hash cleanup: ${error}`
|
|
178
|
+
};
|
|
179
|
+
console.log('Error occurred during hash cleanup:', error);
|
|
180
|
+
}
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
|
|
166
184
|
if(hash && checkHash){ //check if hash exists
|
|
167
185
|
context.log(`Checking hash: ${hash}`);
|
|
168
186
|
const result = await getFileStoreMap(hash);
|
|
@@ -188,7 +206,7 @@ async function main(context, req) {
|
|
|
188
206
|
|
|
189
207
|
if (req.method.toLowerCase() === `post`) {
|
|
190
208
|
const { useGoogle } = req.body?.params || req.query;
|
|
191
|
-
const { url } = await uploadBlob(context, req, !useAzure, useGoogle);
|
|
209
|
+
const { url } = await uploadBlob(context, req, !useAzure, useGoogle, null, hash);
|
|
192
210
|
context.log(`File url: ${url}`);
|
|
193
211
|
if(hash && context?.res?.body){ //save hash after upload
|
|
194
212
|
await setFileStoreMap(hash, context.res.body);
|
|
@@ -82,16 +82,35 @@ export default function Chat({
|
|
|
82
82
|
logger.log('Stopping conversation');
|
|
83
83
|
const wavRecorder = wavRecorderRef.current;
|
|
84
84
|
const wavStreamPlayer = wavStreamPlayerRef.current;
|
|
85
|
+
const socket = socketRef.current;
|
|
85
86
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
try {
|
|
88
|
+
// First stop recording and audio playback
|
|
89
|
+
if (wavRecorder.getStatus() === "recording") {
|
|
90
|
+
await wavRecorder.end();
|
|
91
|
+
}
|
|
92
|
+
if (wavStreamPlayer) {
|
|
93
|
+
await wavStreamPlayer.interrupt();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Clean up socket connection first
|
|
97
|
+
if (socket) {
|
|
98
|
+
// Only emit if we're still connected
|
|
99
|
+
if (socket.connected) {
|
|
100
|
+
socket.emit('conversationCompleted');
|
|
101
|
+
// Wait a bit to ensure the message is sent
|
|
102
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
103
|
+
}
|
|
104
|
+
socket.removeAllListeners();
|
|
105
|
+
socket.disconnect();
|
|
106
|
+
// Create a new socket instance to ensure clean state
|
|
107
|
+
socketRef.current = io(`/?userId=${userId}&userName=${userName}&aiName=${aiName}&voice=${voice}&aiStyle=${aiStyle}&language=${language}`, {
|
|
108
|
+
autoConnect: false,
|
|
109
|
+
reconnection: false
|
|
110
|
+
});
|
|
111
|
+
}
|
|
93
112
|
|
|
94
|
-
//
|
|
113
|
+
// Then clean up audio nodes
|
|
95
114
|
if (sourceNodeRef.current) {
|
|
96
115
|
sourceNodeRef.current.disconnect();
|
|
97
116
|
sourceNodeRef.current = null;
|
|
@@ -109,15 +128,36 @@ export default function Chat({
|
|
|
109
128
|
wavRecorderRef.current = new WavRecorder({sampleRate: 24000});
|
|
110
129
|
wavStreamPlayerRef.current = new WavStreamPlayer({sampleRate: 24000});
|
|
111
130
|
|
|
131
|
+
// Play disconnect sound last
|
|
132
|
+
await SoundEffects.playDisconnect();
|
|
133
|
+
|
|
134
|
+
// Reset state
|
|
135
|
+
setIsRecording(false);
|
|
136
|
+
setIsMuted(false);
|
|
137
|
+
setImageUrls([]);
|
|
138
|
+
setIsAudioPlaying(false);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
logger.error('Error stopping conversation:', error);
|
|
141
|
+
// Even if there's an error, try to reset critical state
|
|
112
142
|
setIsRecording(false);
|
|
113
|
-
|
|
114
|
-
setImageUrls([]); // Clear images on disconnect
|
|
143
|
+
setIsAudioPlaying(false);
|
|
115
144
|
}
|
|
116
|
-
}, []);
|
|
145
|
+
}, [userId, userName, aiName, voice, aiStyle, language]);
|
|
117
146
|
|
|
118
147
|
const startConversation = useCallback(() => {
|
|
119
148
|
logger.log('Starting conversation');
|
|
120
149
|
|
|
150
|
+
// Clean up any existing socket connection first
|
|
151
|
+
if (socketRef.current?.connected) {
|
|
152
|
+
socketRef.current.disconnect();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Create a new socket instance
|
|
156
|
+
socketRef.current = io(`/?userId=${userId}&userName=${userName}&aiName=${aiName}&voice=${voice}&aiStyle=${aiStyle}&language=${language}`, {
|
|
157
|
+
autoConnect: false,
|
|
158
|
+
timeout: 10000 // 10 second connection timeout
|
|
159
|
+
});
|
|
160
|
+
|
|
121
161
|
const socket = socketRef.current;
|
|
122
162
|
|
|
123
163
|
// Remove ALL existing listeners before adding new ones
|