@aj-archipelago/cortex 1.1.20 → 1.1.22
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/default.example.json +84 -0
- package/config.js +17 -4
- package/helper-apps/cortex-file-handler/blobHandler.js +144 -100
- package/helper-apps/cortex-file-handler/fileChunker.js +13 -8
- package/helper-apps/cortex-file-handler/index.js +56 -9
- package/lib/pathwayTools.js +7 -1
- package/lib/requestExecutor.js +4 -4
- package/lib/util.js +163 -1
- package/package.json +2 -1
- package/pathways/categorize.js +23 -0
- package/pathways/chat.js +1 -1
- package/pathways/chat_code.js +19 -0
- package/pathways/chat_context.js +19 -0
- package/pathways/chat_jarvis.js +19 -0
- package/pathways/chat_persist.js +23 -0
- package/pathways/code_review.js +17 -0
- package/pathways/cognitive_delete.js +2 -1
- package/pathways/cognitive_insert.js +1 -0
- package/pathways/cognitive_search.js +1 -0
- package/pathways/embeddings.js +1 -1
- package/pathways/expand_story.js +12 -0
- package/pathways/format_paragraph_turbo.js +16 -0
- package/pathways/format_summarization.js +21 -0
- package/pathways/gemini_15_vision.js +20 -0
- package/pathways/gemini_vision.js +20 -0
- package/pathways/grammar.js +30 -0
- package/pathways/hashtags.js +19 -0
- package/pathways/headline.js +43 -0
- package/pathways/headline_custom.js +169 -0
- package/pathways/highlights.js +22 -0
- package/pathways/image.js +2 -1
- package/pathways/index.js +109 -17
- package/pathways/jira_story.js +18 -0
- package/pathways/keywords.js +4 -0
- package/pathways/language.js +17 -6
- package/pathways/locations.js +93 -0
- package/pathways/quotes.js +19 -0
- package/pathways/rag.js +207 -0
- package/pathways/rag_jarvis.js +254 -0
- package/pathways/rag_search_helper.js +21 -0
- package/pathways/readme.js +18 -0
- package/pathways/release_notes.js +16 -0
- package/pathways/remove_content.js +31 -0
- package/pathways/retrieval.js +23 -0
- package/pathways/run_claude35_sonnet.js +21 -0
- package/pathways/run_claude3_haiku.js +20 -0
- package/pathways/run_gpt35turbo.js +20 -0
- package/pathways/run_gpt4.js +20 -0
- package/pathways/run_gpt4_32.js +20 -0
- package/pathways/select_extension.js +6 -0
- package/pathways/select_services.js +10 -0
- package/pathways/spelling.js +3 -0
- package/pathways/story_angles.js +13 -0
- package/pathways/styleguide/styleguide.js +221 -0
- package/pathways/styleguidemulti.js +127 -0
- package/pathways/subhead.js +48 -0
- package/pathways/summarize_turbo.js +98 -0
- package/pathways/summary.js +31 -12
- package/pathways/sys_claude_35_sonnet.js +19 -0
- package/pathways/sys_claude_3_haiku.js +19 -0
- package/pathways/sys_google_chat.js +19 -0
- package/pathways/sys_google_code_chat.js +19 -0
- package/pathways/sys_google_gemini_chat.js +23 -0
- package/pathways/sys_openai_chat.js +2 -2
- package/pathways/sys_openai_chat_16.js +19 -0
- package/pathways/sys_openai_chat_gpt4.js +19 -0
- package/pathways/sys_openai_chat_gpt4_32.js +19 -0
- package/pathways/sys_openai_chat_gpt4_turbo.js +19 -0
- package/pathways/tags.js +25 -0
- package/pathways/taxonomy.js +135 -0
- package/pathways/timeline.js +51 -0
- package/pathways/topics.js +25 -0
- package/pathways/topics_sentiment.js +20 -0
- package/pathways/transcribe.js +2 -4
- package/pathways/transcribe_neuralspace.js +18 -0
- package/pathways/translate.js +10 -12
- package/pathways/translate_azure.js +13 -0
- package/pathways/translate_context.js +21 -0
- package/pathways/translate_gpt4.js +19 -0
- package/pathways/translate_gpt4_turbo.js +19 -0
- package/pathways/translate_turbo.js +19 -0
- package/pathways/vision.js +9 -7
- package/server/modelExecutor.js +4 -0
- package/server/pathwayResolver.js +19 -1
- package/server/plugins/azureCognitivePlugin.js +10 -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 +32 -13
- 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/main.test.js +2 -2
- package/tests/mocks.js +2 -0
- package/tests/openAiChatPlugin.test.js +4 -0
- package/tests/vision.test.js +0 -34
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Prompt } from '../server/prompt.js';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
|
|
5
|
+
prompt: [
|
|
6
|
+
new Prompt({ messages: [
|
|
7
|
+
{"role": "system", "content": "Assistant is a highly skilled multilingual translator for a prestigious news agency. When the user posts any text in any language, assistant will create a translation of that text in {{to}}. Assistant will produce only the translation and no additional notes or commentary."},
|
|
8
|
+
{"role": "user", "content": "{{{text}}}"}
|
|
9
|
+
]}),
|
|
10
|
+
],
|
|
11
|
+
inputParameters: {
|
|
12
|
+
to: `Arabic`,
|
|
13
|
+
tokenRatio: 0.2,
|
|
14
|
+
},
|
|
15
|
+
inputChunkSize: 500,
|
|
16
|
+
model: 'oai-gpt4',
|
|
17
|
+
enableDuplicateRequests: false,
|
|
18
|
+
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Prompt } from '../server/prompt.js';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
|
|
5
|
+
prompt: [
|
|
6
|
+
new Prompt({ messages: [
|
|
7
|
+
{"role": "system", "content": "Assistant is a highly skilled multilingual translator for a prestigious news agency. When the user posts any text in any language, assistant will create a translation of that text in {{to}}. Assistant will produce only the translation and no additional notes or commentary."},
|
|
8
|
+
{"role": "user", "content": "{{{text}}}"}
|
|
9
|
+
]}),
|
|
10
|
+
],
|
|
11
|
+
inputParameters: {
|
|
12
|
+
to: `Arabic`,
|
|
13
|
+
tokenRatio: 0.2,
|
|
14
|
+
},
|
|
15
|
+
inputChunkSize: 500,
|
|
16
|
+
model: 'oai-gpt4-turbo',
|
|
17
|
+
enableDuplicateRequests: false,
|
|
18
|
+
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Prompt } from '../server/prompt.js';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
|
|
5
|
+
prompt: [
|
|
6
|
+
new Prompt({ messages: [
|
|
7
|
+
{"role": "system", "content": "Assistant is a highly skilled multilingual translator for a prestigious news agency. When the user posts any text in any language, assistant will create a translation of that text in {{to}}. Assistant will produce only the translation and no additional notes or commentary."},
|
|
8
|
+
{"role": "user", "content": "{{{text}}}"}
|
|
9
|
+
]}),
|
|
10
|
+
],
|
|
11
|
+
inputParameters: {
|
|
12
|
+
to: `Arabic`,
|
|
13
|
+
tokenRatio: 0.2,
|
|
14
|
+
},
|
|
15
|
+
inputChunkSize: 500,
|
|
16
|
+
model: 'oai-gpturbo',
|
|
17
|
+
enableDuplicateRequests: false,
|
|
18
|
+
|
|
19
|
+
}
|
package/pathways/vision.js
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import { Prompt } from '../server/prompt.js';
|
|
2
2
|
|
|
3
3
|
export default {
|
|
4
|
-
prompt:
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
prompt:
|
|
5
|
+
[
|
|
6
|
+
new Prompt({ messages: [
|
|
7
|
+
{"role": "system", "content": "Instructions:\nYou are Jarvis Vision, an AI entity working for a prestigious international news agency. Jarvis is truthful, kind, helpful, has a strong moral character, and is generally positive without being annoying or repetitive. Your primary expertise is image analysis. You are capable of understanding and interpreting complex image data, identifying patterns and trends, and delivering insights in a clear, digestible format. You know the current date and time - it is {{now}}."},
|
|
8
|
+
"{{chatHistory}}",
|
|
9
|
+
]}),
|
|
10
|
+
],
|
|
9
11
|
inputParameters: {
|
|
10
12
|
chatHistory: [{role: '', content: []}],
|
|
11
13
|
contextId: ``,
|
|
12
14
|
},
|
|
13
15
|
max_tokens: 1024,
|
|
14
|
-
model: 'oai-
|
|
15
|
-
tokenRatio: 0.96,
|
|
16
|
+
model: 'oai-gpt4o',
|
|
16
17
|
useInputChunking: false,
|
|
17
18
|
enableDuplicateRequests: false,
|
|
19
|
+
timeout: 600,
|
|
18
20
|
}
|
package/server/modelExecutor.js
CHANGED
|
@@ -23,6 +23,7 @@ import Gemini15ChatPlugin from './plugins/gemini15ChatPlugin.js';
|
|
|
23
23
|
import Gemini15VisionPlugin from './plugins/gemini15VisionPlugin.js';
|
|
24
24
|
import AzureBingPlugin from './plugins/azureBingPlugin.js';
|
|
25
25
|
import Claude3VertexPlugin from './plugins/claude3VertexPlugin.js';
|
|
26
|
+
import NeuralSpacePlugin from './plugins/neuralSpacePlugin.js';
|
|
26
27
|
|
|
27
28
|
class ModelExecutor {
|
|
28
29
|
constructor(pathway, model) {
|
|
@@ -57,6 +58,9 @@ class ModelExecutor {
|
|
|
57
58
|
case 'OPENAI-WHISPER':
|
|
58
59
|
plugin = new OpenAIWhisperPlugin(pathway, model);
|
|
59
60
|
break;
|
|
61
|
+
case 'NEURALSPACE':
|
|
62
|
+
plugin = new NeuralSpacePlugin(pathway, model);
|
|
63
|
+
break;
|
|
60
64
|
case 'LOCAL-CPP-MODEL':
|
|
61
65
|
plugin = new LocalModelPlugin(pathway, model);
|
|
62
66
|
break;
|
|
@@ -25,8 +25,10 @@ class PathwayResolver {
|
|
|
25
25
|
this.useInputChunking = pathway.useInputChunking;
|
|
26
26
|
this.chunkMaxTokenLength = 0;
|
|
27
27
|
this.warnings = [];
|
|
28
|
+
this.errors = [];
|
|
28
29
|
this.requestId = uuidv4();
|
|
29
30
|
this.responseParser = new PathwayResponseParser(pathway);
|
|
31
|
+
this.tool = null;
|
|
30
32
|
this.modelName = [
|
|
31
33
|
pathway.model,
|
|
32
34
|
args?.model,
|
|
@@ -178,6 +180,15 @@ class PathwayResolver {
|
|
|
178
180
|
}
|
|
179
181
|
}
|
|
180
182
|
|
|
183
|
+
mergeResults(mergeData) {
|
|
184
|
+
if (mergeData) {
|
|
185
|
+
this.previousResult = mergeData.previousResult ? mergeData.previousResult : this.previousResult;
|
|
186
|
+
this.warnings = [...this.warnings, ...(mergeData.warnings || [])];
|
|
187
|
+
this.errors = [...this.errors, ...(mergeData.errors || [])];
|
|
188
|
+
this.tool = mergeData.tool || this.tool;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
181
192
|
async resolve(args) {
|
|
182
193
|
// Either we're dealing with an async request, stream, or regular request
|
|
183
194
|
if (args.async || args.stream) {
|
|
@@ -244,6 +255,12 @@ class PathwayResolver {
|
|
|
244
255
|
logger.warn(warning);
|
|
245
256
|
}
|
|
246
257
|
|
|
258
|
+
// Add an error and log it
|
|
259
|
+
logError(error) {
|
|
260
|
+
this.errors.push(error);
|
|
261
|
+
logger.error(error);
|
|
262
|
+
}
|
|
263
|
+
|
|
247
264
|
// Here we choose how to handle long input - either summarize or chunk
|
|
248
265
|
processInputText(text) {
|
|
249
266
|
let chunkTokenLength = 0;
|
|
@@ -416,7 +433,8 @@ class PathwayResolver {
|
|
|
416
433
|
}
|
|
417
434
|
}
|
|
418
435
|
|
|
419
|
-
if
|
|
436
|
+
// save the result to the context if requested and no errors
|
|
437
|
+
if (prompt.saveResultTo && this.errors.length === 0) {
|
|
420
438
|
this.savedContext[prompt.saveResultTo] = result;
|
|
421
439
|
}
|
|
422
440
|
return result;
|
|
@@ -34,7 +34,7 @@ class AzureCognitivePlugin extends ModelPlugin {
|
|
|
34
34
|
async getRequestParameters(text, parameters, prompt, mode, indexName, savedContextId, cortexRequest) {
|
|
35
35
|
const combinedParameters = { ...this.promptParameters, ...parameters };
|
|
36
36
|
const { modelPromptText } = this.getCompiledPrompt(text, combinedParameters, prompt);
|
|
37
|
-
const { inputVector, calculateInputVector, privateData, filter, docId, title, chunkNo } = combinedParameters;
|
|
37
|
+
const { inputVector, calculateInputVector, privateData, filter, docId, title, chunkNo, chatId } = combinedParameters;
|
|
38
38
|
const data = {};
|
|
39
39
|
|
|
40
40
|
if (mode == 'delete') {
|
|
@@ -46,6 +46,10 @@ class AzureCognitivePlugin extends ModelPlugin {
|
|
|
46
46
|
searchQuery += ` AND docId:'${docId}'`;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
if (chatId) {
|
|
50
|
+
searchQuery += ` AND chatId:'${chatId}'`;
|
|
51
|
+
}
|
|
52
|
+
|
|
49
53
|
cortexRequest.url = searchUrl;
|
|
50
54
|
cortexRequest.data =
|
|
51
55
|
{ search: searchQuery,
|
|
@@ -75,6 +79,7 @@ class AzureCognitivePlugin extends ModelPlugin {
|
|
|
75
79
|
content: text,
|
|
76
80
|
owner: savedContextId,
|
|
77
81
|
docId: docId || uuidv4(),
|
|
82
|
+
chatId: chatId,
|
|
78
83
|
createdAt: new Date().toISOString()
|
|
79
84
|
}
|
|
80
85
|
|
|
@@ -116,6 +121,10 @@ class AzureCognitivePlugin extends ModelPlugin {
|
|
|
116
121
|
if (indexName == 'indexcortex') { //if private, filter by owner via contextId //privateData &&
|
|
117
122
|
data.filter && (data.filter = data.filter + ' and ');
|
|
118
123
|
data.filter = `owner eq '${savedContextId}'`;
|
|
124
|
+
|
|
125
|
+
if(chatId){
|
|
126
|
+
data.filter += ` and chatId eq '${chatId}'`;
|
|
127
|
+
}
|
|
119
128
|
}
|
|
120
129
|
|
|
121
130
|
return { data };
|
|
@@ -138,7 +138,8 @@ class Claude3VertexPlugin extends OpenAIVisionPlugin {
|
|
|
138
138
|
|
|
139
139
|
modifiedMessages = combinedMessages;
|
|
140
140
|
|
|
141
|
-
// Claude vertex requires an
|
|
141
|
+
// Claude vertex requires an odd number of messages
|
|
142
|
+
// for proper conversation turn-taking
|
|
142
143
|
if (modifiedMessages.length % 2 === 0) {
|
|
143
144
|
modifiedMessages = modifiedMessages.slice(1);
|
|
144
145
|
}
|
|
@@ -76,7 +76,7 @@ class Gemini15ChatPlugin extends ModelPlugin {
|
|
|
76
76
|
});
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
// Gemini requires an
|
|
79
|
+
// Gemini requires an odd number of messages
|
|
80
80
|
if (modifiedMessages.length % 2 === 0) {
|
|
81
81
|
modifiedMessages = modifiedMessages.slice(1);
|
|
82
82
|
}
|
|
@@ -135,7 +135,13 @@ class Gemini15ChatPlugin extends ModelPlugin {
|
|
|
135
135
|
if (data && data.contents && Array.isArray(data.contents)) {
|
|
136
136
|
dataToMerge = data.contents;
|
|
137
137
|
} else if (data && data.candidates && Array.isArray(data.candidates)) {
|
|
138
|
-
|
|
138
|
+
const { content, finishReason, safetyRatings } = data.candidates[0];
|
|
139
|
+
if (finishReason === 'STOP') {
|
|
140
|
+
return content?.parts?.[0]?.text ?? '';
|
|
141
|
+
} else {
|
|
142
|
+
const returnString = `Response was not completed. Finish reason: ${finishReason}, Safety ratings: ${JSON.stringify(safetyRatings, null, 2)}`;
|
|
143
|
+
throw new Error(returnString);
|
|
144
|
+
}
|
|
139
145
|
} else if (Array.isArray(data)) {
|
|
140
146
|
dataToMerge = data;
|
|
141
147
|
} else {
|
|
@@ -154,7 +160,6 @@ class Gemini15ChatPlugin extends ModelPlugin {
|
|
|
154
160
|
cortexRequest.data = { ...(cortexRequest.data || {}), ...requestParameters };
|
|
155
161
|
cortexRequest.params = {}; // query params
|
|
156
162
|
cortexRequest.stream = stream;
|
|
157
|
-
cortexRequest.stream = stream;
|
|
158
163
|
cortexRequest.urlSuffix = cortexRequest.stream ? ':streamGenerateContent?alt=sse' : ':generateContent';
|
|
159
164
|
|
|
160
165
|
const gcpAuthTokenHelper = this.config.get('gcpAuthTokenHelper');
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import Gemini15ChatPlugin from './gemini15ChatPlugin.js';
|
|
2
2
|
import mime from 'mime-types';
|
|
3
|
-
import logger from '../../lib/logger.js';
|
|
4
3
|
|
|
5
4
|
class Gemini15VisionPlugin extends Gemini15ChatPlugin {
|
|
6
5
|
|
|
@@ -51,8 +50,9 @@ class Gemini15VisionPlugin extends Gemini15ChatPlugin {
|
|
|
51
50
|
}
|
|
52
51
|
}
|
|
53
52
|
} catch (e) {
|
|
54
|
-
|
|
53
|
+
// this space intentionally left blank
|
|
55
54
|
}
|
|
55
|
+
|
|
56
56
|
return { text: inputPart };
|
|
57
57
|
};
|
|
58
58
|
|
|
@@ -83,7 +83,7 @@ class Gemini15VisionPlugin extends Gemini15ChatPlugin {
|
|
|
83
83
|
});
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
// Gemini requires an
|
|
86
|
+
// Gemini requires an odd number of messages
|
|
87
87
|
if (modifiedMessages.length % 2 === 0) {
|
|
88
88
|
modifiedMessages = modifiedMessages.slice(1);
|
|
89
89
|
}
|
|
@@ -96,6 +96,22 @@ class Gemini15VisionPlugin extends Gemini15ChatPlugin {
|
|
|
96
96
|
};
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
async execute(text, parameters, prompt, cortexRequest) {
|
|
100
|
+
let result = null;
|
|
101
|
+
try {
|
|
102
|
+
result = await super.execute(text, parameters, prompt, cortexRequest);
|
|
103
|
+
} catch (e) {
|
|
104
|
+
const { data } = e;
|
|
105
|
+
if (data && data.error) {
|
|
106
|
+
if (data.error.code === 400 && data.error.message === 'Precondition check failed.') {
|
|
107
|
+
throw new Error('One or more of the included files is too large to process. Please try again with a smaller file.');
|
|
108
|
+
}
|
|
109
|
+
throw e;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
|
|
99
115
|
}
|
|
100
116
|
|
|
101
117
|
export default Gemini15VisionPlugin;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import GeminiChatPlugin from './geminiChatPlugin.js';
|
|
2
2
|
import mime from 'mime-types';
|
|
3
|
-
import logger from '../../lib/logger.js';
|
|
4
3
|
|
|
5
4
|
class GeminiVisionPlugin extends GeminiChatPlugin {
|
|
6
5
|
|
|
@@ -55,7 +54,7 @@ class GeminiVisionPlugin extends GeminiChatPlugin {
|
|
|
55
54
|
}
|
|
56
55
|
}
|
|
57
56
|
} catch (e) {
|
|
58
|
-
|
|
57
|
+
// this space intentionally left blank
|
|
59
58
|
}
|
|
60
59
|
return { text: partString };
|
|
61
60
|
};
|
|
@@ -87,7 +86,7 @@ class GeminiVisionPlugin extends GeminiChatPlugin {
|
|
|
87
86
|
});
|
|
88
87
|
}
|
|
89
88
|
|
|
90
|
-
// Gemini requires an
|
|
89
|
+
// Gemini requires an odd number of messages
|
|
91
90
|
if (modifiedMessages.length % 2 === 0) {
|
|
92
91
|
modifiedMessages = modifiedMessages.slice(1);
|
|
93
92
|
}
|
|
@@ -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;
|
|
@@ -1,27 +1,38 @@
|
|
|
1
1
|
import OpenAIChatPlugin from './openAiChatPlugin.js';
|
|
2
2
|
|
|
3
|
+
function safeJsonParse(content) {
|
|
4
|
+
try {
|
|
5
|
+
const parsedContent = JSON.parse(content);
|
|
6
|
+
return (typeof parsedContent === 'object' && parsedContent !== null) ? parsedContent : content;
|
|
7
|
+
} catch (e) {
|
|
8
|
+
return content;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
3
11
|
|
|
4
12
|
class OpenAIVisionPlugin extends OpenAIChatPlugin {
|
|
5
|
-
|
|
13
|
+
|
|
6
14
|
tryParseMessages(messages) {
|
|
7
|
-
|
|
8
|
-
messages.map(message => {
|
|
15
|
+
return messages.map(message => {
|
|
9
16
|
try {
|
|
10
|
-
// message.content can be array or string
|
|
11
17
|
if (typeof message.content === 'string') {
|
|
12
|
-
message.content =
|
|
13
|
-
}
|
|
18
|
+
message.content = safeJsonParse(message.content);
|
|
19
|
+
}
|
|
20
|
+
if (Array.isArray(message.content)) {
|
|
14
21
|
message.content = message.content.map(item => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
22
|
+
if (typeof item === 'string') {
|
|
23
|
+
return { type: 'text', text: item };
|
|
24
|
+
} else {
|
|
25
|
+
const parsedItem = safeJsonParse(item);
|
|
26
|
+
const { type, text, image_url, url } = parsedItem;
|
|
27
|
+
return { type, text, image_url: url || image_url };
|
|
28
|
+
}
|
|
18
29
|
});
|
|
19
|
-
}
|
|
30
|
+
}
|
|
20
31
|
} catch (e) {
|
|
21
32
|
return message;
|
|
22
33
|
}
|
|
34
|
+
return message;
|
|
23
35
|
});
|
|
24
|
-
return messages;
|
|
25
36
|
}
|
|
26
37
|
|
|
27
38
|
getRequestParameters(text, parameters, prompt) {
|
|
@@ -29,8 +40,16 @@ class OpenAIVisionPlugin extends OpenAIChatPlugin {
|
|
|
29
40
|
|
|
30
41
|
this.tryParseMessages(requestParameters.messages);
|
|
31
42
|
|
|
32
|
-
|
|
33
|
-
|
|
43
|
+
const modelMaxReturnTokens = this.getModelMaxReturnTokens();
|
|
44
|
+
const maxTokensPrompt = this.promptParameters.max_tokens;
|
|
45
|
+
const maxTokensModel = this.getModelMaxTokenLength() * (1 - this.getPromptTokenRatio());
|
|
46
|
+
|
|
47
|
+
const maxTokens = maxTokensPrompt || maxTokensModel;
|
|
48
|
+
|
|
49
|
+
requestParameters.max_tokens = maxTokens ? Math.min(maxTokens, modelMaxReturnTokens) : modelMaxReturnTokens;
|
|
50
|
+
|
|
51
|
+
if (this.promptParameters.json) {
|
|
52
|
+
//requestParameters.response_format = { type: "json_object", }
|
|
34
53
|
}
|
|
35
54
|
|
|
36
55
|
return requestParameters;
|