@aj-archipelago/cortex 1.1.19 → 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/default.example.json +1 -14
- package/config.js +12 -0
- package/helper-apps/cortex-file-handler/blobHandler.js +54 -13
- package/helper-apps/cortex-file-handler/index.js +8 -7
- package/helper-apps/cortex-file-handler/package.json +1 -0
- package/lib/pathwayTools.js +11 -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 +274 -140
- package/server/plugins/gemini15ChatPlugin.js +8 -3
- package/server/plugins/gemini15VisionPlugin.js +23 -6
- 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 +24 -7
- package/server/plugins/openAiWhisperPlugin.js +5 -152
- package/server/plugins/palmChatPlugin.js +1 -1
- package/server/resolver.js +11 -3
- 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
- package/tests/vision.test.js +1 -1
|
@@ -1,172 +1,306 @@
|
|
|
1
|
-
import OpenAIVisionPlugin from
|
|
2
|
-
import logger from
|
|
1
|
+
import OpenAIVisionPlugin from "./openAiVisionPlugin.js";
|
|
2
|
+
import logger from "../../lib/logger.js";
|
|
3
|
+
import mime from 'mime-types';
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
async function convertContentItem(item) {
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
let imageUrl = "";
|
|
8
|
+
let isDataURL = false;
|
|
9
|
+
let urlData = "";
|
|
10
|
+
let mimeTypeMatch = "";
|
|
11
|
+
let mimeType = "";
|
|
12
|
+
let base64Image = "";
|
|
13
|
+
const allowedMIMETypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
switch (typeof item) {
|
|
17
|
+
case "string":
|
|
18
|
+
return item ? { type: "text", text: item } : null;
|
|
19
|
+
|
|
20
|
+
case "object":
|
|
21
|
+
switch (item.type) {
|
|
22
|
+
case "text":
|
|
23
|
+
return item.text ? { type: "text", text: item.text } : null;
|
|
24
|
+
|
|
25
|
+
case "image_url":
|
|
26
|
+
imageUrl = item.image_url.url || item.image_url;
|
|
27
|
+
if (!imageUrl) {
|
|
28
|
+
logger.warn("Could not parse image URL from content - skipping image content.");
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
11
31
|
|
|
12
|
-
|
|
32
|
+
if (!allowedMIMETypes.includes(mime.lookup(imageUrl) || "")) {
|
|
33
|
+
logger.warn("Unsupported image type - skipping image content.");
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
13
36
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
37
|
+
isDataURL = imageUrl.startsWith("data:");
|
|
38
|
+
urlData = isDataURL ? item.image_url.url : await fetchImageAsDataURL(imageUrl);
|
|
39
|
+
mimeTypeMatch = urlData.match(/data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+).*,.*/);
|
|
40
|
+
mimeType = mimeTypeMatch && mimeTypeMatch[1] ? mimeTypeMatch[1] : "image/jpeg";
|
|
41
|
+
base64Image = urlData.split(",")[1];
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
type: "image",
|
|
45
|
+
source: {
|
|
46
|
+
type: "base64",
|
|
47
|
+
media_type: mimeType,
|
|
48
|
+
data: base64Image,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
default:
|
|
53
|
+
return null;
|
|
20
54
|
}
|
|
55
|
+
|
|
56
|
+
default:
|
|
57
|
+
return null;
|
|
21
58
|
}
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
logger.warn(`Error converting content item: ${e}`);
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
22
65
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
let lastAuthor = '';
|
|
28
|
-
|
|
29
|
-
// Claude needs system messages in a separate field
|
|
30
|
-
const systemMessages = messages.filter(message => message.role === 'system');
|
|
31
|
-
if (systemMessages.length > 0) {
|
|
32
|
-
system = systemMessages.map(message => message.content).join('\n');
|
|
33
|
-
modifiedMessages = messages.filter(message => message.role !== 'system');
|
|
34
|
-
} else {
|
|
35
|
-
modifiedMessages = messages;
|
|
36
|
-
}
|
|
66
|
+
// Fetch image and convert to base 64 data URL
|
|
67
|
+
async function fetchImageAsDataURL(imageUrl) {
|
|
68
|
+
try {
|
|
69
|
+
const response = await fetch(imageUrl);
|
|
37
70
|
|
|
38
|
-
|
|
39
|
-
|
|
71
|
+
if (!response.ok) {
|
|
72
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
73
|
+
}
|
|
40
74
|
|
|
41
|
-
|
|
42
|
-
|
|
75
|
+
const buffer = await response.arrayBuffer();
|
|
76
|
+
const base64Image = Buffer.from(buffer).toString("base64");
|
|
77
|
+
const mimeType = mime.lookup(imageUrl) || "image/jpeg";
|
|
78
|
+
return `data:${mimeType};base64,${base64Image}`;
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
logger.error(`Failed to fetch image: ${imageUrl}. ${e}`);
|
|
82
|
+
throw e;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
43
85
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
});
|
|
86
|
+
class Claude3VertexPlugin extends OpenAIVisionPlugin {
|
|
87
|
+
|
|
88
|
+
parseResponse(data) {
|
|
89
|
+
if (!data) {
|
|
90
|
+
return data;
|
|
91
|
+
}
|
|
52
92
|
|
|
53
|
-
|
|
93
|
+
const { content } = data;
|
|
54
94
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
95
|
+
// if the response is an array, return the text property of the first item
|
|
96
|
+
// if the type property is 'text'
|
|
97
|
+
if (content && Array.isArray(content) && content[0].type === "text") {
|
|
98
|
+
return content[0].text;
|
|
99
|
+
} else {
|
|
100
|
+
return data;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// This code converts messages to the format required by the Claude Vertex API
|
|
105
|
+
async convertMessagesToClaudeVertex(messages) {
|
|
106
|
+
let modifiedMessages = [];
|
|
107
|
+
let system = "";
|
|
108
|
+
let lastAuthor = "";
|
|
59
109
|
|
|
110
|
+
// Claude needs system messages in a separate field
|
|
111
|
+
const systemMessages = messages.filter(
|
|
112
|
+
(message) => message.role === "system"
|
|
113
|
+
);
|
|
114
|
+
if (systemMessages.length > 0) {
|
|
115
|
+
system = systemMessages.map((message) => message.content).join("\n");
|
|
116
|
+
modifiedMessages = messages.filter(
|
|
117
|
+
(message) => message.role !== "system"
|
|
118
|
+
);
|
|
119
|
+
} else {
|
|
120
|
+
modifiedMessages = messages;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// remove any empty messages
|
|
124
|
+
modifiedMessages = modifiedMessages.filter((message) => message.content);
|
|
125
|
+
|
|
126
|
+
// combine any consecutive messages from the same author
|
|
127
|
+
var combinedMessages = [];
|
|
128
|
+
|
|
129
|
+
modifiedMessages.forEach((message) => {
|
|
130
|
+
if (message.role === lastAuthor) {
|
|
131
|
+
combinedMessages[combinedMessages.length - 1].content +=
|
|
132
|
+
"\n" + message.content;
|
|
133
|
+
} else {
|
|
134
|
+
combinedMessages.push(message);
|
|
135
|
+
lastAuthor = message.role;
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
modifiedMessages = combinedMessages;
|
|
140
|
+
|
|
141
|
+
// Claude vertex requires an odd number of messages
|
|
142
|
+
// for proper conversation turn-taking
|
|
143
|
+
if (modifiedMessages.length % 2 === 0) {
|
|
144
|
+
modifiedMessages = modifiedMessages.slice(1);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const claude3Messages = await Promise.all(
|
|
148
|
+
modifiedMessages.map(async (message) => {
|
|
149
|
+
const contentArray = Array.isArray(message.content) ? message.content : [message.content];
|
|
150
|
+
const claude3Content = await Promise.all(contentArray.map(convertContentItem));
|
|
60
151
|
return {
|
|
61
|
-
|
|
62
|
-
|
|
152
|
+
role: message.role,
|
|
153
|
+
content: claude3Content.filter(Boolean),
|
|
63
154
|
};
|
|
64
|
-
|
|
155
|
+
})
|
|
156
|
+
);
|
|
65
157
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
158
|
+
return {
|
|
159
|
+
system,
|
|
160
|
+
modifiedMessages: claude3Messages,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async getRequestParameters(text, parameters, prompt, cortexRequest) {
|
|
165
|
+
const requestParameters = await super.getRequestParameters(
|
|
166
|
+
text,
|
|
167
|
+
parameters,
|
|
168
|
+
prompt,
|
|
169
|
+
cortexRequest
|
|
170
|
+
);
|
|
171
|
+
const { system, modifiedMessages } =
|
|
172
|
+
await this.convertMessagesToClaudeVertex(requestParameters.messages);
|
|
173
|
+
requestParameters.system = system;
|
|
174
|
+
requestParameters.messages = modifiedMessages;
|
|
175
|
+
requestParameters.max_tokens = this.getModelMaxReturnTokens();
|
|
176
|
+
requestParameters.anthropic_version = "vertex-2023-10-16";
|
|
177
|
+
return requestParameters;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Override the logging function to display the messages and responses
|
|
181
|
+
logRequestData(data, responseData, prompt) {
|
|
182
|
+
const { stream, messages, system } = data;
|
|
183
|
+
if (system) {
|
|
184
|
+
const { length, units } = this.getLength(system);
|
|
185
|
+
logger.info(`[system messages sent containing ${length} ${units}]`);
|
|
186
|
+
logger.debug(`${system}`);
|
|
74
187
|
}
|
|
75
188
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
totalUnits = units;
|
|
99
|
-
});
|
|
100
|
-
logger.info(`[chat request contained ${totalLength} ${totalUnits}]`);
|
|
101
|
-
} else {
|
|
102
|
-
const message = messages[0];
|
|
103
|
-
const content = Array.isArray(message.content) ? message.content.map(item => JSON.stringify(item)).join(', ') : message.content;
|
|
104
|
-
const { length, units } = this.getLength(content);
|
|
105
|
-
logger.info(`[request sent containing ${length} ${units}]`);
|
|
106
|
-
logger.debug(`${content}`);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (stream) {
|
|
110
|
-
logger.info(`[response received as an SSE stream]`);
|
|
111
|
-
} else {
|
|
112
|
-
const responseText = this.parseResponse(responseData);
|
|
113
|
-
const { length, units } = this.getLength(responseText);
|
|
114
|
-
logger.info(`[response received containing ${length} ${units}]`);
|
|
115
|
-
logger.debug(`${responseText}`);
|
|
116
|
-
}
|
|
189
|
+
if (messages && messages.length > 1) {
|
|
190
|
+
logger.info(`[chat request sent containing ${messages.length} messages]`);
|
|
191
|
+
let totalLength = 0;
|
|
192
|
+
let totalUnits;
|
|
193
|
+
messages.forEach((message, index) => {
|
|
194
|
+
//message.content string or array
|
|
195
|
+
const content = Array.isArray(message.content)
|
|
196
|
+
? message.content.map((item) => {
|
|
197
|
+
if (item.source && item.source.type === 'base64') {
|
|
198
|
+
item.source.data = '* base64 data truncated for log *';
|
|
199
|
+
}
|
|
200
|
+
return JSON.stringify(item);
|
|
201
|
+
}).join(", ")
|
|
202
|
+
: message.content;
|
|
203
|
+
const words = content.split(" ");
|
|
204
|
+
const { length, units } = this.getLength(content);
|
|
205
|
+
const preview =
|
|
206
|
+
words.length < 41
|
|
207
|
+
? content
|
|
208
|
+
: words.slice(0, 20).join(" ") +
|
|
209
|
+
" ... " +
|
|
210
|
+
words.slice(-20).join(" ");
|
|
117
211
|
|
|
118
|
-
|
|
212
|
+
logger.debug(
|
|
213
|
+
`message ${index + 1}: role: ${
|
|
214
|
+
message.role
|
|
215
|
+
}, ${units}: ${length}, content: "${preview}"`
|
|
216
|
+
);
|
|
217
|
+
totalLength += length;
|
|
218
|
+
totalUnits = units;
|
|
219
|
+
});
|
|
220
|
+
logger.info(`[chat request contained ${totalLength} ${totalUnits}]`);
|
|
221
|
+
} else {
|
|
222
|
+
const message = messages[0];
|
|
223
|
+
const content = Array.isArray(message.content)
|
|
224
|
+
? message.content.map((item) => JSON.stringify(item)).join(", ")
|
|
225
|
+
: message.content;
|
|
226
|
+
const { length, units } = this.getLength(content);
|
|
227
|
+
logger.info(`[request sent containing ${length} ${units}]`);
|
|
228
|
+
logger.debug(`${content}`);
|
|
119
229
|
}
|
|
120
230
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
231
|
+
if (stream) {
|
|
232
|
+
logger.info(`[response received as an SSE stream]`);
|
|
233
|
+
} else {
|
|
234
|
+
const responseText = this.parseResponse(responseData);
|
|
235
|
+
const { length, units } = this.getLength(responseText);
|
|
236
|
+
logger.info(`[response received containing ${length} ${units}]`);
|
|
237
|
+
logger.debug(`${responseText}`);
|
|
238
|
+
}
|
|
124
239
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
240
|
+
prompt &&
|
|
241
|
+
prompt.debugInfo &&
|
|
242
|
+
(prompt.debugInfo += `\n${JSON.stringify(data)}`);
|
|
243
|
+
}
|
|
129
244
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
245
|
+
async execute(text, parameters, prompt, cortexRequest) {
|
|
246
|
+
const requestParameters = await this.getRequestParameters(
|
|
247
|
+
text,
|
|
248
|
+
parameters,
|
|
249
|
+
prompt,
|
|
250
|
+
cortexRequest
|
|
251
|
+
);
|
|
252
|
+
const { stream } = parameters;
|
|
133
253
|
|
|
134
|
-
|
|
135
|
-
|
|
254
|
+
cortexRequest.data = {
|
|
255
|
+
...(cortexRequest.data || {}),
|
|
256
|
+
...requestParameters,
|
|
257
|
+
};
|
|
258
|
+
cortexRequest.params = {}; // query params
|
|
259
|
+
cortexRequest.stream = stream;
|
|
260
|
+
cortexRequest.urlSuffix = cortexRequest.stream
|
|
261
|
+
? ":streamRawPredict"
|
|
262
|
+
: ":rawPredict";
|
|
136
263
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
case 'message_start':
|
|
141
|
-
requestProgress.data = JSON.stringify(eventData.message);
|
|
142
|
-
break;
|
|
143
|
-
case 'content_block_start':
|
|
144
|
-
break;
|
|
145
|
-
case 'ping':
|
|
146
|
-
break;
|
|
147
|
-
case 'content_block_delta':
|
|
148
|
-
if (eventData.delta.type === 'text_delta') {
|
|
149
|
-
requestProgress.data = JSON.stringify(eventData.delta.text);
|
|
150
|
-
}
|
|
151
|
-
break;
|
|
152
|
-
case 'content_block_stop':
|
|
153
|
-
break;
|
|
154
|
-
case 'message_delta':
|
|
155
|
-
break;
|
|
156
|
-
case 'message_stop':
|
|
157
|
-
requestProgress.data = '[DONE]';
|
|
158
|
-
requestProgress.progress = 1;
|
|
159
|
-
break;
|
|
160
|
-
case 'error':
|
|
161
|
-
requestProgress.data = `\n\n*** ${eventData.error.message || eventData.error} ***`;
|
|
162
|
-
requestProgress.progress = 1;
|
|
163
|
-
break;
|
|
164
|
-
}
|
|
264
|
+
const gcpAuthTokenHelper = this.config.get("gcpAuthTokenHelper");
|
|
265
|
+
const authToken = await gcpAuthTokenHelper.getAccessToken();
|
|
266
|
+
cortexRequest.headers.Authorization = `Bearer ${authToken}`;
|
|
165
267
|
|
|
166
|
-
|
|
268
|
+
return this.executeRequest(cortexRequest);
|
|
269
|
+
}
|
|
167
270
|
|
|
271
|
+
processStreamEvent(event, requestProgress) {
|
|
272
|
+
const eventData = JSON.parse(event.data);
|
|
273
|
+
switch (eventData.type) {
|
|
274
|
+
case "message_start":
|
|
275
|
+
requestProgress.data = JSON.stringify(eventData.message);
|
|
276
|
+
break;
|
|
277
|
+
case "content_block_start":
|
|
278
|
+
break;
|
|
279
|
+
case "ping":
|
|
280
|
+
break;
|
|
281
|
+
case "content_block_delta":
|
|
282
|
+
if (eventData.delta.type === "text_delta") {
|
|
283
|
+
requestProgress.data = JSON.stringify(eventData.delta.text);
|
|
284
|
+
}
|
|
285
|
+
break;
|
|
286
|
+
case "content_block_stop":
|
|
287
|
+
break;
|
|
288
|
+
case "message_delta":
|
|
289
|
+
break;
|
|
290
|
+
case "message_stop":
|
|
291
|
+
requestProgress.data = "[DONE]";
|
|
292
|
+
requestProgress.progress = 1;
|
|
293
|
+
break;
|
|
294
|
+
case "error":
|
|
295
|
+
requestProgress.data = `\n\n*** ${
|
|
296
|
+
eventData.error.message || eventData.error
|
|
297
|
+
} ***`;
|
|
298
|
+
requestProgress.progress = 1;
|
|
299
|
+
break;
|
|
168
300
|
}
|
|
169
301
|
|
|
302
|
+
return requestProgress;
|
|
303
|
+
}
|
|
170
304
|
}
|
|
171
305
|
|
|
172
306
|
export default Claude3VertexPlugin;
|
|
@@ -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
|
|
|
@@ -25,9 +24,10 @@ class Gemini15VisionPlugin extends Gemini15ChatPlugin {
|
|
|
25
24
|
}
|
|
26
25
|
|
|
27
26
|
// Convert content to Gemini format, trying to maintain compatibility
|
|
28
|
-
const convertPartToGemini = (
|
|
27
|
+
const convertPartToGemini = (inputPart) => {
|
|
29
28
|
try {
|
|
30
|
-
const part = JSON.parse(
|
|
29
|
+
const part = typeof inputPart === 'string' ? JSON.parse(inputPart) : inputPart;
|
|
30
|
+
|
|
31
31
|
if (typeof part === 'string') {
|
|
32
32
|
return { text: part };
|
|
33
33
|
} else if (part.type === 'text') {
|
|
@@ -50,9 +50,10 @@ class Gemini15VisionPlugin extends Gemini15ChatPlugin {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
} catch (e) {
|
|
53
|
-
|
|
53
|
+
// this space intentionally left blank
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
|
|
56
|
+
return { text: inputPart };
|
|
56
57
|
};
|
|
57
58
|
|
|
58
59
|
const addPartToMessages = (geminiPart) => {
|
|
@@ -82,7 +83,7 @@ class Gemini15VisionPlugin extends Gemini15ChatPlugin {
|
|
|
82
83
|
});
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
// Gemini requires an
|
|
86
|
+
// Gemini requires an odd number of messages
|
|
86
87
|
if (modifiedMessages.length % 2 === 0) {
|
|
87
88
|
modifiedMessages = modifiedMessages.slice(1);
|
|
88
89
|
}
|
|
@@ -95,6 +96,22 @@ class Gemini15VisionPlugin extends Gemini15ChatPlugin {
|
|
|
95
96
|
};
|
|
96
97
|
}
|
|
97
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
|
+
|
|
98
115
|
}
|
|
99
116
|
|
|
100
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
|
}
|