@juspay/neurolink 12.30.2 → 12.31.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/CHANGELOG.md +3 -3
- package/dist/browser/neurolink.min.js +399 -382
- package/dist/cli/loop/optionsSchema.d.ts +1 -1
- package/dist/core/baseProvider.js +1 -0
- package/dist/core/modules/MessageBuilder.js +10 -0
- package/dist/neurolink.js +1 -0
- package/dist/processors/document/ExcelProcessor.d.ts +13 -1
- package/dist/processors/document/ExcelProcessor.js +118 -0
- package/dist/processors/document/WordProcessor.js +21 -0
- package/dist/types/file.d.ts +15 -0
- package/dist/types/generate.d.ts +11 -1
- package/dist/types/processor.d.ts +11 -0
- package/dist/types/stream.d.ts +6 -1
- package/dist/types/utilities.d.ts +19 -0
- package/dist/utils/fileDetector.js +31 -36
- package/dist/utils/htmlToMarkdown.d.ts +23 -0
- package/dist/utils/htmlToMarkdown.js +442 -0
- package/dist/utils/messageBuilder.js +3 -0
- package/dist/utils/multimodalOptionsBuilder.d.ts +3 -0
- package/dist/utils/multimodalOptionsBuilder.js +6 -0
- package/docs-site/static/search-index.json +5 -1
- package/package.json +2 -1
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML to Markdown conversion for document processors.
|
|
3
|
+
*
|
|
4
|
+
* Scoped deliberately to the small, well-formed HTML subset that document
|
|
5
|
+
* converters emit (mammoth for DOCX): headings, paragraphs, emphasis, links,
|
|
6
|
+
* images, lists, block quotes, preformatted text and tables. It is not a
|
|
7
|
+
* general-purpose web-page converter, which is why the repository gains no
|
|
8
|
+
* dependency for it.
|
|
9
|
+
*
|
|
10
|
+
* @module utils/htmlToMarkdown
|
|
11
|
+
*/
|
|
12
|
+
/** Tags that never have a closing tag and so never open a scope. */
|
|
13
|
+
const VOID_TAGS = new Set([
|
|
14
|
+
"area",
|
|
15
|
+
"base",
|
|
16
|
+
"br",
|
|
17
|
+
"col",
|
|
18
|
+
"embed",
|
|
19
|
+
"hr",
|
|
20
|
+
"img",
|
|
21
|
+
"input",
|
|
22
|
+
"link",
|
|
23
|
+
"meta",
|
|
24
|
+
"source",
|
|
25
|
+
"track",
|
|
26
|
+
"wbr",
|
|
27
|
+
]);
|
|
28
|
+
const NAMED_ENTITIES = {
|
|
29
|
+
amp: "&",
|
|
30
|
+
lt: "<",
|
|
31
|
+
gt: ">",
|
|
32
|
+
quot: '"',
|
|
33
|
+
apos: "'",
|
|
34
|
+
nbsp: " ",
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Decode the numeric and named character references a document converter can
|
|
38
|
+
* emit. Unknown references are left verbatim rather than dropped, so no text
|
|
39
|
+
* is silently lost.
|
|
40
|
+
*/
|
|
41
|
+
function decodeEntities(value) {
|
|
42
|
+
return value.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z]+);/g, (match, body) => {
|
|
43
|
+
if (body.startsWith("#")) {
|
|
44
|
+
const codePoint = body.startsWith("#x") || body.startsWith("#X")
|
|
45
|
+
? Number.parseInt(body.slice(2), 16)
|
|
46
|
+
: Number.parseInt(body.slice(1), 10);
|
|
47
|
+
if (Number.isNaN(codePoint) || codePoint < 0 || codePoint > 0x10ffff) {
|
|
48
|
+
return match;
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
return String.fromCodePoint(codePoint);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return match;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const named = NAMED_ENTITIES[body.toLowerCase()];
|
|
58
|
+
return named ?? match;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Parse the HTML subset into a node tree.
|
|
63
|
+
*
|
|
64
|
+
* Unbalanced closing tags are ignored and unclosed elements are closed at the
|
|
65
|
+
* end of input, so malformed markup degrades to partial structure instead of
|
|
66
|
+
* throwing.
|
|
67
|
+
*/
|
|
68
|
+
function parseHtml(html) {
|
|
69
|
+
const root = {
|
|
70
|
+
kind: "element",
|
|
71
|
+
tag: "#root",
|
|
72
|
+
attrs: {},
|
|
73
|
+
children: [],
|
|
74
|
+
};
|
|
75
|
+
const stack = [root];
|
|
76
|
+
const tagPattern = /<\/?([a-zA-Z][a-zA-Z0-9-]*)((?:[^>"']|"[^"]*"|'[^']*')*)>/g;
|
|
77
|
+
let cursor = 0;
|
|
78
|
+
let match;
|
|
79
|
+
const pushText = (raw) => {
|
|
80
|
+
if (!raw) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const current = stack[stack.length - 1];
|
|
84
|
+
current.children.push({ kind: "text", value: decodeEntities(raw) });
|
|
85
|
+
};
|
|
86
|
+
while ((match = tagPattern.exec(html)) !== null) {
|
|
87
|
+
pushText(html.slice(cursor, match.index));
|
|
88
|
+
cursor = match.index + match[0].length;
|
|
89
|
+
const tag = match[1].toLowerCase();
|
|
90
|
+
const isClosing = match[0].startsWith("</");
|
|
91
|
+
const isSelfClosing = match[2].trimEnd().endsWith("/");
|
|
92
|
+
if (isClosing) {
|
|
93
|
+
// Only unwind if the tag is actually open, so a stray `</div>` is
|
|
94
|
+
// inert. Search from the top of the stack down so a closing tag
|
|
95
|
+
// matches the innermost (most recently opened) element with that
|
|
96
|
+
// name — `findIndex` would match the OUTERMOST one instead, and for
|
|
97
|
+
// nested same-tag elements (`<div><div>A</div>B</div>`) that pops both
|
|
98
|
+
// levels at once, reattaching everything after the real inner close to
|
|
99
|
+
// the wrong ancestor.
|
|
100
|
+
let openIndex = -1;
|
|
101
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
102
|
+
if (stack[i].tag === tag) {
|
|
103
|
+
openIndex = i;
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (openIndex > 0) {
|
|
108
|
+
stack.length = openIndex;
|
|
109
|
+
}
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const element = {
|
|
113
|
+
kind: "element",
|
|
114
|
+
tag,
|
|
115
|
+
attrs: parseAttributes(match[2]),
|
|
116
|
+
children: [],
|
|
117
|
+
};
|
|
118
|
+
stack[stack.length - 1].children.push(element);
|
|
119
|
+
if (!isSelfClosing && !VOID_TAGS.has(tag)) {
|
|
120
|
+
stack.push(element);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
pushText(html.slice(cursor));
|
|
124
|
+
return root.children;
|
|
125
|
+
}
|
|
126
|
+
function parseAttributes(raw) {
|
|
127
|
+
const attrs = {};
|
|
128
|
+
const pattern = /([a-zA-Z_:][a-zA-Z0-9_.:-]*)\s*=\s*("([^"]*)"|'([^']*)'|([^\s"'>]+))/g;
|
|
129
|
+
let match;
|
|
130
|
+
while ((match = pattern.exec(raw)) !== null) {
|
|
131
|
+
const value = match[3] ?? match[4] ?? match[5] ?? "";
|
|
132
|
+
attrs[match[1].toLowerCase()] = decodeEntities(value);
|
|
133
|
+
}
|
|
134
|
+
return attrs;
|
|
135
|
+
}
|
|
136
|
+
function isElement(node) {
|
|
137
|
+
return node.kind === "element";
|
|
138
|
+
}
|
|
139
|
+
/** Collapse runs of whitespace the way an HTML renderer would. */
|
|
140
|
+
function collapseWhitespace(value) {
|
|
141
|
+
return value
|
|
142
|
+
.replace(/\s+/g, " ")
|
|
143
|
+
.replace(/&/g, "&")
|
|
144
|
+
.replace(/</g, "<")
|
|
145
|
+
.replace(/>/g, ">");
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Format a link/image destination per CommonMark's angle-bracket form.
|
|
149
|
+
*
|
|
150
|
+
* A bare `(dest)` destination ends at the first whitespace or unbalanced
|
|
151
|
+
* `)`, so a value containing either — an `href`/`src` with a space, or one
|
|
152
|
+
* with unbalanced parentheses — would silently truncate or corrupt the
|
|
153
|
+
* surrounding `[text](dest)` / `` markup. Wrapping in `<...>`
|
|
154
|
+
* makes the destination unambiguous; CommonMark requires any literal `<`
|
|
155
|
+
* or `>` inside that form to be escaped. Ordinary destinations (no
|
|
156
|
+
* whitespace or parens) are returned byte-identical to before.
|
|
157
|
+
*/
|
|
158
|
+
function formatLinkDestination(dest) {
|
|
159
|
+
if (!/[\s()]/.test(dest)) {
|
|
160
|
+
return dest;
|
|
161
|
+
}
|
|
162
|
+
// Backslash first: escaping only `<`/`>` turns `\>` into `\\>`, which
|
|
163
|
+
// CommonMark reads as a literal backslash followed by a bare `>`.
|
|
164
|
+
const escaped = dest
|
|
165
|
+
.replace(/\\/g, "\\\\")
|
|
166
|
+
.replace(/</g, "\\<")
|
|
167
|
+
.replace(/>/g, "\\>");
|
|
168
|
+
return `<${escaped}>`;
|
|
169
|
+
}
|
|
170
|
+
/** Render inline content (emphasis, links, images, line breaks) to markdown. */
|
|
171
|
+
function renderInline(nodes) {
|
|
172
|
+
let out = "";
|
|
173
|
+
for (const node of nodes) {
|
|
174
|
+
if (node.kind === "text") {
|
|
175
|
+
out += collapseWhitespace(node.value);
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
switch (node.tag) {
|
|
179
|
+
case "script":
|
|
180
|
+
case "style":
|
|
181
|
+
break;
|
|
182
|
+
case "strong":
|
|
183
|
+
case "b": {
|
|
184
|
+
const inner = renderInline(node.children).trim();
|
|
185
|
+
out += inner ? `**${inner}**` : "";
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
case "em":
|
|
189
|
+
case "i": {
|
|
190
|
+
const inner = renderInline(node.children).trim();
|
|
191
|
+
out += inner ? `*${inner}*` : "";
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
case "code": {
|
|
195
|
+
const inner = renderInline(node.children).trim();
|
|
196
|
+
out += inner ? `\`${inner}\`` : "";
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
case "a": {
|
|
200
|
+
const inner = renderInline(node.children).trim();
|
|
201
|
+
const href = node.attrs.href;
|
|
202
|
+
out += href ? `[${inner}](${formatLinkDestination(href)})` : inner;
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
case "img": {
|
|
206
|
+
const alt = node.attrs.alt ?? "";
|
|
207
|
+
const src = node.attrs.src ?? "";
|
|
208
|
+
// A DOCX image is inlined as a multi-megabyte data URI; keep the
|
|
209
|
+
// alt text and drop the payload rather than flooding the prompt.
|
|
210
|
+
out += src.startsWith("data:")
|
|
211
|
+
? ``
|
|
212
|
+
: `})`;
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
case "br":
|
|
216
|
+
out += "\n";
|
|
217
|
+
break;
|
|
218
|
+
default:
|
|
219
|
+
out += renderInline(node.children);
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return out;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Extract a `<pre>` block's text content verbatim.
|
|
227
|
+
*
|
|
228
|
+
* `renderInline()` funnels every text node through `collapseWhitespace()`,
|
|
229
|
+
* which does `.replace(/\s+/g, " ")` — right for prose, destructive for
|
|
230
|
+
* code: it collapses every newline, tab and indentation run onto one line.
|
|
231
|
+
* This walks the same tree but keeps each text node's raw value, so a
|
|
232
|
+
* multi-line, indented code sample survives into the fenced block intact.
|
|
233
|
+
* A nested element (e.g. `<pre><code>…</code></pre>`) still contributes its
|
|
234
|
+
* text, but ignores inline markdown semantics like `**`/`*`/`` ` `` —
|
|
235
|
+
* `<pre>` content is literal code, not prose, and injecting markdown markers
|
|
236
|
+
* into it would corrupt the code rather than format it.
|
|
237
|
+
*/
|
|
238
|
+
function renderPreformattedText(nodes) {
|
|
239
|
+
let out = "";
|
|
240
|
+
for (const node of nodes) {
|
|
241
|
+
if (node.kind === "text") {
|
|
242
|
+
out += node.value;
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
if (node.tag === "script" || node.tag === "style") {
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
if (node.tag === "br") {
|
|
249
|
+
out += "\n";
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
out += renderPreformattedText(node.children);
|
|
253
|
+
}
|
|
254
|
+
return out;
|
|
255
|
+
}
|
|
256
|
+
/** Collect the `<tr>` rows under a table, seeing through thead/tbody/tfoot. */
|
|
257
|
+
function collectRows(node) {
|
|
258
|
+
const rows = [];
|
|
259
|
+
for (const child of node.children.filter(isElement)) {
|
|
260
|
+
if (child.tag === "tr") {
|
|
261
|
+
rows.push(child);
|
|
262
|
+
}
|
|
263
|
+
else if (["thead", "tbody", "tfoot"].includes(child.tag)) {
|
|
264
|
+
rows.push(...collectRows(child));
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return rows;
|
|
268
|
+
}
|
|
269
|
+
/** A pipe inside a cell would otherwise split it into two columns. */
|
|
270
|
+
function escapeCell(value) {
|
|
271
|
+
return value
|
|
272
|
+
.replace(/\\/g, "\\\\")
|
|
273
|
+
.replace(/\|/g, "\\|")
|
|
274
|
+
.replace(/\n/g, " ")
|
|
275
|
+
.trim();
|
|
276
|
+
}
|
|
277
|
+
function renderTable(node) {
|
|
278
|
+
const rows = collectRows(node);
|
|
279
|
+
if (rows.length === 0) {
|
|
280
|
+
return "";
|
|
281
|
+
}
|
|
282
|
+
const cellsOf = (row) => row.children
|
|
283
|
+
.filter(isElement)
|
|
284
|
+
.filter((cell) => cell.tag === "td" || cell.tag === "th")
|
|
285
|
+
.map((cell) => escapeCell(renderBlocks(cell.children).replace(/\n+/g, " ")));
|
|
286
|
+
// Markdown has no headerless table, so when the source marked no header row
|
|
287
|
+
// the first row is promoted — that is what a reader sees anyway.
|
|
288
|
+
const header = cellsOf(rows[0]);
|
|
289
|
+
const bodyRows = rows.slice(1).map(cellsOf);
|
|
290
|
+
const columnCount = Math.max(header.length, ...bodyRows.map((r) => r.length), 1);
|
|
291
|
+
const pad = (cells) => Array.from({ length: columnCount }, (_, i) => cells[i] ?? "");
|
|
292
|
+
const lines = [
|
|
293
|
+
`| ${pad(header).join(" | ")} |`,
|
|
294
|
+
`| ${Array.from({ length: columnCount }, () => "---").join(" | ")} |`,
|
|
295
|
+
...bodyRows.map((cells) => `| ${pad(cells).join(" | ")} |`),
|
|
296
|
+
];
|
|
297
|
+
return lines.join("\n");
|
|
298
|
+
}
|
|
299
|
+
function renderList(node, depth) {
|
|
300
|
+
const ordered = node.tag === "ol";
|
|
301
|
+
const items = node.children
|
|
302
|
+
.filter(isElement)
|
|
303
|
+
.filter((child) => child.tag === "li");
|
|
304
|
+
const indent = " ".repeat(depth);
|
|
305
|
+
return items
|
|
306
|
+
.map((item, index) => {
|
|
307
|
+
const marker = ordered ? `${index + 1}.` : "-";
|
|
308
|
+
// Nested lists render themselves at the next depth; everything else on
|
|
309
|
+
// the item is inline content belonging to this bullet.
|
|
310
|
+
const nested = item.children
|
|
311
|
+
.filter(isElement)
|
|
312
|
+
.filter((child) => child.tag === "ul" || child.tag === "ol");
|
|
313
|
+
const own = item.children.filter((child) => !(isElement(child) && (child.tag === "ul" || child.tag === "ol")));
|
|
314
|
+
const label = renderBlocks(own).replace(/\n+/g, " ").trim();
|
|
315
|
+
const nestedText = nested
|
|
316
|
+
.map((child) => renderList(child, depth + 1))
|
|
317
|
+
.filter(Boolean)
|
|
318
|
+
.join("\n");
|
|
319
|
+
return [`${indent}${marker} ${label}`.trimEnd(), nestedText]
|
|
320
|
+
.filter(Boolean)
|
|
321
|
+
.join("\n");
|
|
322
|
+
})
|
|
323
|
+
.join("\n");
|
|
324
|
+
}
|
|
325
|
+
/** Render block-level content, separating blocks with a blank line. */
|
|
326
|
+
function renderBlocks(nodes) {
|
|
327
|
+
const blocks = [];
|
|
328
|
+
let inlineBuffer = "";
|
|
329
|
+
const flushInline = () => {
|
|
330
|
+
const text = inlineBuffer.trim();
|
|
331
|
+
if (text) {
|
|
332
|
+
blocks.push(text);
|
|
333
|
+
}
|
|
334
|
+
inlineBuffer = "";
|
|
335
|
+
};
|
|
336
|
+
for (const node of nodes) {
|
|
337
|
+
if (node.kind === "text") {
|
|
338
|
+
inlineBuffer += collapseWhitespace(node.value);
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
const headingMatch = /^h([1-6])$/.exec(node.tag);
|
|
342
|
+
if (headingMatch) {
|
|
343
|
+
flushInline();
|
|
344
|
+
const level = Number.parseInt(headingMatch[1], 10);
|
|
345
|
+
const text = renderInline(node.children).replace(/\n+/g, " ").trim();
|
|
346
|
+
if (text) {
|
|
347
|
+
blocks.push(`${"#".repeat(level)} ${text}`);
|
|
348
|
+
}
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
switch (node.tag) {
|
|
352
|
+
case "script":
|
|
353
|
+
case "style":
|
|
354
|
+
flushInline();
|
|
355
|
+
break;
|
|
356
|
+
case "p": {
|
|
357
|
+
flushInline();
|
|
358
|
+
const text = renderBlocks(node.children).trim();
|
|
359
|
+
if (text) {
|
|
360
|
+
blocks.push(text);
|
|
361
|
+
}
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
case "ul":
|
|
365
|
+
case "ol": {
|
|
366
|
+
flushInline();
|
|
367
|
+
const text = renderList(node, 0);
|
|
368
|
+
if (text) {
|
|
369
|
+
blocks.push(text);
|
|
370
|
+
}
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
case "table": {
|
|
374
|
+
flushInline();
|
|
375
|
+
const text = renderTable(node);
|
|
376
|
+
if (text) {
|
|
377
|
+
blocks.push(text);
|
|
378
|
+
}
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
381
|
+
case "blockquote": {
|
|
382
|
+
flushInline();
|
|
383
|
+
const inner = renderBlocks(node.children).trim();
|
|
384
|
+
if (inner) {
|
|
385
|
+
blocks.push(inner
|
|
386
|
+
.split("\n")
|
|
387
|
+
.map((line) => (line ? `> ${line}` : ">"))
|
|
388
|
+
.join("\n"));
|
|
389
|
+
}
|
|
390
|
+
break;
|
|
391
|
+
}
|
|
392
|
+
case "pre": {
|
|
393
|
+
flushInline();
|
|
394
|
+
const inner = renderPreformattedText(node.children).replace(/^\n+|\n+$/g, "");
|
|
395
|
+
if (inner) {
|
|
396
|
+
blocks.push(`\`\`\`\n${inner}\n\`\`\``);
|
|
397
|
+
}
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
case "hr": {
|
|
401
|
+
flushInline();
|
|
402
|
+
blocks.push("---");
|
|
403
|
+
break;
|
|
404
|
+
}
|
|
405
|
+
case "div":
|
|
406
|
+
case "section":
|
|
407
|
+
case "article": {
|
|
408
|
+
flushInline();
|
|
409
|
+
const inner = renderBlocks(node.children).trim();
|
|
410
|
+
if (inner) {
|
|
411
|
+
blocks.push(inner);
|
|
412
|
+
}
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
default:
|
|
416
|
+
inlineBuffer += renderInline([node]);
|
|
417
|
+
break;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
flushInline();
|
|
421
|
+
return blocks.join("\n\n");
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Convert an HTML fragment to Markdown.
|
|
425
|
+
*
|
|
426
|
+
* @param html - HTML produced by a document converter
|
|
427
|
+
* @returns Markdown text, or an empty string when the fragment has no content
|
|
428
|
+
*
|
|
429
|
+
* @example
|
|
430
|
+
* ```typescript
|
|
431
|
+
* htmlToMarkdown("<h1>Title</h1><p>Body</p>"); // "# Title\n\nBody"
|
|
432
|
+
* ```
|
|
433
|
+
*/
|
|
434
|
+
export function htmlToMarkdown(html) {
|
|
435
|
+
if (!html || !html.trim()) {
|
|
436
|
+
return "";
|
|
437
|
+
}
|
|
438
|
+
return renderBlocks(parseHtml(html))
|
|
439
|
+
.replace(/[ \t]+$/gm, "")
|
|
440
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
441
|
+
.trim();
|
|
442
|
+
}
|
|
@@ -895,6 +895,9 @@ async function detectFileForUnifiedArray(file, options, provider, genericFileMax
|
|
|
895
895
|
"unknown",
|
|
896
896
|
],
|
|
897
897
|
csvOptions: options.csvOptions,
|
|
898
|
+
// Office options travel the same unified `files` path, so this is where a
|
|
899
|
+
// caller's sheet selection / format choice reaches the XLSX processor.
|
|
900
|
+
officeOptions: options.officeOptions,
|
|
898
901
|
// #478: videos arrive through this unified `files` path, so this is
|
|
899
902
|
// where the CLI's frame/quality/format request has to be handed on.
|
|
900
903
|
videoOptions: options.videoOptions
|
|
@@ -16,6 +16,7 @@ import type { StreamOptions } from "../types/index.js";
|
|
|
16
16
|
* - input.videoFiles: Video files (Buffer | string paths)
|
|
17
17
|
* - csvOptions: CSV parsing options
|
|
18
18
|
* - videoOptions: Video frame/quality/format/transcription options
|
|
19
|
+
* - officeOptions: DOCX/XLSX sheet selection and output-format options
|
|
19
20
|
* - systemPrompt: System-level instructions
|
|
20
21
|
* - conversationMessages: Chat history
|
|
21
22
|
* - temperature: Model temperature (0-1)
|
|
@@ -28,6 +29,7 @@ import type { StreamOptions } from "../types/index.js";
|
|
|
28
29
|
* @returns {object} Normalized options object with:
|
|
29
30
|
* - input: { text, images, content, files, csvFiles, pdfFiles, audioFiles, videoFiles }
|
|
30
31
|
* - csvOptions: CSV processing options
|
|
32
|
+
* - officeOptions: Office (DOCX/XLSX) processing options
|
|
31
33
|
* - systemPrompt: System prompt string
|
|
32
34
|
* - conversationHistory: Message history array
|
|
33
35
|
* - provider: Provider name
|
|
@@ -66,6 +68,7 @@ export declare function buildMultimodalOptions(options: StreamOptions, providerN
|
|
|
66
68
|
outputFormat?: import("../index.js").VisionImageOutputFormat;
|
|
67
69
|
} | undefined;
|
|
68
70
|
videoOptions: import("../index.js").VideoProcessorOptions | undefined;
|
|
71
|
+
officeOptions: import("../index.js").OfficeProcessorOptions | undefined;
|
|
69
72
|
systemPrompt: string | undefined;
|
|
70
73
|
conversationHistory: import("../index.js").ChatMessage[] | undefined;
|
|
71
74
|
provider: string;
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* - input.videoFiles: Video files (Buffer | string paths)
|
|
16
16
|
* - csvOptions: CSV parsing options
|
|
17
17
|
* - videoOptions: Video frame/quality/format/transcription options
|
|
18
|
+
* - officeOptions: DOCX/XLSX sheet selection and output-format options
|
|
18
19
|
* - systemPrompt: System-level instructions
|
|
19
20
|
* - conversationMessages: Chat history
|
|
20
21
|
* - temperature: Model temperature (0-1)
|
|
@@ -27,6 +28,7 @@
|
|
|
27
28
|
* @returns {object} Normalized options object with:
|
|
28
29
|
* - input: { text, images, content, files, csvFiles, pdfFiles, audioFiles, videoFiles }
|
|
29
30
|
* - csvOptions: CSV processing options
|
|
31
|
+
* - officeOptions: Office (DOCX/XLSX) processing options
|
|
30
32
|
* - systemPrompt: System prompt string
|
|
31
33
|
* - conversationHistory: Message history array
|
|
32
34
|
* - provider: Provider name
|
|
@@ -67,6 +69,10 @@ export function buildMultimodalOptions(options, providerName, modelName) {
|
|
|
67
69
|
// `StreamOptions` never reach VideoProcessor on the Bedrock branch, and
|
|
68
70
|
// it silently falls back to the duration-tier default.
|
|
69
71
|
videoOptions: options.videoOptions,
|
|
72
|
+
// Same #1259 whitelist-drop bug class as audioFiles/videoFiles above:
|
|
73
|
+
// omitted here, Bedrock's multimodal generate()/stream() path always got
|
|
74
|
+
// sheetName/formatStyle undefined regardless of what the caller passed.
|
|
75
|
+
officeOptions: options.officeOptions,
|
|
70
76
|
systemPrompt: options.systemPrompt,
|
|
71
77
|
conversationHistory: options.conversationMessages,
|
|
72
78
|
provider: providerName,
|
|
@@ -3960,7 +3960,7 @@
|
|
|
3960
3960
|
{"objectID":"534fbec3fbb29aa94fe464858c0906946aa7d909f9fa82dd2cc4c1f8d6821027","title":"Spans not being sent to Langfuse","url":"/docs/features/observability#spans-not-being-sent-to-langfuse","content":"Problem: Traces don't appear in Langfuse dashboard.\n\nSolution:\nVerify credentials are correct\nCheck health status:\nEnsure is called before process exit\nCheck network connectivity to Langfuse endpoint","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"Spans not being sent to Langfuse","lvl3":""}},
|
|
3961
3961
|
{"objectID":"bc1755900c005626bd213860a41c0169833cf8288e799f4cb83e4ee499aa9396","title":"API Reference","url":"/docs/features/observability#api-reference","content":"The following functions and types are exported from :\n\nFunctions:\n- Set context for Langfuse traces\n- Get current Langfuse context\n- Get OpenTelemetry tracer instance\n- Get span processors for external TracerProvider integration\n\nTypes:\n- Configuration options for Langfuse integration\n- GenAI semantic convention attributes","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"API Reference","lvl3":""}},
|
|
3962
3962
|
{"objectID":"9a2c99612604332f4b2bc32fb673fe19412249aba74ad41f2ed2cfd84a8d1185","title":"See Also","url":"/docs/features/observability#see-also","content":"Telemetry Guide - OpenTelemetry setup with Jaeger\nEnterprise Monitoring - Prometheus and Grafana setup\nAnalytics Reference - Token and cost tracking","hierarchy":{"lvl0":"Features","lvl1":"Observability Guide","lvl2":"See Also","lvl3":""}},
|
|
3963
|
-
{"objectID":"1b22ff57a6c6e2525e50e02118d6029f5ee3779ef701e9fde1b7b97d1a782760","title":"Office Documents Support","url":"/docs/features/office-documents","content":"Office Documents Support\n\nNeuroLink provides seamless Office document support as a multimodal input type - attach DOCX, PPTX, and XLSX documents directly to your AI prompts for document analysis, data extraction, and content processing.\n\nOverview\n\nOffice document support in NeuroLink works as a native multimodal input - the system automatically processes Office files and passes them to the AI provider's document understanding capabilities. The system:\nValidates Office files using magic byte detection and format verification\nChecks provider compatibility (Bedrock, Vertex AI, Anthropic)\nVerifies file size limits per provider\nPasses documents directly to the provider's native document API\nWorks with providers that support native Office document processing\n\nKey Difference from PDF: Similar to PDF files, Office documents are sent as binary documents to providers with native document support. This enables analysis of formatted text, tables, charts, and embedded content within Office files.\n\nSupported File Types\n\n| Format | Extension | MIME Type | Description |\n| --------------------- | --------- | --------------------------------------------------------------------------- | -------------------------------------------------- |\n| Word Document | | | Microsoft Word documents with text, images, tables |\n| PowerPoint | | | Presentations with slides, charts, images |\n| Excel Spreadsheet | | | Spreadsheets with data, formulas, charts |\n\nLegacy Formats:\n\n| Format | Extension | MIME Type | Support |\n| -------------- | --------- | -------------------------- | ------------------ |\n| Word (Legacy) | | | Provider-dependent |\n| Excel (Legacy) | | | Provider-dependent |\n\nQuick Start\n\nSDK Usage\n\nCLI Usage\n\nAPI Reference\n\nGenerateOptions\n\nStreamOptions\n\nOfficeProcessorOptions\n\nFile Input Formats\n\nProvider Support\n\nSupported Providers\n\n| Provider | Max Size | DOCX | PPTX | XLSX | DOC | XLS | Notes |\n| -------------------- | -------- | ---- | ---- | ---- | --- | --- | ------------------------------------ |\n| AWS Bedrock | 5 MB | ✅ | ✅ | ✅ | ✅ | ✅ | Full native support via Converse API |\n| Google Vertex AI | 5 MB | ✅ | ⚠️ | ✅ | ⚠️ | ⚠️ | Best for DOCX and XLSX |\n| Anthropic Claude | 5 MB | ✅ | ⚠️ | ✅ | ⚠️ | ⚠️ | Via document API |\n\nUnsupported Providers\n\nThe following providers do not currently support native Office document processing:\nOpenAI (GPT-4o)\nGoogle AI Studio\nAzure OpenAI\nOllama (local models)\nLiteLLM\nMistral AI\nHugging Face\n\nError Message for Unsupported Providers:\n\nProvider-Specific Features\n\nAWS Bedrock (Recommended)\n\nBedrock offers the most comprehensive Office document support via the Converse API:\n\nSupported Document Formats in Bedrock Converse API:\nOffice formats: , , , \nOther formats: , , , , \n\nGoogle Vertex AI\n\nAnthropic Claude\n\nFeatures\nAuto-Detection\n\nUse the array for automatic file type detection:\nMultiple Document Types\n\nProcess multiple Office documents in a single request:\nMixed Multimodal Inputs\n\nCombine Office documents with other file types:\n\nType Definitions\n\nOfficeFileType\n\nOfficeProcessingResult\n\nOfficeProviderConfig\n\nError Handling\n\nError Types\n\nError Handling Patterns\n\nMetadata Fields\n\nWhen processing Office documents, the following metadata is available:\n\n| Field | Type | Description |\n| ------------------- | ---------------- | -------------------------------- |\n| | | Detection confidence (0-100) |\n| | | File size in bytes |\n| | | Original filename |\n| | | Detected Office format |\n| | | Provider used for processing |\n| | | Estimated page/slide/sheet count |\n| | | Whether document contains images |\n| | | Whether document contains charts |\n\nAccessing Metadata\n\nBest Practices\nChoose the Right Provider\nOptimize File Size\nUse Streaming for Large Documents\nBe Specific in
|
|
3963
|
+
{"objectID":"1b22ff57a6c6e2525e50e02118d6029f5ee3779ef701e9fde1b7b97d1a782760","title":"Office Documents Support","url":"/docs/features/office-documents","content":"Office Documents Support\n\nNeuroLink provides seamless Office document support as a multimodal input type - attach DOCX, PPTX, and XLSX documents directly to your AI prompts for document analysis, data extraction, and content processing.\n\nOverview\n\nOffice document support in NeuroLink works as a native multimodal input - the system automatically processes Office files and passes them to the AI provider's document understanding capabilities. The system:\nValidates Office files using magic byte detection and format verification\nChecks provider compatibility (Bedrock, Vertex AI, Anthropic)\nVerifies file size limits per provider\nPasses documents directly to the provider's native document API\nWorks with providers that support native Office document processing\n\nKey Difference from PDF: Similar to PDF files, Office documents are sent as binary documents to providers with native document support. This enables analysis of formatted text, tables, charts, and embedded content within Office files.\n\nSupported File Types\n\n| Format | Extension | MIME Type | Description |\n| --------------------- | --------- | --------------------------------------------------------------------------- | -------------------------------------------------- |\n| Word Document | | | Microsoft Word documents with text, images, tables |\n| PowerPoint | | | Presentations with slides, charts, images |\n| Excel Spreadsheet | | | Spreadsheets with data, formulas, charts |\n\nLegacy Formats:\n\n| Format | Extension | MIME Type | Support |\n| -------------- | --------- | -------------------------- | ------------------ |\n| Word (Legacy) | | | Provider-dependent |\n| Excel (Legacy) | | | Provider-dependent |\n\nQuick Start\n\nSDK Usage\n\nCLI Usage\n\nAPI Reference\n\nGenerateOptions\n\nStreamOptions\n\nOfficeProcessorOptions\n\nOnly and are read by the XLSX pipeline today; the\nother fields are declared but not yet consumed.\n\nDOCX structure and metrics\n\nA is converted to Markdown before it reaches the model, so headings,\nordered and unordered lists, and tables survive as structure rather than being\nflattened into prose. Each document is prefixed with a metrics line:\n\nThis needs no configuration and applies to every provider, because the\nconversion happens while the prompt is built rather than in a provider adapter.\n\nSelecting and formatting spreadsheet data\n\nNaming a sheet that does not exist does not silently fall back to every sheet —\nthe prompt reports the miss and lists the available sheet names.\n\nFile Input Formats\n\nProvider Support\n\nSupported Providers\n\n| Provider | Max Size | DOCX | PPTX | XLSX | DOC | XLS | Notes |\n| -------------------- | -------- | ---- | ---- | ---- | --- | --- | ------------------------------------ |\n| AWS Bedrock | 5 MB | ✅ | ✅ | ✅ | ✅ | ✅ | Full native support via Converse API |\n| Google Vertex AI | 5 MB | ✅ | ⚠️ | ✅ | ⚠️ | ⚠️ | Best for DOCX and XLSX |\n| Anthropic Claude | 5 MB | ✅ | ⚠️ | ✅ | ⚠️ | ⚠️ | Via document API |\n\nUnsupported Providers\n\nThe following providers do not currently support native Office document processing:\nOpenAI (GPT-4o)\nGoogle AI Studio\nAzure OpenAI\nOllama (local models)\nLiteLLM\nMistral AI\nHugging Face\n\nError Message for Unsupported Providers:\n\nProvider-Specific Features\n\nAWS Bedrock (Recommended)\n\nBedrock offers the most comprehensive Office document support via the Converse API:\n\nSupported Document Formats in Bedrock Converse API:\nOffice formats: , , , \nOther formats: , , , , \n\nGoogle Vertex AI\n\nAnthropic Claude\n\nFeatures\nAuto-Detection\n\nUse the array for automatic file type detection:\nMultiple Document Types\n\nProcess multiple Office documents in a single request:\nMixed Multimodal Inputs\n\nCombine Office documents with other file types:\n\nType Definitions\n\nOfficeFileType\n\nOfficeProcessingResult\n\nOfficeProviderConfig\n\nError Handling\n\nError Types\n\nError Handling Patterns\n\nMetadata Fields\n\nWhen processing Office documents, the following metadata is available:\n\n| Field | Type | Description |\n| ------------------- | ---------------- | -------------------------------- |\n| | | Detection confidence (0-100) |\n| | | File size in bytes |\n| | | Original filename |\n| | | Detected Office format |\n| | | Provider used for processing |\n| | | Estimated page/slide/sheet count |\n| | | Whether document contains images |\n| | | Whether document contains charts |\n\nAccessing Metadata\n\nBest Practices\nChoose the Right Provider\nOptimize File Size\nUse Streaming for Large Documents\nBe Specific in ","hierarchy":{"lvl0":"Features","lvl1":"Office Documents Support","lvl2":"","lvl3":""}},
|
|
3964
3964
|
{"objectID":"20c3155d2c1f39db07b8ffb72304d589cb601d27d43fb6f6f81ec8e83b5af4f2","title":"Office Documents Support","url":"/docs/features/office-documents#office-documents-support","content":"NeuroLink provides seamless Office document support as a multimodal input type - attach DOCX, PPTX, and XLSX documents directly to your AI prompts for document analysis, data extraction, and content processing.","hierarchy":{"lvl0":"Features","lvl1":"Office Documents Support","lvl2":"Office Documents Support","lvl3":""}},
|
|
3965
3965
|
{"objectID":"89922c3be7ba989417dd4f9aa76cf3c0497898b83d430c80f5809846a01ebcab","title":"Overview","url":"/docs/features/office-documents#overview","content":"Office document support in NeuroLink works as a native multimodal input - the system automatically processes Office files and passes them to the AI provider's document understanding capabilities. The system:\nValidates Office files using magic byte detection and format verification\nChecks provider compatibility (Bedrock, Vertex AI, Anthropic)\nVerifies file size limits per provider\nPasses documents directly to the provider's native document API\nWorks with providers that support native Office document processing\n\nKey Difference from PDF: Similar to PDF files, Office documents are sent as binary documents to providers with native document support. This enables analysis of formatted text, tables, charts, and embedded content within Office files.","hierarchy":{"lvl0":"Features","lvl1":"Office Documents Support","lvl2":"Overview","lvl3":""}},
|
|
3966
3966
|
{"objectID":"0b9f9e62a508886292fe189b77c320798552f0b7ac01939ef46d03f3b78126a3","title":"Supported File Types","url":"/docs/features/office-documents#supported-file-types","content":"| Format | Extension | MIME Type | Description |\n| --------------------- | --------- | --------------------------------------------------------------------------- | -------------------------------------------------- |\n| Word Document | | | Microsoft Word documents with text, images, tables |\n| PowerPoint | | | Presentations with slides, charts, images |\n| Excel Spreadsheet | | | Spreadsheets with data, formulas, charts |\n\nLegacy Formats:\n\n| Format | Extension | MIME Type | Support |\n| -------------- | --------- | -------------------------- | ------------------ |\n| Word (Legacy) | | | Provider-dependent |\n| Excel (Legacy) | | | Provider-dependent |","hierarchy":{"lvl0":"Features","lvl1":"Office Documents Support","lvl2":"Supported File Types","lvl3":""}},
|
|
@@ -3972,6 +3972,10 @@
|
|
|
3972
3972
|
{"objectID":"f2a3292cd8b2c1e4fd2b00bc931836682736f6e8f99e379912f3d570c7791916","title":"Auto-detect file types","url":"/docs/features/office-documents#auto-detect-file-types","content":"neurolink generate \"Analyze all documents\" --file report.docx --file data.xlsx --provider bedrock","hierarchy":{"lvl0":"Features","lvl1":"Office Documents Support","lvl2":"Auto-detect file types","lvl3":""}},
|
|
3973
3973
|
{"objectID":"771290e410b0a74d826c5973de02a9a3064fa2b66543b83163fd06fc2be7b37d","title":"Stream mode with Office documents","url":"/docs/features/office-documents#stream-mode-with-office-documents","content":"neurolink stream \"Explain this document in detail\" --file document.docx --provider bedrock","hierarchy":{"lvl0":"Features","lvl1":"Office Documents Support","lvl2":"Stream mode with Office documents","lvl3":""}},
|
|
3974
3974
|
{"objectID":"52189df50a2aa3edc4c43df6d16b7390207ec0b8eaca625c0b90a45190f6f45c","title":"attachments are only available on generate and stream.","url":"/docs/features/office-documents#attachments-are-only-available-on-generate-and-stream","content":"`","hierarchy":{"lvl0":"Features","lvl1":"Office Documents Support","lvl2":"attachments are only available on generate and stream.","lvl3":""}},
|
|
3975
|
+
{"objectID":"beeb0b5e0c628bc0390e7579cc89c3dcb929894cffeb7a04bb8df96f6c8beb50","title":"OfficeProcessorOptions","url":"/docs/features/office-documents#officeprocessoroptions","content":"Only and are read by the XLSX pipeline today; the\nother fields are declared but not yet consumed.","hierarchy":{"lvl0":"Features","lvl1":"Office Documents Support","lvl2":"OfficeProcessorOptions","lvl3":""}},
|
|
3976
|
+
{"objectID":"67803d753a4af41df778c73ea9d4f3055339e3e9c2eaa77b082e5b95d0b5bff3","title":"DOCX structure and metrics","url":"/docs/features/office-documents#docx-structure-and-metrics","content":"A is converted to Markdown before it reaches the model, so headings,\nordered and unordered lists, and tables survive as structure rather than being\nflattened into prose. Each document is prefixed with a metrics line:\n\n`\nDocument: 42 words, 9 paragraphs, 268 characters","hierarchy":{"lvl0":"Features","lvl1":"Office Documents Support","lvl2":"DOCX structure and metrics","lvl3":""}},
|
|
3977
|
+
{"objectID":"e58480b8b9a182fd718393f62b8c0beaeb759f7d39ba9ca286a122ab959c66f1","title":"Adoption","url":"/docs/features/office-documents#adoption","content":"Merchant onboarding automated\nSettlement latency reduced\n\n| Region | Merchants | Volume |\n| --- | --- | --- |\n| APAC | 128 | 44200 |\n`\n\nThis needs no configuration and applies to every provider, because the\nconversion happens while the prompt is built rather than in a provider adapter.","hierarchy":{"lvl0":"Features","lvl1":"Office Documents Support","lvl2":"Adoption","lvl3":""}},
|
|
3978
|
+
{"objectID":"4db4bf0a8dc60ff49e53fb0ea30ad888573cf4b2fc31a8664bd6fab88bb05235","title":"Selecting and formatting spreadsheet data","url":"/docs/features/office-documents#selecting-and-formatting-spreadsheet-data","content":"Naming a sheet that does not exist does not silently fall back to every sheet —\nthe prompt reports the miss and lists the available sheet names.","hierarchy":{"lvl0":"Features","lvl1":"Office Documents Support","lvl2":"Selecting and formatting spreadsheet data","lvl3":""}},
|
|
3975
3979
|
{"objectID":"12b894726ee0b402bad0c3fd17ca5de929c1c2402385ae027effd650cc104429","title":"Supported Providers","url":"/docs/features/office-documents#supported-providers","content":"| Provider | Max Size | DOCX | PPTX | XLSX | DOC | XLS | Notes |\n| -------------------- | -------- | ---- | ---- | ---- | --- | --- | ------------------------------------ |\n| AWS Bedrock | 5 MB | ✅ | ✅ | ✅ | ✅ | ✅ | Full native support via Converse API |\n| Google Vertex AI | 5 MB | ✅ | ⚠️ | ✅ | ⚠️ | ⚠️ | Best for DOCX and XLSX |\n| Anthropic Claude | 5 MB | ✅ | ⚠️ | ✅ | ⚠️ | ⚠️ | Via document API |","hierarchy":{"lvl0":"Features","lvl1":"Office Documents Support","lvl2":"Supported Providers","lvl3":""}},
|
|
3976
3980
|
{"objectID":"05fac2c6f487c6f16a0adbb2535b4675b18732ba064cf3febdcc1539df29e9d8","title":"Unsupported Providers","url":"/docs/features/office-documents#unsupported-providers","content":"The following providers do not currently support native Office document processing:\nOpenAI (GPT-4o)\nGoogle AI Studio\nAzure OpenAI\nOllama (local models)\nLiteLLM\nMistral AI\nHugging Face\n\nError Message for Unsupported Providers:","hierarchy":{"lvl0":"Features","lvl1":"Office Documents Support","lvl2":"Unsupported Providers","lvl3":""}},
|
|
3977
3981
|
{"objectID":"abafed9b3d4d95d62c55f7c337d36384f6ae83e392f8c5640e412a4478ca8d3f","title":"AWS Bedrock (Recommended)","url":"/docs/features/office-documents#aws-bedrock-recommended","content":"Bedrock offers the most comprehensive Office document support via the Converse API:\n\nSupported Document Formats in Bedrock Converse API:\nOffice formats: , , , \nOther formats: , , , ,","hierarchy":{"lvl0":"Features","lvl1":"Office Documents Support","lvl2":"AWS Bedrock (Recommended)","lvl3":""}},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juspay/neurolink",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.31.0",
|
|
4
4
|
"packageManager": "pnpm@10.15.1",
|
|
5
5
|
"description": "The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002/call) for routing, tool selection and context budgeting. MCP-native (4 transports), voice TTS/STT/realtime, RAG, agents, memory, compaction, 9 observability exporters. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · TypeSafe Jev and more.",
|
|
6
6
|
"author": {
|
|
@@ -255,6 +255,7 @@
|
|
|
255
255
|
"test:mcp-result-cache": "pnpm exec tsx test/continuous-test-suite-mcp-result-cache.ts",
|
|
256
256
|
"test:mcp-breaker-resolved-errors": "pnpm exec tsx test/continuous-test-suite-mcp-breaker-resolved-errors.ts",
|
|
257
257
|
"test:archive:security": "pnpm exec tsx test/continuous-test-suite-archive-security.ts",
|
|
258
|
+
"test:office": "pnpm exec tsx test/continuous-test-suite-office.ts",
|
|
258
259
|
"test:office:security": "pnpm exec tsx test/continuous-test-suite-office-security.ts",
|
|
259
260
|
"test:vision-image-format": "pnpm exec tsx test/continuous-test-suite-vision-image-format.ts",
|
|
260
261
|
"test:image-exif": "pnpm exec tsx test/continuous-test-suite-image-exif.ts",
|