Skip to content

Commit 4ae819a

Browse files
fix!: Refactor ConvertDocumentResponse for polymorphic deserialization based on docling-serve API response (#388)
* fix: Refactor ConvertDocumentResponse for polymorphic deserialization (#206) Refactored ConvertDocumentResponse into a sealed abstract base class with three concrete implementations (InBodyConvertDocumentResponse, PreSignedUrlConvertDocumentResponse, ZipArchiveConvertDocumentResponse) to properly handle different response types from Docling Serve API. - Use Jackson @JsonTypeInfo with DEDUCTION for automatic type detection of JSON-based responses - Leverage Java 17 sealed classes for type safety - Update reference implementation in docling-serve-client to handle these new types - Add comprehensive test coverage for all response types - Update documentation and Javadoc - Apply Spotless Fixes #206 Signed-off-by: Arpan Chakraborty <arpan.chakraborty1908@gmail.com> * Updating project version due to breaking change * - Applied requested enhancements. - Removed previously added util package in docling-serve-client. - Moved the utility functions from the Utils class under docling-serve-client to StreamResponse.ResponseHeaders as defaults - Updated whats-new.md to document breaking changes in this release. Signed-off-by: Arpan Chakraborty <Arpan.Chakraborty1908@gmail.com> * - Refactor DoclingServeClient - Update getting-started.md Signed-off-by: Arpan Chakraborty <Arpan.Chakraborty1908@gmail.com> --------- Signed-off-by: Arpan Chakraborty <arpan.chakraborty1908@gmail.com> Signed-off-by: Arpan Chakraborty <Arpan.Chakraborty1908@gmail.com> Co-authored-by: Eric Deandrea <eric@ericdeandrea.dev>
1 parent 62b9975 commit 4ae819a

File tree

23 files changed

+1096
-217
lines changed

23 files changed

+1096
-217
lines changed

.github/project.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
release:
22
previous-version: 0.4.7
3-
current-version: 0.4.8
4-
next-version: 0.4.9
3+
current-version: 0.5.0
4+
next-version: 0.5.1

docling-serve/docling-serve-api/src/main/java/ai/docling/serve/api/DoclingServeConvertApi.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public interface DoclingServeConvertApi {
2121
* Converts the provided document source(s) into a processed document based on the specified options.
2222
*
2323
* @param request the {@link ConvertDocumentRequest} containing the source(s), conversion options, and optional target.
24-
* @return a {@link ConvertDocumentResponse} containing the processed document data, processing details, and any errors.
24+
* @return a {@link ConvertDocumentResponse} describing the convert document response.
2525
* @throws ai.docling.serve.api.validation.ValidationException If request validation fails for any reason.
2626
*/
2727
ConvertDocumentResponse convertSource(ConvertDocumentRequest request);

docling-serve/docling-serve-api/src/main/java/ai/docling/serve/api/DoclingServeTaskApi.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public interface DoclingServeTaskApi {
3232
TaskStatusPollResponse pollTaskStatus(TaskStatusPollRequest request);
3333

3434
/**
35-
* Converts the result of a completed task into a document format as specified in the request.
35+
* Returns the result of a completed convert task.
3636
*
3737
* This method processes the task result identified by the unique task ID provided in
3838
* the request, performs any necessary transformation, and returns a response
Lines changed: 17 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,33 @@
11
package ai.docling.serve.api.convert.response;
22

3-
import java.util.List;
4-
import java.util.Map;
5-
63
import com.fasterxml.jackson.annotation.JsonInclude;
74
import com.fasterxml.jackson.annotation.JsonProperty;
8-
import com.fasterxml.jackson.annotation.JsonSetter;
9-
import com.fasterxml.jackson.annotation.Nulls;
5+
import com.fasterxml.jackson.annotation.JsonSubTypes;
6+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
107

118
/**
12-
* Response returned by the Convert API for a single conversion request.
9+
* Abstract response returned by the Convert API for a single conversion request.
1310
*
1411
* <p>Serialization uses {@link JsonInclude.Include#NON_EMPTY}, so nulls and empty
1512
* collections/strings are omitted from JSON output.</p>
1613
*/
1714
@JsonInclude(JsonInclude.Include.NON_EMPTY)
18-
@tools.jackson.databind.annotation.JsonDeserialize(builder = ConvertDocumentResponse.Builder.class)
19-
@lombok.extern.jackson.Jacksonized
20-
@lombok.Builder(toBuilder = true)
21-
@lombok.Getter
22-
@lombok.ToString
23-
public class ConvertDocumentResponse {
24-
/**
25-
* Converted document content.
26-
*
27-
* @param document the converted document
28-
* @return the converted document
29-
*/
30-
@JsonProperty("document")
31-
private DocumentResponse document;
32-
15+
@JsonTypeInfo(
16+
use = JsonTypeInfo.Id.DEDUCTION
17+
)
18+
@JsonSubTypes({
19+
@JsonSubTypes.Type(InBodyConvertDocumentResponse.class),
20+
@JsonSubTypes.Type(PreSignedUrlConvertDocumentResponse.class),
21+
@JsonSubTypes.Type(ZipArchiveConvertDocumentResponse.class)
22+
})
23+
public abstract sealed class ConvertDocumentResponse permits InBodyConvertDocumentResponse, PreSignedUrlConvertDocumentResponse,
24+
ZipArchiveConvertDocumentResponse {
3325
/**
34-
* List of errors that occurred during conversion.
26+
* Type of response
3527
*
36-
* @param errors the list of errors
37-
* @return the list of errors
28+
* @return the response type
3829
*/
39-
@JsonProperty("errors")
40-
@JsonSetter(nulls = Nulls.AS_EMPTY)
41-
@lombok.Singular
42-
private List<ErrorItem> errors;
30+
@JsonProperty("response_type")
31+
public abstract ResponseType getResponseType();
4332

44-
/**
45-
* Total processing time in seconds.
46-
*
47-
* @param processingTime the processing time in seconds
48-
* @return the processing time in seconds
49-
*/
50-
@JsonProperty("processing_time")
51-
private Double processingTime;
52-
53-
/**
54-
* Conversion status (success, failure, partial_success, etc.).
55-
*
56-
* @param status the conversion status
57-
* @return the conversion status
58-
*/
59-
@JsonProperty("status")
60-
private String status;
61-
62-
/**
63-
* Detailed timing information for processing stages.
64-
*
65-
* @param timings the map of timing information
66-
* @return the map of timing information
67-
*/
68-
@JsonProperty("timings")
69-
@JsonSetter(nulls = Nulls.AS_EMPTY)
70-
@lombok.Singular
71-
private Map<String, Object> timings;
72-
73-
/**
74-
* Builder for creating {@link ConvertDocumentResponse} instances.
75-
* Generated by Lombok's {@code @Builder} annotation.
76-
*
77-
* <p>Builder methods:
78-
* <ul>
79-
* <li>{@code document(DocumentResponse)} - Set the converted document</li>
80-
* <li>{@code error(ErrorItem)} - Add a single error (use with @Singular)</li>
81-
* <li>{@code errors(List<ErrorItem>)} - Set the list of errors</li>
82-
* <li>{@code processingTime(Double)} - Set the processing time in seconds</li>
83-
* <li>{@code status(String)} - Set the conversion status</li>
84-
* <li>{@code timing(String, Object)} - Add a single timing entry (use with @Singular)</li>
85-
* <li>{@code timings(Map<String, Object>)} - Set the map of timing information</li>
86-
* </ul>
87-
*/
88-
@tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "")
89-
public static class Builder { }
9033
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package ai.docling.serve.api.convert.response;
2+
3+
import java.util.List;
4+
import java.util.Map;
5+
6+
import com.fasterxml.jackson.annotation.JsonInclude;
7+
import com.fasterxml.jackson.annotation.JsonProperty;
8+
import com.fasterxml.jackson.annotation.JsonSetter;
9+
import com.fasterxml.jackson.annotation.Nulls;
10+
11+
/**
12+
* Response for single document conversions with in-body content delivery.
13+
*
14+
* <p>This response type is returned when both the following conditions hold:</p>
15+
* <ul>
16+
* <li>The conversion request contains a single source</li>
17+
* <li>The target type is {@link ai.docling.serve.api.convert.request.target.InBodyTarget}</li>
18+
* </ul>
19+
*
20+
* <p>The converted document content is included directly in the response body,
21+
* along with conversion status, processing time, errors, and detailed timing
22+
* information for each processing stage.</p>
23+
*
24+
* <p>Serialization uses {@link JsonInclude.Include#NON_EMPTY}, so nulls and empty
25+
* collections/strings are omitted from JSON output.</p>
26+
*
27+
* @see ConvertDocumentResponse
28+
* @see ResponseType#IN_BODY
29+
* @see ai.docling.serve.api.convert.request.target.InBodyTarget
30+
* @see DocumentResponse
31+
*/
32+
@JsonInclude(JsonInclude.Include.NON_EMPTY)
33+
@tools.jackson.databind.annotation.JsonDeserialize(builder = InBodyConvertDocumentResponse.Builder.class)
34+
@lombok.extern.jackson.Jacksonized
35+
@lombok.Builder(toBuilder = true)
36+
@lombok.Getter
37+
@lombok.ToString
38+
public final class InBodyConvertDocumentResponse extends ConvertDocumentResponse {
39+
/**
40+
* Converted document content.
41+
*
42+
* @param document the converted document
43+
* @return the converted document
44+
*/
45+
@JsonProperty("document")
46+
private DocumentResponse document;
47+
48+
/**
49+
* List of errors that occurred during conversion.
50+
*
51+
* @param errors the list of errors
52+
* @return the list of errors
53+
*/
54+
@JsonProperty("errors")
55+
@JsonSetter(nulls = Nulls.AS_EMPTY)
56+
@lombok.Singular
57+
private List<ErrorItem> errors;
58+
59+
/**
60+
* Total processing time in seconds.
61+
*
62+
* @param processingTime the processing time in seconds
63+
* @return the processing time in seconds
64+
*/
65+
@JsonProperty("processing_time")
66+
private Double processingTime;
67+
68+
/**
69+
* Conversion status (success, failure, partial_success, etc.).
70+
*
71+
* @param status the conversion status
72+
* @return the conversion status
73+
*/
74+
@JsonProperty("status")
75+
private String status;
76+
77+
/**
78+
* Detailed timing information for processing stages.
79+
*
80+
* @param timings the map of timing information
81+
* @return the map of timing information
82+
*/
83+
@JsonProperty("timings")
84+
@JsonSetter(nulls = Nulls.AS_EMPTY)
85+
@lombok.Singular
86+
private Map<String, Object> timings;
87+
88+
@Override
89+
@lombok.ToString.Include
90+
public ResponseType getResponseType() {
91+
return ResponseType.IN_BODY;
92+
}
93+
94+
/**
95+
* Builder for creating {@link InBodyConvertDocumentResponse} instances.
96+
* Generated by Lombok's {@code @Builder} annotation.
97+
*
98+
* <p>Builder methods:
99+
* <ul>
100+
* <li>{@code document(DocumentResponse)} - Set the converted document</li>
101+
* <li>{@code error(ErrorItem)} - Add a single error (use with @Singular)</li>
102+
* <li>{@code errors(List<ErrorItem>)} - Set the list of errors</li>
103+
* <li>{@code processingTime(Double)} - Set the processing time in seconds</li>
104+
* <li>{@code status(String)} - Set the conversion status</li>
105+
* <li>{@code timing(String, Object)} - Add a single timing entry (use with @Singular)</li>
106+
* <li>{@code timings(Map<String, Object>)} - Set the map of timing information</li>
107+
* </ul>
108+
*/
109+
@tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "")
110+
public static class Builder { }
111+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package ai.docling.serve.api.convert.response;
2+
3+
import com.fasterxml.jackson.annotation.JsonInclude;
4+
import com.fasterxml.jackson.annotation.JsonProperty;
5+
6+
/**
7+
* Response for document conversions with S3 or PUT target types.
8+
*
9+
* <p>This response type is returned when the conversion request specifies
10+
* an S3 target or PUT target. The converted documents are stored in the
11+
* specified location.
12+
* The response includes processing statistics and conversion metrics.</p>
13+
*
14+
* <p>This response type is returned in any one the following scenarios:</p>
15+
* <ul>
16+
* <li>Target type is {@link ai.docling.serve.api.convert.request.target.PutTarget}</li>
17+
* <li>Target type is {@link ai.docling.serve.api.convert.request.target.S3Target}</li>
18+
* </ul>
19+
*
20+
* <p>Serialization uses {@link JsonInclude.Include#NON_EMPTY}, so nulls and empty
21+
* collections/strings are omitted from JSON output.</p>
22+
*
23+
* @see ConvertDocumentResponse
24+
* @see ResponseType#PRE_SIGNED_URL
25+
* @see ai.docling.serve.api.convert.request.target.S3Target
26+
* @see ai.docling.serve.api.convert.request.target.PutTarget
27+
*/
28+
@JsonInclude(JsonInclude.Include.NON_EMPTY)
29+
@tools.jackson.databind.annotation.JsonDeserialize(builder = PreSignedUrlConvertDocumentResponse.Builder.class)
30+
@lombok.extern.jackson.Jacksonized
31+
@lombok.Builder(toBuilder = true)
32+
@lombok.Getter
33+
@lombok.ToString
34+
public final class PreSignedUrlConvertDocumentResponse extends ConvertDocumentResponse {
35+
36+
/**
37+
* Total processing time in seconds.
38+
*
39+
* @param processingTime the processing time in seconds
40+
* @return the processing time in seconds
41+
*/
42+
@JsonProperty("processing_time")
43+
private Double processingTime;
44+
45+
/**
46+
* Number of attempted conversions
47+
*
48+
* @param num_converted the number of attempted conversions
49+
* @return the number of attempted conversions
50+
*/
51+
@JsonProperty("num_converted")
52+
private Integer numConverted;
53+
54+
/**
55+
* Number of successful conversions
56+
*
57+
* @param numSucceeded the number of successful conversions
58+
* @return the number of successful conversions
59+
*/
60+
@JsonProperty("num_succeeded")
61+
private Integer numSucceeded;
62+
63+
/**
64+
* Number of failed conversions
65+
*
66+
* @param numFailed the number of failed conversions
67+
* @return the number of failed conversions
68+
*/
69+
@JsonProperty("num_failed")
70+
private Integer numFailed;
71+
72+
@Override
73+
@lombok.ToString.Include
74+
public ResponseType getResponseType() {
75+
return ResponseType.PRE_SIGNED_URL;
76+
}
77+
78+
/**
79+
* Builder for creating {@link PreSignedUrlConvertDocumentResponse} instances.
80+
* Generated by Lombok's {@code @Builder} annotation.
81+
*
82+
* <p>Builder methods:
83+
* <ul>
84+
* <li>{@code processingTime(Double)} - Set the total processing time in seconds</li>
85+
* <li>{@code numConverted(Integer)} - Set the number of attempted conversions</li>
86+
* <li>{@code numSucceeded(Integer)} - Set the number of successful conversions</li>
87+
* <li>{@code numFailed(Integer)} - Set the number of failed conversions</li>
88+
* </ul>
89+
*/
90+
@tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "")
91+
public static class Builder { }
92+
93+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package ai.docling.serve.api.convert.response;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
5+
/**
6+
* Defines the response type for document conversion operations.
7+
*
8+
* <p>Each enumeration value corresponds to a specific response format and determines how
9+
* the converted document content is delivered to the client:
10+
*
11+
* <ul>
12+
* <li>{@link #IN_BODY} - Content is embedded directly in the response body
13+
* ({@link InBodyConvertDocumentResponse})</li>
14+
* <li>{@link #ZIP_ARCHIVE} - Content is packaged and returned as a ZIP archive
15+
* ({@link ZipArchiveConvertDocumentResponse})</li>
16+
* <li>{@link #PRE_SIGNED_URL} - Content is packaged as a ZIP archive and uploaded to the given target URI
17+
* and statistical information is returned.
18+
* ({@link PreSignedUrlConvertDocumentResponse})</li>
19+
* </ul>
20+
*
21+
* @see ConvertDocumentResponse
22+
* @see InBodyConvertDocumentResponse
23+
* @see ZipArchiveConvertDocumentResponse
24+
* @see PreSignedUrlConvertDocumentResponse
25+
*/
26+
public enum ResponseType {
27+
28+
/**
29+
* Represents response type - {@link InBodyConvertDocumentResponse}
30+
*
31+
*/
32+
@JsonProperty("in_body")
33+
IN_BODY,
34+
35+
/**
36+
* Represents response type - {@link ZipArchiveConvertDocumentResponse}
37+
*
38+
*/
39+
@JsonProperty("zip_archive")
40+
ZIP_ARCHIVE,
41+
42+
/**
43+
* Represents response type - {@link PreSignedUrlConvertDocumentResponse}
44+
*
45+
*/
46+
@JsonProperty("presigned_url")
47+
PRE_SIGNED_URL
48+
}

0 commit comments

Comments
 (0)