Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ let response = veryfi_client.process_document_from_url(url, null, categories, fa
});
```

Thinking Mode reviews receipt and invoice extraction before the response is returned. Pass `light` or `deep`, and leave it out for the standard pipeline. It has to be enabled on the account. See [Process a Document](https://docs.veryfi.com/api/receipts-invoices/process-a-document/).

```js
let response = veryfi_client.process_document(file_path, categories, false, {
thinking: 'light'
}).then(response => {
console.log(response)
});
```

### Line items, tags, and other products

```js
Expand Down
2 changes: 1 addition & 1 deletion docs/api-coverage-analysis.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ Kwargs that merge into the **body** are marked **kwargs (body)**. Kwargs passed
| all variants | `compute` | `boolean \| null` | no (default `true`) | kwargs (body) | JSDoc | [docs](https://docs.veryfi.com/api/receipts-invoices/process-a-document/) |
| all variants | `country` | `string \| null` | no | kwargs (body) | JSDoc | [docs](https://docs.veryfi.com/api/receipts-invoices/process-a-document/) |
| all variants | `document_type` | `string \| null` | no | kwargs (body) | JSDoc | [docs](https://docs.veryfi.com/api/receipts-invoices/process-a-document/) |
| all variants | `thinking`, `thinking_llm_version` | `string \| null` | no | kwargs (body) | JSDoc | [docs](https://docs.veryfi.com/api/receipts-invoices/process-a-document/) |
| all variants | `thinking` | `"light" \| "deep" \| null` | no | kwargs (body); omitted when null | None | [docs](https://docs.veryfi.com/api/receipts-invoices/process-a-document/) |
| all variants | `allowed_async_enrichments` | `DocumentEnrichment[]` | no | kwargs (body); TS type too narrow | Widen `VeryfiExtraArgs` | [docs](https://docs.veryfi.com/api/receipts-invoices/process-a-document/) |
| all variants | `device_data` | object | no | kwargs (body); TS type too narrow | Widen `VeryfiExtraArgs` | [docs](https://docs.veryfi.com/api/receipts-invoices/process-a-document/) |
| all variants | `emailed_receipt_id`, `receipt_id` | `integer \| null` | no | kwargs (body) | JSDoc | [docs](https://docs.veryfi.com/api/receipts-invoices/process-a-document/) |
Expand Down
20 changes: 20 additions & 0 deletions lib/documents/applyDocumentProcessOptions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Merge extra Process a Document options into the request body.
* `thinking` is included only when the caller sets it. Omit it, or pass null,
* to use the standard pipeline. Accepted values are `light` and `deep`.
* https://docs.veryfi.com/api/receipts-invoices/process-a-document/
* https://faq.veryfi.com/en/articles/18356750-what-is-thinking-mode-and-how-do-i-use-it
*
* @param {Object} requestArguments Body fields already set by the caller
* @param {Object} [kwargs] Additional request parameters
* @returns {Object} Request body
*/
function applyDocumentProcessOptions(requestArguments, kwargs = {}) {
const options = Object.assign({}, kwargs);
if (options.thinking == null) {
delete options.thinking;
}
return Object.assign(requestArguments, options);
}

module.exports = applyDocumentProcessOptions;
1 change: 1 addition & 0 deletions lib/documents/processDocument.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const fs = require('fs');
* @param {Array} categories List of categories Veryfi can use to categorize the document
* @param {Boolean} auto_delete Delete this document from Veryfi after data has been extracted
* @param {Object} kwargs Additional request parameters
* @param {("light"|"deep")|null} [kwargs.thinking] Thinking Mode for receipts and invoices. `light` runs a smaller agent review, `deep` runs the most thorough review. Omit it to use the standard pipeline. https://docs.veryfi.com/api/receipts-invoices/process-a-document/
* @returns {JSON} Data extracted from the document
*/
Client.prototype.process_document = async function (
Expand Down
4 changes: 3 additions & 1 deletion lib/documents/processDocumentBase64.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const Client = require('../client/constructor');
const applyDocumentProcessOptions = require('./applyDocumentProcessOptions');
/**
* Process a document and extract all the fields from it. https://docs.veryfi.com/api/receipts-invoices/process-a-document/
* @example
Expand All @@ -14,6 +15,7 @@ const Client = require('../client/constructor');
* @param {Array} categories List of categories Veryfi can use to categorize the document
* @param {Boolean} auto_delete Delete this document from Veryfi after data has been extracted
* @param {Object} kwargs Additional request parameters
* @param {("light"|"deep")|null} [kwargs.thinking] Thinking Mode for receipts and invoices. `light` runs a smaller agent review, `deep` runs the most thorough review. Omit it to use the standard pipeline. https://docs.veryfi.com/api/receipts-invoices/process-a-document/
* @returns {JSON} Data extracted from the document
*/
Client.prototype.process_document_from_base64 = async function (
Expand All @@ -31,7 +33,7 @@ Client.prototype.process_document_from_base64 = async function (
"categories": categories,
"auto_delete": auto_delete,
};
request_arguments = Object.assign(request_arguments, kwargs);
request_arguments = applyDocumentProcessOptions(request_arguments, kwargs);
let document = await this._request("POST", endpoint_name, request_arguments, null, false);
return document['data'];
}
6 changes: 4 additions & 2 deletions lib/documents/processDocumentStream.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const Client = require('../client/constructor');
const applyDocumentProcessOptions = require('./applyDocumentProcessOptions');
/**
* Process a document and extract all the fields from it. https://docs.veryfi.com/api/receipts-invoices/process-a-document/
* @example
Expand All @@ -12,7 +13,8 @@ const Client = require('../client/constructor');
* @param {stream.Readable} file ReadStream of a file to submit for data extraction
* @param {String} file_name The file name including the extension
* @param {Boolean} auto_delete Delete this document from Veryfi after data has been extracted
* @param {Object} kwargs Additional request parameters (categories, tags, async, parse_address, crop_document, compute, country, document_type, device_data, package_path, bucket, bounding_boxes, confidence_details)
* @param {Object} kwargs Additional request parameters (categories, tags, async, parse_address, crop_document, compute, country, document_type, device_data, package_path, bucket, bounding_boxes, confidence_details, thinking)
* @param {("light"|"deep")|null} [kwargs.thinking] Thinking Mode for receipts and invoices. `light` runs a smaller agent review, `deep` runs the most thorough review. Omit it to use the standard pipeline. https://docs.veryfi.com/api/receipts-invoices/process-a-document/
* @returns {JSON} Data extracted from the document
*/
Client.prototype.process_document_from_stream = async function (
Expand All @@ -27,7 +29,7 @@ Client.prototype.process_document_from_stream = async function (
"file_name": file_name,
"auto_delete": auto_delete
};
request_arguments = Object.assign(request_arguments, kwargs);
request_arguments = applyDocumentProcessOptions(request_arguments, kwargs);
let document = await this._request("POST", endpoint_name, request_arguments, null, true);
return document['data'];
}
6 changes: 4 additions & 2 deletions lib/documents/processDocumentUrl.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const Client = require('../client/constructor');
const applyDocumentProcessOptions = require('./applyDocumentProcessOptions');
/**
* Process document from url and extract all the fields from it. https://docs.veryfi.com/api/receipts-invoices/process-a-document/
* @example
Expand All @@ -12,7 +13,8 @@ const Client = require('../client/constructor');
* @param {boolean} boost_mode Flag that tells Veryfi whether boost mode should be enabled. When set to 1, Veryfi will skip data enrichment steps, but will process the document faster. Default value for this flag is 0
* @param {string|null} external_id Optional custom document identifier. Use this if you would like to assign your own ID to documents
* @param {int} max_pages_to_process When sending a long document to Veryfi for processing, this parameter controls how many pages of the document will be read and processed, starting from page 1.
* @param {Object} kwargs Additional request parameters (async, tags, parse_address, crop_document, compute, country, document_type, device_data, package_path, bucket, bounding_boxes, confidence_details, thinking, thinking_llm_version, allowed_async_enrichments, emailed_receipt_id, receipt_id)
* @param {Object} kwargs Additional request parameters (async, tags, parse_address, crop_document, compute, country, document_type, device_data, package_path, bucket, bounding_boxes, confidence_details, thinking, allowed_async_enrichments, emailed_receipt_id, receipt_id)
* @param {("light"|"deep")|null} [kwargs.thinking] Thinking Mode for receipts and invoices. `light` runs a smaller agent review, `deep` runs the most thorough review. Omit it to use the standard pipeline. https://docs.veryfi.com/api/receipts-invoices/process-a-document/
* @return {JSON} Data extracted from the document.
*/
Client.prototype.process_document_from_url = async function (
Expand All @@ -35,7 +37,7 @@ Client.prototype.process_document_from_url = async function (
"file_urls": file_urls,
"max_pages_to_process": max_pages_to_process,
};
request_arguments = Object.assign(request_arguments, kwargs);
request_arguments = applyDocumentProcessOptions(request_arguments, kwargs);
let response = await this._request("POST", endpoint_name, request_arguments, null, false);
return response['data'];
}
20 changes: 11 additions & 9 deletions lib/types/Client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {VeryfiDocument} from "./VeryfiDocument";
import {JsonObject, VeryfiExtraArgs} from "./VeryfiExtraArgs";
import {DocumentProcessArgs, JsonObject, VeryfiExtraArgs} from "./VeryfiExtraArgs";

export type {DocumentProcessArgs, ThinkingMode} from "./VeryfiExtraArgs";
import {Tag} from "./Tag";
import * as stream from "node:stream";

Expand Down Expand Up @@ -187,14 +189,14 @@ export declare class Client {
* @param {string} file_path Path on disk to a file to submit for data extraction
* @param {string[]} categories List of categories Veryfi can use to categorize the document
* @param {boolean} auto_delete Delete this document from Veryfi after data has been extracted
* @param {VeryfiExtraArgs} kwargs Additional request parameters
* @param {DocumentProcessArgs} kwargs Additional request parameters. `thinking` is `light` or `deep` for Thinking Mode on receipts and invoices. Omit it for the standard pipeline. https://docs.veryfi.com/api/receipts-invoices/process-a-document/
* @returns {Promise<VeryfiDocument>} Object of data extracted from the document
*/
public process_document(
file_path: string,
categories?: string[],
auto_delete?: boolean,
{...kwargs}?: VeryfiExtraArgs
{...kwargs}?: DocumentProcessArgs
): Promise<VeryfiDocument>;

/**
Expand All @@ -211,15 +213,15 @@ export declare class Client {
* @param {String} file_name The file name including the extension
* @param {Array} categories List of categories Veryfi can use to categorize the document
* @param {Boolean} auto_delete Delete this document from Veryfi after data has been extracted
* @param {Object} kwargs Additional request parameters
* @param {DocumentProcessArgs} kwargs Additional request parameters. `thinking` is `light` or `deep` for Thinking Mode on receipts and invoices. Omit it for the standard pipeline. https://docs.veryfi.com/api/receipts-invoices/process-a-document/
* @returns {JSON} Data extracted from the document
*/
public process_document_from_base64(
base64_encoded_string: string,
file_name: string,
categories?: string[],
auto_delete?: boolean,
{...kwargs}?: VeryfiExtraArgs
{...kwargs}?: DocumentProcessArgs
): Promise<VeryfiDocument>;

/**
Expand All @@ -229,14 +231,14 @@ export declare class Client {
* @param {stream.Readable} file ReadStream of a file to submit for data extraction
* @param {String} file_name The file name including the extension
* @param {Boolean} auto_delete Delete this document from Veryfi after data has been extracted
* @param {Object} kwargs Additional request parameters
* @param {DocumentProcessArgs} kwargs Additional request parameters. `thinking` is `light` or `deep` for Thinking Mode on receipts and invoices. Omit it for the standard pipeline. https://docs.veryfi.com/api/receipts-invoices/process-a-document/
* @returns {JSON} Data extracted from the document
*/
public process_document_from_stream(
file: stream.Readable,
file_name: string,
auto_delete?: boolean,
{...kwargs}?: VeryfiExtraArgs
{...kwargs}?: DocumentProcessArgs
): Promise<VeryfiDocument>;

/**
Expand All @@ -249,7 +251,7 @@ export declare class Client {
* @param {boolean} boost_mode Flag that tells Veryfi whether boost mode should be enabled. When set to 1, Veryfi will skip data enrichment steps, but will process the document faster. Default value for this flag is 0
* @param {string} external_id Optional custom document identifier. Use this if you would like to assign your own ID to documents
* @param {number} max_pages_to_process When sending a long document to Veryfi for processing, this parameter controls how many pages of the document will be read and processed, starting from page 1.
* @param {VeryfiExtraArgs} kwargs Additional request parameters
* @param {DocumentProcessArgs} kwargs Additional request parameters. `thinking` is `light` or `deep` for Thinking Mode on receipts and invoices. Omit it for the standard pipeline. https://docs.veryfi.com/api/receipts-invoices/process-a-document/
* @return {Promise<VeryfiDocument>} Object of data extracted from the document
*/
public process_document_from_url(
Expand All @@ -260,7 +262,7 @@ export declare class Client {
boost_mode?: boolean,
external_id?: string,
max_pages_to_process?: number,
{...kwargs}?: VeryfiExtraArgs
{...kwargs}?: DocumentProcessArgs
): Promise<VeryfiDocument>;

/**
Expand Down
19 changes: 19 additions & 0 deletions lib/types/VeryfiExtraArgs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,23 @@
*/
export declare type VeryfiExtraArgs = Record<string | number | symbol, any>;

/**
* Thinking Mode for receipt and invoice extraction on
* POST /api/v8/partner/documents.
* `light` runs a smaller agent review. `deep` runs the most thorough review.
* Omit the field to use the standard real-time pipeline.
* https://docs.veryfi.com/api/receipts-invoices/process-a-document/
* https://faq.veryfi.com/en/articles/18356750-what-is-thinking-mode-and-how-do-i-use-it
*/
export type ThinkingMode = "light" | "deep";

/**
* Extra body fields for Process a Document.
* https://docs.veryfi.com/api/receipts-invoices/process-a-document/
*/
export interface DocumentProcessArgs {
thinking?: ThinkingMode | null;
[key: string]: any;
}

export type JsonObject = Record<string, any>;
56 changes: 56 additions & 0 deletions test/api-coverage.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,62 @@ describe('API coverage call contracts', () => {
expect(mock.mock.calls[0][2]).toEqual(expect.objectContaining({"categories":["Food"]}));
});

test("process_document forwards thinking light", async () => {
const mock = mockRequest();
await client.process_document('resources/receipt.png', ['Food'], false, {thinking: 'light'});
expect(mock.mock.calls[0][0]).toBe("POST");
expect(mock.mock.calls[0][1]).toBe("/documents/");
expect(mock.mock.calls[0][4]).toBe(true);
expect(mock.mock.calls[0][2]).toEqual(expect.objectContaining({
"categories": ["Food"],
"thinking": "light"
}));
});

test("process_document_from_stream forwards thinking deep", async () => {
const mock = mockRequest();
const {Readable} = require('stream');
await client.process_document_from_stream(Readable.from(Buffer.from('pdf')), 'invoice.pdf', false, {thinking: 'deep'});
expect(mock.mock.calls[0][1]).toBe("/documents/");
expect(mock.mock.calls[0][4]).toBe(true);
expect(mock.mock.calls[0][2]).toEqual(expect.objectContaining({"thinking": "deep", "file_name": "invoice.pdf"}));
});

test("process_document_from_base64 forwards thinking", async () => {
const mock = mockRequest();
await client.process_document_from_base64('abc123', 'invoice.pdf', null, false, {thinking: 'light'});
expect(mock.mock.calls[0][1]).toBe("/documents/");
expect(mock.mock.calls[0][4]).toBe(false);
expect(mock.mock.calls[0][2]).toEqual(expect.objectContaining({
"file_data": "abc123",
"thinking": "light"
}));
});

test("process_document_from_url forwards thinking and omits null", async () => {
const mock = mockRequest();
await client.process_document_from_url(
'https://example.com/invoice.pdf',
null,
null,
false,
false,
null,
1,
{thinking: 'deep', tags: ['reviewed']}
);
expect(mock.mock.calls[0][1]).toBe("/documents/");
expect(mock.mock.calls[0][4]).toBe(false);
expect(mock.mock.calls[0][2]).toEqual(expect.objectContaining({
"file_url": "https://example.com/invoice.pdf",
"thinking": "deep",
"tags": ["reviewed"]
}));

await client.process_document_from_url('https://example.com/invoice.pdf', null, null, false, false, null, 1, {thinking: null});
expect(mock.mock.calls[1][2]).not.toHaveProperty('thinking');
});

test("add_any_document_tag PUTs {name}", async () => {
const mock = mockRequest();
await client.add_any_document_tag('11', 'urgent');
Expand Down
Loading