From 36d0c22fc22350d239f915840215e7d25c22514d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ianar=C3=A9=20S=C3=A9vi?= Date: Wed, 25 Feb 2026 12:45:46 +0100 Subject: [PATCH] :sparkles: add job information to inference object --- src/Parsing/V2/Inference.php | 19 ++++-- src/Parsing/V2/InferenceJob.php | 31 +++++++++ src/Parsing/V2/Job.php | 14 ++-- tests/TestingUtilities.php | 8 +++ tests/V2/Parsing/InferenceResponseTest.php | 51 ++++++++------ tests/V2/Parsing/JobResponseTest.php | 78 ++++++++++++++++++++++ tests/resources | 2 +- 7 files changed, 171 insertions(+), 32 deletions(-) create mode 100644 src/Parsing/V2/InferenceJob.php diff --git a/src/Parsing/V2/Inference.php b/src/Parsing/V2/Inference.php index a72146eb..f93537d7 100644 --- a/src/Parsing/V2/Inference.php +++ b/src/Parsing/V2/Inference.php @@ -7,6 +7,16 @@ */ class Inference { + /** + * @var string ID of the inference. + */ + public string $id; + + /** + * @var InferenceJob Job the inference belongs to. + */ + public InferenceJob $job; + /** * @var InferenceModel Model info for the inference. */ @@ -27,21 +37,17 @@ class Inference */ public InferenceResult $result; - /** - * @var string|null ID of the inference. - */ - public ?string $id; - /** * @param array $serverResponse Raw server response array. */ public function __construct(array $serverResponse) { + $this->id = $serverResponse['id']; + $this->job = new InferenceJob($serverResponse['job']); $this->model = new InferenceModel($serverResponse['model']); $this->file = new InferenceFile($serverResponse['file']); $this->activeOptions = new InferenceActiveOptions($serverResponse['active_options']); $this->result = new InferenceResult($serverResponse['result']); - $this->id = $serverResponse['id'] ?? null; } /** @@ -50,6 +56,7 @@ public function __construct(array $serverResponse) public function __toString(): string { return "Inference\n#########\n" + . "{$this->job}\n" . "{$this->model}\n" . "{$this->file}\n" . "{$this->activeOptions}\n" diff --git a/src/Parsing/V2/InferenceJob.php b/src/Parsing/V2/InferenceJob.php new file mode 100644 index 00000000..a1d798a3 --- /dev/null +++ b/src/Parsing/V2/InferenceJob.php @@ -0,0 +1,31 @@ +id = $serverResponse['id']; + } + + /** + * @return string String representation. + */ + public function __toString(): string + { + return "Job\n===\n" + . ":ID: {$this->id}\n" ; + } +} diff --git a/src/Parsing/V2/Job.php b/src/Parsing/V2/Job.php index f01894cf..395465d7 100644 --- a/src/Parsing/V2/Job.php +++ b/src/Parsing/V2/Job.php @@ -21,9 +21,14 @@ class Job public ?ErrorResponse $error; /** - * @var DateTime|null Timestamp of the job creation. + * @var DateTime Date and time of the Job creation. */ - public ?DateTime $createdAt; + public DateTime $createdAt; + + /** + * @var DateTime|null Date and time of the Job completion. Filled once processing is finished. + */ + public ?DateTime $completedAt; /** * @var string ID of the model. @@ -76,8 +81,9 @@ public function __construct(array $serverResponse) $this->error = new ErrorResponse($serverResponse['error']); } - $this->createdAt = isset($serverResponse['created_at']) - ? $this->parseDate($serverResponse['created_at']) + $this->createdAt = $this->parseDate($serverResponse['created_at']); + $this->completedAt = isset($serverResponse['completed_at']) + ? $this->parseDate($serverResponse['completed_at']) : null; $this->modelId = $serverResponse['model_id']; diff --git a/tests/TestingUtilities.php b/tests/TestingUtilities.php index 3849eb39..e6370923 100644 --- a/tests/TestingUtilities.php +++ b/tests/TestingUtilities.php @@ -29,6 +29,14 @@ public static function getV2DataDir(): string return TestingUtilities::getRootDataDir() . "/v2"; } + /** + * @return string Return the root of the v2 products directory. + */ + public static function getV2ProductDir(): string + { + return TestingUtilities::getV2DataDir() . "/products"; + } + /** * @return string Return the root of the file types directory. */ diff --git a/tests/V2/Parsing/InferenceResponseTest.php b/tests/V2/Parsing/InferenceResponseTest.php index 64e2cf24..89b9b1c6 100644 --- a/tests/V2/Parsing/InferenceResponseTest.php +++ b/tests/V2/Parsing/InferenceResponseTest.php @@ -1,6 +1,6 @@ assertFileExists($fullPath, "Resource file must exist: $resourcePath"); $localResponse = new LocalResponse($fullPath); @@ -39,11 +39,12 @@ private function readFileAsString(string $path): string } /** - * When the async prediction is blank - all properties must be valid + * When the async prediction is blank - all properties must be valid. + * @return void */ public function testAsyncPredictWhenEmptyMustHaveValidProperties(): void { - $response = $this->loadFromResource('v2/products/extraction/financial_document/blank.json'); + $response = $this->loadFromResource('extraction/financial_document/blank.json'); $fields = $response->inference->result->fields; $this->assertCount(21, $fields, 'Expected 21 fields'); @@ -91,11 +92,12 @@ public function testAsyncPredictWhenEmptyMustHaveValidProperties(): void } /** - * When the async prediction is complete - every exposed property must be valid and consistent + * When the async prediction is complete - every exposed property must be valid and consistent. + * @return void */ public function testAsyncPredictWhenCompleteMustExposeAllProperties(): void { - $response = $this->loadFromResource('v2/products/extraction/financial_document/complete.json'); + $response = $this->loadFromResource('extraction/financial_document/complete.json'); $inference = $response->inference; $this->assertNotNull($inference, 'Inference must not be null'); @@ -154,11 +156,12 @@ public function testAsyncPredictWhenCompleteMustExposeAllProperties(): void } /** - * Deep nested fields - all nested structures must be typed correctly + * Deep nested fields - all nested structures must be typed correctly. + * @return void */ public function testDeepNestedFieldsMustExposeCorrectTypes(): void { - $response = $this->loadFromResource('v2/products/extraction/deep_nested_fields.json'); + $response = $this->loadFromResource('extraction/deep_nested_fields.json'); $inference = $response->inference; $this->assertNotNull($inference); @@ -198,11 +201,12 @@ public function testDeepNestedFieldsMustExposeCorrectTypes(): void } /** - * Standard field types - simple / object / list variants must be recognised + * Standard field types - simple / object / list variants must be recognised. + * @return void */ public function testStandardFieldTypesMustExposeCorrectTypes(): void { - $response = $this->loadFromResource('v2/products/extraction/standard_field_types.json'); + $response = $this->loadFromResource('extraction/standard_field_types.json'); $inference = $response->inference; $this->assertNotNull($inference); @@ -279,11 +283,12 @@ public function testStandardFieldTypesMustExposeCorrectTypes(): void } /** - * Raw texts option must be parsed and exposed + * Raw texts option must be parsed and exposed. + * @return void */ public function testRawTextsMustBeAccessible(): void { - $response = $this->loadFromResource('v2/products/extraction/raw_texts.json'); + $response = $this->loadFromResource('extraction/raw_texts.json'); $inference = $response->inference; $this->assertNotNull($inference); @@ -306,13 +311,14 @@ public function testRawTextsMustBeAccessible(): void } /** - * RST display must be parsed and exposed + * RST display must be parsed and exposed. + * @return void */ public function testRstDisplayMustBeAccessible(): void { - $response = $this->loadFromResource('v2/products/extraction/standard_field_types.json'); + $response = $this->loadFromResource('extraction/standard_field_types.json'); $expectedRst = $this->readFileAsString( - \TestingUtilities::getV2DataDir() . '/products/extraction/standard_field_types.rst' + \TestingUtilities::getV2ProductDir() . '/extraction/standard_field_types.rst' ); $inference = $response->inference; $this->assertNotNull($inference); @@ -321,10 +327,11 @@ public function testRstDisplayMustBeAccessible(): void /** * Coordinates & location data must be parsed and exposed. + * @return void */ public function testCoordinatesAndLocationDataMustBeAccessible(): void { - $response = $this->loadFromResource('v2/products/extraction/financial_document/complete_with_coordinates.json'); + $response = $this->loadFromResource('extraction/financial_document/complete_with_coordinates.json'); $inference = $response->inference; $this->assertNotNull($inference); @@ -383,7 +390,7 @@ public function testCoordinatesAndLocationDataMustBeAccessible(): void public function testRagMetadataWhenMatched() { - $response = $this->loadFromResource('v2/products/extraction/rag_matched.json'); + $response = $this->loadFromResource('extraction/rag_matched.json'); $inference = $response->inference; $this->assertNotNull($inference); $this->assertEquals('12345abc-1234-1234-1234-123456789abc', $inference->result->rag->retrievedDocumentId); @@ -391,7 +398,7 @@ public function testRagMetadataWhenMatched() public function testRagMetadataWhenNotMatched() { - $response = $this->loadFromResource('v2/products/extraction/rag_not_matched.json'); + $response = $this->loadFromResource('extraction/rag_not_matched.json'); $inference = $response->inference; $this->assertNotNull($inference); $this->assertNull($inference->result->rag->retrievedDocumentId); @@ -399,7 +406,9 @@ public function testRagMetadataWhenNotMatched() public function testShouldLoadWith422Error() { - $jsonResponse = json_decode(file_get_contents(\TestingUtilities::getV2DataDir() . '/job/fail_422.json'), true); + $jsonResponse = json_decode( + file_get_contents(\TestingUtilities::getV2DataDir() . '/job/fail_422.json'), true + ); $response = new JobResponse($jsonResponse); $this->assertNotNull($response->job); $this->assertInstanceOf(ErrorResponse::class, $response->job->error); @@ -411,7 +420,7 @@ public function testShouldLoadWith422Error() public function testTextContextIsTrue(): void { - $response = $this->loadFromResource('v2/products/extraction/text_context_enabled.json'); + $response = $this->loadFromResource('extraction/text_context_enabled.json'); $inference = $response->inference; $this->assertNotNull($inference); $activeOptions = $inference->activeOptions; @@ -424,7 +433,7 @@ public function testTextContextIsTrue(): void public function testTextContextIsFalse(): void { - $response = $this->loadFromResource('v2/products/extraction/financial_document/complete.json'); + $response = $this->loadFromResource('extraction/financial_document/complete.json'); $inference = $response->inference; $this->assertNotNull($inference); $activeOptions = $inference->activeOptions; diff --git a/tests/V2/Parsing/JobResponseTest.php b/tests/V2/Parsing/JobResponseTest.php index b3d9bbc7..070a2826 100644 --- a/tests/V2/Parsing/JobResponseTest.php +++ b/tests/V2/Parsing/JobResponseTest.php @@ -1 +1,79 @@ assertNotNull($response->job); + $this->assertSame('Processing', $response->job->status); + $this->assertNull($response->job->completedAt); + $this->assertNull($response->job->error); + } + + /** + * Should load when status is Processed. + * @return void + */ + public function testShouldLoadWhenStatusIsProcessed(): void + { + $jsonSample = self::getJobSamples('ok_processed_webhooks_ok.json'); + $response = new JobResponse($jsonSample); + + $this->assertNotNull($response->job); + $this->assertSame('Processed', $response->job->status); + $this->assertInstanceOf(DateTime::class, $response->job->completedAt); + $this->assertNull($response->job->error); + } + + /** + * Should load with 422 error. + * @return void + */ + public function testShouldLoadWith422Error(): void + { + $jsonSample = self::getJobSamples('fail_422.json'); + $response = new JobResponse($jsonSample); + + $this->assertNotNull($response->job); + $this->assertSame('Failed', $response->job->status); + $this->assertInstanceOf(DateTime::class, $response->job->completedAt); + + $this->assertInstanceOf(ErrorResponse::class, $response->job->error); + $this->assertSame(422, $response->job->error->status); + $this->assertStringStartsWith('422-', $response->job->error->code); + $this->assertIsArray($response->job->error->errors); + $this->assertCount(1, $response->job->error->errors); + $this->assertInstanceOf(ErrorItem::class, $response->job->error->errors[0]); + } +} diff --git a/tests/resources b/tests/resources index 37f2e3de..c2e36f5b 160000 --- a/tests/resources +++ b/tests/resources @@ -1 +1 @@ -Subproject commit 37f2e3de48918e3b1a0e4604a9292aaeae05c637 +Subproject commit c2e36f5b635386cb9bb922b517c4e02039b0a122