@aj-archipelago/cortex 1.3.67 → 1.4.0
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 +27 -0
- package/helper-apps/cortex-doc-to-pdf/DocToPdfFunction/__init__.py +3 -0
- package/helper-apps/cortex-doc-to-pdf/DocToPdfFunction/function.json +20 -0
- package/helper-apps/cortex-doc-to-pdf/Dockerfile +46 -0
- package/helper-apps/cortex-doc-to-pdf/README.md +408 -0
- package/helper-apps/cortex-doc-to-pdf/converter.py +157 -0
- package/helper-apps/cortex-doc-to-pdf/docker-compose.yml +23 -0
- package/helper-apps/cortex-doc-to-pdf/document_converter.py +181 -0
- package/helper-apps/cortex-doc-to-pdf/examples/README.md +252 -0
- package/helper-apps/cortex-doc-to-pdf/examples/nodejs-client.js +266 -0
- package/helper-apps/cortex-doc-to-pdf/examples/package-lock.json +297 -0
- package/helper-apps/cortex-doc-to-pdf/examples/package.json +23 -0
- package/helper-apps/cortex-doc-to-pdf/function_app.py +85 -0
- package/helper-apps/cortex-doc-to-pdf/host.json +16 -0
- package/helper-apps/cortex-doc-to-pdf/request_handlers.py +193 -0
- package/helper-apps/cortex-doc-to-pdf/requirements.txt +3 -0
- package/helper-apps/cortex-doc-to-pdf/tests/run_tests.sh +26 -0
- package/helper-apps/cortex-doc-to-pdf/tests/test_conversion.py +320 -0
- package/helper-apps/cortex-doc-to-pdf/tests/test_streaming.py +419 -0
- package/helper-apps/cortex-file-handler/package-lock.json +1 -0
- package/helper-apps/cortex-file-handler/package.json +1 -0
- package/helper-apps/cortex-file-handler/src/services/ConversionService.js +81 -8
- package/helper-apps/cortex-file-handler/tests/FileConversionService.test.js +54 -7
- package/helper-apps/cortex-file-handler/tests/getOperations.test.js +19 -7
- package/lib/encodeCache.js +5 -0
- package/lib/keyValueStorageClient.js +5 -0
- package/lib/logger.js +1 -1
- package/lib/pathwayTools.js +8 -1
- package/lib/redisSubscription.js +6 -0
- package/lib/requestExecutor.js +4 -0
- package/lib/util.js +88 -0
- package/package.json +1 -1
- package/pathways/basePathway.js +3 -3
- package/pathways/bing_afagent.js +1 -0
- package/pathways/gemini_15_vision.js +1 -1
- package/pathways/google_cse.js +2 -2
- package/pathways/image_gemini_25.js +85 -0
- package/pathways/image_prompt_optimizer_gemini_25.js +149 -0
- package/pathways/image_qwen.js +28 -0
- package/pathways/image_seedream4.js +26 -0
- package/pathways/rag.js +1 -1
- package/pathways/rag_jarvis.js +1 -1
- package/pathways/system/entity/sys_entity_continue.js +1 -1
- package/pathways/system/entity/sys_generator_results.js +1 -1
- package/pathways/system/entity/tools/sys_tool_google_search.js +15 -2
- package/pathways/system/entity/tools/sys_tool_grok_x_search.js +3 -3
- package/pathways/system/entity/tools/sys_tool_image.js +28 -23
- package/pathways/system/entity/tools/sys_tool_image_gemini.js +135 -0
- package/server/graphql.js +9 -2
- package/server/modelExecutor.js +4 -0
- package/server/pathwayResolver.js +19 -18
- package/server/plugins/claude3VertexPlugin.js +13 -8
- package/server/plugins/gemini15ChatPlugin.js +15 -10
- package/server/plugins/gemini15VisionPlugin.js +2 -23
- package/server/plugins/gemini25ImagePlugin.js +155 -0
- package/server/plugins/modelPlugin.js +3 -2
- package/server/plugins/openAiChatPlugin.js +6 -6
- package/server/plugins/replicateApiPlugin.js +268 -12
- package/server/plugins/veoVideoPlugin.js +15 -1
- package/server/rest.js +2 -0
- package/server/typeDef.js +96 -10
- package/tests/integration/apptekTranslatePlugin.integration.test.js +1 -1
- package/tests/unit/core/pathwayManager.test.js +2 -4
- package/tests/unit/plugins/gemini25ImagePlugin.test.js +294 -0
package/server/typeDef.js
CHANGED
|
@@ -1,30 +1,113 @@
|
|
|
1
|
+
// Check if a value is a JSON Schema object for parameter typing
|
|
2
|
+
const isJsonSchemaObject = (value) => {
|
|
3
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) return false;
|
|
4
|
+
// Basic JSON Schema indicators
|
|
5
|
+
return (
|
|
6
|
+
typeof value.type === 'string' ||
|
|
7
|
+
value.$ref !== undefined ||
|
|
8
|
+
value.oneOf !== undefined ||
|
|
9
|
+
value.anyOf !== undefined ||
|
|
10
|
+
value.allOf !== undefined ||
|
|
11
|
+
value.enum !== undefined ||
|
|
12
|
+
value.properties !== undefined ||
|
|
13
|
+
value.items !== undefined
|
|
14
|
+
);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Extract the default value from a JSON Schema object or return the value as-is
|
|
18
|
+
const extractValueFromTypeSpec = (value) => {
|
|
19
|
+
if (isJsonSchemaObject(value)) {
|
|
20
|
+
return value.hasOwnProperty('default') ? value.default : undefined;
|
|
21
|
+
}
|
|
22
|
+
return value;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Process parameters to convert any type specification objects to their actual values
|
|
26
|
+
const processPathwayParameters = (params) => {
|
|
27
|
+
if (!params || typeof params !== 'object') {
|
|
28
|
+
return params;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const processed = {};
|
|
32
|
+
for (const [key, value] of Object.entries(params)) {
|
|
33
|
+
processed[key] = extractValueFromTypeSpec(value);
|
|
34
|
+
}
|
|
35
|
+
return processed;
|
|
36
|
+
};
|
|
37
|
+
|
|
1
38
|
const getGraphQlType = (value) => {
|
|
39
|
+
// The value might be an object with JSON Schema type specification
|
|
40
|
+
if (isJsonSchemaObject(value)) {
|
|
41
|
+
const schema = value;
|
|
42
|
+
// Map JSON Schema to GraphQL
|
|
43
|
+
if (schema.type === 'boolean') {
|
|
44
|
+
return { type: 'Boolean', defaultValue: schema.default === undefined ? undefined : schema.default };
|
|
45
|
+
}
|
|
46
|
+
if (schema.type === 'string') {
|
|
47
|
+
return { type: 'String', defaultValue: schema.default === undefined ? undefined : `"${schema.default}"` };
|
|
48
|
+
}
|
|
49
|
+
if (schema.type === 'integer') {
|
|
50
|
+
return { type: 'Int', defaultValue: schema.default };
|
|
51
|
+
}
|
|
52
|
+
if (schema.type === 'number') {
|
|
53
|
+
const def = schema.default;
|
|
54
|
+
return { type: 'Float', defaultValue: def };
|
|
55
|
+
}
|
|
56
|
+
if (schema.type === 'array') {
|
|
57
|
+
// Support arrays of primitive types; fall back to JSON string for complex types
|
|
58
|
+
const items = schema.items || {};
|
|
59
|
+
const def = schema.default;
|
|
60
|
+
const defaultArray = Array.isArray(def) ? JSON.stringify(def) : '[]';
|
|
61
|
+
if (items.type === 'string') {
|
|
62
|
+
return { type: '[String]', defaultValue: defaultArray };
|
|
63
|
+
}
|
|
64
|
+
if (items.type === 'integer') {
|
|
65
|
+
return { type: '[Int]', defaultValue: defaultArray };
|
|
66
|
+
}
|
|
67
|
+
if (items.type === 'number') {
|
|
68
|
+
return { type: '[Float]', defaultValue: defaultArray };
|
|
69
|
+
}
|
|
70
|
+
if (items.type === 'boolean') {
|
|
71
|
+
return { type: '[Boolean]', defaultValue: defaultArray };
|
|
72
|
+
}
|
|
73
|
+
// Unknown item type: pass as serialized JSON string argument
|
|
74
|
+
return { type: 'String', defaultValue: def === undefined ? '"[]"' : `"${JSON.stringify(def).replace(/"/g, '\\"')}"` };
|
|
75
|
+
}
|
|
76
|
+
if (schema.type === 'object' || schema.properties) {
|
|
77
|
+
// Until explicit input types are defined, accept as stringified JSON
|
|
78
|
+
const def = schema.default;
|
|
79
|
+
return { type: 'String', defaultValue: def === undefined ? '"{}"' : `"${JSON.stringify(def).replace(/"/g, '\\"')}"` };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Otherwise, autodetect the type
|
|
2
84
|
switch (typeof value) {
|
|
3
85
|
case 'boolean':
|
|
4
|
-
return {type: 'Boolean'};
|
|
86
|
+
return {type: 'Boolean', defaultValue: value};
|
|
5
87
|
case 'string':
|
|
6
|
-
return {type: 'String'};
|
|
88
|
+
return {type: 'String', defaultValue: `"${value}"`};
|
|
7
89
|
case 'number':
|
|
8
|
-
|
|
90
|
+
// Check if it's an integer or float
|
|
91
|
+
return Number.isInteger(value) ? {type: 'Int', defaultValue: value} : {type: 'Float', defaultValue: value};
|
|
9
92
|
case 'object':
|
|
10
93
|
if (Array.isArray(value)) {
|
|
11
94
|
if (value.length > 0 && typeof(value[0]) === 'string') {
|
|
12
|
-
return {type: '[String]'};
|
|
95
|
+
return {type: '[String]', defaultValue: JSON.stringify(value)};
|
|
13
96
|
}
|
|
14
97
|
else {
|
|
15
|
-
//
|
|
98
|
+
// Check if it's MultiMessage (content is array) or Message (content is string)
|
|
16
99
|
if (Array.isArray(value[0]?.content)) {
|
|
17
|
-
return {type: '[MultiMessage]'};
|
|
100
|
+
return {type: '[MultiMessage]', defaultValue: `"${JSON.stringify(value).replace(/"/g, '\\"')}"`};
|
|
18
101
|
}
|
|
19
102
|
else {
|
|
20
|
-
return {type: '[Message]'};
|
|
103
|
+
return {type: '[Message]', defaultValue: `"${JSON.stringify(value).replace(/"/g, '\\"')}"`};
|
|
21
104
|
}
|
|
22
105
|
}
|
|
23
106
|
} else {
|
|
24
|
-
return {type: `[${value.objName}]
|
|
107
|
+
return {type: `[${value.objName}]`, defaultValue: JSON.stringify(value)};
|
|
25
108
|
}
|
|
26
109
|
default:
|
|
27
|
-
return {type: 'String'};
|
|
110
|
+
return {type: 'String', defaultValue: `"${value}"`};
|
|
28
111
|
}
|
|
29
112
|
};
|
|
30
113
|
|
|
@@ -80,7 +163,7 @@ const getPathwayTypeDefAndExtendQuery = (pathway) => {
|
|
|
80
163
|
};
|
|
81
164
|
});
|
|
82
165
|
|
|
83
|
-
const gqlDefinition = `${type}\n\n${responseType}\n\nextend type Query {${name}(${paramsStr}): ${objName}}`;
|
|
166
|
+
const gqlDefinition = `${type}\n\n${responseType}\n\nextend type Query {${name}${paramsStr ? `(${paramsStr})` : ''}: ${objName}}`;
|
|
84
167
|
|
|
85
168
|
return {
|
|
86
169
|
gqlDefinition,
|
|
@@ -100,4 +183,7 @@ export {
|
|
|
100
183
|
getMessageTypeDefs,
|
|
101
184
|
getPathwayTypeDef,
|
|
102
185
|
userPathwayInputParameters,
|
|
186
|
+
isJsonSchemaObject,
|
|
187
|
+
extractValueFromTypeSpec,
|
|
188
|
+
processPathwayParameters,
|
|
103
189
|
};
|
|
@@ -155,7 +155,7 @@ test.serial('AppTek Plugin: Force failure and test GPT-4 Omni fallback', async (
|
|
|
155
155
|
});
|
|
156
156
|
|
|
157
157
|
// Test AppTek failure with default fallback (translate_groq)
|
|
158
|
-
test
|
|
158
|
+
test('AppTek Plugin: Force failure and test default fallback', async (t) => {
|
|
159
159
|
// Set a longer timeout for this test since Groq might be slower
|
|
160
160
|
t.timeout(180000); // 3 minutes
|
|
161
161
|
|
|
@@ -193,8 +193,7 @@ test('_createPromptObject handles empty system prompt', t => {
|
|
|
193
193
|
|
|
194
194
|
t.true(result instanceof Prompt);
|
|
195
195
|
t.is(result.name, 'test_prompt');
|
|
196
|
-
t.is(result.messages[0].content, '');
|
|
197
|
-
t.is(result.messages[1].content, '{{text}}\n\nTest prompt text');
|
|
196
|
+
t.is(result.messages[0].content, '{{text}}\n\nTest prompt text');
|
|
198
197
|
});
|
|
199
198
|
|
|
200
199
|
test('_createPromptObject handles null system prompt', t => {
|
|
@@ -206,8 +205,7 @@ test('_createPromptObject handles null system prompt', t => {
|
|
|
206
205
|
|
|
207
206
|
t.true(result instanceof Prompt);
|
|
208
207
|
t.is(result.name, 'test_prompt');
|
|
209
|
-
t.is(result.messages[0].content, '');
|
|
210
|
-
t.is(result.messages[1].content, '{{text}}\n\nTest prompt text');
|
|
208
|
+
t.is(result.messages[0].content, '{{text}}\n\nTest prompt text');
|
|
211
209
|
});
|
|
212
210
|
|
|
213
211
|
test('putPathway requires userId and secret', async t => {
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import Gemini25ImagePlugin from '../../../server/plugins/gemini25ImagePlugin.js';
|
|
3
|
+
import { PathwayResolver } from '../../../server/pathwayResolver.js';
|
|
4
|
+
import { config } from '../../../config.js';
|
|
5
|
+
import { requestState } from '../../../server/requestState.js';
|
|
6
|
+
|
|
7
|
+
// Mock logger to prevent issues in tests
|
|
8
|
+
const mockLogger = {
|
|
9
|
+
debug: () => {},
|
|
10
|
+
info: () => {},
|
|
11
|
+
warn: () => {},
|
|
12
|
+
error: () => {}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// Mock the logger module globally
|
|
16
|
+
global.logger = mockLogger;
|
|
17
|
+
|
|
18
|
+
function createResolverWithPlugin(pluginClass, modelName = 'test-model') {
|
|
19
|
+
const pathway = {
|
|
20
|
+
name: 'test-pathway',
|
|
21
|
+
model: modelName,
|
|
22
|
+
prompt: 'test prompt',
|
|
23
|
+
toolCallback: () => {} // Mock tool callback
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const model = {
|
|
27
|
+
name: modelName,
|
|
28
|
+
type: 'GEMINI-2.5-IMAGE'
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const resolver = new PathwayResolver({
|
|
32
|
+
config,
|
|
33
|
+
pathway,
|
|
34
|
+
args: {},
|
|
35
|
+
endpoints: { [modelName]: model }
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
resolver.modelExecutor.plugin = new pluginClass(pathway, model);
|
|
39
|
+
return resolver;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
test('Gemini25ImagePlugin - filters undefined tool calls from buffer', async t => {
|
|
43
|
+
const resolver = createResolverWithPlugin(Gemini25ImagePlugin);
|
|
44
|
+
const plugin = resolver.modelExecutor.plugin;
|
|
45
|
+
|
|
46
|
+
// Simulate buffer with undefined elements
|
|
47
|
+
plugin.toolCallsBuffer = [
|
|
48
|
+
undefined,
|
|
49
|
+
{
|
|
50
|
+
id: 'call_1_1234567890',
|
|
51
|
+
type: 'function',
|
|
52
|
+
function: {
|
|
53
|
+
name: 'test_tool',
|
|
54
|
+
arguments: '{"param": "value"}'
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
// Mock the tool callback
|
|
60
|
+
let capturedToolCalls = null;
|
|
61
|
+
plugin.pathwayToolCallback = (args, message, resolver) => {
|
|
62
|
+
capturedToolCalls = message.tool_calls;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Mock requestProgress and pathwayResolver
|
|
66
|
+
const requestProgress = { progress: 0, started: true };
|
|
67
|
+
const pathwayResolver = { args: {} };
|
|
68
|
+
|
|
69
|
+
// Mock requestState
|
|
70
|
+
requestState[plugin.requestId] = { pathwayResolver };
|
|
71
|
+
|
|
72
|
+
// Simulate a tool_calls finish reason
|
|
73
|
+
const eventData = {
|
|
74
|
+
candidates: [{
|
|
75
|
+
finishReason: 'STOP'
|
|
76
|
+
}]
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Set hadToolCalls to true to trigger tool_calls finish reason
|
|
80
|
+
plugin.hadToolCalls = true;
|
|
81
|
+
|
|
82
|
+
// Process the stream event
|
|
83
|
+
plugin.processStreamEvent({ data: JSON.stringify(eventData) }, requestProgress);
|
|
84
|
+
|
|
85
|
+
// Verify that undefined elements were filtered out
|
|
86
|
+
t.truthy(capturedToolCalls, 'Tool callback should have been called');
|
|
87
|
+
t.is(capturedToolCalls.length, 1, 'Should have filtered out undefined elements');
|
|
88
|
+
t.is(capturedToolCalls[0].function.name, 'test_tool', 'Valid tool call should be preserved');
|
|
89
|
+
|
|
90
|
+
// Clean up
|
|
91
|
+
delete requestState[plugin.requestId];
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('Gemini25ImagePlugin - handles empty buffer gracefully', async t => {
|
|
95
|
+
const resolver = createResolverWithPlugin(Gemini25ImagePlugin);
|
|
96
|
+
const plugin = resolver.modelExecutor.plugin;
|
|
97
|
+
|
|
98
|
+
// Empty buffer
|
|
99
|
+
plugin.toolCallsBuffer = [];
|
|
100
|
+
|
|
101
|
+
// Mock the tool callback
|
|
102
|
+
let callbackCalled = false;
|
|
103
|
+
plugin.pathwayToolCallback = () => {
|
|
104
|
+
callbackCalled = true;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// Mock requestProgress and pathwayResolver
|
|
108
|
+
const requestProgress = { progress: 0, started: true };
|
|
109
|
+
const pathwayResolver = { args: {} };
|
|
110
|
+
|
|
111
|
+
// Mock requestState
|
|
112
|
+
requestState[plugin.requestId] = { pathwayResolver };
|
|
113
|
+
|
|
114
|
+
// Simulate a tool_calls finish reason
|
|
115
|
+
const eventData = {
|
|
116
|
+
candidates: [{
|
|
117
|
+
finishReason: 'STOP'
|
|
118
|
+
}]
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// Set hadToolCalls to true
|
|
122
|
+
plugin.hadToolCalls = true;
|
|
123
|
+
|
|
124
|
+
// Process the stream event
|
|
125
|
+
plugin.processStreamEvent({ data: JSON.stringify(eventData) }, requestProgress);
|
|
126
|
+
|
|
127
|
+
// Verify that callback was not called with empty buffer
|
|
128
|
+
t.falsy(callbackCalled, 'Tool callback should not be called with empty buffer');
|
|
129
|
+
|
|
130
|
+
// Clean up
|
|
131
|
+
delete requestState[plugin.requestId];
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test('Gemini25ImagePlugin - handles image artifacts in streaming', async t => {
|
|
135
|
+
const resolver = createResolverWithPlugin(Gemini25ImagePlugin);
|
|
136
|
+
const plugin = resolver.modelExecutor.plugin;
|
|
137
|
+
|
|
138
|
+
// Mock requestProgress
|
|
139
|
+
const requestProgress = { progress: 0, started: true };
|
|
140
|
+
|
|
141
|
+
// Simulate event with image artifact
|
|
142
|
+
const eventData = {
|
|
143
|
+
candidates: [{
|
|
144
|
+
content: {
|
|
145
|
+
parts: [{
|
|
146
|
+
inlineData: {
|
|
147
|
+
data: 'base64imagedata',
|
|
148
|
+
mimeType: 'image/png'
|
|
149
|
+
}
|
|
150
|
+
}]
|
|
151
|
+
}
|
|
152
|
+
}]
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// Process the stream event
|
|
156
|
+
plugin.processStreamEvent({ data: JSON.stringify(eventData) }, requestProgress);
|
|
157
|
+
|
|
158
|
+
// Verify that image artifacts were captured
|
|
159
|
+
t.truthy(requestProgress.artifacts, 'Artifacts should be created');
|
|
160
|
+
t.is(requestProgress.artifacts.length, 1, 'Should have one image artifact');
|
|
161
|
+
t.is(requestProgress.artifacts[0].type, 'image', 'Artifact should be of type image');
|
|
162
|
+
t.is(requestProgress.artifacts[0].data, 'base64imagedata', 'Image data should be preserved');
|
|
163
|
+
t.is(requestProgress.artifacts[0].mimeType, 'image/png', 'MIME type should be preserved');
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test('Gemini25ImagePlugin - handles multiple image artifacts', async t => {
|
|
167
|
+
const resolver = createResolverWithPlugin(Gemini25ImagePlugin);
|
|
168
|
+
const plugin = resolver.modelExecutor.plugin;
|
|
169
|
+
|
|
170
|
+
// Mock requestProgress
|
|
171
|
+
const requestProgress = { progress: 0, started: true };
|
|
172
|
+
|
|
173
|
+
// Simulate event with multiple image artifacts
|
|
174
|
+
const eventData = {
|
|
175
|
+
candidates: [{
|
|
176
|
+
content: {
|
|
177
|
+
parts: [
|
|
178
|
+
{
|
|
179
|
+
inlineData: {
|
|
180
|
+
data: 'base64image1',
|
|
181
|
+
mimeType: 'image/png'
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
inlineData: {
|
|
186
|
+
data: 'base64image2',
|
|
187
|
+
mimeType: 'image/jpeg'
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
]
|
|
191
|
+
}
|
|
192
|
+
}]
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// Process the stream event
|
|
196
|
+
plugin.processStreamEvent({ data: JSON.stringify(eventData) }, requestProgress);
|
|
197
|
+
|
|
198
|
+
// Verify that multiple image artifacts were captured
|
|
199
|
+
t.truthy(requestProgress.artifacts, 'Artifacts should be created');
|
|
200
|
+
t.is(requestProgress.artifacts.length, 2, 'Should have two image artifacts');
|
|
201
|
+
t.is(requestProgress.artifacts[0].type, 'image', 'First artifact should be of type image');
|
|
202
|
+
t.is(requestProgress.artifacts[0].data, 'base64image1', 'First image data should be preserved');
|
|
203
|
+
t.is(requestProgress.artifacts[0].mimeType, 'image/png', 'First MIME type should be preserved');
|
|
204
|
+
t.is(requestProgress.artifacts[1].type, 'image', 'Second artifact should be of type image');
|
|
205
|
+
t.is(requestProgress.artifacts[1].data, 'base64image2', 'Second image data should be preserved');
|
|
206
|
+
t.is(requestProgress.artifacts[1].mimeType, 'image/jpeg', 'Second MIME type should be preserved');
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test('Gemini25ImagePlugin - handles mixed content with text and images', async t => {
|
|
210
|
+
const resolver = createResolverWithPlugin(Gemini25ImagePlugin);
|
|
211
|
+
const plugin = resolver.modelExecutor.plugin;
|
|
212
|
+
|
|
213
|
+
// Mock requestProgress
|
|
214
|
+
const requestProgress = { progress: 0, started: true };
|
|
215
|
+
|
|
216
|
+
// Simulate event with mixed content (text + image)
|
|
217
|
+
const eventData = {
|
|
218
|
+
candidates: [{
|
|
219
|
+
content: {
|
|
220
|
+
parts: [
|
|
221
|
+
{
|
|
222
|
+
text: 'Here is an image:'
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
inlineData: {
|
|
226
|
+
data: 'base64imagedata',
|
|
227
|
+
mimeType: 'image/png'
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
]
|
|
231
|
+
}
|
|
232
|
+
}]
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// Process the stream event
|
|
236
|
+
plugin.processStreamEvent({ data: JSON.stringify(eventData) }, requestProgress);
|
|
237
|
+
|
|
238
|
+
// Verify that both text content and image artifacts were handled
|
|
239
|
+
t.truthy(requestProgress.artifacts, 'Artifacts should be created');
|
|
240
|
+
t.is(requestProgress.artifacts.length, 1, 'Should have one image artifact');
|
|
241
|
+
t.is(requestProgress.artifacts[0].type, 'image', 'Artifact should be of type image');
|
|
242
|
+
t.is(requestProgress.artifacts[0].data, 'base64imagedata', 'Image data should be preserved');
|
|
243
|
+
|
|
244
|
+
// Clean up
|
|
245
|
+
delete requestState[plugin.requestId];
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test('Gemini25ImagePlugin - handles response_modalities parameter', async t => {
|
|
249
|
+
const resolver = createResolverWithPlugin(Gemini25ImagePlugin);
|
|
250
|
+
const plugin = resolver.modelExecutor.plugin;
|
|
251
|
+
|
|
252
|
+
// Test with response_modalities parameter
|
|
253
|
+
const parameters = {
|
|
254
|
+
response_modalities: '["TEXT", "IMAGE"]'
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const prompt = {
|
|
258
|
+
prompt: 'test prompt'
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const cortexRequest = {
|
|
262
|
+
pathway: {}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const requestParams = plugin.getRequestParameters('test text', parameters, prompt, cortexRequest);
|
|
266
|
+
|
|
267
|
+
// Verify that response_modalities was added to generationConfig
|
|
268
|
+
t.truthy(requestParams.generationConfig.response_modalities, 'response_modalities should be set');
|
|
269
|
+
t.deepEqual(requestParams.generationConfig.response_modalities, ['TEXT', 'IMAGE'], 'response_modalities should be parsed correctly');
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
test('Gemini25ImagePlugin - handles response_modalities from pathway', async t => {
|
|
273
|
+
const resolver = createResolverWithPlugin(Gemini25ImagePlugin);
|
|
274
|
+
const plugin = resolver.modelExecutor.plugin;
|
|
275
|
+
|
|
276
|
+
// Test with response_modalities from pathway
|
|
277
|
+
const parameters = {};
|
|
278
|
+
|
|
279
|
+
const prompt = {
|
|
280
|
+
prompt: 'test prompt'
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
const cortexRequest = {
|
|
284
|
+
pathway: {
|
|
285
|
+
response_modalities: ['TEXT', 'IMAGE']
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const requestParams = plugin.getRequestParameters('test text', parameters, prompt, cortexRequest);
|
|
290
|
+
|
|
291
|
+
// Verify that response_modalities was added to generationConfig
|
|
292
|
+
t.truthy(requestParams.generationConfig.response_modalities, 'response_modalities should be set');
|
|
293
|
+
t.deepEqual(requestParams.generationConfig.response_modalities, ['TEXT', 'IMAGE'], 'response_modalities should be set correctly');
|
|
294
|
+
});
|