Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP feat(Mistral OCR Node): New node #14299

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
55 changes: 55 additions & 0 deletions packages/nodes-base/credentials/MistralOcrApi.credentials.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import type {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';

export class MistralOcrApi implements ICredentialType {
name = 'mistralOcrApi';

displayName = 'Mistral OCR';

documentationUrl = 'https://docs.mistral.ai/capabilities/document/';

properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
required: true,
default: '',
description: 'Your Mistral OCR API key. Get it from your Mistral account.',
},
{
displayName: 'Base URL',
name: 'baseUrl',
type: 'string',
default: 'https://api.mistral.ai',
description: 'The base URL for the Mistral OCR API.',
},
];

authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
Authorization: '=Bearer {{$credentials.apiKey}}',
},
},
};

test: ICredentialTestRequest = {
request: {
baseURL: '={{$credentials.baseUrl}}',
url: '/v1/models',
method: 'GET',
headers: {
Authorization: '=Bearer {{$credentials.apiKey}}',
'Content-Type': 'application/json',
},
json: true,
},
};
}
46 changes: 46 additions & 0 deletions packages/nodes-base/nodes/MistralOcr/GenericFunctions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import type {
IExecuteSingleFunctions,
IHttpRequestOptions,
IN8nHttpFullResponse,
INodeExecutionData,
JsonObject,
} from 'n8n-workflow';
import { NodeApiError } from 'n8n-workflow';

export async function sendErrorPostReceive(
this: IExecuteSingleFunctions,
_data: INodeExecutionData[],
response: IN8nHttpFullResponse,
): Promise<INodeExecutionData[]> {
if (response.statusCode && response.statusCode >= 400) {
const errorBody = response.body as JsonObject;

if (response.statusCode === 422) {
const errors = (errorBody.detail as any[])?.map(
(err) => `${err.loc?.join('.') || 'field'}: ${err.msg || 'Invalid value'}`,
) || ['Invalid request parameters'];

throw new NodeApiError(this.getNode(), errorBody, {
message: 'Validation Error',
description: errors.join('\n'),
});
}

const message = (errorBody.message || response.statusMessage) as string;
throw new NodeApiError(this.getNode(), errorBody, {
message: message || 'Unknown error occurred',
});
}
return [];
}

// ToDo: Remove before completing the pull request
export async function presendTest(
this: IExecuteSingleFunctions,

requestOptions: IHttpRequestOptions,
): Promise<IHttpRequestOptions> {
console.log('requestOptions', requestOptions);

return requestOptions;
}
18 changes: 18 additions & 0 deletions packages/nodes-base/nodes/MistralOcr/MistralOcr.node.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"node": "n8n-nodes-base.mistralOcr",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": ["Utility"],
"resources": {
"credentialDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/credentials/mistralOcr/"
}
],
"primaryDocumentation": [
{
"url": "https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-langchain.mistralOcr/"
}
]
}
}
52 changes: 52 additions & 0 deletions packages/nodes-base/nodes/MistralOcr/MistralOcr.node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import type { INodeType, INodeTypeDescription } from 'n8n-workflow';
import { NodeConnectionTypes } from 'n8n-workflow';

import { mistralOcrOperations, mistralOcrFields } from './descriptions/MistralOcrDescription';

export class MistralOcr implements INodeType {
description: INodeTypeDescription = {
displayName: 'Mistral OCR',
name: 'mistralOcr',
icon: {
light: 'file:mistralOcr.svg',
dark: 'file:mistralOcr.dark.svg',
},
group: ['transform'],
version: 1,
subtitle: '={{ $parameter["operation"] + ": " + $parameter["resource"] }}',
description: 'Extract text and layout information from documents using Mistral’s OCR API.',
defaults: {
name: 'Mistral OCR',
},
inputs: [NodeConnectionTypes.Main],
outputs: [NodeConnectionTypes.Main],
usableAsTool: true,
credentials: [
{
name: 'mistralOcrApi',
required: true,
},
],
requestDefaults: {
baseURL: '={{ $credentials.baseUrl }}',
ignoreHttpStatusErrors: true,
},
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Document',
value: 'document',
},
],
default: 'document',
},
...mistralOcrOperations,
...mistralOcrFields,
],
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import type { INodeProperties } from 'n8n-workflow';

import { presendTest, sendErrorPostReceive } from '../GenericFunctions';

export const mistralOcrOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['document'],
},
},
options: [
{
name: 'Extract Text',
value: 'extractText',
description: 'Extract text from documents using OCR',
action: 'Extract text',
routing: {
request: {
method: 'POST',
url: '/v1/ocr',
headers: {
'Content-Type': 'application/json',
},
},
output: {
postReceive: [sendErrorPostReceive],
},
},
},
],
default: 'extractText',
},
];

export const mistralOcrFields: INodeProperties[] = [
{
displayName: 'Model',
name: 'model',
type: 'options',
options: [{ name: 'mistral-ocr-latest', value: 'mistral-ocr-latest' }],
description: 'The OCR model to use',
required: true,
default: 'mistral-ocr-latest',
routing: {
send: {
type: 'body',
property: 'model',
preSend: [presendTest],
},
},
},
{
displayName: 'Input Type',
name: 'inputType',
type: 'options',
options: [
{ name: 'Binary Data', value: 'binary' },
{ name: 'URL', value: 'url' },
],
description: 'How the document will be provided',
required: true,
default: 'binary',
routing: {
send: {
type: 'body',
property: 'document.type',
value: '={{ $value === "binary" ? "image_data" : "document_url" }}',
},
},
},
{
displayName: 'Binary Property',
name: 'binaryProperty',
type: 'string',
description: 'Name of the binary property that contains the file to process',
placeholder: 'e.g. data',
hint: 'Uploaded document files must not exceed 50 MB in size and should be no longer than 1,000 pages.',
required: true,
default: 'data',
displayOptions: {
show: {
inputType: ['binary'],
},
},
routing: {
send: {
type: 'body',
property: 'document.data',
value:
'={{ { data: $input.binary[binaryProperty].data, mime_type: $input.binary[binaryProperty].mimeType } }}',
},
},
},
{
displayName: 'Document URL',
name: 'documentUrl',
type: 'string',
description: 'URL of the document to process',
placeholder: 'e.g. https://example.com/document.pdf',
required: true,
default: '',
displayOptions: {
show: {
inputType: ['url'],
},
},
routing: {
send: {
type: 'body',
property: 'document.document_url',
},
},
},
{
displayName: 'Enable Batch Processing',
name: 'enableBatchProcessing',
type: 'boolean',
description: 'Whether to process multiple documents in a single API call (more cost-efficient)',
default: false,
routing: {
send: {
type: 'body',
property: 'batch',
value: '={{ $value ? { size: $parameter.batchSize } : undefined }}',
},
},
},
{
displayName: 'Batch Size',
name: 'batchSize',
type: 'number',
description: 'Maximum number of documents to process in a single batch',
default: 25,
typeOptions: {
minValue: 1,
maxValue: 100,
},
required: true,
displayOptions: {
show: {
enableBatchProcessing: [true],
},
},
routing: {
send: {
type: 'body',
property: 'batch.size',
},
},
},
];
1 change: 1 addition & 0 deletions packages/nodes-base/nodes/MistralOcr/mistalOcr.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions packages/nodes-base/package.json
Original file line number Diff line number Diff line change
@@ -240,6 +240,7 @@
"dist/credentials/MiroOAuth2Api.credentials.js",
"dist/credentials/MispApi.credentials.js",
"dist/credentials/MistApi.credentials.js",
"dist/credentials/MistralOcrApi.credentials.js",
"dist/credentials/MoceanApi.credentials.js",
"dist/credentials/MondayComApi.credentials.js",
"dist/credentials/MondayComOAuth2Api.credentials.js",
@@ -649,6 +650,7 @@
"dist/nodes/Microsoft/ToDo/MicrosoftToDo.node.js",
"dist/nodes/Mindee/Mindee.node.js",
"dist/nodes/Misp/Misp.node.js",
"dist/nodes/MistralOcr/MistralOcr.node.js",
"dist/nodes/Mocean/Mocean.node.js",
"dist/nodes/MondayCom/MondayCom.node.js",
"dist/nodes/MongoDb/MongoDb.node.js",