@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.
Files changed (101) hide show
  1. package/config/default.example.json +84 -0
  2. package/config.js +17 -4
  3. package/helper-apps/cortex-file-handler/blobHandler.js +144 -100
  4. package/helper-apps/cortex-file-handler/fileChunker.js +13 -8
  5. package/helper-apps/cortex-file-handler/index.js +56 -9
  6. package/lib/pathwayTools.js +7 -1
  7. package/lib/requestExecutor.js +4 -4
  8. package/lib/util.js +163 -1
  9. package/package.json +2 -1
  10. package/pathways/categorize.js +23 -0
  11. package/pathways/chat.js +1 -1
  12. package/pathways/chat_code.js +19 -0
  13. package/pathways/chat_context.js +19 -0
  14. package/pathways/chat_jarvis.js +19 -0
  15. package/pathways/chat_persist.js +23 -0
  16. package/pathways/code_review.js +17 -0
  17. package/pathways/cognitive_delete.js +2 -1
  18. package/pathways/cognitive_insert.js +1 -0
  19. package/pathways/cognitive_search.js +1 -0
  20. package/pathways/embeddings.js +1 -1
  21. package/pathways/expand_story.js +12 -0
  22. package/pathways/format_paragraph_turbo.js +16 -0
  23. package/pathways/format_summarization.js +21 -0
  24. package/pathways/gemini_15_vision.js +20 -0
  25. package/pathways/gemini_vision.js +20 -0
  26. package/pathways/grammar.js +30 -0
  27. package/pathways/hashtags.js +19 -0
  28. package/pathways/headline.js +43 -0
  29. package/pathways/headline_custom.js +169 -0
  30. package/pathways/highlights.js +22 -0
  31. package/pathways/image.js +2 -1
  32. package/pathways/index.js +109 -17
  33. package/pathways/jira_story.js +18 -0
  34. package/pathways/keywords.js +4 -0
  35. package/pathways/language.js +17 -6
  36. package/pathways/locations.js +93 -0
  37. package/pathways/quotes.js +19 -0
  38. package/pathways/rag.js +207 -0
  39. package/pathways/rag_jarvis.js +254 -0
  40. package/pathways/rag_search_helper.js +21 -0
  41. package/pathways/readme.js +18 -0
  42. package/pathways/release_notes.js +16 -0
  43. package/pathways/remove_content.js +31 -0
  44. package/pathways/retrieval.js +23 -0
  45. package/pathways/run_claude35_sonnet.js +21 -0
  46. package/pathways/run_claude3_haiku.js +20 -0
  47. package/pathways/run_gpt35turbo.js +20 -0
  48. package/pathways/run_gpt4.js +20 -0
  49. package/pathways/run_gpt4_32.js +20 -0
  50. package/pathways/select_extension.js +6 -0
  51. package/pathways/select_services.js +10 -0
  52. package/pathways/spelling.js +3 -0
  53. package/pathways/story_angles.js +13 -0
  54. package/pathways/styleguide/styleguide.js +221 -0
  55. package/pathways/styleguidemulti.js +127 -0
  56. package/pathways/subhead.js +48 -0
  57. package/pathways/summarize_turbo.js +98 -0
  58. package/pathways/summary.js +31 -12
  59. package/pathways/sys_claude_35_sonnet.js +19 -0
  60. package/pathways/sys_claude_3_haiku.js +19 -0
  61. package/pathways/sys_google_chat.js +19 -0
  62. package/pathways/sys_google_code_chat.js +19 -0
  63. package/pathways/sys_google_gemini_chat.js +23 -0
  64. package/pathways/sys_openai_chat.js +2 -2
  65. package/pathways/sys_openai_chat_16.js +19 -0
  66. package/pathways/sys_openai_chat_gpt4.js +19 -0
  67. package/pathways/sys_openai_chat_gpt4_32.js +19 -0
  68. package/pathways/sys_openai_chat_gpt4_turbo.js +19 -0
  69. package/pathways/tags.js +25 -0
  70. package/pathways/taxonomy.js +135 -0
  71. package/pathways/timeline.js +51 -0
  72. package/pathways/topics.js +25 -0
  73. package/pathways/topics_sentiment.js +20 -0
  74. package/pathways/transcribe.js +2 -4
  75. package/pathways/transcribe_neuralspace.js +18 -0
  76. package/pathways/translate.js +10 -12
  77. package/pathways/translate_azure.js +13 -0
  78. package/pathways/translate_context.js +21 -0
  79. package/pathways/translate_gpt4.js +19 -0
  80. package/pathways/translate_gpt4_turbo.js +19 -0
  81. package/pathways/translate_turbo.js +19 -0
  82. package/pathways/vision.js +9 -7
  83. package/server/modelExecutor.js +4 -0
  84. package/server/pathwayResolver.js +19 -1
  85. package/server/plugins/azureCognitivePlugin.js +10 -1
  86. package/server/plugins/claude3VertexPlugin.js +2 -1
  87. package/server/plugins/gemini15ChatPlugin.js +8 -3
  88. package/server/plugins/gemini15VisionPlugin.js +19 -3
  89. package/server/plugins/geminiChatPlugin.js +1 -1
  90. package/server/plugins/geminiVisionPlugin.js +2 -3
  91. package/server/plugins/neuralSpacePlugin.js +252 -0
  92. package/server/plugins/openAiVisionPlugin.js +32 -13
  93. package/server/plugins/openAiWhisperPlugin.js +5 -152
  94. package/server/plugins/palmChatPlugin.js +1 -1
  95. package/server/resolver.js +3 -4
  96. package/server/typeDef.js +1 -0
  97. package/tests/claude3VertexPlugin.test.js +214 -0
  98. package/tests/main.test.js +2 -2
  99. package/tests/mocks.js +2 -0
  100. package/tests/openAiChatPlugin.test.js +4 -0
  101. 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
+ }
@@ -1,18 +1,20 @@
1
1
  import { Prompt } from '../server/prompt.js';
2
2
 
3
3
  export default {
4
- prompt: [
5
- new Prompt({ messages: [
6
- "{{chatHistory}}",
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-gpt4-vision',
15
- tokenRatio: 0.96,
16
+ model: 'oai-gpt4o',
16
17
  useInputChunking: false,
17
18
  enableDuplicateRequests: false,
19
+ timeout: 600,
18
20
  }
@@ -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 (prompt.saveResultTo) {
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 even number of messages
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 even number of messages
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
- return data.candidates[0].content.parts[0].text;
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
- logger.warn(`Unable to parse part - including as string: ${inputPart}`);
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 even number of messages
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;
@@ -81,7 +81,7 @@ class GeminiChatPlugin extends ModelPlugin {
81
81
  });
82
82
  }
83
83
 
84
- // Gemini requires an even number of messages
84
+ // Gemini requires an odd number of messages
85
85
  if (modifiedMessages.length % 2 === 0) {
86
86
  modifiedMessages = modifiedMessages.slice(1);
87
87
  }
@@ -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
- logger.warn(`Unable to parse part - including as string: ${partString}`);
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 even number of messages
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
- //check if elements of messages strings are JSON, if valid JSON parse them to obj
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 = JSON.parse(message.content);
13
- } else if (Array.isArray(message.content)) {
18
+ message.content = safeJsonParse(message.content);
19
+ }
20
+ if (Array.isArray(message.content)) {
14
21
  message.content = message.content.map(item => {
15
- const parsedItem = JSON.parse(item);
16
- const { type, text, image_url, url } = parsedItem;
17
- return { type, text, image_url: url || image_url};
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
- if(this.promptParameters.max_tokens) {
33
- requestParameters.max_tokens = this.promptParameters.max_tokens;
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;