@aj-archipelago/cortex 1.1.20 → 1.1.21
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/config.js +12 -0
- package/helper-apps/cortex-file-handler/blobHandler.js +32 -5
- package/helper-apps/cortex-file-handler/index.js +8 -7
- package/lib/pathwayTools.js +7 -1
- package/lib/requestExecutor.js +4 -4
- package/lib/util.js +163 -1
- package/package.json +1 -1
- package/pathways/index.js +2 -0
- package/pathways/transcribe_neuralspace.js +18 -0
- package/server/modelExecutor.js +4 -0
- package/server/pathwayResolver.js +19 -1
- package/server/plugins/claude3VertexPlugin.js +2 -1
- package/server/plugins/gemini15ChatPlugin.js +8 -3
- package/server/plugins/gemini15VisionPlugin.js +19 -3
- package/server/plugins/geminiChatPlugin.js +1 -1
- package/server/plugins/geminiVisionPlugin.js +2 -3
- package/server/plugins/neuralSpacePlugin.js +252 -0
- package/server/plugins/openAiVisionPlugin.js +19 -8
- package/server/plugins/openAiWhisperPlugin.js +5 -152
- package/server/plugins/palmChatPlugin.js +1 -1
- package/server/resolver.js +3 -4
- package/server/typeDef.js +1 -0
- package/tests/claude3VertexPlugin.test.js +214 -0
- package/tests/mocks.js +2 -0
- package/tests/openAiChatPlugin.test.js +4 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import ModelPlugin from "./modelPlugin.js";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import FormData from "form-data";
|
|
4
|
+
import logger from "../../lib/logger.js";
|
|
5
|
+
import {
|
|
6
|
+
alignSubtitles,
|
|
7
|
+
deleteTempPath,
|
|
8
|
+
downloadFile,
|
|
9
|
+
getMediaChunks,
|
|
10
|
+
} from "../../lib/util.js";
|
|
11
|
+
import CortexRequest from "../../lib/cortexRequest.js";
|
|
12
|
+
import { publishRequestProgress } from "../../lib/redisSubscription.js";
|
|
13
|
+
|
|
14
|
+
const OFFSET_CHUNK = 500; //seconds of each chunk offset, only used if helper does not provide
|
|
15
|
+
|
|
16
|
+
function convertToSrt(timestamps) {
|
|
17
|
+
let srt = "";
|
|
18
|
+
for (let i = 0; i < timestamps.length; i++) {
|
|
19
|
+
const _start = timestamps[i].start ?? timestamps[i].startTime;
|
|
20
|
+
const _end = timestamps[i].end ?? timestamps[i].endTime;
|
|
21
|
+
const _text = timestamps[i].word ?? timestamps[i].text;
|
|
22
|
+
const start = new Date(_start * 1000)
|
|
23
|
+
.toISOString()
|
|
24
|
+
.slice(11, -1)
|
|
25
|
+
.replace(".", ",");
|
|
26
|
+
const end = new Date(_end * 1000)
|
|
27
|
+
.toISOString()
|
|
28
|
+
.slice(11, -1)
|
|
29
|
+
.replace(".", ",");
|
|
30
|
+
srt += `${i + 1}\n${start} --> ${end}\n${_text}\n\n`;
|
|
31
|
+
}
|
|
32
|
+
return srt;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function convertToVtt(timestamps) {
|
|
36
|
+
let vtt = "WEBVTT\n\n";
|
|
37
|
+
for (let i = 0; i < timestamps.length; i++) {
|
|
38
|
+
const _start = timestamps[i].start ?? timestamps[i].startTime;
|
|
39
|
+
const _end = timestamps[i].end ?? timestamps[i].endTime;
|
|
40
|
+
const _text = timestamps[i].word ?? timestamps[i].text;
|
|
41
|
+
const start = new Date(_start * 1000)
|
|
42
|
+
.toISOString()
|
|
43
|
+
.slice(11, -1)
|
|
44
|
+
.replace(".", ",");
|
|
45
|
+
const end = new Date(_end * 1000)
|
|
46
|
+
.toISOString()
|
|
47
|
+
.slice(11, -1)
|
|
48
|
+
.replace(".", ",");
|
|
49
|
+
vtt += `${start} --> ${end}\n${_text}\n\n`;
|
|
50
|
+
}
|
|
51
|
+
return vtt;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
class NeuralSpacePlugin extends ModelPlugin {
|
|
55
|
+
constructor(pathway, model) {
|
|
56
|
+
super(pathway, model);
|
|
57
|
+
this.pathwayResolver = null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async execute(text, parameters, prompt, cortexRequest) {
|
|
61
|
+
const { responseFormat, file, language, wordTimestamped, maxLineWidth } =
|
|
62
|
+
parameters;
|
|
63
|
+
|
|
64
|
+
let chunks = [];
|
|
65
|
+
let offsets = [];
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const { pathwayResolver } = cortexRequest;
|
|
69
|
+
|
|
70
|
+
const { requestId } = pathwayResolver;
|
|
71
|
+
|
|
72
|
+
const mediaChunks = await getMediaChunks(file, requestId);
|
|
73
|
+
|
|
74
|
+
if (!mediaChunks || !mediaChunks.length) {
|
|
75
|
+
throw new Error(
|
|
76
|
+
`Error in getting chunks from media helper for file ${file}`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const uris = mediaChunks.map((chunk) => chunk?.uri || chunk);
|
|
81
|
+
offsets = mediaChunks.map(
|
|
82
|
+
(chunk, index) => chunk?.offset || index * OFFSET_CHUNK
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
let totalCount = uris.length * 2; // [download, request] jobs per chunk
|
|
86
|
+
let completedCount = 0;
|
|
87
|
+
|
|
88
|
+
const sendProgress = () => {
|
|
89
|
+
completedCount++;
|
|
90
|
+
if (completedCount >= totalCount) return;
|
|
91
|
+
|
|
92
|
+
const progress = completedCount / totalCount;
|
|
93
|
+
logger.info(`Progress for ${requestId}: ${progress}`);
|
|
94
|
+
|
|
95
|
+
publishRequestProgress({
|
|
96
|
+
requestId,
|
|
97
|
+
progress,
|
|
98
|
+
data: null,
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
for (let i = 0; i < uris.length; i++) {
|
|
103
|
+
const uri = uris[i];
|
|
104
|
+
try {
|
|
105
|
+
const chunk = await downloadFile(uri);
|
|
106
|
+
chunks.push(chunk);
|
|
107
|
+
sendProgress();
|
|
108
|
+
} catch (err) {
|
|
109
|
+
logger.error(`Error downloading chunk: ${err}`);
|
|
110
|
+
throw err;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const jobs = [];
|
|
115
|
+
|
|
116
|
+
for (const chunk of chunks) {
|
|
117
|
+
const cortexRequest = new CortexRequest({ pathwayResolver });
|
|
118
|
+
cortexRequest.url = this.requestUrl();
|
|
119
|
+
|
|
120
|
+
const formData = new FormData();
|
|
121
|
+
formData.append("files", fs.createReadStream(chunk));
|
|
122
|
+
const configObj = {
|
|
123
|
+
file_transcription: {
|
|
124
|
+
mode: "advanced",
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
//phrase/segment level
|
|
129
|
+
if ((responseFormat && !wordTimestamped) || maxLineWidth) {
|
|
130
|
+
configObj.speaker_diarization = {
|
|
131
|
+
// mode: "speakers",
|
|
132
|
+
// num_speakers: numSpeakers,
|
|
133
|
+
// overrides: {
|
|
134
|
+
// clustering: {
|
|
135
|
+
// threshold: clusteringThreshold,
|
|
136
|
+
// },
|
|
137
|
+
// },
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
configObj.subtitles_guidelines = {
|
|
141
|
+
line_count: 1
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (maxLineWidth) {
|
|
146
|
+
configObj.subtitles_guidelines = {
|
|
147
|
+
character_count: maxLineWidth,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (language) {
|
|
152
|
+
configObj.file_transcription.language_id = language;
|
|
153
|
+
}
|
|
154
|
+
formData.append("config", JSON.stringify(configObj));
|
|
155
|
+
|
|
156
|
+
cortexRequest.data = formData;
|
|
157
|
+
cortexRequest.params = {};
|
|
158
|
+
cortexRequest.headers = {
|
|
159
|
+
...cortexRequest.headers,
|
|
160
|
+
...formData.getHeaders(),
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const result = await this.executeRequest(cortexRequest);
|
|
164
|
+
|
|
165
|
+
const jobId = result?.data?.jobId;
|
|
166
|
+
if (!jobId) {
|
|
167
|
+
logger.error(`Error in creating job: ${JSON.stringify(result)}`);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
logger.info(`Job created successfully with ID: ${jobId}`);
|
|
171
|
+
jobs.push(jobId);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return await this.checkJobStatus(
|
|
175
|
+
jobs,
|
|
176
|
+
pathwayResolver,
|
|
177
|
+
sendProgress,
|
|
178
|
+
responseFormat,
|
|
179
|
+
offsets
|
|
180
|
+
);
|
|
181
|
+
} catch (error) {
|
|
182
|
+
logger.error(`Error occurred while executing: ${error}`);
|
|
183
|
+
throw error;
|
|
184
|
+
} finally {
|
|
185
|
+
for (const chunk of chunks) {
|
|
186
|
+
try {
|
|
187
|
+
await deleteTempPath(chunk);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
// Ignore error
|
|
190
|
+
logger.error(`Error deleting temp file: ${error}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async checkJobStatus(
|
|
197
|
+
jobs,
|
|
198
|
+
pathwayResolver,
|
|
199
|
+
sendProgress,
|
|
200
|
+
responseFormat,
|
|
201
|
+
offsets
|
|
202
|
+
) {
|
|
203
|
+
const textResults = [];
|
|
204
|
+
const timestampResults = [];
|
|
205
|
+
for (let i = 0; i < jobs.length; i++) {
|
|
206
|
+
const jobId = jobs[i];
|
|
207
|
+
const result = await this.getJobStatus(jobId, pathwayResolver);
|
|
208
|
+
const text = result.data.result.transcription.channels[0].transcript;
|
|
209
|
+
textResults.push(text);
|
|
210
|
+
timestampResults.push(
|
|
211
|
+
result.data.result.transcription?.segments?.length > 0 ?
|
|
212
|
+
result.data.result.transcription.segments :
|
|
213
|
+
result.data.result.transcription.channels[0].timestamps
|
|
214
|
+
);
|
|
215
|
+
sendProgress();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (responseFormat) {
|
|
219
|
+
const output = timestampResults.map((t) =>
|
|
220
|
+
responseFormat === "srt" ? convertToSrt(t) : convertToVtt(t)
|
|
221
|
+
);
|
|
222
|
+
return alignSubtitles(output, responseFormat, offsets);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return textResults.join(" ").trim();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async getJobStatus(jobId, pathwayResolver) {
|
|
229
|
+
const cortexRequest = new CortexRequest({ pathwayResolver });
|
|
230
|
+
cortexRequest.url = `${this.requestUrl()}/${jobId}`;
|
|
231
|
+
cortexRequest.method = "GET";
|
|
232
|
+
const result = await this.executeRequest(cortexRequest);
|
|
233
|
+
|
|
234
|
+
const status = result?.data?.status;
|
|
235
|
+
if (!status) {
|
|
236
|
+
throw new Error(`Error in getting job status: ${result}`);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (status === "Completed") {
|
|
240
|
+
return result;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (status === "Failed") {
|
|
244
|
+
throw new Error(`Job failed with error: ${result.data.error}`);
|
|
245
|
+
} else {
|
|
246
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
247
|
+
return this.getJobStatus(jobId, pathwayResolver);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export default NeuralSpacePlugin;
|
|
@@ -4,17 +4,20 @@ import OpenAIChatPlugin from './openAiChatPlugin.js';
|
|
|
4
4
|
class OpenAIVisionPlugin extends OpenAIChatPlugin {
|
|
5
5
|
|
|
6
6
|
tryParseMessages(messages) {
|
|
7
|
-
//check if elements of messages strings are JSON, if valid JSON parse them to obj
|
|
8
7
|
messages.map(message => {
|
|
9
8
|
try {
|
|
10
|
-
// message.content can be array or string
|
|
11
9
|
if (typeof message.content === 'string') {
|
|
12
10
|
message.content = JSON.parse(message.content);
|
|
13
|
-
}
|
|
11
|
+
}
|
|
12
|
+
if (Array.isArray(message.content)) {
|
|
14
13
|
message.content = message.content.map(item => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
if (typeof item === 'string') {
|
|
15
|
+
return { type: 'text', text: item };
|
|
16
|
+
} else {
|
|
17
|
+
const parsedItem = JSON.parse(item);
|
|
18
|
+
const { type, text, image_url, url } = parsedItem;
|
|
19
|
+
return { type, text, image_url: url || image_url };
|
|
20
|
+
}
|
|
18
21
|
});
|
|
19
22
|
}
|
|
20
23
|
} catch (e) {
|
|
@@ -29,8 +32,16 @@ class OpenAIVisionPlugin extends OpenAIChatPlugin {
|
|
|
29
32
|
|
|
30
33
|
this.tryParseMessages(requestParameters.messages);
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
const modelMaxReturnTokens = this.getModelMaxReturnTokens();
|
|
36
|
+
const maxTokensPrompt = this.promptParameters.max_tokens;
|
|
37
|
+
const maxTokensModel = this.getModelMaxTokenLength() * (1 - this.getPromptTokenRatio());
|
|
38
|
+
|
|
39
|
+
const maxTokens = maxTokensPrompt || maxTokensModel;
|
|
40
|
+
|
|
41
|
+
requestParameters.max_tokens = maxTokens ? Math.min(maxTokens, modelMaxReturnTokens) : modelMaxReturnTokens;
|
|
42
|
+
|
|
43
|
+
if (this.promptParameters.json) {
|
|
44
|
+
//requestParameters.response_format = { type: "json_object", }
|
|
34
45
|
}
|
|
35
46
|
|
|
36
47
|
return requestParameters;
|
|
@@ -1,24 +1,13 @@
|
|
|
1
1
|
// openAiWhisperPlugin.js
|
|
2
2
|
import ModelPlugin from './modelPlugin.js';
|
|
3
3
|
import { config } from '../../config.js';
|
|
4
|
-
import subsrt from 'subsrt';
|
|
5
4
|
import FormData from 'form-data';
|
|
6
5
|
import fs from 'fs';
|
|
7
|
-
import { axios } from '../../lib/requestExecutor.js';
|
|
8
|
-
import stream from 'stream';
|
|
9
|
-
import os from 'os';
|
|
10
|
-
import path from 'path';
|
|
11
|
-
import http from 'http';
|
|
12
|
-
import https from 'https';
|
|
13
|
-
import { URL } from 'url';
|
|
14
|
-
import { v4 as uuidv4 } from 'uuid';
|
|
15
|
-
import { promisify } from 'util';
|
|
16
6
|
import { publishRequestProgress } from '../../lib/redisSubscription.js';
|
|
17
7
|
import logger from '../../lib/logger.js';
|
|
18
8
|
import CortexRequest from '../../lib/cortexRequest.js';
|
|
19
|
-
|
|
9
|
+
import { downloadFile, deleteTempPath, convertSrtToText, alignSubtitles, getMediaChunks, markCompletedForCleanUp } from '../../lib/util.js';
|
|
20
10
|
|
|
21
|
-
const API_URL = config.get('whisperMediaApiUrl');
|
|
22
11
|
const WHISPER_TS_API_URL = config.get('whisperTSApiUrl');
|
|
23
12
|
if(WHISPER_TS_API_URL){
|
|
24
13
|
logger.info(`WHISPER API URL using ${WHISPER_TS_API_URL}`);
|
|
@@ -28,147 +17,11 @@ if(WHISPER_TS_API_URL){
|
|
|
28
17
|
|
|
29
18
|
const OFFSET_CHUNK = 500; //seconds of each chunk offset, only used if helper does not provide
|
|
30
19
|
|
|
31
|
-
async function deleteTempPath(path) {
|
|
32
|
-
try {
|
|
33
|
-
if (!path) {
|
|
34
|
-
logger.warn('Temporary path is not defined.');
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
if (!fs.existsSync(path)) {
|
|
38
|
-
logger.warn(`Temporary path ${path} does not exist.`);
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
const stats = fs.statSync(path);
|
|
42
|
-
if (stats.isFile()) {
|
|
43
|
-
fs.unlinkSync(path);
|
|
44
|
-
logger.info(`Temporary file ${path} deleted successfully.`);
|
|
45
|
-
} else if (stats.isDirectory()) {
|
|
46
|
-
fs.rmSync(path, { recursive: true });
|
|
47
|
-
logger.info(`Temporary folder ${path} and its contents deleted successfully.`);
|
|
48
|
-
}
|
|
49
|
-
} catch (err) {
|
|
50
|
-
logger.error(`Error occurred while deleting the temporary path: ${err}`);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function generateUniqueFilename(extension) {
|
|
55
|
-
return `${uuidv4()}.${extension}`;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const downloadFile = async (fileUrl) => {
|
|
59
|
-
const fileExtension = path.extname(fileUrl).slice(1);
|
|
60
|
-
const uniqueFilename = generateUniqueFilename(fileExtension);
|
|
61
|
-
const tempDir = os.tmpdir();
|
|
62
|
-
const localFilePath = `${tempDir}/${uniqueFilename}`;
|
|
63
|
-
|
|
64
|
-
// eslint-disable-next-line no-async-promise-executor
|
|
65
|
-
return new Promise(async (resolve, reject) => {
|
|
66
|
-
try {
|
|
67
|
-
const parsedUrl = new URL(fileUrl);
|
|
68
|
-
const protocol = parsedUrl.protocol === 'https:' ? https : http;
|
|
69
|
-
|
|
70
|
-
const response = await new Promise((resolve, reject) => {
|
|
71
|
-
protocol.get(parsedUrl, (res) => {
|
|
72
|
-
if (res.statusCode === 200) {
|
|
73
|
-
resolve(res);
|
|
74
|
-
} else {
|
|
75
|
-
reject(new Error(`HTTP request failed with status code ${res.statusCode}`));
|
|
76
|
-
}
|
|
77
|
-
}).on('error', reject);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
await pipeline(response, fs.createWriteStream(localFilePath));
|
|
81
|
-
logger.info(`Downloaded file to ${localFilePath}`);
|
|
82
|
-
resolve(localFilePath);
|
|
83
|
-
} catch (error) {
|
|
84
|
-
fs.unlink(localFilePath, () => {
|
|
85
|
-
reject(error);
|
|
86
|
-
});
|
|
87
|
-
//throw error;
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
// convert srt format to text
|
|
93
|
-
function convertToText(str) {
|
|
94
|
-
return str
|
|
95
|
-
.split('\n')
|
|
96
|
-
.filter(line => !line.match(/^\d+$/) && !line.match(/^\d{2}:\d{2}:\d{2},\d{3} --> \d{2}:\d{2}:\d{2},\d{3}$/) && line !== '')
|
|
97
|
-
.join(' ');
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function alignSubtitles(subtitles, format, offsets) {
|
|
101
|
-
const result = [];
|
|
102
|
-
|
|
103
|
-
function preprocessStr(str) {
|
|
104
|
-
try{
|
|
105
|
-
if(!str) return '';
|
|
106
|
-
return str.trim().replace(/(\n\n)(?!\n)/g, '\n\n\n');
|
|
107
|
-
}catch(e){
|
|
108
|
-
logger.error(`An error occurred in content text preprocessing: ${e}`);
|
|
109
|
-
return '';
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function shiftSubtitles(subtitle, shiftOffset) {
|
|
114
|
-
const captions = subsrt.parse(preprocessStr(subtitle));
|
|
115
|
-
const resynced = subsrt.resync(captions, { offset: shiftOffset });
|
|
116
|
-
return resynced;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
for (let i = 0; i < subtitles.length; i++) {
|
|
120
|
-
result.push(...shiftSubtitles(subtitles[i], offsets[i]*1000)); // convert to milliseconds
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
try {
|
|
124
|
-
//if content has needed html style tags, keep them
|
|
125
|
-
for(const obj of result) {
|
|
126
|
-
if(obj && obj.content){
|
|
127
|
-
obj.text = obj.content;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
} catch (error) {
|
|
131
|
-
logger.error(`An error occurred in content text parsing: ${error}`);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return subsrt.build(result, { format: format === 'vtt' ? 'vtt' : 'srt' });
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
|
|
138
20
|
class OpenAIWhisperPlugin extends ModelPlugin {
|
|
139
21
|
constructor(pathway, model) {
|
|
140
22
|
super(pathway, model);
|
|
141
23
|
}
|
|
142
24
|
|
|
143
|
-
async getMediaChunks(file, requestId) {
|
|
144
|
-
try {
|
|
145
|
-
if (API_URL) {
|
|
146
|
-
//call helper api and get list of file uris
|
|
147
|
-
const res = await axios.get(API_URL, { params: { uri: file, requestId } });
|
|
148
|
-
return res.data;
|
|
149
|
-
} else {
|
|
150
|
-
logger.info(`No API_URL set, returning file as chunk`);
|
|
151
|
-
return [file];
|
|
152
|
-
}
|
|
153
|
-
} catch (err) {
|
|
154
|
-
logger.error(`Error getting media chunks list from api: ${err}`);
|
|
155
|
-
throw err;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
async markCompletedForCleanUp(requestId) {
|
|
160
|
-
try {
|
|
161
|
-
if (API_URL) {
|
|
162
|
-
//call helper api to mark processing as completed
|
|
163
|
-
const res = await axios.delete(API_URL, { params: { requestId } });
|
|
164
|
-
logger.info(`Marked request ${requestId} as completed:`, res.data);
|
|
165
|
-
return res.data;
|
|
166
|
-
}
|
|
167
|
-
} catch (err) {
|
|
168
|
-
logger.error(`Error marking request ${requestId} as completed: ${err}`);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
25
|
// Execute the request to the OpenAI Whisper API
|
|
173
26
|
async execute(text, parameters, prompt, cortexRequest) {
|
|
174
27
|
const { pathwayResolver } = cortexRequest;
|
|
@@ -252,7 +105,7 @@ class OpenAIWhisperPlugin extends ModelPlugin {
|
|
|
252
105
|
|
|
253
106
|
if(!wordTimestamped && !responseFormat){
|
|
254
107
|
//if no response format, convert to text
|
|
255
|
-
return
|
|
108
|
+
return convertSrtToText(res);
|
|
256
109
|
}
|
|
257
110
|
return res;
|
|
258
111
|
}
|
|
@@ -324,7 +177,7 @@ let offsets = [];
|
|
|
324
177
|
let uris = []
|
|
325
178
|
|
|
326
179
|
try {
|
|
327
|
-
const mediaChunks = await
|
|
180
|
+
const mediaChunks = await getMediaChunks(file, requestId);
|
|
328
181
|
|
|
329
182
|
if (!mediaChunks || !mediaChunks.length) {
|
|
330
183
|
throw new Error(`Error in getting chunks from media helper for file ${file}`);
|
|
@@ -363,14 +216,14 @@ try {
|
|
|
363
216
|
}
|
|
364
217
|
}
|
|
365
218
|
|
|
366
|
-
await
|
|
219
|
+
await markCompletedForCleanUp(requestId);
|
|
367
220
|
|
|
368
221
|
//check cleanup for whisper temp uploaded files url
|
|
369
222
|
const regex = /whispertempfiles\/([a-z0-9-]+)/;
|
|
370
223
|
const match = file.match(regex);
|
|
371
224
|
if (match && match[1]) {
|
|
372
225
|
const extractedValue = match[1];
|
|
373
|
-
await
|
|
226
|
+
await markCompletedForCleanUp(extractedValue);
|
|
374
227
|
logger.info(`Cleaned temp whisper file ${file} with request id ${extractedValue}`);
|
|
375
228
|
}
|
|
376
229
|
|
|
@@ -100,7 +100,7 @@ class PalmChatPlugin extends ModelPlugin {
|
|
|
100
100
|
throw new Error(`Prompt is too long to successfully call the model at ${tokenLength} tokens. The model will not be called.`);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
// Ensure there are an
|
|
103
|
+
// Ensure there are an odd number of messages for turn taking
|
|
104
104
|
if (requestMessages.length % 2 === 0) {
|
|
105
105
|
requestMessages = requestMessages.slice(1);
|
|
106
106
|
}
|
package/server/resolver.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { fulfillWithTimeout } from '../lib/promiser.js';
|
|
2
2
|
import { PathwayResolver } from './pathwayResolver.js';
|
|
3
|
-
import logger from '../lib/logger.js';
|
|
4
3
|
|
|
5
4
|
// This resolver uses standard parameters required by Apollo server:
|
|
6
5
|
// (parent, args, contextValue, info)
|
|
@@ -22,16 +21,16 @@ const rootResolver = async (parent, args, contextValue, info) => {
|
|
|
22
21
|
try {
|
|
23
22
|
result = await fulfillWithTimeout(pathway.resolver(parent, args, contextValue, info), pathway.timeout);
|
|
24
23
|
} catch (error) {
|
|
25
|
-
|
|
24
|
+
pathwayResolver.logError(error);
|
|
26
25
|
result = error.message || error.toString();
|
|
27
26
|
}
|
|
28
27
|
|
|
29
|
-
const { warnings, previousResult, savedContextId, tool } = pathwayResolver;
|
|
28
|
+
const { warnings, errors, previousResult, savedContextId, tool } = pathwayResolver;
|
|
30
29
|
|
|
31
30
|
// Add request parameters back as debug
|
|
32
31
|
const debug = pathwayResolver.prompts.map(prompt => prompt.debugInfo || '').join('\n').trim();
|
|
33
32
|
|
|
34
|
-
return { debug, result, warnings, previousResult, tool, contextId: savedContextId }
|
|
33
|
+
return { debug, result, warnings, errors, previousResult, tool, contextId: savedContextId }
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
// This resolver is used by the root resolver to process the request
|