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
3 changes: 3 additions & 0 deletions src/parsing/localResponseBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ export abstract class LocalResponseBase {
/**
* @param inputFile - The input file, which can be a Buffer, string, or PathLike.
*/
if (this.initialized) {
return;
}
if (Buffer.isBuffer(this.inputHandle)) {
this.file = this.inputHandle;
} else if (typeof this.inputHandle === "string") {
Expand Down
2 changes: 1 addition & 1 deletion src/v2/parsing/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ export {
export { LocalResponse } from "./localResponse.js";
export { BaseResponse } from "./baseResponse.js";
export type { ResponseConstructor } from "./baseResponse.js";
export { RawText, RagMetadata } from "./inference/field/index.js";
export * as field from "./inference/field/index.js";
2 changes: 1 addition & 1 deletion src/v2/parsing/inference/field/fieldLocation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class FieldLocation {
readonly page: number | undefined;

constructor(serverResponse: StringDict) {
this.polygon = serverResponse["polygon"] as Polygon;
this.polygon = "polygon" in serverResponse ? new Polygon(...serverResponse["polygon"]) : null;
this.page = "page" in serverResponse ? serverResponse["page"] : undefined;
}

Expand Down
5 changes: 3 additions & 2 deletions src/v2/parsing/localResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ export class LocalResponse extends LocalResponseBase {
): Promise<ResponseT> {
try {
return new responseClass(await this.asDict());
} catch {
throw new MindeeError("Invalid response provided.");
} catch (error) {
console.error(error);
throw new MindeeError(`Invalid response provided: ${error}`);
}
}
}
2 changes: 1 addition & 1 deletion src/v2/product/crop/cropItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export class CropItem {
location: FieldLocation;

constructor(serverResponse: StringDict) {
this.objectType = serverResponse["objectType"];
this.objectType = serverResponse["object_type"];
this.location = new FieldLocation(serverResponse["location"]);
}

Expand Down
17 changes: 13 additions & 4 deletions src/v2/product/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
export { Classification } from "./classification/index.js";
export { Crop } from "./crop/index.js";
export { Classification, ClassificationResponse } from "./classification/index.js";
export * as classification from "./classification/index.js";

export { Crop, CropResponse } from "./crop/index.js";
export * as crop from "./crop/index.js";

export { Extraction, ExtractionResponse } from "./extraction/index.js";
export { Ocr } from "./ocr/index.js";
export { Split } from "./split/index.js";
export * as extraction from "./extraction/index.js";

export { Ocr, OcrResponse } from "./ocr/index.js";
export * as ocr from "./ocr/index.js";

export { Split, SplitResponse } from "./split/index.js";
export * as split from "./split/index.js";
3 changes: 3 additions & 0 deletions src/v2/product/ocr/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ export { Ocr } from "./ocr.js";
export { OcrParameters } from "./ocrParameters.js";
export { OcrResponse } from "./ocrResponse.js";
export { OcrInference } from "./ocrInference.js";
export { OcrResult } from "./ocrResult.js";
export { OcrPage } from "./ocrPage.js";
export { OcrWord } from "./ocrWord.js";
2 changes: 1 addition & 1 deletion src/v2/product/ocr/ocrParameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ import { logger } from "@/logger.js";
export class OcrParameters extends BaseParameters {
constructor(params: BaseParametersConstructor & {}) {
super({ ...params });
logger.debug("Ocr parameters initialized.");
logger.debug("OCR parameters initialized.");
}
}
2 changes: 1 addition & 1 deletion src/v2/product/ocr/ocrWord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export class OcrWord {

constructor(serverResponse: StringDict) {
this.content = serverResponse["content"];
this.polygon = new Polygon(serverResponse["polygon"]);
this.polygon = new Polygon(...serverResponse["polygon"]);
}

toString(): string {
Expand Down
18 changes: 18 additions & 0 deletions tests/v2/product/classification.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { expect } from "chai";
import path from "node:path";
import { ClassificationResponse } from "@/v2/product/index.js";

import { V2_PRODUCT_PATH } from "../../index.js";
import { loadV2Response } from "./utils.js";


describe("MindeeV2 - Classification Response", async () => {
it("should load a single result", async () => {
const response = await loadV2Response(
ClassificationResponse,
path.join(V2_PRODUCT_PATH, "classification", "classification_single.json")
);
const classification = response.inference.result.classification;
expect(classification.documentType).to.equal("invoice");
});
});
92 changes: 92 additions & 0 deletions tests/v2/product/crop.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { expect } from "chai";
import path from "node:path";
import { Polygon } from "@/geometry/index.js";
import { crop } from "@/v2/product/index.js";

import { V2_PRODUCT_PATH } from "../../index.js";
import { loadV2Response } from "./utils.js";

describe("MindeeV2 - Crop Response", async () => {
it("should load a single result", async () => {
const response = await loadV2Response(
crop.CropResponse,
path.join(V2_PRODUCT_PATH, "crop", "crop_single.json")
);
// Validate inference metadata
expect(response.inference.id).to.equal("12345678-1234-1234-1234-123456789abc");
expect(response.inference.model.id).to.equal("test-model-id");

// Validate file metadata
expect(response.inference.file.name).to.equal("sample.jpeg");
expect(response.inference.file.pageCount).to.equal(1);
expect(response.inference.file.mimeType).to.equal("image/jpeg");

// Validate crops
const crops: crop.CropItem[] = response.inference.result.crops;
expect(crops).to.be.an("array").that.has.lengthOf(1);

const firstCrop = crops[0];
expect(firstCrop.objectType).to.equal("invoice");
expect(firstCrop.location.page).to.equal(0);

const polygon: Polygon = firstCrop.location.polygon!;
expect(polygon.length).to.equal(4);
expect(polygon.length).to.equal(4);
expect(polygon[0][0]).to.equal(0.15);
expect(polygon[0][1]).to.equal(0.254);
expect(polygon[1][0]).to.equal(0.85);
expect(polygon[1][1]).to.equal(0.254);
expect(polygon[2][0]).to.equal(0.85);
expect(polygon[2][1]).to.equal(0.947);
expect(polygon[3][0]).to.equal(0.15);
expect(polygon[3][1]).to.equal(0.947);
});

it("should load multiple results", async () => {
const response = await loadV2Response(
crop.CropResponse,
path.join(V2_PRODUCT_PATH, "crop", "crop_multiple.json")
);
// Validate inference metadata
expect(response.inference.id).to.equal("12345678-1234-1234-1234-123456789abc");
expect(response.inference.model.id).to.equal("test-model-id");

// Validate file metadata
expect(response.inference.file.name).to.equal("default_sample.jpg");
expect(response.inference.file.pageCount).to.equal(1);
expect(response.inference.file.mimeType).to.equal("image/jpeg");

const crops: crop.CropItem[] = response.inference.result.crops;
expect(crops).to.be.an("array").that.has.lengthOf(2);

// Validate first crop item
const firstCrop: crop.CropItem = crops[0];
expect(firstCrop.objectType).to.equal("invoice");
expect(firstCrop.location.page).to.equal(0);
const firstPolygon: Polygon = firstCrop.location.polygon!;
expect(firstPolygon.length).to.equal(4);
expect(firstPolygon[0][0]).to.equal(0.214);
expect(firstPolygon[0][1]).to.equal(0.079);
expect(firstPolygon[1][0]).to.equal(0.476);
expect(firstPolygon[1][1]).to.equal(0.079);
expect(firstPolygon[2][0]).to.equal(0.476);
expect(firstPolygon[2][1]).to.equal(0.979);
expect(firstPolygon[3][0]).to.equal(0.214);
expect(firstPolygon[3][1]).to.equal(0.979);

// Validate second crop item
const secondCrop: crop.CropItem = crops[1];
expect(secondCrop.objectType).to.equal("invoice");
expect(secondCrop.location.page).to.equal(0);
const secondPolygon: Polygon = secondCrop.location.polygon!;
expect(secondPolygon.length).to.equal(4);
expect(secondPolygon[0][0]).to.equal(0.547);
expect(secondPolygon[0][1]).to.equal(0.15);
expect(secondPolygon[1][0]).to.equal(0.862);
expect(secondPolygon[1][1]).to.equal(0.15);
expect(secondPolygon[2][0]).to.equal(0.862);
expect(secondPolygon[2][1]).to.equal(0.97);
expect(secondPolygon[3][0]).to.equal(0.547);
expect(secondPolygon[3][1]).to.equal(0.97);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,24 @@ import {
ObjectField,
SimpleField,
} from "@/v2/parsing/inference/field/index.js";
import {
LocalResponse,
RagMetadata,
RawText,
} from "@/v2/parsing/index.js";
import { V2_PRODUCT_PATH } from "../../index.js";
import { field } from "@/v2/parsing/index.js";
import { ExtractionResponse } from "@/v2/product/index.js";

import { V2_PRODUCT_PATH } from "../../index.js";
import { loadV2Response } from "./utils.js";

const findocPath = path.join(V2_PRODUCT_PATH, "extraction", "financial_document");
const extractionPath = path.join(V2_PRODUCT_PATH, "extraction");
const deepNestedFieldPath = path.join(extractionPath, "deep_nested_fields.json");
const standardFieldPath = path.join(extractionPath, "standard_field_types.json");
const standardFieldRstPath = path.join(extractionPath, "standard_field_types.rst");
const locationFieldPath = path.join(findocPath, "complete_with_coordinates.json");

async function loadV2Extraction(resourcePath: string): Promise<ExtractionResponse> {
const localResponse = new LocalResponse(resourcePath);
await localResponse.init();
return localResponse.deserializeResponse(ExtractionResponse);
}

describe("MindeeV2 - Extraction Response", async () => {
describe("Financial Document", async () => {
it("should load a blank inference with valid properties", async () => {
const response = await loadV2Extraction(
const response = await loadV2Response(
ExtractionResponse,
path.join(findocPath, "blank.json")
);
const fields = response.inference.result.fields;
Expand Down Expand Up @@ -64,7 +57,8 @@ describe("MindeeV2 - Extraction Response", async () => {
});

it("should load a complete inference with valid properties", async () => {
const response = await loadV2Extraction(
const response = await loadV2Response(
ExtractionResponse,
path.join(findocPath, "complete.json")
);
const inference = response.inference;
Expand Down Expand Up @@ -129,7 +123,9 @@ describe("MindeeV2 - Extraction Response", async () => {

describe("Deeply Nested", async () => {
it("should load a deep nested object", async () => {
const response = await loadV2Extraction(deepNestedFieldPath);
const response = await loadV2Response(
ExtractionResponse, deepNestedFieldPath
);
const fields = response.inference.result.fields;
expect(fields.get("field_simple")).to.be.an.instanceof(SimpleField);
expect(fields.get("field_object")).to.be.an.instanceof(ObjectField);
Expand Down Expand Up @@ -165,7 +161,9 @@ describe("MindeeV2 - Extraction Response", async () => {

describe("Standard Field Types", async () => {
it("should recognize simple fields", async () => {
const response = await loadV2Extraction(standardFieldPath);
const response = await loadV2Response(
ExtractionResponse, standardFieldPath
);
const fields = response.inference.result.fields;

expect(fields.get("field_simple_string")).to.be.instanceOf(SimpleField);
Expand Down Expand Up @@ -211,7 +209,9 @@ describe("MindeeV2 - Extraction Response", async () => {
});

it("should recognize simple list fields", async () => {
const response = await loadV2Extraction(standardFieldPath);
const response = await loadV2Response(
ExtractionResponse, standardFieldPath
);
const fields = response.inference.result.fields;

expect(fields.get("field_simple_list")).to.be.instanceOf(ListField);
Expand All @@ -226,7 +226,9 @@ describe("MindeeV2 - Extraction Response", async () => {
});

it("should recognize object fields", async () => {
const response = await loadV2Extraction(standardFieldPath);
const response = await loadV2Response(
ExtractionResponse, standardFieldPath
);
const fields = response.inference.result.fields;

expect(fields.get("field_object")).to.be.instanceOf(ObjectField);
Expand All @@ -246,7 +248,9 @@ describe("MindeeV2 - Extraction Response", async () => {
});

it("should recognize object list fields", async () => {
const response = await loadV2Extraction(standardFieldPath);
const response = await loadV2Response(
ExtractionResponse, standardFieldPath
);
const fields = response.inference.result.fields;

expect(fields.get("field_object_list")).to.be.instanceOf(ListField);
Expand All @@ -272,13 +276,13 @@ describe("MindeeV2 - Extraction Response", async () => {

describe("Raw Text", async () => {
it("raw text should be exposed", async () => {
const response = await loadV2Extraction(
path.join(extractionPath, "raw_texts.json")
const response = await loadV2Response(
ExtractionResponse, path.join(extractionPath, "raw_texts.json")
);
expect(response.inference.result.rag).to.be.undefined;

const rawText = response.inference.result.rawText;
expect(rawText).to.be.instanceOf(RawText);
expect(rawText).to.be.instanceOf(field.RawText);

const pages = rawText?.pages;
if (pages === undefined) throw new Error("pages is undefined");
Expand All @@ -291,27 +295,29 @@ describe("MindeeV2 - Extraction Response", async () => {

describe("RAG Metadata", async () => {
it("RAG metadata when matched", async () => {
const response = await loadV2Extraction(
path.join(extractionPath, "rag_matched.json")
const response = await loadV2Response(
ExtractionResponse, path.join(extractionPath, "rag_matched.json")
);
const rag = response.inference.result.rag;
expect(rag).to.be.instanceOf(RagMetadata);
expect(rag).to.be.instanceOf(field.RagMetadata);
expect(rag?.retrievedDocumentId).to.eq("12345abc-1234-1234-1234-123456789abc");
});

it("RAG metadata when not matched", async () => {
const response = await loadV2Extraction(
path.join(extractionPath, "rag_not_matched.json")
const response = await loadV2Response(
ExtractionResponse, path.join(extractionPath, "rag_not_matched.json")
);
const rag = response.inference.result.rag;
expect(rag).to.be.instanceOf(RagMetadata);
expect(rag).to.be.instanceOf(field.RagMetadata);
expect(rag?.retrievedDocumentId).to.be.undefined;
});
});

describe("RST Display", async () => {
it("to be properly exposed", async () => {
const response = await loadV2Extraction(standardFieldPath);
const response = await loadV2Response(
ExtractionResponse, standardFieldPath
);
const rstString = await fs.readFile(standardFieldRstPath, "utf8");

expect(response.inference).to.not.be.null;
Expand All @@ -321,8 +327,9 @@ describe("MindeeV2 - Extraction Response", async () => {

describe("Field Locations and Confidence", async () => {
it("to be properly exposed", async () => {
const response = await loadV2Extraction(locationFieldPath);

const response = await loadV2Response(
ExtractionResponse, locationFieldPath
);
expect(response.inference).to.not.be.null;

const dateField = response.inference.result.fields.get("date") as SimpleField;
Expand Down
Loading
Loading