From ff721f474b4e4ab14e052072c45b9ede0515ce06 Mon Sep 17 00:00:00 2001
From: sebastianMindee <130448732+sebastianMindee@users.noreply.github.com>
Date: Thu, 23 Jul 2026 17:19:00 +0200
Subject: [PATCH 1/3] :sparkles: add RAG search API
---
bin/V2/SearchModelsCommand.php | 5 +-
bin/V2/SearchRagDocumentsCommand.php | 103 ++++++++++++++++++
bin/cli.php | 5 +-
src/Parsing/DateHelper.php | 51 +++++++++
src/V2/Client.php | 26 +++--
src/V2/ClientOptions/BaseSearchParameters.php | 37 +++++++
.../ClientOptions/ModelSearchParameters.php | 41 +++++++
.../RagDocumentSearchParameters.php | 50 +++++++++
src/V2/Http/MindeeApiV2.php | 50 ++++++---
src/V2/Parsing/Job/Job.php | 24 +---
src/V2/Parsing/Job/JobWebhook.php | 22 +---
src/V2/Parsing/Search/BaseSearchResponse.php | 51 +++++++++
src/V2/Parsing/Search/ModelSearchResponse.php | 37 +++++++
src/V2/Parsing/Search/RagDocument.php | 57 ++++++++++
.../Search/RagDocumentSearchResponse.php | 37 +++++++
src/V2/Parsing/Search/RagDocuments.php | 48 ++++++++
src/V2/Parsing/Search/SearchResponse.php | 44 +-------
tests/V2/Cli/MindeeCliCommandV2Test.php | 37 +++++++
.../Parsing/RagDocumentSearchResponseTest.php | 55 ++++++++++
tests/resources | 2 +-
20 files changed, 672 insertions(+), 110 deletions(-)
create mode 100644 bin/V2/SearchRagDocumentsCommand.php
create mode 100644 src/Parsing/DateHelper.php
create mode 100644 src/V2/ClientOptions/BaseSearchParameters.php
create mode 100644 src/V2/ClientOptions/ModelSearchParameters.php
create mode 100644 src/V2/ClientOptions/RagDocumentSearchParameters.php
create mode 100644 src/V2/Parsing/Search/BaseSearchResponse.php
create mode 100644 src/V2/Parsing/Search/ModelSearchResponse.php
create mode 100644 src/V2/Parsing/Search/RagDocument.php
create mode 100644 src/V2/Parsing/Search/RagDocumentSearchResponse.php
create mode 100644 src/V2/Parsing/Search/RagDocuments.php
create mode 100644 tests/V2/Parsing/RagDocumentSearchResponseTest.php
diff --git a/bin/V2/SearchModelsCommand.php b/bin/V2/SearchModelsCommand.php
index c4ff273e..3777b5a6 100644
--- a/bin/V2/SearchModelsCommand.php
+++ b/bin/V2/SearchModelsCommand.php
@@ -6,6 +6,7 @@
use Exception;
use Mindee\V2\Client;
+use Mindee\V2\ClientOptions\ModelSearchParameters;
use Mindee\V2\Error\MindeeV2HttpException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
@@ -83,7 +84,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$client = new Client($apiKey ?: null);
try {
- $response = $client->searchModels($name ?: null, $modelType ?: null);
+ $response = $client->searchModels(
+ new ModelSearchParameters($name ?: null, $modelType ?: null)
+ );
} catch (MindeeV2HttpException $e) {
$output->writeln('' . $e->getMessage() . '');
return Command::FAILURE;
diff --git a/bin/V2/SearchRagDocumentsCommand.php b/bin/V2/SearchRagDocumentsCommand.php
new file mode 100644
index 00000000..47d22d44
--- /dev/null
+++ b/bin/V2/SearchRagDocumentsCommand.php
@@ -0,0 +1,103 @@
+setName('search-rag-docs')
+ ->setDescription('Search available RAG documents for a given model.')
+ ->addOption(
+ 'api-key',
+ 'k',
+ InputOption::VALUE_REQUIRED,
+ 'Mindee V2 API key. Falls back to the MINDEE_V2_API_KEY environment variable.'
+ )
+ ->addOption(
+ 'model-id',
+ 'm',
+ InputOption::VALUE_REQUIRED,
+ 'Filter by model ID.'
+ )
+ ->addOption(
+ 'filename',
+ 'f',
+ InputOption::VALUE_REQUIRED,
+ 'Filter by file name partial match (case insensitive).'
+ )
+ ->addOption(
+ 'raw-json',
+ 'r',
+ InputOption::VALUE_NONE,
+ 'Whether to output the raw JSON response.'
+ );
+ }
+
+ /**
+ * @param InputInterface $input CLI input.
+ * @param OutputInterface $output CLI output.
+ * @return integer Exit code.
+ */
+ protected function execute(InputInterface $input, OutputInterface $output): int
+ {
+ $apiKey = $input->getOption('api-key');
+ if (!$apiKey && !getenv('MINDEE_V2_API_KEY')) {
+ $output->writeln(
+ 'The Mindee V2 API key is missing. '
+ . "Please provide it via the '--api-key' option or the MINDEE_V2_API_KEY environment variable."
+ );
+ return Command::FAILURE;
+ }
+
+ $modelId = $input->getOption('model-id');
+ if (!$modelId) {
+ $output->writeln('The --model-id option is required.');
+ return Command::FAILURE;
+ }
+ $filename = $input->getOption('filename');
+ $raw = (bool) $input->getOption('raw-json');
+
+ $client = new Client($apiKey ?: null);
+
+ try {
+ $response = $client->searchRagDocuments(
+ new RagDocumentSearchParameters($modelId, $filename ?: null)
+ );
+ } catch (MindeeV2HttpException $e) {
+ $output->writeln('' . $e->getMessage() . '');
+ return Command::FAILURE;
+ } catch (Exception $e) {
+ $output->writeln("Something went wrong, '" . $e->getMessage() . "' was raised.");
+ return Command::FAILURE;
+ }
+
+ if ($raw) {
+ $output->writeln($response->getRawHttp());
+ } else {
+ $output->write((string) $response);
+ }
+ return Command::SUCCESS;
+ }
+}
diff --git a/bin/cli.php b/bin/cli.php
index 7ab44b10..fa47ed6f 100755
--- a/bin/cli.php
+++ b/bin/cli.php
@@ -15,6 +15,7 @@
require __DIR__ . '/V2/OcrCommand.php';
require __DIR__ . '/V2/SplitCommand.php';
require __DIR__ . '/V2/SearchModelsCommand.php';
+require __DIR__ . '/V2/SearchRagDocumentsCommand.php';
use Exception;
use Mindee\Cli\V2\ClassificationCommand;
@@ -22,6 +23,7 @@
use Mindee\Cli\V2\ExtractionCommand;
use Mindee\Cli\V2\OcrCommand;
use Mindee\Cli\V2\SearchModelsCommand;
+use Mindee\Cli\V2\SearchRagDocumentsCommand;
use Mindee\Cli\V2\SplitCommand;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\ArgvInput;
@@ -139,8 +141,9 @@ function mindeeRewriteArgvForV1Compat(array $argv, array $knownTopLevelCommands)
$cli->add($command);
}
$cli->add(new SearchModelsCommand());
+$cli->add(new SearchRagDocumentsCommand());
-$knownTopLevelCommands = ['v1', 'search-models', 'list', 'help', 'completion'];
+$knownTopLevelCommands = ['v1', 'search-models', 'search-rag-docs', 'list', 'help', 'completion'];
foreach ($v2InferenceCommands as $command) {
$knownTopLevelCommands[] = $command->getName();
}
diff --git a/src/Parsing/DateHelper.php b/src/Parsing/DateHelper.php
new file mode 100644
index 00000000..c6f6c448
--- /dev/null
+++ b/src/Parsing/DateHelper.php
@@ -0,0 +1,51 @@
+mindeeApi->searchModels($modelName, $modelType);
+ return $this->mindeeApi->searchModels($params ?? new ModelSearchParameters());
+ }
+
+ /**
+ * Searches for a list of RAG documents matching the given criteria.
+ * @param RagDocumentSearchParameters $params Search parameters.
+ * @return RagDocumentSearchResponse The list of RAG documents matching the criteria.
+ */
+ public function searchRagDocuments(RagDocumentSearchParameters $params): RagDocumentSearchResponse
+ {
+ return $this->mindeeApi->searchRagDocuments($params);
}
}
diff --git a/src/V2/ClientOptions/BaseSearchParameters.php b/src/V2/ClientOptions/BaseSearchParameters.php
new file mode 100644
index 00000000..5d222509
--- /dev/null
+++ b/src/V2/ClientOptions/BaseSearchParameters.php
@@ -0,0 +1,37 @@
+ Query parameters.
+ */
+ public function getQueryParams(): array
+ {
+ $params = [];
+ if ($this->page !== null && $this->page > 0) {
+ $params['page'] = (string) $this->page;
+ }
+ if ($this->perPage !== null && $this->perPage > 0) {
+ $params['per_page'] = (string) $this->perPage;
+ }
+ return $params;
+ }
+}
diff --git a/src/V2/ClientOptions/ModelSearchParameters.php b/src/V2/ClientOptions/ModelSearchParameters.php
new file mode 100644
index 00000000..51452233
--- /dev/null
+++ b/src/V2/ClientOptions/ModelSearchParameters.php
@@ -0,0 +1,41 @@
+ Query parameters.
+ */
+ public function getQueryParams(): array
+ {
+ $params = parent::getQueryParams();
+ if (!empty($this->name)) {
+ $params['name'] = $this->name;
+ }
+ if (!empty($this->modelType)) {
+ $params['model_type'] = $this->modelType;
+ }
+ return $params;
+ }
+}
diff --git a/src/V2/ClientOptions/RagDocumentSearchParameters.php b/src/V2/ClientOptions/RagDocumentSearchParameters.php
new file mode 100644
index 00000000..46367fa2
--- /dev/null
+++ b/src/V2/ClientOptions/RagDocumentSearchParameters.php
@@ -0,0 +1,50 @@
+ Query parameters.
+ * @throws MindeeException Throws if the model ID is not provided.
+ */
+ public function getQueryParams(): array
+ {
+ $params = parent::getQueryParams();
+ if (!empty($this->modelId)) {
+ $params['model_id'] = $this->modelId;
+ } else {
+ throw new MindeeException(
+ "ModelId is required in RagDocumentSearchParameters.",
+ ErrorCode::USER_INPUT_ERROR
+ );
+ }
+ if (!empty($this->filename)) {
+ $params['filename'] = $this->filename;
+ }
+ return $params;
+ }
+}
diff --git a/src/V2/Http/MindeeApiV2.php b/src/V2/Http/MindeeApiV2.php
index 6835c1b9..5af23128 100644
--- a/src/V2/Http/MindeeApiV2.php
+++ b/src/V2/Http/MindeeApiV2.php
@@ -18,12 +18,15 @@
use Mindee\Input\LocalInputSource;
use Mindee\Input\UrlInputSource;
use Mindee\V2\ClientOptions\BaseParameters;
+use Mindee\V2\ClientOptions\ModelSearchParameters;
+use Mindee\V2\ClientOptions\RagDocumentSearchParameters;
use Mindee\V2\Error\MindeeV2HttpException;
use Mindee\V2\Error\MindeeV2HttpUnknownException;
use Mindee\V2\Parsing\Error\ErrorResponse;
use Mindee\V2\Parsing\Inference\BaseResponse;
use Mindee\V2\Parsing\Job\JobResponse;
-use Mindee\V2\Parsing\Search\SearchResponse;
+use Mindee\V2\Parsing\Search\ModelSearchResponse;
+use Mindee\V2\Parsing\Search\RagDocumentSearchResponse;
use ReflectionClass;
use ReflectionException;
use ReflectionProperty;
@@ -389,20 +392,16 @@ private function checkValidResponse(array $result): void
}
/**
+ * Makes a GET call to a search endpoint.
+ * @param string $path Search endpoint path (e.g. `/v2/search/models`).
+ * @param array $queryParams Query parameters to append.
* @return array> Server response.
*/
- private function reqGetSearchModels(?string $modelName = null, ?string $modelType = null): array
+ private function reqGetSearch(string $path, array $queryParams): array
{
- $url = $this->baseUrl . "/v2/search/models";
- $params = [];
- if ($modelName) {
- $params['name'] = $modelName;
- }
- if ($modelType) {
- $params['model_type'] = $modelType;
- }
- if (!empty($params)) {
- $url .= '?' . http_build_query($params);
+ $url = $this->baseUrl . $path;
+ if (!empty($queryParams)) {
+ $url .= '?' . http_build_query($queryParams);
}
$ch = $this->initChannel();
@@ -418,13 +417,28 @@ private function reqGetSearchModels(?string $modelName = null, ?string $modelTyp
}
/**
- * Retrieves a list of models based on criteria.
- * @param string|null $modelName Optional model name to filter by.
- * @param string|null $modelType Optional model type to filter by.
- * @return SearchResponse The list of models matching the criteria.
+ * Retrieves a list of models matching the given criteria.
+ * @param ModelSearchParameters $params Search parameters.
+ * @return ModelSearchResponse The list of models matching the criteria.
*/
- public function searchModels(?string $modelName = null, ?string $modelType = null): SearchResponse
+ public function searchModels(ModelSearchParameters $params): ModelSearchResponse
{
- return $this->processResponse(SearchResponse::class, $this->reqGetSearchModels($modelName, $modelType));
+ return $this->processResponse(
+ ModelSearchResponse::class,
+ $this->reqGetSearch("/v2/search/models", $params->getQueryParams())
+ );
+ }
+
+ /**
+ * Retrieves a list of RAG documents matching the given criteria.
+ * @param RagDocumentSearchParameters $params Search parameters.
+ * @return RagDocumentSearchResponse The list of RAG documents matching the criteria.
+ */
+ public function searchRagDocuments(RagDocumentSearchParameters $params): RagDocumentSearchResponse
+ {
+ return $this->processResponse(
+ RagDocumentSearchResponse::class,
+ $this->reqGetSearch("/v2/search/rag-documents", $params->getQueryParams())
+ );
}
}
diff --git a/src/V2/Parsing/Job/Job.php b/src/V2/Parsing/Job/Job.php
index 982853c4..3e2d923d 100644
--- a/src/V2/Parsing/Job/Job.php
+++ b/src/V2/Parsing/Job/Job.php
@@ -5,7 +5,7 @@
namespace Mindee\V2\Parsing\Job;
use DateTime;
-use Exception;
+use Mindee\Parsing\DateHelper;
use Mindee\V2\Parsing\Error\ErrorResponse;
use function array_key_exists;
@@ -86,9 +86,9 @@ public function __construct(array $rawResponse)
$this->error = new ErrorResponse($rawResponse['error']);
}
- $this->createdAt = $this->parseDate($rawResponse['created_at']);
+ $this->createdAt = DateHelper::parseDate($rawResponse['created_at']);
$this->completedAt = isset($rawResponse['completed_at'])
- ? $this->parseDate($rawResponse['completed_at'])
+ ? DateHelper::parseDate($rawResponse['completed_at'])
: null;
$this->modelId = $rawResponse['model_id'];
@@ -103,22 +103,4 @@ public function __construct(array $rawResponse)
}
}
}
-
- /**
- * Parse a date string into a DateTime object.
- *
- * @param string|null $dateString Date string to parse.
- */
- private function parseDate(?string $dateString): ?DateTime
- {
- if (empty($dateString)) {
- return null;
- }
-
- try {
- return new DateTime($dateString);
- } catch (Exception) {
- return null;
- }
- }
}
diff --git a/src/V2/Parsing/Job/JobWebhook.php b/src/V2/Parsing/Job/JobWebhook.php
index e12d5ccf..8c9f2f94 100644
--- a/src/V2/Parsing/Job/JobWebhook.php
+++ b/src/V2/Parsing/Job/JobWebhook.php
@@ -5,7 +5,7 @@
namespace Mindee\V2\Parsing\Job;
use DateTime;
-use Exception;
+use Mindee\Parsing\DateHelper;
use Mindee\V2\Parsing\Error\ErrorResponse;
/**
@@ -40,29 +40,11 @@ public function __construct(array $rawResponse)
{
$this->id = $rawResponse['id'];
$this->createdAt = isset($rawResponse['created_at'])
- ? $this->parseDate($rawResponse['created_at'])
+ ? DateHelper::parseDate($rawResponse['created_at'])
: null;
$this->status = $rawResponse['status'];
$this->error = isset($rawResponse['error'])
? new ErrorResponse($rawResponse['error'])
: null;
}
-
- /**
- * Parse a date string into a DateTime object.
- *
- * @param string|null $dateString Date string to parse.
- */
- private function parseDate(?string $dateString): ?DateTime
- {
- if (empty($dateString)) {
- return null;
- }
-
- try {
- return new DateTime($dateString);
- } catch (Exception) {
- return null;
- }
- }
}
diff --git a/src/V2/Parsing/Search/BaseSearchResponse.php b/src/V2/Parsing/Search/BaseSearchResponse.php
new file mode 100644
index 00000000..143a07b2
--- /dev/null
+++ b/src/V2/Parsing/Search/BaseSearchResponse.php
@@ -0,0 +1,51 @@
+> $rawResponse Raw server response array.
+ */
+ public function __construct(array $rawResponse)
+ {
+ parent::__construct($rawResponse);
+ $this->pagination = new PaginationMetadata($rawResponse['pagination']);
+ }
+
+ /**
+ * Lines composing the response-specific body (header + items).
+ *
+ * @return array Body lines.
+ */
+ abstract protected function bodyLines(): array;
+
+ /**
+ * @return string String representation.
+ */
+ public function __toString(): string
+ {
+ return implode("\n", array_merge(
+ $this->bodyLines(),
+ [
+ 'Pagination Metadata',
+ '###################',
+ (string) $this->pagination,
+ '',
+ ]
+ ));
+ }
+}
diff --git a/src/V2/Parsing/Search/ModelSearchResponse.php b/src/V2/Parsing/Search/ModelSearchResponse.php
new file mode 100644
index 00000000..4b573f81
--- /dev/null
+++ b/src/V2/Parsing/Search/ModelSearchResponse.php
@@ -0,0 +1,37 @@
+> $rawResponse Raw server response array.
+ */
+ public function __construct(array $rawResponse)
+ {
+ parent::__construct($rawResponse);
+ $this->models = new SearchModels($rawResponse['models']);
+ }
+
+ /**
+ * @return array Body lines.
+ */
+ protected function bodyLines(): array
+ {
+ return [
+ 'Models',
+ '######',
+ (string) $this->models,
+ ];
+ }
+}
diff --git a/src/V2/Parsing/Search/RagDocument.php b/src/V2/Parsing/Search/RagDocument.php
new file mode 100644
index 00000000..cfb26a45
--- /dev/null
+++ b/src/V2/Parsing/Search/RagDocument.php
@@ -0,0 +1,57 @@
+> $rawResponse Raw server response array.
+ */
+ public function __construct(array $rawResponse)
+ {
+ $this->id = $rawResponse['id'];
+ $this->modelId = $rawResponse['model_id'];
+ $this->filename = $rawResponse['filename'];
+ $this->createdAt = new DateTimeImmutable($rawResponse['created_at']);
+ $this->totalMatches = $rawResponse['total_matches'];
+ $this->lastMatchAt = DateHelper::parseDateImmutable($rawResponse['last_match_at'] ?? null);
+ $this->status = $rawResponse['status'];
+ }
+}
diff --git a/src/V2/Parsing/Search/RagDocumentSearchResponse.php b/src/V2/Parsing/Search/RagDocumentSearchResponse.php
new file mode 100644
index 00000000..4157fdef
--- /dev/null
+++ b/src/V2/Parsing/Search/RagDocumentSearchResponse.php
@@ -0,0 +1,37 @@
+> $rawResponse Raw server response array.
+ */
+ public function __construct(array $rawResponse)
+ {
+ parent::__construct($rawResponse);
+ $this->ragDocuments = new RagDocuments($rawResponse['rag_documents']);
+ }
+
+ /**
+ * @return array Body lines.
+ */
+ protected function bodyLines(): array
+ {
+ return [
+ 'RAG Documents',
+ '############',
+ (string) $this->ragDocuments,
+ ];
+ }
+}
diff --git a/src/V2/Parsing/Search/RagDocuments.php b/src/V2/Parsing/Search/RagDocuments.php
new file mode 100644
index 00000000..0231dc53
--- /dev/null
+++ b/src/V2/Parsing/Search/RagDocuments.php
@@ -0,0 +1,48 @@
+
+ */
+class RagDocuments extends ArrayObject implements Stringable
+{
+ /**
+ * @param array>> $prediction Raw prediction.
+ */
+ public function __construct(array $prediction)
+ {
+ $documents = array_map(static fn($entry) => new RagDocument($entry), $prediction);
+
+ parent::__construct($documents);
+ }
+
+ /**
+ * Default string representation.
+ */
+ public function __toString(): string
+ {
+ if ($this->count() === 0) {
+ return "\n";
+ }
+
+ $lines = [];
+ foreach ($this as $document) {
+ $lines[] = "* :ID: " . $document->id;
+ $lines[] = " :Model ID: " . $document->modelId;
+ $lines[] = " :Filename: " . $document->filename;
+ $lines[] = " :Created At: " . $document->createdAt->format(DATE_ATOM);
+ $lines[] = " :Total Matches: " . $document->totalMatches;
+ $lines[] = " :Last Match At: " . ($document->lastMatchAt?->format(DATE_ATOM) ?? '');
+ $lines[] = " :Status: " . $document->status;
+ }
+
+ return implode("\n", $lines) . "\n";
+ }
+}
diff --git a/src/V2/Parsing/Search/SearchResponse.php b/src/V2/Parsing/Search/SearchResponse.php
index c58d6d32..64d261b3 100644
--- a/src/V2/Parsing/Search/SearchResponse.php
+++ b/src/V2/Parsing/Search/SearchResponse.php
@@ -4,47 +4,9 @@
namespace Mindee\V2\Parsing\Search;
-use Mindee\V2\Parsing\Inference\BaseResponse;
-use Stringable;
-
/**
* Models search response.
+ *
+ * @deprecated Use {@see ModelSearchResponse} instead.
*/
-class SearchResponse extends BaseResponse implements Stringable
-{
- /**
- * @var SearchModels Parsed search payload.
- */
- public SearchModels $models;
-
- /**
- * @var PaginationMetadata Pagination metadata for the search results.
- */
- public PaginationMetadata $pagination;
-
- /**
- * @param array> $rawResponse Raw server response array.
- */
- public function __construct(array $rawResponse)
- {
- parent::__construct($rawResponse);
- $this->models = new SearchModels($rawResponse['models']);
- $this->pagination = new PaginationMetadata($rawResponse['pagination']);
- }
-
- /**
- * @return string String representation.
- */
- public function __toString(): string
- {
- return implode("\n", [
- 'Models',
- '######',
- (string) $this->models,
- 'Pagination Metadata',
- '###################',
- (string) $this->pagination,
- '',
- ]);
- }
-}
+class SearchResponse extends ModelSearchResponse {}
diff --git a/tests/V2/Cli/MindeeCliCommandV2Test.php b/tests/V2/Cli/MindeeCliCommandV2Test.php
index 422b04b1..200a30da 100644
--- a/tests/V2/Cli/MindeeCliCommandV2Test.php
+++ b/tests/V2/Cli/MindeeCliCommandV2Test.php
@@ -49,6 +49,7 @@ public function testListShouldShowAllV2Commands(): void
self::assertStringContainsString('ocr', $stdout);
self::assertStringContainsString('split', $stdout);
self::assertStringContainsString('search-models', $stdout);
+ self::assertStringContainsString('search-rag-docs', $stdout);
self::assertStringContainsString('v1', $stdout);
}
@@ -195,6 +196,42 @@ public function testSearchModelsMissingApiKeyMustFail(): void
);
}
+ public function testSearchRagDocsHelpExposesExpectedOptions(): void
+ {
+ $cmdOutput = MindeeCliV2TestingUtilities::executeTest(['search-rag-docs', '--help']);
+ self::assertSame(0, $cmdOutput['code']);
+ $stdout = implode("\n", $cmdOutput['output']);
+ self::assertStringContainsString('--api-key', $stdout);
+ self::assertStringContainsString('--model-id', $stdout);
+ self::assertStringContainsString('--filename', $stdout);
+ self::assertStringContainsString('--raw-json', $stdout);
+ }
+
+ public function testSearchRagDocsMissingApiKeyMustFail(): void
+ {
+ $cmdOutput = MindeeCliV2TestingUtilities::executeTest(
+ ['search-rag-docs', '--model-id', 'some-model-id'],
+ ['MINDEE_V2_API_KEY' => false]
+ );
+ self::assertSame(1, $cmdOutput['code']);
+ self::assertStringContainsString(
+ 'API key is missing',
+ implode("\n", $cmdOutput['output'])
+ );
+ }
+
+ public function testSearchRagDocsMissingModelIdMustFail(): void
+ {
+ $cmdOutput = MindeeCliV2TestingUtilities::executeTest(
+ ['search-rag-docs', '-k', 'dummy-key']
+ );
+ self::assertSame(1, $cmdOutput['code']);
+ self::assertStringContainsString(
+ '--model-id',
+ implode("\n", $cmdOutput['output'])
+ );
+ }
+
public function testV1BackwardCompatibilityDispatch(): void
{
$cmdOutput = MindeeCliV2TestingUtilities::executeTest(
diff --git a/tests/V2/Parsing/RagDocumentSearchResponseTest.php b/tests/V2/Parsing/RagDocumentSearchResponseTest.php
new file mode 100644
index 00000000..166915aa
--- /dev/null
+++ b/tests/V2/Parsing/RagDocumentSearchResponseTest.php
@@ -0,0 +1,55 @@
+ragDocuments);
+ foreach ($response->ragDocuments as $document) {
+ self::assertInstanceOf(RagDocument::class, $document);
+ self::assertNotEmpty($document->id);
+ self::assertNotEmpty($document->modelId);
+ self::assertNotEmpty($document->filename);
+ }
+
+ $firstItem = $response->ragDocuments[0];
+ self::assertEquals("cc831599-c545-48b7-aa27-6d7ccd5b8d32", $firstItem->id);
+ self::assertEquals("12345678-1234-1234-1234-123456789abc", $firstItem->modelId);
+ self::assertEquals("invoice_01.pdf", $firstItem->filename);
+ self::assertEquals(0, $firstItem->totalMatches);
+ self::assertNull($firstItem->lastMatchAt);
+ self::assertEquals("Processing", $firstItem->status);
+
+ $thirdItem = $response->ragDocuments[2];
+ self::assertEquals("a6bcae7d-0439-476b-8a63-5a39ec05dc21", $thirdItem->id);
+ self::assertEquals("invoice_03.pdf", $thirdItem->filename);
+ self::assertEquals(5, $thirdItem->totalMatches);
+ self::assertNotNull($thirdItem->lastMatchAt);
+ self::assertEquals("Active", $thirdItem->status);
+
+ self::assertEquals(50, $response->pagination->perPage);
+ self::assertEquals(1, $response->pagination->page);
+ self::assertEquals(3, $response->pagination->totalItems);
+ self::assertEquals(1, $response->pagination->totalPages);
+ }
+}
diff --git a/tests/resources b/tests/resources
index e41ab97c..4b7f3376 160000
--- a/tests/resources
+++ b/tests/resources
@@ -1 +1 @@
-Subproject commit e41ab97c2833f15ea5c4edf221c2d197d212f632
+Subproject commit 4b7f33766fab0e67804b84447b73c80a902d886a
From 0f64651faf7bef98709da511d4bf14249d616a85 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ianar=C3=A9=20S=C3=A9vi?=
Date: Fri, 7 Aug 2026 17:56:24 +0200
Subject: [PATCH 2/3] harmonize
---
bin/V2/BaseInferenceCommand.php | 6 ++---
bin/V2/ClassificationCommand.php | 4 ++--
bin/V2/CropCommand.php | 4 ++--
bin/V2/ExtractionCommand.php | 4 ++--
bin/V2/OcrCommand.php | 4 ++--
bin/V2/SearchModelsCommand.php | 2 +-
bin/V2/SearchRagDocumentsCommand.php | 2 +-
bin/V2/SplitCommand.php | 4 ++--
src/V2/Client.php | 22 +++++++++----------
...rameters.php => BaseProductParameters.php} | 2 +-
src/V2/Http/MindeeApiV2.php | 14 ++++++------
.../Params/ClassificationParameters.php | 4 ++--
src/V2/Product/Crop/Params/CropParameters.php | 4 ++--
.../Params/ExtractionParameters.php | 4 ++--
src/V2/Product/Ocr/Params/OcrParameters.php | 4 ++--
.../Product/Split/Params/SplitParameters.php | 4 ++--
.../Models}/ModelSearchParameters.php | 4 +++-
.../RagDocumentSearchParameters.php | 3 ++-
...Test.php => BaseProductParametersTest.php} | 6 ++---
19 files changed, 52 insertions(+), 49 deletions(-)
rename src/V2/ClientOptions/{BaseParameters.php => BaseProductParameters.php} (97%)
rename src/V2/{ClientOptions => Search/Models}/ModelSearchParameters.php (92%)
rename src/V2/{ClientOptions => Search/RagDocuments}/RagDocumentSearchParameters.php (93%)
rename tests/V2/ClientOptions/{BaseParametersTest.php => BaseProductParametersTest.php} (80%)
diff --git a/bin/V2/BaseInferenceCommand.php b/bin/V2/BaseInferenceCommand.php
index bb6b4cee..27d5549d 100644
--- a/bin/V2/BaseInferenceCommand.php
+++ b/bin/V2/BaseInferenceCommand.php
@@ -8,7 +8,7 @@
use Mindee\Input\PathInput;
use Mindee\Input\UrlInputSource;
use Mindee\V2\Client;
-use Mindee\V2\ClientOptions\BaseParameters;
+use Mindee\V2\ClientOptions\BaseProductParameters;
use Mindee\V2\Error\MindeeV2HttpException;
use Mindee\V2\Parsing\Inference\BaseResponse;
use Symfony\Component\Console\Command\Command;
@@ -177,13 +177,13 @@ private function resolveInputSource(string $path): PathInput|UrlInputSource|null
* @param InputInterface $input CLI input, used to read product-specific options.
* @param string $modelId Model identifier.
* @param string|null $alias Optional alias.
- * @return BaseParameters Parameters object for the V2 client.
+ * @return BaseProductParameters Parameters object for the V2 client.
*/
abstract protected function buildParameters(
InputInterface $input,
string $modelId,
?string $alias
- ): BaseParameters;
+ ): BaseProductParameters;
/**
* @return class-string Fully-qualified product response class.
diff --git a/bin/V2/ClassificationCommand.php b/bin/V2/ClassificationCommand.php
index 1f90016d..c272bfe3 100644
--- a/bin/V2/ClassificationCommand.php
+++ b/bin/V2/ClassificationCommand.php
@@ -4,7 +4,7 @@
namespace Mindee\Cli\V2;
-use Mindee\V2\ClientOptions\BaseParameters;
+use Mindee\V2\ClientOptions\BaseProductParameters;
use Mindee\V2\Product\Classification\ClassificationResponse;
use Mindee\V2\Product\Classification\Params\ClassificationParameters;
use Symfony\Component\Console\Input\InputInterface;
@@ -37,7 +37,7 @@ protected function buildParameters(
InputInterface $input,
string $modelId,
?string $alias
- ): BaseParameters {
+ ): BaseProductParameters {
return new ClassificationParameters($modelId, $alias);
}
}
diff --git a/bin/V2/CropCommand.php b/bin/V2/CropCommand.php
index 3ff5578b..446649c5 100644
--- a/bin/V2/CropCommand.php
+++ b/bin/V2/CropCommand.php
@@ -4,7 +4,7 @@
namespace Mindee\Cli\V2;
-use Mindee\V2\ClientOptions\BaseParameters;
+use Mindee\V2\ClientOptions\BaseProductParameters;
use Mindee\V2\Product\Crop\CropResponse;
use Mindee\V2\Product\Crop\Params\CropParameters;
use Symfony\Component\Console\Input\InputInterface;
@@ -37,7 +37,7 @@ protected function buildParameters(
InputInterface $input,
string $modelId,
?string $alias
- ): BaseParameters {
+ ): BaseProductParameters {
return new CropParameters($modelId, $alias);
}
}
diff --git a/bin/V2/ExtractionCommand.php b/bin/V2/ExtractionCommand.php
index 07f8bf27..8ab0e9a6 100644
--- a/bin/V2/ExtractionCommand.php
+++ b/bin/V2/ExtractionCommand.php
@@ -4,7 +4,7 @@
namespace Mindee\Cli\V2;
-use Mindee\V2\ClientOptions\BaseParameters;
+use Mindee\V2\ClientOptions\BaseProductParameters;
use Mindee\V2\Parsing\Inference\BaseResponse;
use Mindee\V2\Product\Extraction\ExtractionResponse;
use Mindee\V2\Product\Extraction\Params\ExtractionParameters;
@@ -76,7 +76,7 @@ protected function buildParameters(
InputInterface $input,
string $modelId,
?string $alias
- ): BaseParameters {
+ ): BaseProductParameters {
$rag = (bool) $input->getOption('rag');
$rawText = (bool) $input->getOption('raw-text');
$confidence = (bool) $input->getOption('confidence');
diff --git a/bin/V2/OcrCommand.php b/bin/V2/OcrCommand.php
index 31079382..e4243687 100644
--- a/bin/V2/OcrCommand.php
+++ b/bin/V2/OcrCommand.php
@@ -4,7 +4,7 @@
namespace Mindee\Cli\V2;
-use Mindee\V2\ClientOptions\BaseParameters;
+use Mindee\V2\ClientOptions\BaseProductParameters;
use Mindee\V2\Product\Ocr\OcrResponse;
use Mindee\V2\Product\Ocr\Params\OcrParameters;
use Symfony\Component\Console\Input\InputInterface;
@@ -37,7 +37,7 @@ protected function buildParameters(
InputInterface $input,
string $modelId,
?string $alias
- ): BaseParameters {
+ ): BaseProductParameters {
return new OcrParameters($modelId, $alias);
}
}
diff --git a/bin/V2/SearchModelsCommand.php b/bin/V2/SearchModelsCommand.php
index 3777b5a6..9bb62661 100644
--- a/bin/V2/SearchModelsCommand.php
+++ b/bin/V2/SearchModelsCommand.php
@@ -6,8 +6,8 @@
use Exception;
use Mindee\V2\Client;
-use Mindee\V2\ClientOptions\ModelSearchParameters;
use Mindee\V2\Error\MindeeV2HttpException;
+use Mindee\V2\Search\Models\ModelSearchParameters;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
diff --git a/bin/V2/SearchRagDocumentsCommand.php b/bin/V2/SearchRagDocumentsCommand.php
index 47d22d44..a91ffbfd 100644
--- a/bin/V2/SearchRagDocumentsCommand.php
+++ b/bin/V2/SearchRagDocumentsCommand.php
@@ -6,8 +6,8 @@
use Exception;
use Mindee\V2\Client;
-use Mindee\V2\ClientOptions\RagDocumentSearchParameters;
use Mindee\V2\Error\MindeeV2HttpException;
+use Mindee\V2\Search\RagDocuments\RagDocumentSearchParameters;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
diff --git a/bin/V2/SplitCommand.php b/bin/V2/SplitCommand.php
index c7bde490..9badd983 100644
--- a/bin/V2/SplitCommand.php
+++ b/bin/V2/SplitCommand.php
@@ -4,7 +4,7 @@
namespace Mindee\Cli\V2;
-use Mindee\V2\ClientOptions\BaseParameters;
+use Mindee\V2\ClientOptions\BaseProductParameters;
use Mindee\V2\Product\Split\Params\SplitParameters;
use Mindee\V2\Product\Split\SplitResponse;
use Symfony\Component\Console\Input\InputInterface;
@@ -37,7 +37,7 @@ protected function buildParameters(
InputInterface $input,
string $modelId,
?string $alias
- ): BaseParameters {
+ ): BaseProductParameters {
return new SplitParameters($modelId, $alias);
}
}
diff --git a/src/V2/Client.php b/src/V2/Client.php
index 9006825e..48e940bf 100644
--- a/src/V2/Client.php
+++ b/src/V2/Client.php
@@ -9,14 +9,14 @@
use Mindee\Error\MindeeException;
use Mindee\Http\CancellationToken;
use Mindee\Input\InputSource;
-use Mindee\V2\ClientOptions\BaseParameters;
-use Mindee\V2\ClientOptions\ModelSearchParameters;
-use Mindee\V2\ClientOptions\RagDocumentSearchParameters;
+use Mindee\V2\ClientOptions\BaseProductParameters;
use Mindee\V2\Http\MindeeApiV2;
use Mindee\V2\Parsing\Inference\BaseResponse;
use Mindee\V2\Parsing\Job\JobResponse;
use Mindee\V2\Parsing\Search\ModelSearchResponse;
use Mindee\V2\Parsing\Search\RagDocumentSearchResponse;
+use Mindee\V2\Search\Models\ModelSearchParameters;
+use Mindee\V2\Search\RagDocuments\RagDocumentSearchParameters;
/**
* Mindee Client V2.
@@ -43,14 +43,14 @@ public function __construct(?string $apiKey = null)
/**
* Send the document to an asynchronous endpoint and return its ID in the queue.
* @param InputSource $inputSource File to parse.
- * @param BaseParameters $params Parameters relating to prediction options.
+ * @param BaseProductParameters $params Parameters relating to prediction options.
* @return JobResponse A JobResponse containing the job (queue) corresponding to a document.
* @throws MindeeException Throws if the input document is not provided.
* @category Asynchronous
*/
public function enqueue(
InputSource $inputSource,
- BaseParameters $params
+ BaseProductParameters $params
): JobResponse {
return $this->mindeeApi->reqPostEnqueue($inputSource, $params);
}
@@ -105,18 +105,18 @@ public function getJob(string $jobId): JobResponse
* @param string $responseClass The response class to construct.
* @phpstan-param class-string $responseClass
* @param InputSource $inputDoc Input document to parse.
- * @param BaseParameters $params Parameters relating to prediction options.
+ * @param BaseProductParameters $params Parameters relating to prediction options.
* @param PollingOptions|null $pollingOptions Options to apply to the polling.
* @param CancellationToken|null $cancellationToken CancellationToken to check for cancellation.
* @return BaseResponse A response containing parsing results.
* @throws MindeeException Throws if enqueueing fails, job fails, or times out.
*/
public function enqueueAndGetResult(
- string $responseClass,
- InputSource $inputDoc,
- BaseParameters $params,
- ?PollingOptions $pollingOptions = null,
- ?CancellationToken $cancellationToken = null
+ string $responseClass,
+ InputSource $inputDoc,
+ BaseProductParameters $params,
+ ?PollingOptions $pollingOptions = null,
+ ?CancellationToken $cancellationToken = null
): BaseResponse {
if (!$pollingOptions) {
$pollingOptions = new PollingOptions();
diff --git a/src/V2/ClientOptions/BaseParameters.php b/src/V2/ClientOptions/BaseProductParameters.php
similarity index 97%
rename from src/V2/ClientOptions/BaseParameters.php
rename to src/V2/ClientOptions/BaseProductParameters.php
index f146d025..62492eb1 100644
--- a/src/V2/ClientOptions/BaseParameters.php
+++ b/src/V2/ClientOptions/BaseProductParameters.php
@@ -7,7 +7,7 @@
/**
* Base parameters for running an inference.
*/
-abstract class BaseParameters
+abstract class BaseProductParameters
{
/**
* @var string|null Optional file alias.
diff --git a/src/V2/Http/MindeeApiV2.php b/src/V2/Http/MindeeApiV2.php
index 5af23128..a16e5f1d 100644
--- a/src/V2/Http/MindeeApiV2.php
+++ b/src/V2/Http/MindeeApiV2.php
@@ -17,9 +17,7 @@
use Mindee\Input\InputSource;
use Mindee\Input\LocalInputSource;
use Mindee\Input\UrlInputSource;
-use Mindee\V2\ClientOptions\BaseParameters;
-use Mindee\V2\ClientOptions\ModelSearchParameters;
-use Mindee\V2\ClientOptions\RagDocumentSearchParameters;
+use Mindee\V2\ClientOptions\BaseProductParameters;
use Mindee\V2\Error\MindeeV2HttpException;
use Mindee\V2\Error\MindeeV2HttpUnknownException;
use Mindee\V2\Parsing\Error\ErrorResponse;
@@ -27,6 +25,8 @@
use Mindee\V2\Parsing\Job\JobResponse;
use Mindee\V2\Parsing\Search\ModelSearchResponse;
use Mindee\V2\Parsing\Search\RagDocumentSearchResponse;
+use Mindee\V2\Search\Models\ModelSearchParameters;
+use Mindee\V2\Search\RagDocuments\RagDocumentSearchParameters;
use ReflectionClass;
use ReflectionException;
use ReflectionProperty;
@@ -164,11 +164,11 @@ protected function setAPIKey(?string $apiKey = null): void
/**
* @param InputSource $inputDoc Input document.
- * @param BaseParameters $params Parameters for the inference.
+ * @param BaseProductParameters $params Parameters for the inference.
* @return JobResponse Server response wrapped in a JobResponse object.
* @throws MindeeException Throws if the model ID is not provided.
*/
- public function reqPostEnqueue(InputSource $inputDoc, BaseParameters $params): JobResponse
+ public function reqPostEnqueue(InputSource $inputDoc, BaseProductParameters $params): JobResponse
{
if (!isset($params->modelId)) {
throw new MindeeException("Model ID must be provided.", ErrorCode::USER_INPUT_ERROR);
@@ -339,13 +339,13 @@ private function sendGetRequest(string $url): array
* Starts a CURL session using POST.
*
* @param InputSource $inputSource File to upload.
- * @param BaseParameters $params Parameters.
+ * @param BaseProductParameters $params Parameters.
* @return array> Server response.
* @throws MindeeException Throws if the cURL operation doesn't go succeed.
*/
private function documentEnqueuePost(
InputSource $inputSource,
- BaseParameters $params
+ BaseProductParameters $params
): array {
$ch = $this->initChannel();
$postFields = $params->asHash();
diff --git a/src/V2/Product/Classification/Params/ClassificationParameters.php b/src/V2/Product/Classification/Params/ClassificationParameters.php
index 057f9e1c..7a626759 100644
--- a/src/V2/Product/Classification/Params/ClassificationParameters.php
+++ b/src/V2/Product/Classification/Params/ClassificationParameters.php
@@ -5,12 +5,12 @@
namespace Mindee\V2\Product\Classification\Params;
use Mindee\ClientOptions\PollingOptions;
-use Mindee\V2\ClientOptions\BaseParameters;
+use Mindee\V2\ClientOptions\BaseProductParameters;
/**
* Parameters for a classification utility inference.
*/
-class ClassificationParameters extends BaseParameters
+class ClassificationParameters extends BaseProductParameters
{
/**
* @var string Slug of the endpoint.
diff --git a/src/V2/Product/Crop/Params/CropParameters.php b/src/V2/Product/Crop/Params/CropParameters.php
index e7cdf652..52fcef5d 100644
--- a/src/V2/Product/Crop/Params/CropParameters.php
+++ b/src/V2/Product/Crop/Params/CropParameters.php
@@ -5,12 +5,12 @@
namespace Mindee\V2\Product\Crop\Params;
use Mindee\ClientOptions\PollingOptions;
-use Mindee\V2\ClientOptions\BaseParameters;
+use Mindee\V2\ClientOptions\BaseProductParameters;
/**
* Parameters for a crop utility inference.
*/
-class CropParameters extends BaseParameters
+class CropParameters extends BaseProductParameters
{
/**
* @var string Slug of the endpoint.
diff --git a/src/V2/Product/Extraction/Params/ExtractionParameters.php b/src/V2/Product/Extraction/Params/ExtractionParameters.php
index 5deaf086..30c8371f 100644
--- a/src/V2/Product/Extraction/Params/ExtractionParameters.php
+++ b/src/V2/Product/Extraction/Params/ExtractionParameters.php
@@ -4,12 +4,12 @@
namespace Mindee\V2\Product\Extraction\Params;
-use Mindee\V2\ClientOptions\BaseParameters;
+use Mindee\V2\ClientOptions\BaseProductParameters;
/**
* Parameters accepted by the asynchronous **inference** v2 endpoint.
*/
-class ExtractionParameters extends BaseParameters
+class ExtractionParameters extends BaseProductParameters
{
/**
* @var string|null Additional text context used by the model during inference.
diff --git a/src/V2/Product/Ocr/Params/OcrParameters.php b/src/V2/Product/Ocr/Params/OcrParameters.php
index 51423730..17587a06 100644
--- a/src/V2/Product/Ocr/Params/OcrParameters.php
+++ b/src/V2/Product/Ocr/Params/OcrParameters.php
@@ -5,12 +5,12 @@
namespace Mindee\V2\Product\Ocr\Params;
use Mindee\ClientOptions\PollingOptions;
-use Mindee\V2\ClientOptions\BaseParameters;
+use Mindee\V2\ClientOptions\BaseProductParameters;
/**
* Parameters for an ocr utility inference.
*/
-class OcrParameters extends BaseParameters
+class OcrParameters extends BaseProductParameters
{
/**
* @var string Slug of the endpoint.
diff --git a/src/V2/Product/Split/Params/SplitParameters.php b/src/V2/Product/Split/Params/SplitParameters.php
index 8de1262e..4b13806d 100644
--- a/src/V2/Product/Split/Params/SplitParameters.php
+++ b/src/V2/Product/Split/Params/SplitParameters.php
@@ -5,12 +5,12 @@
namespace Mindee\V2\Product\Split\Params;
use Mindee\ClientOptions\PollingOptions;
-use Mindee\V2\ClientOptions\BaseParameters;
+use Mindee\V2\ClientOptions\BaseProductParameters;
/**
* Parameters for a split utility inference.
*/
-class SplitParameters extends BaseParameters
+class SplitParameters extends BaseProductParameters
{
/**
* @var string Slug of the endpoint.
diff --git a/src/V2/ClientOptions/ModelSearchParameters.php b/src/V2/Search/Models/ModelSearchParameters.php
similarity index 92%
rename from src/V2/ClientOptions/ModelSearchParameters.php
rename to src/V2/Search/Models/ModelSearchParameters.php
index 51452233..59e231d7 100644
--- a/src/V2/ClientOptions/ModelSearchParameters.php
+++ b/src/V2/Search/Models/ModelSearchParameters.php
@@ -2,7 +2,9 @@
declare(strict_types=1);
-namespace Mindee\V2\ClientOptions;
+namespace Mindee\V2\Search\Models;
+
+use Mindee\V2\ClientOptions\BaseSearchParameters;
/**
* Search parameters for models.
diff --git a/src/V2/ClientOptions/RagDocumentSearchParameters.php b/src/V2/Search/RagDocuments/RagDocumentSearchParameters.php
similarity index 93%
rename from src/V2/ClientOptions/RagDocumentSearchParameters.php
rename to src/V2/Search/RagDocuments/RagDocumentSearchParameters.php
index 46367fa2..63a762f2 100644
--- a/src/V2/ClientOptions/RagDocumentSearchParameters.php
+++ b/src/V2/Search/RagDocuments/RagDocumentSearchParameters.php
@@ -2,10 +2,11 @@
declare(strict_types=1);
-namespace Mindee\V2\ClientOptions;
+namespace Mindee\V2\Search\RagDocuments;
use Mindee\Error\ErrorCode;
use Mindee\Error\MindeeException;
+use Mindee\V2\ClientOptions\BaseSearchParameters;
/**
* Search parameters for RAG documents.
diff --git a/tests/V2/ClientOptions/BaseParametersTest.php b/tests/V2/ClientOptions/BaseProductParametersTest.php
similarity index 80%
rename from tests/V2/ClientOptions/BaseParametersTest.php
rename to tests/V2/ClientOptions/BaseProductParametersTest.php
index 6603b97b..aa3cd11f 100644
--- a/tests/V2/ClientOptions/BaseParametersTest.php
+++ b/tests/V2/ClientOptions/BaseProductParametersTest.php
@@ -4,14 +4,14 @@
namespace V2\ClientOptions;
-use Mindee\V2\ClientOptions\BaseParameters;
+use Mindee\V2\ClientOptions\BaseProductParameters;
use PHPUnit\Framework\TestCase;
-class BaseParametersTest extends TestCase
+class BaseProductParametersTest extends TestCase
{
public function testAsHashShouldSerializeMultipleWebhookIdsAsIndexedFields(): void
{
- $params = new class ('model-id', null, ['first-id', 'second-id']) extends BaseParameters {
+ $params = new class ('model-id', null, ['first-id', 'second-id']) extends BaseProductParameters {
public static string $slug = 'test';
};
From e416291740319a65de385563de85558d390e96c4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ianar=C3=A9=20S=C3=A9vi?=
Date: Mon, 10 Aug 2026 13:04:55 +0200
Subject: [PATCH 3/3] use generic search method
---
bin/V2/SearchModelsCommand.php | 4 +-
bin/V2/SearchRagDocumentsCommand.php | 4 +-
src/V2/Client.php | 33 ++++++----
.../ClientOptions/BaseProductParameters.php | 2 +-
src/V2/ClientOptions/BaseSearchParameters.php | 5 ++
src/V2/Http/MindeeApiV2.php | 50 ++++------------
.../Params/ClassificationParameters.php | 4 +-
src/V2/Product/Crop/Params/CropParameters.php | 4 +-
.../Params/ExtractionParameters.php | 4 +-
src/V2/Product/Ocr/Params/OcrParameters.php | 4 +-
.../Search/Models/ModelSearchParameters.php | 5 ++
.../RagDocumentSearchParameters.php | 5 ++
tests/V2/Parsing/SearchResponseTest.php | 44 --------------
tests/V2/Search/ModelSearchFunctional.php | 60 +++++++++++++++++++
tests/V2/Search/ModelSearchTest.php | 44 ++++++++++++++
.../V2/Search/RagDocumentSearchFunctional.php | 35 +++++++++++
.../RagDocumentSearchTest.php} | 43 ++++++-------
17 files changed, 226 insertions(+), 124 deletions(-)
delete mode 100644 tests/V2/Parsing/SearchResponseTest.php
create mode 100644 tests/V2/Search/ModelSearchFunctional.php
create mode 100644 tests/V2/Search/ModelSearchTest.php
create mode 100644 tests/V2/Search/RagDocumentSearchFunctional.php
rename tests/V2/{Parsing/RagDocumentSearchResponseTest.php => Search/RagDocumentSearchTest.php} (56%)
diff --git a/bin/V2/SearchModelsCommand.php b/bin/V2/SearchModelsCommand.php
index 9bb62661..3031b1b4 100644
--- a/bin/V2/SearchModelsCommand.php
+++ b/bin/V2/SearchModelsCommand.php
@@ -7,6 +7,7 @@
use Exception;
use Mindee\V2\Client;
use Mindee\V2\Error\MindeeV2HttpException;
+use Mindee\V2\Parsing\Search\ModelSearchResponse;
use Mindee\V2\Search\Models\ModelSearchParameters;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
@@ -84,7 +85,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$client = new Client($apiKey ?: null);
try {
- $response = $client->searchModels(
+ $response = $client->search(
+ ModelSearchResponse::class,
new ModelSearchParameters($name ?: null, $modelType ?: null)
);
} catch (MindeeV2HttpException $e) {
diff --git a/bin/V2/SearchRagDocumentsCommand.php b/bin/V2/SearchRagDocumentsCommand.php
index a91ffbfd..7a9f07af 100644
--- a/bin/V2/SearchRagDocumentsCommand.php
+++ b/bin/V2/SearchRagDocumentsCommand.php
@@ -7,6 +7,7 @@
use Exception;
use Mindee\V2\Client;
use Mindee\V2\Error\MindeeV2HttpException;
+use Mindee\V2\Parsing\Search\RagDocumentSearchResponse;
use Mindee\V2\Search\RagDocuments\RagDocumentSearchParameters;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
@@ -82,7 +83,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$client = new Client($apiKey ?: null);
try {
- $response = $client->searchRagDocuments(
+ $response = $client->search(
+ RagDocumentSearchResponse::class,
new RagDocumentSearchParameters($modelId, $filename ?: null)
);
} catch (MindeeV2HttpException $e) {
diff --git a/src/V2/Client.php b/src/V2/Client.php
index 48e940bf..ac2a8b1d 100644
--- a/src/V2/Client.php
+++ b/src/V2/Client.php
@@ -13,10 +13,10 @@
use Mindee\V2\Http\MindeeApiV2;
use Mindee\V2\Parsing\Inference\BaseResponse;
use Mindee\V2\Parsing\Job\JobResponse;
+use Mindee\V2\ClientOptions\BaseSearchParameters;
+use Mindee\V2\Parsing\Search\BaseSearchResponse;
use Mindee\V2\Parsing\Search\ModelSearchResponse;
-use Mindee\V2\Parsing\Search\RagDocumentSearchResponse;
use Mindee\V2\Search\Models\ModelSearchParameters;
-use Mindee\V2\Search\RagDocuments\RagDocumentSearchParameters;
/**
* Mindee Client V2.
@@ -168,22 +168,31 @@ public function enqueueAndGetResult(
}
/**
- * Searches for a list of available models matching the given criteria.
- * @param ModelSearchParameters|null $params Search parameters (name, model type, pagination).
- * @return ModelSearchResponse The list of models matching the criteria.
+ * Searches for resources matching the given criteria.
+ *
+ * @template T of BaseSearchResponse
+ * @param string $responseClass The response class to construct.
+ * @phpstan-param class-string $responseClass
+ * @param BaseSearchParameters $params Search parameters.
+ * @return T
*/
- public function searchModels(?ModelSearchParameters $params = null): ModelSearchResponse
+ public function search(string $responseClass, BaseSearchParameters $params): BaseSearchResponse
{
- return $this->mindeeApi->searchModels($params ?? new ModelSearchParameters());
+ return $this->mindeeApi->reqGetSearch($responseClass, $params);
}
/**
- * Searches for a list of RAG documents matching the given criteria.
- * @param RagDocumentSearchParameters $params Search parameters.
- * @return RagDocumentSearchResponse The list of RAG documents matching the criteria.
+ * Searches for a list of available models for the given API key.
+ * @param string|null $modelName Optional model name to filter by.
+ * @param string|null $modelType Optional model type to filter by.
+ * @return ModelSearchResponse The list of models matching the criteria.
+ * @deprecated Use search(ModelSearchResponse::class, new ModelSearchParameters(...)) instead.
*/
- public function searchRagDocuments(RagDocumentSearchParameters $params): RagDocumentSearchResponse
+ public function searchModels(?string $modelName = null, ?string $modelType = null): ModelSearchResponse
{
- return $this->mindeeApi->searchRagDocuments($params);
+ return $this->mindeeApi->reqGetSearch(
+ ModelSearchResponse::class,
+ new ModelSearchParameters($modelName, $modelType)
+ );
}
}
diff --git a/src/V2/ClientOptions/BaseProductParameters.php b/src/V2/ClientOptions/BaseProductParameters.php
index 62492eb1..91ad08fd 100644
--- a/src/V2/ClientOptions/BaseProductParameters.php
+++ b/src/V2/ClientOptions/BaseProductParameters.php
@@ -20,7 +20,7 @@ abstract class BaseProductParameters
public array $webhookIds;
/**
- * @var string Slug of the endpoint.
+ * @var string Slug of the product.
*/
public static string $slug;
diff --git a/src/V2/ClientOptions/BaseSearchParameters.php b/src/V2/ClientOptions/BaseSearchParameters.php
index 5d222509..9d1da486 100644
--- a/src/V2/ClientOptions/BaseSearchParameters.php
+++ b/src/V2/ClientOptions/BaseSearchParameters.php
@@ -9,6 +9,11 @@
*/
abstract class BaseSearchParameters
{
+ /**
+ * @var string Slug of the resource.
+ */
+ public static string $slug;
+
/**
* @param integer|null $page 1-based page index.
* @param integer|null $perPage Number of items per page.
diff --git a/src/V2/Http/MindeeApiV2.php b/src/V2/Http/MindeeApiV2.php
index a16e5f1d..162edd42 100644
--- a/src/V2/Http/MindeeApiV2.php
+++ b/src/V2/Http/MindeeApiV2.php
@@ -18,15 +18,13 @@
use Mindee\Input\LocalInputSource;
use Mindee\Input\UrlInputSource;
use Mindee\V2\ClientOptions\BaseProductParameters;
+use Mindee\V2\ClientOptions\BaseSearchParameters;
use Mindee\V2\Error\MindeeV2HttpException;
use Mindee\V2\Error\MindeeV2HttpUnknownException;
use Mindee\V2\Parsing\Error\ErrorResponse;
use Mindee\V2\Parsing\Inference\BaseResponse;
use Mindee\V2\Parsing\Job\JobResponse;
-use Mindee\V2\Parsing\Search\ModelSearchResponse;
-use Mindee\V2\Parsing\Search\RagDocumentSearchResponse;
-use Mindee\V2\Search\Models\ModelSearchParameters;
-use Mindee\V2\Search\RagDocuments\RagDocumentSearchParameters;
+use Mindee\V2\Parsing\Search\BaseSearchResponse;
use ReflectionClass;
use ReflectionException;
use ReflectionProperty;
@@ -392,14 +390,18 @@ private function checkValidResponse(array $result): void
}
/**
- * Makes a GET call to a search endpoint.
- * @param string $path Search endpoint path (e.g. `/v2/search/models`).
- * @param array $queryParams Query parameters to append.
- * @return array> Server response.
+ * Makes a GET call to a search endpoint and returns the deserialized response.
+ *
+ * @template T of BaseSearchResponse
+ * @param string $responseClass The response class to construct.
+ * @phpstan-param class-string $responseClass
+ * @param BaseSearchParameters $params Search parameters (slug and query params derived from this).
+ * @return T
*/
- private function reqGetSearch(string $path, array $queryParams): array
+ public function reqGetSearch(string $responseClass, BaseSearchParameters $params): BaseResponse
{
- $url = $this->baseUrl . $path;
+ $queryParams = $params->getQueryParams();
+ $url = $this->baseUrl . "/v2/search/" . $params::$slug;
if (!empty($queryParams)) {
$url .= '?' . http_build_query($queryParams);
}
@@ -413,32 +415,6 @@ private function reqGetSearch(string $path, array $queryParams): array
'code' => curl_getinfo($ch, CURLINFO_HTTP_CODE),
];
curl_close($ch);
- return $resp;
- }
-
- /**
- * Retrieves a list of models matching the given criteria.
- * @param ModelSearchParameters $params Search parameters.
- * @return ModelSearchResponse The list of models matching the criteria.
- */
- public function searchModels(ModelSearchParameters $params): ModelSearchResponse
- {
- return $this->processResponse(
- ModelSearchResponse::class,
- $this->reqGetSearch("/v2/search/models", $params->getQueryParams())
- );
- }
-
- /**
- * Retrieves a list of RAG documents matching the given criteria.
- * @param RagDocumentSearchParameters $params Search parameters.
- * @return RagDocumentSearchResponse The list of RAG documents matching the criteria.
- */
- public function searchRagDocuments(RagDocumentSearchParameters $params): RagDocumentSearchResponse
- {
- return $this->processResponse(
- RagDocumentSearchResponse::class,
- $this->reqGetSearch("/v2/search/rag-documents", $params->getQueryParams())
- );
+ return $this->processResponse($responseClass, $resp);
}
}
diff --git a/src/V2/Product/Classification/Params/ClassificationParameters.php b/src/V2/Product/Classification/Params/ClassificationParameters.php
index 7a626759..537bf5ca 100644
--- a/src/V2/Product/Classification/Params/ClassificationParameters.php
+++ b/src/V2/Product/Classification/Params/ClassificationParameters.php
@@ -8,12 +8,12 @@
use Mindee\V2\ClientOptions\BaseProductParameters;
/**
- * Parameters for a classification utility inference.
+ * Parameters accepted by the asynchronous Classification product endpoint.
*/
class ClassificationParameters extends BaseProductParameters
{
/**
- * @var string Slug of the endpoint.
+ * @var string Slug of the prodcut.
*/
public static string $slug = "classification";
diff --git a/src/V2/Product/Crop/Params/CropParameters.php b/src/V2/Product/Crop/Params/CropParameters.php
index 52fcef5d..d81fd378 100644
--- a/src/V2/Product/Crop/Params/CropParameters.php
+++ b/src/V2/Product/Crop/Params/CropParameters.php
@@ -8,12 +8,12 @@
use Mindee\V2\ClientOptions\BaseProductParameters;
/**
- * Parameters for a crop utility inference.
+ * Parameters accepted by the asynchronous Crop product endpoint.
*/
class CropParameters extends BaseProductParameters
{
/**
- * @var string Slug of the endpoint.
+ * @var string Slug of the product.
*/
public static string $slug = "crop";
diff --git a/src/V2/Product/Extraction/Params/ExtractionParameters.php b/src/V2/Product/Extraction/Params/ExtractionParameters.php
index 30c8371f..e2ca64af 100644
--- a/src/V2/Product/Extraction/Params/ExtractionParameters.php
+++ b/src/V2/Product/Extraction/Params/ExtractionParameters.php
@@ -7,7 +7,7 @@
use Mindee\V2\ClientOptions\BaseProductParameters;
/**
- * Parameters accepted by the asynchronous **inference** v2 endpoint.
+ * Parameters accepted by the asynchronous Extraction product endpoint.
*/
class ExtractionParameters extends BaseProductParameters
{
@@ -23,7 +23,7 @@ class ExtractionParameters extends BaseProductParameters
public ?DataSchema $dataSchema;
/**
- * @var string Slug of the endpoint.
+ * @var string Slug of the product.
*/
public static string $slug = "extraction";
diff --git a/src/V2/Product/Ocr/Params/OcrParameters.php b/src/V2/Product/Ocr/Params/OcrParameters.php
index 17587a06..7882ded1 100644
--- a/src/V2/Product/Ocr/Params/OcrParameters.php
+++ b/src/V2/Product/Ocr/Params/OcrParameters.php
@@ -8,12 +8,12 @@
use Mindee\V2\ClientOptions\BaseProductParameters;
/**
- * Parameters for an ocr utility inference.
+ * Parameters accepted by the asynchronous OCR product endpoint.
*/
class OcrParameters extends BaseProductParameters
{
/**
- * @var string Slug of the endpoint.
+ * @var string Slug of the product.
*/
public static string $slug = "ocr";
diff --git a/src/V2/Search/Models/ModelSearchParameters.php b/src/V2/Search/Models/ModelSearchParameters.php
index 59e231d7..4756f1f1 100644
--- a/src/V2/Search/Models/ModelSearchParameters.php
+++ b/src/V2/Search/Models/ModelSearchParameters.php
@@ -11,6 +11,11 @@
*/
class ModelSearchParameters extends BaseSearchParameters
{
+ /**
+ * @var string Slug of the resource.
+ */
+ public static string $slug = "models";
+
/**
* @param string|null $name Case-insensitive search term for the model name.
* @param string|null $modelType Case-insensitive search term for the model type.
diff --git a/src/V2/Search/RagDocuments/RagDocumentSearchParameters.php b/src/V2/Search/RagDocuments/RagDocumentSearchParameters.php
index 63a762f2..d25a9cd7 100644
--- a/src/V2/Search/RagDocuments/RagDocumentSearchParameters.php
+++ b/src/V2/Search/RagDocuments/RagDocumentSearchParameters.php
@@ -13,6 +13,11 @@
*/
class RagDocumentSearchParameters extends BaseSearchParameters
{
+ /**
+ * @var string Slug of the resource.
+ */
+ public static string $slug = "rag-documents";
+
/**
* @param string|null $modelId Model identifier to search in (required).
* @param string|null $filename Case-insensitive substring search on filename.
diff --git a/tests/V2/Parsing/SearchResponseTest.php b/tests/V2/Parsing/SearchResponseTest.php
deleted file mode 100644
index bc3f6fce..00000000
--- a/tests/V2/Parsing/SearchResponseTest.php
+++ /dev/null
@@ -1,44 +0,0 @@
-models);
- foreach ($response->models as $model) {
- self::assertInstanceOf(SearchModel::class, $model);
- self::assertNotEmpty($model->id);
- self::assertNotEmpty($model->name);
- }
- self::assertCount(2, $response->models[0]->webhooks);
- self::assertEquals("https://failure.mindee.com", $response->models[0]->webhooks[0]->url);
-
- self::assertEquals(50, $response->pagination->perPage);
- self::assertEquals(1, $response->pagination->page);
- self::assertGreaterThanOrEqual(5, $response->pagination->totalItems);
- self::assertEquals(1, $response->pagination->totalPages);
- }
-}
diff --git a/tests/V2/Search/ModelSearchFunctional.php b/tests/V2/Search/ModelSearchFunctional.php
new file mode 100644
index 00000000..ad8f9515
--- /dev/null
+++ b/tests/V2/Search/ModelSearchFunctional.php
@@ -0,0 +1,60 @@
+client = new Client(getenv('MINDEE_V2_API_KEY') ?: null);
+ }
+
+ public function testModelSearch_mustHaveResults(): void
+ {
+ $response = $this->client->search(ModelSearchResponse::class, new ModelSearchParameters());
+
+ self::assertNotNull($response);
+ self::assertNotNull($response->models);
+ self::assertNotEmpty($response->models);
+ self::assertNotNull($response->pagination);
+ self::assertGreaterThan(1, $response->pagination->totalItems);
+ self::assertEquals(1, $response->pagination->page);
+ }
+
+ public function testModelSearch_mustReturnEmpty(): void
+ {
+ $response = $this->client->search(
+ ModelSearchResponse::class,
+ new ModelSearchParameters(name: "je n'existe pas tralala")
+ );
+
+ self::assertNotNull($response);
+ self::assertNotNull($response->models);
+ self::assertEmpty($response->models);
+ self::assertNotNull($response->pagination);
+ self::assertEquals(0, $response->pagination->totalItems);
+ self::assertEquals(1, $response->pagination->page);
+ }
+
+ public function testModelSearch_mustReturnEmptyObsolete(): void
+ {
+ /** @phpstan-ignore method.deprecated */
+ $response = $this->client->searchModels("je n'existe pas tralala");
+
+ self::assertNotNull($response);
+ self::assertNotNull($response->models);
+ self::assertEmpty($response->models);
+ self::assertNotNull($response->pagination);
+ self::assertEquals(0, $response->pagination->totalItems);
+ self::assertEquals(1, $response->pagination->page);
+ }
+}
diff --git a/tests/V2/Search/ModelSearchTest.php b/tests/V2/Search/ModelSearchTest.php
new file mode 100644
index 00000000..c87d0388
--- /dev/null
+++ b/tests/V2/Search/ModelSearchTest.php
@@ -0,0 +1,44 @@
+models);
+ self::assertEquals(5, $response->pagination->totalItems);
+ self::assertEquals(1, $response->pagination->page);
+ self::assertEquals(50, $response->pagination->perPage);
+ self::assertEquals(1, $response->pagination->totalPages);
+
+ $firstItem = $response->models[0];
+ self::assertEquals("Extraction With Webhooks", $firstItem->name);
+ self::assertEquals("afde5151-aa11-aa11-9289-fa04e50ca3b9", $firstItem->id);
+ self::assertEquals("extraction", $firstItem->modelType);
+
+ self::assertCount(2, $firstItem->webhooks);
+ self::assertEquals("a2286ed9-aa11-aa11-bdc5-2f8496c5641a", $firstItem->webhooks[0]->id);
+ self::assertEquals("FAILURE", $firstItem->webhooks[0]->name);
+ self::assertEquals("https://failure.mindee.com", $firstItem->webhooks[0]->url);
+
+ $lastItem = $response->models[4];
+ self::assertEquals("Extraction Without Webhooks Key", $lastItem->name);
+ self::assertEquals("e14e0923-ee55-ee55-a335-8d2110917d7b", $lastItem->id);
+ }
+}
diff --git a/tests/V2/Search/RagDocumentSearchFunctional.php b/tests/V2/Search/RagDocumentSearchFunctional.php
new file mode 100644
index 00000000..f31d0c15
--- /dev/null
+++ b/tests/V2/Search/RagDocumentSearchFunctional.php
@@ -0,0 +1,35 @@
+client = new Client(getenv('MINDEE_V2_API_KEY') ?: null);
+ $this->findocModelId = getenv('MINDEE_V2_FINDOC_MODEL_ID') ?: '';
+ }
+
+ public function testRagDocumentSearch_mustHaveResults(): void
+ {
+ $response = $this->client->search(
+ RagDocumentSearchResponse::class,
+ new RagDocumentSearchParameters(modelId: $this->findocModelId)
+ );
+
+ self::assertNotNull($response);
+ self::assertNotNull($response->ragDocuments);
+ self::assertNotNull($response->pagination);
+ self::assertEquals(1, $response->pagination->page);
+ }
+}
diff --git a/tests/V2/Parsing/RagDocumentSearchResponseTest.php b/tests/V2/Search/RagDocumentSearchTest.php
similarity index 56%
rename from tests/V2/Parsing/RagDocumentSearchResponseTest.php
rename to tests/V2/Search/RagDocumentSearchTest.php
index 166915aa..89a10496 100644
--- a/tests/V2/Parsing/RagDocumentSearchResponseTest.php
+++ b/tests/V2/Search/RagDocumentSearchTest.php
@@ -1,55 +1,58 @@
ragDocuments);
- foreach ($response->ragDocuments as $document) {
- self::assertInstanceOf(RagDocument::class, $document);
- self::assertNotEmpty($document->id);
- self::assertNotEmpty($document->modelId);
- self::assertNotEmpty($document->filename);
- }
+ self::assertEquals(3, $response->pagination->totalItems);
+ self::assertEquals(1, $response->pagination->page);
+ self::assertEquals(50, $response->pagination->perPage);
+ self::assertEquals(1, $response->pagination->totalPages);
$firstItem = $response->ragDocuments[0];
self::assertEquals("cc831599-c545-48b7-aa27-6d7ccd5b8d32", $firstItem->id);
self::assertEquals("12345678-1234-1234-1234-123456789abc", $firstItem->modelId);
self::assertEquals("invoice_01.pdf", $firstItem->filename);
+ self::assertEquals(new DateTimeImmutable("2026-06-30T13:13:46.168586Z"), $firstItem->createdAt);
self::assertEquals(0, $firstItem->totalMatches);
self::assertNull($firstItem->lastMatchAt);
self::assertEquals("Processing", $firstItem->status);
+ $secondItem = $response->ragDocuments[1];
+ self::assertEquals("27467e4c-5602-4315-90d9-3d2da69b05ab", $secondItem->id);
+ self::assertEquals("12345678-1234-1234-1234-123456789abc", $secondItem->modelId);
+ self::assertEquals("invoice_02.pdf", $secondItem->filename);
+ self::assertEquals(new DateTimeImmutable("2026-06-30T13:13:46.168586Z"), $secondItem->createdAt);
+ self::assertEquals(0, $secondItem->totalMatches);
+ self::assertNull($secondItem->lastMatchAt);
+ self::assertEquals("Draft", $secondItem->status);
+
$thirdItem = $response->ragDocuments[2];
self::assertEquals("a6bcae7d-0439-476b-8a63-5a39ec05dc21", $thirdItem->id);
+ self::assertEquals("12345678-1234-1234-1234-jobid1234567", $thirdItem->modelId);
self::assertEquals("invoice_03.pdf", $thirdItem->filename);
+ self::assertEquals(new DateTimeImmutable("2026-06-17T14:35:46.228006Z"), $thirdItem->createdAt);
self::assertEquals(5, $thirdItem->totalMatches);
- self::assertNotNull($thirdItem->lastMatchAt);
+ self::assertEquals(new DateTimeImmutable("2026-06-18T14:35:46.248006Z"), $thirdItem->lastMatchAt);
self::assertEquals("Active", $thirdItem->status);
-
- self::assertEquals(50, $response->pagination->perPage);
- self::assertEquals(1, $response->pagination->page);
- self::assertEquals(3, $response->pagination->totalItems);
- self::assertEquals(1, $response->pagination->totalPages);
}
}