@juspay/neurolink 12.19.2 → 12.20.1
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/CHANGELOG.md +2 -2
- package/dist/adapters/imageFormatSupport.d.ts +5 -2
- package/dist/adapters/imageFormatSupport.js +47 -24
- package/dist/browser/neurolink.min.js +394 -394
- package/dist/cli/commands/proxy.js +171 -25
- package/dist/cli/commands/telemetry.d.ts +4 -1
- package/dist/cli/commands/telemetry.js +113 -0
- package/dist/cli/loop/optionsSchema.d.ts +1 -1
- package/dist/cli/utils/nativeTelemetry.d.ts +19 -0
- package/dist/cli/utils/nativeTelemetry.js +322 -0
- package/dist/core/modules/MessageBuilder.js +2 -0
- package/dist/neurolink.js +1 -0
- package/dist/processors/config/fileExtensions.d.ts +8 -1
- package/dist/processors/config/fileExtensions.js +8 -1
- package/dist/proxy/bodyCapturePolicy.d.ts +6 -0
- package/dist/proxy/bodyCapturePolicy.js +132 -0
- package/dist/proxy/otelLogSink.d.ts +11 -1
- package/dist/proxy/otelLogSink.js +174 -27
- package/dist/proxy/otlpLogTransport.d.ts +4 -2
- package/dist/proxy/otlpLogTransport.js +22 -7
- package/dist/proxy/requestLogger.js +11 -3
- package/dist/proxy/restartControl.js +18 -3
- package/dist/proxy/rollingProxyServer.js +48 -1
- package/dist/proxy/rollingWorkerProcess.js +87 -10
- package/dist/proxy/rollingWorkerProtocol.js +4 -2
- package/dist/proxy/rollingWorkerSupervisor.js +47 -10
- package/dist/proxy/socketWorkerRuntime.js +90 -4
- package/dist/proxy/updateChecker.d.ts +3 -3
- package/dist/proxy/updateChecker.js +16 -9
- package/dist/proxy/updateCoordinator.d.ts +10 -1
- package/dist/proxy/updateCoordinator.js +68 -1
- package/dist/proxy/updateState.d.ts +2 -0
- package/dist/proxy/updateState.js +15 -0
- package/dist/rag/pipeline/RAGPipeline.js +29 -0
- package/dist/rag/ragIntegration.js +21 -1
- package/dist/types/cli.d.ts +18 -0
- package/dist/types/file.d.ts +10 -0
- package/dist/types/generate.d.ts +27 -1
- package/dist/types/proxy.d.ts +97 -3
- package/dist/types/stream.d.ts +13 -1
- package/dist/utils/imageProcessor.js +151 -6
- package/dist/utils/messageBuilder.d.ts +2 -2
- package/dist/utils/messageBuilder.js +23 -13
- package/dist/utils/multimodalOptionsBuilder.d.ts +3 -0
- package/dist/utils/multimodalOptionsBuilder.js +1 -0
- package/docs-site/static/search-index.json +1 -1
- package/package.json +2 -1
- package/scripts/observability/NATIVE-DURABLE.md +68 -0
- package/scripts/observability/otel-collector.proxy-native-durable.yaml +2 -2
- package/scripts/observability/proxy-telemetry-check.mjs +135 -50
|
@@ -65,6 +65,153 @@ const RETRYABLE_ERROR_CODES = new Set([
|
|
|
65
65
|
"ETIMEDOUT",
|
|
66
66
|
"ERR_NETWORK",
|
|
67
67
|
]);
|
|
68
|
+
/**
|
|
69
|
+
* Bytes of prologue {@link hasSvgRoot} decodes and scans.
|
|
70
|
+
*
|
|
71
|
+
* A real SVG reaches its root within a few hundred bytes, so 4KB is generous.
|
|
72
|
+
* The cap exists because scanning the whole buffer made every unrecognized
|
|
73
|
+
* binary — and every BMP/TIFF/ICO/AVIF, whose checks run after this one — pay
|
|
74
|
+
* a full UTF-8 decode, and let a crafted unterminated doctype walk every byte
|
|
75
|
+
* (measured: 322ms on 32MB, 69ms to reject a 16MB BMP).
|
|
76
|
+
*/
|
|
77
|
+
const SVG_PROLOGUE_SCAN_BYTES = 4096;
|
|
78
|
+
/**
|
|
79
|
+
* Find an SVG root after XML's permitted leading constructs.
|
|
80
|
+
*
|
|
81
|
+
* Deliberately a discriminator, not an XML parser. Raster magic is checked
|
|
82
|
+
* first; this only distinguishes text reaching an SVG root after a UTF-8 BOM,
|
|
83
|
+
* XML whitespace, processing instructions, comments, or a doctype. The doctype
|
|
84
|
+
* scanner tracks quotes, comments/PIs, and the internal subset so a `>` or
|
|
85
|
+
* `]` inside any of them cannot expose a later `<svg` early. The root itself
|
|
86
|
+
* may carry an XML namespace prefix (`<s:svg xmlns:s="...">`), which is
|
|
87
|
+
* accepted alongside the bare `<svg` form.
|
|
88
|
+
*
|
|
89
|
+
* Scanning a bounded window makes "unterminated" ambiguous, and the two cases
|
|
90
|
+
* must resolve OPPOSITE ways:
|
|
91
|
+
*
|
|
92
|
+
* - the WINDOW ran out — the rest of the document is unreadable from here, so
|
|
93
|
+
* fail CLOSED and answer SVG. This function gates a rejection, so answering
|
|
94
|
+
* raster on an unterminated prologue would hand back the exact bypass it
|
|
95
|
+
* exists to close: prepend filler past the window and sail through as
|
|
96
|
+
* raster. A raster file never opens `<!--` or `<!doctype`, so the
|
|
97
|
+
* false-positive cost is a skipped image; the other direction is the
|
|
98
|
+
* vulnerability.
|
|
99
|
+
* - the BUFFER ran out — the whole document was read and is malformed, so it
|
|
100
|
+
* is genuinely not an SVG.
|
|
101
|
+
*/
|
|
102
|
+
function hasSvgRoot(input) {
|
|
103
|
+
const windowed = input.length > SVG_PROLOGUE_SCAN_BYTES;
|
|
104
|
+
const text = input
|
|
105
|
+
.subarray(0, Math.min(input.length, SVG_PROLOGUE_SCAN_BYTES))
|
|
106
|
+
.toString("utf8");
|
|
107
|
+
let position = text.charCodeAt(0) === 0xfeff ? 1 : 0;
|
|
108
|
+
const skipWhitespace = () => {
|
|
109
|
+
while (/\s/.test(text[position] ?? "")) {
|
|
110
|
+
position++;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
const skipDoctype = () => {
|
|
114
|
+
let quote;
|
|
115
|
+
let subsetDepth = 0;
|
|
116
|
+
let index = position + 9;
|
|
117
|
+
while (index < text.length) {
|
|
118
|
+
const char = text[index];
|
|
119
|
+
if (quote) {
|
|
120
|
+
if (char === quote) {
|
|
121
|
+
quote = undefined;
|
|
122
|
+
}
|
|
123
|
+
index++;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
// Comments and processing instructions are valid markup inside the
|
|
127
|
+
// internal subset (and, leniently, anywhere else in the declaration)
|
|
128
|
+
// and may themselves contain `]` or `>` — e.g. `<!-- ] > -->`. Skip
|
|
129
|
+
// them as opaque spans, the same way the outer scan does, so their
|
|
130
|
+
// contents can never be misread as the subset-close or doctype-end
|
|
131
|
+
// delimiter.
|
|
132
|
+
if (text.startsWith("<!--", index)) {
|
|
133
|
+
const end = text.indexOf("-->", index + 4);
|
|
134
|
+
if (end < 0) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
index = end + 3;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (text.startsWith("<?", index)) {
|
|
141
|
+
const end = text.indexOf("?>", index + 2);
|
|
142
|
+
if (end < 0) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
index = end + 2;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (char === '"' || char === "'") {
|
|
149
|
+
quote = char;
|
|
150
|
+
}
|
|
151
|
+
else if (char === "[") {
|
|
152
|
+
subsetDepth++;
|
|
153
|
+
}
|
|
154
|
+
else if (char === "]" && subsetDepth > 0) {
|
|
155
|
+
subsetDepth--;
|
|
156
|
+
}
|
|
157
|
+
else if (char === ">" && subsetDepth === 0) {
|
|
158
|
+
position = index + 1;
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
index++;
|
|
162
|
+
}
|
|
163
|
+
return false;
|
|
164
|
+
};
|
|
165
|
+
skipWhitespace();
|
|
166
|
+
while (position < text.length) {
|
|
167
|
+
if (text.startsWith("<?", position)) {
|
|
168
|
+
const end = text.indexOf("?>", position + 2);
|
|
169
|
+
if (end < 0) {
|
|
170
|
+
return windowed;
|
|
171
|
+
}
|
|
172
|
+
position = end + 2;
|
|
173
|
+
}
|
|
174
|
+
else if (text.startsWith("<!--", position)) {
|
|
175
|
+
const end = text.indexOf("-->", position + 4);
|
|
176
|
+
if (end < 0) {
|
|
177
|
+
return windowed;
|
|
178
|
+
}
|
|
179
|
+
position = end + 3;
|
|
180
|
+
}
|
|
181
|
+
else if (text.slice(position, position + 9).toLowerCase() === "<!doctype") {
|
|
182
|
+
if (!skipDoctype()) {
|
|
183
|
+
return windowed;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
skipWhitespace();
|
|
190
|
+
}
|
|
191
|
+
const rest = text.slice(position);
|
|
192
|
+
// Accepts an optional XML namespace prefix ahead of the tag name — an
|
|
193
|
+
// NCName (letters/digits/`.`/`-`/`_`, not starting with a digit) followed
|
|
194
|
+
// by `:` — so `<s:svg xmlns:s="...">` matches the same way bare `<svg`
|
|
195
|
+
// does.
|
|
196
|
+
if (/^<(?:[A-Za-z_][\w.-]*:)?svg(?:[\s/>])/i.test(rest)) {
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
// A fixed "`<svg` plus its delimiter is 5 characters" cutoff only worked
|
|
200
|
+
// when the tag name was the literal `svg`. With a namespace prefix now
|
|
201
|
+
// accepted, the prefix is unbounded (any NCName), so no fixed length can
|
|
202
|
+
// bound "may still be mid-token" — `<s:svg` (6 chars) is just as
|
|
203
|
+
// truncatable as `<svg` (4) was. Instead, check whether `rest` could still
|
|
204
|
+
// be the START of a string this function would accept: an optional NCName
|
|
205
|
+
// prefix, optionally followed by `:` and however much of the literal
|
|
206
|
+
// `svg` has been seen so far, with nothing left over. Anything left over
|
|
207
|
+
// (a character that cannot continue the prefix, isn't `:`, or doesn't
|
|
208
|
+
// continue "svg") proves `rest` can never complete into a match, so it is
|
|
209
|
+
// safe to answer false even though the window is cut. An empty `rest` is
|
|
210
|
+
// the same ambiguity in the degenerate case (no root text observed yet).
|
|
211
|
+
const isUnresolvedPrefix = rest.length === 0 ||
|
|
212
|
+
/^<(?:[A-Za-z_][\w.-]*)?(?::(?:s(?:v(?:g)?)?)?)?$/i.test(rest);
|
|
213
|
+
return windowed && isUnresolvedPrefix;
|
|
214
|
+
}
|
|
68
215
|
/**
|
|
69
216
|
* Determines if an HTTP error is retryable based on status code
|
|
70
217
|
* Only network errors and certain HTTP status codes should be retried
|
|
@@ -452,12 +599,10 @@ export class ImageProcessor {
|
|
|
452
599
|
return "image/webp";
|
|
453
600
|
}
|
|
454
601
|
}
|
|
455
|
-
// SVG
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
return "image/svg+xml";
|
|
460
|
-
}
|
|
602
|
+
// SVG is text-based and may legally put a BOM, whitespace, an XML
|
|
603
|
+
// declaration, comments, or a doctype before its root element.
|
|
604
|
+
if (hasSvgRoot(input)) {
|
|
605
|
+
return "image/svg+xml";
|
|
461
606
|
}
|
|
462
607
|
// ISO-BMFF images: "ftyp" box at offset 4, major brand at offset 8.
|
|
463
608
|
// AVIF has codec-specific major brands. HEIC uses HEVC image/sequence
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { GenerateOptions, MultimodalChatMessage, StreamOptions, TextGenerationOptions } from "../types/index.js";
|
|
1
|
+
import type { GenerateOptions, MultimodalChatMessage, StreamOptions, TextGenerationOptions, VisionImageOutputFormat } from "../types/index.js";
|
|
2
2
|
import type { ModelMessage } from "../types/index.js";
|
|
3
3
|
/**
|
|
4
4
|
* Type-safe conversion from MultimodalChatMessage[] to ModelMessage[]
|
|
@@ -56,4 +56,4 @@ export declare function buildMultimodalMessagesArray(options: GenerateOptions, p
|
|
|
56
56
|
* Entries that are `http(s)` URLs are left alone — they have no bytes yet, and
|
|
57
57
|
* `processImageToBase64` converts them once they are downloaded.
|
|
58
58
|
*/
|
|
59
|
-
export declare function normalizeVisionImageFormats(input: GenerateOptions["input"]): Promise<void>;
|
|
59
|
+
export declare function normalizeVisionImageFormats(input: GenerateOptions["input"], outputFormat?: VisionImageOutputFormat): Promise<void>;
|
|
@@ -1352,7 +1352,7 @@ export async function buildMultimodalMessagesArray(options, provider, model) {
|
|
|
1352
1352
|
await processUnifiedFilesArray(options, maxSize, provider);
|
|
1353
1353
|
// Detection can append images (PDF page renders, video keyframes, a HEIC
|
|
1354
1354
|
// photo), so compatibility conversion has to run after it, not before.
|
|
1355
|
-
await normalizeVisionImageFormats(inp);
|
|
1355
|
+
await normalizeVisionImageFormats(inp, options.imageOptions?.outputFormat);
|
|
1356
1356
|
// Process explicit CSV files array
|
|
1357
1357
|
await processExplicitCsvFiles(options);
|
|
1358
1358
|
// Post-processing budget enforcement and token usage logging
|
|
@@ -1486,7 +1486,7 @@ export async function buildMultimodalMessagesArray(options, provider, model) {
|
|
|
1486
1486
|
// the multimodal converter below, so the bytes were dropped and only the
|
|
1487
1487
|
// metadata summary folded into `text` survived. Exactly the failure this
|
|
1488
1488
|
// change exists to remove, reintroduced through the other door.
|
|
1489
|
-
userContent = await convertContentToProviderFormat(inp.content, provider, model, options.pdfOptions, inp.nativeAudioFiles ?? []);
|
|
1489
|
+
userContent = await convertContentToProviderFormat(inp.content, provider, model, options.pdfOptions, inp.nativeAudioFiles ?? [], options.imageOptions?.outputFormat);
|
|
1490
1490
|
}
|
|
1491
1491
|
else if ((inp.images && inp.images.length > 0) ||
|
|
1492
1492
|
pdfFiles.length > 0 ||
|
|
@@ -1495,7 +1495,10 @@ export async function buildMultimodalMessagesArray(options, provider, model) {
|
|
|
1495
1495
|
// recording was dropped and only its metadata summary — already folded
|
|
1496
1496
|
// into `text` — ever reached the model.
|
|
1497
1497
|
(inp.nativeAudioFiles?.length ?? 0) > 0) {
|
|
1498
|
-
userContent = await convertMultimodalToProviderFormat(inp.text ?? "", inp.images || [], pdfFiles, provider, model,
|
|
1498
|
+
userContent = await convertMultimodalToProviderFormat(inp.text ?? "", inp.images || [], pdfFiles, provider, model, {
|
|
1499
|
+
audioFiles: inp.nativeAudioFiles ?? [],
|
|
1500
|
+
outputFormat: options.imageOptions?.outputFormat,
|
|
1501
|
+
});
|
|
1499
1502
|
}
|
|
1500
1503
|
else {
|
|
1501
1504
|
userContent = inp.text;
|
|
@@ -1571,7 +1574,7 @@ async function appendCsvContentToText(csvItems, baseText) {
|
|
|
1571
1574
|
/**
|
|
1572
1575
|
* Convert advanced content format to provider-specific format
|
|
1573
1576
|
*/
|
|
1574
|
-
async function convertContentToProviderFormat(content, provider, _model, pdfOptions, audioFiles = []) {
|
|
1577
|
+
async function convertContentToProviderFormat(content, provider, _model, pdfOptions, audioFiles = [], outputFormat) {
|
|
1575
1578
|
const textContent = content.find((c) => c.type === "text");
|
|
1576
1579
|
const imageContent = content.filter((c) => c.type === "image");
|
|
1577
1580
|
const pdfContent = content.filter((c) => c.type === "pdf");
|
|
@@ -1624,7 +1627,10 @@ async function convertContentToProviderFormat(content, provider, _model, pdfOpti
|
|
|
1624
1627
|
await enforceAggregatePdfLimits(pdfFiles, provider, {
|
|
1625
1628
|
trustSuppliedPageCounts: false,
|
|
1626
1629
|
});
|
|
1627
|
-
return await convertMultimodalToProviderFormat(text, images, pdfFiles, provider, _model,
|
|
1630
|
+
return await convertMultimodalToProviderFormat(text, images, pdfFiles, provider, _model, {
|
|
1631
|
+
audioFiles,
|
|
1632
|
+
outputFormat,
|
|
1633
|
+
});
|
|
1628
1634
|
}
|
|
1629
1635
|
/**
|
|
1630
1636
|
* Check if a string is an internet URL
|
|
@@ -1868,7 +1874,7 @@ async function convertFilePathToBase64(filePath) {
|
|
|
1868
1874
|
* Passing raw base64 avoids this because `new URL(base64string)` throws and
|
|
1869
1875
|
* the SDK treats the string as inline base64 data instead.
|
|
1870
1876
|
*/
|
|
1871
|
-
async function processImageToBase64(image, index) {
|
|
1877
|
+
async function processImageToBase64(image, index, outputFormat) {
|
|
1872
1878
|
let imageData;
|
|
1873
1879
|
let mimeType = "image/jpeg"; // Default mime type
|
|
1874
1880
|
if (typeof image === "string") {
|
|
@@ -1947,7 +1953,7 @@ async function processImageToBase64(image, index) {
|
|
|
1947
1953
|
const context = `image input at index ${index}`;
|
|
1948
1954
|
ImageProcessor.validateSize(base64DecodedByteLength(imageData), context);
|
|
1949
1955
|
const rawImage = Buffer.from(imageData, "base64");
|
|
1950
|
-
const compatible = await toVisionCompatibleImage(rawImage, mimeType);
|
|
1956
|
+
const compatible = await toVisionCompatibleImage(rawImage, mimeType, outputFormat);
|
|
1951
1957
|
if (compatible.converted) {
|
|
1952
1958
|
imageData = compatible.buffer.toString("base64");
|
|
1953
1959
|
mimeType = compatible.mimeType;
|
|
@@ -1968,7 +1974,7 @@ async function processImageToBase64(image, index) {
|
|
|
1968
1974
|
* Entries that are `http(s)` URLs are left alone — they have no bytes yet, and
|
|
1969
1975
|
* `processImageToBase64` converts them once they are downloaded.
|
|
1970
1976
|
*/
|
|
1971
|
-
export async function normalizeVisionImageFormats(input) {
|
|
1977
|
+
export async function normalizeVisionImageFormats(input, outputFormat) {
|
|
1972
1978
|
const images = input?.images;
|
|
1973
1979
|
if (!images || images.length === 0) {
|
|
1974
1980
|
return;
|
|
@@ -1985,7 +1991,7 @@ export async function normalizeVisionImageFormats(input) {
|
|
|
1985
1991
|
if (!source || !needsVisionTranscode(source.mimeType)) {
|
|
1986
1992
|
continue;
|
|
1987
1993
|
}
|
|
1988
|
-
const compatible = await toVisionCompatibleImage(source.buffer, source.mimeType);
|
|
1994
|
+
const compatible = await toVisionCompatibleImage(source.buffer, source.mimeType, outputFormat);
|
|
1989
1995
|
if (!compatible.converted) {
|
|
1990
1996
|
continue;
|
|
1991
1997
|
}
|
|
@@ -2115,7 +2121,7 @@ async function readImageSourceForConversion(payload) {
|
|
|
2115
2121
|
* - Buffers/Data URIs: Processed normally
|
|
2116
2122
|
* - Supports alt text for accessibility (included as context in text parts)
|
|
2117
2123
|
*/
|
|
2118
|
-
async function convertSimpleImagesToProviderFormat(text, images, provider, _model) {
|
|
2124
|
+
async function convertSimpleImagesToProviderFormat(text, images, provider, _model, outputFormat) {
|
|
2119
2125
|
// Validate image count against provider-specific limits before processing
|
|
2120
2126
|
ProviderImageAdapter.validateImageCount(images.length, provider, _model);
|
|
2121
2127
|
// For Vercel AI SDK, we need to return the content in the standard format
|
|
@@ -2172,7 +2178,7 @@ async function convertSimpleImagesToProviderFormat(text, images, provider, _mode
|
|
|
2172
2178
|
for (const [index, { data: image }] of actualImages.entries()) {
|
|
2173
2179
|
try {
|
|
2174
2180
|
// Use helper function to process image and reduce nesting depth
|
|
2175
|
-
const { imageData, mimeType } = await processImageToBase64(image, index);
|
|
2181
|
+
const { imageData, mimeType } = await processImageToBase64(image, index, outputFormat);
|
|
2176
2182
|
content.push({
|
|
2177
2183
|
type: "image",
|
|
2178
2184
|
image: imageData,
|
|
@@ -2195,13 +2201,17 @@ async function convertSimpleImagesToProviderFormat(text, images, provider, _mode
|
|
|
2195
2201
|
async function convertMultimodalToProviderFormat(text, images,
|
|
2196
2202
|
// The canonical entry shape (#309) rather than a fourth copy of it inline —
|
|
2197
2203
|
// which is what let the render knobs stop short of this function.
|
|
2198
|
-
pdfFiles, provider, model,
|
|
2204
|
+
pdfFiles, provider, model,
|
|
2205
|
+
// Bundled rather than two more positional params: a 7th would trip the
|
|
2206
|
+
// repo's `max-params` ceiling of 6.
|
|
2207
|
+
mediaOptions = {}) {
|
|
2208
|
+
const { audioFiles = [], outputFormat } = mediaOptions;
|
|
2199
2209
|
const content = [
|
|
2200
2210
|
{ type: "text", text },
|
|
2201
2211
|
];
|
|
2202
2212
|
// Add images if present
|
|
2203
2213
|
if (images.length > 0) {
|
|
2204
|
-
const imageContent = await convertSimpleImagesToProviderFormat("", images, provider, model);
|
|
2214
|
+
const imageContent = await convertSimpleImagesToProviderFormat("", images, provider, model, outputFormat);
|
|
2205
2215
|
if (Array.isArray(imageContent)) {
|
|
2206
2216
|
imageContent.forEach((item) => {
|
|
2207
2217
|
if (item.type !== "text") {
|
|
@@ -61,6 +61,9 @@ export declare function buildMultimodalOptions(options: StreamOptions, providerN
|
|
|
61
61
|
scale?: number;
|
|
62
62
|
maxPages?: number;
|
|
63
63
|
} | undefined;
|
|
64
|
+
imageOptions: {
|
|
65
|
+
outputFormat?: import("../index.js").VisionImageOutputFormat;
|
|
66
|
+
} | undefined;
|
|
64
67
|
systemPrompt: string | undefined;
|
|
65
68
|
conversationHistory: import("../index.js").ChatMessage[] | undefined;
|
|
66
69
|
provider: string;
|
|
@@ -59,6 +59,7 @@ export function buildMultimodalOptions(options, providerName, modelName) {
|
|
|
59
59
|
},
|
|
60
60
|
csvOptions: options.csvOptions,
|
|
61
61
|
pdfOptions: options.pdfOptions,
|
|
62
|
+
imageOptions: options.imageOptions,
|
|
62
63
|
systemPrompt: options.systemPrompt,
|
|
63
64
|
conversationHistory: options.conversationMessages,
|
|
64
65
|
provider: providerName,
|