33import com .mindee .MindeeException ;
44import com .mindee .input .LocalInputSource ;
55import com .mindee .input .URLInputSource ;
6+ import com .mindee .v2 .clientoptions .BaseAnnotationParameters ;
67import com .mindee .v2 .clientoptions .BaseProductParameters ;
8+ import com .mindee .v2 .clientoptions .BaseRagDocumentUploadParameters ;
79import com .mindee .v2 .clientoptions .BaseSearchParameters ;
810import com .mindee .v2 .clientoptions .PollingOptions ;
911import com .mindee .v2 .http .MindeeApiV2 ;
1012import com .mindee .v2 .http .MindeeHttpApiV2 ;
1113import com .mindee .v2 .http .MindeeHttpExceptionV2 ;
14+ import com .mindee .v2 .parsing .BaseRagAnnotationResponse ;
1215import com .mindee .v2 .parsing .BaseResponse ;
1316import com .mindee .v2 .parsing .JobResponse ;
1417import com .mindee .v2 .parsing .error .ErrorResponse ;
@@ -78,10 +81,10 @@ public JobResponse enqueue(
7881 * Can be used for polling.
7982 */
8083 public JobResponse getJobFromUrl (String pollingUrl ) {
81- logger .log (System .Logger .Level .INFO , "Getting Job at: {0}" , pollingUrl );
8284 if (pollingUrl == null || pollingUrl .isBlank ()) {
8385 throw new IllegalArgumentException ("Job URL cannot be null or blank." );
8486 }
87+ logger .log (System .Logger .Level .INFO , "Getting Job at: {0}" , pollingUrl );
8588 return mindeeApi .reqGetJobByUrl (pollingUrl );
8689 }
8790
@@ -90,10 +93,10 @@ public JobResponse getJobFromUrl(String pollingUrl) {
9093 * Can be used for polling.
9194 */
9295 public JobResponse getJob (String jobId ) {
93- logger .log (System .Logger .Level .INFO , "Getting job ID: {0}" , jobId );
9496 if (jobId == null || jobId .isBlank ()) {
9597 throw new IllegalArgumentException ("jobId must not be null or blank." );
9698 }
99+ logger .log (System .Logger .Level .INFO , "Getting job ID: {0}" , jobId );
97100 return mindeeApi .reqGetJobById (jobId );
98101 }
99102
@@ -105,10 +108,10 @@ public <TResponse extends BaseResponse> TResponse getResult(
105108 Class <TResponse > responseClass ,
106109 String inferenceId
107110 ) {
108- logger .log (System .Logger .Level .INFO , "Getting result with ID: {0}" , inferenceId );
109- if (inferenceId == null || inferenceId .trim ().isEmpty ()) {
111+ if (inferenceId == null || inferenceId .isBlank ()) {
110112 throw new IllegalArgumentException ("inferenceId must not be null or blank." );
111113 }
114+ logger .log (System .Logger .Level .INFO , "Getting result with ID: {0}" , inferenceId );
112115 return mindeeApi .reqGetResultById (responseClass , inferenceId );
113116 }
114117
@@ -120,10 +123,10 @@ public <TResponse extends BaseResponse> TResponse getResultFromUrl(
120123 Class <TResponse > responseClass ,
121124 String inferenceUrl
122125 ) {
123- logger .log (System .Logger .Level .INFO , "Getting result at: {0}" , inferenceUrl );
124- if (inferenceUrl == null || inferenceUrl .trim ().isEmpty ()) {
126+ if (inferenceUrl == null || inferenceUrl .isBlank ()) {
125127 throw new IllegalArgumentException ("inferenceUrl must not be null or blank." );
126128 }
129+ logger .log (System .Logger .Level .INFO , "Getting result at: {0}" , inferenceUrl );
127130 return mindeeApi .reqGetResultByUrl (responseClass , inferenceUrl );
128131 }
129132
@@ -277,6 +280,235 @@ public SearchResponse searchModels(String modelName, String modelType) {
277280 .reqGetSearch (ModelSearchParameters .builder ().name (modelName ).modelType (modelType ).build ());
278281 }
279282
283+ /**
284+ * Not recommended for general use, prefer {@link #uploadAndGetRagDocument}.
285+ * You will need to poll until the document is ready for use.
286+ * Add a document to the RAG database.
287+ *
288+ * @param inputSource The file to upload.
289+ * @param parameters RAG document upload parameters.
290+ * @return an instance of {@link BaseRagAnnotationResponse}.
291+ */
292+ public <TAnnotationResponse extends BaseRagAnnotationResponse > TAnnotationResponse uploadRagDocument (
293+ LocalInputSource inputSource ,
294+ BaseRagDocumentUploadParameters <TAnnotationResponse > parameters
295+ ) throws IOException {
296+ logger .log (System .Logger .Level .INFO , "Adding a document to the RAG database" );
297+ return mindeeApi .reqPostRagDocument (parameters , inputSource );
298+ }
299+
300+ /**
301+ * Add a document to the RAG database, poll, and return the initial annotation.
302+ *
303+ * @param inputSource The file to upload.
304+ * @param parameters RAG document upload parameters.
305+ * @return an instance of {@link BaseRagAnnotationResponse}.
306+ */
307+ public <TAnnotationResponse extends BaseRagAnnotationResponse > TAnnotationResponse uploadAndGetRagDocument (
308+ LocalInputSource inputSource ,
309+ BaseRagDocumentUploadParameters <TAnnotationResponse > parameters
310+ ) throws IOException , InterruptedException {
311+ return uploadAndGetRagDocument (inputSource , parameters , null );
312+ }
313+
314+ /**
315+ * Add a document to the RAG database, poll, and return the initial annotation.
316+ *
317+ * @param inputSource The file to upload.
318+ * @param parameters RAG document upload parameters.
319+ * @param pollingOptions Polling options (if null, default options are used).
320+ * @return an instance of {@link BaseRagAnnotationResponse}.
321+ */
322+ public <TAnnotationResponse extends BaseRagAnnotationResponse > TAnnotationResponse uploadAndGetRagDocument (
323+ LocalInputSource inputSource ,
324+ BaseRagDocumentUploadParameters <TAnnotationResponse > parameters ,
325+ PollingOptions pollingOptions
326+ ) throws IOException , InterruptedException {
327+ if (pollingOptions == null ) {
328+ pollingOptions = PollingOptions .builder ().build ();
329+ }
330+ TAnnotationResponse initialResponse = uploadRagDocument (inputSource , parameters );
331+ if (!"Processing" .equals (initialResponse .getStatus ())) {
332+ return initialResponse ;
333+ }
334+ return pollForRagDocument (parameters .getResponseClass (), initialResponse , pollingOptions );
335+ }
336+
337+ /**
338+ * Not recommended for general use, prefer {@link #getReadyRagDocument}.
339+ * You will need to poll until the document is ready for use.
340+ * Get a document's info and annotations from the RAG database.
341+ *
342+ * @param responseClass The class of the response.
343+ * @param documentId The document's ID.
344+ * @return an instance of {@link BaseRagAnnotationResponse}.
345+ */
346+ public <TAnnotationResponse extends BaseRagAnnotationResponse > TAnnotationResponse getRagDocument (
347+ Class <TAnnotationResponse > responseClass ,
348+ String documentId
349+ ) {
350+ if (documentId == null || documentId .isBlank ()) {
351+ throw new IllegalArgumentException ("documentId must not be null or blank." );
352+ }
353+ logger .log (System .Logger .Level .INFO , "Getting RAG document ID: {0}" , documentId );
354+ return mindeeApi .reqGetRagAnnotation (responseClass , documentId );
355+ }
356+
357+ /**
358+ * Get a document's info and annotations from the RAG database, polling if it is still processing.
359+ *
360+ * @param responseClass The class of the response.
361+ * @param documentId The document's ID.
362+ * @return an instance of {@link BaseRagAnnotationResponse}.
363+ */
364+ public <TAnnotationResponse extends BaseRagAnnotationResponse > TAnnotationResponse getReadyRagDocument (
365+ Class <TAnnotationResponse > responseClass ,
366+ String documentId
367+ ) throws InterruptedException {
368+ return getReadyRagDocument (responseClass , documentId , null );
369+ }
370+
371+ /**
372+ * Get a document's info and annotations from the RAG database, polling if it is still processing.
373+ *
374+ * @param responseClass The class of the response.
375+ * @param documentId The document's ID.
376+ * @param pollingOptions Polling options (if null, default options are used).
377+ * @return an instance of {@link BaseRagAnnotationResponse}.
378+ */
379+ public <TAnnotationResponse extends BaseRagAnnotationResponse > TAnnotationResponse getReadyRagDocument (
380+ Class <TAnnotationResponse > responseClass ,
381+ String documentId ,
382+ PollingOptions pollingOptions
383+ ) throws InterruptedException {
384+ TAnnotationResponse initialResponse = getRagDocument (responseClass , documentId );
385+ if (!"Processing" .equals (initialResponse .getStatus ())) {
386+ return initialResponse ;
387+ }
388+
389+ if (pollingOptions == null ) {
390+ pollingOptions = PollingOptions .builder ().build ();
391+ }
392+ return pollForRagDocument (responseClass , initialResponse , pollingOptions );
393+ }
394+
395+ /**
396+ * Not recommended for general use, prefer {@link #updateAndGetRagAnnotation}.
397+ * You will need to poll until the document is ready for use.
398+ * Update a document's annotations in the RAG database.
399+ *
400+ * @param parameters Annotation parameters.
401+ * @return an instance of {@link BaseRagAnnotationResponse}.
402+ */
403+ public <TAnnotationResponse extends BaseRagAnnotationResponse > TAnnotationResponse updateRagAnnotation (
404+ BaseAnnotationParameters <TAnnotationResponse > parameters
405+ ) {
406+ logger
407+ .log (System .Logger .Level .INFO , "Updating RAG document ID: {0}" , parameters .getDocumentId ());
408+ return mindeeApi .reqPatchRagAnnotation (parameters );
409+ }
410+
411+ /**
412+ * Update a document's annotations in the RAG database and poll until complete.
413+ *
414+ * @param parameters Annotation parameters.
415+ * @return an instance of {@link BaseRagAnnotationResponse}.
416+ */
417+ public <TAnnotationResponse extends BaseRagAnnotationResponse > TAnnotationResponse updateAndGetRagAnnotation (
418+ BaseAnnotationParameters <TAnnotationResponse > parameters
419+ ) throws InterruptedException {
420+ return updateAndGetRagAnnotation (parameters , null );
421+ }
422+
423+ /**
424+ * Update a document's annotations in the RAG database and poll until complete.
425+ *
426+ * @param parameters Annotation parameters.
427+ * @param pollingOptions Polling options (if null, default options are used).
428+ * @return an instance of {@link BaseRagAnnotationResponse}.
429+ */
430+ public <TAnnotationResponse extends BaseRagAnnotationResponse > TAnnotationResponse updateAndGetRagAnnotation (
431+ BaseAnnotationParameters <TAnnotationResponse > parameters ,
432+ PollingOptions pollingOptions
433+ ) throws InterruptedException {
434+ TAnnotationResponse initialResponse = updateRagAnnotation (parameters );
435+ if (!"Processing" .equals (initialResponse .getStatus ())) {
436+ return initialResponse ;
437+ }
438+
439+ if (pollingOptions == null ) {
440+ pollingOptions = PollingOptions .builder ().build ();
441+ }
442+ return pollForRagDocument (parameters .getResponseClass (), initialResponse , pollingOptions );
443+ }
444+
445+ /**
446+ * Delete a document from the RAG database.
447+ * For extraction models only.
448+ *
449+ * @param documentId The document's ID.
450+ * @return true if successful.
451+ */
452+ public boolean deleteExtractionRagDocument (String documentId ) {
453+ if (documentId == null || documentId .isBlank ()) {
454+ throw new IllegalArgumentException ("documentId must not be null or blank." );
455+ }
456+ logger .log (System .Logger .Level .INFO , "Deleting RAG document ID: {0}" , documentId );
457+ return mindeeApi .reqDeleteExtractionRagDocument (documentId );
458+ }
459+
460+ /**
461+ * Poll until the RAG document is finished processing or the max number of attempts is reached.
462+ *
463+ * @param responseClass The class of the response.
464+ * @param initialResponse The initial annotation response.
465+ * @param pollingOptions Polling options.
466+ * @return an instance of {@link BaseRagAnnotationResponse}.
467+ * @throws InterruptedException Throws if the thread is interrupted.
468+ */
469+ private <TAnnotationResponse extends BaseRagAnnotationResponse > TAnnotationResponse pollForRagDocument (
470+ Class <TAnnotationResponse > responseClass ,
471+ BaseRagAnnotationResponse initialResponse ,
472+ PollingOptions pollingOptions
473+ ) throws InterruptedException {
474+ logger
475+ .log (System .Logger .Level .INFO , "Polling for RAG document ID: {0}" , initialResponse .getId ());
476+ int maxRetries = pollingOptions .getMaxRetries () + 1 ;
477+
478+ logger
479+ .log (
480+ System .Logger .Level .DEBUG ,
481+ "Waiting {0} seconds before attempting to retrieve the result..." ,
482+ pollingOptions .getInitialDelaySec ()
483+ );
484+
485+ interruptibleSleep (
486+ (long ) (pollingOptions .getInitialDelaySec () * 1000 ),
487+ pollingOptions .getCancelToken ()
488+ );
489+
490+ String documentId = initialResponse .getId ();
491+ long intervalMillis = (long ) (pollingOptions .getIntervalSec () * 1000 );
492+ int retryCount = 1 ;
493+
494+ while (retryCount < maxRetries ) {
495+ logger .log (System .Logger .Level .DEBUG , "Poll attempt {0} of {1}" , retryCount , maxRetries );
496+
497+ TAnnotationResponse response = getRagDocument (responseClass , documentId );
498+ retryCount ++;
499+
500+ String status = response .getStatus ();
501+ if ("Processing" .equals (status )) {
502+ interruptibleSleep (intervalMillis , pollingOptions .getCancelToken ());
503+ } else if ("Failed" .equals (status )) {
504+ throw new MindeeException ("RAG failed without an error payload." );
505+ } else {
506+ return response ;
507+ }
508+ }
509+ throw new MindeeException ("RAG polling not complete after " + retryCount + " attempts." );
510+ }
511+
280512 /**
281513 * Common logic for polling an asynchronous job for local & url files.
282514 *
0 commit comments