From 945e7bd4ffee7247a1d9da5bf34ea2b135a0d7a3 Mon Sep 17 00:00:00 2001 From: Eric Deandrea Date: Thu, 23 Oct 2025 15:45:52 -0400 Subject: [PATCH 01/13] feat: idea for api design Signed-off-by: Eric Deandrea --- api/build.gradle.kts | 1 + .../main/java/ai/docling/api/DoclingApi.java | 36 +++ .../convert/response/DocumentResponse.java | 260 ++++++++++++++++- .../main/java/ai/docling/api/util/Utils.java | 168 +++++++++++ .../ai/docling/api/util/ValidationUtils.java | 274 ++++++++++++++++++ .../ConvertDocumentResponseTests.java | 29 +- .../response/DocumentResponseTests.java | 53 ++-- .../java/ai/docling/api/util/UtilsTests.java | 161 ++++++++++ .../api/util/ValidationUtilsTests.java | 264 +++++++++++++++++ .../kotlin/docling-java-shared.gradle.kts | 1 + .../src/main/kotlin/docling-shared.gradle.kts | 2 + .../java/ai/docling/client/DoclingClient.java | 132 ++++++--- .../ai/docling/client/DoclingClientTests.java | 15 +- .../test/java/ai/docling/client/Images.java | 15 - docs/build.gradle.kts | 13 +- .../testcontainers/DoclingContainer.java | 9 +- .../config/DoclingContainerConfig.java | 7 +- 17 files changed, 1305 insertions(+), 135 deletions(-) create mode 100644 api/src/main/java/ai/docling/api/util/Utils.java create mode 100644 api/src/main/java/ai/docling/api/util/ValidationUtils.java create mode 100644 api/src/test/java/ai/docling/api/util/UtilsTests.java create mode 100644 api/src/test/java/ai/docling/api/util/ValidationUtilsTests.java create mode 100644 buildSrc/src/main/kotlin/docling-shared.gradle.kts delete mode 100644 client/src/test/java/ai/docling/client/Images.java diff --git a/api/build.gradle.kts b/api/build.gradle.kts index b587440..20275c0 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -6,5 +6,6 @@ dependencies { implementation(platform(libs.jackson.bom)) implementation("com.fasterxml.jackson.core:jackson-annotations") + implementation("tools.jackson.core:jackson-databind") testImplementation(project(":docling-testcontainers")) } diff --git a/api/src/main/java/ai/docling/api/DoclingApi.java b/api/src/main/java/ai/docling/api/DoclingApi.java index bdf3cc8..55be038 100644 --- a/api/src/main/java/ai/docling/api/DoclingApi.java +++ b/api/src/main/java/ai/docling/api/DoclingApi.java @@ -9,8 +9,44 @@ */ public interface DoclingApi { + /** + * Executes a health check for the API and retrieves the health status of the service. + * + * @return a {@link HealthCheckResponse} object containing the health status of the API. + */ HealthCheckResponse health(); + /** + * Converts the provided document source(s) into a processed document based on the specified options. + * + * @param request the {@link ConvertDocumentRequest} containing the source(s), conversion options, and optional target. + * @return a {@link ConvertDocumentResponse} containing the processed document data, processing details, and any errors. + */ ConvertDocumentResponse convertSource(ConvertDocumentRequest request); + /** + * Creates and returns a builder instance capable of constructing a duplicate or modified + * version of the current API instance. The builder provides a customizable way to adjust + * configuration or properties before constructing a new API instance. + * + * @return a {@link DoclingApiBuilder} initialized with the state of the current API instance. + */ + > DoclingApiBuilder toBuilder(); + + /** + * A builder interface for constructing implementations of {@link DoclingApi}. This interface + * supports a fluent API for setting configuration properties before building an instance. + * + * @param the type of the {@link DoclingApi} implementation being built. + * @param the type of the concrete builder implementation. + */ + interface DoclingApiBuilder> { + /** + * Builds and returns an instance of the specified type, representing the completed configuration + * of the builder. The returned instance is typically an implementation of the Docling API. + * + * @return an instance of type {@code T} representing a configured Docling API client. + */ + T build(); + } } diff --git a/api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java b/api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java index 008c9c9..8351b0b 100644 --- a/api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java +++ b/api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java @@ -2,33 +2,269 @@ import java.util.HashMap; import java.util.Map; +import java.util.Optional; import org.jspecify.annotations.Nullable; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -@JsonInclude(JsonInclude.Include.NON_NULL) -public record DocumentResponse( +import tools.jackson.databind.annotation.JsonDeserialize; +import tools.jackson.databind.annotation.JsonPOJOBuilder; - @JsonProperty("doctags_content") @Nullable String doctagsContent, +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = DocumentResponse.Builder.class) +public interface DocumentResponse { + /** + * Retrieves the content of the doc tags, if available. + * + * @return the content of the doc tags, or null if not present + */ + @Nullable + String doctagsContent(); - @JsonProperty("filename") String filename, + /** + * Retrieves the filename associated with the document. + * + * @return the filename of the document as a string + */ + String filename(); - @JsonProperty("html_content") @Nullable String htmlContent, + /** + * Retrieves the HTML content associated with the document, if available. + * + * @return the HTML content as a string, or null if not present + */ + @Nullable + String htmlContent(); - @JsonProperty("json_content") @Nullable Map jsonContent, + /** + * Retrieves the JSON content associated with the document. + * + * @return a map representing the JSON content, or an empty map if no JSON content is present + */ + Map jsonContent(); - @JsonProperty("md_content") @Nullable String markdownContent, + /** + * Retrieves the Markdown content associated with the document, if available. + * + * @return the Markdown content as a string, or null if no Markdown content is present + */ + @Nullable + String markdownContent(); - @JsonProperty("text_content") @Nullable String textContent + /** + * Retrieves the plain text content associated with the document, if available. + * + * @return the plain text content as a string, or null if no text content is present + */ + @Nullable + String textContent(); -) { + /** + * Creates a new {@code Builder} instance initialized with the current state of the {@code DocumentResponse}. + * + * @return a {@code Builder} instance populated with the values from this {@code DocumentResponse} + */ + default Builder toBuilder() { + return new Builder(this); + } + + /** + * Creates and returns a new instance of the {@code Builder} class, which can be used to + * construct a {@code DocumentResponse} object in a step-by-step manner. + * + * @return a new {@code Builder} instance + */ + static Builder builder() { + return new Builder(); + } + + /** + * Default implementation of the {@link DocumentResponse} interface. + * This record represents the response containing document data in various formats. + * It is an immutable data structure that consolidates information related to a document, + * such as its filename, content in multiple formats, and metadata. + * + * Each instance ensures the provided JSON content is unmodifiable by copying + * the input map if it is present, or initializing it to an empty map otherwise. + */ + record DefaultDocumentResponse(String doctagsContent, + String filename, + String htmlContent, + Map jsonContent, + String markdownContent, + String textContent) implements DocumentResponse { - public DocumentResponse { - if (jsonContent != null) { - jsonContent = new HashMap<>(jsonContent); + public DefaultDocumentResponse { + jsonContent = Optional.ofNullable(jsonContent) + .map(Map::copyOf) + .orElseGet(Map::of); + } + + public DefaultDocumentResponse(Builder builder) { + this(builder.doctagsContent, + builder.filename, + builder.htmlContent, + builder.jsonContent, + builder.markdownContent, + builder.textContent); } } + /** + * A builder class for constructing instances of {@code DocumentResponse}. + * + * This class provides a step-by-step approach to configure and create a + * {@code DocumentResponse} object. Each method in this class sets a specific + * property of the object being built. Once all the desired properties are set, + * the {@code build} method is used to create the final {@code DocumentResponse} + * instance. + * + * The builder supports customization of various document-related attributes, + * including doc tags content, filename, HTML content, JSON content, Markdown + * content, and plain text content. + * + * By default, the builder initializes attributes with an empty state or default + * values. If a {@code DocumentResponse} instance is provided to the constructor, + * the builder is pre-populated with the attributes from the given response. + * + * This class is intended for internal use and is protected to restrict its + * accessibility outside the defining package or class hierarchy. + */ + @JsonPOJOBuilder(withPrefix = "") + class Builder { + protected String doctagsContent; + protected String filename; + protected String htmlContent; + protected Map jsonContent = new HashMap<>(); + protected String markdownContent; + protected String textContent; + + /** + * Constructs a new {@code Builder} instance. + * + * This constructor initializes a builder with default or empty states for all + * attributes. It is protected to restrict direct instantiation outside of the + * defining package or class hierarchy. + * + * The {@code Builder} class is primarily used to facilitate the creation of + * {@code DocumentResponse} objects through a step-by-step configuration process. + */ + protected Builder() { + + } + + /** + * Constructs a new {@code Builder} instance using the provided {@code DocumentResponse}. + * + * This constructor initializes the builder's fields with the data from the given + * {@code DocumentResponse} object. It allows for the creation of a {@code Builder} + * instance pre-populated with the state of an existing {@code DocumentResponse}. + * + * @param documentResponse the {@code DocumentResponse} instance whose data will + * populate the fields of this builder + */ + protected Builder(DocumentResponse documentResponse) { + this.doctagsContent = documentResponse.doctagsContent(); + this.filename = documentResponse.filename(); + this.htmlContent = documentResponse.htmlContent(); + this.jsonContent = documentResponse.jsonContent(); + this.markdownContent = documentResponse.markdownContent(); + this.textContent = documentResponse.textContent(); + } + + /** + * Sets the doctags content for the builder instance. + * + * @param doctagsContent the doctags content to be set + * @return this Builder instance for method chaining + */ + @JsonProperty("doctags_content") + public Builder doctagsContent(String doctagsContent) { + this.doctagsContent = doctagsContent; + return this; + } + + /** + * Sets the filename for the builder instance. + * + * @param filename the filename to be set + * @return this Builder instance for method chaining + */ + @JsonProperty("filename") + public Builder filename(String filename) { + this.filename = filename; + return this; + } + + /** + * Sets the HTML content for the builder instance. + * + * @param htmlContent the HTML content to be set + * @return this Builder instance for method chaining + */ + @JsonProperty("html_content") + public Builder htmlContent(String htmlContent) { + this.htmlContent = htmlContent; + return this; + } + + /** + * Sets the JSON content for the builder instance. + * + * The JSON content is represented as a map of key-value pairs, where the keys + * are {@code String} objects, and the values are {@code Object} instances. + * + * @param jsonContent the JSON content to be set, represented as a {@code Map} + * @return this {@link Builder} instance for method chaining + */ + @JsonProperty("json_content") + public Builder jsonContent(Map jsonContent) { + this.jsonContent = jsonContent; + return this; + } + + /** + * Sets the Markdown content for this builder instance. + * + * The Markdown content represents the textual data formatted in Markdown syntax, + * which can include headings, lists, links, and other Markdown elements. + * + * @param markdownContent the Markdown content to be set, represented as a {@code String} + * @return this {@link Builder} instance for method chaining + */ + @JsonProperty("md_content") + public Builder markdownContent(String markdownContent) { + this.markdownContent = markdownContent; + return this; + } + + /** + * Sets the plain text content for this builder instance. + * + * The plain text content represents unformatted textual data that can be + * used for display or processing purposes within the application. + * + * @param textContent the plain text content to be set, represented as a {@code String} + * @return this {@link Builder} instance for method chaining + */ + @JsonProperty("text_content") + public Builder textContent(String textContent) { + this.textContent = textContent; + return this; + } + + /** + * Creates and returns a {@link DocumentResponse} instance based on the current state of this {@link Builder}. + * + *

The returned {@link DocumentResponse} will encapsulate the values configured in the builder, + * and further modifications to the builder instance will not affect the created {@code DocumentResponse}. + * + * @return a new {@code DocumentResponse} instance constructed from the builder's state + */ + public DocumentResponse build() { + return new DefaultDocumentResponse(this); + } + } } diff --git a/api/src/main/java/ai/docling/api/util/Utils.java b/api/src/main/java/ai/docling/api/util/Utils.java new file mode 100644 index 0000000..1b8a436 --- /dev/null +++ b/api/src/main/java/ai/docling/api/util/Utils.java @@ -0,0 +1,168 @@ +package ai.docling.api.util; + +import static ai.docling.api.util.ValidationUtils.ensureNotEmpty; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +/** + * Utility methods. + */ +public final class Utils { + private Utils() {} + + /** + * Returns the first non-null value from the provided array of values. + * If all values are null, an new IllegalArgumentException is thrown. + * + * @param name A non-null string representing the name associated with the values. + * @param values An array of potentially nullable values to search through. + * @return The first non-null value in the array. + * @throws IllegalArgumentException If all values are null or if the array is empty. + */ + @SafeVarargs + @NonNull + public static T firstNotNull(@NonNull String name, @Nullable T... values) { + ensureNotEmpty(values, name + " values"); + + return Stream.of(values) + .filter(Objects::nonNull) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("At least one of the given '%s' values must be not null".formatted(name))); + } + + /** + * Returns the given value if it is not {@code null}, otherwise returns the given default value. + * + * @param value The value to return if it is not {@code null}. + * @param defaultValue The value to return if the value is {@code null}. + * @param The type of the value. + * @return the given value if it is not {@code null}, otherwise returns the given default value. + */ + public static T getOrDefault(T value, T defaultValue) { + return Optional.ofNullable(value).orElse(defaultValue); + } + + /** + * Returns the given list if it is not {@code null} and not empty, otherwise returns the given default list. + * + * @param list The list to return if it is not {@code null} and not empty. + * @param defaultList The list to return if the list is {@code null} or empty. + * @param The type of the value. + * @return the given list if it is not {@code null} and not empty, otherwise returns the given default list. + */ + public static List getOrDefault(List list, List defaultList) { + return isNullOrEmpty(list) ? defaultList : list; + } + + /** + * Returns the given map if it is not {@code null} and not empty, otherwise returns the given default map. + * + * @param map The map to return if it is not {@code null} and not empty. + * @param defaultMap The map to return if the map is {@code null} or empty. + * @return the given map if it is not {@code null} and not empty, otherwise returns the given default map. + */ + public static Map getOrDefault(Map map, Map defaultMap) { + return isNullOrEmpty(map) ? defaultMap : map; + } + + /** + * Returns the given value if it is not {@code null}, otherwise returns the value returned by the given supplier. + * + * @param value The value to return if it is not {@code null}. + * @param defaultValueSupplier The supplier to call if the value is {@code null}. + * @param The type of the value. + * @return the given value if it is not {@code null}, otherwise returns the value returned by the given supplier. + */ + public static T getOrDefault(@Nullable T value, Supplier defaultValueSupplier) { + return Optional.ofNullable(value).orElseGet(defaultValueSupplier::get); + } + + /** + * Is the given string {@code null} or blank? + * + * @param string The string to check. + * @return true if the string is {@code null} or blank. + */ + public static boolean isNullOrBlank(String string) { + return string == null || string.trim().isBlank(); + } + + /** + * Is the given string {@code null} or empty ("")? + * + * @param string The string to check. + * @return true if the string is {@code null} or empty. + */ + public static boolean isNullOrEmpty(String string) { + return string == null || string.isEmpty(); + } + + /** + * Is the given string not {@code null} and not blank? + * + * @param string The string to check. + * @return true if there's something in the string. + */ + public static boolean isNotNullOrBlank(String string) { + return !isNullOrBlank(string); + } + + /** + * Is the given string not {@code null} and not empty ("")? + * + * @param string The string to check. + * @return true if the given string is not {@code null} and not empty ("")? + */ + public static boolean isNotNullOrEmpty(String string) { + return !isNullOrEmpty(string); + } + + /** + * Is the collection {@code null} or empty? + * + * @param collection The collection to check. + * @return {@code true} if the collection is {@code null} or {@link Collection#isEmpty()}, otherwise {@code false}. + */ + public static boolean isNullOrEmpty(Collection collection) { + return (collection == null) || collection.isEmpty(); + } + + /** + * Is the iterable object {@code null} or empty? + * + * @param iterable The iterable object to check. + * @return {@code true} if the iterable object is {@code null} or there are no objects to iterate over, otherwise {@code false}. + */ + public static boolean isNullOrEmpty(Iterable iterable) { + return (iterable == null) || !iterable.iterator().hasNext(); + } + + /** + * Utility method to check if an array is null or has no elements. + * + * @param array the array to check + * @return {@code true} if the array is null or has no elements, otherwise {@code false} + */ + public static boolean isNullOrEmpty(T[] array) { + return (array == null) || (array.length == 0); + } + + /** + * Is the map object {@code null} or empty? + * + * @param map The iterable object to check. + * @return {@code true} if the map object is {@code null} or empty map, otherwise {@code false}. + */ + public static boolean isNullOrEmpty(@Nullable Map map) { + return (map == null) || map.isEmpty(); + } +} diff --git a/api/src/main/java/ai/docling/api/util/ValidationUtils.java b/api/src/main/java/ai/docling/api/util/ValidationUtils.java new file mode 100644 index 0000000..bcaec31 --- /dev/null +++ b/api/src/main/java/ai/docling/api/util/ValidationUtils.java @@ -0,0 +1,274 @@ +package ai.docling.api.util; + +import static ai.docling.api.util.Utils.isNullOrBlank; +import static ai.docling.api.util.Utils.isNullOrEmpty; + +import java.util.Collection; +import java.util.Map; +import java.util.Objects; + +/** + * Utility class for validating method arguments. + */ +public final class ValidationUtils { + private ValidationUtils() {} + + /** + * Ensure that the two values are equal. + * @param lhs the left hand side value. + * @param rhs the right hand side value. + * @param format the format string for the exception message. + * @param args the format arguments for the exception message. + */ + public static void ensureEq(Object lhs, Object rhs, String format, Object... args) { + if (!Objects.equals(lhs, rhs)) { + throw new IllegalArgumentException(format.formatted(args)); + } + } + + /** + * Ensures that the given object is not null. + * @param object The object to check. + * @param name The name of the object to be used in the exception message. + * @return The object if it is not null. + * @param The type of the object. + * @throws IllegalArgumentException if the object is null. + */ + public static T ensureNotNull(T object, String name) { + return ensureNotNull(object, "%s cannot be null", name); + } + + /** + * Ensures that the given object is not null. + * @param object The object to check. + * @param format The format of the exception message. + * @param args The arguments for the exception message. + * @return The object if it is not null. + * @param The type of the object. + */ + public static T ensureNotNull(T object, String format, Object... args) { + if (object == null) { + throw new IllegalArgumentException(format.formatted(args)); + } + return object; + } + + /** + * Ensures that the given collection is not null and not empty. + * @param collection The collection to check. + * @param name The name of the collection to be used in the exception message. + * @return The collection if it is not null and not empty. + * @param The type of the collection. + * @throws IllegalArgumentException if the collection is null or empty. + */ + public static Collection ensureNotEmpty(Collection collection, String name) { + if (isNullOrEmpty(collection)) { + throw new IllegalArgumentException("%s cannot be null or empty".formatted(name)); + } + + return collection; + } + + /** + * Ensures that the given array is not null and not empty. + * @param array The array to check. + * @param name The name of the array to be used in the exception message. + * @return The array if it is not null and not empty. + * @param The component type of the array. + * @throws IllegalArgumentException if the array is null or empty. + */ + public static T[] ensureNotEmpty(T[] array, String name) { + return ensureNotEmpty(array, "%s cannot be null or empty", name); + } + + /** + * Ensures that the given array is not null and not empty. + * @param array The array to check. + * @param format The format of the exception message. + * @param args The arguments for the exception message. + * @return The array if it is not null and not empty. + * @param The component type of the array. + * @throws IllegalArgumentException if the array is null or empty. + */ + public static T[] ensureNotEmpty(T[] array, String format, Object... args) { + if (array == null || array.length == 0) { + throw new IllegalArgumentException(format.formatted(args)); + } + return array; + } + + /** + * Ensures that the given map is not null and not empty. + * + * @param map The map to check. + * @param name The name of the map to be used in the exception message. + * @param The type of the key. + * @param The type of the value. + * @return The map if it is not null and not empty. + * @throws IllegalArgumentException if the collection is null or empty. + */ + public static Map ensureNotEmpty(Map map, String name) { + if (isNullOrEmpty(map)) { + throw new IllegalArgumentException("%s cannot be null or empty".formatted(name)); + } + + return map; + } + + /** + * Ensures that the given string is not null and not empty. + * @param string The string to check. + * @param name The name of the string to be used in the exception message. + * @return The string if it is not null and not empty. + * @throws IllegalArgumentException if the string is null or empty. + */ + public static String ensureNotEmpty(String string, String name) { + return ensureNotEmpty(string, "%s cannot be null or empty", name); + } + + /** + * Ensures that the given string is not null and not empty. + * @param string The string to check. + * @param format The format of the exception message. + * @param args The arguments for the exception message. + * @return The string if it is not null and not empty. + * @throws IllegalArgumentException if the string is null or empty. + */ + public static String ensureNotEmpty(String string, String format, Object... args) { + if (isNullOrEmpty(string)) { + throw new IllegalArgumentException(format.formatted(args)); + } + return string; + } + + /** + * Ensures that the given string is not null and not blank. + * @param string The string to check. + * @param name The name of the string to be used in the exception message. + * @return The string if it is not null and not blank. + * @throws IllegalArgumentException if the string is null or blank. + */ + public static String ensureNotBlank(String string, String name) { + return ensureNotBlank(string, "%s cannot be null or blank", name); + } + + /** + * Ensures that the given string is not null and not blank. + * @param string The string to check. + * @param format The format of the exception message. + * @param args The arguments for the exception message. + * @return The string if it is not null and not blank. + * @throws IllegalArgumentException if the string is null or blank. + */ + public static String ensureNotBlank(String string, String format, Object... args) { + if (isNullOrBlank(string)) { + throw new IllegalArgumentException(format.formatted(args)); + } + return string; + } + + /** + * Ensures that the given expression is true. + * @param expression The expression to check. + * @param msg The message to be used in the exception. + * @throws IllegalArgumentException if the expression is false. + */ + public static void ensureTrue(boolean expression, String msg) { + if (!expression) { + throw new IllegalArgumentException(msg); + } + } + + /** + * Ensures that the given integer is not negative. + * @param i The integer to check. + * @param name The logical name of the integer, to be used in the exception. + * @return The value if it is not negative. + * @throws IllegalArgumentException if the integer is negative. + */ + public static int ensureNotNegative(Integer i, String name) { + if (i == null || i < 0) { + throw new IllegalArgumentException("%s must not be negative, but is: %s".formatted(name, i)); + } + + return i; + } + + /** + * Ensures that the given expression is true. + * @param i The expression to check. + * @param name The message to be used in the exception. + * @return The value if it is greater than zero. + * @throws IllegalArgumentException if the expression is false. + */ + public static int ensureGreaterThanZero(Integer i, String name) { + if (i == null || i <= 0) { + throw new IllegalArgumentException("%s must be greater than zero, but is: %s".formatted(name, i)); + } + + return i; + } + + /** + * Ensures that the given expression is true. + * @param i The expression to check. + * @param name The message to be used in the exception. + * @return The value if it is greater than zero. + * @throws IllegalArgumentException if the expression is false. + */ + public static double ensureGreaterThanZero(Double i, String name) { + if (i == null || i <= 0) { + throw new IllegalArgumentException("%s must be greater than zero, but is: %s".formatted(name, i)); + } + + return i; + } + + /** + * Ensures that the given Double value is in {@code [min, max]}. + * @param d The value to check. + * @param min The minimum value. + * @param max The maximum value. + * @param name The value name to be used in the exception. + * @return The value if it is in {@code [min, max]}. + * @throws IllegalArgumentException if the value is not in {@code [min, max]}. + */ + public static double ensureBetween(Double d, double min, double max, String name) { + if (d == null || d < min || d > max) { + throw new IllegalArgumentException("%s must be between %s and %s, but is: %s".formatted(name, min, max, d)); + } + return d; + } + + /** + * Ensures that the given Integer value is in {@code [min, max]}. + * @param i The value to check. + * @param min The minimum value. + * @param max The maximum value. + * @param name The value name to be used in the exception. + * @return The value if it is in {@code [min, max]}. + * @throws IllegalArgumentException if the value is not in {@code [min, max]}. + */ + public static int ensureBetween(Integer i, int min, int max, String name) { + if (i == null || i < min || i > max) { + throw new IllegalArgumentException("%s must be between %s and %s, but is: %s".formatted(name, min, max, i)); + } + return i; + } + + /** + * Ensures that the given Long value is in {@code [min, max]}. + * @param i The value to check. + * @param min The minimum value. + * @param max The maximum value. + * @param name The value name to be used in the exception. + * @return The value if it is in {@code [min, max]}. + * @throws IllegalArgumentException if the value is not in {@code [min, max]}. + */ + public static long ensureBetween(Long i, long min, long max, String name) { + if (i == null || i < min || i > max) { + throw new IllegalArgumentException("%s must be between %s and %s, but is: %s".formatted( name, min, max, i)); + } + return i; + } +} diff --git a/api/src/test/java/ai/docling/api/convert/response/ConvertDocumentResponseTests.java b/api/src/test/java/ai/docling/api/convert/response/ConvertDocumentResponseTests.java index 71d9725..6464dc1 100644 --- a/api/src/test/java/ai/docling/api/convert/response/ConvertDocumentResponseTests.java +++ b/api/src/test/java/ai/docling/api/convert/response/ConvertDocumentResponseTests.java @@ -16,14 +16,14 @@ class ConvertDocumentResponseTests { @Test void createResponseWithAllFields() { - DocumentResponse document = new DocumentResponse( - "doctags content", - "test-file.pdf", - "content", - Map.of("key", "value"), - "# Markdown content", - "Plain text content" - ); + DocumentResponse document = DocumentResponse.builder() + .doctagsContent("doctags content") + .filename("test-file.pdf") + .htmlContent("content") + .jsonContent(Map.of("key", "value")) + .markdownContent("# Markdown content") + .textContent("Plain text content") + .build(); List errors = List.of( new ErrorItem("parser", "Parse error", "pdf_module"), @@ -71,14 +71,11 @@ void createResponseWithNullFields() { @Test void createResponseWithEmptyCollections() { - DocumentResponse document = new DocumentResponse( - null, - "empty-file.txt", - null, - Map.of(), - null, - "" - ); + DocumentResponse document = DocumentResponse.builder() + .filename("empty-file.txt") + .jsonContent(Map.of()) + .textContent("") + .build(); List errors = List.of(); Map timings = Map.of(); diff --git a/api/src/test/java/ai/docling/api/convert/response/DocumentResponseTests.java b/api/src/test/java/ai/docling/api/convert/response/DocumentResponseTests.java index c39d0a2..1642d2a 100644 --- a/api/src/test/java/ai/docling/api/convert/response/DocumentResponseTests.java +++ b/api/src/test/java/ai/docling/api/convert/response/DocumentResponseTests.java @@ -11,7 +11,6 @@ * Unit tests for {@link DocumentResponse}. */ class DocumentResponseTests { - @Test void createResponseWithAllFields() { String doctagsContent = "doctags content"; @@ -25,14 +24,14 @@ void createResponseWithAllFields() { String markdownContent = "# Test Document\n\nThis is a test document."; String textContent = "Test Document\n\nThis is a test document."; - DocumentResponse response = new DocumentResponse( - doctagsContent, - filename, - htmlContent, - jsonContent, - markdownContent, - textContent - ); + DocumentResponse response = DocumentResponse.builder() + .doctagsContent(doctagsContent) + .filename(filename) + .htmlContent(htmlContent) + .jsonContent(jsonContent) + .markdownContent(markdownContent) + .textContent(textContent) + .build(); assertThat(response.doctagsContent()).isEqualTo(doctagsContent); assertThat(response.filename()).isEqualTo(filename); @@ -44,19 +43,12 @@ void createResponseWithAllFields() { @Test void createResponseWithNullFields() { - DocumentResponse response = new DocumentResponse( - null, - null, - null, - null, - null, - null - ); + DocumentResponse response = DocumentResponse.builder().build(); assertThat(response.doctagsContent()).isNull(); assertThat(response.filename()).isNull(); assertThat(response.htmlContent()).isNull(); - assertThat(response.jsonContent()).isNull(); + assertThat(response.jsonContent()).isNotNull().isEmpty(); assertThat(response.markdownContent()).isNull(); assertThat(response.textContent()).isNull(); } @@ -68,14 +60,12 @@ void createResponseWithEmptyFields() { String markdownContent = ""; String textContent = ""; - DocumentResponse response = new DocumentResponse( - null, - filename, - null, - jsonContent, - markdownContent, - textContent - ); + DocumentResponse response = DocumentResponse.builder() + .filename(filename) + .jsonContent(jsonContent) + .markdownContent(markdownContent) + .textContent(textContent) + .build(); assertThat(response.doctagsContent()).isNull(); assertThat(response.filename()).isEqualTo(filename); @@ -92,14 +82,9 @@ void documentResponseIsImmutable() { "count", 1 )); - DocumentResponse response = new DocumentResponse( - null, - null, - null, - jsonContent, - null, - null - ); + DocumentResponse response = DocumentResponse.builder() + .jsonContent(jsonContent) + .build(); assertThat(response.jsonContent()).isEqualTo(jsonContent); diff --git a/api/src/test/java/ai/docling/api/util/UtilsTests.java b/api/src/test/java/ai/docling/api/util/UtilsTests.java new file mode 100644 index 0000000..972adf2 --- /dev/null +++ b/api/src/test/java/ai/docling/api/util/UtilsTests.java @@ -0,0 +1,161 @@ +package ai.docling.api.util; + +import static java.util.Collections.emptyList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class UtilsTests { + @Test + void get_or_default() { + assertThat(Utils.getOrDefault("foo", "bar")).isEqualTo("foo"); + assertThat(Utils.getOrDefault(null, "bar")).isEqualTo("bar"); + + assertThat(Utils.getOrDefault("foo", () -> "bar")).isEqualTo("foo"); + assertThat(Utils.getOrDefault(null, () -> "bar")).isEqualTo("bar"); + } + + @Test + void get_or_default_list() { + List nullList = null; + List emptyList = List.of(); + List list1 = List.of(1); + List list2 = List.of(2); + + assertThat(Utils.getOrDefault(nullList, nullList)).isSameAs(nullList); + assertThat(Utils.getOrDefault(nullList, emptyList)).isSameAs(emptyList); + + assertThat(Utils.getOrDefault(emptyList, nullList)).isSameAs(nullList); + assertThat(Utils.getOrDefault(emptyList, emptyList)).isSameAs(emptyList); + + assertThat(Utils.getOrDefault(nullList, list1)).isSameAs(list1); + assertThat(Utils.getOrDefault(emptyList, list1)).isSameAs(list1); + + assertThat(Utils.getOrDefault(list1, list2)).isSameAs(list1).isNotSameAs(list2); + } + + @Test + void get_or_default_map() { + Map nullMap = null; + Map emptyMap = Map.of(); + Map map1 = Map.of("one", "1"); + Map map2 = Map.of("two", "2"); + + assertThat(Utils.getOrDefault(nullMap, nullMap)).isSameAs(nullMap); + assertThat(Utils.getOrDefault(nullMap, emptyMap)).isSameAs(emptyMap); + + assertThat(Utils.getOrDefault(emptyMap, nullMap)).isSameAs(nullMap); + assertThat(Utils.getOrDefault(emptyMap, emptyMap)).isSameAs(emptyMap); + + assertThat(Utils.getOrDefault(nullMap, map1)).isSameAs(map1); + assertThat(Utils.getOrDefault(emptyMap, map1)).isSameAs(map1); + + assertThat(Utils.getOrDefault(map1, map2)).isSameAs(map1).isNotSameAs(map2); + } + + @Test + void is_null_or_blank() { + assertThat(Utils.isNullOrBlank(null)).isTrue(); + assertThat(Utils.isNullOrBlank("")).isTrue(); + assertThat(Utils.isNullOrBlank(" ")).isTrue(); + assertThat(Utils.isNullOrBlank("foo")).isFalse(); + + assertThat(Utils.isNotNullOrBlank(null)).isFalse(); + assertThat(Utils.isNotNullOrBlank("")).isFalse(); + assertThat(Utils.isNotNullOrBlank(" ")).isFalse(); + assertThat(Utils.isNotNullOrBlank("foo")).isTrue(); + } + + @Test + void string_is_null_or_empty() { + assertThat(Utils.isNullOrEmpty((String) null)).isTrue(); + assertThat(Utils.isNullOrEmpty("")).isTrue(); + assertThat(Utils.isNullOrEmpty(" ")).isFalse(); + assertThat(Utils.isNullOrEmpty("\n")).isFalse(); + assertThat(Utils.isNullOrEmpty("foo")).isFalse(); + } + + @Test + void string_is_not_null_or_empty() { + assertThat(Utils.isNotNullOrEmpty(null)).isFalse(); + assertThat(Utils.isNotNullOrEmpty("")).isFalse(); + assertThat(Utils.isNotNullOrEmpty(" ")).isTrue(); + assertThat(Utils.isNotNullOrEmpty("\n")).isTrue(); + assertThat(Utils.isNotNullOrEmpty("foo")).isTrue(); + } + + @Test + void collection_is_null_or_empty() { + assertThat(Utils.isNullOrEmpty((Collection) null)).isTrue(); + assertThat(Utils.isNullOrEmpty(emptyList())).isTrue(); + assertThat(Utils.isNullOrEmpty(Collections.singletonList("abc"))).isFalse(); + } + + @Test + void iterable_is_null_or_empty() { + assertThat(Utils.isNullOrEmpty((Iterable) null)).isTrue(); + assertThat(Utils.isNullOrEmpty((Iterable) emptyList())).isTrue(); + assertThat(Utils.isNullOrEmpty((Iterable) Collections.singletonList("abc"))) + .isFalse(); + } + + @Test + void array_is_null_or_empty() { + // Null array + assertThat(Utils.isNullOrEmpty((Object[]) null)).isTrue(); + + // Empty array + assertThat(Utils.isNullOrEmpty(new Object[0])).isTrue(); + + // Non-empty array with one element + assertThat(Utils.isNullOrEmpty(new Object[] {"abc"})).isFalse(); + + // Non-empty array with multiple elements + assertThat(Utils.isNullOrEmpty(new Object[] {"a", "b", "c"})).isFalse(); + + // Array with a null element (still non-empty) + assertThat(Utils.isNullOrEmpty(new Object[] {null})).isFalse(); + + // Mixed null and non-null elements + assertThat(Utils.isNullOrEmpty(new Object[] {null, "xyz"})).isFalse(); + } + + @MethodSource + @ParameterizedTest + void test_firstNotNull(Object[] values, Object expected) { + assertThat(Utils.firstNotNull("testParam", values)).isEqualTo(expected); + } + + static Stream test_firstNotNull() { + return Stream.of( + Arguments.of(new Object[] {"first", "second"}, "first"), + Arguments.of(new Object[] {null, "second"}, "second"), + Arguments.of(new Object[] {null, null, "third"}, "third"), + Arguments.of(new Object[] {42, null}, 42), + Arguments.of(new Object[] {null, true}, true)); + } + + @Test + void firstNotNull_throwsWhenAllValuesAreNull() { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> Utils.firstNotNull("testParam", (Object) null, null)) + .withMessageContaining("At least one of the given 'testParam' values must be not null"); + } + + @Test + void firstNotNull_throwsWhenValuesArrayIsEmpty() { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> Utils.firstNotNull("testParam")) + .withMessageContaining("testParam values cannot be null or empty"); + } +} diff --git a/api/src/test/java/ai/docling/api/util/ValidationUtilsTests.java b/api/src/test/java/ai/docling/api/util/ValidationUtilsTests.java new file mode 100644 index 0000000..52de035 --- /dev/null +++ b/api/src/test/java/ai/docling/api/util/ValidationUtilsTests.java @@ -0,0 +1,264 @@ +package ai.docling.api.util; + +import static ai.docling.api.util.ValidationUtils.ensureBetween; +import static ai.docling.api.util.ValidationUtils.ensureEq; +import static ai.docling.api.util.ValidationUtils.ensureGreaterThanZero; +import static ai.docling.api.util.ValidationUtils.ensureNotNegative; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; + +class ValidationUtilsTests { + @Test + void ensure_eq() { + ensureEq(1, 1, "test"); + ensureEq("abc", "abc", "test"); + ensureEq(null, null, "test"); + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> ensureEq(1, 2, "test %d", 7)) + .withMessageContaining("test 7"); + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> ensureEq(1, null, "test")) + .withMessageContaining("test"); + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> ensureEq(null, 1, "test")) + .withMessageContaining("test"); + } + + @Test + void ensure_not_null() { + { + Object obj = new Object(); + assertThat(ValidationUtils.ensureNotNull(obj, "test")).isSameAs(obj); + } + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> ValidationUtils.ensureNotNull(null, "test")) + .withMessage("test cannot be null"); + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> ValidationUtils.ensureNotNull(null, "test %d", 7)) + .withMessage("test 7"); + } + + @Test + void ensure_not_empty_string() { + { + String str = " abc "; + assertThat(ValidationUtils.ensureNotEmpty(str, "test")).isSameAs(str); + } + + { + String str = ""; + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> ValidationUtils.ensureNotEmpty(str, "test")) + .withMessageContaining("test cannot be null or empty"); + } + + { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> ValidationUtils.ensureNotEmpty((String) null, "test")) + .withMessageContaining("test cannot be null or empty"); + } + } + + @Test + void ensure_not_empty_collection() { + { + List list = new ArrayList<>(); + list.add(new Object()); + assertThat(ValidationUtils.ensureNotEmpty(list, "test")).isSameAs(list); + } + + { + List list = new ArrayList<>(); + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> ValidationUtils.ensureNotEmpty(list, "test")) + .withMessageContaining("test cannot be null or empty"); + } + + { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> ValidationUtils.ensureNotEmpty((Collection) null, "test")) + .withMessageContaining("test cannot be null or empty"); + } + } + + @Test + void ensure_not_empty_array() { + { + Object[] array = {new Object()}; + assertThat(ValidationUtils.ensureNotEmpty(array, "test")).isSameAs(array); + } + + { + Object[] array = {}; + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> ValidationUtils.ensureNotEmpty(array, "test")) + .withMessageContaining("test cannot be null or empty"); + } + + { + Object[] array = {}; + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> + ValidationUtils.ensureNotEmpty(array, "%s", "Parameterized type has no type arguments.")) + .withMessageContaining("Parameterized type has no type arguments."); + } + + { + Object[] array = null; + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> ValidationUtils.ensureNotEmpty(array, "test")) + .withMessageContaining("test cannot be null or empty"); + } + } + + @Test + void ensure_not_empty_map() { + { + Map map = new HashMap<>(); + map.put(new Object(), new Object()); + assertThat(ValidationUtils.ensureNotEmpty(map, "test")).isSameAs(map); + } + + { + Map map = new HashMap<>(); + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> ValidationUtils.ensureNotEmpty(map, "test")) + .withMessageContaining("test cannot be null or empty"); + } + + { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> ValidationUtils.ensureNotEmpty((Map) null, "test")) + .withMessageContaining("test cannot be null or empty"); + } + } + + @Test + void ensure_not_blank() { + { + String str = " abc "; + assertThat(ValidationUtils.ensureNotBlank(str, "test")).isSameAs(str); + } + + { + String str = " "; + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> ValidationUtils.ensureNotBlank(str, "test")) + .withMessageContaining("test cannot be null or blank"); + } + + { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> ValidationUtils.ensureNotBlank(null, "test")) + .withMessageContaining("test cannot be null or blank"); + } + } + + @Test + void ensure_true() { + { + ValidationUtils.ensureTrue(true, "test"); + } + + { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> ValidationUtils.ensureTrue(false, "test")) + .withMessageContaining("test"); + } + } + + @ParameterizedTest + @ValueSource(ints = { 0, 1, Integer.MAX_VALUE}) + void should_not_throw_when_positive(Integer i) { + ensureNotNegative(i, "integer"); + } + + @ParameterizedTest + @NullSource + @ValueSource(ints = {Integer.MIN_VALUE, -1}) + void should_throw_when_negative(Integer i) { + assertThatThrownBy(() -> ensureNotNegative(i, "integer")) + .isExactlyInstanceOf(IllegalArgumentException.class) + .hasMessage("integer must not be negative, but is: " + i); + } + + @ParameterizedTest + @ValueSource(ints = {1, Integer.MAX_VALUE}) + void should_not_throw_when_greater_than_0(Integer i) { + ensureGreaterThanZero(i, "integer"); + } + + @ParameterizedTest + @NullSource + @ValueSource(ints = {Integer.MIN_VALUE, 0}) + void should_throw_when_when_not_greater_than_0(Integer i) { + assertThatThrownBy(() -> ensureGreaterThanZero(i, "integer")) + .isExactlyInstanceOf(IllegalArgumentException.class) + .hasMessage("integer must be greater than zero, but is: " + i); + } + + @ParameterizedTest + @ValueSource(doubles = {0.0, 0.5, 1.0}) + void should_not_throw_when_between(Double d) { + ensureBetween(d, 0.0, 1.0, "test"); + } + + @ParameterizedTest + @NullSource + @ValueSource(doubles = {-0.1, 1.1}) + void should_throw_when_not_between(Double d) { + assertThatThrownBy(() -> ensureBetween(d, 0.0, 1.0, "test")) + .isExactlyInstanceOf(IllegalArgumentException.class) + .hasMessage("test must be between 0.0 and 1.0, but is: " + d); + } + + @Test + void ensure_between_int() { + { + ensureBetween(1, 0, 1, "test"); + } + { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> ensureBetween(2, 0, 1, "test")) + .withMessageContaining("test must be between 0 and 1, but is: 2"); + } + { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> ensureBetween(-1, 0, 1, "test")) + .withMessageContaining("test must be between 0 and 1, but is: -1"); + } + } + + @Test + void ensure_between_long() { + { + ensureBetween(1L, 0, 1, "test"); + } + { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> ensureBetween(2L, 0, 1, "test")) + .withMessageContaining("test must be between 0 and 1, but is: 2"); + } + { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> ensureBetween(-1L, 0, 1, "test")) + .withMessageContaining("test must be between 0 and 1, but is: -1"); + } + } +} diff --git a/buildSrc/src/main/kotlin/docling-java-shared.gradle.kts b/buildSrc/src/main/kotlin/docling-java-shared.gradle.kts index c49700a..63cdfc7 100644 --- a/buildSrc/src/main/kotlin/docling-java-shared.gradle.kts +++ b/buildSrc/src/main/kotlin/docling-java-shared.gradle.kts @@ -1,4 +1,5 @@ plugins { + id("docling-shared") `java-library` `maven-publish` } diff --git a/buildSrc/src/main/kotlin/docling-shared.gradle.kts b/buildSrc/src/main/kotlin/docling-shared.gradle.kts new file mode 100644 index 0000000..3f505e5 --- /dev/null +++ b/buildSrc/src/main/kotlin/docling-shared.gradle.kts @@ -0,0 +1,2 @@ +group = "ai.docling" +version = property("version").toString() diff --git a/client/src/main/java/ai/docling/client/DoclingClient.java b/client/src/main/java/ai/docling/client/DoclingClient.java index a09ed2d..df0f6f2 100644 --- a/client/src/main/java/ai/docling/client/DoclingClient.java +++ b/client/src/main/java/ai/docling/client/DoclingClient.java @@ -1,5 +1,8 @@ package ai.docling.client; +import static ai.docling.api.util.ValidationUtils.ensureNotBlank; +import static ai.docling.api.util.ValidationUtils.ensureNotNull; + import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; @@ -18,26 +21,29 @@ * Default implementation for the Docling API client. */ public class DoclingClient implements DoclingApi { - private static final URI DEFAULT_BASE_URL = URI.create("http://localhost:5001"); private final URI baseUrl; private final HttpClient httpClient; private final JsonMapper jsonMapper; - private DoclingClient(URI baseUrl, HttpClient httpClient, JsonMapper jsonMapper) { - this.baseUrl = baseUrl; - this.httpClient = httpClient; - this.jsonMapper = jsonMapper; - } + private DoclingClient(Builder builder) { + this.baseUrl = ensureNotNull(builder.baseUrl, "baseUrl"); - public static Builder builder() { - return new Builder(); + if (Objects.equals(this.baseUrl.getScheme(), "http")) { + // Docling Serve uses Python FastAPI which causes errors when called from JDK HttpClient. + // The HttpClient uses HTTP 2 by default and then falls back to HTTP 1.1 if not supported. + // However, the way FastAPI works results in the fallback not happening, making the call fail. + builder.httpClientBuilder.version(HttpClient.Version.HTTP_1_1); + } + + this.httpClient = ensureNotNull(builder.httpClientBuilder, "httpClientBuilder").build(); + this.jsonMapper = ensureNotNull(builder.jsonMapperBuilder, "jsonMapperBuilder").build(); } @Override public HealthCheckResponse health() { - HttpRequest httpRequest = HttpRequest.newBuilder() + var httpRequest = HttpRequest.newBuilder() .uri(baseUrl.resolve("/health")) .header("Accept", "application/json") .GET() @@ -51,7 +57,7 @@ public HealthCheckResponse health() { @Override public ConvertDocumentResponse convertSource(ConvertDocumentRequest request) { - HttpRequest httpRequest = HttpRequest.newBuilder() + var httpRequest = HttpRequest.newBuilder() .uri(baseUrl.resolve("/v1/convert/source")) .header("Accept", "application/json") .header("Content-Type", "application/json") @@ -64,56 +70,112 @@ public ConvertDocumentResponse convertSource(ConvertDocumentRequest request) { .join(); } - public static final class Builder { + @Override + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Creates and returns a new instance of {@link Builder} for constructing a {@link DoclingClient}. + * The builder allows customization of configuration such as base URL, HTTP client, and JSON mapper. + * + * @return a new {@link Builder} instance for creating a {@link DoclingClient}. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * A builder class for creating instances of {@link DoclingClient}. This builder supports a fluent + * API for configuring properties such as the base URL, HTTP client, and JSON mapper. + * + *

The {@link Builder} provides customization options through the available methods and ensures + * proper validation of inputs during configuration. + * + *

An instance of {@code Builder} can be obtained via {@link DoclingClient#builder()} or + * {@link DoclingClient#toBuilder()}. + * + *

This class is an implementation of {@link DoclingApiBuilder}, allowing it to construct + * {@code DoclingClient} instances as part of the API construction process. + */ + public static final class Builder implements DoclingApiBuilder { private URI baseUrl = DEFAULT_BASE_URL; private HttpClient.Builder httpClientBuilder = HttpClient.newBuilder(); private JsonMapper.Builder jsonMapperBuilder = JsonMapper.builder(); - private Builder() {} + private Builder() { + } + private Builder(DoclingClient doclingClient) { + this.baseUrl = doclingClient.baseUrl; + this.httpClientBuilder = HttpClient.newBuilder(); + this.jsonMapperBuilder = doclingClient.jsonMapper.rebuild(); + } + + /** + * Sets the base URL for the Docling API service. + * + *

The URL string will be parsed and converted to a {@link URI}. + * + * @param baseUrl the base URL as a string (must not be blank) + * @return this {@link Builder} instance for method chaining + * @throws IllegalArgumentException if baseUrl is null or blank + */ public Builder baseUrl(String baseUrl) { - if (baseUrl == null || baseUrl.isBlank()) { - throw new IllegalArgumentException("baseUrl cannot be null or empty"); - } - this.baseUrl = URI.create(baseUrl); + this.baseUrl = URI.create(ensureNotBlank(baseUrl, "baseUrl")); return this; } + /** + * Sets the base URL for the Docling API service. + * + * @param baseUrl the base URL as a {@link URI} + * @return this {@link Builder} instance for method chaining + */ public Builder baseUrl(URI baseUrl) { - if (baseUrl == null) { - throw new IllegalArgumentException("baseUrl cannot be null"); - } this.baseUrl = baseUrl; return this; } + /** + * Sets the HTTP client builder to be used for creating the underlying HTTP client. + * + *

This allows customization of HTTP client properties such as timeouts, + * proxy settings, SSL context, and other connection parameters. + * + * @param httpClientBuilder the {@link HttpClient.Builder} to use + * @return this {@link Builder} instance for method chaining + */ public Builder httpClientBuilder(HttpClient.Builder httpClientBuilder) { - if (httpClientBuilder == null) { - throw new IllegalArgumentException("httpClientBuilder cannot be null"); - } this.httpClientBuilder = httpClientBuilder; return this; } + /** + * Sets the JSON mapper builder to be used for creating the JSON mapper. + * + *

This allows customization of JSON serialization and deserialization behavior, + * such as configuring features, modules, or property naming strategies. + * + * @param jsonMapperBuilder the {@link JsonMapper.Builder} to use + * @return this {@link Builder} instance for method chaining + */ public Builder jsonParser(JsonMapper.Builder jsonMapperBuilder) { - if (jsonMapperBuilder == null) { - throw new IllegalArgumentException("jsonMapperBuilder cannot be null"); - } this.jsonMapperBuilder = jsonMapperBuilder; return this; } + /** + * Builds and returns a new {@link DoclingClient} instance with the configured properties. + * + *

This method validates all required parameters and constructs the client. + * + * @return a new {@link DoclingClient} instance + * @throws IllegalArgumentException if any required parameter is null + */ + @Override public DoclingClient build() { - if (Objects.equals(baseUrl.getScheme(), "http")) { - // Docling Serve uses Python FastAPI which causes errors when called from JDK HttpClient. - // The HttpClient uses HTTP 2 by default and then falls back to HTTP 1.1 if not supported. - // However, the way FastAPI works results in the fallback not happening, making the call fail. - httpClientBuilder.version(HttpClient.Version.HTTP_1_1); - } - - return new DoclingClient(baseUrl, httpClientBuilder.build(), jsonMapperBuilder.build()); + return new DoclingClient(this); } - } - } diff --git a/client/src/test/java/ai/docling/client/DoclingClientTests.java b/client/src/test/java/ai/docling/client/DoclingClientTests.java index c54f6af..cb239d5 100644 --- a/client/src/test/java/ai/docling/client/DoclingClientTests.java +++ b/client/src/test/java/ai/docling/client/DoclingClientTests.java @@ -14,6 +14,7 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; +import ai.docling.api.DoclingApi; import ai.docling.api.convert.request.ConvertDocumentRequest; import ai.docling.api.convert.request.options.ConvertDocumentOptions; import ai.docling.api.convert.request.options.TableFormerMode; @@ -27,22 +28,21 @@ */ @Testcontainers class DoclingClientTests { - @Container private static final DoclingContainer doclingContainer = new DoclingContainer( DoclingContainerConfig.builder() - .imageName(Images.DOCLING) + .imageName(DoclingContainerConfig.DOCLING_IMAGE) .enableUi(true) .build(), Optional.of(Duration.ofMinutes(2)) ); - private static DoclingClient doclingClient; + private static DoclingApi doclingClient; @BeforeAll static void setUp() { doclingClient = DoclingClient.builder() - .baseUrl("http://localhost:%s".formatted(doclingContainer.getMappedPort(Images.DOCLING_DEFAULT_PORT))) + .baseUrl("http://localhost:%s".formatted(doclingContainer.getMappedPort(DoclingContainerConfig.DEFAULT_DOCLING_PORT))) .build(); } @@ -50,8 +50,10 @@ static void setUp() { void shouldSuccessfullyCallHealthEndpoint() { HealthCheckResponse response = doclingClient.health(); - assertThat(response).isNotNull(); - assertThat(response.status()).isEqualTo("ok"); + assertThat(response) + .isNotNull() + .extracting(HealthCheckResponse::status) + .isEqualTo("ok"); } @Test @@ -124,5 +126,4 @@ private static byte[] readFileFromClasspath(String filePath) throws IOException return inputStream.readAllBytes(); } } - } diff --git a/client/src/test/java/ai/docling/client/Images.java b/client/src/test/java/ai/docling/client/Images.java deleted file mode 100644 index b6df360..0000000 --- a/client/src/test/java/ai/docling/client/Images.java +++ /dev/null @@ -1,15 +0,0 @@ -package ai.docling.client; - -/** - * Images used in tests. - */ -public class Images { - - public static final String DOCLING = "ghcr.io/docling-project/docling-serve:v1.5.1"; - - public static final int DOCLING_DEFAULT_PORT = 5001; - - private Images() { - } - -} diff --git a/docs/build.gradle.kts b/docs/build.gradle.kts index 29381b1..185de75 100644 --- a/docs/build.gradle.kts +++ b/docs/build.gradle.kts @@ -1,15 +1,8 @@ plugins { - id("docling-java-shared") + id("docling-shared") id("ru.vyarus.mkdocs") version "4.0.1" } -dependencies { - compileOnly(project(":docling-api")) - compileOnly(project(":docling-client")) - compileOnly(project(":docling-testing")) - compileOnly(project(":docling-testcontainers")) -} - python { pip( "mkdocs:1.6.1", @@ -43,3 +36,7 @@ tasks.withType().configureEach { tasks.withType().configureEach { enabled = false } + +tasks.register("build") { + dependsOn(tasks.named("mkdocsBuild")) +} diff --git a/testcontainers/src/main/java/ai/docling/testcontainers/DoclingContainer.java b/testcontainers/src/main/java/ai/docling/testcontainers/DoclingContainer.java index 982f34a..8d8127b 100644 --- a/testcontainers/src/main/java/ai/docling/testcontainers/DoclingContainer.java +++ b/testcontainers/src/main/java/ai/docling/testcontainers/DoclingContainer.java @@ -20,11 +20,6 @@ public class DoclingContainer extends GenericContainer { private static final Logger LOG = Logger.getLogger(DoclingContainer.class.getName()); - /** - * The default container port that docling runs on - */ - public static final int DEFAULT_DOCLING_PORT = 5001; - /** * The dynamic host name determined from TestContainers */ @@ -43,7 +38,7 @@ public DoclingContainer(DoclingContainerConfig config, Optional timeou this.config = config; // Configure the container - withExposedPorts(DEFAULT_DOCLING_PORT); + withExposedPorts(DoclingContainerConfig.DEFAULT_DOCLING_PORT); withEnv(config.containerEnv()); waitingFor(Wait.forHttp("/health")); @@ -60,6 +55,6 @@ public DoclingContainer(DoclingContainerConfig config, Optional timeou * @return the dynamically mapped port on the host machine for the Docling container */ public int getPort() { - return getMappedPort(DEFAULT_DOCLING_PORT); + return getMappedPort(DoclingContainerConfig.DEFAULT_DOCLING_PORT); } } diff --git a/testcontainers/src/main/java/ai/docling/testcontainers/config/DoclingContainerConfig.java b/testcontainers/src/main/java/ai/docling/testcontainers/config/DoclingContainerConfig.java index 3603049..911190b 100644 --- a/testcontainers/src/main/java/ai/docling/testcontainers/config/DoclingContainerConfig.java +++ b/testcontainers/src/main/java/ai/docling/testcontainers/config/DoclingContainerConfig.java @@ -13,7 +13,12 @@ public interface DoclingContainerConfig { /** * Default image name */ - String DOCLING_IMAGE = "quay.io/docling-project/docling-serve:v1.6.0"; + String DOCLING_IMAGE = "ghcr.io/docling-project/docling-serve:v1.6.0"; + + /** + * The default container port that docling runs on + */ + int DEFAULT_DOCLING_PORT = 5001; /** * The container image name to use. From 2760a896deb110c159065ad725995d624ca8e546 Mon Sep 17 00:00:00 2001 From: Eric Deandrea Date: Tue, 28 Oct 2025 08:18:42 -0400 Subject: [PATCH 02/13] refactor: Fix up build plugin & dependencies structure Signed-off-by: Eric Deandrea --- docs/build.gradle.kts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/build.gradle.kts b/docs/build.gradle.kts index 158e8fe..4ff9144 100644 --- a/docs/build.gradle.kts +++ b/docs/build.gradle.kts @@ -40,7 +40,3 @@ tasks.withType().configureEach { tasks.withType().configureEach { enabled = false } - -tasks.register("build") { - dependsOn(tasks.named("mkdocsBuild")) -} From 8f88ebee8db236b90036390dc7622c61a1f2400a Mon Sep 17 00:00:00 2001 From: Eric Deandrea Date: Tue, 28 Oct 2025 12:39:44 -0400 Subject: [PATCH 03/13] feat: idea for api design Support both Jackson 2 & Jackson 3 Signed-off-by: Eric Deandrea --- api/build.gradle.kts | 1 + .../java/ai/docling/api/convert/response/DocumentResponse.java | 2 ++ gradle/libs.versions.toml | 3 +++ 3 files changed, 6 insertions(+) diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 4c8df76..3a9cce2 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -6,5 +6,6 @@ dependencies { implementation(platform(libs.jackson.bom)) implementation(libs.jackson.annotations) implementation(libs.jackson.databind) + implementation(libs.jackson2.databind) testImplementation(project(":docling-testcontainers")) } diff --git a/api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java b/api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java index 8351b0b..3edc06c 100644 --- a/api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java +++ b/api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java @@ -14,6 +14,7 @@ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonDeserialize(builder = DocumentResponse.Builder.class) +@com.fasterxml.jackson.databind.annotation.JsonDeserialize(builder = DocumentResponse.Builder.class) public interface DocumentResponse { /** * Retrieves the content of the doc tags, if available. @@ -133,6 +134,7 @@ public DefaultDocumentResponse(Builder builder) { * accessibility outside the defining package or class hierarchy. */ @JsonPOJOBuilder(withPrefix = "") + @com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "") class Builder { protected String doctagsContent; protected String filename; diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 45f5f3a..8b054d2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,6 +2,7 @@ junit = "6.0.0" assertj = "3.27.6" jackson = "3.0.1" +jackson2 = "2.20.0" jspecify = "1.0.0" testcontainers = "2.0.1" @@ -11,8 +12,10 @@ junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter" } junit-platform = { group = "org.junit.platform", name = "junit-platform-launcher" } assertj-core = { group = "org.assertj", name = "assertj-core", version.ref = "assertj" } jackson-bom = { group = "tools.jackson", name = "jackson-bom", version.ref = "jackson" } +jackson2-bom = { group = "com.fasterxml.jackson", name = "jackson-bom", version.ref = "jackson2" } jackson-annotations = { group = "com.fasterxml.jackson.core", name = "jackson-annotations" } jackson-databind = { group = "tools.jackson.core", name = "jackson-databind" } +jackson2-databind = { group = "com.fasterxml.jackson.core", name = "jackson-databind", version.ref = "jackson2" } jspecify = { group = "org.jspecify", name = "jspecify", version.ref = "jspecify" } testcontainers-bom = { group = "org.testcontainers", name = "testcontainers-bom", version.ref = "testcontainers"} testcontainers-junit-jupiter = { group = "org.testcontainers", name = "testcontainers-junit-jupiter" } From 1b3c2d93e0629e98901e1d194fc080d7131ed3b8 Mon Sep 17 00:00:00 2001 From: Eric Deandrea Date: Tue, 28 Oct 2025 15:48:19 -0400 Subject: [PATCH 04/13] feat: idea for api design Support both Jackson 2 & Jackson 3 Signed-off-by: Eric Deandrea --- client/build.gradle.kts | 3 +- .../java/ai/docling/client/DoclingClient.java | 161 +++++++++--------- .../client/DoclingClientBuilderFactory.java | 95 +++++++++++ .../docling/client/DoclingJackson2Client.java | 110 ++++++++++++ .../docling/client/DoclingJackson3Client.java | 99 +++++++++++ ...s.java => AbstractDoclingClientTests.java} | 25 +-- .../DoclingClientBuilderFactoryTests.java | 59 +++++++ .../client/DoclingJackson2ClientTests.java | 25 +++ .../client/DoclingJackson3ClientTests.java | 25 +++ 9 files changed, 501 insertions(+), 101 deletions(-) create mode 100644 client/src/main/java/ai/docling/client/DoclingClientBuilderFactory.java create mode 100644 client/src/main/java/ai/docling/client/DoclingJackson2Client.java create mode 100644 client/src/main/java/ai/docling/client/DoclingJackson3Client.java rename client/src/test/java/ai/docling/client/{DoclingClientTests.java => AbstractDoclingClientTests.java} (82%) create mode 100644 client/src/test/java/ai/docling/client/DoclingClientBuilderFactoryTests.java create mode 100644 client/src/test/java/ai/docling/client/DoclingJackson2ClientTests.java create mode 100644 client/src/test/java/ai/docling/client/DoclingJackson3ClientTests.java diff --git a/client/build.gradle.kts b/client/build.gradle.kts index f3774d0..48f2e26 100644 --- a/client/build.gradle.kts +++ b/client/build.gradle.kts @@ -5,7 +5,8 @@ plugins { dependencies { api(platform(libs.jackson.bom)) api(project(":docling-api")) - api(libs.jackson.databind) + compileOnly(libs.jackson.databind) + compileOnly(libs.jackson2.databind) testImplementation(platform(libs.testcontainers.bom)) testImplementation(libs.testcontainers.junit.jupiter) diff --git a/client/src/main/java/ai/docling/client/DoclingClient.java b/client/src/main/java/ai/docling/client/DoclingClient.java index df0f6f2..6ce7898 100644 --- a/client/src/main/java/ai/docling/client/DoclingClient.java +++ b/client/src/main/java/ai/docling/client/DoclingClient.java @@ -15,19 +15,24 @@ import ai.docling.api.convert.response.ConvertDocumentResponse; import ai.docling.api.health.HealthCheckResponse; -import tools.jackson.databind.json.JsonMapper; - /** - * Default implementation for the Docling API client. + * Abstract class representing a client for interacting with the Docling API. + * + *

This class handles the foundational functionality needed to perform HTTP + * requests to the Docling API, with customizable base URL and HTTP client + * configurations. It provides abstract methods for JSON serialization and + * deserialization, allowing implementation-specific customization. + * + *

Concrete subclasses must implement {@link #readValue(String, Class)} and + * {@link #writeValueAsString(Object)} for serialization and deserialization behavior. */ -public class DoclingClient implements DoclingApi { - private static final URI DEFAULT_BASE_URL = URI.create("http://localhost:5001"); +public abstract class DoclingClient implements DoclingApi { + protected static final URI DEFAULT_BASE_URL = URI.create("http://localhost:5001"); private final URI baseUrl; private final HttpClient httpClient; - private final JsonMapper jsonMapper; - private DoclingClient(Builder builder) { + protected DoclingClient(DoclingClientBuilder builder) { this.baseUrl = ensureNotNull(builder.baseUrl, "baseUrl"); if (Objects.equals(this.baseUrl.getScheme(), "http")) { @@ -38,9 +43,29 @@ private DoclingClient(Builder builder) { } this.httpClient = ensureNotNull(builder.httpClientBuilder, "httpClientBuilder").build(); - this.jsonMapper = ensureNotNull(builder.jsonMapperBuilder, "jsonMapperBuilder").build(); } + /** + * Reads and deserializes the given JSON string into an instance of the specified type. + * + * @param json the JSON string to deserialize; must not be {@code null} + * @param valueType the {@link Class} of the target type; must not be {@code null} + * @param the type of the object to be deserialized + * @return an instance of {@code T} deserialized from the provided JSON + * @throws RuntimeException if the JSON parsing fails + */ + protected abstract T readValue(String json, Class valueType); + + /** + * Serializes the given object into its JSON string representation. + * + * @param the type of the object to serialize + * @param value the object to serialize; must not be {@code null} + * @return the JSON string representation of the given object + * @throws RuntimeException if serialization fails + */ + protected abstract String writeValueAsString(T value); + @Override public HealthCheckResponse health() { var httpRequest = HttpRequest.newBuilder() @@ -51,7 +76,7 @@ public HealthCheckResponse health() { return httpClient.sendAsync(httpRequest, BodyHandlers.ofString()) .thenApply(HttpResponse::body) - .thenApply(json -> jsonMapper.readValue(json, HealthCheckResponse.class)) + .thenApply(json -> readValue(json, HealthCheckResponse.class)) .join(); } @@ -61,80 +86,79 @@ public ConvertDocumentResponse convertSource(ConvertDocumentRequest request) { .uri(baseUrl.resolve("/v1/convert/source")) .header("Accept", "application/json") .header("Content-Type", "application/json") - .POST(HttpRequest.BodyPublishers.ofString(jsonMapper.writeValueAsString(request))) + .POST(HttpRequest.BodyPublishers.ofString(writeValueAsString(request))) .build(); return httpClient.sendAsync(httpRequest, BodyHandlers.ofString()) .thenApply(HttpResponse::body) - .thenApply(json -> jsonMapper.readValue(json, ConvertDocumentResponse.class)) + .thenApply(json -> readValue(json, ConvertDocumentResponse.class)) .join(); } - @Override - public Builder toBuilder() { - return new Builder(this); - } - /** - * Creates and returns a new instance of {@link Builder} for constructing a {@link DoclingClient}. - * The builder allows customization of configuration such as base URL, HTTP client, and JSON mapper. + * Abstract base class for building instances of {@link DoclingClient}. * - * @return a new {@link Builder} instance for creating a {@link DoclingClient}. - */ - public static Builder builder() { - return new Builder(); - } - - /** - * A builder class for creating instances of {@link DoclingClient}. This builder supports a fluent - * API for configuring properties such as the base URL, HTTP client, and JSON mapper. - * - *

The {@link Builder} provides customization options through the available methods and ensures - * proper validation of inputs during configuration. + *

This builder class provides methods for configuring shared properties such as the + * base URL and the HTTP client. Concrete subclasses may extend this builder to + * add additional configuration options. * - *

An instance of {@code Builder} can be obtained via {@link DoclingClient#builder()} or - * {@link DoclingClient#toBuilder()}. - * - *

This class is an implementation of {@link DoclingApiBuilder}, allowing it to construct - * {@code DoclingClient} instances as part of the API construction process. + * @param the type of {@link DoclingClient} being built + * @param the type of the builder implementation */ - public static final class Builder implements DoclingApiBuilder { + public abstract static class DoclingClientBuilder> implements DoclingApiBuilder { private URI baseUrl = DEFAULT_BASE_URL; private HttpClient.Builder httpClientBuilder = HttpClient.newBuilder(); - private JsonMapper.Builder jsonMapperBuilder = JsonMapper.builder(); - private Builder() { + /** + * Protected constructor for use by subclasses of {@link DoclingClientBuilder}. + * + *

Initializes a new instance of the builder with default configuration values. + * This constructor ensures that the builder cannot be instantiated directly, + * promoting proper use and extension by subclasses. + */ + protected DoclingClientBuilder() { } - private Builder(DoclingClient doclingClient) { + /** + * Initializes a new {@link DoclingClientBuilder} instance using the configuration + * from the provided {@link DoclingClient}. + * + * @param doclingClient the {@link DoclingClient} whose configuration (e.g., base URL) + * will be used to initialize the builder + */ + protected DoclingClientBuilder(DoclingClient doclingClient) { this.baseUrl = doclingClient.baseUrl; this.httpClientBuilder = HttpClient.newBuilder(); - this.jsonMapperBuilder = doclingClient.jsonMapper.rebuild(); } /** - * Sets the base URL for the Docling API service. + * Sets the base URL for the client. * - *

The URL string will be parsed and converted to a {@link URI}. + *

This method configures the base URL that will be used for all API requests + * executed by the client. The provided URL must be non-null and not blank. * - * @param baseUrl the base URL as a string (must not be blank) - * @return this {@link Builder} instance for method chaining - * @throws IllegalArgumentException if baseUrl is null or blank + * @param baseUrl the base URL to use, as a {@code String} + * @return this builder instance for method chaining + * @throws IllegalArgumentException if {@code baseUrl} is null, blank, or invalid */ - public Builder baseUrl(String baseUrl) { + public B baseUrl(String baseUrl) { this.baseUrl = URI.create(ensureNotBlank(baseUrl, "baseUrl")); - return this; + return (B) this; } /** - * Sets the base URL for the Docling API service. + * Sets the base URL for the client. * - * @param baseUrl the base URL as a {@link URI} - * @return this {@link Builder} instance for method chaining + *

This method configures the base URL that will be used for all API requests + * executed by the client. The provided URL must be non-null. + * + * @param baseUrl the base URL to use, as a {@link URI} + * @return this builder instance for method chaining + * @throws IllegalArgumentException if {@code baseUrl} is null */ - public Builder baseUrl(URI baseUrl) { + public B baseUrl(URI baseUrl) { this.baseUrl = baseUrl; - return this; + return (B) this; } /** @@ -144,38 +168,11 @@ public Builder baseUrl(URI baseUrl) { * proxy settings, SSL context, and other connection parameters. * * @param httpClientBuilder the {@link HttpClient.Builder} to use - * @return this {@link Builder} instance for method chaining + * @return this {@link DoclingJackson3Client.Builder} instance for method chaining */ - public Builder httpClientBuilder(HttpClient.Builder httpClientBuilder) { + public B httpClientBuilder(HttpClient.Builder httpClientBuilder) { this.httpClientBuilder = httpClientBuilder; - return this; - } - - /** - * Sets the JSON mapper builder to be used for creating the JSON mapper. - * - *

This allows customization of JSON serialization and deserialization behavior, - * such as configuring features, modules, or property naming strategies. - * - * @param jsonMapperBuilder the {@link JsonMapper.Builder} to use - * @return this {@link Builder} instance for method chaining - */ - public Builder jsonParser(JsonMapper.Builder jsonMapperBuilder) { - this.jsonMapperBuilder = jsonMapperBuilder; - return this; - } - - /** - * Builds and returns a new {@link DoclingClient} instance with the configured properties. - * - *

This method validates all required parameters and constructs the client. - * - * @return a new {@link DoclingClient} instance - * @throws IllegalArgumentException if any required parameter is null - */ - @Override - public DoclingClient build() { - return new DoclingClient(this); + return (B) this; } } } diff --git a/client/src/main/java/ai/docling/client/DoclingClientBuilderFactory.java b/client/src/main/java/ai/docling/client/DoclingClientBuilderFactory.java new file mode 100644 index 0000000..065423a --- /dev/null +++ b/client/src/main/java/ai/docling/client/DoclingClientBuilderFactory.java @@ -0,0 +1,95 @@ +package ai.docling.client; + +import ai.docling.client.DoclingClient.DoclingClientBuilder; + +/** + * A factory class for creating instances of {@link DoclingClientBuilder}. + * + *

This factory determines which version of Jackson (2 or 3) is available + * on the application's classpath and provides a corresponding builder for + * creating {@link DoclingClient} implementations. + * + *

If neither Jackson 2 nor Jackson 3 is present on the classpath, an + * {@link IllegalStateException} is thrown. + * + *

The factory uses a type-safe generic method to support custom subclasses of + * {@link DoclingClient} and {@link DoclingClientBuilder}. + */ +public final class DoclingClientBuilderFactory { + private DoclingClientBuilderFactory() { + } + + /** + * Creates and returns a new instance of a {@link DoclingClientBuilder} compatible + * with the Jackson version present on the provided classloader's classpath. + * + *

If Jackson 3 is detected, returns a builder for {@code DoclingJackson3Client}. + * If Jackson 2 is detected, returns a builder for {@code DoclingJackson2Client}. + * If neither version of Jackson is found on the classpath, an + * {@link IllegalStateException} is thrown. + * + *

The method uses generics to support custom implementations of + * {@link DoclingClient} and {@link DoclingClientBuilder}. + * + * @param the type of {@link DoclingClient} to be created by the builder + * @param the type of {@link DoclingClientBuilder} to be returned + * @param classLoader the {@link ClassLoader} used to check for Jackson's presence + * @return a compatible {@link DoclingClientBuilder} instance + * @throws IllegalStateException if neither Jackson 2 nor Jackson 3 is available on the classpath + */ + public static > B newBuilder(ClassLoader classLoader) { + if (JacksonVersion.JACKSON_3.isOnClasspath(classLoader)) { + return (B) DoclingJackson3Client.builder(); + } + else if (JacksonVersion.JACKSON_2.isOnClasspath(classLoader)) { + return (B) DoclingJackson2Client.builder(); + } + + throw new IllegalStateException("Neither Jackson 2 or 3 is on the classpath."); + } + + /** + * Creates and returns a new instance of a {@link DoclingClientBuilder} that is + * compatible with the version of Jackson available on the application's classpath. + * + *

If Jackson 3 is found on the classpath, it returns a builder for + * {@code DoclingJackson3Client}. If Jackson 2 is found, it returns a builder + * for {@code DoclingJackson2Client}. If neither are found, an + * {@link IllegalStateException} is thrown. + * + *

This method utilizes generics to support custom implementations of + * {@link DoclingClient} and {@link DoclingClientBuilder}. + * + * @param the type of {@link DoclingClient} to be created by the builder + * @param the type of {@link DoclingClientBuilder} to be returned + * @return a compatible {@link DoclingClientBuilder} instance + * @throws IllegalStateException if neither Jackson 2 nor Jackson 3 is available on the classpath + */ + public static > B newBuilder() { + return newBuilder(Thread.currentThread().getContextClassLoader()); + } + + private enum JacksonVersion { + JACKSON_2("com.fasterxml.jackson.databind.json.JsonMapper"), + JACKSON_3("tools.jackson.databind.json.JsonMapper"); + + private final String jacksonClassName; + + JacksonVersion(String jacksonClassName) { + this.jacksonClassName = jacksonClassName; + } + + private boolean isOnClasspath() { + return isOnClasspath(Thread.currentThread().getContextClassLoader()); + } + + private boolean isOnClasspath(ClassLoader classLoader) { + try { + Class.forName(this.jacksonClassName, false, classLoader); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } + } +} diff --git a/client/src/main/java/ai/docling/client/DoclingJackson2Client.java b/client/src/main/java/ai/docling/client/DoclingJackson2Client.java new file mode 100644 index 0000000..3e1a74f --- /dev/null +++ b/client/src/main/java/ai/docling/client/DoclingJackson2Client.java @@ -0,0 +1,110 @@ +package ai.docling.client; + +import static ai.docling.api.util.ValidationUtils.ensureNotNull; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.json.JsonMapper; + +/** + * A specialized implementation of {@link DoclingClient} that integrates with the Jackson + * library for JSON serialization and deserialization using {@link com.fasterxml.jackson.databind.json.JsonMapper}. + *

+ * This client provides methods to convert objects to JSON strings and parse JSON strings + * into Java objects, leveraging the capabilities of Jackson's {@link com.fasterxml.jackson.databind.json.JsonMapper}. + * + *

Instances of this client can be created through the {@link Builder} by configuring + * properties such as the {@link com.fasterxml.jackson.databind.json.JsonMapper.Builder}. + */ +public class DoclingJackson2Client extends DoclingClient { + private final JsonMapper jsonMapper; + + private DoclingJackson2Client(Builder builder) { + super(builder);; + this.jsonMapper = ensureNotNull(builder.jsonMapperBuilder, "jsonMapperBuilder").build(); + } + + @Override + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Creates and returns a new {@link Builder} instance for configuring and constructing + * a {@link DoclingJackson2Client}. + * + *

The returned {@link Builder} can be used to customize the client by setting + * various properties, such as the JSON mapper builder, before creating a new + * {@link DoclingJackson2Client}. + * + * @return a new {@link Builder} instance for configuring and building a {@link DoclingJackson2Client} + */ + public static Builder builder() { + return new Builder(); + } + + @Override + protected T readValue(String json, Class valueType) { + try { + return this.jsonMapper.readValue(json, valueType); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + @Override + protected String writeValueAsString(T value) { + try { + return this.jsonMapper.writeValueAsString(value); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + /** + * A builder class for constructing instances of {@link DoclingJackson2Client}. + * + *

This builder extends {@link DoclingClientBuilder} to provide additional configuration + * specific to {@link DoclingJackson2Client}, such as customizing the JSON mapper. + * Use this builder to configure and create a new, immutable {@link DoclingJackson2Client} instance. + * + *

The builder supports method chaining for fluent configuration. + */ + public static final class Builder extends DoclingClientBuilder { + private JsonMapper.Builder jsonMapperBuilder = JsonMapper.builder(); + + private Builder() { + } + + private Builder(DoclingJackson2Client doclingClient) { + super(doclingClient); + this.jsonMapperBuilder = doclingClient.jsonMapper.rebuild(); + } + + /** + * Sets the JSON mapper builder to be used for creating the JSON mapper. + * + *

This allows customization of JSON serialization and deserialization behavior, + * such as configuring features, modules, or property naming strategies. + * + * @param jsonMapperBuilder the {@link JsonMapper.Builder} to use + * @return this {@link Builder} instance for method chaining + */ + public Builder jsonParser(JsonMapper.Builder jsonMapperBuilder) { + this.jsonMapperBuilder = jsonMapperBuilder; + return this; + } + + /** + * Builds and returns a new {@link DoclingJackson2Client} instance with the configured properties. + * + *

This method validates all required parameters and constructs the client. + * + * @return a new {@link DoclingJackson2Client} instance + * @throws IllegalArgumentException if any required parameter is null + */ + @Override + public DoclingJackson2Client build() { + return new DoclingJackson2Client(this); + } + } +} diff --git a/client/src/main/java/ai/docling/client/DoclingJackson3Client.java b/client/src/main/java/ai/docling/client/DoclingJackson3Client.java new file mode 100644 index 0000000..d6b82fb --- /dev/null +++ b/client/src/main/java/ai/docling/client/DoclingJackson3Client.java @@ -0,0 +1,99 @@ +package ai.docling.client; + +import static ai.docling.api.util.ValidationUtils.ensureNotNull; + +import tools.jackson.databind.json.JsonMapper; + +/** + * A client implementation that leverages Jackson 3 for JSON serialization and deserialization. + * + *

This class extends {@link DoclingClient} and provides a concrete implementation for reading + * and writing JSON data using a {@link JsonMapper} instance. + */ +public class DoclingJackson3Client extends DoclingClient { + private final JsonMapper jsonMapper; + + private DoclingJackson3Client(Builder builder) { + super(builder);; + this.jsonMapper = ensureNotNull(builder.jsonMapperBuilder, "jsonMapperBuilder").build(); + } + + @Override + public Builder toBuilder() { + return new Builder(this); + } + + /** + * Creates a new {@link Builder} instance for configuring and constructing a {@link DoclingJackson3Client}. + * + *

This method serves as the entry point for creating a new {@link DoclingJackson3Client.Builder} + * to customize and build a {@code DoclingJackson3Client}. + * + * @return a new {@link Builder} instance + */ + public static Builder builder() { + return new Builder(); + } + + @Override + protected T readValue(String json, Class valueType) { + return this.jsonMapper.readValue(json, valueType); + } + + @Override + protected String writeValueAsString(T value) { + return this.jsonMapper.writeValueAsString(value); + } + + /** + * A builder class for configuring and constructing instances of {@link DoclingJackson3Client}. + * + *

This builder extends {@link DoclingClientBuilder}, allowing customization of common client + * parameters such as base URL and HTTP client, while also providing additional functionality + * specific to JSON mapping via a {@link JsonMapper.Builder}. + * + *

Instances of this builder can be initialized either as a new builder or based on an + * existing {@link DoclingJackson3Client}. + * + *

This class is not publicly instantiable; use {@link DoclingJackson3Client#builder()} + * or {@link DoclingJackson3Client#toBuilder()} to obtain a builder instance. + */ + public static final class Builder extends DoclingClientBuilder { + private JsonMapper.Builder jsonMapperBuilder = JsonMapper.builder(); + + private Builder() { + } + + private Builder(DoclingJackson3Client doclingClient) { + super(doclingClient); + this.jsonMapperBuilder = doclingClient.jsonMapper.rebuild(); + } + + /** + * Sets the JSON mapper builder to be used for creating the JSON mapper. + * + *

This allows customization of JSON serialization and deserialization behavior, + * such as configuring features, modules, or property naming strategies. + * + * @param jsonMapperBuilder the {@link JsonMapper.Builder} to use + * @return this {@link Builder} instance for method chaining + */ + public Builder jsonParser(JsonMapper.Builder jsonMapperBuilder) { + this.jsonMapperBuilder = jsonMapperBuilder; + return this; + } + + /** + * Builds and returns a new {@link DoclingJackson3Client} instance with the configured properties. + * + *

This method validates all required parameters and constructs the client. + * + * @return a new {@link DoclingJackson3Client} instance + * @throws IllegalArgumentException if any required parameter is null + */ + @Override + public DoclingJackson3Client build() { + return new DoclingJackson3Client(this); + } + } +} diff --git a/client/src/test/java/ai/docling/client/DoclingClientTests.java b/client/src/test/java/ai/docling/client/AbstractDoclingClientTests.java similarity index 82% rename from client/src/test/java/ai/docling/client/DoclingClientTests.java rename to client/src/test/java/ai/docling/client/AbstractDoclingClientTests.java index cb239d5..d77bba9 100644 --- a/client/src/test/java/ai/docling/client/DoclingClientTests.java +++ b/client/src/test/java/ai/docling/client/AbstractDoclingClientTests.java @@ -9,7 +9,6 @@ import java.util.Base64; import java.util.Optional; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @@ -23,13 +22,10 @@ import ai.docling.testcontainers.DoclingContainer; import ai.docling.testcontainers.config.DoclingContainerConfig; -/** - * Integration tests for {@link DoclingClient}. - */ @Testcontainers -class DoclingClientTests { +abstract class AbstractDoclingClientTests { @Container - private static final DoclingContainer doclingContainer = new DoclingContainer( + protected static final DoclingContainer doclingContainer = new DoclingContainer( DoclingContainerConfig.builder() .imageName(DoclingContainerConfig.DOCLING_IMAGE) .enableUi(true) @@ -37,18 +33,11 @@ class DoclingClientTests { Optional.of(Duration.ofMinutes(2)) ); - private static DoclingApi doclingClient; - - @BeforeAll - static void setUp() { - doclingClient = DoclingClient.builder() - .baseUrl("http://localhost:%s".formatted(doclingContainer.getMappedPort(DoclingContainerConfig.DEFAULT_DOCLING_PORT))) - .build(); - } + protected abstract DoclingApi getDoclingClient(); @Test void shouldSuccessfullyCallHealthEndpoint() { - HealthCheckResponse response = doclingClient.health(); + HealthCheckResponse response = getDoclingClient().health(); assertThat(response) .isNotNull() @@ -62,7 +51,7 @@ void shouldConvertHttpSourceSuccessfully() { .addHttpSources(URI.create("https://docs.arconia.io/arconia-cli/latest/development/dev/")) .build(); - ConvertDocumentResponse response = doclingClient.convertSource(request); + ConvertDocumentResponse response = getDoclingClient().convertSource(request); assertThat(response).isNotNull(); @@ -84,7 +73,7 @@ void shouldConvertFileSourceSuccessfully() throws IOException { .addFileSources("story.pdf", Base64.getEncoder().encodeToString(fileResource)) .build(); - ConvertDocumentResponse response = doclingClient.convertSource(request); + ConvertDocumentResponse response = getDoclingClient().convertSource(request); assertThat(response).isNotNull(); assertThat(response.status()).isNotEmpty(); @@ -111,7 +100,7 @@ void shouldHandleConversionWithDifferentDocumentOptions() { .options(options) .build(); - ConvertDocumentResponse response = doclingClient.convertSource(request); + ConvertDocumentResponse response = getDoclingClient().convertSource(request); assertThat(response).isNotNull(); assertThat(response.status()).isNotEmpty(); diff --git a/client/src/test/java/ai/docling/client/DoclingClientBuilderFactoryTests.java b/client/src/test/java/ai/docling/client/DoclingClientBuilderFactoryTests.java new file mode 100644 index 0000000..0a917ad --- /dev/null +++ b/client/src/test/java/ai/docling/client/DoclingClientBuilderFactoryTests.java @@ -0,0 +1,59 @@ +package ai.docling.client; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.stream.Stream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import ai.docling.client.DoclingClient.DoclingClientBuilder; + +class DoclingClientBuilderFactoryTests { + @Test + void correctBuilderWhenBothArePresent() { + assertThat(DoclingClientBuilderFactory.newBuilder()) + .isNotNull() + .isExactlyInstanceOf(DoclingJackson3Client.Builder.class); + } + + @ParameterizedTest + @MethodSource("correctBuilderArguments") + void correctBuilder(String classToHide, Class expectedBuilderClass) { + var classLoader = new DelegatingClassLoader(classToHide, Thread.currentThread().getContextClassLoader()); + + assertThat(DoclingClientBuilderFactory.newBuilder(classLoader)) + .isNotNull() + .isExactlyInstanceOf(expectedBuilderClass); + } + + static Stream correctBuilderArguments() { + return Stream.of( + Arguments.of("com.fasterxml.jackson.databind.json.JsonMapper", DoclingJackson3Client.Builder.class), + Arguments.of("tools.jackson.databind.json.JsonMapper", DoclingJackson2Client.Builder.class) + ); + } + + private static class DelegatingClassLoader extends ClassLoader { + private final String classToHide; + private final ClassLoader parent; + + public DelegatingClassLoader(String classToHide, ClassLoader parent) { + super(parent); + + this.classToHide = classToHide; + this.parent = parent; + } + + @Override + public Class loadClass(String name) throws ClassNotFoundException { + if (classToHide.equals(name)) { + throw new ClassNotFoundException("Class %s not found".formatted(classToHide)); + } + + return super.loadClass(name); + } + } +} diff --git a/client/src/test/java/ai/docling/client/DoclingJackson2ClientTests.java b/client/src/test/java/ai/docling/client/DoclingJackson2ClientTests.java new file mode 100644 index 0000000..97a0aeb --- /dev/null +++ b/client/src/test/java/ai/docling/client/DoclingJackson2ClientTests.java @@ -0,0 +1,25 @@ +package ai.docling.client; + +import org.junit.jupiter.api.BeforeAll; + +import ai.docling.api.DoclingApi; +import ai.docling.testcontainers.config.DoclingContainerConfig; + +/** + * Integration tests for {@link DoclingJackson2Client}. + */ +class DoclingJackson2ClientTests extends AbstractDoclingClientTests { + private static DoclingApi doclingClient; + + @BeforeAll + static void setUp() { + doclingClient = DoclingJackson2Client.builder() + .baseUrl("http://localhost:%s".formatted(doclingContainer.getMappedPort(DoclingContainerConfig.DEFAULT_DOCLING_PORT))) + .build(); + } + + @Override + protected DoclingApi getDoclingClient() { + return doclingClient; + } +} diff --git a/client/src/test/java/ai/docling/client/DoclingJackson3ClientTests.java b/client/src/test/java/ai/docling/client/DoclingJackson3ClientTests.java new file mode 100644 index 0000000..f6a2a05 --- /dev/null +++ b/client/src/test/java/ai/docling/client/DoclingJackson3ClientTests.java @@ -0,0 +1,25 @@ +package ai.docling.client; + +import org.junit.jupiter.api.BeforeAll; + +import ai.docling.api.DoclingApi; +import ai.docling.testcontainers.config.DoclingContainerConfig; + +/** + * Integration tests for {@link DoclingClient}. + */ +class DoclingJackson3ClientTests extends AbstractDoclingClientTests { + private static DoclingApi doclingClient; + + @BeforeAll + static void setUp() { + doclingClient = DoclingJackson3Client.builder() + .baseUrl("http://localhost:%s".formatted(doclingContainer.getMappedPort(DoclingContainerConfig.DEFAULT_DOCLING_PORT))) + .build(); + } + + @Override + protected DoclingApi getDoclingClient() { + return doclingClient; + } +} From cf3b812713e63263f73d0283496ca29cd95742f4 Mon Sep 17 00:00:00 2001 From: Eric Deandrea Date: Tue, 28 Oct 2025 16:34:34 -0400 Subject: [PATCH 05/13] feat: idea for api design Support both Jackson 2 & Jackson 3 Signed-off-by: Eric Deandrea --- api/build.gradle.kts | 4 ++-- client/build.gradle.kts | 2 +- gradle/libs.versions.toml | 23 ++++++++++++++++------- testcontainers/build.gradle.kts | 4 ++-- 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 3a9cce2..03416e7 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -3,8 +3,8 @@ plugins { } dependencies { - implementation(platform(libs.jackson.bom)) - implementation(libs.jackson.annotations) + api(platform(libs.jackson.bom)) + api(libs.jackson.annotations) implementation(libs.jackson.databind) implementation(libs.jackson2.databind) testImplementation(project(":docling-testcontainers")) diff --git a/client/build.gradle.kts b/client/build.gradle.kts index 48f2e26..463c0d9 100644 --- a/client/build.gradle.kts +++ b/client/build.gradle.kts @@ -3,8 +3,8 @@ plugins { } dependencies { - api(platform(libs.jackson.bom)) api(project(":docling-api")) + api(platform(libs.jackson.bom)) compileOnly(libs.jackson.databind) compileOnly(libs.jackson2.databind) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8b054d2..40f0975 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,21 +1,30 @@ [versions] -junit = "6.0.0" assertj = "3.27.6" -jackson = "3.0.1" jackson2 = "2.20.0" +jackson3 = "3.0.1" jspecify = "1.0.0" +junit = "6.0.0" testcontainers = "2.0.1" [libraries] -junit-bom = { group = "org.junit", name = "junit-bom", version.ref = "junit" } -junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter" } -junit-platform = { group = "org.junit.platform", name = "junit-platform-launcher" } +# assertj assertj-core = { group = "org.assertj", name = "assertj-core", version.ref = "assertj" } -jackson-bom = { group = "tools.jackson", name = "jackson-bom", version.ref = "jackson" } -jackson2-bom = { group = "com.fasterxml.jackson", name = "jackson-bom", version.ref = "jackson2" } + +# Jackson jackson-annotations = { group = "com.fasterxml.jackson.core", name = "jackson-annotations" } +jackson-bom = { group = "tools.jackson", name = "jackson-bom", version.ref = "jackson3" } +jackson2-bom = { group = "com.fasterxml.jackson", name = "jackson-bom", version.ref = "jackson2" } jackson-databind = { group = "tools.jackson.core", name = "jackson-databind" } jackson2-databind = { group = "com.fasterxml.jackson.core", name = "jackson-databind", version.ref = "jackson2" } + +# JSpecify jspecify = { group = "org.jspecify", name = "jspecify", version.ref = "jspecify" } + +# JUnit +junit-bom = { group = "org.junit", name = "junit-bom", version.ref = "junit" } +junit-jupiter = { group = "org.junit.jupiter", name = "junit-jupiter" } +junit-platform = { group = "org.junit.platform", name = "junit-platform-launcher" } + +# Testcontainers testcontainers-bom = { group = "org.testcontainers", name = "testcontainers-bom", version.ref = "testcontainers"} testcontainers-junit-jupiter = { group = "org.testcontainers", name = "testcontainers-junit-jupiter" } diff --git a/testcontainers/build.gradle.kts b/testcontainers/build.gradle.kts index de043c3..572b18f 100644 --- a/testcontainers/build.gradle.kts +++ b/testcontainers/build.gradle.kts @@ -6,6 +6,6 @@ dependencies { api(platform(libs.testcontainers.bom)) api("org.testcontainers:testcontainers") testImplementation(platform(libs.jackson.bom)) - testImplementation("org.testcontainers:testcontainers-junit-jupiter") - testImplementation("tools.jackson.core:jackson-databind") + testImplementation(libs.jackson.databind) + testImplementation(libs.testcontainers.junit.jupiter) } From 01de8c7dac1c30dd07792bcedbbfe4a5bcf2acbb Mon Sep 17 00:00:00 2001 From: Eric Deandrea Date: Tue, 28 Oct 2025 16:41:29 -0400 Subject: [PATCH 06/13] feat: idea for api design Support both Jackson 2 & Jackson 3 Signed-off-by: Eric Deandrea --- .../client/DoclingClientBuilderFactory.java | 2 +- .../docling/client/DoclingJackson2Client.java | 2 +- .../docling/client/DoclingJackson3Client.java | 2 +- .../DoclingClientBuilderFactoryTests.java | 34 ++++++++++++++----- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/client/src/main/java/ai/docling/client/DoclingClientBuilderFactory.java b/client/src/main/java/ai/docling/client/DoclingClientBuilderFactory.java index 065423a..6366dc5 100644 --- a/client/src/main/java/ai/docling/client/DoclingClientBuilderFactory.java +++ b/client/src/main/java/ai/docling/client/DoclingClientBuilderFactory.java @@ -45,7 +45,7 @@ else if (JacksonVersion.JACKSON_2.isOnClasspath(classLoader)) { return (B) DoclingJackson2Client.builder(); } - throw new IllegalStateException("Neither Jackson 2 or 3 is on the classpath."); + throw new IllegalStateException("Neither Jackson 2 or 3 is on the classpath"); } /** diff --git a/client/src/main/java/ai/docling/client/DoclingJackson2Client.java b/client/src/main/java/ai/docling/client/DoclingJackson2Client.java index 3e1a74f..155d071 100644 --- a/client/src/main/java/ai/docling/client/DoclingJackson2Client.java +++ b/client/src/main/java/ai/docling/client/DoclingJackson2Client.java @@ -38,7 +38,7 @@ public Builder toBuilder() { * * @return a new {@link Builder} instance for configuring and building a {@link DoclingJackson2Client} */ - public static Builder builder() { + static Builder builder() { return new Builder(); } diff --git a/client/src/main/java/ai/docling/client/DoclingJackson3Client.java b/client/src/main/java/ai/docling/client/DoclingJackson3Client.java index d6b82fb..ee8fda9 100644 --- a/client/src/main/java/ai/docling/client/DoclingJackson3Client.java +++ b/client/src/main/java/ai/docling/client/DoclingJackson3Client.java @@ -31,7 +31,7 @@ public Builder toBuilder() { * * @return a new {@link Builder} instance */ - public static Builder builder() { + static Builder builder() { return new Builder(); } diff --git a/client/src/test/java/ai/docling/client/DoclingClientBuilderFactoryTests.java b/client/src/test/java/ai/docling/client/DoclingClientBuilderFactoryTests.java index 0a917ad..73a8f00 100644 --- a/client/src/test/java/ai/docling/client/DoclingClientBuilderFactoryTests.java +++ b/client/src/test/java/ai/docling/client/DoclingClientBuilderFactoryTests.java @@ -1,7 +1,10 @@ package ai.docling.client; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import java.util.HashSet; +import java.util.Set; import java.util.stream.Stream; import org.junit.jupiter.api.Test; @@ -19,10 +22,19 @@ void correctBuilderWhenBothArePresent() { .isExactlyInstanceOf(DoclingJackson3Client.Builder.class); } + @Test + void noBuilderWhenNeitherArePresent() { + var classLoader = new ClassHidingClassLoader("com.fasterxml.jackson.databind.json.JsonMapper", "tools.jackson.databind.json.JsonMapper"); + + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(() -> DoclingClientBuilderFactory.newBuilder(classLoader)) + .withMessage("Neither Jackson 2 or 3 is on the classpath"); + } + @ParameterizedTest @MethodSource("correctBuilderArguments") void correctBuilder(String classToHide, Class expectedBuilderClass) { - var classLoader = new DelegatingClassLoader(classToHide, Thread.currentThread().getContextClassLoader()); + var classLoader = new ClassHidingClassLoader(classToHide); assertThat(DoclingClientBuilderFactory.newBuilder(classLoader)) .isNotNull() @@ -36,21 +48,25 @@ static Stream correctBuilderArguments() { ); } - private static class DelegatingClassLoader extends ClassLoader { - private final String classToHide; - private final ClassLoader parent; + private static class ClassHidingClassLoader extends ClassLoader { + private final Set classedToHide = new HashSet<>(); - public DelegatingClassLoader(String classToHide, ClassLoader parent) { + public ClassHidingClassLoader(String... classesToHide) { + this(Thread.currentThread().getContextClassLoader(), classesToHide); + } + + public ClassHidingClassLoader(ClassLoader parent, String... classesToHide) { super(parent); - this.classToHide = classToHide; - this.parent = parent; + if (classesToHide != null) { + this.classedToHide.addAll(Set.of(classesToHide)); + } } @Override public Class loadClass(String name) throws ClassNotFoundException { - if (classToHide.equals(name)) { - throw new ClassNotFoundException("Class %s not found".formatted(classToHide)); + if (this.classedToHide.contains(name)) { + throw new ClassNotFoundException("Class %s not found".formatted(name)); } return super.loadClass(name); From 1ab7efdb143a6bbc27019a8696754f58ffbe6b9b Mon Sep 17 00:00:00 2001 From: Eric Deandrea Date: Wed, 29 Oct 2025 08:32:01 -0400 Subject: [PATCH 07/13] feat: idea for api design Support both Jackson 2 & Jackson 3 Signed-off-by: Eric Deandrea --- .../ai/docling/api/convert/response/DocumentResponse.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java b/api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java index 3edc06c..48a920b 100644 --- a/api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java +++ b/api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java @@ -9,11 +9,8 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -import tools.jackson.databind.annotation.JsonDeserialize; -import tools.jackson.databind.annotation.JsonPOJOBuilder; - @JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonDeserialize(builder = DocumentResponse.Builder.class) +@tools.jackson.databind.annotation.JsonDeserialize(builder = DocumentResponse.Builder.class) @com.fasterxml.jackson.databind.annotation.JsonDeserialize(builder = DocumentResponse.Builder.class) public interface DocumentResponse { /** @@ -133,7 +130,7 @@ public DefaultDocumentResponse(Builder builder) { * This class is intended for internal use and is protected to restrict its * accessibility outside the defining package or class hierarchy. */ - @JsonPOJOBuilder(withPrefix = "") + @tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "") @com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "") class Builder { protected String doctagsContent; From 446880441816ed78aa69de6ecdac549b3e123ee0 Mon Sep 17 00:00:00 2001 From: Eric Deandrea Date: Wed, 29 Oct 2025 13:36:21 -0400 Subject: [PATCH 08/13] feat: idea for api design Convert all to POJOs Signed-off-by: Eric Deandrea --- api/build.gradle.kts | 6 +- .../request/ConvertDocumentRequest.java | 246 ++-- .../options/ConvertDocumentOptions.java | 1259 ++++++++++++----- .../options/PictureDescriptionApi.java | 284 ++-- .../options/PictureDescriptionLocal.java | 162 ++- .../convert/request/options/VlmModelApi.java | 305 +++- .../request/options/VlmModelLocal.java | 304 ++-- .../convert/request/source/FileSource.java | 117 +- .../convert/request/source/HttpSource.java | 149 +- .../api/convert/request/source/Source.java | 46 +- .../convert/request/target/InBodyTarget.java | 33 +- .../api/convert/request/target/PutTarget.java | 85 +- .../api/convert/request/target/Target.java | 49 +- .../api/convert/request/target/ZipTarget.java | 40 +- .../response/ConvertDocumentResponse.java | 180 ++- .../convert/response/DocumentResponse.java | 395 +++--- .../api/convert/response/ErrorItem.java | 123 +- .../ai/docling/api/core/package-info.java | 7 - .../api/health/HealthCheckResponse.java | 47 +- .../request/ConvertDocumentRequestTests.java | 112 +- .../options/ConvertDocumentOptionsTests.java | 34 +- .../options/PictureDescriptionApiTests.java | 80 +- .../options/PictureDescriptionLocalTests.java | 82 +- .../request/source/FileSourceTests.java | 58 +- .../request/source/HttpSourceTests.java | 72 +- .../request/target/InBodyTargetTests.java | 10 +- .../request/target/PutTargetTests.java | 14 +- .../request/target/ZipTargetTests.java | 8 +- .../ConvertDocumentResponseTests.java | 124 +- .../response/DocumentResponseTests.java | 91 +- .../api/convert/response/ErrorItemTests.java | 49 +- .../api/health/HealthCheckResponseTests.java | 12 +- .../kotlin/docling-java-shared.gradle.kts | 6 + client/build.gradle.kts | 6 +- .../client/AbstractDoclingClientTests.java | 67 +- 35 files changed, 3133 insertions(+), 1529 deletions(-) delete mode 100644 api/src/main/java/ai/docling/api/core/package-info.java diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 03416e7..7bd6bd8 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -3,9 +3,7 @@ plugins { } dependencies { - api(platform(libs.jackson.bom)) - api(libs.jackson.annotations) - implementation(libs.jackson.databind) - implementation(libs.jackson2.databind) + implementation(platform(libs.jackson.bom)) + implementation(libs.jackson.annotations) testImplementation(project(":docling-testcontainers")) } diff --git a/api/src/main/java/ai/docling/api/convert/request/ConvertDocumentRequest.java b/api/src/main/java/ai/docling/api/convert/request/ConvertDocumentRequest.java index af1b64a..1754a97 100644 --- a/api/src/main/java/ai/docling/api/convert/request/ConvertDocumentRequest.java +++ b/api/src/main/java/ai/docling/api/convert/request/ConvertDocumentRequest.java @@ -1,7 +1,9 @@ package ai.docling.api.convert.request; -import java.net.URI; +import static ai.docling.api.util.ValidationUtils.ensureNotNull; + import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.jspecify.annotations.Nullable; @@ -15,113 +17,171 @@ import ai.docling.api.convert.request.source.Source; import ai.docling.api.convert.request.target.Target; -@JsonInclude(JsonInclude.Include.NON_NULL) -public record ConvertDocumentRequest( - - @JsonProperty("sources") List sources, - - @JsonProperty("options") ConvertDocumentOptions options, - - @JsonProperty("target") @Nullable Target target - -) { - - public ConvertDocumentRequest { - if (sources == null || sources.isEmpty()) { - throw new IllegalArgumentException("sources cannot be null or empty"); - } - if (options == null) { - throw new IllegalArgumentException("options cannot be null"); +/** + * Represents a request to convert a document. The request includes the source(s) of the document, + * options for the conversion process, and the target specification for the converted output. + * + *

This class is serialized into JSON to conform to the API specification using + * {@link JsonProperty} annotations. Fields with {@code null} values or empty collections + * are omitted from the serialized JSON using {@link JsonInclude}. + */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class ConvertDocumentRequest { + @JsonProperty("sources") + private List sources = new ArrayList<>(); + + @JsonProperty("options") + private ConvertDocumentOptions options; + + @JsonProperty("target") + private Target target; + + private static void checkSources(List sources) { + if ((sources == null) || sources.isEmpty()) { + throw new IllegalArgumentException("sources must not be null or empty"); } - if (! (sources.stream().allMatch(HttpSource.class::isInstance) || sources.stream().allMatch(FileSource.class::isInstance))) { + + if (!(sources.stream().allMatch(HttpSource.class::isInstance) || sources.stream().allMatch(FileSource.class::isInstance))) { throw new IllegalArgumentException("All sources must be of the same type (HttpSource or FileSource)"); } - sources = new ArrayList<>(sources); } - public static Builder builder() { - return new Builder(); + /** + * Retrieves an unmodifiable view of the list of document sources associated with this request. + * Modifications to the returned list will result in an {@link UnsupportedOperationException}. + * + * @return an unmodifiable {@link List} of {@link Source} objects representing the document sources. + */ + public List getSources() { + return Collections.unmodifiableList(sources); } - public static class Builder { - private List sources = new ArrayList<>(); - private ConvertDocumentOptions options = ConvertDocumentOptions.builder().build(); - @Nullable - Target target; - - private Builder() { - } - - public Builder httpSources(List httpSources) { - if (httpSources == null) { - throw new IllegalArgumentException("httpSources cannot be null"); - } - this.sources = new ArrayList<>(httpSources); - return this; - } - - public Builder addHttpSources(String... urls) { - if (urls == null) { - throw new IllegalArgumentException("urls cannot be null"); - } - for (String url : urls) { - if (url == null || url.isBlank()) { - throw new IllegalArgumentException("url cannot be null or empty"); - } - this.sources.add(HttpSource.from(url)); - } - return this; + /** + * Sets the list of sources for the document conversion request. The existing list of sources + * will be cleared before adding the provided sources. If the provided list is {@code null}, + * the sources list will remain cleared. + * + * @param sources a {@link List} of {@link Source} objects representing the document sources to set. + * Elements in the list must not be {@code null}, but the list itself may be + * {@code null}. + * @throws IllegalArgumentException if the resulting sources list is empty after processing. + */ + public void setSources(@Nullable List sources) { + checkSources(sources); + this.sources.clear(); + + if (sources != null) { + this.sources.addAll(sources); } + } - public Builder addHttpSources(URI... urls) { - if (urls == null) { - throw new IllegalArgumentException("urls cannot be null"); - } - for (URI url : urls) { - if (url == null) { - throw new IllegalArgumentException("url cannot be null"); - } - this.sources.add(HttpSource.from(url)); - } - return this; - } + /** + * Adds a list of document sources to this {@link ConvertDocumentRequest} and returns the updated instance. + * + *

This method modifies the list of document sources for the current request using the provided + * {@code sources} list. The list replaces any existing sources. If the provided list is {@code null}, + * the sources are cleared. + * + * @param sources a {@link List} of {@link Source} objects to set as the new document sources for the request. + * The list itself may be {@code null}, but individual elements within the list must not be {@code null}. + * @return the updated {@link ConvertDocumentRequest} instance. + */ + public ConvertDocumentRequest withSources(@Nullable List sources) { + setSources(sources); + return this; + } - public Builder fileSources(List fileSources) { - if (fileSources == null) { - throw new IllegalArgumentException("fileSources cannot be null"); - } - this.sources = new ArrayList<>(fileSources); - return this; - } + /** + * Retrieves the document conversion options associated with this request. + * + * @return the {@link ConvertDocumentOptions} object representing the conversion options, + * or {@code null} if no options have been set. + */ + @Nullable + public ConvertDocumentOptions getOptions() { + return options; + } - public Builder addFileSources(String filename, String base64String) { - if (filename == null || filename.isBlank()) { - throw new IllegalArgumentException("filename cannot be null or empty"); - } - if (base64String == null || base64String.isBlank()) { - throw new IllegalArgumentException("base64String cannot be null or empty"); - } - this.sources.add(FileSource.from(filename, base64String)); - return this; - } + /** + * Sets the document conversion options for this request. + * + *

The provided {@link ConvertDocumentOptions} object defines the specific + * settings to be applied during the document conversion process. If {@code options} + * is {@code null}, the current options will be cleared. + * + * @param options a {@link ConvertDocumentOptions} object representing the conversion + * settings to apply, or {@code null} to clear the options. + */ + public void setOptions(ConvertDocumentOptions options) { + this.options = ensureNotNull(options, "options"); + } - public Builder options(ConvertDocumentOptions options) { - if (options == null) { - throw new IllegalArgumentException("options cannot be null"); - } - this.options = options; - return this; - } + /** + * Updates the document conversion options for this request and returns the updated instance. + * + *

This method modifies the {@link ConvertDocumentOptions} associated with the current + * {@link ConvertDocumentRequest}. If the provided {@code options} is {@code null}, the options + * will be cleared. + * + * @param options a {@link ConvertDocumentOptions} object specifying the settings to apply + * for the document conversion, or {@code null} to clear the options. + * @return the updated {@link ConvertDocumentRequest} instance. + */ + public ConvertDocumentRequest withOptions(ConvertDocumentOptions options) { + setOptions(options); + return this; + } - public Builder target(@Nullable Target target) { - this.target = target; - return this; - } + /** + * Retrieves the {@link Target} associated with this {@link ConvertDocumentRequest}. + * + *

The {@link Target} defines where and how the converted document should be delivered. + * For example, it may specify delivery in the response body, uploading to a URI, or + * zipping the document for inclusion in the response. + * + * @return the {@link Target} instance representing the delivery method for the converted + * document, or {@code null} if no target has been set. + */ + @Nullable + public Target getTarget() { + return target; + } - public ConvertDocumentRequest build() { - return new ConvertDocumentRequest(sources, options, target); - } + /** + * Sets the {@link Target} for this {@link ConvertDocumentRequest}. + * + *

The {@link Target} defines how and where the converted document should be delivered. It may + * represent delivery in the response body, uploading to a specified URI, or zipping for inclusion + * in the response. + * + * @param target the {@link Target} instance representing the delivery method and destination + * for the converted document, or {@code null} to unset the existing target. + */ + public void setTarget(@Nullable Target target) { + this.target = target; + } + /** + * Updates the {@link Target} for this {@link ConvertDocumentRequest} and returns the updated instance. + * + *

The {@link Target} specifies how and where the converted document will be delivered, such as + * including it in the response body, uploading it to a specific URI, or zipping it. + * + * @param target the {@link Target} instance indicating the delivery method and destination for the + * converted document, or {@code null} to clear the existing target. + * @return the updated {@link ConvertDocumentRequest} instance for method chaining. + */ + public ConvertDocumentRequest withTarget(@Nullable Target target) { + setTarget(target); + return this; } + @Override + public String toString() { + return "ConvertDocumentRequest{" + + "sources=" + (sources == null ? "null" : sources.toString()) + + ", options=" + (options == null ? "null" : options.toString()) + + ", target=" + (target == null ? "null" : target.toString()) + + '}'; + } } diff --git a/api/src/main/java/ai/docling/api/convert/request/options/ConvertDocumentOptions.java b/api/src/main/java/ai/docling/api/convert/request/options/ConvertDocumentOptions.java index 6d6fd1d..88e883c 100644 --- a/api/src/main/java/ai/docling/api/convert/request/options/ConvertDocumentOptions.java +++ b/api/src/main/java/ai/docling/api/convert/request/options/ConvertDocumentOptions.java @@ -1,7 +1,9 @@ package ai.docling.api.convert.request.options; + import java.time.Duration; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.jspecify.annotations.Nullable; @@ -12,397 +14,1010 @@ /** * Options for configuring the document conversion process with Docling. */ -@JsonInclude(JsonInclude.Include.NON_NULL) -public record ConvertDocumentOptions( +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class ConvertDocumentOptions { + @JsonProperty("from_formats") + private List fromFormats = new ArrayList<>(); - @JsonProperty("from_formats") @Nullable List fromFormats, + @JsonProperty("to_formats") + private List toFormats = new ArrayList<>(); - @JsonProperty("to_formats") @Nullable List toFormats, + @JsonProperty("image_export_mode") + private ImageRefMode imageExportMode; - @JsonProperty("image_export_mode") @Nullable ImageRefMode imageExportMode, + @JsonProperty("do_ocr") + private Boolean doOcr; - @JsonProperty("do_ocr") @Nullable Boolean doOcr, + @JsonProperty("force_ocr") + private Boolean forceOcr; - @JsonProperty("force_ocr") @Nullable Boolean forceOcr, + @JsonProperty("ocr_engine") + private OcrEngine ocrEngine; - @JsonProperty("ocr_engine") @Nullable OcrEngine ocrEngine, + @JsonProperty("ocr_lang") + private List ocrLang = new ArrayList<>(); - @JsonProperty("ocr_lang") @Nullable List ocrLang, + @JsonProperty("pdf_backend") + private PdfBackend pdfBackend; - @JsonProperty("pdf_backend") @Nullable PdfBackend pdfBackend, + @JsonProperty("table_mode") + private TableFormerMode tableMode; - @JsonProperty("table_mode") @Nullable TableFormerMode tableMode, + @JsonProperty("table_cell_matching") + private Boolean tableCellMatching; - @JsonProperty("table_cell_matching") @Nullable Boolean tableCellMatching, + @JsonProperty("pipeline") + private ProcessingPipeline pipeline; - @JsonProperty("pipeline") @Nullable ProcessingPipeline pipeline, + @JsonProperty("page_range") + private List pageRange = new ArrayList<>(); - @JsonProperty("page_range") @Nullable Integer @Nullable [] pageRange, + @JsonProperty("document_timeout") + private Duration documentTimeout; - @JsonProperty("document_timeout") @Nullable Duration documentTimeout, + @JsonProperty("abort_on_error") + private Boolean abortOnError; - @JsonProperty("abort_on_error") @Nullable Boolean abortOnError, + @JsonProperty("do_table_structure") + private Boolean doTableStructure; - @JsonProperty("do_table_structure") @Nullable Boolean doTableStructure, + @JsonProperty("include_images") + private Boolean includeImages; - @JsonProperty("include_images") @Nullable Boolean includeImages, + @JsonProperty("images_scale") + private Double imagesScale; - @JsonProperty("images_scale") @Nullable Double imagesScale, + @JsonProperty("md_page_break_placeholder") + private String mdPageBreakPlaceholder; - @JsonProperty("md_page_break_placeholder") @Nullable String mdPageBreakPlaceholder, + @JsonProperty("do_code_enrichment") + private Boolean doCodeEnrichment; - @JsonProperty("do_code_enrichment") @Nullable Boolean doCodeEnrichment, + @JsonProperty("do_formula_enrichment") + private Boolean doFormulaEnrichment; - @JsonProperty("do_formula_enrichment") @Nullable Boolean doFormulaEnrichment, + @JsonProperty("do_picture_classification") + private Boolean doPictureClassification; - @JsonProperty("do_picture_classification") @Nullable Boolean doPictureClassification, + @JsonProperty("do_picture_description") + private Boolean doPictureDescription; - @JsonProperty("do_picture_description") @Nullable Boolean doPictureDescription, + @JsonProperty("picture_description_area_threshold") + private Double pictureDescriptionAreaThreshold; - @JsonProperty("picture_description_area_threshold") @Nullable Double pictureDescriptionAreaThreshold, + @JsonProperty("picture_description_local") + private PictureDescriptionLocal pictureDescriptionLocal; - @JsonProperty("picture_description_local") @Nullable PictureDescriptionLocal pictureDescriptionLocal, + @JsonProperty("picture_description_api") + private PictureDescriptionApi pictureDescriptionApi; - @JsonProperty("picture_description_api") @Nullable PictureDescriptionApi pictureDescriptionApi, + @JsonProperty("vlm_pipeline_model") + private VlmModelType vlmPipelineModel; - @JsonProperty("vlm_pipeline_model") @Nullable VlmModelType vlmPipelineModel, + @JsonProperty("vlm_pipeline_model_local") + private String vlmPipelineModelLocal; - @JsonProperty("vlm_pipeline_model_local") @Nullable String vlmPipelineModelLocal, + @JsonProperty("vlm_pipeline_model_api") + private String vlmPipelineModelApi; - @JsonProperty("vlm_pipeline_model_api") @Nullable String vlmPipelineModelApi + /** + * Gets whether to abort document conversion on error. + * + * @return true if conversion should abort on error, false otherwise, or null if not set + */ + @Nullable + public Boolean getAbortOnError() { + return abortOnError; + } -) { + /** + * Sets whether to abort document conversion on error. + * + * @param abortOnError true to abort on error, false to continue, or null to use default + */ + public void setAbortOnError(@Nullable Boolean abortOnError) { + this.abortOnError = abortOnError; + } - public ConvertDocumentOptions { - if (pictureDescriptionLocal != null && pictureDescriptionApi != null) { - throw new IllegalArgumentException("picture_description_local and picture_description_api cannot both be set"); - } + /** + * Gets whether code enrichment is enabled during conversion. + * + * @return true if code enrichment is enabled, false otherwise, or null if not set + */ + @Nullable + public Boolean getDoCodeEnrichment() { + return doCodeEnrichment; } - private ConvertDocumentOptions(Builder builder) { - this(builder.fromFormats, builder.toFormats, builder.imageExportMode, builder.doOcr, builder.forceOcr, builder.ocrEngine, builder.ocrLang, builder.pdfBackend, builder.tableMode, builder.tableCellMatching, builder.pipeline, builder.pageRange, builder.documentTimeout, builder.abortOnError, builder.doTableStructure, builder.includeImages, builder.imagesScale, builder.mdPageBreakPlaceholder, builder.doCodeEnrichment, builder.doFormulaEnrichment, builder.doPictureClassification, builder.doPictureDescription, builder.pictureDescriptionAreaThreshold, builder.pictureDescriptionLocal, builder.pictureDescriptionApi, builder.vlmPipelineModel, builder.vlmPipelineModelLocal, builder.vlmPipelineModelApi); - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - @Nullable - private List fromFormats; - @Nullable - private List toFormats; - @Nullable - private ImageRefMode imageExportMode; - @Nullable - private Boolean doOcr; - @Nullable - private Boolean forceOcr; - @Nullable - private OcrEngine ocrEngine; - @Nullable - private List ocrLang; - @Nullable - private PdfBackend pdfBackend; - @Nullable - private TableFormerMode tableMode; - @Nullable - private Boolean tableCellMatching; - @Nullable - private ProcessingPipeline pipeline; - @Nullable - private Integer @Nullable [] pageRange; - @Nullable - private Duration documentTimeout; - @Nullable - private Boolean abortOnError; - @Nullable - private Boolean doTableStructure; - @Nullable - private Boolean includeImages; - @Nullable - private Double imagesScale; - @Nullable - private String mdPageBreakPlaceholder; - @Nullable - private Boolean doCodeEnrichment; - @Nullable - private Boolean doFormulaEnrichment; - @Nullable - private Boolean doPictureClassification; - @Nullable - private Boolean doPictureDescription; - @Nullable - private Double pictureDescriptionAreaThreshold; - @Nullable - private PictureDescriptionLocal pictureDescriptionLocal; - @Nullable - private PictureDescriptionApi pictureDescriptionApi; - @Nullable - private VlmModelType vlmPipelineModel; - @Nullable - private String vlmPipelineModelLocal; - @Nullable - private String vlmPipelineModelApi; - - private Builder() { - } + /** + * Sets whether to enable code enrichment during conversion. + * + * @param doCodeEnrichment true to enable code enrichment, false to disable, or null to use default + */ + public void setDoCodeEnrichment(@Nullable Boolean doCodeEnrichment) { + this.doCodeEnrichment = doCodeEnrichment; + } - /** - * Input format(s) to convert from. - */ - public Builder fromFormats(@Nullable List fromFormats) { - if (fromFormats == null) { - this.fromFormats = null; - } else { - this.fromFormats = new ArrayList<>(fromFormats); - } - return this; - } + /** + * Gets the timeout duration for document conversion. + * + * @return the timeout duration, or null if not set + */ + @Nullable + public Duration getDocumentTimeout() { + return documentTimeout; + } - /** - * Output format(s) to convert to. - */ - public Builder toFormats(@Nullable List toFormats) { - if (toFormats == null) { - this.toFormats = null; - } else { - this.toFormats = new ArrayList<>(toFormats); - } - return this; - } + /** + * Sets the timeout duration for document conversion. + * + * @param documentTimeout the timeout duration, or null to use default + */ + public void setDocumentTimeout(@Nullable Duration documentTimeout) { + this.documentTimeout = documentTimeout; + } - /** - * Image export mode for the document (in case of JSON, Markdown or HTML). - */ - public Builder imageExportMode(@Nullable ImageRefMode imageExportMode) { - this.imageExportMode = imageExportMode; - return this; - } + /** + * Gets whether formula enrichment is enabled during conversion. + * + * @return true if formula enrichment is enabled, false otherwise, or null if not set + */ + @Nullable + public Boolean getDoFormulaEnrichment() { + return doFormulaEnrichment; + } - /** - * If enabled, the bitmap content will be processed using OCR. - */ - public Builder doOcr(@Nullable Boolean doOcr) { - this.doOcr = doOcr; - return this; - } + /** + * Sets whether to enable formula enrichment during conversion. + * + * @param doFormulaEnrichment true to enable formula enrichment, false to disable, or null to use default + */ + public void setDoFormulaEnrichment(@Nullable Boolean doFormulaEnrichment) { + this.doFormulaEnrichment = doFormulaEnrichment; + } - /** - * If enabled, replace existing text with OCR-generated text over content. - */ - public Builder forceOcr(@Nullable Boolean forceOcr) { - this.forceOcr = forceOcr; - return this; - } + /** + * Gets whether OCR (Optical Character Recognition) is enabled. + * + * @return true if OCR is enabled, false otherwise, or null if not set + */ + @Nullable + public Boolean getDoOcr() { + return doOcr; + } - /** - * The OCR engine to use. - */ - public Builder ocrEngine(@Nullable OcrEngine ocrEngine) { - this.ocrEngine = ocrEngine; - return this; - } + /** + * Sets whether to enable OCR (Optical Character Recognition). + * + * @param doOcr true to enable OCR, false to disable, or null to use default + */ + public void setDoOcr(@Nullable Boolean doOcr) { + this.doOcr = doOcr; + } - /** - * List of languages used by the OCR engine. Note that each OCR engine has different values for the language names. - */ - public Builder ocrLang(@Nullable List ocrLang) { - if (ocrLang == null) { - this.ocrLang = null; - } else { - this.ocrLang = new ArrayList<>(ocrLang); - } - return this; - } + /** + * Gets whether picture classification is enabled during conversion. + * + * @return true if picture classification is enabled, false otherwise, or null if not set + */ + @Nullable + public Boolean getDoPictureClassification() { + return doPictureClassification; + } - /** - * The PDF backend to use. - */ - public Builder pdfBackend(@Nullable PdfBackend pdfBackend) { - this.pdfBackend = pdfBackend; - return this; - } + /** + * Sets whether to enable picture classification during conversion. + * + * @param doPictureClassification true to enable picture classification, false to disable, or null to use default + */ + public void setDoPictureClassification(@Nullable Boolean doPictureClassification) { + this.doPictureClassification = doPictureClassification; + } - /** - * Mode to use for table structure. - */ - public Builder tableMode(@Nullable TableFormerMode tableMode) { - this.tableMode = tableMode; - return this; - } + /** + * Gets whether picture description is enabled during conversion. + * + * @return true if picture description is enabled, false otherwise, or null if not set + */ + @Nullable + public Boolean getDoPictureDescription() { + return doPictureDescription; + } - /** - * If true, matches table cells predictions back to PDF cells. Can break table output if PDF cells are merged across table columns. - * If false, let table structure model define the text cells, ignore PDF cells. - */ - public Builder tableCellMatching(@Nullable Boolean tableCellMatching) { - this.tableCellMatching = tableCellMatching; - return this; - } + /** + * Sets whether to enable picture description during conversion. + * + * @param doPictureDescription true to enable picture description, false to disable, or null to use default + */ + public void setDoPictureDescription(@Nullable Boolean doPictureDescription) { + this.doPictureDescription = doPictureDescription; + } - /** - * Choose the pipeline to process PDF or image files. - */ - public Builder pipeline(@Nullable ProcessingPipeline pipeline) { - this.pipeline = pipeline; - return this; - } + /** + * Gets whether table structure analysis is enabled during conversion. + * + * @return true if table structure analysis is enabled, false otherwise, or null if not set + */ + @Nullable + public Boolean getDoTableStructure() { + return doTableStructure; + } - /** - * Only convert a range of pages. The page number starts at 1. - */ - public Builder pageRange(@Nullable Integer fromPage, @Nullable Integer toPage) { - if ((fromPage == null && toPage != null) || (fromPage != null && toPage == null)) { - throw new IllegalArgumentException("fromPage and toPage must both be null or both not null"); - } - if (fromPage != null) { - this.pageRange = new Integer[] { fromPage, toPage }; - } - else { - this.pageRange = null; - } - return this; - } + /** + * Sets whether to enable table structure analysis during conversion. + * + * @param doTableStructure true to enable table structure analysis, false to disable, or null to use default + */ + public void setDoTableStructure(@Nullable Boolean doTableStructure) { + this.doTableStructure = doTableStructure; + } - /** - * The timeout for processing each document. - */ - public Builder documentTimeout(@Nullable Duration documentTimeout) { - this.documentTimeout = documentTimeout; - return this; - } + /** + * Gets whether OCR should be forced even if text is already available. + * + * @return true if OCR is forced, false otherwise, or null if not set + */ + @Nullable + public Boolean getForceOcr() { + return forceOcr; + } - /** - * Abort on error if enabled. - */ - public Builder abortOnError(@Nullable Boolean abortOnError) { - this.abortOnError = abortOnError; - return this; - } + /** + * Sets whether to force OCR even if text is already available. + * + * @param forceOcr true to force OCR, false otherwise, or null to use default + */ + public void setForceOcr(@Nullable Boolean forceOcr) { + this.forceOcr = forceOcr; + } - /** - * If enabled, the table structure will be extracted. - */ - public Builder doTableStructure(@Nullable Boolean doTableStructure) { - this.doTableStructure = doTableStructure; - return this; - } + /** + * Gets the list of input document formats to process. + * + * @return an unmodifiable list of input formats + */ + public List getFromFormats() { + return Collections.unmodifiableList(fromFormats); + } - /** - * If enabled, images will be extracted from the document. - */ - public Builder includeImages(@Nullable Boolean includeImages) { - this.includeImages = includeImages; - return this; - } + /** + * Sets the list of input document formats to process. + * + * @param fromFormats the list of input formats, or null to clear + */ + public void setFromFormats(@Nullable List fromFormats) { + this.fromFormats.clear(); - /** - * Scale factor for images. - */ - public Builder imagesScale(@Nullable Double imagesScale) { - this.imagesScale = imagesScale; - return this; + if (fromFormats != null) { + this.fromFormats.addAll(fromFormats); } + } - /** - * Add this placeholder betweek pages in the markdown output. - */ - public Builder mdPageBreakPlaceholder(@Nullable String mdPageBreakPlaceholder) { - this.mdPageBreakPlaceholder = mdPageBreakPlaceholder; - return this; - } + /** + * Gets the image export mode for handling images during conversion. + * + * @return the image export mode, or null if not set + */ + @Nullable + public ImageRefMode getImageExportMode() { + return imageExportMode; + } - /** - * If enabled, perform OCR code enrichment. - */ - public Builder doCodeEnrichment(@Nullable Boolean doCodeEnrichment) { - this.doCodeEnrichment = doCodeEnrichment; - return this; - } + /** + * Sets the image export mode for handling images during conversion. + * + * @param imageExportMode the image export mode, or null to use default + */ + public void setImageExportMode(@Nullable ImageRefMode imageExportMode) { + this.imageExportMode = imageExportMode; + } - /** - * If enabled, perform formula OCR, return LaTeX code. - */ - public Builder doFormulaEnrichment(@Nullable Boolean doFormulaEnrichment) { - this.doFormulaEnrichment = doFormulaEnrichment; - return this; - } + /** + * Gets the scale factor for exported images. + * + * @return the image scale factor, or null if not set + */ + @Nullable + public Double getImagesScale() { + return imagesScale; + } - /** - * If enabled, classify pictures in documents. - */ - public Builder doPictureClassification(@Nullable Boolean doPictureClassification) { - this.doPictureClassification = doPictureClassification; - return this; - } + /** + * Sets the scale factor for exported images. + * + * @param imagesScale the image scale factor, or null to use default + */ + public void setImagesScale(@Nullable Double imagesScale) { + this.imagesScale = imagesScale; + } - /** - * If enabled, describe pictures in documents. - */ - public Builder doPictureDescription(@Nullable Boolean doPictureDescription) { - this.doPictureDescription = doPictureDescription; - return this; - } + /** + * Gets whether images should be included in the converted output. + * + * @return true if images should be included, false otherwise, or null if not set + */ + @Nullable + public Boolean getIncludeImages() { + return includeImages; + } - /** - * Minimum percentage of the area for a picture to be processed with the models. - */ - public Builder pictureDescriptionAreaThreshold(@Nullable Double pictureDescriptionAreaThreshold) { - this.pictureDescriptionAreaThreshold = pictureDescriptionAreaThreshold; - return this; - } + /** + * Sets whether images should be included in the converted output. + * + * @param includeImages true to include images, false to exclude, or null to use default + */ + public void setIncludeImages(@Nullable Boolean includeImages) { + this.includeImages = includeImages; + } - /** - * Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. - * This parameter is mutually exclusive with picture_description_api. - */ - public Builder pictureDescriptionLocal(@Nullable PictureDescriptionLocal pictureDescriptionLocal) { - this.pictureDescriptionLocal = pictureDescriptionLocal; - return this; + /** + * Gets the placeholder string for page breaks in Markdown output. + * + * @return the page break placeholder string, or null if not set + */ + @Nullable + public String getMdPageBreakPlaceholder() { + return mdPageBreakPlaceholder; + } + + /** + * Sets the placeholder string for page breaks in Markdown output. + * + * @param mdPageBreakPlaceholder the page break placeholder string, or null to use default + */ + public void setMdPageBreakPlaceholder(@Nullable String mdPageBreakPlaceholder) { + this.mdPageBreakPlaceholder = mdPageBreakPlaceholder; + } + + /** + * Gets the OCR engine to use for text recognition. + * + * @return the OCR engine, or null if not set + */ + @Nullable + public OcrEngine getOcrEngine() { + return ocrEngine; + } + + /** + * Sets the OCR engine to use for text recognition. + * + * @param ocrEngine the OCR engine, or null to use default + */ + public void setOcrEngine(@Nullable OcrEngine ocrEngine) { + this.ocrEngine = ocrEngine; + } + + /** + * Gets the list of language codes for OCR processing. + * + * @return an unmodifiable list of language codes + */ + public List getOcrLang() { + return Collections.unmodifiableList(ocrLang); + } + + /** + * Sets the list of language codes for OCR processing. + * + * @param ocrLang the list of language codes, or null to clear + */ + public void setOcrLang(@Nullable List ocrLang) { + this.ocrLang.clear(); + + if (ocrLang != null) { + this.ocrLang.addAll(ocrLang); } + } + + /** + * Gets the page range to process during conversion. + * + * @return an unmodifiable list containing the page range (from and to page numbers) + */ + public List getPageRange() { + return Collections.unmodifiableList(pageRange); + } + + /** + * Sets the page range to process during conversion. + * + * @param pageRange a list containing the page range (from and to page numbers), or null to clear + */ + public void setPageRange(@Nullable List pageRange) { + this.pageRange.clear(); - /** - * API details for using a vision-language model in the picture description. - * This parameter is mutually exclusive with picture_description_local. - */ - public Builder pictureDescriptionApi(@Nullable PictureDescriptionApi pictureDescriptionApi) { - this.pictureDescriptionApi = pictureDescriptionApi; - return this; + if (pageRange != null) { + this.pageRange.addAll(pageRange); } + } - /** - * Preset of local and API models for the vlm pipeline. - * This parameter is mutually exclusive with vlm_pipeline_model_local and vlm_pipeline_model_api. Use the other options for more parameters. - */ - public Builder vlmPipelineModel(@Nullable VlmModelType vlmPipelineModel) { - this.vlmPipelineModel = vlmPipelineModel; - return this; + /** + * Gets the PDF backend to use for processing PDF documents. + * + * @return the PDF backend, or null if not set + */ + @Nullable + public PdfBackend getPdfBackend() { + return pdfBackend; + } + + /** + * Sets the PDF backend to use for processing PDF documents. + * + * @param pdfBackend the PDF backend, or null to use default + */ + public void setPdfBackend(@Nullable PdfBackend pdfBackend) { + this.pdfBackend = pdfBackend; + } + + /** + * Gets the API-based picture description configuration. + * + * @return the picture description API configuration, or null if not set + */ + @Nullable + public PictureDescriptionApi getPictureDescriptionApi() { + return pictureDescriptionApi; + } + + private static void checkPictureDescription(PictureDescriptionApi pictureDescriptionApi, PictureDescriptionLocal pictureDescriptionLocal) { + if ((pictureDescriptionLocal != null) && (pictureDescriptionApi != null)) { + throw new IllegalArgumentException("picture_description_local and picture_description_api cannot both be set"); } + } - /** - * Options for running a local vision-language model for the vlm pipeline. The parameters refer to a model hosted on Hugging Face. - * This parameter is mutually exclusive with vlm_pipeline_model_api and vlm_pipeline_model. - */ - public Builder vlmPipelineModelLocal(@Nullable String vlmPipelineModelLocal) { - this.vlmPipelineModelLocal = vlmPipelineModelLocal; - return this; + /** + * Sets the API-based picture description configuration. + * + * @param pictureDescriptionApi the picture description API configuration, or null to disable + * @throws IllegalArgumentException if both API and local picture description are set + */ + public void setPictureDescriptionApi(@Nullable PictureDescriptionApi pictureDescriptionApi) { + checkPictureDescription(pictureDescriptionApi, pictureDescriptionLocal); + this.pictureDescriptionApi = pictureDescriptionApi; + } + + /** + * Gets the area threshold for picture description. + * + * @return the area threshold value, or null if not set + */ + @Nullable + public Double getPictureDescriptionAreaThreshold() { + return pictureDescriptionAreaThreshold; + } + + /** + * Sets the area threshold for picture description. + * + * @param pictureDescriptionAreaThreshold the area threshold value, or null to use default + */ + public void setPictureDescriptionAreaThreshold(@Nullable Double pictureDescriptionAreaThreshold) { + this.pictureDescriptionAreaThreshold = pictureDescriptionAreaThreshold; + } + + /** + * Gets the local picture description configuration. + * + * @return the picture description local configuration, or null if not set + */ + @Nullable + public PictureDescriptionLocal getPictureDescriptionLocal() { + return pictureDescriptionLocal; + } + + /** + * Sets the local picture description configuration. + * + * @param pictureDescriptionLocal the picture description local configuration, or null to disable + * @throws IllegalArgumentException if both API and local picture description are set + */ + public void setPictureDescriptionLocal(@Nullable PictureDescriptionLocal pictureDescriptionLocal) { + checkPictureDescription(pictureDescriptionApi, pictureDescriptionLocal); + this.pictureDescriptionLocal = pictureDescriptionLocal; + } + + /** + * Gets the processing pipeline to use for document conversion. + * + * @return the processing pipeline, or null if not set + */ + @Nullable + public ProcessingPipeline getPipeline() { + return pipeline; + } + + /** + * Sets the processing pipeline to use for document conversion. + * + * @param pipeline the processing pipeline, or null to use default + */ + public void setPipeline(@Nullable ProcessingPipeline pipeline) { + this.pipeline = pipeline; + } + + /** + * Gets whether table cell matching is enabled. + * + * @return true if table cell matching is enabled, false otherwise, or null if not set + */ + @Nullable + public Boolean getTableCellMatching() { + return tableCellMatching; + } + + /** + * Sets whether to enable table cell matching. + * + * @param tableCellMatching true to enable table cell matching, false to disable, or null to use default + */ + public void setTableCellMatching(@Nullable Boolean tableCellMatching) { + this.tableCellMatching = tableCellMatching; + } + + /** + * Gets the table processing mode. + * + * @return the table mode, or null if not set + */ + @Nullable + public TableFormerMode getTableMode() { + return tableMode; + } + + /** + * Sets the table processing mode. + * + * @param tableMode the table mode, or null to use default + */ + public void setTableMode(@Nullable TableFormerMode tableMode) { + this.tableMode = tableMode; + } + + /** + * Gets the list of output formats for the converted document. + * + * @return the list of output formats, or null if not set + */ + public List getToFormats() { + return Collections.unmodifiableList(toFormats); + } + + /** + * Sets the list of output formats for the converted document. + * + * @param toFormats the list of output formats, or null to use default + */ + public void setToFormats(@Nullable List toFormats) { + this.toFormats.clear(); + + if (toFormats != null) { + this.toFormats.addAll(toFormats); } + } + + /** + * Gets the Vision-Language Model (VLM) pipeline model type. + * + * @return the VLM model type, or null if not set + */ + @Nullable + public VlmModelType getVlmPipelineModel() { + return vlmPipelineModel; + } + + /** + * Sets the Vision-Language Model (VLM) pipeline model type. + * + * @param vlmPipelineModel the VLM model type, or null to use default + */ + public void setVlmPipelineModel(@Nullable VlmModelType vlmPipelineModel) { + this.vlmPipelineModel = vlmPipelineModel; + } + + /** + * Gets the API-based VLM pipeline model identifier. + * + * @return the API model identifier, or null if not set + */ + @Nullable + public String getVlmPipelineModelApi() { + return vlmPipelineModelApi; + } + + /** + * Sets the API-based VLM pipeline model identifier. + * + * @param vlmPipelineModelApi the API model identifier, or null to use default + */ + public void setVlmPipelineModelApi(@Nullable String vlmPipelineModelApi) { + this.vlmPipelineModelApi = vlmPipelineModelApi; + } + + /** + * Gets the local VLM pipeline model identifier. + * + * @return the local model identifier, or null if not set + */ + @Nullable + public String getVlmPipelineModelLocal() { + return vlmPipelineModelLocal; + } + + /** + * Sets the local VLM pipeline model identifier. + * + * @param vlmPipelineModelLocal the local model identifier, or null to use default + */ + public void setVlmPipelineModelLocal(@Nullable String vlmPipelineModelLocal) { + this.vlmPipelineModelLocal = vlmPipelineModelLocal; + } + + /** + * Sets whether to abort document conversion on error and returns this instance for method chaining. + * + * @param abortOnError true to abort on error, false to continue, or null to use default + * @return this options instance + */ + public ConvertDocumentOptions withAbortOnError(@Nullable Boolean abortOnError) { + setAbortOnError(abortOnError); + return this; + } + + /** + * Sets whether to enable code enrichment and returns this instance for method chaining. + * + * @param doCodeEnrichment true to enable code enrichment, false to disable, or null to use default + * @return this options instance + */ + public ConvertDocumentOptions withDoCodeEnrichment(@Nullable Boolean doCodeEnrichment) { + setDoCodeEnrichment(doCodeEnrichment); + return this; + } + + /** + * Sets the timeout duration for document conversion and returns this instance for method chaining. + * + * @param documentTimeout the timeout duration, or null to use default + * @return this options instance + */ + public ConvertDocumentOptions withDocumentTimeout(@Nullable Duration documentTimeout) { + setDocumentTimeout(documentTimeout); + return this; + } + + /** + * Sets whether to enable formula enrichment and returns this instance for method chaining. + * + * @param doFormulaEnrichment true to enable formula enrichment, false to disable, or null to use default + * @return this options instance + */ + public ConvertDocumentOptions withDoFormulaEnrichment(@Nullable Boolean doFormulaEnrichment) { + setDoFormulaEnrichment(doFormulaEnrichment); + return this; + } + + /** + * Sets whether to enable OCR and returns this instance for method chaining. + * + * @param doOcr true to enable OCR, false to disable, or null to use default + * @return this options instance + */ + public ConvertDocumentOptions withDoOcr(@Nullable Boolean doOcr) { + setDoOcr(doOcr); + return this; + } + + /** + * Sets whether to enable picture classification and returns this instance for method chaining. + * + * @param doPictureClassification true to enable picture classification, false to disable, or null to use default + * @return this options instance + */ + public ConvertDocumentOptions withDoPictureClassification(@Nullable Boolean doPictureClassification) { + setDoPictureClassification(doPictureClassification); + return this; + } + + /** + * Sets whether to enable picture description and returns this instance for method chaining. + * + * @param doPictureDescription true to enable picture description, false to disable, or null to use default + * @return this options instance + */ + public ConvertDocumentOptions withDoPictureDescription(@Nullable Boolean doPictureDescription) { + setDoPictureDescription(doPictureDescription); + return this; + } + + /** + * Sets whether to enable table structure analysis and returns this instance for method chaining. + * + * @param doTableStructure true to enable table structure analysis, false to disable, or null to use default + * @return this options instance + */ + public ConvertDocumentOptions withDoTableStructure(@Nullable Boolean doTableStructure) { + setDoTableStructure(doTableStructure); + return this; + } + + /** + * Sets whether to force OCR and returns this instance for method chaining. + * + * @param forceOcr true to force OCR, false otherwise, or null to use default + * @return this options instance + */ + public ConvertDocumentOptions withForceOcr(@Nullable Boolean forceOcr) { + setForceOcr(forceOcr); + return this; + } + + /** + * Sets the list of input formats and returns this instance for method chaining. + * + * @param fromFormats the list of input formats, or null to clear + * @return this options instance + */ + public ConvertDocumentOptions withFromFormats(@Nullable List fromFormats) { + setFromFormats(fromFormats); + return this; + } + + /** + * Sets the image export mode and returns this instance for method chaining. + * + * @param imageExportMode the image export mode, or null to use default + * @return this options instance + */ + public ConvertDocumentOptions withImageExportMode(@Nullable ImageRefMode imageExportMode) { + setImageExportMode(imageExportMode); + return this; + } + + /** + * Sets the image scale factor and returns this instance for method chaining. + * + * @param imagesScale the image scale factor, or null to use default + * @return this options instance + */ + public ConvertDocumentOptions withImagesScale(@Nullable Double imagesScale) { + setImagesScale(imagesScale); + return this; + } + + /** + * Sets whether to include images and returns this instance for method chaining. + * + * @param includeImages true to include images, false to exclude, or null to use default + * @return this options instance + */ + public ConvertDocumentOptions withIncludeImages(@Nullable Boolean includeImages) { + setIncludeImages(includeImages); + return this; + } - /** - * API details for using a vision-language model for the vlm pipeline. - * This parameter is mutually exclusive with vlm_pipeline_model_local and vlm_pipeline_model. - */ - public Builder vlmPipelineModelApi(@Nullable String vlmPipelineModelApi) { - this.vlmPipelineModelApi = vlmPipelineModelApi; - return this; + /** + * Sets the Markdown page break placeholder and returns this instance for method chaining. + * + * @param mdPageBreakPlaceholder the page break placeholder string, or null to use default + * @return this options instance + */ + public ConvertDocumentOptions withMdPageBreakPlaceholder(@Nullable String mdPageBreakPlaceholder) { + setMdPageBreakPlaceholder(mdPageBreakPlaceholder); + return this; + } + + /** + * Sets the OCR engine and returns this instance for method chaining. + * + * @param ocrEngine the OCR engine, or null to use default + * @return this options instance + */ + public ConvertDocumentOptions withOcrEngine(@Nullable OcrEngine ocrEngine) { + setOcrEngine(ocrEngine); + return this; + } + + /** + * Sets the OCR language codes and returns this instance for method chaining. + * + * @param ocrLang the list of language codes, or null to clear + * @return this options instance + */ + public ConvertDocumentOptions withOcrLang(@Nullable List ocrLang) { + setOcrLang(ocrLang); + return this; + } + + /** + * Sets the page range using from and to page numbers and returns this instance for method chaining. + * + * @param fromPage the starting page number, or null to clear the range + * @param toPage the ending page number, or null to clear the range + * @return this options instance + * @throws IllegalArgumentException if only one of fromPage or toPage is null + */ + public ConvertDocumentOptions withPageRange(@Nullable Integer fromPage, @Nullable Integer toPage) { + if (((fromPage == null) && (toPage != null)) || ((fromPage != null) && (toPage == null))) { + throw new IllegalArgumentException("fromPage and toPage must both be null or both not null"); } - public ConvertDocumentOptions build() { - return new ConvertDocumentOptions(this); + if (fromPage != null) { + setPageRange(List.of(fromPage, toPage)); + } + else { + setPageRange(List.of()); } + + return this; + } + + /** + * Sets the page range and returns this instance for method chaining. + * + * @param pageRange a list containing the page range (from and to page numbers), or null to clear + * @return this options instance + */ + public ConvertDocumentOptions withPageRange(@Nullable List pageRange) { + setPageRange(pageRange); + return this; + } + /** + * Sets the PDF backend and returns this instance for method chaining. + * + * @param pdfBackend the PDF backend, or null to use default + * @return this options instance + */ + public ConvertDocumentOptions withPdfBackend(@Nullable PdfBackend pdfBackend) { + setPdfBackend(pdfBackend); + return this; } + /** + * Sets the API-based picture description configuration and returns this instance for method chaining. + * + * @param pictureDescriptionApi the picture description API configuration, or null to disable + * @return this options instance + * @throws IllegalArgumentException if both API and local picture description are set + */ + public ConvertDocumentOptions withPictureDescriptionApi(@Nullable PictureDescriptionApi pictureDescriptionApi) { + setPictureDescriptionApi(pictureDescriptionApi); + return this; + } + + /** + * Sets the picture description area threshold and returns this instance for method chaining. + * + * @param pictureDescriptionAreaThreshold the area threshold value, or null to use default + * @return this options instance + */ + public ConvertDocumentOptions withPictureDescriptionAreaThreshold(@Nullable Double pictureDescriptionAreaThreshold) { + setPictureDescriptionAreaThreshold(pictureDescriptionAreaThreshold); + return this; + } + + /** + * Sets the local picture description configuration and returns this instance for method chaining. + * + * @param pictureDescriptionLocal the picture description local configuration, or null to disable + * @return this options instance + * @throws IllegalArgumentException if both API and local picture description are set + */ + public ConvertDocumentOptions withPictureDescriptionLocal(@Nullable PictureDescriptionLocal pictureDescriptionLocal) { + setPictureDescriptionLocal(pictureDescriptionLocal); + return this; + } + + /** + * Sets the processing pipeline and returns this instance for method chaining. + * + * @param pipeline the processing pipeline, or null to use default + * @return this options instance + */ + public ConvertDocumentOptions withPipeline(@Nullable ProcessingPipeline pipeline) { + setPipeline(pipeline); + return this; + } + + /** + * Sets whether to enable table cell matching and returns this instance for method chaining. + * + * @param tableCellMatching true to enable table cell matching, false to disable, or null to use default + * @return this options instance + */ + public ConvertDocumentOptions withTableCellMatching(@Nullable Boolean tableCellMatching) { + setTableCellMatching(tableCellMatching); + return this; + } + + /** + * Sets the table processing mode and returns this instance for method chaining. + * + * @param tableMode the table mode, or null to use default + * @return this options instance + */ + public ConvertDocumentOptions withTableMode(@Nullable TableFormerMode tableMode) { + setTableMode(tableMode); + return this; + } + + /** + * Sets the list of output formats and returns this instance for method chaining. + * + * @param toFormats the list of output formats, or null to use default + * @return this options instance + */ + public ConvertDocumentOptions withToFormats(@Nullable List toFormats) { + setToFormats(toFormats); + return this; + } + + /** + * Sets the VLM pipeline model type and returns this instance for method chaining. + * + * @param vlmPipelineModel the VLM model type, or null to use default + * @return this options instance + */ + public ConvertDocumentOptions withVlmPipelineModel(@Nullable VlmModelType vlmPipelineModel) { + setVlmPipelineModel(vlmPipelineModel); + return this; + } + + /** + * Sets the API-based VLM pipeline model identifier and returns this instance for method chaining. + * + * @param vlmPipelineModelApi the API model identifier, or null to use default + * @return this options instance + */ + public ConvertDocumentOptions withVlmPipelineModelApi(@Nullable String vlmPipelineModelApi) { + setVlmPipelineModelApi(vlmPipelineModelApi); + return this; + } + + /** + * Sets the local VLM pipeline model identifier and returns this instance for method chaining. + * + * @param vlmPipelineModelLocal the local model identifier, or null to use default + * @return this options instance + */ + public ConvertDocumentOptions withVlmPipelineModelLocal(@Nullable String vlmPipelineModelLocal) { + setVlmPipelineModelLocal(vlmPipelineModelLocal); + return this; + } + + @Override + public String toString() { + return "ConvertDocumentOptions{" + + "abortOnError=" + abortOnError + + ", fromFormats=" + fromFormats + + ", toFormats=" + toFormats + + ", imageExportMode=" + imageExportMode + + ", doOcr=" + doOcr + + ", forceOcr=" + forceOcr + + ", ocrEngine=" + ocrEngine + + ", ocrLang=" + ocrLang + + ", pdfBackend=" + pdfBackend + + ", tableMode=" + tableMode + + ", tableCellMatching=" + tableCellMatching + + ", pipeline=" + pipeline + + ", pageRange=" + pageRange + + ", documentTimeout=" + documentTimeout + + ", doTableStructure=" + doTableStructure + + ", includeImages=" + includeImages + + ", imagesScale=" + imagesScale + + ", mdPageBreakPlaceholder='" + mdPageBreakPlaceholder + '\'' + + ", doCodeEnrichment=" + doCodeEnrichment + + ", doFormulaEnrichment=" + doFormulaEnrichment + + ", doPictureClassification=" + doPictureClassification + + ", doPictureDescription=" + doPictureDescription + + ", pictureDescriptionAreaThreshold=" + pictureDescriptionAreaThreshold + + ", pictureDescriptionLocal=" + pictureDescriptionLocal + + ", pictureDescriptionApi=" + pictureDescriptionApi + + ", vlmPipelineModel=" + vlmPipelineModel + + ", vlmPipelineModelLocal='" + vlmPipelineModelLocal + '\'' + + ", vlmPipelineModelApi='" + vlmPipelineModelApi + '\'' + + '}'; + } } diff --git a/api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionApi.java b/api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionApi.java index caa7c1d..ee99c86 100644 --- a/api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionApi.java +++ b/api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionApi.java @@ -1,7 +1,10 @@ package ai.docling.api.convert.request.options; +import static ai.docling.api.util.ValidationUtils.ensureNotNull; + import java.net.URI; import java.time.Duration; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -10,111 +13,228 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -@JsonInclude(JsonInclude.Include.NON_NULL) -public record PictureDescriptionApi( +/** + * Configuration options for picture description API integration. + * This class encapsulates settings for making API calls to describe pictures, + * including URL, headers, parameters, timeout, concurrency, and custom prompts. + */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class PictureDescriptionApi { + @JsonProperty("url") + private URI url; + + @JsonProperty("headers") + private Map headers = new HashMap<>(); + + @JsonProperty("params") + private Map params = new HashMap<>(); + + @JsonProperty("timeout") + private Duration timeout; + + @JsonProperty("concurrency") + private Integer concurrency; + + @JsonProperty("prompt") + private String prompt; + + /** + * Gets the URL of the picture description API endpoint. + * + * @return the API endpoint URL, or null if not set + */ + @Nullable + public URI getUrl() { + return url; + } - @JsonProperty("url") URI url, + /** + * Sets the URL of the picture description API endpoint. + * + * @param url the API endpoint URL to set + */ + public void setUrl(URI url) { + this.url = ensureNotNull(url, "url"); + } - @JsonProperty("headers") @Nullable Map headers, + /** + * Sets the URL of the picture description API endpoint and returns this instance. + * + * @param url the API endpoint URL to set + * @return this instance for method chaining + */ + public PictureDescriptionApi withUrl(URI url) { + setUrl(url); + return this; + } - @JsonProperty("params") @Nullable Map params, + /** + * Gets the headers to be sent with the API request. + * + * @return the map of headers, or null if not set + */ + @Nullable + public Map getHeaders() { + return Collections.unmodifiableMap(headers); + } - @JsonProperty("timeout") @Nullable Duration timeout, + /** + * Sets the headers to be sent with the API request. + * + * @param headers the map of headers to set + */ + public void setHeaders(@Nullable Map headers) { + this.headers.clear(); - @JsonProperty("concurrency") @Nullable Integer concurrency, + if (headers != null) { + this.headers.putAll(headers); + } + } - @JsonProperty("prompt") @Nullable String prompt + /** + * Sets the headers to be sent with the API request and returns this instance. + * + * @param headers the map of headers to set + * @return this instance for method chaining + */ + public PictureDescriptionApi withHeaders(@Nullable Map headers) { + setHeaders(headers); + return this; + } -) { + /** + * Gets the parameters to be sent with the API request. + * + * @return the map of parameters, or null if not set + */ + @Nullable + public Map getParams() { + return Collections.unmodifiableMap(params); + } - public PictureDescriptionApi { - if (url == null) { - throw new IllegalArgumentException("url cannot be null"); - } + /** + * Sets the parameters to be sent with the API request. + * + * @param params the map of parameters to set + */ + public void setParams(@Nullable Map params) { + this.params.clear(); - if (headers != null) { - headers = new HashMap<>(headers); - } if (params != null) { - params = new HashMap<>(params); + this.params.putAll(params); } } - public static Builder builder() { - return new Builder(); + /** + * Sets the parameters to be sent with the API request and returns this instance. + * + * @param params the map of parameters to set + * @return this instance for method chaining + */ + public PictureDescriptionApi withParams(@Nullable Map params) { + setParams(params); + return this; } - public static class Builder { - - @Nullable - private URI url; - @Nullable - private Map headers; - @Nullable - private Map params; - @Nullable - private Duration timeout; - @Nullable - private Integer concurrency; - @Nullable - private String prompt; - - private Builder() { - } + /** + * Gets the timeout duration for the API request. + * + * @return the timeout duration, or null if not set + */ + @Nullable + public Duration getTimeout() { + return timeout; + } - /** - * Endpoint which accepts OpenAI API compatible requests. - */ - public Builder url(URI url) { - this.url = url; - return this; - } + /** + * Sets the timeout duration for the API request. + * + * @param timeout the timeout duration to set + */ + public void setTimeout(@Nullable Duration timeout) { + this.timeout = timeout; + } - /** - * Headers used for calling the API endpoint. - * For example, it could include authentication headers. - */ - public Builder headers(@Nullable Map headers) { - this.headers = headers; - return this; - } + /** + * Sets the timeout duration for the API request and returns this instance. + * + * @param timeout the timeout duration to set + * @return this instance for method chaining + */ + public PictureDescriptionApi withTimeout(@Nullable Duration timeout) { + setTimeout(timeout); + return this; + } - /** - * Model parameters. - */ - public Builder params(@Nullable Map params) { - this.params = params; - return this; - } + /** + * Gets the maximum number of concurrent API requests allowed. + * + * @return the concurrency limit, or null if not set + */ + @Nullable + public Integer getConcurrency() { + return concurrency; + } - /** - * Timeout for the API request. - */ - public Builder timeout(@Nullable Duration timeout) { - this.timeout = timeout; - return this; - } + /** + * Sets the maximum number of concurrent API requests allowed. + * + * @param concurrency the concurrency limit to set + */ + public void setConcurrency(@Nullable Integer concurrency) { + this.concurrency = concurrency; + } - /** - * Maximum number of concurrent requests to the API. - */ - public Builder concurrency(@Nullable Integer concurrency) { - this.concurrency = concurrency; - return this; - } + /** + * Sets the maximum number of concurrent API requests allowed and returns this instance. + * + * @param concurrency the concurrency limit to set + * @return this instance for method chaining + */ + public PictureDescriptionApi withConcurrency(@Nullable Integer concurrency) { + setConcurrency(concurrency); + return this; + } - /** - * Prompt used when calling the vision-language model. - */ - public Builder prompt(@Nullable String prompt) { - this.prompt = prompt; - return this; - } + /** + * Gets the custom prompt to be used for picture description. + * + * @return the custom prompt, or null if not set + */ + @Nullable + public String getPrompt() { + return prompt; + } - public PictureDescriptionApi build() { - return new PictureDescriptionApi(url, headers, params, timeout, concurrency, prompt); - } + /** + * Sets the custom prompt to be used for picture description. + * + * @param prompt the custom prompt to set + */ + public void setPrompt(@Nullable String prompt) { + this.prompt = prompt; + } + /** + * Sets the custom prompt to be used for picture description and returns this instance. + * + * @param prompt the custom prompt to set + * @return this instance for method chaining + */ + public PictureDescriptionApi withPrompt(@Nullable String prompt) { + setPrompt(prompt); + return this; } + @Override + public String toString() { + return "PictureDescriptionApi{" + + "url=" + (url == null ? "null" : url.toString()) + + ", headers=" + (headers == null ? "null" : headers.toString()) + + ", params=" + (params == null ? "null" : params.toString()) + + ", timeout=" + (timeout == null ? "null" : timeout.toString()) + + ", concurrency=" + (concurrency == null ? "null" : concurrency.toString()) + + ", prompt=" + (prompt == null ? "null" : "'" + prompt + "'") + + '}'; + } } diff --git a/api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionLocal.java b/api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionLocal.java index 2ecfdd1..400809b 100644 --- a/api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionLocal.java +++ b/api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionLocal.java @@ -1,5 +1,8 @@ package ai.docling.api.convert.request.options; +import static ai.docling.api.util.ValidationUtils.ensureNotEmpty; + +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -8,70 +11,123 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -@JsonInclude(JsonInclude.Include.NON_NULL) -public record PictureDescriptionLocal( - - @JsonProperty("repo_id") String repoId, - - @JsonProperty("prompt") @Nullable String prompt, - - @JsonProperty("generation_config") @Nullable Map generationConfig - -) { - - public PictureDescriptionLocal { - if (repoId == null || repoId.isBlank()) { - throw new IllegalArgumentException("repoId cannot be null or empty"); - } - - if (generationConfig != null) { - generationConfig = new HashMap<>(generationConfig); - } +/** + * Configuration for local picture description generation. + * This class contains settings for generating descriptions of pictures using a local model. + */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class PictureDescriptionLocal { + @JsonProperty("repo_id") + private String repoId; + + @JsonProperty("prompt") + private String prompt; + + @JsonProperty("generation_config") + private Map generationConfig = new HashMap<>(); + + /** + * Gets the repository identifier for the local model. + * + * @return the repository ID, or null if not set + */ + @Nullable + public String getRepoId() { + return repoId; } - public static Builder builder() { - return new Builder(); + /** + * Sets the repository identifier for the local model. + * + * @param repoId the repository ID to set, or null to clear + */ + public void setRepoId(String repoId) { + this.repoId = ensureNotEmpty(repoId, "repoId"); } - public static class Builder { - @Nullable - private String repoId; - @Nullable - private String prompt; - @Nullable - private Map generationConfig; + /** + * Sets the repository identifier for the local model (fluent API). + * + * @param repoId the repository ID to set, or null to clear + * @return this instance for method chaining + */ + public PictureDescriptionLocal withRepoId(String repoId) { + setRepoId(repoId); + return this; + } - private Builder() { - } + /** + * Gets the prompt used for picture description generation. + * + * @return the prompt, or null if not set + */ + @Nullable + public String getPrompt() { + return prompt; + } - /** - * Repository id from the Hugging Face Hub. - */ - public Builder repoId(String repoId) { - this.repoId = repoId; - return this; - } + /** + * Sets the prompt used for picture description generation. + * + * @param prompt the prompt to set, or null to clear + */ + public void setPrompt(@Nullable String prompt) { + this.prompt = prompt; + } - /** - * Prompt used when calling the vision-language model. - */ - public Builder prompt(@Nullable String prompt) { - this.prompt = prompt; - return this; - } + /** + * Sets the prompt used for picture description generation (fluent API). + * + * @param prompt the prompt to set, or null to clear + * @return this instance for method chaining + */ + public PictureDescriptionLocal withPrompt(@Nullable String prompt) { + setPrompt(prompt); + return this; + } - /** - * Config from Hugging Face - */ - public Builder generationConfig(@Nullable Map generationConfig) { - this.generationConfig = generationConfig; - return this; - } + /** + * Gets an unmodifiable view of the generation configuration parameters. + * + * @return an unmodifiable map of generation configuration parameters + */ + @Nullable + public Map getGenerationConfig() { + return Collections.unmodifiableMap(generationConfig); + } - public PictureDescriptionLocal build() { - return new PictureDescriptionLocal(repoId, prompt, generationConfig); + /** + * Sets the generation configuration parameters. + * The provided map is copied to avoid external modifications. + * + * @param generationConfig the generation configuration parameters to set, or null to clear + */ + public void setGenerationConfig(@Nullable Map generationConfig) { + this.generationConfig.clear(); + + if (generationConfig != null) { + this.generationConfig.putAll(generationConfig); } + } + /** + * Sets the generation configuration parameters (fluent API). + * The provided map is copied to avoid external modifications. + * + * @param generationConfig the generation configuration parameters to set, or null to clear + * @return this instance for method chaining + */ + public PictureDescriptionLocal withGenerationConfig(@Nullable Map generationConfig) { + setGenerationConfig(generationConfig); + return this; } + @Override + public String toString() { + return "PictureDescriptionLocal{" + + "repoId=" + (repoId == null ? "null" : "'" + repoId + "'") + + ", prompt=" + (prompt == null ? "null" : "'" + prompt + "'") + + ", generationConfig=" + (generationConfig == null ? "null" : generationConfig.toString()) + + '}'; + } } diff --git a/api/src/main/java/ai/docling/api/convert/request/options/VlmModelApi.java b/api/src/main/java/ai/docling/api/convert/request/options/VlmModelApi.java index 7b6a3e3..b03aede 100644 --- a/api/src/main/java/ai/docling/api/convert/request/options/VlmModelApi.java +++ b/api/src/main/java/ai/docling/api/convert/request/options/VlmModelApi.java @@ -2,6 +2,7 @@ import java.net.URI; import java.time.Duration; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -14,137 +15,291 @@ * API details for using a vision-language model for the VLM pipeline. */ @JsonInclude(JsonInclude.Include.NON_NULL) -public record VlmModelApi( +public class VlmModelApi { + @JsonProperty("url") + private URI url; - @JsonProperty("url") URI url, + @JsonProperty("headers") + private Map headers = new HashMap<>(); - @JsonProperty("headers") @Nullable Map headers, + @JsonProperty("params") + private Map params = new HashMap<>(); - @JsonProperty("params") @Nullable Map params, + @JsonProperty("timeout") + private Duration timeout; - @JsonProperty("timeout") @Nullable Duration timeout, + @JsonProperty("concurrency") + private Integer concurrency; - @JsonProperty("concurrency") @Nullable Integer concurrency, + @JsonProperty("prompt") + private String prompt; - @JsonProperty("prompt") @Nullable String prompt, + @JsonProperty("scale") + private Integer scale; - @JsonProperty("scale") @Nullable Integer scale, + @JsonProperty("response_format") + private ResponseFormat responseFormat; - @JsonProperty("response_format") ResponseFormat responseFormat + /** + * Gets the endpoint which accepts OpenAI API compatible requests. + * + * @return the API endpoint URL + */ + @Nullable + public URI getUrl() { + return url; + } -) { + /** + * Sets the endpoint which accepts OpenAI API compatible requests. + * + * @param url the API endpoint URL + */ + public void setUrl(@Nullable URI url) { + this.url = url; + } - public VlmModelApi { - if (url == null) { - throw new IllegalArgumentException("url cannot be null"); + /** + * Sets the endpoint which accepts OpenAI API compatible requests. + * + * @param url the API endpoint URL + * @return this instance for method chaining + */ + public VlmModelApi withUrl(@Nullable URI url) { + setUrl(url); + return this; } - if (responseFormat == null) { - throw new IllegalArgumentException("responseFormat cannot be null"); + + /** + * Gets the headers used for calling the API endpoint. + * For example, it could include authentication headers. + * + * @return the headers map + */ + public Map getHeaders() { + return Collections.unmodifiableMap(headers); } - if (headers != null) { - headers = new HashMap<>(headers); + /** + * Sets the headers used for calling the API endpoint. + * For example, it could include authentication headers. + * + * @param headers the headers map + */ + public void setHeaders(@Nullable Map headers) { + this.headers.clear(); + + if (headers != null) { + this.headers.putAll(headers); + } } - if (params != null) { - params = new HashMap<>(params); + + /** + * Sets the headers used for calling the API endpoint. + * For example, it could include authentication headers. + * + * @param headers the headers map + * @return this instance for method chaining + */ + public VlmModelApi withHeaders(@Nullable Map headers) { + setHeaders(headers); + return this; } - } - public static Builder builder() { - return new Builder(); - } + /** + * Gets the model parameters. + * + * @return the model parameters + */ + public Map getParams() { + return Collections.unmodifiableMap(params); + } - public static class Builder { + /** + * Sets the model parameters. + * + * @param params the model parameters + */ + public void setParams(@Nullable Map params) { + this.params.clear(); + + if (params != null) { + this.params.putAll(params); + } + } + /** + * Sets the model parameters. + * + * @param params the model parameters + * @return this instance for method chaining + */ + public VlmModelApi withParams(@Nullable Map params) { + setParams(params); + return this; + } + + /** + * Gets the timeout for the API request. + * + * @return the timeout duration + */ @Nullable - private URI url; - @Nullable - private Map headers; - @Nullable - private Map params; - @Nullable - private Duration timeout; - @Nullable - private Integer concurrency; - @Nullable - private String prompt; - @Nullable - private Integer scale; + public Duration getTimeout() { + return timeout; + } + + /** + * Sets the timeout for the API request. + * + * @param timeout the timeout duration + */ + public void setTimeout(@Nullable Duration timeout) { + this.timeout = timeout; + } + + /** + * Sets the timeout for the API request. + * + * @param timeout the timeout duration + * @return this instance for method chaining + */ + public VlmModelApi withTimeout(@Nullable Duration timeout) { + setTimeout(timeout); + return this; + } + + /** + * Gets the maximum number of concurrent requests to the API. + * + * @return the concurrency limit + */ @Nullable - private ResponseFormat responseFormat; + public Integer getConcurrency() { + return concurrency; + } - private Builder() { + /** + * Sets the maximum number of concurrent requests to the API. + * + * @param concurrency the concurrency limit + */ + public void setConcurrency(@Nullable Integer concurrency) { + this.concurrency = concurrency; } /** - * Endpoint which accepts OpenAI API compatible requests. + * Sets the maximum number of concurrent requests to the API. + * + * @param concurrency the concurrency limit + * @return this instance for method chaining */ - public Builder url(URI url) { - this.url = url; - return this; + public VlmModelApi withConcurrency(@Nullable Integer concurrency) { + setConcurrency(concurrency); + return this; } /** - * Headers used for calling the API endpoint. - * For example, it could include authentication headers. + * Gets the prompt used when calling the vision-language model. + * + * @return the prompt */ - public Builder headers(@Nullable Map headers) { - this.headers = headers; - return this; + @Nullable + public String getPrompt() { + return prompt; } /** - * Model parameters. + * Sets the prompt used when calling the vision-language model. + * + * @param prompt the prompt */ - public Builder params(@Nullable Map params) { - this.params = params; - return this; + public void setPrompt(@Nullable String prompt) { + this.prompt = prompt; } /** - * Timeout for the API request. + * Sets the prompt used when calling the vision-language model. + * + * @param prompt the prompt + * @return this instance for method chaining */ - public Builder timeout(@Nullable Duration timeout) { - this.timeout = timeout; - return this; + public VlmModelApi withPrompt(@Nullable String prompt) { + setPrompt(prompt); + return this; } /** - * Maximum number of concurrent requests to the API. + * Gets the scale factor of the images used. + * + * @return the scale factor */ - public Builder concurrency(@Nullable Integer concurrency) { - this.concurrency = concurrency; - return this; + @Nullable + public Integer getScale() { + return scale; } /** - * Prompt used when calling the vision-language model. + * Sets the scale factor of the images used. + * + * @param scale the scale factor */ - public Builder prompt(@Nullable String prompt) { - this.prompt = prompt; - return this; + public void setScale(@Nullable Integer scale) { + this.scale = scale; } /** - * Scale factor of the images used. + * Sets the scale factor of the images used. + * + * @param scale the scale factor + * @return this instance for method chaining */ - public Builder scale(@Nullable Integer scale) { - this.scale = scale; - return this; + public VlmModelApi withScale(@Nullable Integer scale) { + setScale(scale); + return this; } /** - * Type of response generated by the model. + * Gets the type of response generated by the model. + * + * @return the response format */ - public Builder responseFormat(ResponseFormat responseFormat) { - this.responseFormat = responseFormat; - return this; + @Nullable + public ResponseFormat getResponseFormat() { + return responseFormat; } - public VlmModelApi build() { - return new VlmModelApi(url, headers, params, timeout, concurrency, prompt, scale, responseFormat); + /** + * Sets the type of response generated by the model. + * + * @param responseFormat the response format + */ + public void setResponseFormat(@Nullable ResponseFormat responseFormat) { + this.responseFormat = responseFormat; } - } + /** + * Sets the type of response generated by the model. + * + * @param responseFormat the response format + * @return this instance for method chaining + */ + public VlmModelApi withResponseFormat(@Nullable ResponseFormat responseFormat) { + setResponseFormat(responseFormat); + return this; + } + @Override + public String toString() { + return "VlmModelApi{" + + "url=" + url + + ", headers=" + headers + + ", params=" + params + + ", timeout=" + timeout + + ", concurrency=" + concurrency + + ", prompt='" + prompt + '\'' + + ", scale=" + scale + + ", responseFormat=" + responseFormat + + '}'; + } } diff --git a/api/src/main/java/ai/docling/api/convert/request/options/VlmModelLocal.java b/api/src/main/java/ai/docling/api/convert/request/options/VlmModelLocal.java index d078d71..91c4a22 100644 --- a/api/src/main/java/ai/docling/api/convert/request/options/VlmModelLocal.java +++ b/api/src/main/java/ai/docling/api/convert/request/options/VlmModelLocal.java @@ -1,5 +1,6 @@ package ai.docling.api.convert.request.options; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -13,146 +14,279 @@ * The parameters refer to a model hosted on Hugging Face. */ @JsonInclude(JsonInclude.Include.NON_NULL) -public record VlmModelLocal( - - @JsonProperty("repo_id") String repoId, +public class VlmModelLocal { + @JsonProperty("repo_id") + private String repoId; - @JsonProperty("prompt") @Nullable String prompt, + @JsonProperty("prompt") + private String prompt; - @JsonProperty("scale") @Nullable Integer scale, + @JsonProperty("scale") + private Integer scale; - @JsonProperty("response_format") ResponseFormat responseFormat, + @JsonProperty("response_format") + private ResponseFormat responseFormat; - @JsonProperty("inference_framework") InferenceFramework inferenceFramework, + @JsonProperty("inference_framework") + private InferenceFramework inferenceFramework; - @JsonProperty("transformers_model_type") @Nullable TransformersModelType transformersModelType, + @JsonProperty("transformers_model_type") + private TransformersModelType transformersModelType; - @JsonProperty("extra_generation_config") @Nullable Map extraGenerationConfig + @JsonProperty("extra_generation_config") + private Map extraGenerationConfig = new HashMap<>(); -) { + /** + * Gets the repository id from the Hugging Face Hub. + * + * @return the repository id + */ + @Nullable + public String getRepoId() { + return repoId; + } - public VlmModelLocal { - if (repoId == null || repoId.isBlank()) { - throw new IllegalArgumentException("repoId cannot be null or empty"); + /** + * Sets the repository id from the Hugging Face Hub. + * + * @param repoId the repository id + */ + public void setRepoId(@Nullable String repoId) { + this.repoId = repoId; } - if (responseFormat == null) { - throw new IllegalArgumentException("responseFormat cannot be null"); + + /** + * Sets the repository id from the Hugging Face Hub. + * + * @param repoId the repository id + * @return this instance for method chaining + */ + public VlmModelLocal withRepoId(@Nullable String repoId) { + setRepoId(repoId); + return this; } - if (inferenceFramework == null) { - throw new IllegalArgumentException("inferenceFramework cannot be null"); + + /** + * Gets the prompt used when calling the vision-language model. + * + * @return the prompt + */ + @Nullable + public String getPrompt() { + return prompt; } - if (extraGenerationConfig != null) { - extraGenerationConfig = new HashMap<>(extraGenerationConfig); + /** + * Sets the prompt used when calling the vision-language model. + * + * @param prompt the prompt + */ + public void setPrompt(@Nullable String prompt) { + this.prompt = prompt; } - } - public static Builder builder() { - return new Builder(); - } + /** + * Sets the prompt used when calling the vision-language model. + * + * @param prompt the prompt + * @return this instance for method chaining + */ + public VlmModelLocal withPrompt(@Nullable String prompt) { + setPrompt(prompt); + return this; + } - public static class Builder { - @Nullable - private String repoId; - @Nullable - private String prompt; - @Nullable - private Integer scale; - @Nullable - private ResponseFormat responseFormat; - @Nullable - private InferenceFramework inferenceFramework; + /** + * Gets the scale factor of the images used. + * + * @return the scale factor + */ @Nullable - private TransformersModelType transformersModelType; + public Integer getScale() { + return scale; + } + + /** + * Sets the scale factor of the images used. + * + * @param scale the scale factor + */ + public void setScale(@Nullable Integer scale) { + this.scale = scale; + } + + /** + * Sets the scale factor of the images used. + * + * @param scale the scale factor + * @return this instance for method chaining + */ + public VlmModelLocal withScale(@Nullable Integer scale) { + setScale(scale); + return this; + } + + /** + * Gets the type of response generated by the model. + * + * @return the response format + */ @Nullable - private Map extraGenerationConfig; + public ResponseFormat getResponseFormat() { + return responseFormat; + } - private Builder() { + /** + * Sets the type of response generated by the model. + * + * @param responseFormat the response format + */ + public void setResponseFormat(@Nullable ResponseFormat responseFormat) { + this.responseFormat = responseFormat; } /** - * Repository id from the Hugging Face Hub. + * Sets the type of response generated by the model. + * + * @param responseFormat the response format + * @return this instance for method chaining */ - public Builder repoId(String repoId) { - this.repoId = repoId; - return this; + public VlmModelLocal withResponseFormat(@Nullable ResponseFormat responseFormat) { + setResponseFormat(responseFormat); + return this; } /** - * Prompt used when calling the vision-language model. + * Gets the inference framework to use. + * + * @return the inference framework */ - public Builder prompt(@Nullable String prompt) { - this.prompt = prompt; - return this; + @Nullable + public InferenceFramework getInferenceFramework() { + return inferenceFramework; } /** - * Scale factor of the images used. + * Sets the inference framework to use. + * + * @param inferenceFramework the inference framework */ - public Builder scale(@Nullable Integer scale) { - this.scale = scale; - return this; + public void setInferenceFramework(@Nullable InferenceFramework inferenceFramework) { + this.inferenceFramework = inferenceFramework; } /** - * Type of response generated by the model. + * Sets the inference framework to use. + * + * @param inferenceFramework the inference framework + * @return this instance for method chaining */ - public Builder responseFormat(ResponseFormat responseFormat) { - this.responseFormat = responseFormat; - return this; + public VlmModelLocal withInferenceFramework(@Nullable InferenceFramework inferenceFramework) { + setInferenceFramework(inferenceFramework); + return this; } /** - * Inference framework to use. + * Gets the type of transformers auto-model to use. + * + * @return the transformers model type */ - public Builder inferenceFramework(InferenceFramework inferenceFramework) { - this.inferenceFramework = inferenceFramework; - return this; + @Nullable + public TransformersModelType getTransformersModelType() { + return transformersModelType; } /** - * Type of transformers auto-model to use. + * Sets the type of transformers auto-model to use. + * + * @param transformersModelType the transformers model type */ - public Builder transformersModelType(@Nullable TransformersModelType transformersModelType) { - this.transformersModelType = transformersModelType; - return this; + public void setTransformersModelType(@Nullable TransformersModelType transformersModelType) { + this.transformersModelType = transformersModelType; } /** - * Config from Hugging Face + * Sets the type of transformers auto-model to use. + * + * @param transformersModelType the transformers model type + * @return this instance for method chaining */ - public Builder extraGenerationConfig(@Nullable Map extraGenerationConfig) { - this.extraGenerationConfig = extraGenerationConfig; - return this; + public VlmModelLocal withTransformersModelType(@Nullable TransformersModelType transformersModelType) { + setTransformersModelType(transformersModelType); + return this; } - public VlmModelLocal build() { - return new VlmModelLocal(repoId, prompt, scale, responseFormat, inferenceFramework, transformersModelType, extraGenerationConfig); + /** + * Gets the extra generation config from Hugging Face. + * + * @return the extra generation config + * @see Hugging Face GenerationConfig + */ + @Nullable + public Map getExtraGenerationConfig() { + return Collections.unmodifiableMap(extraGenerationConfig); } - } + /** + * Sets the extra generation config from Hugging Face. + * + * @param extraGenerationConfig the extra generation config + * @see Hugging Face GenerationConfig + */ + public void setExtraGenerationConfig(@Nullable Map extraGenerationConfig) { + this.extraGenerationConfig.clear(); - /** - * Inference framework to use. - */ - public enum InferenceFramework { + if (extraGenerationConfig != null) { + this.extraGenerationConfig.putAll(extraGenerationConfig); + } + } - @JsonProperty("mlx") MLX, - @JsonProperty("transformers") TRANSFORMERS, - @JsonProperty("vllm") VLLM + /** + * Sets the extra generation config from Hugging Face. + * + * @param extraGenerationConfig the extra generation config + * @return this instance for method chaining + * @see Hugging Face GenerationConfig + */ + public VlmModelLocal withExtraGenerationConfig(@Nullable Map extraGenerationConfig) { + setExtraGenerationConfig(extraGenerationConfig); + return this; + } - } + @Override + public String toString() { + return "VlmModelLocal{" + + "repoId='" + repoId + '\'' + + ", prompt='" + prompt + '\'' + + ", scale=" + scale + + ", responseFormat=" + responseFormat + + ", inferenceFramework=" + inferenceFramework + + ", transformersModelType=" + transformersModelType + + ", extraGenerationConfig=" + extraGenerationConfig + + '}'; + } - /** - * Type of transformers auto-model to use. - */ - public enum TransformersModelType { + /** + * Inference framework to use. + */ + public enum InferenceFramework { + + @JsonProperty("mlx") MLX, + @JsonProperty("transformers") TRANSFORMERS, + @JsonProperty("vllm") VLLM - @JsonProperty("automodel") AUTOMODEL, - @JsonProperty("automodel-vision2seq") AUTOMODEL_VISION2SEQ, - @JsonProperty("automodel-causallm") AUTOMODEL_CAUSALLM, - @JsonProperty("automodel-imagetexttotext") AUTOMODEL_IMAGETEXTTOTEXT + } - } + /** + * Type of transformers auto-model to use. + */ + public enum TransformersModelType { + + @JsonProperty("automodel") AUTOMODEL, + @JsonProperty("automodel-vision2seq") AUTOMODEL_VISION2SEQ, + @JsonProperty("automodel-causallm") AUTOMODEL_CAUSALLM, + @JsonProperty("automodel-imagetexttotext") AUTOMODEL_IMAGETEXTTOTEXT + + } } diff --git a/api/src/main/java/ai/docling/api/convert/request/source/FileSource.java b/api/src/main/java/ai/docling/api/convert/request/source/FileSource.java index 76cf41f..911c262 100644 --- a/api/src/main/java/ai/docling/api/convert/request/source/FileSource.java +++ b/api/src/main/java/ai/docling/api/convert/request/source/FileSource.java @@ -1,72 +1,93 @@ package ai.docling.api.convert.request.source; +import static ai.docling.api.util.ValidationUtils.ensureNotBlank; + import org.jspecify.annotations.Nullable; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; /** - * Represents a file source for a document to convert. + * Represents a file-based data source. + * This class provides functionality to handle Base64-encoded file content and filenames + * for file sources. Inherits from {@link Source}. + * + *

JSON serialization/deserialization: + *

    + *
  • {@code base64_string} - Represents the Base64-encoded content of the file.
  • + *
  • {@code filename} - Represents the name of the uploaded file.
  • + *
+ * + *

By default, JSON includes non-empty fields only. */ -@JsonInclude(JsonInclude.Include.NON_NULL) -public record FileSource( - - @JsonProperty("kind") Kind kind, - - @JsonProperty("base64_string") String base64String, +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public final class FileSource extends Source { + @JsonProperty("base64_string") + private String base64String; - @JsonProperty("filename") String filename + @JsonProperty("filename") + private String filename; -) implements Source { - - public FileSource { - if (base64String == null || base64String.isBlank()) { - throw new IllegalArgumentException("base64String cannot be null or empty"); - } - if (filename == null || filename.isBlank()) { - throw new IllegalArgumentException("filename cannot be null or empty"); - } - kind = Kind.FILE; + public FileSource() { + super(); + setKind(Kind.FILE); } - public static FileSource from(String filename, String base64String) { - return FileSource.builder().base64String(base64String).filename(filename).build(); + /** + * Base64 content of the file. + * @return base64 content or {@code null} + */ + @Nullable + public String getBase64String() { + return base64String; } - public static Builder builder() { - return new Builder(); + /** + * Sets the base64 content of the file. + * @param base64String may be {@code null} + */ + public void setBase64String(String base64String) { + this.base64String = ensureNotBlank(base64String, "base64String"); } - public static class Builder { - - @Nullable - private String base64String; - @Nullable - private String filename; - - private Builder() { - } + public FileSource withBase64String(String base64String) { + setBase64String(base64String); + return this; + } - /** - * Content of the file serialized in base64. - */ - public Builder base64String(String base64String) { - this.base64String = base64String; - return this; - } + /** + * Filename of the uploaded document. + * @return filename or {@code null} + */ + @Nullable + public String getFilename() { + return filename; + } - /** - * Filename of the uploaded document. - */ - public Builder filename(String filename) { - this.filename = filename; - return this; - } + /** + * Sets the filename of the uploaded document. + * @param filename may be {@code null} + */ + public void setFilename(String filename) { + this.filename = ensureNotBlank(filename, "filename"); + } - public FileSource build() { - return new FileSource(Kind.FILE, base64String, filename); - } + public FileSource withFilename(String filename) { + setFilename(filename); + return this; + } + @Override + public String toString() { + return "FileSource{" + + "kind='" + getKind() + "'" + + ", base64String=" + (base64String == null ? "null" : "'" + base64String + "'") + + ", filename=" + (filename == null ? "null" : "'" + filename + "'") + + '}'; } + @Override + public void setKind(@Nullable Kind kind) { + super.setKind(Kind.FILE); + } } diff --git a/api/src/main/java/ai/docling/api/convert/request/source/HttpSource.java b/api/src/main/java/ai/docling/api/convert/request/source/HttpSource.java index 952c2fc..0e7c2f3 100644 --- a/api/src/main/java/ai/docling/api/convert/request/source/HttpSource.java +++ b/api/src/main/java/ai/docling/api/convert/request/source/HttpSource.java @@ -1,6 +1,9 @@ package ai.docling.api.convert.request.source; +import static ai.docling.api.util.ValidationUtils.ensureNotNull; + import java.net.URI; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -10,71 +13,119 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** - * Represents an HTTP source for a document to convert. + * Represents an HTTP-based data source. + * This class is a concrete implementation of {@link Source} and provides + * functionality for configuring an HTTP endpoint with optional headers. + * + *

JSON serialization/deserialization: + *

    + *
  • {@code url} - Represents the URI of the HTTP source.
  • + *
  • {@code headers} - A map of custom HTTP headers for the request.
  • + *
+ * + *

By default, JSON includes non-empty fields only. */ -@JsonInclude(JsonInclude.Include.NON_NULL) -public record HttpSource( - - @JsonProperty("kind") Source.Kind kind, - - @JsonProperty("url") URI url, +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public final class HttpSource extends Source { + @JsonProperty("url") + private URI url; - @JsonProperty("headers") @Nullable Map headers + @JsonProperty("headers") + private Map headers = new HashMap<>(); -) implements Source { - - public HttpSource { - if (url == null) { - throw new IllegalArgumentException("url cannot be null"); - } - if (headers != null) { - headers = new HashMap<>(headers); - } - kind = Source.Kind.HTTP; + public HttpSource() { + super(); + setKind(Kind.HTTP); } - public static HttpSource from(String url) { - return HttpSource.builder().url(URI.create(url)).build(); + /** + * Retrieves the URL associated with this HTTP source. + * + * @return the {@link URI} representing the URL of the HTTP source, or {@code null} if not set. + */ + @Nullable + public URI getUrl() { + return url; } - public static HttpSource from(URI url) { - return HttpSource.builder().url(url).build(); + /** + * Sets the {@code url} for this HTTP source. The URL represents the endpoint + * to be used by this HTTP-based source. + * + * @param url the {@link URI} of the HTTP source; may be {@code null}. + * A {@code null} value indicates that the URL is unset. + */ + public void setUrl(URI url) { + this.url = ensureNotNull(url, "url"); } - public static Builder builder() { - return new Builder(); + /** + * Sets the {@code url} for this {@code HttpSource} and returns the updated instance. + * This method allows chaining while configuring the {@link HttpSource}. + * + * @param url the {@link URI} representing the HTTP source's endpoint; may be {@code null}, + * indicating that the URL is unset. + * @return the updated {@code HttpSource} instance with the specified {@code url}. + */ + public HttpSource withUrl(URI url) { + setUrl(url); + return this; } - public static class Builder { - - @Nullable - private URI url; - @Nullable - private Map headers; - - private Builder() { - } + /** + * Retrieves an unmodifiable view of the HTTP headers associated with the {@link HttpSource}. + * The returned map contains key-value pairs representing the custom headers configured + * for this HTTP source. + * + * @return an unmodifiable {@link Map} containing the HTTP headers, where the keys are + * {@link String} objects representing header names, and the values are + * {@link Object} objects representing header values. Never {@code null}. + */ + public Map getHeaders() { + return Collections.unmodifiableMap(headers); + } - /** - * HTTP url to process. - */ - public Builder url(URI url) { - this.url = url; - return this; - } + /** + * Configures the HTTP headers for this {@link HttpSource}. + * If a non-{@code null} map is provided, it replaces the existing headers with the new ones. + * If {@code null} is supplied, all headers are cleared. + * + * @param headers a {@link Map} where keys are {@link String} objects representing header names, + * and values are {@link Object} instances representing header values; may be {@code null}. + */ + public void setHeaders(@Nullable Map headers) { + this.headers.clear(); - /** - * Additional headers used to fetch the urls (e.g. authorization or agent). - */ - public Builder headers(@Nullable Map headers) { - this.headers = headers; - return this; + if (headers != null) { + this.headers.putAll(headers); } + } - public HttpSource build() { - return new HttpSource(Source.Kind.HTTP, url, headers); - } + /** + * Updates the {@link HttpSource} with the specified HTTP headers and returns the updated instance. + * This method replaces the existing headers with the ones provided in the supplied map. + * + * @param headers a {@link Map} where keys are {@link String} objects representing header names, + * and values are {@link Object} instances representing header values; + * may be {@code null} to clear all headers. + * @return the updated {@link HttpSource} instance with the provided headers configuration. + */ + public HttpSource withHeaders(@Nullable Map headers) { + setHeaders(headers); + return this; + } + @Override + public String toString() { + return "HttpSource{" + + "kind='" + getKind() + "'" + + ", url=" + (url == null ? "null" : url.toString()) + + ", headers=" + (headers == null ? "null" : headers.toString()) + + '}'; } + @Override + public void setKind(@Nullable Kind kind) { + super.setKind(Kind.HTTP); + } } diff --git a/api/src/main/java/ai/docling/api/convert/request/source/Source.java b/api/src/main/java/ai/docling/api/convert/request/source/Source.java index e82c043..dd0892b 100644 --- a/api/src/main/java/ai/docling/api/convert/request/source/Source.java +++ b/api/src/main/java/ai/docling/api/convert/request/source/Source.java @@ -1,17 +1,57 @@ package ai.docling.api.convert.request.source; +import org.jspecify.annotations.Nullable; + import com.fasterxml.jackson.annotation.JsonProperty; /** * Source of the document. */ -public sealed interface Source permits FileSource, HttpSource { - +public sealed abstract class Source> permits FileSource, HttpSource { + /** + * Enum representing the type of {@link Source}. + *

    + *
  • {@code HTTP} - Represents an HTTP-based source (identified by {@code "http"}).
  • + *
  • {@code FILE} - Represents a file-based source (identified by {@code "file"}).
  • + *
+ * The enum values are serialized and deserialized using their respective JSON property + * values as defined by the {@link JsonProperty} annotation. + */ enum Kind { - @JsonProperty("http") HTTP, @JsonProperty("file") FILE + } + @JsonProperty("kind") + private Kind kind = Kind.FILE; + + /** + * Retrieves the {@link Kind} of this {@link Source}. + * + * @return the {@link Kind} of this {@code Source}, or {@code null} if not set. + */ + @Nullable + public Kind getKind() { + return kind; + } + + /** + * Sets the {@link Kind} of this {@code Source}. + * + * @param kind the {@link Kind} representing the type of this source; may be {@code null}. + */ + public void setKind(@Nullable Kind kind) { + this.kind = kind; } + /** + * Sets the {@link Kind} of this {@link Source} and returns the updated instance of {@link Source}. + * + * @param kind the {@link Kind} representing the type of this source; may be {@code null}. + * @return the updated {@code Source} instance with the newly set {@link Kind}. + */ + public S withKind(@Nullable Kind kind) { + setKind(kind); + return (S) this; + } } diff --git a/api/src/main/java/ai/docling/api/convert/request/target/InBodyTarget.java b/api/src/main/java/ai/docling/api/convert/request/target/InBodyTarget.java index e7a7a47..88c3e55 100644 --- a/api/src/main/java/ai/docling/api/convert/request/target/InBodyTarget.java +++ b/api/src/main/java/ai/docling/api/convert/request/target/InBodyTarget.java @@ -1,20 +1,35 @@ package ai.docling.api.convert.request.target; +import org.jspecify.annotations.Nullable; + import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; /** - * Target for including the converted document directly in the body of the response as text. + * Target for including the converted document in the response body. + * + *

This is a concrete implementation of {@link Target}, where the {@code Kind} is set to + * {@code INBODY}. The converted document will be delivered directly in the response body. + * + *

Uses JSON serialization annotations to include only non-empty fields in the output. + * + *

This class overrides {@link Object#toString()} for a string representation of the instance. */ -@JsonInclude(JsonInclude.Include.NON_NULL) -public record InBodyTarget(@JsonProperty("kind") Target.Kind kind) implements Target { - - public InBodyTarget { - kind = Target.Kind.INBODY; +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public final class InBodyTarget extends Target { + public InBodyTarget() { + super(); + setKind(Kind.INBODY); } - public static InBodyTarget create() { - return new InBodyTarget(Target.Kind.INBODY); + @Override + public String toString() { + return "InBodyTarget{" + + "kind='" + getKind() + "'" + + '}'; } + @Override + public void setKind(@Nullable Kind kind) { + super.setKind(Kind.INBODY); + } } diff --git a/api/src/main/java/ai/docling/api/convert/request/target/PutTarget.java b/api/src/main/java/ai/docling/api/convert/request/target/PutTarget.java index d25ac00..3bfe958 100644 --- a/api/src/main/java/ai/docling/api/convert/request/target/PutTarget.java +++ b/api/src/main/java/ai/docling/api/convert/request/target/PutTarget.java @@ -1,25 +1,88 @@ package ai.docling.api.convert.request.target; +import static ai.docling.api.util.ValidationUtils.ensureNotNull; + import java.net.URI; +import org.jspecify.annotations.Nullable; + import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; /** - * Target for sending the converted document to a specified URI. + * A concrete {@link Target} implementation for delivering a converted document to a specified URI via an HTTP PUT operation. + * + *

The {@link PutTarget} class represents a delivery method where the document is sent to the given {@code URL}. + * Supported serialized fields include: + *

    + *
  • {@code kind} - inherited from the {@link Target} base class and always set to {@code PUT} for this implementation.
  • + *
  • {@code url} - the URI to which the document should be delivered.
  • + *
+ * + *

This class provides JSON serialization annotations to include only non-empty fields in the serialization output. + * The {@code url} is nullable, allowing the absence of a URI to be explicitly represented. + * + *

The {@link #withUrl(URI)} method enables method chaining for setting the {@code url}. */ -@JsonInclude(JsonInclude.Include.NON_NULL) -public record PutTarget(@JsonProperty("kind") Kind kind, @JsonProperty("url") URI url) implements Target { - - public PutTarget { - if (url == null) { - throw new IllegalArgumentException("url cannot be null"); - } - kind = Kind.PUT; +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public final class PutTarget extends Target { + @JsonProperty("url") + private URI url; + + public PutTarget() { + super(); + setKind(Kind.PUT); + } + + /** + * Retrieves the {@code URI} to which the converted document should be delivered. + * + *

The {@code url} represents the target location for sending the document using an HTTP PUT + * operation. It may be {@code null} if no URI has been specified. + * + * @return the {@link URI} representing the document's delivery destination, or {@code null} + * if no target URI is set. + */ + @Nullable + public URI getUrl() { + return url; } - public static PutTarget create(URI url) { - return new PutTarget(Kind.PUT, url); + /** + * Sets the {@code URI} to which the converted document should be delivered. + * + *

The {@code url} specifies the target location for sending the document using an HTTP PUT operation. + * + * @param url the {@link URI} representing the delivery location, or {@code null} to explicitly unset the target URI. + */ + public void setUrl(URI url) { + this.url = ensureNotNull(url, "url"); } + /** + * Sets the {@code URL} to which the converted document should be delivered and returns the current + * {@link PutTarget} instance for method chaining. + * + *

This method allows convenient chaining by setting the {@code url} and returning the same instance. + * + * @param url the {@link URI} representing the delivery location, or {@code null} to explicitly unset the target URI. + * @return the current {@link PutTarget} instance with the updated {@code url}. + */ + public PutTarget withUrl(URI url) { + setUrl(url); + return this; + } + + @Override + public String toString() { + return "PutTarget{" + + "kind='" + getKind() + "'" + + ", url=" + (url == null ? "null" : url.toString()) + + '}'; + } + + @Override + public void setKind(@Nullable Kind kind) { + super.setKind(Kind.PUT); + } } diff --git a/api/src/main/java/ai/docling/api/convert/request/target/Target.java b/api/src/main/java/ai/docling/api/convert/request/target/Target.java index 26ed381..4bf7f80 100644 --- a/api/src/main/java/ai/docling/api/convert/request/target/Target.java +++ b/api/src/main/java/ai/docling/api/convert/request/target/Target.java @@ -1,18 +1,59 @@ package ai.docling.api.convert.request.target; +import org.jspecify.annotations.Nullable; + import com.fasterxml.jackson.annotation.JsonProperty; /** - * Target of the document conversion. + * Represents an abstract target for defining where and how the converted document should be delivered. + * + *

The {@link Target} class is a sealed type that is extended by specific concrete implementations: + * {@link InBodyTarget}, {@link PutTarget}, and {@link ZipTarget}. These implementations specify different + * delivery methods, such as including the document in the response body, sending it to a specified URI, or + * zipping it for inclusion in the response. + * + * @param The specific subtype of {@link Target}, enabling type-safe chaining. */ -public sealed interface Target permits InBodyTarget, PutTarget, ZipTarget { - +public sealed abstract class Target permits InBodyTarget, PutTarget, ZipTarget { enum Kind { - @JsonProperty("inbody") INBODY, @JsonProperty("put") PUT, @JsonProperty("zip") ZIP + } + @JsonProperty("kind") + private Kind kind = Kind.INBODY; + + /** + * Retrieves the {@code Kind} associated with this {@link Target} instance. + * + * @return the {@link Kind} that represents the method of document delivery, + * or {@code null} if no kind is set. + */ + @Nullable + public Kind getKind() { + return kind; + } + + /** + * Sets the {@link Kind} for this {@link Target}. The {@code Kind} specifies the delivery method for + * the converted document, such as including it in the response body, sending it to a URI, or zipping it. + * + * @param kind the {@link Kind} representing the delivery method, or {@code null} to unset the kind. + */ + public void setKind(@Nullable Kind kind) { + this.kind = kind; } + /** + * Sets the {@link Kind} for this {@link Target} instance and returns the current instance for method chaining. + * + * @param kind the {@link Kind} representing the delivery method (e.g., {@code INBODY}, {@code PUT}, {@code ZIP}), + * or {@code null} to unset the kind. + * @return the current instance of {@code T}, enabling method chaining. + */ + public T withKind(@Nullable Kind kind) { + setKind(kind); + return (T) this; + } } diff --git a/api/src/main/java/ai/docling/api/convert/request/target/ZipTarget.java b/api/src/main/java/ai/docling/api/convert/request/target/ZipTarget.java index ce96fca..1061275 100644 --- a/api/src/main/java/ai/docling/api/convert/request/target/ZipTarget.java +++ b/api/src/main/java/ai/docling/api/convert/request/target/ZipTarget.java @@ -1,20 +1,42 @@ package ai.docling.api.convert.request.target; +import org.jspecify.annotations.Nullable; + import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; /** - * Target for zipping the converted document and including it in the response. + * Represents a specific {@link Target} that delivers the converted document as a zipped resource. + * + *

{@code ZipTarget} is one of the concrete implementations of the abstract {@link Target} class. + * It supports the zipped delivery method, denoted by the {@link Kind#ZIP} value. + * + *

This class overrides methods to specialize the behavior for the ZIP delivery kind: + *

    + *
  • {@link #withKind(Kind)}: Returns a {@code ZipTarget} instance with the specified delivery kind.
  • + *
  • {@link #toString()}: Produces a string representation of the {@code ZipTarget} instance.
  • + *
+ * + *

Instances of this class will only include non-empty JSON fields during serialization, + * as governed by {@link JsonInclude.Include#NON_EMPTY}. + * + *

The {@link ZipTarget} instances are immutable and final. */ -@JsonInclude(JsonInclude.Include.NON_NULL) -public record ZipTarget(@JsonProperty("kind") Target.Kind kind) implements Target { - - public ZipTarget { - kind = Target.Kind.ZIP; +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public final class ZipTarget extends Target { + public ZipTarget() { + super(); + setKind(Kind.ZIP); } - public static ZipTarget create() { - return new ZipTarget(Target.Kind.ZIP); + @Override + public String toString() { + return "ZipTarget{" + + "kind='" + getKind() + "'" + + '}'; } + @Override + public void setKind(@Nullable Kind kind) { + super.setKind(Kind.ZIP); + } } diff --git a/api/src/main/java/ai/docling/api/convert/response/ConvertDocumentResponse.java b/api/src/main/java/ai/docling/api/convert/response/ConvertDocumentResponse.java index 5053548..ff5df06 100644 --- a/api/src/main/java/ai/docling/api/convert/response/ConvertDocumentResponse.java +++ b/api/src/main/java/ai/docling/api/convert/response/ConvertDocumentResponse.java @@ -1,6 +1,7 @@ package ai.docling.api.convert.response; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -10,28 +11,185 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -@JsonInclude(JsonInclude.Include.NON_NULL) -public record ConvertDocumentResponse( +/** + * Response returned by the Convert API for a single conversion request. + * + *

Serialization uses {@link JsonInclude.Include#NON_EMPTY}, so nulls and empty + * collections/strings are omitted from JSON output.

+ */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class ConvertDocumentResponse { - @JsonProperty("document") DocumentResponse document, + @JsonProperty("document") + private DocumentResponse document; - @JsonProperty("errors") @Nullable List errors, + @JsonProperty("errors") + private List errors = new ArrayList<>(); - @JsonProperty("processing_time") Double processingTime, + @JsonProperty("processing_time") + private Double processingTime; - @JsonProperty("status") String status, + @JsonProperty("status") + private String status; - @JsonProperty("timings") @Nullable Map timings + @JsonProperty("timings") + private Map timings = new HashMap<>(); -) { + // Getters/setters + /** + * The converted document payload. + * @return document or {@code null} + */ + @Nullable + public DocumentResponse getDocument() { + return document; + } + + /** + * Sets the converted document payload. + * @param document document; may be {@code null} + */ + public void setDocument(@Nullable DocumentResponse document) { + this.document = document; + } + + /** + * Fluent setter for {@link #setDocument(DocumentResponse)}. + * @param document document; may be {@code null} + * @return this instance for chaining + */ + public ConvertDocumentResponse withDocument(@Nullable DocumentResponse document) { + setDocument(document); + return this; + } + + /** + * List of errors, if any. When non-null, returned as an unmodifiable view. + * @return errors list or {@code null} + */ + public List getErrors() { + return Collections.unmodifiableList(errors); + } + + /** + * Sets the errors list; defensively copies when non-null. + * @param errors list of errors; may be {@code null} + */ + public void setErrors(@Nullable List errors) { + this.errors.clear(); - public ConvertDocumentResponse { if (errors != null) { - errors = new ArrayList<>(errors); + this.errors.addAll(errors); } + } + + /** + * Fluent setter for {@link #setErrors(List)}. + * @param errors list of errors; may be {@code null} + * @return this instance for chaining + */ + public ConvertDocumentResponse withErrors(@Nullable List errors) { + setErrors(errors); + return this; + } + + /** + * Total processing time in seconds. + * @return processing time or {@code null} + */ + @Nullable + public Double getProcessingTime() { + return processingTime; + } + + /** + * Sets the processing time in seconds. + * @param processingTime processing time; may be {@code null} + */ + public void setProcessingTime(@Nullable Double processingTime) { + this.processingTime = processingTime; + } + + /** + * Fluent setter for {@link #setProcessingTime(Double)}. + * @param processingTime processing time; may be {@code null} + * @return this instance for chaining + */ + public ConvertDocumentResponse withProcessingTime(@Nullable Double processingTime) { + setProcessingTime(processingTime); + return this; + } + + /** + * Status string of the conversion. + * @return status or {@code null} + */ + @Nullable + public String getStatus() { + return status; + } + + /** + * Sets the status string of the conversion. + * @param status status; may be {@code null} + */ + public void setStatus(@Nullable String status) { + this.status = status; + } + + /** + * Fluent setter for {@link #setStatus(String)}. + * @param status status; may be {@code null} + * @return this instance for chaining + */ + public ConvertDocumentResponse withStatus(@Nullable String status) { + setStatus(status); + return this; + } + + /** + * Timings map with detailed timing information. When non-null, returned as an + * unmodifiable view. + * @return timings or {@code null} + */ + @Nullable + public Map getTimings() { + return Collections.unmodifiableMap(timings); + } + + /** + * Sets the timings map; defensively copies when non-null. + * @param timings timings; may be {@code null} + */ + public void setTimings(@Nullable Map timings) { + this.timings.clear(); + if (timings != null) { - timings = new HashMap<>(timings); + this.timings.putAll(timings); } } + /** + * Fluent setter for {@link #setTimings(Map)}. + * @param timings timings; may be {@code null} + * @return this instance for chaining + */ + public ConvertDocumentResponse withTimings(@Nullable Map timings) { + setTimings(timings); + return this; + } + + /** + * Returns a string representation with actual values. + */ + @Override + public String toString() { + return "ConvertDocumentResponse{" + + "document=" + (document == null ? "null" : document.toString()) + + ", errors=" + (errors == null ? "null" : errors.toString()) + + ", processingTime=" + (processingTime == null ? "null" : processingTime.toString()) + + ", status=" + (status == null ? "null" : "'" + status + "'") + + ", timings=" + (timings == null ? "null" : timings.toString()) + + '}'; + } } diff --git a/api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java b/api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java index 48a920b..ff25fc7 100644 --- a/api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java +++ b/api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java @@ -1,269 +1,258 @@ package ai.docling.api.convert.response; +import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.Optional; import org.jspecify.annotations.Nullable; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@tools.jackson.databind.annotation.JsonDeserialize(builder = DocumentResponse.Builder.class) -@com.fasterxml.jackson.databind.annotation.JsonDeserialize(builder = DocumentResponse.Builder.class) -public interface DocumentResponse { +/** + * A simple mutable POJO representing the converted document returned by the + * Docling Convert API. The properties map 1:1 to the JSON payload using the + * {@link JsonProperty} names declared on each field. + * + *

Serialization is configured with {@link JsonInclude.Include#NON_EMPTY}, + * meaning nulls, empty strings, and empty collections/maps are omitted from the + * serialized JSON.

+ * + *

Notes on mutability and maps:

+ *
    + *
  • {@code jsonContent} defaults to an empty map and is never exposed as + * {@code null}. The getter returns an unmodifiable view.
  • + *
  • When setting {@code jsonContent} with a non-null map, the reference is + * preserved; subsequent external modifications to the provided map are + * observable via {@link #getJsonContent()}.
  • + *
+ */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class DocumentResponse { + + @JsonProperty("doctags_content") + private String doctagsContent; + + @JsonProperty("filename") + private String filename; + + @JsonProperty("html_content") + private String htmlContent; + + @JsonProperty("json_content") + private Map jsonContent = new HashMap<>(); + + @JsonProperty("md_content") + private String markdownContent; + + @JsonProperty("text_content") + private String textContent; + + // Getters and setters /** - * Retrieves the content of the doc tags, if available. + * Returns the DocTags representation of the document, if available. * - * @return the content of the doc tags, or null if not present + * @return doctags content, or {@code null} if not present */ @Nullable - String doctagsContent(); + public String getDoctagsContent() { + return doctagsContent; + } /** - * Retrieves the filename associated with the document. + * Sets the DocTags representation of the document. * - * @return the filename of the document as a string + * @param doctagsContent the DocTags content; may be {@code null} */ - String filename(); + public void setDoctagsContent(@Nullable String doctagsContent) { + this.doctagsContent = doctagsContent; + } /** - * Retrieves the HTML content associated with the document, if available. + * Returns the original filename of the processed document. * - * @return the HTML content as a string, or null if not present + * @return filename, or {@code null} if unknown */ @Nullable - String htmlContent(); + public String getFilename() { + return filename; + } /** - * Retrieves the JSON content associated with the document. + * Sets the original filename of the processed document. * - * @return a map representing the JSON content, or an empty map if no JSON content is present + * @param filename the filename; may be {@code null} */ - Map jsonContent(); + public void setFilename(@Nullable String filename) { + this.filename = filename; + } /** - * Retrieves the Markdown content associated with the document, if available. + * Returns the HTML content produced by the conversion. * - * @return the Markdown content as a string, or null if no Markdown content is present + * @return HTML content, or {@code null} if not produced */ @Nullable - String markdownContent(); + public String getHtmlContent() { + return htmlContent; + } /** - * Retrieves the plain text content associated with the document, if available. + * Sets the HTML content produced by the conversion. * - * @return the plain text content as a string, or null if no text content is present + * @param htmlContent the HTML content; may be {@code null} */ - @Nullable - String textContent(); + public void setHtmlContent(@Nullable String htmlContent) { + this.htmlContent = htmlContent; + } /** - * Creates a new {@code Builder} instance initialized with the current state of the {@code DocumentResponse}. + * Returns an unmodifiable view of the JSON content map. Never {@code null}. + * When set via {@link #setJsonContent(Map)}, the provided map reference is + * preserved (if non-null), so external changes to that map will be reflected + * here. * - * @return a {@code Builder} instance populated with the values from this {@code DocumentResponse} + * @return unmodifiable view of the JSON content map (possibly empty) */ - default Builder toBuilder() { - return new Builder(this); + public Map getJsonContent() { + return Collections.unmodifiableMap(jsonContent); } /** - * Creates and returns a new instance of the {@code Builder} class, which can be used to - * construct a {@code DocumentResponse} object in a step-by-step manner. + * Sets the JSON content map. If {@code jsonContent} is {@code null}, an empty + * map is assigned. Otherwise, the provided map reference is preserved so that + * subsequent modifications to the same map are visible via + * {@link #getJsonContent()}. * - * @return a new {@code Builder} instance + * @param jsonContent the JSON content map; may be {@code null} */ - static Builder builder() { - return new Builder(); + public void setJsonContent(@Nullable Map jsonContent) { + this.jsonContent.clear(); + + if (jsonContent != null) { + this.jsonContent.putAll(jsonContent); + } } /** - * Default implementation of the {@link DocumentResponse} interface. - * This record represents the response containing document data in various formats. - * It is an immutable data structure that consolidates information related to a document, - * such as its filename, content in multiple formats, and metadata. + * Returns the Markdown content produced by the conversion. * - * Each instance ensures the provided JSON content is unmodifiable by copying - * the input map if it is present, or initializing it to an empty map otherwise. + * @return Markdown content, or {@code null} if not produced */ - record DefaultDocumentResponse(String doctagsContent, - String filename, - String htmlContent, - Map jsonContent, - String markdownContent, - String textContent) implements DocumentResponse { - - public DefaultDocumentResponse { - jsonContent = Optional.ofNullable(jsonContent) - .map(Map::copyOf) - .orElseGet(Map::of); - } - - public DefaultDocumentResponse(Builder builder) { - this(builder.doctagsContent, - builder.filename, - builder.htmlContent, - builder.jsonContent, - builder.markdownContent, - builder.textContent); - } + @Nullable + public String getMarkdownContent() { + return markdownContent; } /** - * A builder class for constructing instances of {@code DocumentResponse}. - * - * This class provides a step-by-step approach to configure and create a - * {@code DocumentResponse} object. Each method in this class sets a specific - * property of the object being built. Once all the desired properties are set, - * the {@code build} method is used to create the final {@code DocumentResponse} - * instance. - * - * The builder supports customization of various document-related attributes, - * including doc tags content, filename, HTML content, JSON content, Markdown - * content, and plain text content. + * Sets the Markdown content produced by the conversion. * - * By default, the builder initializes attributes with an empty state or default - * values. If a {@code DocumentResponse} instance is provided to the constructor, - * the builder is pre-populated with the attributes from the given response. - * - * This class is intended for internal use and is protected to restrict its - * accessibility outside the defining package or class hierarchy. + * @param markdownContent the Markdown content; may be {@code null} */ - @tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "") - @com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "") - class Builder { - protected String doctagsContent; - protected String filename; - protected String htmlContent; - protected Map jsonContent = new HashMap<>(); - protected String markdownContent; - protected String textContent; - - /** - * Constructs a new {@code Builder} instance. - * - * This constructor initializes a builder with default or empty states for all - * attributes. It is protected to restrict direct instantiation outside of the - * defining package or class hierarchy. - * - * The {@code Builder} class is primarily used to facilitate the creation of - * {@code DocumentResponse} objects through a step-by-step configuration process. - */ - protected Builder() { + public void setMarkdownContent(@Nullable String markdownContent) { + this.markdownContent = markdownContent; + } - } + /** + * Returns the plain text content produced by the conversion. + * + * @return plain text content, or {@code null} if not produced + */ + @Nullable + public String getTextContent() { + return textContent; + } - /** - * Constructs a new {@code Builder} instance using the provided {@code DocumentResponse}. - * - * This constructor initializes the builder's fields with the data from the given - * {@code DocumentResponse} object. It allows for the creation of a {@code Builder} - * instance pre-populated with the state of an existing {@code DocumentResponse}. - * - * @param documentResponse the {@code DocumentResponse} instance whose data will - * populate the fields of this builder - */ - protected Builder(DocumentResponse documentResponse) { - this.doctagsContent = documentResponse.doctagsContent(); - this.filename = documentResponse.filename(); - this.htmlContent = documentResponse.htmlContent(); - this.jsonContent = documentResponse.jsonContent(); - this.markdownContent = documentResponse.markdownContent(); - this.textContent = documentResponse.textContent(); - } + /** + * Sets the plain text content produced by the conversion. + * + * @param textContent the text content; may be {@code null} + */ + public void setTextContent(@Nullable String textContent) { + this.textContent = textContent; + } - /** - * Sets the doctags content for the builder instance. - * - * @param doctagsContent the doctags content to be set - * @return this Builder instance for method chaining - */ - @JsonProperty("doctags_content") - public Builder doctagsContent(String doctagsContent) { - this.doctagsContent = doctagsContent; - return this; - } + // Fluent builder-style methods + /** + * Fluent setter for {@link #setDoctagsContent(String)}. + * + * @param doctagsContent the DocTags content; may be {@code null} + * @return this instance for chaining + */ + public DocumentResponse withDoctagsContent(@Nullable String doctagsContent) { + setDoctagsContent(doctagsContent); + return this; + } - /** - * Sets the filename for the builder instance. - * - * @param filename the filename to be set - * @return this Builder instance for method chaining - */ - @JsonProperty("filename") - public Builder filename(String filename) { - this.filename = filename; - return this; - } + /** + * Fluent setter for {@link #setFilename(String)}. + * + * @param filename the filename; may be {@code null} + * @return this instance for chaining + */ + public DocumentResponse withFilename(@Nullable String filename) { + setFilename(filename); + return this; + } - /** - * Sets the HTML content for the builder instance. - * - * @param htmlContent the HTML content to be set - * @return this Builder instance for method chaining - */ - @JsonProperty("html_content") - public Builder htmlContent(String htmlContent) { - this.htmlContent = htmlContent; - return this; - } + /** + * Fluent setter for {@link #setHtmlContent(String)}. + * + * @param htmlContent the HTML content; may be {@code null} + * @return this instance for chaining + */ + public DocumentResponse withHtmlContent(@Nullable String htmlContent) { + setHtmlContent(htmlContent); + return this; + } - /** - * Sets the JSON content for the builder instance. - * - * The JSON content is represented as a map of key-value pairs, where the keys - * are {@code String} objects, and the values are {@code Object} instances. - * - * @param jsonContent the JSON content to be set, represented as a {@code Map} - * @return this {@link Builder} instance for method chaining - */ - @JsonProperty("json_content") - public Builder jsonContent(Map jsonContent) { - this.jsonContent = jsonContent; - return this; - } + /** + * Fluent setter for {@link #setJsonContent(Map)}. + * + * @param jsonContent the JSON content map; may be {@code null} + * @return this instance for chaining + */ + public DocumentResponse withJsonContent(@Nullable Map jsonContent) { + setJsonContent(jsonContent); + return this; + } - /** - * Sets the Markdown content for this builder instance. - * - * The Markdown content represents the textual data formatted in Markdown syntax, - * which can include headings, lists, links, and other Markdown elements. - * - * @param markdownContent the Markdown content to be set, represented as a {@code String} - * @return this {@link Builder} instance for method chaining - */ - @JsonProperty("md_content") - public Builder markdownContent(String markdownContent) { - this.markdownContent = markdownContent; - return this; - } + /** + * Fluent setter for {@link #setMarkdownContent(String)}. + * + * @param markdownContent the Markdown content; may be {@code null} + * @return this instance for chaining + */ + public DocumentResponse withMarkdownContent(@Nullable String markdownContent) { + setMarkdownContent(markdownContent); + return this; + } - /** - * Sets the plain text content for this builder instance. - * - * The plain text content represents unformatted textual data that can be - * used for display or processing purposes within the application. - * - * @param textContent the plain text content to be set, represented as a {@code String} - * @return this {@link Builder} instance for method chaining - */ - @JsonProperty("text_content") - public Builder textContent(String textContent) { - this.textContent = textContent; - return this; - } + /** + * Fluent setter for {@link #setTextContent(String)}. + * + * @param textContent the text content; may be {@code null} + * @return this instance for chaining + */ + public DocumentResponse withTextContent(@Nullable String textContent) { + setTextContent(textContent); + return this; + } - /** - * Creates and returns a {@link DocumentResponse} instance based on the current state of this {@link Builder}. - * - *

The returned {@link DocumentResponse} will encapsulate the values configured in the builder, - * and further modifications to the builder instance will not affect the created {@code DocumentResponse}. - * - * @return a new {@code DocumentResponse} instance constructed from the builder's state - */ - public DocumentResponse build() { - return new DefaultDocumentResponse(this); - } + /** + * Returns a string representation containing the actual values of all fields. + * + * @return string representation of this response + */ + @Override + public String toString() { + return "DocumentResponse{" + + "doctagsContent=" + (doctagsContent == null ? "null" : "'" + doctagsContent + "'") + + ", filename=" + (filename == null ? "null" : "'" + filename + "'") + + ", htmlContent=" + (htmlContent == null ? "null" : "'" + htmlContent + "'") + + ", jsonContent=" + (jsonContent == null ? "null" : jsonContent.toString()) + + ", markdownContent=" + (markdownContent == null ? "null" : "'" + markdownContent + "'") + + ", textContent=" + (textContent == null ? "null" : "'" + textContent + "'") + + '}'; } } diff --git a/api/src/main/java/ai/docling/api/convert/response/ErrorItem.java b/api/src/main/java/ai/docling/api/convert/response/ErrorItem.java index cb4b927..363496f 100644 --- a/api/src/main/java/ai/docling/api/convert/response/ErrorItem.java +++ b/api/src/main/java/ai/docling/api/convert/response/ErrorItem.java @@ -1,16 +1,127 @@ package ai.docling.api.convert.response; +import org.jspecify.annotations.Nullable; + import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -@JsonInclude(JsonInclude.Include.NON_NULL) -public record ErrorItem( +/** + * Represents an individual error produced during document conversion. + * + *

Serialization uses {@link JsonInclude.Include#NON_EMPTY}, so nulls and empty + * strings are omitted from JSON output.

+ */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class ErrorItem { + + @JsonProperty("component_type") + private String componentType; + + @JsonProperty("error_message") + private String errorMessage; + + @JsonProperty("module_name") + private String moduleName; + + /** + * Returns the component type that produced the error. + * + * @return component type or {@code null} + */ + @Nullable + public String getComponentType() { + return componentType; + } + + /** + * Sets the component type that produced the error. + * + * @param componentType component type; may be {@code null} + */ + public void setComponentType(@Nullable String componentType) { + this.componentType = componentType; + } + + /** + * Fluent setter for {@link #setComponentType(String)}. + * + * @param componentType component type; may be {@code null} + * @return this for chaining + */ + public ErrorItem withComponentType(@Nullable String componentType) { + setComponentType(componentType); + return this; + } + + /** + * Returns the human-readable error message. + * + * @return error message or {@code null} + */ + @Nullable + public String getErrorMessage() { + return errorMessage; + } + + /** + * Sets the human-readable error message. + * + * @param errorMessage error message; may be {@code null} + */ + public void setErrorMessage(@Nullable String errorMessage) { + this.errorMessage = errorMessage; + } + + /** + * Fluent setter for {@link #setErrorMessage(String)}. + * + * @param errorMessage error message; may be {@code null} + * @return this for chaining + */ + public ErrorItem withErrorMessage(@Nullable String errorMessage) { + setErrorMessage(errorMessage); + return this; + } - @JsonProperty("component_type") String componentType, + /** + * Returns the module name where the error originated. + * + * @return module name or {@code null} + */ + @Nullable + public String getModuleName() { + return moduleName; + } - @JsonProperty("error_message") String errorMessage, + /** + * Sets the module name where the error originated. + * + * @param moduleName module name; may be {@code null} + */ + public void setModuleName(@Nullable String moduleName) { + this.moduleName = moduleName; + } - @JsonProperty("module_name") String moduleName + /** + * Fluent setter for {@link #setModuleName(String)}. + * + * @param moduleName module name; may be {@code null} + * @return this for chaining + */ + public ErrorItem withModuleName(@Nullable String moduleName) { + setModuleName(moduleName); + return this; + } -) { + /** + * Returns a string representation with actual field values. + */ + @Override + public String toString() { + return "ErrorItem{" + + "componentType=" + (componentType == null ? "null" : "'" + componentType + "'") + + ", errorMessage=" + (errorMessage == null ? "null" : "'" + errorMessage + "'") + + ", moduleName=" + (moduleName == null ? "null" : "'" + moduleName + "'") + + '}'; + } } diff --git a/api/src/main/java/ai/docling/api/core/package-info.java b/api/src/main/java/ai/docling/api/core/package-info.java deleted file mode 100644 index 821f2ec..0000000 --- a/api/src/main/java/ai/docling/api/core/package-info.java +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Docling Core APIs. - */ -@NullMarked -package ai.docling.api.core; - -import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/ai/docling/api/health/HealthCheckResponse.java b/api/src/main/java/ai/docling/api/health/HealthCheckResponse.java index ad77711..5eef555 100644 --- a/api/src/main/java/ai/docling/api/health/HealthCheckResponse.java +++ b/api/src/main/java/ai/docling/api/health/HealthCheckResponse.java @@ -5,10 +5,49 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -@JsonInclude(JsonInclude.Include.NON_NULL) -public record HealthCheckResponse( +/** + * Simple health check response returned by the API. + * + *

Serialization uses {@link JsonInclude.Include#NON_EMPTY} so nulls and empty strings + * are omitted from JSON.

+ */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class HealthCheckResponse { - @JsonProperty("status") @Nullable String status + @JsonProperty("status") + private String status; -) { + /** + * Returns the health status string. + * @return status or {@code null} + */ + @Nullable + public String getStatus() { + return status; + } + + /** + * Sets the health status string. + * @param status may be {@code null} + */ + public void setStatus(@Nullable String status) { + this.status = status; + } + + /** + * Fluent setter for {@link #setStatus(String)}. + * @param status may be {@code null} + * @return this instance for chaining + */ + public HealthCheckResponse withStatus(@Nullable String status) { + setStatus(status); + return this; + } + + @Override + public String toString() { + return "HealthCheckResponse{" + + "status=" + (status == null ? "null" : "'" + status + "'") + + '}'; + } } diff --git a/api/src/test/java/ai/docling/api/convert/request/ConvertDocumentRequestTests.java b/api/src/test/java/ai/docling/api/convert/request/ConvertDocumentRequestTests.java index a3e14bf..6e6d4ef 100644 --- a/api/src/test/java/ai/docling/api/convert/request/ConvertDocumentRequestTests.java +++ b/api/src/test/java/ai/docling/api/convert/request/ConvertDocumentRequestTests.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.InstanceOfAssertFactories.type; import java.net.URI; import java.util.ArrayList; @@ -19,99 +20,90 @@ * Unit tests for {@link ConvertDocumentRequest}. */ class ConvertDocumentRequestTests { - - @Test - void whenSourcesIsNullThenThrow() { - assertThatThrownBy(() -> new ConvertDocumentRequest(null, ConvertDocumentOptions.builder().build(), null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("sources cannot be null or empty"); - } - - @Test - void whenSourcesIsEmptyThenThrow() { - assertThatThrownBy(() -> new ConvertDocumentRequest(List.of(), ConvertDocumentOptions.builder().build(), null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("sources cannot be null or empty"); - } - @Test void whenOptionsIsNullThenThrow() { - assertThatThrownBy(() -> new ConvertDocumentRequest(List.of(HttpSource.from("http://example.com")), null, null)) + var uri = URI.create("http://example.com"); + var httpSource = new HttpSource().withUrl(uri); + + assertThatThrownBy(() -> new ConvertDocumentRequest().withSources(List.of(httpSource)).withOptions(null)) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("options cannot be null"); } @Test void buildWithHttpSourcesAsList() { - ConvertDocumentRequest request = ConvertDocumentRequest.builder() - .httpSources(List.of(HttpSource.from("http://example.com"))) - .target(InBodyTarget.create()) - .build(); - assertThat(request.sources()).hasSize(1); - assertThat(request.sources().get(0)).isInstanceOf(HttpSource.class); + ConvertDocumentRequest request = new ConvertDocumentRequest() + .withSources(List.of(new HttpSource().withUrl(URI.create("http://example.com")))) + .withTarget(new InBodyTarget()); + assertThat(request.getSources()).hasSize(1); + assertThat(request.getSources().get(0)).isInstanceOf(HttpSource.class); } @Test void buildWithHttpSourcesAsVarargs() { - ConvertDocumentRequest request = ConvertDocumentRequest.builder() - .addHttpSources(URI.create("http://example.com"), URI.create("http://example.org")) - .build(); - assertThat(request.sources()).hasSize(2); - assertThat(request.sources().get(0)).isInstanceOf(HttpSource.class); - assertThat(request.sources().get(1)).isInstanceOf(HttpSource.class); + ConvertDocumentRequest request = new ConvertDocumentRequest() + .withSources(List.of(new HttpSource().withUrl(URI.create("http://example.com")), new HttpSource().withUrl(URI.create("http://example.org")))); + + assertThat(request.getSources()) + .hasSize(2) + .allSatisfy(source -> assertThat(source).isInstanceOf(HttpSource.class)); } @Test void buildWithFileSourcesAsList() { - ConvertDocumentRequest request = ConvertDocumentRequest.builder() - .fileSources(List.of(FileSource.from("file:///path/to/file.txt", "content"))) - .build(); - assertThat(request.sources()).hasSize(1); - assertThat(request.sources().get(0)).isInstanceOf(FileSource.class); + ConvertDocumentRequest request = new ConvertDocumentRequest() + .withSources(List.of(new FileSource().withFilename("file:///path/to/file.txt").withBase64String("content"))); + + assertThat(request.getSources()) + .hasSize(1) + .allSatisfy(source -> assertThat(source).isInstanceOf(FileSource.class)); } @Test void buildWithFileSourcesAsVarargs() { - ConvertDocumentRequest request = ConvertDocumentRequest.builder() - .addFileSources("file1.txt", "base64string1") - .addFileSources("file2.txt", "base64string2") - .target(ZipTarget.create()) - .build(); - assertThat(request.sources()).hasSize(2); - assertThat(request.sources().get(0)).isInstanceOf(FileSource.class); - assertThat(request.sources().get(1)).isInstanceOf(FileSource.class); + ConvertDocumentRequest request = new ConvertDocumentRequest() + .withSources(List.of( + new FileSource().withFilename("file1.txt").withBase64String("base64string1"), + new FileSource().withFilename("file2.txt").withBase64String("base64string2") + ) + ) + .withTarget(new ZipTarget()); + + assertThat(request.getSources()) + .hasSize(2) + .allSatisfy(source -> assertThat(source).isInstanceOf(FileSource.class)); } @Test void whenMixedSourcesThenThrow() { - assertThatThrownBy(() -> ConvertDocumentRequest.builder() - .httpSources(List.of(HttpSource.from("http://example.com"))) - .addFileSources("file.txt", "base64string") - .build()) + var sources = List.of( + new HttpSource().withUrl(URI.create("http://example.com")), + new FileSource().withFilename("file.txt").withBase64String("base64string") + ); + assertThatThrownBy(() -> new ConvertDocumentRequest().withSources(sources)) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("All sources must be of the same type (HttpSource or FileSource)"); } @Test void convertDocumentRequestIsImmutable() { - List sources = new ArrayList<>(List.of(FileSource.from("test.txt", "dGVzdCBjb250ZW50"))); + List sources = new ArrayList<>(List.of(new FileSource().withFilename("test.txt").withBase64String("dGVzdCBjb250ZW50"))); - ConvertDocumentRequest request = ConvertDocumentRequest.builder() - .fileSources(sources) - .options(ConvertDocumentOptions.builder() - .doOcr(true) - .build()) - .target(InBodyTarget.create()) - .build(); + ConvertDocumentRequest request = new ConvertDocumentRequest() + .withSources(sources) + .withOptions(new ConvertDocumentOptions().withDoOcr(true)) + .withTarget(new InBodyTarget()); - assertThat(request.sources()).isEqualTo(sources); + assertThat(request.getSources()) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrderElementsOf(sources); - sources.add(FileSource.from("changed.txt", "Y2hhbmdlZA==")); + sources.add(new FileSource().withFilename("changed.txt").withBase64String("Y2hhbmdlZA==")); - assertThat(request.sources()).hasSize(1); - FileSource originalFileSource = (FileSource) request.sources().get(0); - assertThat(originalFileSource.filename()).isEqualTo("test.txt"); - assertThat(originalFileSource.base64String()).isEqualTo("dGVzdCBjb250ZW50"); + assertThat(request.getSources()) + .singleElement() + .asInstanceOf(type(FileSource.class)) + .extracting(FileSource::getFilename, FileSource::getBase64String) + .containsExactly("test.txt", "dGVzdCBjb250ZW50"); } - } diff --git a/api/src/test/java/ai/docling/api/convert/request/options/ConvertDocumentOptionsTests.java b/api/src/test/java/ai/docling/api/convert/request/options/ConvertDocumentOptionsTests.java index c818d77..3ab61eb 100644 --- a/api/src/test/java/ai/docling/api/convert/request/options/ConvertDocumentOptionsTests.java +++ b/api/src/test/java/ai/docling/api/convert/request/options/ConvertDocumentOptionsTests.java @@ -11,39 +11,31 @@ * Unit tests for {@link ConvertDocumentOptions}. */ class ConvertDocumentOptionsTests { - - @Test - void builder() { - var options = ConvertDocumentOptions.builder().build(); - assertThat(options).isNotNull(); - } - @Test void convertDocumentOptionsIsImmutable() { List fromFormats = new ArrayList<>(List.of(InputFormat.PDF)); List toFormats = new ArrayList<>(List.of(OutputFormat.MARKDOWN)); List ocrLang = new ArrayList<>(List.of("en", "de")); - ConvertDocumentOptions options = ConvertDocumentOptions.builder() - .fromFormats(fromFormats) - .toFormats(toFormats) - .ocrLang(ocrLang) - .build(); + ConvertDocumentOptions options = new ConvertDocumentOptions() + .withFromFormats(fromFormats) + .withToFormats(toFormats) + .withOcrLang(ocrLang); - assertThat(options.fromFormats()).isEqualTo(fromFormats); - assertThat(options.toFormats()).isEqualTo(toFormats); - assertThat(options.ocrLang()).isEqualTo(ocrLang); + assertThat(options.getFromFormats()).isEqualTo(fromFormats); + assertThat(options.getToFormats()).isEqualTo(toFormats); + assertThat(options.getOcrLang()).containsExactlyInAnyOrderElementsOf(ocrLang); fromFormats.add(InputFormat.DOCX); toFormats.add(OutputFormat.JSON); ocrLang.add("fr"); - assertThat(options.fromFormats()).hasSize(1); - assertThat(options.fromFormats().get(0)).isEqualTo(InputFormat.PDF); - assertThat(options.toFormats()).hasSize(1); - assertThat(options.toFormats().get(0)).isEqualTo(OutputFormat.MARKDOWN); - assertThat(options.ocrLang()).hasSize(2); - assertThat(options.ocrLang()).containsExactly("en", "de"); + assertThat(options.getFromFormats()).hasSize(1); + assertThat(options.getFromFormats().get(0)).isEqualTo(InputFormat.PDF); + assertThat(options.getToFormats()).hasSize(1); + assertThat(options.getToFormats().get(0)).isEqualTo(OutputFormat.MARKDOWN); + assertThat(options.getOcrLang()).hasSize(2); + assertThat(options.getOcrLang()).containsExactly("en", "de"); } } diff --git a/api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionApiTests.java b/api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionApiTests.java index fae9de4..aed4677 100644 --- a/api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionApiTests.java +++ b/api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionApiTests.java @@ -24,48 +24,39 @@ void createApiWithAllFields() { String prompt = "Describe this image in detail"; Integer concurrency = 2; - PictureDescriptionApi api = new PictureDescriptionApi( - url, - headers, - params, - timeout, - concurrency, - prompt - ); - - assertThat(api.url()).isEqualTo(url); - assertThat(api.headers()).isEqualTo(headers); - assertThat(api.params()).isEqualTo(params); - assertThat(api.timeout()).isEqualTo(timeout); - assertThat(api.concurrency()).isEqualTo(concurrency); - assertThat(api.prompt()).isEqualTo(prompt); + PictureDescriptionApi api = new PictureDescriptionApi() + .withUrl(url) + .withHeaders(headers) + .withParams(params) + .withTimeout(timeout) + .withConcurrency(concurrency) + .withPrompt(prompt); + + assertThat(api.getUrl()).isEqualTo(url); + assertThat(api.getHeaders()).isEqualTo(headers); + assertThat(api.getParams()).isEqualTo(params); + assertThat(api.getTimeout()).isEqualTo(timeout); + assertThat(api.getConcurrency()).isEqualTo(concurrency); + assertThat(api.getPrompt()).isEqualTo(prompt); } @Test void createApiWithOnlyRequiredFields() { URI url = URI.create("https://api.example.com/vision"); - PictureDescriptionApi api = PictureDescriptionApi.builder() - .url(url) - .build(); + PictureDescriptionApi api = new PictureDescriptionApi() + .withUrl(url); - assertThat(api.url()).isEqualTo(url); - assertThat(api.headers()).isNull(); - assertThat(api.params()).isNull(); - assertThat(api.timeout()).isNull(); - assertThat(api.prompt()).isNull(); + assertThat(api.getUrl()).isEqualTo(url); + assertThat(api.getHeaders()).isEmpty(); + assertThat(api.getParams()).isEmpty(); + assertThat(api.getTimeout()).isNull(); + assertThat(api.getPrompt()).isNull(); } @Test void createApiWithNullUrlThrowsException() { - assertThatThrownBy(() -> new PictureDescriptionApi( - null, - null, - null, - null, - null, - null - )) + assertThatThrownBy(() -> new PictureDescriptionApi().withUrl(null)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("url cannot be null"); } @@ -76,25 +67,24 @@ void pictureDescriptionApiIsImmutable() { Map headers = new HashMap<>(Map.of("Authorization", "Bearer original")); Map params = new HashMap<>(Map.of("model", "original-model")); - PictureDescriptionApi api = PictureDescriptionApi.builder() - .url(url) - .headers(headers) - .params(params) - .timeout(Duration.ofSeconds(10)) - .concurrency(3) - .prompt("Original prompt") - .build(); + PictureDescriptionApi api = new PictureDescriptionApi() + .withUrl(url) + .withHeaders(headers) + .withParams(params) + .withTimeout(Duration.ofSeconds(10)) + .withConcurrency(3) + .withPrompt("Original prompt"); - assertThat(api.headers()).isEqualTo(headers); - assertThat(api.params()).isEqualTo(params); + assertThat(api.getHeaders()).isEqualTo(headers); + assertThat(api.getParams()).isEqualTo(params); headers.put("X-Custom-Header", "modified"); params.put("temperature", 0.8); - assertThat(api.headers()).hasSize(1); - assertThat(api.headers().get("Authorization")).isEqualTo("Bearer original"); - assertThat(api.params()).hasSize(1); - assertThat(api.params().get("model")).isEqualTo("original-model"); + assertThat(api.getHeaders()).hasSize(1); + assertThat(api.getHeaders().get("Authorization")).isEqualTo("Bearer original"); + assertThat(api.getParams()).hasSize(1); + assertThat(api.getParams().get("model")).isEqualTo("original-model"); } } diff --git a/api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionLocalTests.java b/api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionLocalTests.java index 4eb39ba..84a5b98 100644 --- a/api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionLocalTests.java +++ b/api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionLocalTests.java @@ -7,6 +7,9 @@ import java.util.Map; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; /** * Unit tests for {@link PictureDescriptionLocal}. @@ -19,61 +22,33 @@ void createLocalWithAllFields() { String prompt = "Describe this image in detail"; Map generationConfig = Map.of("max_length", 100, "temperature", 0.7); - PictureDescriptionLocal local = new PictureDescriptionLocal( - repoId, - prompt, - generationConfig - ); + PictureDescriptionLocal local = new PictureDescriptionLocal() + .withRepoId(repoId) + .withPrompt(prompt) + .withGenerationConfig(generationConfig); - assertThat(local.repoId()).isEqualTo(repoId); - assertThat(local.prompt()).isEqualTo(prompt); - assertThat(local.generationConfig()).isEqualTo(generationConfig); + assertThat(local.getRepoId()).isEqualTo(repoId); + assertThat(local.getPrompt()).isEqualTo(prompt); + assertThat(local.getGenerationConfig()).isEqualTo(generationConfig); } @Test void createLocalWithOnlyRequiredFields() { String repoId = "microsoft/Florence-2-large"; - PictureDescriptionLocal local = new PictureDescriptionLocal( - repoId, - null, - null - ); + PictureDescriptionLocal local = new PictureDescriptionLocal() + .withRepoId(repoId); - assertThat(local.repoId()).isEqualTo(repoId); - assertThat(local.prompt()).isNull(); - assertThat(local.generationConfig()).isNull(); + assertThat(local.getRepoId()).isEqualTo(repoId); + assertThat(local.getPrompt()).isNull(); + assertThat(local.getGenerationConfig()).isNotNull().isEmpty(); } - @Test - void createLocalWithNullRepoIdThrowsException() { - assertThatThrownBy(() -> new PictureDescriptionLocal( - null, - null, - null - )) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("repoId cannot be null or empty"); - } - - @Test - void createLocalWithEmptyRepoIdThrowsException() { - assertThatThrownBy(() -> new PictureDescriptionLocal( - "", - null, - null - )) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("repoId cannot be null or empty"); - } - - @Test - void createLocalWithBlankRepoIdThrowsException() { - assertThatThrownBy(() -> new PictureDescriptionLocal( - " ", - null, - null - )) + @ParameterizedTest + @NullAndEmptySource + @ValueSource(strings = " ") + void createLocalWithNullRepoIdThrowsException(String repoId) { + assertThatThrownBy(() -> new PictureDescriptionLocal().withRepoId(null)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("repoId cannot be null or empty"); } @@ -83,19 +58,18 @@ void pictureDescriptionLocalIsImmutable() { String repoId = "microsoft/Florence-2-large"; Map generationConfig = new HashMap<>(Map.of("max_length", 100)); - PictureDescriptionLocal local = new PictureDescriptionLocal( - repoId, - "Original prompt", - generationConfig - ); + PictureDescriptionLocal local = new PictureDescriptionLocal() + .withRepoId(repoId) + .withPrompt("Original prompt") + .withGenerationConfig(generationConfig); - assertThat(local.generationConfig()).isEqualTo(generationConfig); + assertThat(local.getGenerationConfig()).isEqualTo(generationConfig); generationConfig.put("temperature", 0.8); - assertThat(local.generationConfig()).hasSize(1); - assertThat(local.generationConfig().get("max_length")).isEqualTo(100); - assertThat(local.generationConfig()).doesNotContainKey("temperature"); + assertThat(local.getGenerationConfig()).hasSize(1); + assertThat(local.getGenerationConfig().get("max_length")).isEqualTo(100); + assertThat(local.getGenerationConfig()).doesNotContainKey("temperature"); } } diff --git a/api/src/test/java/ai/docling/api/convert/request/source/FileSourceTests.java b/api/src/test/java/ai/docling/api/convert/request/source/FileSourceTests.java index 722dd86..b92c5de 100644 --- a/api/src/test/java/ai/docling/api/convert/request/source/FileSourceTests.java +++ b/api/src/test/java/ai/docling/api/convert/request/source/FileSourceTests.java @@ -12,44 +12,44 @@ class FileSourceTests { @Test void whenBase64StringIsNullThenThrow() { - assertThatThrownBy(() -> new FileSource(Source.Kind.FILE, null, "test.txt")) + assertThatThrownBy(() -> new FileSource().withFilename("test.txt").withBase64String(null)) .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("base64String cannot be null or empty"); + .hasMessageContaining("base64String cannot be null or blank"); } @Test void whenBase64StringIsEmptyThenThrow() { - assertThatThrownBy(() -> new FileSource(Source.Kind.FILE, "", "test.txt")) + assertThatThrownBy(() -> new FileSource().withFilename("test.txt").withBase64String("")) .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("base64String cannot be null or empty"); + .hasMessageContaining("base64String cannot be null or blank"); } @Test void whenBase64StringIsBlankThenThrow() { - assertThatThrownBy(() -> new FileSource(Source.Kind.FILE, " ", "test.txt")) + assertThatThrownBy(() -> new FileSource().withFilename("test.txt").withBase64String(" ")) .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("base64String cannot be null or empty"); + .hasMessageContaining("base64String cannot be null or blank"); } @Test void whenFilenameIsNullThenThrow() { - assertThatThrownBy(() -> new FileSource(Source.Kind.FILE, "dGVzdCBjb250ZW50", null)) + assertThatThrownBy(() -> new FileSource().withFilename(null).withBase64String("dGVzdCBjb250ZW50")) .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("filename cannot be null or empty"); + .hasMessageContaining("filename cannot be null or blank"); } @Test void whenFilenameIsEmptyThenThrow() { - assertThatThrownBy(() -> new FileSource(Source.Kind.FILE, "dGVzdCBjb250ZW50", "")) + assertThatThrownBy(() -> new FileSource().withFilename("").withBase64String("dGVzdCBjb250ZW50")) .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("filename cannot be null or empty"); + .hasMessageContaining("filename cannot be null or blank"); } @Test void whenFilenameIsBlankThenThrow() { - assertThatThrownBy(() -> new FileSource(Source.Kind.FILE, "dGVzdCBjb250ZW50", " ")) + assertThatThrownBy(() -> new FileSource().withFilename(" ").withBase64String("dGVzdCBjb250ZW50")) .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("filename cannot be null or empty"); + .hasMessageContaining("filename cannot be null or blank"); } @Test @@ -57,18 +57,18 @@ void whenValidParametersThenCreateFileSource() { String base64String = "dGVzdCBjb250ZW50"; String filename = "test.txt"; - FileSource fileSource = new FileSource(Source.Kind.FILE, base64String, filename); + FileSource fileSource = new FileSource().withBase64String(base64String).withFilename(filename); - assertThat(fileSource.kind()).isEqualTo(Source.Kind.FILE); - assertThat(fileSource.base64String()).isEqualTo(base64String); - assertThat(fileSource.filename()).isEqualTo(filename); + assertThat(fileSource.getKind()).isEqualTo(Source.Kind.FILE); + assertThat(fileSource.getBase64String()).isEqualTo(base64String); + assertThat(fileSource.getFilename()).isEqualTo(filename); } @Test void kindIsAlwaysSetToFile() { - FileSource fileSource = new FileSource(Source.Kind.HTTP, "dGVzdCBjb250ZW50", "test.txt"); + FileSource fileSource = new FileSource().withKind(Source.Kind.HTTP).withBase64String("dGVzdCBjb250ZW50").withFilename("test.txt"); - assertThat(fileSource.kind()).isEqualTo(Source.Kind.FILE); + assertThat(fileSource.getKind()).isEqualTo(Source.Kind.FILE); } @Test @@ -76,11 +76,11 @@ void fromStaticMethodCreatesFileSource() { String filename = "document.pdf"; String base64String = "dGVzdCBjb250ZW50"; - FileSource fileSource = FileSource.from(filename, base64String); + FileSource fileSource = new FileSource().withFilename(filename).withBase64String(base64String); - assertThat(fileSource.kind()).isEqualTo(Source.Kind.FILE); - assertThat(fileSource.base64String()).isEqualTo(base64String); - assertThat(fileSource.filename()).isEqualTo(filename); + assertThat(fileSource.getKind()).isEqualTo(Source.Kind.FILE); + assertThat(fileSource.getBase64String()).isEqualTo(base64String); + assertThat(fileSource.getFilename()).isEqualTo(filename); } @Test @@ -88,14 +88,14 @@ void builderCreatesFileSource() { String filename = "presentation.pptx"; String base64String = "dGVzdCBjb250ZW50"; - FileSource fileSource = FileSource.builder() - .filename(filename) - .base64String(base64String) - .build(); + FileSource fileSource = new FileSource() + .withFilename(filename) + .withBase64String(base64String) + ; - assertThat(fileSource.kind()).isEqualTo(Source.Kind.FILE); - assertThat(fileSource.base64String()).isEqualTo(base64String); - assertThat(fileSource.filename()).isEqualTo(filename); + assertThat(fileSource.getKind()).isEqualTo(Source.Kind.FILE); + assertThat(fileSource.getBase64String()).isEqualTo(base64String); + assertThat(fileSource.getFilename()).isEqualTo(filename); } } diff --git a/api/src/test/java/ai/docling/api/convert/request/source/HttpSourceTests.java b/api/src/test/java/ai/docling/api/convert/request/source/HttpSourceTests.java index 3286b82..b049276 100644 --- a/api/src/test/java/ai/docling/api/convert/request/source/HttpSourceTests.java +++ b/api/src/test/java/ai/docling/api/convert/request/source/HttpSourceTests.java @@ -9,6 +9,8 @@ import org.junit.jupiter.api.Test; +import ai.docling.api.convert.request.source.Source.Kind; + /** * Unit tests for {@link HttpSource}. */ @@ -16,7 +18,7 @@ class HttpSourceTests { @Test void whenUrlIsNullThenThrow() { - assertThatThrownBy(() -> new HttpSource(Source.Kind.HTTP, null, null)) + assertThatThrownBy(() -> new HttpSource().withUrl(null)) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("url cannot be null"); } @@ -26,52 +28,41 @@ void whenValidParametersThenCreateHttpSource() { URI url = URI.create("https://example.com/document.pdf"); Map headers = Map.of("Authorization", "Bearer token123"); - HttpSource httpSource = new HttpSource(Source.Kind.HTTP, url, headers); + HttpSource httpSource = new HttpSource().withUrl(url).withHeaders(headers); - assertThat(httpSource.kind()).isEqualTo(Source.Kind.HTTP); - assertThat(httpSource.url()).isEqualTo(url); - assertThat(httpSource.headers()).isEqualTo(headers); + assertThat(httpSource.getKind()).isEqualTo(Source.Kind.HTTP); + assertThat(httpSource.getUrl()).isEqualTo(url); + assertThat(httpSource.getHeaders()).isEqualTo(headers); } @Test void whenRequiredParametersThenCreateHttpSource() { URI url = URI.create("https://example.com/document.pdf"); - HttpSource httpSource = new HttpSource(Source.Kind.HTTP, url, null); + HttpSource httpSource = new HttpSource().withUrl(url); - assertThat(httpSource.kind()).isEqualTo(Source.Kind.HTTP); - assertThat(httpSource.url()).isEqualTo(url); - assertThat(httpSource.headers()).isNull(); + assertThat(httpSource.getKind()).isEqualTo(Source.Kind.HTTP); + assertThat(httpSource.getUrl()).isEqualTo(url); + assertThat(httpSource.getHeaders()).isNotNull().isEmpty(); } @Test void kindIsAlwaysSetToHttp() { URI url = URI.create("https://example.com/document.pdf"); - HttpSource httpSource = new HttpSource(Source.Kind.FILE, url, null); - - assertThat(httpSource.kind()).isEqualTo(Source.Kind.HTTP); - } - - @Test - void fromStaticMethodWithStringCreatesHttpSource() { - String urlString = "https://example.com/document.pdf"; - - HttpSource httpSource = HttpSource.from(urlString); + HttpSource httpSource = new HttpSource().withUrl(url); - assertThat(httpSource.kind()).isEqualTo(Source.Kind.HTTP); - assertThat(httpSource.url()).isEqualTo(URI.create(urlString)); - assertThat(httpSource.headers()).isNull(); + assertThat(httpSource.getKind()).isEqualTo(Kind.HTTP); } @Test void fromStaticMethodWithUriCreatesHttpSource() { URI url = URI.create("https://example.com/document.pdf"); - HttpSource httpSource = HttpSource.from(url); + HttpSource httpSource = new HttpSource().withUrl(url); - assertThat(httpSource.kind()).isEqualTo(Source.Kind.HTTP); - assertThat(httpSource.url()).isEqualTo(url); - assertThat(httpSource.headers()).isNull(); + assertThat(httpSource.getKind()).isEqualTo(Source.Kind.HTTP); + assertThat(httpSource.getUrl()).isEqualTo(url); + assertThat(httpSource.getHeaders()).isNotNull().isEmpty(); } @Test @@ -79,32 +70,31 @@ void builderCreatesHttpSource() { URI url = URI.create("https://example.com/presentation.pptx"); Map headers = Map.of("User-Agent", "test-agent"); - HttpSource httpSource = HttpSource.builder() - .url(url) - .headers(headers) - .build(); + HttpSource httpSource = new HttpSource() + .withUrl(url) + .withHeaders(headers) + ; - assertThat(httpSource.kind()).isEqualTo(Source.Kind.HTTP); - assertThat(httpSource.url()).isEqualTo(url); - assertThat(httpSource.headers()).isEqualTo(headers); + assertThat(httpSource.getKind()).isEqualTo(Source.Kind.HTTP); + assertThat(httpSource.getUrl()).isEqualTo(url); + assertThat(httpSource.getHeaders()).isEqualTo(headers); } @Test void httpSourceIsImmutable() { Map headers = new HashMap<>(Map.of("Authorization", "Bearer token123")); - HttpSource httpSource = HttpSource.builder() - .url(URI.create("https://example.com/test.pdf")) - .headers(headers) - .build(); + HttpSource httpSource = new HttpSource() + .withUrl(URI.create("https://example.com/test.pdf")) + .withHeaders(headers); - assertThat(httpSource.headers()).isEqualTo(headers); + assertThat(httpSource.getHeaders()).isEqualTo(headers); headers.put("newStuff", "awesome"); - assertThat(httpSource.headers()).size().isEqualTo(1); - assertThat(httpSource.headers()).containsEntry("Authorization", "Bearer token123"); - assertThat(httpSource.headers()).doesNotContainKey("newStuff"); + assertThat(httpSource.getHeaders()).size().isEqualTo(1); + assertThat(httpSource.getHeaders()).containsEntry("Authorization", "Bearer token123"); + assertThat(httpSource.getHeaders()).doesNotContainKey("newStuff"); } } diff --git a/api/src/test/java/ai/docling/api/convert/request/target/InBodyTargetTests.java b/api/src/test/java/ai/docling/api/convert/request/target/InBodyTargetTests.java index 85b96c7..184ed6f 100644 --- a/api/src/test/java/ai/docling/api/convert/request/target/InBodyTargetTests.java +++ b/api/src/test/java/ai/docling/api/convert/request/target/InBodyTargetTests.java @@ -4,6 +4,8 @@ import org.junit.jupiter.api.Test; +import ai.docling.api.convert.request.target.Target.Kind; + /** * Unit tests for {@link InBodyTarget}. */ @@ -11,16 +13,16 @@ class InBodyTargetTests { @Test void whenValidParametersThenCreateInBodyTarget() { - InBodyTarget inBodyTarget = InBodyTarget.create(); + InBodyTarget inBodyTarget = new InBodyTarget(); - assertThat(inBodyTarget.kind()).isEqualTo(Target.Kind.INBODY); + assertThat(inBodyTarget.getKind()).isEqualTo(Kind.INBODY); } @Test void kindIsAlwaysSetToInBody() { - InBodyTarget inBodyTarget = new InBodyTarget(Target.Kind.PUT); + InBodyTarget inBodyTarget = new InBodyTarget().withKind(Kind.PUT); - assertThat(inBodyTarget.kind()).isEqualTo(Target.Kind.INBODY); + assertThat(inBodyTarget.getKind()).isEqualTo(Kind.INBODY); } } diff --git a/api/src/test/java/ai/docling/api/convert/request/target/PutTargetTests.java b/api/src/test/java/ai/docling/api/convert/request/target/PutTargetTests.java index e89a663..6193959 100644 --- a/api/src/test/java/ai/docling/api/convert/request/target/PutTargetTests.java +++ b/api/src/test/java/ai/docling/api/convert/request/target/PutTargetTests.java @@ -7,6 +7,8 @@ import org.junit.jupiter.api.Test; +import ai.docling.api.convert.request.target.Target.Kind; + /** * Unit tests for {@link PutTarget}. */ @@ -14,7 +16,7 @@ class PutTargetTests { @Test void whenUriIsNullThenThrow() { - assertThatThrownBy(() -> PutTarget.create(null)) + assertThatThrownBy(() -> new PutTarget().withUrl(null)) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("url cannot be null"); } @@ -23,19 +25,19 @@ void whenUriIsNullThenThrow() { void whenValidParametersThenCreatePutTarget() { URI uri = URI.create("https://example.com/upload"); - PutTarget putTarget = PutTarget.create(uri); + PutTarget putTarget = new PutTarget().withUrl(uri); - assertThat(putTarget.kind()).isEqualTo(Target.Kind.PUT); - assertThat(putTarget.url()).isEqualTo(uri); + assertThat(putTarget.getKind()).isEqualTo(Target.Kind.PUT); + assertThat(putTarget.getUrl()).isEqualTo(uri); } @Test void kindIsAlwaysSetToPut() { URI uri = URI.create("https://example.com/upload"); - PutTarget putTarget = new PutTarget(Target.Kind.INBODY, uri); + PutTarget putTarget = new PutTarget().withKind(Kind.INBODY).withUrl(uri); - assertThat(putTarget.kind()).isEqualTo(Target.Kind.PUT); + assertThat(putTarget.getKind()).isEqualTo(Kind.PUT); } } diff --git a/api/src/test/java/ai/docling/api/convert/request/target/ZipTargetTests.java b/api/src/test/java/ai/docling/api/convert/request/target/ZipTargetTests.java index f31d6fa..51c2ecf 100644 --- a/api/src/test/java/ai/docling/api/convert/request/target/ZipTargetTests.java +++ b/api/src/test/java/ai/docling/api/convert/request/target/ZipTargetTests.java @@ -11,16 +11,16 @@ class ZipTargetTests { @Test void whenValidParametersThenCreateZipTarget() { - ZipTarget zipTarget = ZipTarget.create(); + ZipTarget zipTarget = new ZipTarget(); - assertThat(zipTarget.kind()).isEqualTo(Target.Kind.ZIP); + assertThat(zipTarget.getKind()).isEqualTo(Target.Kind.ZIP); } @Test void kindIsAlwaysSetToZip() { - ZipTarget zipTarget = new ZipTarget(Target.Kind.PUT); + ZipTarget zipTarget = new ZipTarget().withKind(Target.Kind.PUT); - assertThat(zipTarget.kind()).isEqualTo(Target.Kind.ZIP); + assertThat(zipTarget.getKind()).isEqualTo(Target.Kind.ZIP); } } diff --git a/api/src/test/java/ai/docling/api/convert/response/ConvertDocumentResponseTests.java b/api/src/test/java/ai/docling/api/convert/response/ConvertDocumentResponseTests.java index 6464dc1..1f9cc90 100644 --- a/api/src/test/java/ai/docling/api/convert/response/ConvertDocumentResponseTests.java +++ b/api/src/test/java/ai/docling/api/convert/response/ConvertDocumentResponseTests.java @@ -16,18 +16,17 @@ class ConvertDocumentResponseTests { @Test void createResponseWithAllFields() { - DocumentResponse document = DocumentResponse.builder() - .doctagsContent("doctags content") - .filename("test-file.pdf") - .htmlContent("content") - .jsonContent(Map.of("key", "value")) - .markdownContent("# Markdown content") - .textContent("Plain text content") - .build(); + DocumentResponse document = new DocumentResponse() + .withDoctagsContent("doctags content") + .withFilename("test-file.pdf") + .withHtmlContent("content") + .withJsonContent(Map.of("key", "value")) + .withMarkdownContent("# Markdown content") + .withTextContent("Plain text content"); List errors = List.of( - new ErrorItem("parser", "Parse error", "pdf_module"), - new ErrorItem("converter", "Conversion warning", "html_module") + new ErrorItem().withComponentType("parser").withErrorMessage("Parse error").withModuleName("pdf_module"), + new ErrorItem().withComponentType("converter").withErrorMessage("Conversion warning").withModuleName("html_module") ); Double processingTime = 1.5; @@ -37,90 +36,71 @@ void createResponseWithAllFields() { "convert_time", 0.7 ); - ConvertDocumentResponse response = new ConvertDocumentResponse( - document, - errors, - processingTime, - status, - timings - ); - - assertThat(response.document()).isEqualTo(document); - assertThat(response.errors()).isEqualTo(errors); - assertThat(response.processingTime()).isEqualTo(processingTime); - assertThat(response.status()).isEqualTo(status); - assertThat(response.timings()).isEqualTo(timings); + ConvertDocumentResponse response = new ConvertDocumentResponse() + .withDocument(document) + .withErrors(errors) + .withProcessingTime(processingTime) + .withStatus(status) + .withTimings(timings); + + assertThat(response.getDocument()).isEqualTo(document); + assertThat(response.getErrors()).containsExactlyInAnyOrderElementsOf(errors); + assertThat(response.getProcessingTime()).isEqualTo(processingTime); + assertThat(response.getStatus()).isEqualTo(status); + assertThat(response.getTimings()).containsExactlyInAnyOrderEntriesOf(timings); } @Test void createResponseWithNullFields() { - ConvertDocumentResponse response = new ConvertDocumentResponse( - null, - null, - null, - null, - null - ); + ConvertDocumentResponse response = new ConvertDocumentResponse(); - assertThat(response.document()).isNull(); - assertThat(response.errors()).isNull(); - assertThat(response.processingTime()).isNull(); - assertThat(response.status()).isNull(); - assertThat(response.timings()).isNull(); + assertThat(response.getDocument()).isNull(); + assertThat(response.getErrors()).isNotNull().isEmpty(); + assertThat(response.getProcessingTime()).isNull(); + assertThat(response.getStatus()).isNull(); + assertThat(response.getTimings()).isNotNull().isEmpty(); } @Test void createResponseWithEmptyCollections() { - DocumentResponse document = DocumentResponse.builder() - .filename("empty-file.txt") - .jsonContent(Map.of()) - .textContent("") - .build(); - - List errors = List.of(); - Map timings = Map.of(); - - ConvertDocumentResponse response = new ConvertDocumentResponse( - document, - errors, - 0.1, - "completed", - timings - ); - - assertThat(response.document()).isEqualTo(document); - assertThat(response.errors()).isEmpty(); - assertThat(response.processingTime()).isEqualTo(0.1); - assertThat(response.status()).isEqualTo("completed"); - assertThat(response.timings()).isEmpty(); + DocumentResponse document = new DocumentResponse() + .withFilename("empty-file.txt") + .withTextContent(""); + + ConvertDocumentResponse response = new ConvertDocumentResponse() + .withDocument(document) + .withProcessingTime(0.1) + .withStatus("completed"); + + assertThat(response.getDocument()).isEqualTo(document); + assertThat(response.getErrors()).isEmpty(); + assertThat(response.getProcessingTime()).isEqualTo(0.1); + assertThat(response.getStatus()).isEqualTo("completed"); + assertThat(response.getTimings()).isEmpty(); } @Test void convertDocumentResponseIsImmutable() { List errors = new ArrayList<>(List.of( - new ErrorItem("original", "Original error", "original_module") + new ErrorItem().withComponentType("original").withErrorMessage("Original error").withModuleName("original_module") )); Map timings = new HashMap<>(Map.of("original_time", 1.0)); - ConvertDocumentResponse response = new ConvertDocumentResponse( - null, - errors, - null, - null, - timings - ); + ConvertDocumentResponse response = new ConvertDocumentResponse() + .withErrors(errors) + .withTimings(timings); - assertThat(response.errors()).isEqualTo(errors); - assertThat(response.timings()).isEqualTo(timings); + assertThat(response.getErrors()).containsExactlyInAnyOrderElementsOf(errors); + assertThat(response.getTimings()).containsExactlyInAnyOrderEntriesOf(timings); - errors.add(new ErrorItem("modified", "Modified error", "modified_module")); + errors.add(new ErrorItem().withComponentType("modified").withErrorMessage("Modified error").withModuleName("modified_module")); timings.put("modified_time", 3.0); - assertThat(response.errors()).hasSize(1); - assertThat(response.errors().get(0).errorMessage()).isEqualTo("Original error"); - assertThat(response.timings()).hasSize(1); - assertThat(response.timings().get("original_time")).isEqualTo(1.0); + assertThat(response.getErrors()).hasSize(1); + assertThat(response.getErrors().get(0).getErrorMessage()).isEqualTo("Original error"); + assertThat(response.getTimings()).hasSize(1); + assertThat(response.getTimings().get("original_time")).isEqualTo(1.0); } } diff --git a/api/src/test/java/ai/docling/api/convert/response/DocumentResponseTests.java b/api/src/test/java/ai/docling/api/convert/response/DocumentResponseTests.java index 1642d2a..659b551 100644 --- a/api/src/test/java/ai/docling/api/convert/response/DocumentResponseTests.java +++ b/api/src/test/java/ai/docling/api/convert/response/DocumentResponseTests.java @@ -24,76 +24,75 @@ void createResponseWithAllFields() { String markdownContent = "# Test Document\n\nThis is a test document."; String textContent = "Test Document\n\nThis is a test document."; - DocumentResponse response = DocumentResponse.builder() - .doctagsContent(doctagsContent) - .filename(filename) - .htmlContent(htmlContent) - .jsonContent(jsonContent) - .markdownContent(markdownContent) - .textContent(textContent) - .build(); - - assertThat(response.doctagsContent()).isEqualTo(doctagsContent); - assertThat(response.filename()).isEqualTo(filename); - assertThat(response.htmlContent()).isEqualTo(htmlContent); - assertThat(response.jsonContent()).isEqualTo(jsonContent); - assertThat(response.markdownContent()).isEqualTo(markdownContent); - assertThat(response.textContent()).isEqualTo(textContent); + DocumentResponse response = new DocumentResponse() + .withDoctagsContent(doctagsContent) + .withFilename(filename) + .withHtmlContent(htmlContent) + .withJsonContent(new HashMap<>(jsonContent)) + .withMarkdownContent(markdownContent) + .withTextContent(textContent); + + assertThat(response.getDoctagsContent()).isEqualTo(doctagsContent); + assertThat(response.getFilename()).isEqualTo(filename); + assertThat(response.getHtmlContent()).isEqualTo(htmlContent); + assertThat(response.getJsonContent()).containsExactlyInAnyOrderEntriesOf(jsonContent); + assertThat(response.getMarkdownContent()).isEqualTo(markdownContent); + assertThat(response.getTextContent()).isEqualTo(textContent); } @Test void createResponseWithNullFields() { - DocumentResponse response = DocumentResponse.builder().build(); - - assertThat(response.doctagsContent()).isNull(); - assertThat(response.filename()).isNull(); - assertThat(response.htmlContent()).isNull(); - assertThat(response.jsonContent()).isNotNull().isEmpty(); - assertThat(response.markdownContent()).isNull(); - assertThat(response.textContent()).isNull(); + DocumentResponse response = new DocumentResponse(); + + assertThat(response.getDoctagsContent()).isNull(); + assertThat(response.getFilename()).isNull(); + assertThat(response.getHtmlContent()).isNull(); + assertThat(response.getJsonContent()).isNotNull().isEmpty(); + assertThat(response.getMarkdownContent()).isNull(); + assertThat(response.getTextContent()).isNull(); } @Test void createResponseWithEmptyFields() { String filename = "empty-document.txt"; - Map jsonContent = Map.of(); String markdownContent = ""; String textContent = ""; - DocumentResponse response = DocumentResponse.builder() - .filename(filename) - .jsonContent(jsonContent) - .markdownContent(markdownContent) - .textContent(textContent) - .build(); - - assertThat(response.doctagsContent()).isNull(); - assertThat(response.filename()).isEqualTo(filename); - assertThat(response.htmlContent()).isNull(); - assertThat(response.jsonContent()).isEmpty(); - assertThat(response.markdownContent()).isEmpty(); - assertThat(response.textContent()).isEmpty(); + DocumentResponse response = new DocumentResponse() + .withFilename(filename) + .withMarkdownContent(markdownContent) + .withTextContent(textContent); + + assertThat(response.getDoctagsContent()).isNull(); + assertThat(response.getFilename()).isEqualTo(filename); + assertThat(response.getHtmlContent()).isNull(); + assertThat(response.getJsonContent()).isNotNull().isEmpty(); + assertThat(response.getMarkdownContent()).isEmpty(); + assertThat(response.getTextContent()).isEmpty(); } @Test - void documentResponseIsImmutable() { + void documentResponseJsonContentReflectsOriginalMapChanges() { Map jsonContent = new HashMap<>(Map.of( "original", "value", "count", 1 )); - DocumentResponse response = DocumentResponse.builder() - .jsonContent(jsonContent) - .build(); + DocumentResponse response = new DocumentResponse(); + response.setJsonContent(jsonContent); - assertThat(response.jsonContent()).isEqualTo(jsonContent); + assertThat(response.getJsonContent()).containsExactlyInAnyOrderEntriesOf(jsonContent); jsonContent.put("modified", "new value"); - assertThat(response.jsonContent()).hasSize(2); - assertThat(response.jsonContent().get("original")).isEqualTo("value"); - assertThat(response.jsonContent().get("count")).isEqualTo(1); - assertThat(response.jsonContent()).doesNotContainKey("modified"); + assertThat(response.getJsonContent()).hasSize(2); + + response.setJsonContent(jsonContent); + assertThat(response.getJsonContent()).hasSize(3); + + assertThat(response.getJsonContent().get("original")).isEqualTo("value"); + assertThat(response.getJsonContent().get("count")).isEqualTo(1); + assertThat(response.getJsonContent()).containsKey("modified"); } } diff --git a/api/src/test/java/ai/docling/api/convert/response/ErrorItemTests.java b/api/src/test/java/ai/docling/api/convert/response/ErrorItemTests.java index 6aaf8ee..7d37b19 100644 --- a/api/src/test/java/ai/docling/api/convert/response/ErrorItemTests.java +++ b/api/src/test/java/ai/docling/api/convert/response/ErrorItemTests.java @@ -15,28 +15,26 @@ void createErrorItemWithAllFields() { String errorMessage = "Failed to parse document structure"; String moduleName = "docling.core.parser"; - ErrorItem errorItem = new ErrorItem( - componentType, - errorMessage, - moduleName - ); - - assertThat(errorItem.componentType()).isEqualTo(componentType); - assertThat(errorItem.errorMessage()).isEqualTo(errorMessage); - assertThat(errorItem.moduleName()).isEqualTo(moduleName); + ErrorItem errorItem = new ErrorItem() + .withComponentType(componentType) + .withErrorMessage(errorMessage) + .withModuleName(moduleName); + + assertThat(errorItem.getComponentType()).isEqualTo(componentType); + assertThat(errorItem.getErrorMessage()).isEqualTo(errorMessage); + assertThat(errorItem.getModuleName()).isEqualTo(moduleName); } @Test void createErrorItemWithNullFields() { - ErrorItem errorItem = new ErrorItem( - null, - null, - null - ); - - assertThat(errorItem.componentType()).isNull(); - assertThat(errorItem.errorMessage()).isNull(); - assertThat(errorItem.moduleName()).isNull(); + ErrorItem errorItem = new ErrorItem() + .withComponentType(null) + .withErrorMessage(null) + .withModuleName(null); + + assertThat(errorItem.getComponentType()).isNull(); + assertThat(errorItem.getErrorMessage()).isNull(); + assertThat(errorItem.getModuleName()).isNull(); } @Test @@ -45,15 +43,14 @@ void createErrorItemWithEmptyFields() { String errorMessage = ""; String moduleName = ""; - ErrorItem errorItem = new ErrorItem( - componentType, - errorMessage, - moduleName - ); + ErrorItem errorItem = new ErrorItem() + .withComponentType(componentType) + .withErrorMessage(errorMessage) + .withModuleName(moduleName); - assertThat(errorItem.componentType()).isEmpty(); - assertThat(errorItem.errorMessage()).isEmpty(); - assertThat(errorItem.moduleName()).isEmpty(); + assertThat(errorItem.getComponentType()).isEmpty(); + assertThat(errorItem.getErrorMessage()).isEmpty(); + assertThat(errorItem.getModuleName()).isEmpty(); } } diff --git a/api/src/test/java/ai/docling/api/health/HealthCheckResponseTests.java b/api/src/test/java/ai/docling/api/health/HealthCheckResponseTests.java index 8dcb2c3..6f3d411 100644 --- a/api/src/test/java/ai/docling/api/health/HealthCheckResponseTests.java +++ b/api/src/test/java/ai/docling/api/health/HealthCheckResponseTests.java @@ -13,25 +13,25 @@ class HealthCheckResponseTests { void whenValidParametersThenCreateHealthCheckResponse() { String status = "healthy"; - HealthCheckResponse response = new HealthCheckResponse(status); + HealthCheckResponse response = new HealthCheckResponse().withStatus(status); - assertThat(response.status()).isEqualTo(status); + assertThat(response.getStatus()).isEqualTo(status); } @Test void whenStatusIsNullThenCreateHealthCheckResponse() { - HealthCheckResponse response = new HealthCheckResponse(null); + HealthCheckResponse response = new HealthCheckResponse().withStatus(null); - assertThat(response.status()).isNull(); + assertThat(response.getStatus()).isNull(); } @Test void whenEmptyStatusThenCreateHealthCheckResponse() { String status = ""; - HealthCheckResponse response = new HealthCheckResponse(status); + HealthCheckResponse response = new HealthCheckResponse().withStatus(status); - assertThat(response.status()).isEqualTo(status); + assertThat(response.getStatus()).isEqualTo(status); } } diff --git a/buildSrc/src/main/kotlin/docling-java-shared.gradle.kts b/buildSrc/src/main/kotlin/docling-java-shared.gradle.kts index b2ea6ee..20eb7c2 100644 --- a/buildSrc/src/main/kotlin/docling-java-shared.gradle.kts +++ b/buildSrc/src/main/kotlin/docling-java-shared.gradle.kts @@ -1,3 +1,5 @@ +import org.gradle.api.tasks.testing.logging.TestExceptionFormat + plugins { id("docling-shared") `java-library` @@ -78,6 +80,10 @@ tasks.withType { testLogging { events("PASSED", "FAILED", "SKIPPED", "STANDARD_OUT", "STANDARD_ERROR") showStandardStreams = true + showCauses = true + showExceptions = true + showStackTraces = true + exceptionFormat = TestExceptionFormat.FULL } } diff --git a/client/build.gradle.kts b/client/build.gradle.kts index 463c0d9..fa84ec0 100644 --- a/client/build.gradle.kts +++ b/client/build.gradle.kts @@ -4,9 +4,9 @@ plugins { dependencies { api(project(":docling-api")) - api(platform(libs.jackson.bom)) - compileOnly(libs.jackson.databind) - compileOnly(libs.jackson2.databind) + implementation(platform(libs.jackson.bom)) + implementation(libs.jackson.databind) + implementation(libs.jackson2.databind) testImplementation(platform(libs.testcontainers.bom)) testImplementation(libs.testcontainers.junit.jupiter) diff --git a/client/src/test/java/ai/docling/client/AbstractDoclingClientTests.java b/client/src/test/java/ai/docling/client/AbstractDoclingClientTests.java index d77bba9..9b61f51 100644 --- a/client/src/test/java/ai/docling/client/AbstractDoclingClientTests.java +++ b/client/src/test/java/ai/docling/client/AbstractDoclingClientTests.java @@ -7,24 +7,23 @@ import java.net.URI; import java.time.Duration; import java.util.Base64; +import java.util.List; import java.util.Optional; import org.junit.jupiter.api.Test; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; import ai.docling.api.DoclingApi; import ai.docling.api.convert.request.ConvertDocumentRequest; import ai.docling.api.convert.request.options.ConvertDocumentOptions; import ai.docling.api.convert.request.options.TableFormerMode; +import ai.docling.api.convert.request.source.FileSource; +import ai.docling.api.convert.request.source.HttpSource; import ai.docling.api.convert.response.ConvertDocumentResponse; import ai.docling.api.health.HealthCheckResponse; import ai.docling.testcontainers.DoclingContainer; import ai.docling.testcontainers.config.DoclingContainerConfig; -@Testcontainers abstract class AbstractDoclingClientTests { - @Container protected static final DoclingContainer doclingContainer = new DoclingContainer( DoclingContainerConfig.builder() .imageName(DoclingContainerConfig.DOCLING_IMAGE) @@ -33,6 +32,10 @@ abstract class AbstractDoclingClientTests { Optional.of(Duration.ofMinutes(2)) ); + static { + doclingContainer.start(); + } + protected abstract DoclingApi getDoclingClient(); @Test @@ -41,70 +44,66 @@ void shouldSuccessfullyCallHealthEndpoint() { assertThat(response) .isNotNull() - .extracting(HealthCheckResponse::status) + .extracting(HealthCheckResponse::getStatus) .isEqualTo("ok"); } @Test void shouldConvertHttpSourceSuccessfully() { - ConvertDocumentRequest request = ConvertDocumentRequest.builder() - .addHttpSources(URI.create("https://docs.arconia.io/arconia-cli/latest/development/dev/")) - .build(); + ConvertDocumentRequest request = new ConvertDocumentRequest() + .withSources(List.of(new HttpSource().withUrl(URI.create("https://docs.arconia.io/arconia-cli/latest/development/dev/")))); ConvertDocumentResponse response = getDoclingClient().convertSource(request); assertThat(response).isNotNull(); - assertThat(response.status()).isNotEmpty(); - assertThat(response.document()).isNotNull(); - assertThat(response.document().filename()).isNotEmpty(); + assertThat(response.getStatus()).isNotEmpty(); + assertThat(response.getDocument()).isNotNull(); + assertThat(response.getDocument().getFilename()).isNotEmpty(); - if (response.processingTime() != null) { - assertThat(response.processingTime()).isPositive(); + if (response.getProcessingTime() != null) { + assertThat(response.getProcessingTime()).isPositive(); } - assertThat(response.document().markdownContent()).isNotEmpty(); + assertThat(response.getDocument().getMarkdownContent()).isNotEmpty(); } @Test void shouldConvertFileSourceSuccessfully() throws IOException { var fileResource = readFileFromClasspath("story.pdf"); - ConvertDocumentRequest request = ConvertDocumentRequest.builder() - .addFileSources("story.pdf", Base64.getEncoder().encodeToString(fileResource)) - .build(); + ConvertDocumentRequest request = new ConvertDocumentRequest() + .withSources(List.of(new FileSource().withFilename("story.pdf").withBase64String(Base64.getEncoder().encodeToString(fileResource)))); ConvertDocumentResponse response = getDoclingClient().convertSource(request); assertThat(response).isNotNull(); - assertThat(response.status()).isNotEmpty(); - assertThat(response.document()).isNotNull(); - assertThat(response.document().filename()).isEqualTo("story.pdf"); + assertThat(response.getStatus()).isNotEmpty(); + assertThat(response.getDocument()).isNotNull(); + assertThat(response.getDocument().getFilename()).isEqualTo("story.pdf"); - if (response.processingTime() != null) { - assertThat(response.processingTime()).isPositive(); + if (response.getProcessingTime() != null) { + assertThat(response.getProcessingTime()).isPositive(); } - assertThat(response.document().markdownContent()).isNotEmpty(); + assertThat(response.getDocument().getMarkdownContent()).isNotEmpty(); } @Test void shouldHandleConversionWithDifferentDocumentOptions() { - ConvertDocumentOptions options = ConvertDocumentOptions.builder() - .doOcr(true) - .includeImages(true) - .tableMode(TableFormerMode.FAST) - .build(); + ConvertDocumentOptions options = new ConvertDocumentOptions() + .withDoOcr(true) + .withIncludeImages(true) + .withTableMode(TableFormerMode.FAST); - ConvertDocumentRequest request = ConvertDocumentRequest.builder() - .addHttpSources(URI.create("https://docs.arconia.io/arconia-cli/latest/development/dev/")) - .options(options) - .build(); + ConvertDocumentRequest request = new ConvertDocumentRequest() + .withSources(List.of(new HttpSource().withUrl(URI.create("https://docs.arconia.io/arconia-cli/latest/development/dev/")))) + .withOptions(options); ConvertDocumentResponse response = getDoclingClient().convertSource(request); assertThat(response).isNotNull(); - assertThat(response.status()).isNotEmpty(); - assertThat(response.document()).isNotNull(); + assertThat(response.getStatus()).isNotEmpty(); + assertThat(response.getDocument()).isNotNull(); } private static byte[] readFileFromClasspath(String filePath) throws IOException { From cc57513f0520fc00273f772ae3c507331c78eada Mon Sep 17 00:00:00 2001 From: Eric Deandrea Date: Wed, 29 Oct 2025 14:22:38 -0400 Subject: [PATCH 09/13] feat: idea for api design Rename modules Signed-off-by: Eric Deandrea --- .github/workflows/build.yml | 12 ++--- .github/workflows/docs.yml | 2 +- README.md | 22 ++++---- .../ai/docling/api/convert/package-info.java | 7 --- .../java/ai/docling/client/package-info.java | 7 --- .../client/DoclingJackson2ClientTests.java | 25 --------- .../client/DoclingJackson3ClientTests.java | 25 --------- {api => docling-api}/build.gradle.kts | 0 .../main/java/ai/docling/api/DoclingApi.java | 0 .../request/ConvertDocumentRequest.java | 0 .../options/ConvertDocumentOptions.java | 0 .../convert/request/options/ImageRefMode.java | 0 .../convert/request/options/InputFormat.java | 0 .../convert/request/options/OcrEngine.java | 0 .../convert/request/options/OutputFormat.java | 0 .../convert/request/options/PdfBackend.java | 0 .../options/PictureDescriptionApi.java | 0 .../options/PictureDescriptionLocal.java | 0 .../request/options/ProcessingPipeline.java | 0 .../request/options/ResponseFormat.java | 0 .../request/options/TableFormerMode.java | 0 .../convert/request/options/VlmModelApi.java | 0 .../request/options/VlmModelLocal.java | 0 .../convert/request/options/VlmModelType.java | 0 .../convert/request/options/package-info.java | 0 .../api/convert/request/package-info.java | 0 .../convert/request/source/FileSource.java | 0 .../convert/request/source/HttpSource.java | 0 .../api/convert/request/source/Source.java | 0 .../convert/request/source/package-info.java | 4 ++ .../convert/request/target/InBodyTarget.java | 0 .../api/convert/request/target/PutTarget.java | 0 .../api/convert/request/target/Target.java | 0 .../api/convert/request/target/ZipTarget.java | 0 .../convert/request/target/package-info.java | 4 ++ .../response/ConvertDocumentResponse.java | 0 .../convert/response/DocumentResponse.java | 0 .../api/convert/response/ErrorItem.java | 0 .../api/convert/response/package-info.java | 0 .../api/health/HealthCheckResponse.java | 0 .../ai/docling/api/health/package-info.java | 0 .../java/ai/docling/api/package-info.java | 0 .../main/java/ai/docling/api/util/Utils.java | 0 .../ai/docling/api/util/ValidationUtils.java | 0 .../ai/docling/api/util}/package-info.java | 2 +- .../request/ConvertDocumentRequestTests.java | 0 .../options/ConvertDocumentOptionsTests.java | 0 .../options/PictureDescriptionApiTests.java | 0 .../options/PictureDescriptionLocalTests.java | 0 .../request/source/FileSourceTests.java | 0 .../request/source/HttpSourceTests.java | 0 .../request/target/InBodyTargetTests.java | 0 .../request/target/PutTargetTests.java | 0 .../request/target/ZipTargetTests.java | 0 .../ConvertDocumentResponseTests.java | 0 .../response/DocumentResponseTests.java | 0 .../api/convert/response/ErrorItemTests.java | 0 .../api/health/HealthCheckResponseTests.java | 0 .../java/ai/docling/api/util/UtilsTests.java | 0 .../api/util/ValidationUtilsTests.java | 0 .../build.gradle.kts | 0 .../client/serve/DoclingServeClient.java | 26 ++++----- .../DoclingServeClientBuilderFactory.java | 50 +++++++++--------- .../serve/DoclingServeJackson2Client.java | 34 ++++++------ .../serve/DoclingServeJackson3Client.java | 36 ++++++------- .../ai/docling/client/serve/package-info.java | 4 ++ .../AbstractDoclingServeClientTests.java | 14 ++--- ...DoclingServeClientBuilderFactoryTests.java | 18 +++---- .../DoclingServeJackson2ClientTests.java | 25 +++++++++ .../DoclingServeJackson3ClientTests.java | 25 +++++++++ .../src/test/resources/story.pdf | Bin .../build.gradle.kts | 0 .../docling/testcontainers/package-info.java | 0 .../serve/DoclingServeContainer.java | 20 +++---- .../config/DefaultDoclingContainerConfig.java | 4 +- .../config/DoclingServeContainerConfig.java | 28 +++++----- .../serve/config/package-info.java | 4 ++ .../testcontainers/serve/package-info.java | 4 ++ .../DoclingServeContainerAvailableTests.java | 12 ++--- {testing => docling-testing}/build.gradle.kts | 0 docs/build.gradle.kts | 2 +- docs/src/doc/docs/client.md | 1 - docs/src/doc/docs/serve-client.md | 1 + docs/src/doc/mkdocs.yml | 2 +- settings.gradle.kts | 8 +-- 85 files changed, 214 insertions(+), 214 deletions(-) delete mode 100644 api/src/main/java/ai/docling/api/convert/package-info.java delete mode 100644 client/src/main/java/ai/docling/client/package-info.java delete mode 100644 client/src/test/java/ai/docling/client/DoclingJackson2ClientTests.java delete mode 100644 client/src/test/java/ai/docling/client/DoclingJackson3ClientTests.java rename {api => docling-api}/build.gradle.kts (100%) rename {api => docling-api}/src/main/java/ai/docling/api/DoclingApi.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/request/ConvertDocumentRequest.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/request/options/ConvertDocumentOptions.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/request/options/ImageRefMode.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/request/options/InputFormat.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/request/options/OcrEngine.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/request/options/OutputFormat.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/request/options/PdfBackend.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionApi.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionLocal.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/request/options/ProcessingPipeline.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/request/options/ResponseFormat.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/request/options/TableFormerMode.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/request/options/VlmModelApi.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/request/options/VlmModelLocal.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/request/options/VlmModelType.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/request/options/package-info.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/request/package-info.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/request/source/FileSource.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/request/source/HttpSource.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/request/source/Source.java (100%) create mode 100644 docling-api/src/main/java/ai/docling/api/convert/request/source/package-info.java rename {api => docling-api}/src/main/java/ai/docling/api/convert/request/target/InBodyTarget.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/request/target/PutTarget.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/request/target/Target.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/request/target/ZipTarget.java (100%) create mode 100644 docling-api/src/main/java/ai/docling/api/convert/request/target/package-info.java rename {api => docling-api}/src/main/java/ai/docling/api/convert/response/ConvertDocumentResponse.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/response/DocumentResponse.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/response/ErrorItem.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/convert/response/package-info.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/health/HealthCheckResponse.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/health/package-info.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/package-info.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/util/Utils.java (100%) rename {api => docling-api}/src/main/java/ai/docling/api/util/ValidationUtils.java (100%) rename {testcontainers/src/main/java/ai/docling/testcontainers/config => docling-api/src/main/java/ai/docling/api/util}/package-info.java (57%) rename {api => docling-api}/src/test/java/ai/docling/api/convert/request/ConvertDocumentRequestTests.java (100%) rename {api => docling-api}/src/test/java/ai/docling/api/convert/request/options/ConvertDocumentOptionsTests.java (100%) rename {api => docling-api}/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionApiTests.java (100%) rename {api => docling-api}/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionLocalTests.java (100%) rename {api => docling-api}/src/test/java/ai/docling/api/convert/request/source/FileSourceTests.java (100%) rename {api => docling-api}/src/test/java/ai/docling/api/convert/request/source/HttpSourceTests.java (100%) rename {api => docling-api}/src/test/java/ai/docling/api/convert/request/target/InBodyTargetTests.java (100%) rename {api => docling-api}/src/test/java/ai/docling/api/convert/request/target/PutTargetTests.java (100%) rename {api => docling-api}/src/test/java/ai/docling/api/convert/request/target/ZipTargetTests.java (100%) rename {api => docling-api}/src/test/java/ai/docling/api/convert/response/ConvertDocumentResponseTests.java (100%) rename {api => docling-api}/src/test/java/ai/docling/api/convert/response/DocumentResponseTests.java (100%) rename {api => docling-api}/src/test/java/ai/docling/api/convert/response/ErrorItemTests.java (100%) rename {api => docling-api}/src/test/java/ai/docling/api/health/HealthCheckResponseTests.java (100%) rename {api => docling-api}/src/test/java/ai/docling/api/util/UtilsTests.java (100%) rename {api => docling-api}/src/test/java/ai/docling/api/util/ValidationUtilsTests.java (100%) rename {client => docling-serve-client}/build.gradle.kts (100%) rename client/src/main/java/ai/docling/client/DoclingClient.java => docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeClient.java (87%) rename client/src/main/java/ai/docling/client/DoclingClientBuilderFactory.java => docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeClientBuilderFactory.java (57%) rename client/src/main/java/ai/docling/client/DoclingJackson2Client.java => docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeJackson2Client.java (73%) rename client/src/main/java/ai/docling/client/DoclingJackson3Client.java => docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeJackson3Client.java (67%) create mode 100644 docling-serve-client/src/main/java/ai/docling/client/serve/package-info.java rename client/src/test/java/ai/docling/client/AbstractDoclingClientTests.java => docling-serve-client/src/test/java/ai/docling/client/serve/AbstractDoclingServeClientTests.java (90%) rename client/src/test/java/ai/docling/client/DoclingClientBuilderFactoryTests.java => docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeClientBuilderFactoryTests.java (75%) create mode 100644 docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson2ClientTests.java create mode 100644 docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson3ClientTests.java rename {client => docling-serve-client}/src/test/resources/story.pdf (100%) rename {testcontainers => docling-testcontainers}/build.gradle.kts (100%) rename {testcontainers => docling-testcontainers}/src/main/java/ai/docling/testcontainers/package-info.java (100%) rename testcontainers/src/main/java/ai/docling/testcontainers/DoclingContainer.java => docling-testcontainers/src/main/java/ai/docling/testcontainers/serve/DoclingServeContainer.java (69%) rename {testcontainers/src/main/java/ai/docling/testcontainers => docling-testcontainers/src/main/java/ai/docling/testcontainers/serve}/config/DefaultDoclingContainerConfig.java (86%) rename testcontainers/src/main/java/ai/docling/testcontainers/config/DoclingContainerConfig.java => docling-testcontainers/src/main/java/ai/docling/testcontainers/serve/config/DoclingServeContainerConfig.java (85%) create mode 100644 docling-testcontainers/src/main/java/ai/docling/testcontainers/serve/config/package-info.java create mode 100644 docling-testcontainers/src/main/java/ai/docling/testcontainers/serve/package-info.java rename testcontainers/src/test/java/ai/docling/testcontainers/DoclingContainerAvailableTests.java => docling-testcontainers/src/test/java/ai/docling/testcontainers/serve/DoclingServeContainerAvailableTests.java (82%) rename {testing => docling-testing}/build.gradle.kts (100%) delete mode 100644 docs/src/doc/docs/client.md create mode 100644 docs/src/doc/docs/serve-client.md diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b1b9a05..774a2a4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,10 +28,10 @@ jobs: - 21 - 25 module: - - api - - client - - testing - - testcontainers + - docling-api + - docling-serve-client + - docling-testing + - docling-testcontainers name: jvm-build-test-${{ matrix.module }}-java${{ matrix.java }} steps: - uses: actions/checkout@v5 @@ -46,7 +46,7 @@ jobs: - name: build-test-${{ matrix.module }}-java${{ matrix.java }} run: | ./gradlew --no-daemon -Pjava.version=${{ matrix.java }} \ - :docling-${{ matrix.module }}:clean :docling-${{ matrix.module }}:build + :docling-${{ matrix.module }}:clean :${{ matrix.module }}:build docs: runs-on: ubuntu-latest @@ -68,4 +68,4 @@ jobs: cache: gradle - name: Generate documentation - run: ./gradlew --console=plain clean :docling-docs:mkdocsBuild + run: ./gradlew --console=plain clean :docs:mkdocsBuild diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 739daa5..51b528d 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -45,7 +45,7 @@ jobs: cache: gradle - name: Generate documentation - run: ./gradlew --console=plain clean :docling-docs:mkdocsBuild + run: ./gradlew --console=plain clean :docs:mkdocsBuild - name: Setup pages uses: actions/configure-pages@v5 diff --git a/README.md b/README.md index 680b4aa..d94a209 100644 --- a/README.md +++ b/README.md @@ -26,10 +26,10 @@ This is repository for Docling Java, a Java API for using [Docling](https://gith This project aims to provide the following artifacts: -* [`docling-api`](api): The core API for interacting with Docling. Should be framework-agnostic. -* [`docling-client`](client): A reference implementation of the [`docling-api`](api) using Java's [`HttpClient`](https://openjdk.org/groups/net/httpclient/intro.html) and [Jackson](https://github.com/FasterXML/jackson). -* [`docling-testing`](testing): Utilities for testing Docling -* [`docling-testcontainers`](testcontainers): A [Testcontainers module](https://testcontainers.com/) for running Docling in a Docker container. +* [`docling-api`](docling-api): The core API for interacting with Docling. Should be framework-agnostic. +* [`docling-serve-client`](docling-serve-client): A reference implementation of the [`docling-api`](docling-api) using Java's [`HttpClient`](https://openjdk.org/groups/net/httpclient/intro.html) and [Jackson](https://github.com/FasterXML/jackson) to connect to a [Docling Serve](https://github.com/docling-project/docling-serve) endpoint. +* [`docling-testing`](docling-testing): Utilities for testing Docling integration. +* [`docling-testcontainers`](docling-testcontainers): A [Testcontainers module](https://testcontainers.com/) for running Docling in a Docker container. ## Getting started @@ -38,19 +38,20 @@ Use `DoclingApi.convertSource()` to convert individual documents. For example: ```java import ai.docling.api.DoclingApi; import ai.docling.api.convert.request.ConvertDocumentRequest; +import ai.docling.api.convert.request.source.HttpSource; import ai.docling.api.convert.response.ConvertDocumentResponse; -import ai.docling.client.DoclingClient; +import ai.docling.client.serve.DoclingServeClientBuilderFactory; -DoclingApi doclingApi = DoclingClient.builder() - .baseUrl("") +DoclingApi doclingApi = DoclingServeClientBuilderFactory.newBuilder() + .baseUrl("") .build(); -ConvertDocumentRequest request = ConvertDocumentRequest.builder() - .addHttpSources(URI.create("https://arxiv.org/pdf/2408.09869")) +ConvertDocumentRequest request = new ConvertDocumentRequest() + .withSources(List.of(new HttpSource().withUrl(URI.create("https://arxiv.org/pdf/2408.09869")))) .build(); ConvertDocumentResponse response = doclingApi.convertSource(request); -System.out.println(response.document().markdownContent()); +System.out.println(response.getDocument().getMarkdownContent()); ``` More [usage information](https://docling-project.github.io/docling-java) are available in the docs. @@ -59,7 +60,6 @@ More [usage information](https://docling-project.github.io/docling-java) are ava Please feel free to connect with us using the [discussion section](https://github.com/docling-project/docling-java/discussions). - ## Contributing Please read [Contributing to Docling Java](CONTRIBUTING.md) for details. diff --git a/api/src/main/java/ai/docling/api/convert/package-info.java b/api/src/main/java/ai/docling/api/convert/package-info.java deleted file mode 100644 index d2999f2..0000000 --- a/api/src/main/java/ai/docling/api/convert/package-info.java +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Docling Serve - Convert APIs. - */ -@NullMarked -package ai.docling.api.convert; - -import org.jspecify.annotations.NullMarked; diff --git a/client/src/main/java/ai/docling/client/package-info.java b/client/src/main/java/ai/docling/client/package-info.java deleted file mode 100644 index bd0bc15..0000000 --- a/client/src/main/java/ai/docling/client/package-info.java +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Docling Java Client. - */ -@NullMarked -package ai.docling.client; - -import org.jspecify.annotations.NullMarked; diff --git a/client/src/test/java/ai/docling/client/DoclingJackson2ClientTests.java b/client/src/test/java/ai/docling/client/DoclingJackson2ClientTests.java deleted file mode 100644 index 97a0aeb..0000000 --- a/client/src/test/java/ai/docling/client/DoclingJackson2ClientTests.java +++ /dev/null @@ -1,25 +0,0 @@ -package ai.docling.client; - -import org.junit.jupiter.api.BeforeAll; - -import ai.docling.api.DoclingApi; -import ai.docling.testcontainers.config.DoclingContainerConfig; - -/** - * Integration tests for {@link DoclingJackson2Client}. - */ -class DoclingJackson2ClientTests extends AbstractDoclingClientTests { - private static DoclingApi doclingClient; - - @BeforeAll - static void setUp() { - doclingClient = DoclingJackson2Client.builder() - .baseUrl("http://localhost:%s".formatted(doclingContainer.getMappedPort(DoclingContainerConfig.DEFAULT_DOCLING_PORT))) - .build(); - } - - @Override - protected DoclingApi getDoclingClient() { - return doclingClient; - } -} diff --git a/client/src/test/java/ai/docling/client/DoclingJackson3ClientTests.java b/client/src/test/java/ai/docling/client/DoclingJackson3ClientTests.java deleted file mode 100644 index f6a2a05..0000000 --- a/client/src/test/java/ai/docling/client/DoclingJackson3ClientTests.java +++ /dev/null @@ -1,25 +0,0 @@ -package ai.docling.client; - -import org.junit.jupiter.api.BeforeAll; - -import ai.docling.api.DoclingApi; -import ai.docling.testcontainers.config.DoclingContainerConfig; - -/** - * Integration tests for {@link DoclingClient}. - */ -class DoclingJackson3ClientTests extends AbstractDoclingClientTests { - private static DoclingApi doclingClient; - - @BeforeAll - static void setUp() { - doclingClient = DoclingJackson3Client.builder() - .baseUrl("http://localhost:%s".formatted(doclingContainer.getMappedPort(DoclingContainerConfig.DEFAULT_DOCLING_PORT))) - .build(); - } - - @Override - protected DoclingApi getDoclingClient() { - return doclingClient; - } -} diff --git a/api/build.gradle.kts b/docling-api/build.gradle.kts similarity index 100% rename from api/build.gradle.kts rename to docling-api/build.gradle.kts diff --git a/api/src/main/java/ai/docling/api/DoclingApi.java b/docling-api/src/main/java/ai/docling/api/DoclingApi.java similarity index 100% rename from api/src/main/java/ai/docling/api/DoclingApi.java rename to docling-api/src/main/java/ai/docling/api/DoclingApi.java diff --git a/api/src/main/java/ai/docling/api/convert/request/ConvertDocumentRequest.java b/docling-api/src/main/java/ai/docling/api/convert/request/ConvertDocumentRequest.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/request/ConvertDocumentRequest.java rename to docling-api/src/main/java/ai/docling/api/convert/request/ConvertDocumentRequest.java diff --git a/api/src/main/java/ai/docling/api/convert/request/options/ConvertDocumentOptions.java b/docling-api/src/main/java/ai/docling/api/convert/request/options/ConvertDocumentOptions.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/request/options/ConvertDocumentOptions.java rename to docling-api/src/main/java/ai/docling/api/convert/request/options/ConvertDocumentOptions.java diff --git a/api/src/main/java/ai/docling/api/convert/request/options/ImageRefMode.java b/docling-api/src/main/java/ai/docling/api/convert/request/options/ImageRefMode.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/request/options/ImageRefMode.java rename to docling-api/src/main/java/ai/docling/api/convert/request/options/ImageRefMode.java diff --git a/api/src/main/java/ai/docling/api/convert/request/options/InputFormat.java b/docling-api/src/main/java/ai/docling/api/convert/request/options/InputFormat.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/request/options/InputFormat.java rename to docling-api/src/main/java/ai/docling/api/convert/request/options/InputFormat.java diff --git a/api/src/main/java/ai/docling/api/convert/request/options/OcrEngine.java b/docling-api/src/main/java/ai/docling/api/convert/request/options/OcrEngine.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/request/options/OcrEngine.java rename to docling-api/src/main/java/ai/docling/api/convert/request/options/OcrEngine.java diff --git a/api/src/main/java/ai/docling/api/convert/request/options/OutputFormat.java b/docling-api/src/main/java/ai/docling/api/convert/request/options/OutputFormat.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/request/options/OutputFormat.java rename to docling-api/src/main/java/ai/docling/api/convert/request/options/OutputFormat.java diff --git a/api/src/main/java/ai/docling/api/convert/request/options/PdfBackend.java b/docling-api/src/main/java/ai/docling/api/convert/request/options/PdfBackend.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/request/options/PdfBackend.java rename to docling-api/src/main/java/ai/docling/api/convert/request/options/PdfBackend.java diff --git a/api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionApi.java b/docling-api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionApi.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionApi.java rename to docling-api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionApi.java diff --git a/api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionLocal.java b/docling-api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionLocal.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionLocal.java rename to docling-api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionLocal.java diff --git a/api/src/main/java/ai/docling/api/convert/request/options/ProcessingPipeline.java b/docling-api/src/main/java/ai/docling/api/convert/request/options/ProcessingPipeline.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/request/options/ProcessingPipeline.java rename to docling-api/src/main/java/ai/docling/api/convert/request/options/ProcessingPipeline.java diff --git a/api/src/main/java/ai/docling/api/convert/request/options/ResponseFormat.java b/docling-api/src/main/java/ai/docling/api/convert/request/options/ResponseFormat.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/request/options/ResponseFormat.java rename to docling-api/src/main/java/ai/docling/api/convert/request/options/ResponseFormat.java diff --git a/api/src/main/java/ai/docling/api/convert/request/options/TableFormerMode.java b/docling-api/src/main/java/ai/docling/api/convert/request/options/TableFormerMode.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/request/options/TableFormerMode.java rename to docling-api/src/main/java/ai/docling/api/convert/request/options/TableFormerMode.java diff --git a/api/src/main/java/ai/docling/api/convert/request/options/VlmModelApi.java b/docling-api/src/main/java/ai/docling/api/convert/request/options/VlmModelApi.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/request/options/VlmModelApi.java rename to docling-api/src/main/java/ai/docling/api/convert/request/options/VlmModelApi.java diff --git a/api/src/main/java/ai/docling/api/convert/request/options/VlmModelLocal.java b/docling-api/src/main/java/ai/docling/api/convert/request/options/VlmModelLocal.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/request/options/VlmModelLocal.java rename to docling-api/src/main/java/ai/docling/api/convert/request/options/VlmModelLocal.java diff --git a/api/src/main/java/ai/docling/api/convert/request/options/VlmModelType.java b/docling-api/src/main/java/ai/docling/api/convert/request/options/VlmModelType.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/request/options/VlmModelType.java rename to docling-api/src/main/java/ai/docling/api/convert/request/options/VlmModelType.java diff --git a/api/src/main/java/ai/docling/api/convert/request/options/package-info.java b/docling-api/src/main/java/ai/docling/api/convert/request/options/package-info.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/request/options/package-info.java rename to docling-api/src/main/java/ai/docling/api/convert/request/options/package-info.java diff --git a/api/src/main/java/ai/docling/api/convert/request/package-info.java b/docling-api/src/main/java/ai/docling/api/convert/request/package-info.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/request/package-info.java rename to docling-api/src/main/java/ai/docling/api/convert/request/package-info.java diff --git a/api/src/main/java/ai/docling/api/convert/request/source/FileSource.java b/docling-api/src/main/java/ai/docling/api/convert/request/source/FileSource.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/request/source/FileSource.java rename to docling-api/src/main/java/ai/docling/api/convert/request/source/FileSource.java diff --git a/api/src/main/java/ai/docling/api/convert/request/source/HttpSource.java b/docling-api/src/main/java/ai/docling/api/convert/request/source/HttpSource.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/request/source/HttpSource.java rename to docling-api/src/main/java/ai/docling/api/convert/request/source/HttpSource.java diff --git a/api/src/main/java/ai/docling/api/convert/request/source/Source.java b/docling-api/src/main/java/ai/docling/api/convert/request/source/Source.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/request/source/Source.java rename to docling-api/src/main/java/ai/docling/api/convert/request/source/Source.java diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/source/package-info.java b/docling-api/src/main/java/ai/docling/api/convert/request/source/package-info.java new file mode 100644 index 0000000..59dd975 --- /dev/null +++ b/docling-api/src/main/java/ai/docling/api/convert/request/source/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package ai.docling.api.convert.request.source; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/ai/docling/api/convert/request/target/InBodyTarget.java b/docling-api/src/main/java/ai/docling/api/convert/request/target/InBodyTarget.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/request/target/InBodyTarget.java rename to docling-api/src/main/java/ai/docling/api/convert/request/target/InBodyTarget.java diff --git a/api/src/main/java/ai/docling/api/convert/request/target/PutTarget.java b/docling-api/src/main/java/ai/docling/api/convert/request/target/PutTarget.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/request/target/PutTarget.java rename to docling-api/src/main/java/ai/docling/api/convert/request/target/PutTarget.java diff --git a/api/src/main/java/ai/docling/api/convert/request/target/Target.java b/docling-api/src/main/java/ai/docling/api/convert/request/target/Target.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/request/target/Target.java rename to docling-api/src/main/java/ai/docling/api/convert/request/target/Target.java diff --git a/api/src/main/java/ai/docling/api/convert/request/target/ZipTarget.java b/docling-api/src/main/java/ai/docling/api/convert/request/target/ZipTarget.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/request/target/ZipTarget.java rename to docling-api/src/main/java/ai/docling/api/convert/request/target/ZipTarget.java diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/target/package-info.java b/docling-api/src/main/java/ai/docling/api/convert/request/target/package-info.java new file mode 100644 index 0000000..66dfd27 --- /dev/null +++ b/docling-api/src/main/java/ai/docling/api/convert/request/target/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package ai.docling.api.convert.request.target; + +import org.jspecify.annotations.NullMarked; diff --git a/api/src/main/java/ai/docling/api/convert/response/ConvertDocumentResponse.java b/docling-api/src/main/java/ai/docling/api/convert/response/ConvertDocumentResponse.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/response/ConvertDocumentResponse.java rename to docling-api/src/main/java/ai/docling/api/convert/response/ConvertDocumentResponse.java diff --git a/api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java b/docling-api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java rename to docling-api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java diff --git a/api/src/main/java/ai/docling/api/convert/response/ErrorItem.java b/docling-api/src/main/java/ai/docling/api/convert/response/ErrorItem.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/response/ErrorItem.java rename to docling-api/src/main/java/ai/docling/api/convert/response/ErrorItem.java diff --git a/api/src/main/java/ai/docling/api/convert/response/package-info.java b/docling-api/src/main/java/ai/docling/api/convert/response/package-info.java similarity index 100% rename from api/src/main/java/ai/docling/api/convert/response/package-info.java rename to docling-api/src/main/java/ai/docling/api/convert/response/package-info.java diff --git a/api/src/main/java/ai/docling/api/health/HealthCheckResponse.java b/docling-api/src/main/java/ai/docling/api/health/HealthCheckResponse.java similarity index 100% rename from api/src/main/java/ai/docling/api/health/HealthCheckResponse.java rename to docling-api/src/main/java/ai/docling/api/health/HealthCheckResponse.java diff --git a/api/src/main/java/ai/docling/api/health/package-info.java b/docling-api/src/main/java/ai/docling/api/health/package-info.java similarity index 100% rename from api/src/main/java/ai/docling/api/health/package-info.java rename to docling-api/src/main/java/ai/docling/api/health/package-info.java diff --git a/api/src/main/java/ai/docling/api/package-info.java b/docling-api/src/main/java/ai/docling/api/package-info.java similarity index 100% rename from api/src/main/java/ai/docling/api/package-info.java rename to docling-api/src/main/java/ai/docling/api/package-info.java diff --git a/api/src/main/java/ai/docling/api/util/Utils.java b/docling-api/src/main/java/ai/docling/api/util/Utils.java similarity index 100% rename from api/src/main/java/ai/docling/api/util/Utils.java rename to docling-api/src/main/java/ai/docling/api/util/Utils.java diff --git a/api/src/main/java/ai/docling/api/util/ValidationUtils.java b/docling-api/src/main/java/ai/docling/api/util/ValidationUtils.java similarity index 100% rename from api/src/main/java/ai/docling/api/util/ValidationUtils.java rename to docling-api/src/main/java/ai/docling/api/util/ValidationUtils.java diff --git a/testcontainers/src/main/java/ai/docling/testcontainers/config/package-info.java b/docling-api/src/main/java/ai/docling/api/util/package-info.java similarity index 57% rename from testcontainers/src/main/java/ai/docling/testcontainers/config/package-info.java rename to docling-api/src/main/java/ai/docling/api/util/package-info.java index 2b6e2d9..bad3294 100644 --- a/testcontainers/src/main/java/ai/docling/testcontainers/config/package-info.java +++ b/docling-api/src/main/java/ai/docling/api/util/package-info.java @@ -1,4 +1,4 @@ @NullMarked -package ai.docling.testcontainers.config; +package ai.docling.api.util; import org.jspecify.annotations.NullMarked; diff --git a/api/src/test/java/ai/docling/api/convert/request/ConvertDocumentRequestTests.java b/docling-api/src/test/java/ai/docling/api/convert/request/ConvertDocumentRequestTests.java similarity index 100% rename from api/src/test/java/ai/docling/api/convert/request/ConvertDocumentRequestTests.java rename to docling-api/src/test/java/ai/docling/api/convert/request/ConvertDocumentRequestTests.java diff --git a/api/src/test/java/ai/docling/api/convert/request/options/ConvertDocumentOptionsTests.java b/docling-api/src/test/java/ai/docling/api/convert/request/options/ConvertDocumentOptionsTests.java similarity index 100% rename from api/src/test/java/ai/docling/api/convert/request/options/ConvertDocumentOptionsTests.java rename to docling-api/src/test/java/ai/docling/api/convert/request/options/ConvertDocumentOptionsTests.java diff --git a/api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionApiTests.java b/docling-api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionApiTests.java similarity index 100% rename from api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionApiTests.java rename to docling-api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionApiTests.java diff --git a/api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionLocalTests.java b/docling-api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionLocalTests.java similarity index 100% rename from api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionLocalTests.java rename to docling-api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionLocalTests.java diff --git a/api/src/test/java/ai/docling/api/convert/request/source/FileSourceTests.java b/docling-api/src/test/java/ai/docling/api/convert/request/source/FileSourceTests.java similarity index 100% rename from api/src/test/java/ai/docling/api/convert/request/source/FileSourceTests.java rename to docling-api/src/test/java/ai/docling/api/convert/request/source/FileSourceTests.java diff --git a/api/src/test/java/ai/docling/api/convert/request/source/HttpSourceTests.java b/docling-api/src/test/java/ai/docling/api/convert/request/source/HttpSourceTests.java similarity index 100% rename from api/src/test/java/ai/docling/api/convert/request/source/HttpSourceTests.java rename to docling-api/src/test/java/ai/docling/api/convert/request/source/HttpSourceTests.java diff --git a/api/src/test/java/ai/docling/api/convert/request/target/InBodyTargetTests.java b/docling-api/src/test/java/ai/docling/api/convert/request/target/InBodyTargetTests.java similarity index 100% rename from api/src/test/java/ai/docling/api/convert/request/target/InBodyTargetTests.java rename to docling-api/src/test/java/ai/docling/api/convert/request/target/InBodyTargetTests.java diff --git a/api/src/test/java/ai/docling/api/convert/request/target/PutTargetTests.java b/docling-api/src/test/java/ai/docling/api/convert/request/target/PutTargetTests.java similarity index 100% rename from api/src/test/java/ai/docling/api/convert/request/target/PutTargetTests.java rename to docling-api/src/test/java/ai/docling/api/convert/request/target/PutTargetTests.java diff --git a/api/src/test/java/ai/docling/api/convert/request/target/ZipTargetTests.java b/docling-api/src/test/java/ai/docling/api/convert/request/target/ZipTargetTests.java similarity index 100% rename from api/src/test/java/ai/docling/api/convert/request/target/ZipTargetTests.java rename to docling-api/src/test/java/ai/docling/api/convert/request/target/ZipTargetTests.java diff --git a/api/src/test/java/ai/docling/api/convert/response/ConvertDocumentResponseTests.java b/docling-api/src/test/java/ai/docling/api/convert/response/ConvertDocumentResponseTests.java similarity index 100% rename from api/src/test/java/ai/docling/api/convert/response/ConvertDocumentResponseTests.java rename to docling-api/src/test/java/ai/docling/api/convert/response/ConvertDocumentResponseTests.java diff --git a/api/src/test/java/ai/docling/api/convert/response/DocumentResponseTests.java b/docling-api/src/test/java/ai/docling/api/convert/response/DocumentResponseTests.java similarity index 100% rename from api/src/test/java/ai/docling/api/convert/response/DocumentResponseTests.java rename to docling-api/src/test/java/ai/docling/api/convert/response/DocumentResponseTests.java diff --git a/api/src/test/java/ai/docling/api/convert/response/ErrorItemTests.java b/docling-api/src/test/java/ai/docling/api/convert/response/ErrorItemTests.java similarity index 100% rename from api/src/test/java/ai/docling/api/convert/response/ErrorItemTests.java rename to docling-api/src/test/java/ai/docling/api/convert/response/ErrorItemTests.java diff --git a/api/src/test/java/ai/docling/api/health/HealthCheckResponseTests.java b/docling-api/src/test/java/ai/docling/api/health/HealthCheckResponseTests.java similarity index 100% rename from api/src/test/java/ai/docling/api/health/HealthCheckResponseTests.java rename to docling-api/src/test/java/ai/docling/api/health/HealthCheckResponseTests.java diff --git a/api/src/test/java/ai/docling/api/util/UtilsTests.java b/docling-api/src/test/java/ai/docling/api/util/UtilsTests.java similarity index 100% rename from api/src/test/java/ai/docling/api/util/UtilsTests.java rename to docling-api/src/test/java/ai/docling/api/util/UtilsTests.java diff --git a/api/src/test/java/ai/docling/api/util/ValidationUtilsTests.java b/docling-api/src/test/java/ai/docling/api/util/ValidationUtilsTests.java similarity index 100% rename from api/src/test/java/ai/docling/api/util/ValidationUtilsTests.java rename to docling-api/src/test/java/ai/docling/api/util/ValidationUtilsTests.java diff --git a/client/build.gradle.kts b/docling-serve-client/build.gradle.kts similarity index 100% rename from client/build.gradle.kts rename to docling-serve-client/build.gradle.kts diff --git a/client/src/main/java/ai/docling/client/DoclingClient.java b/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeClient.java similarity index 87% rename from client/src/main/java/ai/docling/client/DoclingClient.java rename to docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeClient.java index 6ce7898..99cdd9c 100644 --- a/client/src/main/java/ai/docling/client/DoclingClient.java +++ b/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeClient.java @@ -1,4 +1,4 @@ -package ai.docling.client; +package ai.docling.client.serve; import static ai.docling.api.util.ValidationUtils.ensureNotBlank; import static ai.docling.api.util.ValidationUtils.ensureNotNull; @@ -26,13 +26,13 @@ *

Concrete subclasses must implement {@link #readValue(String, Class)} and * {@link #writeValueAsString(Object)} for serialization and deserialization behavior. */ -public abstract class DoclingClient implements DoclingApi { +public abstract class DoclingServeClient implements DoclingApi { protected static final URI DEFAULT_BASE_URL = URI.create("http://localhost:5001"); private final URI baseUrl; private final HttpClient httpClient; - protected DoclingClient(DoclingClientBuilder builder) { + protected DoclingServeClient(DoclingServeClientBuilder builder) { this.baseUrl = ensureNotNull(builder.baseUrl, "baseUrl"); if (Objects.equals(this.baseUrl.getScheme(), "http")) { @@ -96,37 +96,37 @@ public ConvertDocumentResponse convertSource(ConvertDocumentRequest request) { } /** - * Abstract base class for building instances of {@link DoclingClient}. + * Abstract base class for building instances of {@link DoclingServeClient}. * *

This builder class provides methods for configuring shared properties such as the * base URL and the HTTP client. Concrete subclasses may extend this builder to * add additional configuration options. * - * @param the type of {@link DoclingClient} being built + * @param the type of {@link DoclingServeClient} being built * @param the type of the builder implementation */ - public abstract static class DoclingClientBuilder> implements DoclingApiBuilder { + public abstract static class DoclingServeClientBuilder> implements DoclingApiBuilder { private URI baseUrl = DEFAULT_BASE_URL; private HttpClient.Builder httpClientBuilder = HttpClient.newBuilder(); /** - * Protected constructor for use by subclasses of {@link DoclingClientBuilder}. + * Protected constructor for use by subclasses of {@link DoclingServeClientBuilder}. * *

Initializes a new instance of the builder with default configuration values. * This constructor ensures that the builder cannot be instantiated directly, * promoting proper use and extension by subclasses. */ - protected DoclingClientBuilder() { + protected DoclingServeClientBuilder() { } /** - * Initializes a new {@link DoclingClientBuilder} instance using the configuration - * from the provided {@link DoclingClient}. + * Initializes a new {@link DoclingServeClientBuilder} instance using the configuration + * from the provided {@link DoclingServeClient}. * - * @param doclingClient the {@link DoclingClient} whose configuration (e.g., base URL) + * @param doclingClient the {@link DoclingServeClient} whose configuration (e.g., base URL) * will be used to initialize the builder */ - protected DoclingClientBuilder(DoclingClient doclingClient) { + protected DoclingServeClientBuilder(DoclingServeClient doclingClient) { this.baseUrl = doclingClient.baseUrl; this.httpClientBuilder = HttpClient.newBuilder(); } @@ -168,7 +168,7 @@ public B baseUrl(URI baseUrl) { * proxy settings, SSL context, and other connection parameters. * * @param httpClientBuilder the {@link HttpClient.Builder} to use - * @return this {@link DoclingJackson3Client.Builder} instance for method chaining + * @return this {@link DoclingServeJackson3Client.Builder} instance for method chaining */ public B httpClientBuilder(HttpClient.Builder httpClientBuilder) { this.httpClientBuilder = httpClientBuilder; diff --git a/client/src/main/java/ai/docling/client/DoclingClientBuilderFactory.java b/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeClientBuilderFactory.java similarity index 57% rename from client/src/main/java/ai/docling/client/DoclingClientBuilderFactory.java rename to docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeClientBuilderFactory.java index 6366dc5..b8dbdf1 100644 --- a/client/src/main/java/ai/docling/client/DoclingClientBuilderFactory.java +++ b/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeClientBuilderFactory.java @@ -1,71 +1,71 @@ -package ai.docling.client; +package ai.docling.client.serve; -import ai.docling.client.DoclingClient.DoclingClientBuilder; +import ai.docling.client.serve.DoclingServeClient.DoclingServeClientBuilder; /** - * A factory class for creating instances of {@link DoclingClientBuilder}. + * A factory class for creating instances of {@link DoclingServeClientBuilder}. * *

This factory determines which version of Jackson (2 or 3) is available * on the application's classpath and provides a corresponding builder for - * creating {@link DoclingClient} implementations. + * creating {@link DoclingServeClient} implementations. * *

If neither Jackson 2 nor Jackson 3 is present on the classpath, an * {@link IllegalStateException} is thrown. * *

The factory uses a type-safe generic method to support custom subclasses of - * {@link DoclingClient} and {@link DoclingClientBuilder}. + * {@link DoclingServeClient} and {@link DoclingServeClientBuilder}. */ -public final class DoclingClientBuilderFactory { - private DoclingClientBuilderFactory() { +public final class DoclingServeClientBuilderFactory { + private DoclingServeClientBuilderFactory() { } /** - * Creates and returns a new instance of a {@link DoclingClientBuilder} compatible + * Creates and returns a new instance of a {@link DoclingServeClientBuilder} compatible * with the Jackson version present on the provided classloader's classpath. * - *

If Jackson 3 is detected, returns a builder for {@code DoclingJackson3Client}. - * If Jackson 2 is detected, returns a builder for {@code DoclingJackson2Client}. + *

If Jackson 3 is detected, returns a builder for {@code DoclingServeJackson3Client}. + * If Jackson 2 is detected, returns a builder for {@code DoclingServeJackson2Client}. * If neither version of Jackson is found on the classpath, an * {@link IllegalStateException} is thrown. * *

The method uses generics to support custom implementations of - * {@link DoclingClient} and {@link DoclingClientBuilder}. + * {@link DoclingServeClient} and {@link DoclingServeClientBuilder}. * - * @param the type of {@link DoclingClient} to be created by the builder - * @param the type of {@link DoclingClientBuilder} to be returned + * @param the type of {@link DoclingServeClient} to be created by the builder + * @param the type of {@link DoclingServeClientBuilder} to be returned * @param classLoader the {@link ClassLoader} used to check for Jackson's presence - * @return a compatible {@link DoclingClientBuilder} instance + * @return a compatible {@link DoclingServeClientBuilder} instance * @throws IllegalStateException if neither Jackson 2 nor Jackson 3 is available on the classpath */ - public static > B newBuilder(ClassLoader classLoader) { + public static > B newBuilder(ClassLoader classLoader) { if (JacksonVersion.JACKSON_3.isOnClasspath(classLoader)) { - return (B) DoclingJackson3Client.builder(); + return (B) DoclingServeJackson3Client.builder(); } else if (JacksonVersion.JACKSON_2.isOnClasspath(classLoader)) { - return (B) DoclingJackson2Client.builder(); + return (B) DoclingServeJackson2Client.builder(); } throw new IllegalStateException("Neither Jackson 2 or 3 is on the classpath"); } /** - * Creates and returns a new instance of a {@link DoclingClientBuilder} that is + * Creates and returns a new instance of a {@link DoclingServeClientBuilder} that is * compatible with the version of Jackson available on the application's classpath. * *

If Jackson 3 is found on the classpath, it returns a builder for - * {@code DoclingJackson3Client}. If Jackson 2 is found, it returns a builder - * for {@code DoclingJackson2Client}. If neither are found, an + * {@code DoclingServeJackson3Client}. If Jackson 2 is found, it returns a builder + * for {@code DoclingServeJackson2Client}. If neither are found, an * {@link IllegalStateException} is thrown. * *

This method utilizes generics to support custom implementations of - * {@link DoclingClient} and {@link DoclingClientBuilder}. + * {@link DoclingServeClient} and {@link DoclingServeClientBuilder}. * - * @param the type of {@link DoclingClient} to be created by the builder - * @param the type of {@link DoclingClientBuilder} to be returned - * @return a compatible {@link DoclingClientBuilder} instance + * @param the type of {@link DoclingServeClient} to be created by the builder + * @param the type of {@link DoclingServeClientBuilder} to be returned + * @return a compatible {@link DoclingServeClientBuilder} instance * @throws IllegalStateException if neither Jackson 2 nor Jackson 3 is available on the classpath */ - public static > B newBuilder() { + public static > B newBuilder() { return newBuilder(Thread.currentThread().getContextClassLoader()); } diff --git a/client/src/main/java/ai/docling/client/DoclingJackson2Client.java b/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeJackson2Client.java similarity index 73% rename from client/src/main/java/ai/docling/client/DoclingJackson2Client.java rename to docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeJackson2Client.java index 155d071..dd5b653 100644 --- a/client/src/main/java/ai/docling/client/DoclingJackson2Client.java +++ b/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeJackson2Client.java @@ -1,4 +1,4 @@ -package ai.docling.client; +package ai.docling.client.serve; import static ai.docling.api.util.ValidationUtils.ensureNotNull; @@ -6,7 +6,7 @@ import com.fasterxml.jackson.databind.json.JsonMapper; /** - * A specialized implementation of {@link DoclingClient} that integrates with the Jackson + * A specialized implementation of {@link DoclingServeClient} that integrates with the Jackson * library for JSON serialization and deserialization using {@link com.fasterxml.jackson.databind.json.JsonMapper}. *

* This client provides methods to convert objects to JSON strings and parse JSON strings @@ -15,10 +15,10 @@ *

Instances of this client can be created through the {@link Builder} by configuring * properties such as the {@link com.fasterxml.jackson.databind.json.JsonMapper.Builder}. */ -public class DoclingJackson2Client extends DoclingClient { +public class DoclingServeJackson2Client extends DoclingServeClient { private final JsonMapper jsonMapper; - private DoclingJackson2Client(Builder builder) { + private DoclingServeJackson2Client(Builder builder) { super(builder);; this.jsonMapper = ensureNotNull(builder.jsonMapperBuilder, "jsonMapperBuilder").build(); } @@ -30,13 +30,13 @@ public Builder toBuilder() { /** * Creates and returns a new {@link Builder} instance for configuring and constructing - * a {@link DoclingJackson2Client}. + * a {@link DoclingServeJackson2Client}. * *

The returned {@link Builder} can be used to customize the client by setting * various properties, such as the JSON mapper builder, before creating a new - * {@link DoclingJackson2Client}. + * {@link DoclingServeJackson2Client}. * - * @return a new {@link Builder} instance for configuring and building a {@link DoclingJackson2Client} + * @return a new {@link Builder} instance for configuring and building a {@link DoclingServeJackson2Client} */ static Builder builder() { return new Builder(); @@ -61,21 +61,21 @@ protected String writeValueAsString(T value) { } /** - * A builder class for constructing instances of {@link DoclingJackson2Client}. + * A builder class for constructing instances of {@link DoclingServeJackson2Client}. * - *

This builder extends {@link DoclingClientBuilder} to provide additional configuration - * specific to {@link DoclingJackson2Client}, such as customizing the JSON mapper. - * Use this builder to configure and create a new, immutable {@link DoclingJackson2Client} instance. + *

This builder extends {@link DoclingServeClientBuilder} to provide additional configuration + * specific to {@link DoclingServeJackson2Client}, such as customizing the JSON mapper. + * Use this builder to configure and create a new, immutable {@link DoclingServeJackson2Client} instance. * *

The builder supports method chaining for fluent configuration. */ - public static final class Builder extends DoclingClientBuilder { + public static final class Builder extends DoclingServeClientBuilder { private JsonMapper.Builder jsonMapperBuilder = JsonMapper.builder(); private Builder() { } - private Builder(DoclingJackson2Client doclingClient) { + private Builder(DoclingServeJackson2Client doclingClient) { super(doclingClient); this.jsonMapperBuilder = doclingClient.jsonMapper.rebuild(); } @@ -95,16 +95,16 @@ public Builder jsonParser(JsonMapper.Builder jsonMapperBuilder) { } /** - * Builds and returns a new {@link DoclingJackson2Client} instance with the configured properties. + * Builds and returns a new {@link DoclingServeJackson2Client} instance with the configured properties. * *

This method validates all required parameters and constructs the client. * - * @return a new {@link DoclingJackson2Client} instance + * @return a new {@link DoclingServeJackson2Client} instance * @throws IllegalArgumentException if any required parameter is null */ @Override - public DoclingJackson2Client build() { - return new DoclingJackson2Client(this); + public DoclingServeJackson2Client build() { + return new DoclingServeJackson2Client(this); } } } diff --git a/client/src/main/java/ai/docling/client/DoclingJackson3Client.java b/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeJackson3Client.java similarity index 67% rename from client/src/main/java/ai/docling/client/DoclingJackson3Client.java rename to docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeJackson3Client.java index ee8fda9..ae70f1f 100644 --- a/client/src/main/java/ai/docling/client/DoclingJackson3Client.java +++ b/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeJackson3Client.java @@ -1,4 +1,4 @@ -package ai.docling.client; +package ai.docling.client.serve; import static ai.docling.api.util.ValidationUtils.ensureNotNull; @@ -7,13 +7,13 @@ /** * A client implementation that leverages Jackson 3 for JSON serialization and deserialization. * - *

This class extends {@link DoclingClient} and provides a concrete implementation for reading + *

This class extends {@link DoclingServeClient} and provides a concrete implementation for reading * and writing JSON data using a {@link JsonMapper} instance. */ -public class DoclingJackson3Client extends DoclingClient { +public class DoclingServeJackson3Client extends DoclingServeClient { private final JsonMapper jsonMapper; - private DoclingJackson3Client(Builder builder) { + private DoclingServeJackson3Client(Builder builder) { super(builder);; this.jsonMapper = ensureNotNull(builder.jsonMapperBuilder, "jsonMapperBuilder").build(); } @@ -24,10 +24,10 @@ public Builder toBuilder() { } /** - * Creates a new {@link Builder} instance for configuring and constructing a {@link DoclingJackson3Client}. + * Creates a new {@link Builder} instance for configuring and constructing a {@link DoclingServeJackson3Client}. * - *

This method serves as the entry point for creating a new {@link DoclingJackson3Client.Builder} - * to customize and build a {@code DoclingJackson3Client}. + *

This method serves as the entry point for creating a new {@link DoclingServeJackson3Client.Builder} + * to customize and build a {@code DoclingServeJackson3Client}. * * @return a new {@link Builder} instance */ @@ -46,25 +46,25 @@ protected String writeValueAsString(T value) { } /** - * A builder class for configuring and constructing instances of {@link DoclingJackson3Client}. + * A builder class for configuring and constructing instances of {@link DoclingServeJackson3Client}. * - *

This builder extends {@link DoclingClientBuilder}, allowing customization of common client + *

This builder extends {@link DoclingServeClientBuilder}, allowing customization of common client * parameters such as base URL and HTTP client, while also providing additional functionality * specific to JSON mapping via a {@link JsonMapper.Builder}. * *

Instances of this builder can be initialized either as a new builder or based on an - * existing {@link DoclingJackson3Client}. + * existing {@link DoclingServeJackson3Client}. * - *

This class is not publicly instantiable; use {@link DoclingJackson3Client#builder()} - * or {@link DoclingJackson3Client#toBuilder()} to obtain a builder instance. + *

This class is not publicly instantiable; use {@link DoclingServeJackson3Client#builder()} + * or {@link DoclingServeJackson3Client#toBuilder()} to obtain a builder instance. */ - public static final class Builder extends DoclingClientBuilder { + public static final class Builder extends DoclingServeClientBuilder { private JsonMapper.Builder jsonMapperBuilder = JsonMapper.builder(); private Builder() { } - private Builder(DoclingJackson3Client doclingClient) { + private Builder(DoclingServeJackson3Client doclingClient) { super(doclingClient); this.jsonMapperBuilder = doclingClient.jsonMapper.rebuild(); } @@ -84,16 +84,16 @@ public Builder jsonParser(JsonMapper.Builder jsonMapperBuilder) { } /** - * Builds and returns a new {@link DoclingJackson3Client} instance with the configured properties. + * Builds and returns a new {@link DoclingServeJackson3Client} instance with the configured properties. * *

This method validates all required parameters and constructs the client. * - * @return a new {@link DoclingJackson3Client} instance + * @return a new {@link DoclingServeJackson3Client} instance * @throws IllegalArgumentException if any required parameter is null */ @Override - public DoclingJackson3Client build() { - return new DoclingJackson3Client(this); + public DoclingServeJackson3Client build() { + return new DoclingServeJackson3Client(this); } } } diff --git a/docling-serve-client/src/main/java/ai/docling/client/serve/package-info.java b/docling-serve-client/src/main/java/ai/docling/client/serve/package-info.java new file mode 100644 index 0000000..1c3eb23 --- /dev/null +++ b/docling-serve-client/src/main/java/ai/docling/client/serve/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package ai.docling.client.serve; + +import org.jspecify.annotations.NullMarked; diff --git a/client/src/test/java/ai/docling/client/AbstractDoclingClientTests.java b/docling-serve-client/src/test/java/ai/docling/client/serve/AbstractDoclingServeClientTests.java similarity index 90% rename from client/src/test/java/ai/docling/client/AbstractDoclingClientTests.java rename to docling-serve-client/src/test/java/ai/docling/client/serve/AbstractDoclingServeClientTests.java index 9b61f51..335229f 100644 --- a/client/src/test/java/ai/docling/client/AbstractDoclingClientTests.java +++ b/docling-serve-client/src/test/java/ai/docling/client/serve/AbstractDoclingServeClientTests.java @@ -1,4 +1,4 @@ -package ai.docling.client; +package ai.docling.client.serve; import static org.assertj.core.api.Assertions.assertThat; @@ -20,13 +20,13 @@ import ai.docling.api.convert.request.source.HttpSource; import ai.docling.api.convert.response.ConvertDocumentResponse; import ai.docling.api.health.HealthCheckResponse; -import ai.docling.testcontainers.DoclingContainer; -import ai.docling.testcontainers.config.DoclingContainerConfig; +import ai.docling.testcontainers.serve.DoclingServeContainer; +import ai.docling.testcontainers.serve.config.DoclingServeContainerConfig; -abstract class AbstractDoclingClientTests { - protected static final DoclingContainer doclingContainer = new DoclingContainer( - DoclingContainerConfig.builder() - .imageName(DoclingContainerConfig.DOCLING_IMAGE) +abstract class AbstractDoclingServeClientTests { + protected static final DoclingServeContainer doclingContainer = new DoclingServeContainer( + DoclingServeContainerConfig.builder() + .imageName(DoclingServeContainerConfig.DOCLING_SERVE_IMAGE) .enableUi(true) .build(), Optional.of(Duration.ofMinutes(2)) diff --git a/client/src/test/java/ai/docling/client/DoclingClientBuilderFactoryTests.java b/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeClientBuilderFactoryTests.java similarity index 75% rename from client/src/test/java/ai/docling/client/DoclingClientBuilderFactoryTests.java rename to docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeClientBuilderFactoryTests.java index 73a8f00..9b80163 100644 --- a/client/src/test/java/ai/docling/client/DoclingClientBuilderFactoryTests.java +++ b/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeClientBuilderFactoryTests.java @@ -1,4 +1,4 @@ -package ai.docling.client; +package ai.docling.client.serve; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -12,14 +12,14 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import ai.docling.client.DoclingClient.DoclingClientBuilder; +import ai.docling.client.serve.DoclingServeClient.DoclingServeClientBuilder; -class DoclingClientBuilderFactoryTests { +class DoclingServeClientBuilderFactoryTests { @Test void correctBuilderWhenBothArePresent() { - assertThat(DoclingClientBuilderFactory.newBuilder()) + assertThat(DoclingServeClientBuilderFactory.newBuilder()) .isNotNull() - .isExactlyInstanceOf(DoclingJackson3Client.Builder.class); + .isExactlyInstanceOf(DoclingServeJackson3Client.Builder.class); } @Test @@ -27,7 +27,7 @@ void noBuilderWhenNeitherArePresent() { var classLoader = new ClassHidingClassLoader("com.fasterxml.jackson.databind.json.JsonMapper", "tools.jackson.databind.json.JsonMapper"); assertThatExceptionOfType(IllegalStateException.class) - .isThrownBy(() -> DoclingClientBuilderFactory.newBuilder(classLoader)) + .isThrownBy(() -> DoclingServeClientBuilderFactory.newBuilder(classLoader)) .withMessage("Neither Jackson 2 or 3 is on the classpath"); } @@ -36,15 +36,15 @@ void noBuilderWhenNeitherArePresent() { void correctBuilder(String classToHide, Class expectedBuilderClass) { var classLoader = new ClassHidingClassLoader(classToHide); - assertThat(DoclingClientBuilderFactory.newBuilder(classLoader)) + assertThat(DoclingServeClientBuilderFactory.newBuilder(classLoader)) .isNotNull() .isExactlyInstanceOf(expectedBuilderClass); } static Stream correctBuilderArguments() { return Stream.of( - Arguments.of("com.fasterxml.jackson.databind.json.JsonMapper", DoclingJackson3Client.Builder.class), - Arguments.of("tools.jackson.databind.json.JsonMapper", DoclingJackson2Client.Builder.class) + Arguments.of("com.fasterxml.jackson.databind.json.JsonMapper", DoclingServeJackson3Client.Builder.class), + Arguments.of("tools.jackson.databind.json.JsonMapper", DoclingServeJackson2Client.Builder.class) ); } diff --git a/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson2ClientTests.java b/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson2ClientTests.java new file mode 100644 index 0000000..9cc0b1f --- /dev/null +++ b/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson2ClientTests.java @@ -0,0 +1,25 @@ +package ai.docling.client.serve; + +import org.junit.jupiter.api.BeforeAll; + +import ai.docling.api.DoclingApi; +import ai.docling.testcontainers.serve.config.DoclingServeContainerConfig; + +/** + * Integration tests for {@link DoclingServeJackson2Client}. + */ +class DoclingServeJackson2ClientTests extends AbstractDoclingServeClientTests { + private static DoclingApi doclingClient; + + @BeforeAll + static void setUp() { + doclingClient = DoclingServeJackson2Client.builder() + .baseUrl("http://localhost:%s".formatted(doclingContainer.getMappedPort(DoclingServeContainerConfig.DEFAULT_DOCLING_SERVE_PORT))) + .build(); + } + + @Override + protected DoclingApi getDoclingClient() { + return doclingClient; + } +} diff --git a/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson3ClientTests.java b/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson3ClientTests.java new file mode 100644 index 0000000..75a4a9b --- /dev/null +++ b/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson3ClientTests.java @@ -0,0 +1,25 @@ +package ai.docling.client.serve; + +import org.junit.jupiter.api.BeforeAll; + +import ai.docling.api.DoclingApi; +import ai.docling.testcontainers.serve.config.DoclingServeContainerConfig; + +/** + * Integration tests for {@link DoclingServeClient}. + */ +class DoclingServeJackson3ClientTests extends AbstractDoclingServeClientTests { + private static DoclingApi doclingClient; + + @BeforeAll + static void setUp() { + doclingClient = DoclingServeJackson3Client.builder() + .baseUrl("http://localhost:%s".formatted(doclingContainer.getMappedPort(DoclingServeContainerConfig.DEFAULT_DOCLING_SERVE_PORT))) + .build(); + } + + @Override + protected DoclingApi getDoclingClient() { + return doclingClient; + } +} diff --git a/client/src/test/resources/story.pdf b/docling-serve-client/src/test/resources/story.pdf similarity index 100% rename from client/src/test/resources/story.pdf rename to docling-serve-client/src/test/resources/story.pdf diff --git a/testcontainers/build.gradle.kts b/docling-testcontainers/build.gradle.kts similarity index 100% rename from testcontainers/build.gradle.kts rename to docling-testcontainers/build.gradle.kts diff --git a/testcontainers/src/main/java/ai/docling/testcontainers/package-info.java b/docling-testcontainers/src/main/java/ai/docling/testcontainers/package-info.java similarity index 100% rename from testcontainers/src/main/java/ai/docling/testcontainers/package-info.java rename to docling-testcontainers/src/main/java/ai/docling/testcontainers/package-info.java diff --git a/testcontainers/src/main/java/ai/docling/testcontainers/DoclingContainer.java b/docling-testcontainers/src/main/java/ai/docling/testcontainers/serve/DoclingServeContainer.java similarity index 69% rename from testcontainers/src/main/java/ai/docling/testcontainers/DoclingContainer.java rename to docling-testcontainers/src/main/java/ai/docling/testcontainers/serve/DoclingServeContainer.java index 8d8127b..164cba3 100644 --- a/testcontainers/src/main/java/ai/docling/testcontainers/DoclingContainer.java +++ b/docling-testcontainers/src/main/java/ai/docling/testcontainers/serve/DoclingServeContainer.java @@ -1,4 +1,4 @@ -package ai.docling.testcontainers; +package ai.docling.testcontainers.serve; import java.time.Duration; import java.util.Optional; @@ -8,7 +8,7 @@ import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.utility.DockerImageName; -import ai.docling.testcontainers.config.DoclingContainerConfig; +import ai.docling.testcontainers.serve.config.DoclingServeContainerConfig; /** * A container wrapper class designed to work with the Docling service. @@ -17,28 +17,28 @@ * users to set environment variables, handle port mappings, and configure * health checks for the container. */ -public class DoclingContainer extends GenericContainer { - private static final Logger LOG = Logger.getLogger(DoclingContainer.class.getName()); +public class DoclingServeContainer extends GenericContainer { + private static final Logger LOG = Logger.getLogger(DoclingServeContainer.class.getName()); /** * The dynamic host name determined from TestContainers */ - private DoclingContainerConfig config; + private DoclingServeContainerConfig config; /** - * Constructs a new DoclingContainer instance with the specified configuration and optional timeout. + * Constructs a new DoclingServeContainer instance with the specified configuration and optional timeout. * The container is set up with the necessary environment variables, exposed ports, * health check configuration, and an optional timeout for container startup. * * @param config the configuration details for the Docling container, including image name, environment variables, and other settings * @param timeout an optional duration specifying the maximum timeout for the container startup; a default timeout of 1 minute is used if not specified */ - public DoclingContainer(DoclingContainerConfig config, Optional timeout) { - super(DockerImageName.parse(config.imageName()).asCompatibleSubstituteFor(DoclingContainerConfig.DOCLING_IMAGE)); + public DoclingServeContainer(DoclingServeContainerConfig config, Optional timeout) { + super(DockerImageName.parse(config.imageName()).asCompatibleSubstituteFor(DoclingServeContainerConfig.DOCLING_SERVE_IMAGE)); this.config = config; // Configure the container - withExposedPorts(DoclingContainerConfig.DEFAULT_DOCLING_PORT); + withExposedPorts(DoclingServeContainerConfig.DEFAULT_DOCLING_SERVE_PORT); withEnv(config.containerEnv()); waitingFor(Wait.forHttp("/health")); @@ -55,6 +55,6 @@ public DoclingContainer(DoclingContainerConfig config, Optional timeou * @return the dynamically mapped port on the host machine for the Docling container */ public int getPort() { - return getMappedPort(DoclingContainerConfig.DEFAULT_DOCLING_PORT); + return getMappedPort(DoclingServeContainerConfig.DEFAULT_DOCLING_SERVE_PORT); } } diff --git a/testcontainers/src/main/java/ai/docling/testcontainers/config/DefaultDoclingContainerConfig.java b/docling-testcontainers/src/main/java/ai/docling/testcontainers/serve/config/DefaultDoclingContainerConfig.java similarity index 86% rename from testcontainers/src/main/java/ai/docling/testcontainers/config/DefaultDoclingContainerConfig.java rename to docling-testcontainers/src/main/java/ai/docling/testcontainers/serve/config/DefaultDoclingContainerConfig.java index 404fc40..52b5561 100644 --- a/testcontainers/src/main/java/ai/docling/testcontainers/config/DefaultDoclingContainerConfig.java +++ b/docling-testcontainers/src/main/java/ai/docling/testcontainers/serve/config/DefaultDoclingContainerConfig.java @@ -1,9 +1,9 @@ -package ai.docling.testcontainers.config; +package ai.docling.testcontainers.serve.config; import java.util.Map; import java.util.Optional; -final class DefaultDoclingContainerConfig implements DoclingContainerConfig { +final class DefaultDoclingContainerConfig implements DoclingServeContainerConfig { private final String imageName; private final boolean enableUi; private final Map containerEnv; diff --git a/testcontainers/src/main/java/ai/docling/testcontainers/config/DoclingContainerConfig.java b/docling-testcontainers/src/main/java/ai/docling/testcontainers/serve/config/DoclingServeContainerConfig.java similarity index 85% rename from testcontainers/src/main/java/ai/docling/testcontainers/config/DoclingContainerConfig.java rename to docling-testcontainers/src/main/java/ai/docling/testcontainers/serve/config/DoclingServeContainerConfig.java index 911190b..c2c43dc 100644 --- a/testcontainers/src/main/java/ai/docling/testcontainers/config/DoclingContainerConfig.java +++ b/docling-testcontainers/src/main/java/ai/docling/testcontainers/serve/config/DoclingServeContainerConfig.java @@ -1,4 +1,4 @@ -package ai.docling.testcontainers.config; +package ai.docling.testcontainers.serve.config; import java.util.Map; @@ -9,16 +9,16 @@ * the container image name, toggling the UI, and setting environment variables * for the container. */ -public interface DoclingContainerConfig { +public interface DoclingServeContainerConfig { /** * Default image name */ - String DOCLING_IMAGE = "ghcr.io/docling-project/docling-serve:v1.6.0"; + String DOCLING_SERVE_IMAGE = "ghcr.io/docling-project/docling-serve:v1.6.0"; /** * The default container port that docling runs on */ - int DEFAULT_DOCLING_PORT = 5001; + int DEFAULT_DOCLING_SERVE_PORT = 5001; /** * The container image name to use. @@ -50,16 +50,16 @@ default Builder toBuilder() { /** * Creates and returns a new instance of the {@link Builder} class. * - * @return a new {@link Builder} instance for configuring and building a {@link DoclingContainerConfig}. + * @return a new {@link Builder} instance for configuring and building a {@link DoclingServeContainerConfig}. */ static Builder builder() { return new Builder(); } /** - * A builder class for constructing instances of {@link DoclingContainerConfig}. + * A builder class for constructing instances of {@link DoclingServeContainerConfig}. * This class provides methods for setting various configurations and properties - * needed to create a {@link DoclingContainerConfig} instance. + * needed to create a {@link DoclingServeContainerConfig} instance. */ class Builder { /** @@ -94,18 +94,18 @@ class Builder { /** * Initializes a new instance of the {@link Builder} class. * This constructor is protected to restrict direct instantiation outside the package - * and is primarily used for creating builder instances within the {@link DoclingContainerConfig} implementation. + * and is primarily used for creating builder instances within the {@link DoclingServeContainerConfig} implementation. */ protected Builder() { } /** - * Constructs a {@link Builder} instance initialized with the settings from the given {@link DoclingContainerConfig}. + * Constructs a {@link Builder} instance initialized with the settings from the given {@link DoclingServeContainerConfig}. * This constructor populates the builder fields using the corresponding values from the provided configuration. * - * @param config the {@link DoclingContainerConfig} instance whose properties will be used to initialize the builder + * @param config the {@link DoclingServeContainerConfig} instance whose properties will be used to initialize the builder */ - protected Builder(DoclingContainerConfig config) { + protected Builder(DoclingServeContainerConfig config) { this.imageName = config.imageName(); this.enableUi = config.enableUi(); this.containerEnv = config.containerEnv(); @@ -162,12 +162,12 @@ public Builder containerEnv(String key, String value) { } /** - * Builds and returns a new instance of {@link DoclingContainerConfig} based on the current builder state. + * Builds and returns a new instance of {@link DoclingServeContainerConfig} based on the current builder state. * - * @return a newly constructed {@link DoclingContainerConfig} instance containing the configuration + * @return a newly constructed {@link DoclingServeContainerConfig} instance containing the configuration * properties and values set within this builder. */ - public DoclingContainerConfig build() { + public DoclingServeContainerConfig build() { return new DefaultDoclingContainerConfig(this); } } diff --git a/docling-testcontainers/src/main/java/ai/docling/testcontainers/serve/config/package-info.java b/docling-testcontainers/src/main/java/ai/docling/testcontainers/serve/config/package-info.java new file mode 100644 index 0000000..358373e --- /dev/null +++ b/docling-testcontainers/src/main/java/ai/docling/testcontainers/serve/config/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package ai.docling.testcontainers.serve.config; + +import org.jspecify.annotations.NullMarked; diff --git a/docling-testcontainers/src/main/java/ai/docling/testcontainers/serve/package-info.java b/docling-testcontainers/src/main/java/ai/docling/testcontainers/serve/package-info.java new file mode 100644 index 0000000..3bff896 --- /dev/null +++ b/docling-testcontainers/src/main/java/ai/docling/testcontainers/serve/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package ai.docling.testcontainers.serve; + +import org.jspecify.annotations.NullMarked; diff --git a/testcontainers/src/test/java/ai/docling/testcontainers/DoclingContainerAvailableTests.java b/docling-testcontainers/src/test/java/ai/docling/testcontainers/serve/DoclingServeContainerAvailableTests.java similarity index 82% rename from testcontainers/src/test/java/ai/docling/testcontainers/DoclingContainerAvailableTests.java rename to docling-testcontainers/src/test/java/ai/docling/testcontainers/serve/DoclingServeContainerAvailableTests.java index e177e76..756f4e0 100644 --- a/testcontainers/src/test/java/ai/docling/testcontainers/DoclingContainerAvailableTests.java +++ b/docling-testcontainers/src/test/java/ai/docling/testcontainers/serve/DoclingServeContainerAvailableTests.java @@ -1,4 +1,4 @@ -package ai.docling.testcontainers; +package ai.docling.testcontainers.serve; import static org.assertj.core.api.Assertions.assertThat; @@ -14,19 +14,19 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; -import ai.docling.testcontainers.config.DoclingContainerConfig; +import ai.docling.testcontainers.serve.config.DoclingServeContainerConfig; import tools.jackson.databind.ObjectMapper; @Testcontainers -class DoclingContainerAvailableTests { +class DoclingServeContainerAvailableTests { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private record HealthResponse(String status) {} @Container - private final DoclingContainer doclingContainer = new DoclingContainer( - DoclingContainerConfig.builder() - .imageName(DoclingContainerConfig.DOCLING_IMAGE) + private final DoclingServeContainer doclingContainer = new DoclingServeContainer( + DoclingServeContainerConfig.builder() + .imageName(DoclingServeContainerConfig.DOCLING_SERVE_IMAGE) .enableUi(true) .build(), Optional.of(Duration.ofMinutes(2)) diff --git a/testing/build.gradle.kts b/docling-testing/build.gradle.kts similarity index 100% rename from testing/build.gradle.kts rename to docling-testing/build.gradle.kts diff --git a/docs/build.gradle.kts b/docs/build.gradle.kts index 4ff9144..5765e40 100644 --- a/docs/build.gradle.kts +++ b/docs/build.gradle.kts @@ -23,7 +23,7 @@ mkdocs { "project-groupId" to "${project.group}", "project-artifactId" to "${rootProject.name}", "api-artifactId" to project(":docling-api").name, - "client-artifactId" to project(":docling-client").name, + "serve-client-artifactId" to project(":docling-serve-client").name, "testing-artifactId" to project(":docling-testing").name, "testcontainers-artifactId" to project(":docling-testcontainers").name ) diff --git a/docs/src/doc/docs/client.md b/docs/src/doc/docs/client.md deleted file mode 100644 index e03a47e..0000000 --- a/docs/src/doc/docs/client.md +++ /dev/null @@ -1 +0,0 @@ -# Client diff --git a/docs/src/doc/docs/serve-client.md b/docs/src/doc/docs/serve-client.md new file mode 100644 index 0000000..48664b7 --- /dev/null +++ b/docs/src/doc/docs/serve-client.md @@ -0,0 +1 @@ +# Docling Serve Client diff --git a/docs/src/doc/mkdocs.yml b/docs/src/doc/mkdocs.yml index 8a12597..1654a90 100644 --- a/docs/src/doc/mkdocs.yml +++ b/docs/src/doc/mkdocs.yml @@ -58,7 +58,7 @@ nav: - Getting started: getting-started.md - Artifacts: - API: api.md - - Client: client.md + - Serve Client: serve-client.md - Testing: testing.md - Testcontainers: testcontainers.md diff --git a/settings.gradle.kts b/settings.gradle.kts index 1f3c8b8..8c306e3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -4,10 +4,4 @@ plugins { } rootProject.name = "docling-java" -include("api", "client", "docs", "testing", "testcontainers") - -project(":api").name = "docling-api" -project(":client").name = "docling-client" -project(":docs").name = "docling-docs" -project(":testing").name = "docling-testing" -project(":testcontainers").name = "docling-testcontainers" +include("docling-api", "docling-serve-client", "docs", "docling-testing", "docling-testcontainers") From e2dbccffb1b475f619f1c56fdc75e51f77069039 Mon Sep 17 00:00:00 2001 From: Eric Deandrea Date: Wed, 29 Oct 2025 14:26:11 -0400 Subject: [PATCH 10/13] feat: idea for api design Fix build Signed-off-by: Eric Deandrea --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 774a2a4..118d389 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,7 +46,7 @@ jobs: - name: build-test-${{ matrix.module }}-java${{ matrix.java }} run: | ./gradlew --no-daemon -Pjava.version=${{ matrix.java }} \ - :docling-${{ matrix.module }}:clean :${{ matrix.module }}:build + :${{ matrix.module }}:clean :${{ matrix.module }}:build docs: runs-on: ubuntu-latest From 962906865264ef2b4019061de1ac725ad1257878 Mon Sep 17 00:00:00 2001 From: Eric Deandrea Date: Thu, 30 Oct 2025 14:04:45 -0400 Subject: [PATCH 11/13] feat: idea for api design Use lombok Signed-off-by: Eric Deandrea --- docling-api/build.gradle.kts | 15 +- docling-api/src/lombok.config | 5 + .../main/java/ai/docling/api/DoclingApi.java | 41 + .../request/ConvertDocumentRequest.java | 172 +--- .../options/ConvertDocumentOptions.java | 972 +----------------- .../options/PictureDescriptionApi.java | 224 +--- .../options/PictureDescriptionLocal.java | 123 +-- .../convert/request/options/VlmModelApi.java | 289 +----- .../request/options/VlmModelLocal.java | 252 +---- .../convert/request/source/FileSource.java | 77 +- .../convert/request/source/HttpSource.java | 117 +-- .../api/convert/request/source/Source.java | 53 +- .../convert/request/target/InBodyTarget.java | 26 +- .../api/convert/request/target/PutTarget.java | 72 +- .../api/convert/request/target/Target.java | 59 +- .../api/convert/request/target/ZipTarget.java | 27 +- .../response/ConvertDocumentResponse.java | 181 +--- .../convert/response/DocumentResponse.java | 228 +--- .../api/convert/response/ErrorItem.java | 112 +- .../api/health/HealthCheckResponse.java | 42 +- .../request/ConvertDocumentRequestTests.java | 58 +- .../options/ConvertDocumentOptionsTests.java | 9 +- .../options/PictureDescriptionApiTests.java | 39 +- .../options/PictureDescriptionLocalTests.java | 27 +- .../request/source/FileSourceTests.java | 67 +- .../request/source/HttpSourceTests.java | 47 +- .../request/target/InBodyTargetTests.java | 28 - .../request/target/PutTargetTests.java | 19 +- .../request/target/ZipTargetTests.java | 26 - .../ConvertDocumentResponseTests.java | 61 +- .../response/DocumentResponseTests.java | 34 +- .../api/convert/response/ErrorItemTests.java | 23 +- .../api/health/HealthCheckResponseTests.java | 10 +- .../client/serve/DoclingServeClient.java | 119 ++- .../serve/DoclingServeJackson2Client.java | 1 - .../AbstractDoclingServeClientTests.java | 33 +- .../DoclingServeJackson2ClientTests.java | 2 + .../DoclingServeJackson3ClientTests.java | 2 + gradle.properties | 1 - gradle/libs.versions.toml | 5 + 40 files changed, 630 insertions(+), 3068 deletions(-) create mode 100644 docling-api/src/lombok.config delete mode 100644 docling-api/src/test/java/ai/docling/api/convert/request/target/InBodyTargetTests.java delete mode 100644 docling-api/src/test/java/ai/docling/api/convert/request/target/ZipTargetTests.java diff --git a/docling-api/build.gradle.kts b/docling-api/build.gradle.kts index 7bd6bd8..ef464fb 100644 --- a/docling-api/build.gradle.kts +++ b/docling-api/build.gradle.kts @@ -1,9 +1,20 @@ plugins { id("docling-java-shared") + alias(libs.plugins.lombok) +} + +lombok { + version = libs.versions.lombok.asProvider().get() } dependencies { - implementation(platform(libs.jackson.bom)) - implementation(libs.jackson.annotations) + compileOnly(platform(libs.jackson.bom)) + compileOnly(libs.jackson.annotations) + compileOnly(libs.jackson.databind) + compileOnly(libs.jackson2.databind) testImplementation(project(":docling-testcontainers")) } + +tasks.withType { + exclude("**/lombok.config") +} diff --git a/docling-api/src/lombok.config b/docling-api/src/lombok.config new file mode 100644 index 0000000..8677580 --- /dev/null +++ b/docling-api/src/lombok.config @@ -0,0 +1,5 @@ +lombok.copyableAnnotations+=org.jspecify.annotations.Nullable +lombok.addGeneratedAnnotation=false +lombok.addLombokGeneratedAnnotation=false +lombok.builder.className=Builder +lombok.nonNull.exceptionType=IllegalArgumentException diff --git a/docling-api/src/main/java/ai/docling/api/DoclingApi.java b/docling-api/src/main/java/ai/docling/api/DoclingApi.java index 55be038..1f574e8 100644 --- a/docling-api/src/main/java/ai/docling/api/DoclingApi.java +++ b/docling-api/src/main/java/ai/docling/api/DoclingApi.java @@ -41,6 +41,47 @@ public interface DoclingApi { * @param the type of the concrete builder implementation. */ interface DoclingApiBuilder> { + /** + * Enables logging of requests for the API client being built. This can be useful for + * debugging or monitoring the behavior of requests made to the API. + * + *

This method influences the logging behavior of requests initiated by the client + * without specifying further configuration details. + * + * @return {@code this} builder instance for fluent API usage. + */ + default B logRequests() { + return logRequests(true); + } + + /** + * Configures whether logging of API requests is enabled or not. Logging can help monitor or debug + * request communications with the API. + * + * @param logRequests {@code true} to enable request logging; {@code false} to disable it. + * @return {@code this} builder instance for fluent API usage. + */ + B logRequests(boolean logRequests); + + /** + * Enables logging of responses for the API client being built. This can assist in debugging or + * monitoring the behavior of responses received from the API. + * + * @return {@code this} builder instance for fluent API usage. + */ + default B logResponses() { + return logResponses(true); + } + + /** + * Configures whether logging of API responses is enabled or not. Logging can help monitor or debug + * the responses received from the API. + * + * @param logResponses {@code true} to enable response logging; {@code false} to disable it. + * @return {@code this} builder instance for fluent API usage. + */ + B logResponses(boolean logResponses); + /** * Builds and returns an instance of the specified type, representing the completed configuration * of the builder. The returned instance is typically an implementation of the Docling API. diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/ConvertDocumentRequest.java b/docling-api/src/main/java/ai/docling/api/convert/request/ConvertDocumentRequest.java index 1754a97..caa0baa 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/ConvertDocumentRequest.java +++ b/docling-api/src/main/java/ai/docling/api/convert/request/ConvertDocumentRequest.java @@ -1,19 +1,15 @@ package ai.docling.api.convert.request; -import static ai.docling.api.util.ValidationUtils.ensureNotNull; - -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import org.jspecify.annotations.Nullable; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; import ai.docling.api.convert.request.options.ConvertDocumentOptions; -import ai.docling.api.convert.request.source.FileSource; -import ai.docling.api.convert.request.source.HttpSource; import ai.docling.api.convert.request.source.Source; import ai.docling.api.convert.request.target.Target; @@ -26,162 +22,26 @@ * are omitted from the serialized JSON using {@link JsonInclude}. */ @JsonInclude(JsonInclude.Include.NON_EMPTY) +@tools.jackson.databind.annotation.JsonDeserialize(builder = ConvertDocumentRequest.Builder.class) +@lombok.extern.jackson.Jacksonized +@lombok.Builder(toBuilder = true) +@lombok.Getter +@lombok.ToString public class ConvertDocumentRequest { @JsonProperty("sources") - private List sources = new ArrayList<>(); + @JsonSetter(nulls = Nulls.AS_EMPTY) + @lombok.Singular + private List sources; @JsonProperty("options") - private ConvertDocumentOptions options; + @lombok.NonNull + @lombok.Builder.Default + private ConvertDocumentOptions options = ConvertDocumentOptions.builder().build(); @JsonProperty("target") - private Target target; - - private static void checkSources(List sources) { - if ((sources == null) || sources.isEmpty()) { - throw new IllegalArgumentException("sources must not be null or empty"); - } - - if (!(sources.stream().allMatch(HttpSource.class::isInstance) || sources.stream().allMatch(FileSource.class::isInstance))) { - throw new IllegalArgumentException("All sources must be of the same type (HttpSource or FileSource)"); - } - } - - /** - * Retrieves an unmodifiable view of the list of document sources associated with this request. - * Modifications to the returned list will result in an {@link UnsupportedOperationException}. - * - * @return an unmodifiable {@link List} of {@link Source} objects representing the document sources. - */ - public List getSources() { - return Collections.unmodifiableList(sources); - } - - /** - * Sets the list of sources for the document conversion request. The existing list of sources - * will be cleared before adding the provided sources. If the provided list is {@code null}, - * the sources list will remain cleared. - * - * @param sources a {@link List} of {@link Source} objects representing the document sources to set. - * Elements in the list must not be {@code null}, but the list itself may be - * {@code null}. - * @throws IllegalArgumentException if the resulting sources list is empty after processing. - */ - public void setSources(@Nullable List sources) { - checkSources(sources); - this.sources.clear(); - - if (sources != null) { - this.sources.addAll(sources); - } - } - - /** - * Adds a list of document sources to this {@link ConvertDocumentRequest} and returns the updated instance. - * - *

This method modifies the list of document sources for the current request using the provided - * {@code sources} list. The list replaces any existing sources. If the provided list is {@code null}, - * the sources are cleared. - * - * @param sources a {@link List} of {@link Source} objects to set as the new document sources for the request. - * The list itself may be {@code null}, but individual elements within the list must not be {@code null}. - * @return the updated {@link ConvertDocumentRequest} instance. - */ - public ConvertDocumentRequest withSources(@Nullable List sources) { - setSources(sources); - return this; - } - - /** - * Retrieves the document conversion options associated with this request. - * - * @return the {@link ConvertDocumentOptions} object representing the conversion options, - * or {@code null} if no options have been set. - */ - @Nullable - public ConvertDocumentOptions getOptions() { - return options; - } - - /** - * Sets the document conversion options for this request. - * - *

The provided {@link ConvertDocumentOptions} object defines the specific - * settings to be applied during the document conversion process. If {@code options} - * is {@code null}, the current options will be cleared. - * - * @param options a {@link ConvertDocumentOptions} object representing the conversion - * settings to apply, or {@code null} to clear the options. - */ - public void setOptions(ConvertDocumentOptions options) { - this.options = ensureNotNull(options, "options"); - } - - /** - * Updates the document conversion options for this request and returns the updated instance. - * - *

This method modifies the {@link ConvertDocumentOptions} associated with the current - * {@link ConvertDocumentRequest}. If the provided {@code options} is {@code null}, the options - * will be cleared. - * - * @param options a {@link ConvertDocumentOptions} object specifying the settings to apply - * for the document conversion, or {@code null} to clear the options. - * @return the updated {@link ConvertDocumentRequest} instance. - */ - public ConvertDocumentRequest withOptions(ConvertDocumentOptions options) { - setOptions(options); - return this; - } - - /** - * Retrieves the {@link Target} associated with this {@link ConvertDocumentRequest}. - * - *

The {@link Target} defines where and how the converted document should be delivered. - * For example, it may specify delivery in the response body, uploading to a URI, or - * zipping the document for inclusion in the response. - * - * @return the {@link Target} instance representing the delivery method for the converted - * document, or {@code null} if no target has been set. - */ @Nullable - public Target getTarget() { - return target; - } - - /** - * Sets the {@link Target} for this {@link ConvertDocumentRequest}. - * - *

The {@link Target} defines how and where the converted document should be delivered. It may - * represent delivery in the response body, uploading to a specified URI, or zipping for inclusion - * in the response. - * - * @param target the {@link Target} instance representing the delivery method and destination - * for the converted document, or {@code null} to unset the existing target. - */ - public void setTarget(@Nullable Target target) { - this.target = target; - } - - /** - * Updates the {@link Target} for this {@link ConvertDocumentRequest} and returns the updated instance. - * - *

The {@link Target} specifies how and where the converted document will be delivered, such as - * including it in the response body, uploading it to a specific URI, or zipping it. - * - * @param target the {@link Target} instance indicating the delivery method and destination for the - * converted document, or {@code null} to clear the existing target. - * @return the updated {@link ConvertDocumentRequest} instance for method chaining. - */ - public ConvertDocumentRequest withTarget(@Nullable Target target) { - setTarget(target); - return this; - } + private Target target; - @Override - public String toString() { - return "ConvertDocumentRequest{" + - "sources=" + (sources == null ? "null" : sources.toString()) + - ", options=" + (options == null ? "null" : options.toString()) + - ", target=" + (target == null ? "null" : target.toString()) + - '}'; - } + @tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "") + public static class Builder { } } diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/options/ConvertDocumentOptions.java b/docling-api/src/main/java/ai/docling/api/convert/request/options/ConvertDocumentOptions.java index 88e883c..ff64a19 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/options/ConvertDocumentOptions.java +++ b/docling-api/src/main/java/ai/docling/api/convert/request/options/ConvertDocumentOptions.java @@ -1,1023 +1,141 @@ package ai.docling.api.convert.request.options; - import java.time.Duration; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import org.jspecify.annotations.Nullable; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; /** * Options for configuring the document conversion process with Docling. */ @JsonInclude(JsonInclude.Include.NON_EMPTY) +@tools.jackson.databind.annotation.JsonDeserialize(builder = ConvertDocumentOptions.Builder.class) +@lombok.extern.jackson.Jacksonized +@lombok.Builder(toBuilder = true) +@lombok.Getter +@lombok.ToString public class ConvertDocumentOptions { @JsonProperty("from_formats") - private List fromFormats = new ArrayList<>(); + @JsonSetter(nulls = Nulls.AS_EMPTY) + @lombok.Singular + private List fromFormats; @JsonProperty("to_formats") - private List toFormats = new ArrayList<>(); + @JsonSetter(nulls = Nulls.AS_EMPTY) + @lombok.Singular + private List toFormats; @JsonProperty("image_export_mode") + @Nullable private ImageRefMode imageExportMode; @JsonProperty("do_ocr") + @Nullable private Boolean doOcr; @JsonProperty("force_ocr") + @Nullable private Boolean forceOcr; @JsonProperty("ocr_engine") + @Nullable private OcrEngine ocrEngine; @JsonProperty("ocr_lang") - private List ocrLang = new ArrayList<>(); + @JsonSetter(nulls = Nulls.AS_EMPTY) + @lombok.Singular("ocrLang") + private List ocrLang; @JsonProperty("pdf_backend") + @Nullable private PdfBackend pdfBackend; @JsonProperty("table_mode") + @Nullable private TableFormerMode tableMode; @JsonProperty("table_cell_matching") + @Nullable private Boolean tableCellMatching; @JsonProperty("pipeline") + @Nullable private ProcessingPipeline pipeline; @JsonProperty("page_range") - private List pageRange = new ArrayList<>(); + @JsonSetter(nulls = Nulls.AS_EMPTY) + @lombok.Singular("pageRange") + private List pageRange; @JsonProperty("document_timeout") + @Nullable private Duration documentTimeout; @JsonProperty("abort_on_error") + @Nullable private Boolean abortOnError; @JsonProperty("do_table_structure") + @Nullable private Boolean doTableStructure; @JsonProperty("include_images") + @Nullable private Boolean includeImages; @JsonProperty("images_scale") + @Nullable private Double imagesScale; @JsonProperty("md_page_break_placeholder") + @Nullable private String mdPageBreakPlaceholder; @JsonProperty("do_code_enrichment") + @Nullable private Boolean doCodeEnrichment; @JsonProperty("do_formula_enrichment") + @Nullable private Boolean doFormulaEnrichment; @JsonProperty("do_picture_classification") + @Nullable private Boolean doPictureClassification; @JsonProperty("do_picture_description") + @Nullable private Boolean doPictureDescription; @JsonProperty("picture_description_area_threshold") + @Nullable private Double pictureDescriptionAreaThreshold; @JsonProperty("picture_description_local") + @Nullable private PictureDescriptionLocal pictureDescriptionLocal; @JsonProperty("picture_description_api") + @Nullable private PictureDescriptionApi pictureDescriptionApi; @JsonProperty("vlm_pipeline_model") + @Nullable private VlmModelType vlmPipelineModel; @JsonProperty("vlm_pipeline_model_local") + @Nullable private String vlmPipelineModelLocal; @JsonProperty("vlm_pipeline_model_api") + @Nullable private String vlmPipelineModelApi; - /** - * Gets whether to abort document conversion on error. - * - * @return true if conversion should abort on error, false otherwise, or null if not set - */ - @Nullable - public Boolean getAbortOnError() { - return abortOnError; - } - - /** - * Sets whether to abort document conversion on error. - * - * @param abortOnError true to abort on error, false to continue, or null to use default - */ - public void setAbortOnError(@Nullable Boolean abortOnError) { - this.abortOnError = abortOnError; - } - - /** - * Gets whether code enrichment is enabled during conversion. - * - * @return true if code enrichment is enabled, false otherwise, or null if not set - */ - @Nullable - public Boolean getDoCodeEnrichment() { - return doCodeEnrichment; - } - - /** - * Sets whether to enable code enrichment during conversion. - * - * @param doCodeEnrichment true to enable code enrichment, false to disable, or null to use default - */ - public void setDoCodeEnrichment(@Nullable Boolean doCodeEnrichment) { - this.doCodeEnrichment = doCodeEnrichment; - } - - /** - * Gets the timeout duration for document conversion. - * - * @return the timeout duration, or null if not set - */ - @Nullable - public Duration getDocumentTimeout() { - return documentTimeout; - } - - /** - * Sets the timeout duration for document conversion. - * - * @param documentTimeout the timeout duration, or null to use default - */ - public void setDocumentTimeout(@Nullable Duration documentTimeout) { - this.documentTimeout = documentTimeout; - } - - /** - * Gets whether formula enrichment is enabled during conversion. - * - * @return true if formula enrichment is enabled, false otherwise, or null if not set - */ - @Nullable - public Boolean getDoFormulaEnrichment() { - return doFormulaEnrichment; - } - - /** - * Sets whether to enable formula enrichment during conversion. - * - * @param doFormulaEnrichment true to enable formula enrichment, false to disable, or null to use default - */ - public void setDoFormulaEnrichment(@Nullable Boolean doFormulaEnrichment) { - this.doFormulaEnrichment = doFormulaEnrichment; - } - - /** - * Gets whether OCR (Optical Character Recognition) is enabled. - * - * @return true if OCR is enabled, false otherwise, or null if not set - */ - @Nullable - public Boolean getDoOcr() { - return doOcr; - } - - /** - * Sets whether to enable OCR (Optical Character Recognition). - * - * @param doOcr true to enable OCR, false to disable, or null to use default - */ - public void setDoOcr(@Nullable Boolean doOcr) { - this.doOcr = doOcr; - } - - /** - * Gets whether picture classification is enabled during conversion. - * - * @return true if picture classification is enabled, false otherwise, or null if not set - */ - @Nullable - public Boolean getDoPictureClassification() { - return doPictureClassification; - } - - /** - * Sets whether to enable picture classification during conversion. - * - * @param doPictureClassification true to enable picture classification, false to disable, or null to use default - */ - public void setDoPictureClassification(@Nullable Boolean doPictureClassification) { - this.doPictureClassification = doPictureClassification; - } - - /** - * Gets whether picture description is enabled during conversion. - * - * @return true if picture description is enabled, false otherwise, or null if not set - */ - @Nullable - public Boolean getDoPictureDescription() { - return doPictureDescription; - } - - /** - * Sets whether to enable picture description during conversion. - * - * @param doPictureDescription true to enable picture description, false to disable, or null to use default - */ - public void setDoPictureDescription(@Nullable Boolean doPictureDescription) { - this.doPictureDescription = doPictureDescription; - } - - /** - * Gets whether table structure analysis is enabled during conversion. - * - * @return true if table structure analysis is enabled, false otherwise, or null if not set - */ - @Nullable - public Boolean getDoTableStructure() { - return doTableStructure; - } - - /** - * Sets whether to enable table structure analysis during conversion. - * - * @param doTableStructure true to enable table structure analysis, false to disable, or null to use default - */ - public void setDoTableStructure(@Nullable Boolean doTableStructure) { - this.doTableStructure = doTableStructure; - } - - /** - * Gets whether OCR should be forced even if text is already available. - * - * @return true if OCR is forced, false otherwise, or null if not set - */ - @Nullable - public Boolean getForceOcr() { - return forceOcr; - } - - /** - * Sets whether to force OCR even if text is already available. - * - * @param forceOcr true to force OCR, false otherwise, or null to use default - */ - public void setForceOcr(@Nullable Boolean forceOcr) { - this.forceOcr = forceOcr; - } - - /** - * Gets the list of input document formats to process. - * - * @return an unmodifiable list of input formats - */ - public List getFromFormats() { - return Collections.unmodifiableList(fromFormats); - } - - /** - * Sets the list of input document formats to process. - * - * @param fromFormats the list of input formats, or null to clear - */ - public void setFromFormats(@Nullable List fromFormats) { - this.fromFormats.clear(); - - if (fromFormats != null) { - this.fromFormats.addAll(fromFormats); - } - } - - /** - * Gets the image export mode for handling images during conversion. - * - * @return the image export mode, or null if not set - */ - @Nullable - public ImageRefMode getImageExportMode() { - return imageExportMode; - } - - /** - * Sets the image export mode for handling images during conversion. - * - * @param imageExportMode the image export mode, or null to use default - */ - public void setImageExportMode(@Nullable ImageRefMode imageExportMode) { - this.imageExportMode = imageExportMode; - } - - /** - * Gets the scale factor for exported images. - * - * @return the image scale factor, or null if not set - */ - @Nullable - public Double getImagesScale() { - return imagesScale; - } - - /** - * Sets the scale factor for exported images. - * - * @param imagesScale the image scale factor, or null to use default - */ - public void setImagesScale(@Nullable Double imagesScale) { - this.imagesScale = imagesScale; - } - - /** - * Gets whether images should be included in the converted output. - * - * @return true if images should be included, false otherwise, or null if not set - */ - @Nullable - public Boolean getIncludeImages() { - return includeImages; - } - - /** - * Sets whether images should be included in the converted output. - * - * @param includeImages true to include images, false to exclude, or null to use default - */ - public void setIncludeImages(@Nullable Boolean includeImages) { - this.includeImages = includeImages; - } - - /** - * Gets the placeholder string for page breaks in Markdown output. - * - * @return the page break placeholder string, or null if not set - */ - @Nullable - public String getMdPageBreakPlaceholder() { - return mdPageBreakPlaceholder; - } - - /** - * Sets the placeholder string for page breaks in Markdown output. - * - * @param mdPageBreakPlaceholder the page break placeholder string, or null to use default - */ - public void setMdPageBreakPlaceholder(@Nullable String mdPageBreakPlaceholder) { - this.mdPageBreakPlaceholder = mdPageBreakPlaceholder; - } - - /** - * Gets the OCR engine to use for text recognition. - * - * @return the OCR engine, or null if not set - */ - @Nullable - public OcrEngine getOcrEngine() { - return ocrEngine; - } - - /** - * Sets the OCR engine to use for text recognition. - * - * @param ocrEngine the OCR engine, or null to use default - */ - public void setOcrEngine(@Nullable OcrEngine ocrEngine) { - this.ocrEngine = ocrEngine; - } - - /** - * Gets the list of language codes for OCR processing. - * - * @return an unmodifiable list of language codes - */ - public List getOcrLang() { - return Collections.unmodifiableList(ocrLang); - } - - /** - * Sets the list of language codes for OCR processing. - * - * @param ocrLang the list of language codes, or null to clear - */ - public void setOcrLang(@Nullable List ocrLang) { - this.ocrLang.clear(); - - if (ocrLang != null) { - this.ocrLang.addAll(ocrLang); - } - } - - /** - * Gets the page range to process during conversion. - * - * @return an unmodifiable list containing the page range (from and to page numbers) - */ - public List getPageRange() { - return Collections.unmodifiableList(pageRange); - } - - /** - * Sets the page range to process during conversion. - * - * @param pageRange a list containing the page range (from and to page numbers), or null to clear - */ - public void setPageRange(@Nullable List pageRange) { - this.pageRange.clear(); - - if (pageRange != null) { - this.pageRange.addAll(pageRange); - } - } - - /** - * Gets the PDF backend to use for processing PDF documents. - * - * @return the PDF backend, or null if not set - */ - @Nullable - public PdfBackend getPdfBackend() { - return pdfBackend; - } - - /** - * Sets the PDF backend to use for processing PDF documents. - * - * @param pdfBackend the PDF backend, or null to use default - */ - public void setPdfBackend(@Nullable PdfBackend pdfBackend) { - this.pdfBackend = pdfBackend; - } - - /** - * Gets the API-based picture description configuration. - * - * @return the picture description API configuration, or null if not set - */ - @Nullable - public PictureDescriptionApi getPictureDescriptionApi() { - return pictureDescriptionApi; - } - - private static void checkPictureDescription(PictureDescriptionApi pictureDescriptionApi, PictureDescriptionLocal pictureDescriptionLocal) { - if ((pictureDescriptionLocal != null) && (pictureDescriptionApi != null)) { - throw new IllegalArgumentException("picture_description_local and picture_description_api cannot both be set"); - } - } - - /** - * Sets the API-based picture description configuration. - * - * @param pictureDescriptionApi the picture description API configuration, or null to disable - * @throws IllegalArgumentException if both API and local picture description are set - */ - public void setPictureDescriptionApi(@Nullable PictureDescriptionApi pictureDescriptionApi) { - checkPictureDescription(pictureDescriptionApi, pictureDescriptionLocal); - this.pictureDescriptionApi = pictureDescriptionApi; - } - - /** - * Gets the area threshold for picture description. - * - * @return the area threshold value, or null if not set - */ - @Nullable - public Double getPictureDescriptionAreaThreshold() { - return pictureDescriptionAreaThreshold; - } - - /** - * Sets the area threshold for picture description. - * - * @param pictureDescriptionAreaThreshold the area threshold value, or null to use default - */ - public void setPictureDescriptionAreaThreshold(@Nullable Double pictureDescriptionAreaThreshold) { - this.pictureDescriptionAreaThreshold = pictureDescriptionAreaThreshold; - } - - /** - * Gets the local picture description configuration. - * - * @return the picture description local configuration, or null if not set - */ - @Nullable - public PictureDescriptionLocal getPictureDescriptionLocal() { - return pictureDescriptionLocal; - } - - /** - * Sets the local picture description configuration. - * - * @param pictureDescriptionLocal the picture description local configuration, or null to disable - * @throws IllegalArgumentException if both API and local picture description are set - */ - public void setPictureDescriptionLocal(@Nullable PictureDescriptionLocal pictureDescriptionLocal) { - checkPictureDescription(pictureDescriptionApi, pictureDescriptionLocal); - this.pictureDescriptionLocal = pictureDescriptionLocal; - } - - /** - * Gets the processing pipeline to use for document conversion. - * - * @return the processing pipeline, or null if not set - */ - @Nullable - public ProcessingPipeline getPipeline() { - return pipeline; - } - - /** - * Sets the processing pipeline to use for document conversion. - * - * @param pipeline the processing pipeline, or null to use default - */ - public void setPipeline(@Nullable ProcessingPipeline pipeline) { - this.pipeline = pipeline; - } - - /** - * Gets whether table cell matching is enabled. - * - * @return true if table cell matching is enabled, false otherwise, or null if not set - */ - @Nullable - public Boolean getTableCellMatching() { - return tableCellMatching; - } - - /** - * Sets whether to enable table cell matching. - * - * @param tableCellMatching true to enable table cell matching, false to disable, or null to use default - */ - public void setTableCellMatching(@Nullable Boolean tableCellMatching) { - this.tableCellMatching = tableCellMatching; - } - - /** - * Gets the table processing mode. - * - * @return the table mode, or null if not set - */ - @Nullable - public TableFormerMode getTableMode() { - return tableMode; - } - - /** - * Sets the table processing mode. - * - * @param tableMode the table mode, or null to use default - */ - public void setTableMode(@Nullable TableFormerMode tableMode) { - this.tableMode = tableMode; - } - - /** - * Gets the list of output formats for the converted document. - * - * @return the list of output formats, or null if not set - */ - public List getToFormats() { - return Collections.unmodifiableList(toFormats); - } - - /** - * Sets the list of output formats for the converted document. - * - * @param toFormats the list of output formats, or null to use default - */ - public void setToFormats(@Nullable List toFormats) { - this.toFormats.clear(); - - if (toFormats != null) { - this.toFormats.addAll(toFormats); - } - } - - /** - * Gets the Vision-Language Model (VLM) pipeline model type. - * - * @return the VLM model type, or null if not set - */ - @Nullable - public VlmModelType getVlmPipelineModel() { - return vlmPipelineModel; - } - - /** - * Sets the Vision-Language Model (VLM) pipeline model type. - * - * @param vlmPipelineModel the VLM model type, or null to use default - */ - public void setVlmPipelineModel(@Nullable VlmModelType vlmPipelineModel) { - this.vlmPipelineModel = vlmPipelineModel; - } - - /** - * Gets the API-based VLM pipeline model identifier. - * - * @return the API model identifier, or null if not set - */ - @Nullable - public String getVlmPipelineModelApi() { - return vlmPipelineModelApi; - } - - /** - * Sets the API-based VLM pipeline model identifier. - * - * @param vlmPipelineModelApi the API model identifier, or null to use default - */ - public void setVlmPipelineModelApi(@Nullable String vlmPipelineModelApi) { - this.vlmPipelineModelApi = vlmPipelineModelApi; - } - - /** - * Gets the local VLM pipeline model identifier. - * - * @return the local model identifier, or null if not set - */ - @Nullable - public String getVlmPipelineModelLocal() { - return vlmPipelineModelLocal; - } - - /** - * Sets the local VLM pipeline model identifier. - * - * @param vlmPipelineModelLocal the local model identifier, or null to use default - */ - public void setVlmPipelineModelLocal(@Nullable String vlmPipelineModelLocal) { - this.vlmPipelineModelLocal = vlmPipelineModelLocal; - } - - /** - * Sets whether to abort document conversion on error and returns this instance for method chaining. - * - * @param abortOnError true to abort on error, false to continue, or null to use default - * @return this options instance - */ - public ConvertDocumentOptions withAbortOnError(@Nullable Boolean abortOnError) { - setAbortOnError(abortOnError); - return this; - } - - /** - * Sets whether to enable code enrichment and returns this instance for method chaining. - * - * @param doCodeEnrichment true to enable code enrichment, false to disable, or null to use default - * @return this options instance - */ - public ConvertDocumentOptions withDoCodeEnrichment(@Nullable Boolean doCodeEnrichment) { - setDoCodeEnrichment(doCodeEnrichment); - return this; - } - - /** - * Sets the timeout duration for document conversion and returns this instance for method chaining. - * - * @param documentTimeout the timeout duration, or null to use default - * @return this options instance - */ - public ConvertDocumentOptions withDocumentTimeout(@Nullable Duration documentTimeout) { - setDocumentTimeout(documentTimeout); - return this; - } - - /** - * Sets whether to enable formula enrichment and returns this instance for method chaining. - * - * @param doFormulaEnrichment true to enable formula enrichment, false to disable, or null to use default - * @return this options instance - */ - public ConvertDocumentOptions withDoFormulaEnrichment(@Nullable Boolean doFormulaEnrichment) { - setDoFormulaEnrichment(doFormulaEnrichment); - return this; - } - - /** - * Sets whether to enable OCR and returns this instance for method chaining. - * - * @param doOcr true to enable OCR, false to disable, or null to use default - * @return this options instance - */ - public ConvertDocumentOptions withDoOcr(@Nullable Boolean doOcr) { - setDoOcr(doOcr); - return this; - } - - /** - * Sets whether to enable picture classification and returns this instance for method chaining. - * - * @param doPictureClassification true to enable picture classification, false to disable, or null to use default - * @return this options instance - */ - public ConvertDocumentOptions withDoPictureClassification(@Nullable Boolean doPictureClassification) { - setDoPictureClassification(doPictureClassification); - return this; - } - - /** - * Sets whether to enable picture description and returns this instance for method chaining. - * - * @param doPictureDescription true to enable picture description, false to disable, or null to use default - * @return this options instance - */ - public ConvertDocumentOptions withDoPictureDescription(@Nullable Boolean doPictureDescription) { - setDoPictureDescription(doPictureDescription); - return this; - } - - /** - * Sets whether to enable table structure analysis and returns this instance for method chaining. - * - * @param doTableStructure true to enable table structure analysis, false to disable, or null to use default - * @return this options instance - */ - public ConvertDocumentOptions withDoTableStructure(@Nullable Boolean doTableStructure) { - setDoTableStructure(doTableStructure); - return this; - } - - /** - * Sets whether to force OCR and returns this instance for method chaining. - * - * @param forceOcr true to force OCR, false otherwise, or null to use default - * @return this options instance - */ - public ConvertDocumentOptions withForceOcr(@Nullable Boolean forceOcr) { - setForceOcr(forceOcr); - return this; - } - - /** - * Sets the list of input formats and returns this instance for method chaining. - * - * @param fromFormats the list of input formats, or null to clear - * @return this options instance - */ - public ConvertDocumentOptions withFromFormats(@Nullable List fromFormats) { - setFromFormats(fromFormats); - return this; - } - - /** - * Sets the image export mode and returns this instance for method chaining. - * - * @param imageExportMode the image export mode, or null to use default - * @return this options instance - */ - public ConvertDocumentOptions withImageExportMode(@Nullable ImageRefMode imageExportMode) { - setImageExportMode(imageExportMode); - return this; - } - - /** - * Sets the image scale factor and returns this instance for method chaining. - * - * @param imagesScale the image scale factor, or null to use default - * @return this options instance - */ - public ConvertDocumentOptions withImagesScale(@Nullable Double imagesScale) { - setImagesScale(imagesScale); - return this; - } - - /** - * Sets whether to include images and returns this instance for method chaining. - * - * @param includeImages true to include images, false to exclude, or null to use default - * @return this options instance - */ - public ConvertDocumentOptions withIncludeImages(@Nullable Boolean includeImages) { - setIncludeImages(includeImages); - return this; - } - - /** - * Sets the Markdown page break placeholder and returns this instance for method chaining. - * - * @param mdPageBreakPlaceholder the page break placeholder string, or null to use default - * @return this options instance - */ - public ConvertDocumentOptions withMdPageBreakPlaceholder(@Nullable String mdPageBreakPlaceholder) { - setMdPageBreakPlaceholder(mdPageBreakPlaceholder); - return this; - } - - /** - * Sets the OCR engine and returns this instance for method chaining. - * - * @param ocrEngine the OCR engine, or null to use default - * @return this options instance - */ - public ConvertDocumentOptions withOcrEngine(@Nullable OcrEngine ocrEngine) { - setOcrEngine(ocrEngine); - return this; - } - - /** - * Sets the OCR language codes and returns this instance for method chaining. - * - * @param ocrLang the list of language codes, or null to clear - * @return this options instance - */ - public ConvertDocumentOptions withOcrLang(@Nullable List ocrLang) { - setOcrLang(ocrLang); - return this; - } - - /** - * Sets the page range using from and to page numbers and returns this instance for method chaining. - * - * @param fromPage the starting page number, or null to clear the range - * @param toPage the ending page number, or null to clear the range - * @return this options instance - * @throws IllegalArgumentException if only one of fromPage or toPage is null - */ - public ConvertDocumentOptions withPageRange(@Nullable Integer fromPage, @Nullable Integer toPage) { - if (((fromPage == null) && (toPage != null)) || ((fromPage != null) && (toPage == null))) { - throw new IllegalArgumentException("fromPage and toPage must both be null or both not null"); - } - - if (fromPage != null) { - setPageRange(List.of(fromPage, toPage)); - } - else { - setPageRange(List.of()); - } - - return this; - } - - /** - * Sets the page range and returns this instance for method chaining. - * - * @param pageRange a list containing the page range (from and to page numbers), or null to clear - * @return this options instance - */ - public ConvertDocumentOptions withPageRange(@Nullable List pageRange) { - setPageRange(pageRange); - return this; - } - - /** - * Sets the PDF backend and returns this instance for method chaining. - * - * @param pdfBackend the PDF backend, or null to use default - * @return this options instance - */ - public ConvertDocumentOptions withPdfBackend(@Nullable PdfBackend pdfBackend) { - setPdfBackend(pdfBackend); - return this; - } - - /** - * Sets the API-based picture description configuration and returns this instance for method chaining. - * - * @param pictureDescriptionApi the picture description API configuration, or null to disable - * @return this options instance - * @throws IllegalArgumentException if both API and local picture description are set - */ - public ConvertDocumentOptions withPictureDescriptionApi(@Nullable PictureDescriptionApi pictureDescriptionApi) { - setPictureDescriptionApi(pictureDescriptionApi); - return this; - } - - /** - * Sets the picture description area threshold and returns this instance for method chaining. - * - * @param pictureDescriptionAreaThreshold the area threshold value, or null to use default - * @return this options instance - */ - public ConvertDocumentOptions withPictureDescriptionAreaThreshold(@Nullable Double pictureDescriptionAreaThreshold) { - setPictureDescriptionAreaThreshold(pictureDescriptionAreaThreshold); - return this; - } - - /** - * Sets the local picture description configuration and returns this instance for method chaining. - * - * @param pictureDescriptionLocal the picture description local configuration, or null to disable - * @return this options instance - * @throws IllegalArgumentException if both API and local picture description are set - */ - public ConvertDocumentOptions withPictureDescriptionLocal(@Nullable PictureDescriptionLocal pictureDescriptionLocal) { - setPictureDescriptionLocal(pictureDescriptionLocal); - return this; - } - - /** - * Sets the processing pipeline and returns this instance for method chaining. - * - * @param pipeline the processing pipeline, or null to use default - * @return this options instance - */ - public ConvertDocumentOptions withPipeline(@Nullable ProcessingPipeline pipeline) { - setPipeline(pipeline); - return this; - } - - /** - * Sets whether to enable table cell matching and returns this instance for method chaining. - * - * @param tableCellMatching true to enable table cell matching, false to disable, or null to use default - * @return this options instance - */ - public ConvertDocumentOptions withTableCellMatching(@Nullable Boolean tableCellMatching) { - setTableCellMatching(tableCellMatching); - return this; - } - - /** - * Sets the table processing mode and returns this instance for method chaining. - * - * @param tableMode the table mode, or null to use default - * @return this options instance - */ - public ConvertDocumentOptions withTableMode(@Nullable TableFormerMode tableMode) { - setTableMode(tableMode); - return this; - } - - /** - * Sets the list of output formats and returns this instance for method chaining. - * - * @param toFormats the list of output formats, or null to use default - * @return this options instance - */ - public ConvertDocumentOptions withToFormats(@Nullable List toFormats) { - setToFormats(toFormats); - return this; - } - - /** - * Sets the VLM pipeline model type and returns this instance for method chaining. - * - * @param vlmPipelineModel the VLM model type, or null to use default - * @return this options instance - */ - public ConvertDocumentOptions withVlmPipelineModel(@Nullable VlmModelType vlmPipelineModel) { - setVlmPipelineModel(vlmPipelineModel); - return this; - } - - /** - * Sets the API-based VLM pipeline model identifier and returns this instance for method chaining. - * - * @param vlmPipelineModelApi the API model identifier, or null to use default - * @return this options instance - */ - public ConvertDocumentOptions withVlmPipelineModelApi(@Nullable String vlmPipelineModelApi) { - setVlmPipelineModelApi(vlmPipelineModelApi); - return this; - } - - /** - * Sets the local VLM pipeline model identifier and returns this instance for method chaining. - * - * @param vlmPipelineModelLocal the local model identifier, or null to use default - * @return this options instance - */ - public ConvertDocumentOptions withVlmPipelineModelLocal(@Nullable String vlmPipelineModelLocal) { - setVlmPipelineModelLocal(vlmPipelineModelLocal); - return this; - } - - @Override - public String toString() { - return "ConvertDocumentOptions{" + - "abortOnError=" + abortOnError + - ", fromFormats=" + fromFormats + - ", toFormats=" + toFormats + - ", imageExportMode=" + imageExportMode + - ", doOcr=" + doOcr + - ", forceOcr=" + forceOcr + - ", ocrEngine=" + ocrEngine + - ", ocrLang=" + ocrLang + - ", pdfBackend=" + pdfBackend + - ", tableMode=" + tableMode + - ", tableCellMatching=" + tableCellMatching + - ", pipeline=" + pipeline + - ", pageRange=" + pageRange + - ", documentTimeout=" + documentTimeout + - ", doTableStructure=" + doTableStructure + - ", includeImages=" + includeImages + - ", imagesScale=" + imagesScale + - ", mdPageBreakPlaceholder='" + mdPageBreakPlaceholder + '\'' + - ", doCodeEnrichment=" + doCodeEnrichment + - ", doFormulaEnrichment=" + doFormulaEnrichment + - ", doPictureClassification=" + doPictureClassification + - ", doPictureDescription=" + doPictureDescription + - ", pictureDescriptionAreaThreshold=" + pictureDescriptionAreaThreshold + - ", pictureDescriptionLocal=" + pictureDescriptionLocal + - ", pictureDescriptionApi=" + pictureDescriptionApi + - ", vlmPipelineModel=" + vlmPipelineModel + - ", vlmPipelineModelLocal='" + vlmPipelineModelLocal + '\'' + - ", vlmPipelineModelApi='" + vlmPipelineModelApi + '\'' + - '}'; - } + @tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "") + public static class Builder { } } diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionApi.java b/docling-api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionApi.java index ee99c86..369e26b 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionApi.java +++ b/docling-api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionApi.java @@ -1,17 +1,15 @@ package ai.docling.api.convert.request.options; -import static ai.docling.api.util.ValidationUtils.ensureNotNull; - import java.net.URI; import java.time.Duration; -import java.util.Collections; -import java.util.HashMap; import java.util.Map; import org.jspecify.annotations.Nullable; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; /** * Configuration options for picture description API integration. @@ -19,222 +17,38 @@ * including URL, headers, parameters, timeout, concurrency, and custom prompts. */ @JsonInclude(JsonInclude.Include.NON_EMPTY) +@tools.jackson.databind.annotation.JsonDeserialize(builder = PictureDescriptionApi.Builder.class) +@lombok.extern.jackson.Jacksonized +@lombok.Builder(toBuilder = true) +@lombok.Getter +@lombok.ToString public class PictureDescriptionApi { @JsonProperty("url") + @lombok.NonNull private URI url; @JsonProperty("headers") - private Map headers = new HashMap<>(); + @JsonSetter(nulls = Nulls.AS_EMPTY) + @lombok.Singular + private Map headers; @JsonProperty("params") - private Map params = new HashMap<>(); + @JsonSetter(nulls = Nulls.AS_EMPTY) + @lombok.Singular + private Map params; @JsonProperty("timeout") + @Nullable private Duration timeout; @JsonProperty("concurrency") + @Nullable private Integer concurrency; @JsonProperty("prompt") - private String prompt; - - /** - * Gets the URL of the picture description API endpoint. - * - * @return the API endpoint URL, or null if not set - */ - @Nullable - public URI getUrl() { - return url; - } - - /** - * Sets the URL of the picture description API endpoint. - * - * @param url the API endpoint URL to set - */ - public void setUrl(URI url) { - this.url = ensureNotNull(url, "url"); - } - - /** - * Sets the URL of the picture description API endpoint and returns this instance. - * - * @param url the API endpoint URL to set - * @return this instance for method chaining - */ - public PictureDescriptionApi withUrl(URI url) { - setUrl(url); - return this; - } - - /** - * Gets the headers to be sent with the API request. - * - * @return the map of headers, or null if not set - */ - @Nullable - public Map getHeaders() { - return Collections.unmodifiableMap(headers); - } - - /** - * Sets the headers to be sent with the API request. - * - * @param headers the map of headers to set - */ - public void setHeaders(@Nullable Map headers) { - this.headers.clear(); - - if (headers != null) { - this.headers.putAll(headers); - } - } - - /** - * Sets the headers to be sent with the API request and returns this instance. - * - * @param headers the map of headers to set - * @return this instance for method chaining - */ - public PictureDescriptionApi withHeaders(@Nullable Map headers) { - setHeaders(headers); - return this; - } - - /** - * Gets the parameters to be sent with the API request. - * - * @return the map of parameters, or null if not set - */ - @Nullable - public Map getParams() { - return Collections.unmodifiableMap(params); - } - - /** - * Sets the parameters to be sent with the API request. - * - * @param params the map of parameters to set - */ - public void setParams(@Nullable Map params) { - this.params.clear(); - - if (params != null) { - this.params.putAll(params); - } - } - - /** - * Sets the parameters to be sent with the API request and returns this instance. - * - * @param params the map of parameters to set - * @return this instance for method chaining - */ - public PictureDescriptionApi withParams(@Nullable Map params) { - setParams(params); - return this; - } - - /** - * Gets the timeout duration for the API request. - * - * @return the timeout duration, or null if not set - */ - @Nullable - public Duration getTimeout() { - return timeout; - } - - /** - * Sets the timeout duration for the API request. - * - * @param timeout the timeout duration to set - */ - public void setTimeout(@Nullable Duration timeout) { - this.timeout = timeout; - } - - /** - * Sets the timeout duration for the API request and returns this instance. - * - * @param timeout the timeout duration to set - * @return this instance for method chaining - */ - public PictureDescriptionApi withTimeout(@Nullable Duration timeout) { - setTimeout(timeout); - return this; - } - - /** - * Gets the maximum number of concurrent API requests allowed. - * - * @return the concurrency limit, or null if not set - */ @Nullable - public Integer getConcurrency() { - return concurrency; - } - - /** - * Sets the maximum number of concurrent API requests allowed. - * - * @param concurrency the concurrency limit to set - */ - public void setConcurrency(@Nullable Integer concurrency) { - this.concurrency = concurrency; - } - - /** - * Sets the maximum number of concurrent API requests allowed and returns this instance. - * - * @param concurrency the concurrency limit to set - * @return this instance for method chaining - */ - public PictureDescriptionApi withConcurrency(@Nullable Integer concurrency) { - setConcurrency(concurrency); - return this; - } - - /** - * Gets the custom prompt to be used for picture description. - * - * @return the custom prompt, or null if not set - */ - @Nullable - public String getPrompt() { - return prompt; - } - - /** - * Sets the custom prompt to be used for picture description. - * - * @param prompt the custom prompt to set - */ - public void setPrompt(@Nullable String prompt) { - this.prompt = prompt; - } - - /** - * Sets the custom prompt to be used for picture description and returns this instance. - * - * @param prompt the custom prompt to set - * @return this instance for method chaining - */ - public PictureDescriptionApi withPrompt(@Nullable String prompt) { - setPrompt(prompt); - return this; - } + private String prompt; - @Override - public String toString() { - return "PictureDescriptionApi{" + - "url=" + (url == null ? "null" : url.toString()) + - ", headers=" + (headers == null ? "null" : headers.toString()) + - ", params=" + (params == null ? "null" : params.toString()) + - ", timeout=" + (timeout == null ? "null" : timeout.toString()) + - ", concurrency=" + (concurrency == null ? "null" : concurrency.toString()) + - ", prompt=" + (prompt == null ? "null" : "'" + prompt + "'") + - '}'; - } + @tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "") + public static class Builder { } } diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionLocal.java b/docling-api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionLocal.java index 400809b..c1a2dcf 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionLocal.java +++ b/docling-api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionLocal.java @@ -1,133 +1,38 @@ package ai.docling.api.convert.request.options; -import static ai.docling.api.util.ValidationUtils.ensureNotEmpty; - -import java.util.Collections; -import java.util.HashMap; import java.util.Map; import org.jspecify.annotations.Nullable; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; /** * Configuration for local picture description generation. * This class contains settings for generating descriptions of pictures using a local model. */ @JsonInclude(JsonInclude.Include.NON_EMPTY) +@tools.jackson.databind.annotation.JsonDeserialize(builder = PictureDescriptionLocal.Builder.class) +@lombok.extern.jackson.Jacksonized +@lombok.Builder(toBuilder = true) +@lombok.Getter +@lombok.ToString public class PictureDescriptionLocal { @JsonProperty("repo_id") + @lombok.NonNull private String repoId; @JsonProperty("prompt") + @Nullable private String prompt; @JsonProperty("generation_config") - private Map generationConfig = new HashMap<>(); - - /** - * Gets the repository identifier for the local model. - * - * @return the repository ID, or null if not set - */ - @Nullable - public String getRepoId() { - return repoId; - } - - /** - * Sets the repository identifier for the local model. - * - * @param repoId the repository ID to set, or null to clear - */ - public void setRepoId(String repoId) { - this.repoId = ensureNotEmpty(repoId, "repoId"); - } - - /** - * Sets the repository identifier for the local model (fluent API). - * - * @param repoId the repository ID to set, or null to clear - * @return this instance for method chaining - */ - public PictureDescriptionLocal withRepoId(String repoId) { - setRepoId(repoId); - return this; - } - - /** - * Gets the prompt used for picture description generation. - * - * @return the prompt, or null if not set - */ - @Nullable - public String getPrompt() { - return prompt; - } - - /** - * Sets the prompt used for picture description generation. - * - * @param prompt the prompt to set, or null to clear - */ - public void setPrompt(@Nullable String prompt) { - this.prompt = prompt; - } - - /** - * Sets the prompt used for picture description generation (fluent API). - * - * @param prompt the prompt to set, or null to clear - * @return this instance for method chaining - */ - public PictureDescriptionLocal withPrompt(@Nullable String prompt) { - setPrompt(prompt); - return this; - } - - /** - * Gets an unmodifiable view of the generation configuration parameters. - * - * @return an unmodifiable map of generation configuration parameters - */ - @Nullable - public Map getGenerationConfig() { - return Collections.unmodifiableMap(generationConfig); - } - - /** - * Sets the generation configuration parameters. - * The provided map is copied to avoid external modifications. - * - * @param generationConfig the generation configuration parameters to set, or null to clear - */ - public void setGenerationConfig(@Nullable Map generationConfig) { - this.generationConfig.clear(); - - if (generationConfig != null) { - this.generationConfig.putAll(generationConfig); - } - } - - /** - * Sets the generation configuration parameters (fluent API). - * The provided map is copied to avoid external modifications. - * - * @param generationConfig the generation configuration parameters to set, or null to clear - * @return this instance for method chaining - */ - public PictureDescriptionLocal withGenerationConfig(@Nullable Map generationConfig) { - setGenerationConfig(generationConfig); - return this; - } + @JsonSetter(nulls = Nulls.AS_EMPTY) + @lombok.Singular("generationConfig") + private Map generationConfig; - @Override - public String toString() { - return "PictureDescriptionLocal{" + - "repoId=" + (repoId == null ? "null" : "'" + repoId + "'") + - ", prompt=" + (prompt == null ? "null" : "'" + prompt + "'") + - ", generationConfig=" + (generationConfig == null ? "null" : generationConfig.toString()) + - '}'; - } + @tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "") + public static class Builder { } } diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/options/VlmModelApi.java b/docling-api/src/main/java/ai/docling/api/convert/request/options/VlmModelApi.java index b03aede..a8382ba 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/options/VlmModelApi.java +++ b/docling-api/src/main/java/ai/docling/api/convert/request/options/VlmModelApi.java @@ -2,304 +2,59 @@ import java.net.URI; import java.time.Duration; -import java.util.Collections; -import java.util.HashMap; import java.util.Map; import org.jspecify.annotations.Nullable; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; /** * API details for using a vision-language model for the VLM pipeline. */ -@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@tools.jackson.databind.annotation.JsonDeserialize(builder = VlmModelApi.Builder.class) +@lombok.extern.jackson.Jacksonized +@lombok.Builder(toBuilder = true) +@lombok.Getter +@lombok.ToString public class VlmModelApi { @JsonProperty("url") + @Nullable private URI url; @JsonProperty("headers") - private Map headers = new HashMap<>(); + @JsonSetter(nulls = Nulls.AS_EMPTY) + @lombok.Singular + private Map headers; @JsonProperty("params") - private Map params = new HashMap<>(); + @JsonSetter(nulls = Nulls.AS_EMPTY) + @lombok.Singular + private Map params; @JsonProperty("timeout") + @Nullable private Duration timeout; @JsonProperty("concurrency") + @Nullable private Integer concurrency; @JsonProperty("prompt") + @Nullable private String prompt; @JsonProperty("scale") + @Nullable private Integer scale; @JsonProperty("response_format") - private ResponseFormat responseFormat; - - /** - * Gets the endpoint which accepts OpenAI API compatible requests. - * - * @return the API endpoint URL - */ - @Nullable - public URI getUrl() { - return url; - } - - /** - * Sets the endpoint which accepts OpenAI API compatible requests. - * - * @param url the API endpoint URL - */ - public void setUrl(@Nullable URI url) { - this.url = url; - } - - /** - * Sets the endpoint which accepts OpenAI API compatible requests. - * - * @param url the API endpoint URL - * @return this instance for method chaining - */ - public VlmModelApi withUrl(@Nullable URI url) { - setUrl(url); - return this; - } - - /** - * Gets the headers used for calling the API endpoint. - * For example, it could include authentication headers. - * - * @return the headers map - */ - public Map getHeaders() { - return Collections.unmodifiableMap(headers); - } - - /** - * Sets the headers used for calling the API endpoint. - * For example, it could include authentication headers. - * - * @param headers the headers map - */ - public void setHeaders(@Nullable Map headers) { - this.headers.clear(); - - if (headers != null) { - this.headers.putAll(headers); - } - } - - /** - * Sets the headers used for calling the API endpoint. - * For example, it could include authentication headers. - * - * @param headers the headers map - * @return this instance for method chaining - */ - public VlmModelApi withHeaders(@Nullable Map headers) { - setHeaders(headers); - return this; - } - - /** - * Gets the model parameters. - * - * @return the model parameters - */ - public Map getParams() { - return Collections.unmodifiableMap(params); - } - - /** - * Sets the model parameters. - * - * @param params the model parameters - */ - public void setParams(@Nullable Map params) { - this.params.clear(); - - if (params != null) { - this.params.putAll(params); - } - } - - /** - * Sets the model parameters. - * - * @param params the model parameters - * @return this instance for method chaining - */ - public VlmModelApi withParams(@Nullable Map params) { - setParams(params); - return this; - } - - /** - * Gets the timeout for the API request. - * - * @return the timeout duration - */ - @Nullable - public Duration getTimeout() { - return timeout; - } - - /** - * Sets the timeout for the API request. - * - * @param timeout the timeout duration - */ - public void setTimeout(@Nullable Duration timeout) { - this.timeout = timeout; - } - - /** - * Sets the timeout for the API request. - * - * @param timeout the timeout duration - * @return this instance for method chaining - */ - public VlmModelApi withTimeout(@Nullable Duration timeout) { - setTimeout(timeout); - return this; - } - - /** - * Gets the maximum number of concurrent requests to the API. - * - * @return the concurrency limit - */ - @Nullable - public Integer getConcurrency() { - return concurrency; - } - - /** - * Sets the maximum number of concurrent requests to the API. - * - * @param concurrency the concurrency limit - */ - public void setConcurrency(@Nullable Integer concurrency) { - this.concurrency = concurrency; - } - - /** - * Sets the maximum number of concurrent requests to the API. - * - * @param concurrency the concurrency limit - * @return this instance for method chaining - */ - public VlmModelApi withConcurrency(@Nullable Integer concurrency) { - setConcurrency(concurrency); - return this; - } - - /** - * Gets the prompt used when calling the vision-language model. - * - * @return the prompt - */ - @Nullable - public String getPrompt() { - return prompt; - } - - /** - * Sets the prompt used when calling the vision-language model. - * - * @param prompt the prompt - */ - public void setPrompt(@Nullable String prompt) { - this.prompt = prompt; - } - - /** - * Sets the prompt used when calling the vision-language model. - * - * @param prompt the prompt - * @return this instance for method chaining - */ - public VlmModelApi withPrompt(@Nullable String prompt) { - setPrompt(prompt); - return this; - } - - /** - * Gets the scale factor of the images used. - * - * @return the scale factor - */ @Nullable - public Integer getScale() { - return scale; - } - - /** - * Sets the scale factor of the images used. - * - * @param scale the scale factor - */ - public void setScale(@Nullable Integer scale) { - this.scale = scale; - } - - /** - * Sets the scale factor of the images used. - * - * @param scale the scale factor - * @return this instance for method chaining - */ - public VlmModelApi withScale(@Nullable Integer scale) { - setScale(scale); - return this; - } - - /** - * Gets the type of response generated by the model. - * - * @return the response format - */ - @Nullable - public ResponseFormat getResponseFormat() { - return responseFormat; - } - - /** - * Sets the type of response generated by the model. - * - * @param responseFormat the response format - */ - public void setResponseFormat(@Nullable ResponseFormat responseFormat) { - this.responseFormat = responseFormat; - } - - /** - * Sets the type of response generated by the model. - * - * @param responseFormat the response format - * @return this instance for method chaining - */ - public VlmModelApi withResponseFormat(@Nullable ResponseFormat responseFormat) { - setResponseFormat(responseFormat); - return this; - } + private ResponseFormat responseFormat; - @Override - public String toString() { - return "VlmModelApi{" + - "url=" + url + - ", headers=" + headers + - ", params=" + params + - ", timeout=" + timeout + - ", concurrency=" + concurrency + - ", prompt='" + prompt + '\'' + - ", scale=" + scale + - ", responseFormat=" + responseFormat + - '}'; - } + @tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "") + public static class Builder { } } diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/options/VlmModelLocal.java b/docling-api/src/main/java/ai/docling/api/convert/request/options/VlmModelLocal.java index 91c4a22..c836b72 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/options/VlmModelLocal.java +++ b/docling-api/src/main/java/ai/docling/api/convert/request/options/VlmModelLocal.java @@ -1,270 +1,56 @@ package ai.docling.api.convert.request.options; -import java.util.Collections; -import java.util.HashMap; import java.util.Map; import org.jspecify.annotations.Nullable; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; /** * Options for running a local vision-language model for the VLM pipeline. * The parameters refer to a model hosted on Hugging Face. */ -@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@tools.jackson.databind.annotation.JsonDeserialize(builder = VlmModelLocal.Builder.class) +@lombok.extern.jackson.Jacksonized +@lombok.Builder(toBuilder = true) +@lombok.Getter +@lombok.ToString public class VlmModelLocal { @JsonProperty("repo_id") + @Nullable private String repoId; @JsonProperty("prompt") + @Nullable private String prompt; @JsonProperty("scale") + @Nullable private Integer scale; @JsonProperty("response_format") + @Nullable private ResponseFormat responseFormat; @JsonProperty("inference_framework") + @Nullable private InferenceFramework inferenceFramework; @JsonProperty("transformers_model_type") + @Nullable private TransformersModelType transformersModelType; @JsonProperty("extra_generation_config") - private Map extraGenerationConfig = new HashMap<>(); - - /** - * Gets the repository id from the Hugging Face Hub. - * - * @return the repository id - */ - @Nullable - public String getRepoId() { - return repoId; - } - - /** - * Sets the repository id from the Hugging Face Hub. - * - * @param repoId the repository id - */ - public void setRepoId(@Nullable String repoId) { - this.repoId = repoId; - } - - /** - * Sets the repository id from the Hugging Face Hub. - * - * @param repoId the repository id - * @return this instance for method chaining - */ - public VlmModelLocal withRepoId(@Nullable String repoId) { - setRepoId(repoId); - return this; - } - - /** - * Gets the prompt used when calling the vision-language model. - * - * @return the prompt - */ - @Nullable - public String getPrompt() { - return prompt; - } - - /** - * Sets the prompt used when calling the vision-language model. - * - * @param prompt the prompt - */ - public void setPrompt(@Nullable String prompt) { - this.prompt = prompt; - } - - /** - * Sets the prompt used when calling the vision-language model. - * - * @param prompt the prompt - * @return this instance for method chaining - */ - public VlmModelLocal withPrompt(@Nullable String prompt) { - setPrompt(prompt); - return this; - } - - /** - * Gets the scale factor of the images used. - * - * @return the scale factor - */ - @Nullable - public Integer getScale() { - return scale; - } - - /** - * Sets the scale factor of the images used. - * - * @param scale the scale factor - */ - public void setScale(@Nullable Integer scale) { - this.scale = scale; - } - - /** - * Sets the scale factor of the images used. - * - * @param scale the scale factor - * @return this instance for method chaining - */ - public VlmModelLocal withScale(@Nullable Integer scale) { - setScale(scale); - return this; - } - - /** - * Gets the type of response generated by the model. - * - * @return the response format - */ - @Nullable - public ResponseFormat getResponseFormat() { - return responseFormat; - } + @JsonSetter(nulls = Nulls.AS_EMPTY) + @lombok.Singular("extraGenerationConfig") + private Map extraGenerationConfig; - /** - * Sets the type of response generated by the model. - * - * @param responseFormat the response format - */ - public void setResponseFormat(@Nullable ResponseFormat responseFormat) { - this.responseFormat = responseFormat; - } - - /** - * Sets the type of response generated by the model. - * - * @param responseFormat the response format - * @return this instance for method chaining - */ - public VlmModelLocal withResponseFormat(@Nullable ResponseFormat responseFormat) { - setResponseFormat(responseFormat); - return this; - } - - /** - * Gets the inference framework to use. - * - * @return the inference framework - */ - @Nullable - public InferenceFramework getInferenceFramework() { - return inferenceFramework; - } - - /** - * Sets the inference framework to use. - * - * @param inferenceFramework the inference framework - */ - public void setInferenceFramework(@Nullable InferenceFramework inferenceFramework) { - this.inferenceFramework = inferenceFramework; - } - - /** - * Sets the inference framework to use. - * - * @param inferenceFramework the inference framework - * @return this instance for method chaining - */ - public VlmModelLocal withInferenceFramework(@Nullable InferenceFramework inferenceFramework) { - setInferenceFramework(inferenceFramework); - return this; - } - - /** - * Gets the type of transformers auto-model to use. - * - * @return the transformers model type - */ - @Nullable - public TransformersModelType getTransformersModelType() { - return transformersModelType; - } - - /** - * Sets the type of transformers auto-model to use. - * - * @param transformersModelType the transformers model type - */ - public void setTransformersModelType(@Nullable TransformersModelType transformersModelType) { - this.transformersModelType = transformersModelType; - } - - /** - * Sets the type of transformers auto-model to use. - * - * @param transformersModelType the transformers model type - * @return this instance for method chaining - */ - public VlmModelLocal withTransformersModelType(@Nullable TransformersModelType transformersModelType) { - setTransformersModelType(transformersModelType); - return this; - } - - /** - * Gets the extra generation config from Hugging Face. - * - * @return the extra generation config - * @see Hugging Face GenerationConfig - */ - @Nullable - public Map getExtraGenerationConfig() { - return Collections.unmodifiableMap(extraGenerationConfig); - } - - /** - * Sets the extra generation config from Hugging Face. - * - * @param extraGenerationConfig the extra generation config - * @see Hugging Face GenerationConfig - */ - public void setExtraGenerationConfig(@Nullable Map extraGenerationConfig) { - this.extraGenerationConfig.clear(); - - if (extraGenerationConfig != null) { - this.extraGenerationConfig.putAll(extraGenerationConfig); - } - } - - /** - * Sets the extra generation config from Hugging Face. - * - * @param extraGenerationConfig the extra generation config - * @return this instance for method chaining - * @see Hugging Face GenerationConfig - */ - public VlmModelLocal withExtraGenerationConfig(@Nullable Map extraGenerationConfig) { - setExtraGenerationConfig(extraGenerationConfig); - return this; - } - - @Override - public String toString() { - return "VlmModelLocal{" + - "repoId='" + repoId + '\'' + - ", prompt='" + prompt + '\'' + - ", scale=" + scale + - ", responseFormat=" + responseFormat + - ", inferenceFramework=" + inferenceFramework + - ", transformersModelType=" + transformersModelType + - ", extraGenerationConfig=" + extraGenerationConfig + - '}'; - } + @tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "") + public static class Builder { } /** * Inference framework to use. diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/source/FileSource.java b/docling-api/src/main/java/ai/docling/api/convert/request/source/FileSource.java index 911c262..97874c7 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/source/FileSource.java +++ b/docling-api/src/main/java/ai/docling/api/convert/request/source/FileSource.java @@ -1,9 +1,5 @@ package ai.docling.api.convert.request.source; -import static ai.docling.api.util.ValidationUtils.ensureNotBlank; - -import org.jspecify.annotations.Nullable; - import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; @@ -21,73 +17,20 @@ *

By default, JSON includes non-empty fields only. */ @JsonInclude(JsonInclude.Include.NON_EMPTY) -public final class FileSource extends Source { +@tools.jackson.databind.annotation.JsonDeserialize(builder = FileSource.Builder.class) +@lombok.extern.jackson.Jacksonized +@lombok.Builder(toBuilder = true) +@lombok.Getter +@lombok.ToString +public final class FileSource extends Source { @JsonProperty("base64_string") + @lombok.NonNull private String base64String; @JsonProperty("filename") + @lombok.NonNull private String filename; - public FileSource() { - super(); - setKind(Kind.FILE); - } - - /** - * Base64 content of the file. - * @return base64 content or {@code null} - */ - @Nullable - public String getBase64String() { - return base64String; - } - - /** - * Sets the base64 content of the file. - * @param base64String may be {@code null} - */ - public void setBase64String(String base64String) { - this.base64String = ensureNotBlank(base64String, "base64String"); - } - - public FileSource withBase64String(String base64String) { - setBase64String(base64String); - return this; - } - - /** - * Filename of the uploaded document. - * @return filename or {@code null} - */ - @Nullable - public String getFilename() { - return filename; - } - - /** - * Sets the filename of the uploaded document. - * @param filename may be {@code null} - */ - public void setFilename(String filename) { - this.filename = ensureNotBlank(filename, "filename"); - } - - public FileSource withFilename(String filename) { - setFilename(filename); - return this; - } - - @Override - public String toString() { - return "FileSource{" + - "kind='" + getKind() + "'" + - ", base64String=" + (base64String == null ? "null" : "'" + base64String + "'") + - ", filename=" + (filename == null ? "null" : "'" + filename + "'") + - '}'; - } - - @Override - public void setKind(@Nullable Kind kind) { - super.setKind(Kind.FILE); - } + @tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "") + public static class Builder { } } diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/source/HttpSource.java b/docling-api/src/main/java/ai/docling/api/convert/request/source/HttpSource.java index 0e7c2f3..507b520 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/source/HttpSource.java +++ b/docling-api/src/main/java/ai/docling/api/convert/request/source/HttpSource.java @@ -1,16 +1,12 @@ package ai.docling.api.convert.request.source; -import static ai.docling.api.util.ValidationUtils.ensureNotNull; - import java.net.URI; -import java.util.Collections; -import java.util.HashMap; import java.util.Map; -import org.jspecify.annotations.Nullable; - import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; /** * Represents an HTTP-based data source. @@ -26,106 +22,21 @@ *

By default, JSON includes non-empty fields only. */ @JsonInclude(JsonInclude.Include.NON_EMPTY) -public final class HttpSource extends Source { +@tools.jackson.databind.annotation.JsonDeserialize(builder = HttpSource.Builder.class) +@lombok.extern.jackson.Jacksonized +@lombok.Builder(toBuilder = true) +@lombok.Getter +@lombok.ToString +public final class HttpSource extends Source { @JsonProperty("url") + @lombok.NonNull private URI url; @JsonProperty("headers") - private Map headers = new HashMap<>(); - - public HttpSource() { - super(); - setKind(Kind.HTTP); - } - - /** - * Retrieves the URL associated with this HTTP source. - * - * @return the {@link URI} representing the URL of the HTTP source, or {@code null} if not set. - */ - @Nullable - public URI getUrl() { - return url; - } - - /** - * Sets the {@code url} for this HTTP source. The URL represents the endpoint - * to be used by this HTTP-based source. - * - * @param url the {@link URI} of the HTTP source; may be {@code null}. - * A {@code null} value indicates that the URL is unset. - */ - public void setUrl(URI url) { - this.url = ensureNotNull(url, "url"); - } - - /** - * Sets the {@code url} for this {@code HttpSource} and returns the updated instance. - * This method allows chaining while configuring the {@link HttpSource}. - * - * @param url the {@link URI} representing the HTTP source's endpoint; may be {@code null}, - * indicating that the URL is unset. - * @return the updated {@code HttpSource} instance with the specified {@code url}. - */ - public HttpSource withUrl(URI url) { - setUrl(url); - return this; - } - - /** - * Retrieves an unmodifiable view of the HTTP headers associated with the {@link HttpSource}. - * The returned map contains key-value pairs representing the custom headers configured - * for this HTTP source. - * - * @return an unmodifiable {@link Map} containing the HTTP headers, where the keys are - * {@link String} objects representing header names, and the values are - * {@link Object} objects representing header values. Never {@code null}. - */ - public Map getHeaders() { - return Collections.unmodifiableMap(headers); - } - - /** - * Configures the HTTP headers for this {@link HttpSource}. - * If a non-{@code null} map is provided, it replaces the existing headers with the new ones. - * If {@code null} is supplied, all headers are cleared. - * - * @param headers a {@link Map} where keys are {@link String} objects representing header names, - * and values are {@link Object} instances representing header values; may be {@code null}. - */ - public void setHeaders(@Nullable Map headers) { - this.headers.clear(); - - if (headers != null) { - this.headers.putAll(headers); - } - } - - /** - * Updates the {@link HttpSource} with the specified HTTP headers and returns the updated instance. - * This method replaces the existing headers with the ones provided in the supplied map. - * - * @param headers a {@link Map} where keys are {@link String} objects representing header names, - * and values are {@link Object} instances representing header values; - * may be {@code null} to clear all headers. - * @return the updated {@link HttpSource} instance with the provided headers configuration. - */ - public HttpSource withHeaders(@Nullable Map headers) { - setHeaders(headers); - return this; - } - - @Override - public String toString() { - return "HttpSource{" + - "kind='" + getKind() + "'" + - ", url=" + (url == null ? "null" : url.toString()) + - ", headers=" + (headers == null ? "null" : headers.toString()) + - '}'; - } + @JsonSetter(nulls = Nulls.AS_EMPTY) + @lombok.Singular + private Map headers; - @Override - public void setKind(@Nullable Kind kind) { - super.setKind(Kind.HTTP); - } + @tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "") + public static class Builder { } } diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/source/Source.java b/docling-api/src/main/java/ai/docling/api/convert/request/source/Source.java index dd0892b..2247867 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/source/Source.java +++ b/docling-api/src/main/java/ai/docling/api/convert/request/source/Source.java @@ -1,13 +1,27 @@ package ai.docling.api.convert.request.source; -import org.jspecify.annotations.Nullable; - +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; /** * Source of the document. */ -public sealed abstract class Source> permits FileSource, HttpSource { +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "kind" +) +@JsonSubTypes({ + @Type(value = FileSource.class, name = "file"), + @Type(value = HttpSource.class, name = "http") +}) +@lombok.Getter +@lombok.ToString +public sealed abstract class Source permits FileSource, HttpSource { /** * Enum representing the type of {@link Source}. *

    @@ -21,37 +35,4 @@ enum Kind { @JsonProperty("http") HTTP, @JsonProperty("file") FILE } - - @JsonProperty("kind") - private Kind kind = Kind.FILE; - - /** - * Retrieves the {@link Kind} of this {@link Source}. - * - * @return the {@link Kind} of this {@code Source}, or {@code null} if not set. - */ - @Nullable - public Kind getKind() { - return kind; - } - - /** - * Sets the {@link Kind} of this {@code Source}. - * - * @param kind the {@link Kind} representing the type of this source; may be {@code null}. - */ - public void setKind(@Nullable Kind kind) { - this.kind = kind; - } - - /** - * Sets the {@link Kind} of this {@link Source} and returns the updated instance of {@link Source}. - * - * @param kind the {@link Kind} representing the type of this source; may be {@code null}. - * @return the updated {@code Source} instance with the newly set {@link Kind}. - */ - public S withKind(@Nullable Kind kind) { - setKind(kind); - return (S) this; - } } diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/target/InBodyTarget.java b/docling-api/src/main/java/ai/docling/api/convert/request/target/InBodyTarget.java index 88c3e55..5779f66 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/target/InBodyTarget.java +++ b/docling-api/src/main/java/ai/docling/api/convert/request/target/InBodyTarget.java @@ -1,7 +1,5 @@ package ai.docling.api.convert.request.target; -import org.jspecify.annotations.Nullable; - import com.fasterxml.jackson.annotation.JsonInclude; /** @@ -15,21 +13,13 @@ *

    This class overrides {@link Object#toString()} for a string representation of the instance. */ @JsonInclude(JsonInclude.Include.NON_EMPTY) -public final class InBodyTarget extends Target { - public InBodyTarget() { - super(); - setKind(Kind.INBODY); - } - - @Override - public String toString() { - return "InBodyTarget{" + - "kind='" + getKind() + "'" + - '}'; - } +@tools.jackson.databind.annotation.JsonDeserialize(builder = InBodyTarget.Builder.class) +@lombok.extern.jackson.Jacksonized +@lombok.Builder(toBuilder = true) +@lombok.Getter +@lombok.ToString +public final class InBodyTarget extends Target { - @Override - public void setKind(@Nullable Kind kind) { - super.setKind(Kind.INBODY); - } + @tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "") + public static class Builder { } } diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/target/PutTarget.java b/docling-api/src/main/java/ai/docling/api/convert/request/target/PutTarget.java index 3bfe958..5863452 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/target/PutTarget.java +++ b/docling-api/src/main/java/ai/docling/api/convert/request/target/PutTarget.java @@ -1,11 +1,7 @@ package ai.docling.api.convert.request.target; -import static ai.docling.api.util.ValidationUtils.ensureNotNull; - import java.net.URI; -import org.jspecify.annotations.Nullable; - import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; @@ -21,68 +17,18 @@ * *

    This class provides JSON serialization annotations to include only non-empty fields in the serialization output. * The {@code url} is nullable, allowing the absence of a URI to be explicitly represented. - * - *

    The {@link #withUrl(URI)} method enables method chaining for setting the {@code url}. */ @JsonInclude(JsonInclude.Include.NON_EMPTY) -public final class PutTarget extends Target { +@tools.jackson.databind.annotation.JsonDeserialize(builder = PutTarget.Builder.class) +@lombok.extern.jackson.Jacksonized +@lombok.Builder(toBuilder = true) +@lombok.Getter +@lombok.ToString +public final class PutTarget extends Target { @JsonProperty("url") + @lombok.NonNull private URI url; - public PutTarget() { - super(); - setKind(Kind.PUT); - } - - /** - * Retrieves the {@code URI} to which the converted document should be delivered. - * - *

    The {@code url} represents the target location for sending the document using an HTTP PUT - * operation. It may be {@code null} if no URI has been specified. - * - * @return the {@link URI} representing the document's delivery destination, or {@code null} - * if no target URI is set. - */ - @Nullable - public URI getUrl() { - return url; - } - - /** - * Sets the {@code URI} to which the converted document should be delivered. - * - *

    The {@code url} specifies the target location for sending the document using an HTTP PUT operation. - * - * @param url the {@link URI} representing the delivery location, or {@code null} to explicitly unset the target URI. - */ - public void setUrl(URI url) { - this.url = ensureNotNull(url, "url"); - } - - /** - * Sets the {@code URL} to which the converted document should be delivered and returns the current - * {@link PutTarget} instance for method chaining. - * - *

    This method allows convenient chaining by setting the {@code url} and returning the same instance. - * - * @param url the {@link URI} representing the delivery location, or {@code null} to explicitly unset the target URI. - * @return the current {@link PutTarget} instance with the updated {@code url}. - */ - public PutTarget withUrl(URI url) { - setUrl(url); - return this; - } - - @Override - public String toString() { - return "PutTarget{" + - "kind='" + getKind() + "'" + - ", url=" + (url == null ? "null" : url.toString()) + - '}'; - } - - @Override - public void setKind(@Nullable Kind kind) { - super.setKind(Kind.PUT); - } + @tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "") + public static class Builder { } } diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/target/Target.java b/docling-api/src/main/java/ai/docling/api/convert/request/target/Target.java index 4bf7f80..b32eaae 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/target/Target.java +++ b/docling-api/src/main/java/ai/docling/api/convert/request/target/Target.java @@ -1,8 +1,10 @@ package ai.docling.api.convert.request.target; -import org.jspecify.annotations.Nullable; - +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; /** * Represents an abstract target for defining where and how the converted document should be delivered. @@ -11,49 +13,24 @@ * {@link InBodyTarget}, {@link PutTarget}, and {@link ZipTarget}. These implementations specify different * delivery methods, such as including the document in the response body, sending it to a specified URI, or * zipping it for inclusion in the response. - * - * @param The specific subtype of {@link Target}, enabling type-safe chaining. */ -public sealed abstract class Target permits InBodyTarget, PutTarget, ZipTarget { +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "kind" +) +@JsonSubTypes({ + @Type(value = InBodyTarget.class, name = "inbody"), + @Type(value = PutTarget.class, name = "put"), + @Type(value = ZipTarget.class, name = "zip") +}) +@lombok.Getter +@lombok.ToString +public sealed abstract class Target permits InBodyTarget, PutTarget, ZipTarget { enum Kind { @JsonProperty("inbody") INBODY, @JsonProperty("put") PUT, @JsonProperty("zip") ZIP } - - @JsonProperty("kind") - private Kind kind = Kind.INBODY; - - /** - * Retrieves the {@code Kind} associated with this {@link Target} instance. - * - * @return the {@link Kind} that represents the method of document delivery, - * or {@code null} if no kind is set. - */ - @Nullable - public Kind getKind() { - return kind; - } - - /** - * Sets the {@link Kind} for this {@link Target}. The {@code Kind} specifies the delivery method for - * the converted document, such as including it in the response body, sending it to a URI, or zipping it. - * - * @param kind the {@link Kind} representing the delivery method, or {@code null} to unset the kind. - */ - public void setKind(@Nullable Kind kind) { - this.kind = kind; - } - - /** - * Sets the {@link Kind} for this {@link Target} instance and returns the current instance for method chaining. - * - * @param kind the {@link Kind} representing the delivery method (e.g., {@code INBODY}, {@code PUT}, {@code ZIP}), - * or {@code null} to unset the kind. - * @return the current instance of {@code T}, enabling method chaining. - */ - public T withKind(@Nullable Kind kind) { - setKind(kind); - return (T) this; - } } diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/target/ZipTarget.java b/docling-api/src/main/java/ai/docling/api/convert/request/target/ZipTarget.java index 1061275..d6f1c2c 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/target/ZipTarget.java +++ b/docling-api/src/main/java/ai/docling/api/convert/request/target/ZipTarget.java @@ -1,7 +1,5 @@ package ai.docling.api.convert.request.target; -import org.jspecify.annotations.Nullable; - import com.fasterxml.jackson.annotation.JsonInclude; /** @@ -12,7 +10,6 @@ * *

    This class overrides methods to specialize the behavior for the ZIP delivery kind: *

      - *
    • {@link #withKind(Kind)}: Returns a {@code ZipTarget} instance with the specified delivery kind.
    • *
    • {@link #toString()}: Produces a string representation of the {@code ZipTarget} instance.
    • *
    * @@ -22,21 +19,13 @@ *

    The {@link ZipTarget} instances are immutable and final. */ @JsonInclude(JsonInclude.Include.NON_EMPTY) -public final class ZipTarget extends Target { - public ZipTarget() { - super(); - setKind(Kind.ZIP); - } - - @Override - public String toString() { - return "ZipTarget{" + - "kind='" + getKind() + "'" + - '}'; - } +@tools.jackson.databind.annotation.JsonDeserialize(builder = ZipTarget.Builder.class) +@lombok.extern.jackson.Jacksonized +@lombok.Builder(toBuilder = true) +@lombok.Getter +@lombok.ToString +public final class ZipTarget extends Target { - @Override - public void setKind(@Nullable Kind kind) { - super.setKind(Kind.ZIP); - } + @tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "") + public static class Builder { } } diff --git a/docling-api/src/main/java/ai/docling/api/convert/response/ConvertDocumentResponse.java b/docling-api/src/main/java/ai/docling/api/convert/response/ConvertDocumentResponse.java index ff5df06..63e0e44 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/response/ConvertDocumentResponse.java +++ b/docling-api/src/main/java/ai/docling/api/convert/response/ConvertDocumentResponse.java @@ -1,8 +1,5 @@ package ai.docling.api.convert.response; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -10,6 +7,8 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; /** * Response returned by the Convert API for a single conversion request. @@ -18,178 +17,34 @@ * collections/strings are omitted from JSON output.

    */ @JsonInclude(JsonInclude.Include.NON_EMPTY) +@tools.jackson.databind.annotation.JsonDeserialize(builder = ConvertDocumentResponse.Builder.class) +@lombok.extern.jackson.Jacksonized +@lombok.Builder(toBuilder = true) +@lombok.Getter +@lombok.ToString public class ConvertDocumentResponse { - @JsonProperty("document") + @Nullable private DocumentResponse document; @JsonProperty("errors") - private List errors = new ArrayList<>(); + @JsonSetter(nulls = Nulls.AS_EMPTY) + @lombok.Singular + private List errors; @JsonProperty("processing_time") + @Nullable private Double processingTime; @JsonProperty("status") + @Nullable private String status; @JsonProperty("timings") - private Map timings = new HashMap<>(); - - // Getters/setters - /** - * The converted document payload. - * @return document or {@code null} - */ - @Nullable - public DocumentResponse getDocument() { - return document; - } - - /** - * Sets the converted document payload. - * @param document document; may be {@code null} - */ - public void setDocument(@Nullable DocumentResponse document) { - this.document = document; - } - - /** - * Fluent setter for {@link #setDocument(DocumentResponse)}. - * @param document document; may be {@code null} - * @return this instance for chaining - */ - public ConvertDocumentResponse withDocument(@Nullable DocumentResponse document) { - setDocument(document); - return this; - } - - /** - * List of errors, if any. When non-null, returned as an unmodifiable view. - * @return errors list or {@code null} - */ - public List getErrors() { - return Collections.unmodifiableList(errors); - } - - /** - * Sets the errors list; defensively copies when non-null. - * @param errors list of errors; may be {@code null} - */ - public void setErrors(@Nullable List errors) { - this.errors.clear(); - - if (errors != null) { - this.errors.addAll(errors); - } - } - - /** - * Fluent setter for {@link #setErrors(List)}. - * @param errors list of errors; may be {@code null} - * @return this instance for chaining - */ - public ConvertDocumentResponse withErrors(@Nullable List errors) { - setErrors(errors); - return this; - } - - /** - * Total processing time in seconds. - * @return processing time or {@code null} - */ - @Nullable - public Double getProcessingTime() { - return processingTime; - } - - /** - * Sets the processing time in seconds. - * @param processingTime processing time; may be {@code null} - */ - public void setProcessingTime(@Nullable Double processingTime) { - this.processingTime = processingTime; - } - - /** - * Fluent setter for {@link #setProcessingTime(Double)}. - * @param processingTime processing time; may be {@code null} - * @return this instance for chaining - */ - public ConvertDocumentResponse withProcessingTime(@Nullable Double processingTime) { - setProcessingTime(processingTime); - return this; - } - - /** - * Status string of the conversion. - * @return status or {@code null} - */ - @Nullable - public String getStatus() { - return status; - } - - /** - * Sets the status string of the conversion. - * @param status status; may be {@code null} - */ - public void setStatus(@Nullable String status) { - this.status = status; - } - - /** - * Fluent setter for {@link #setStatus(String)}. - * @param status status; may be {@code null} - * @return this instance for chaining - */ - public ConvertDocumentResponse withStatus(@Nullable String status) { - setStatus(status); - return this; - } - - /** - * Timings map with detailed timing information. When non-null, returned as an - * unmodifiable view. - * @return timings or {@code null} - */ - @Nullable - public Map getTimings() { - return Collections.unmodifiableMap(timings); - } - - /** - * Sets the timings map; defensively copies when non-null. - * @param timings timings; may be {@code null} - */ - public void setTimings(@Nullable Map timings) { - this.timings.clear(); - - if (timings != null) { - this.timings.putAll(timings); - } - } - - /** - * Fluent setter for {@link #setTimings(Map)}. - * @param timings timings; may be {@code null} - * @return this instance for chaining - */ - public ConvertDocumentResponse withTimings(@Nullable Map timings) { - setTimings(timings); - return this; - } + @JsonSetter(nulls = Nulls.AS_EMPTY) + @lombok.Singular + private Map timings; - /** - * Returns a string representation with actual values. - */ - @Override - public String toString() { - return "ConvertDocumentResponse{" + - "document=" + (document == null ? "null" : document.toString()) + - ", errors=" + (errors == null ? "null" : errors.toString()) + - ", processingTime=" + (processingTime == null ? "null" : processingTime.toString()) + - ", status=" + (status == null ? "null" : "'" + status + "'") + - ", timings=" + (timings == null ? "null" : timings.toString()) + - '}'; - } + @tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "") + public static class Builder { } } diff --git a/docling-api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java b/docling-api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java index ff25fc7..02e8994 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java +++ b/docling-api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java @@ -1,13 +1,13 @@ package ai.docling.api.convert.response; -import java.util.Collections; -import java.util.HashMap; import java.util.Map; import org.jspecify.annotations.Nullable; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; /** * A simple mutable POJO representing the converted document returned by the @@ -28,231 +28,37 @@ *
*/ @JsonInclude(JsonInclude.Include.NON_EMPTY) +@tools.jackson.databind.annotation.JsonDeserialize(builder = DocumentResponse.Builder.class) +@lombok.extern.jackson.Jacksonized +@lombok.Builder(toBuilder = true) +@lombok.Getter +@lombok.ToString public class DocumentResponse { - @JsonProperty("doctags_content") + @Nullable private String doctagsContent; @JsonProperty("filename") + @Nullable private String filename; @JsonProperty("html_content") + @Nullable private String htmlContent; @JsonProperty("json_content") - private Map jsonContent = new HashMap<>(); + @JsonSetter(nulls = Nulls.AS_EMPTY) + @lombok.Singular("jsonContent") + private Map jsonContent; @JsonProperty("md_content") + @Nullable private String markdownContent; @JsonProperty("text_content") - private String textContent; - - // Getters and setters - /** - * Returns the DocTags representation of the document, if available. - * - * @return doctags content, or {@code null} if not present - */ - @Nullable - public String getDoctagsContent() { - return doctagsContent; - } - - /** - * Sets the DocTags representation of the document. - * - * @param doctagsContent the DocTags content; may be {@code null} - */ - public void setDoctagsContent(@Nullable String doctagsContent) { - this.doctagsContent = doctagsContent; - } - - /** - * Returns the original filename of the processed document. - * - * @return filename, or {@code null} if unknown - */ - @Nullable - public String getFilename() { - return filename; - } - - /** - * Sets the original filename of the processed document. - * - * @param filename the filename; may be {@code null} - */ - public void setFilename(@Nullable String filename) { - this.filename = filename; - } - - /** - * Returns the HTML content produced by the conversion. - * - * @return HTML content, or {@code null} if not produced - */ - @Nullable - public String getHtmlContent() { - return htmlContent; - } - - /** - * Sets the HTML content produced by the conversion. - * - * @param htmlContent the HTML content; may be {@code null} - */ - public void setHtmlContent(@Nullable String htmlContent) { - this.htmlContent = htmlContent; - } - - /** - * Returns an unmodifiable view of the JSON content map. Never {@code null}. - * When set via {@link #setJsonContent(Map)}, the provided map reference is - * preserved (if non-null), so external changes to that map will be reflected - * here. - * - * @return unmodifiable view of the JSON content map (possibly empty) - */ - public Map getJsonContent() { - return Collections.unmodifiableMap(jsonContent); - } - - /** - * Sets the JSON content map. If {@code jsonContent} is {@code null}, an empty - * map is assigned. Otherwise, the provided map reference is preserved so that - * subsequent modifications to the same map are visible via - * {@link #getJsonContent()}. - * - * @param jsonContent the JSON content map; may be {@code null} - */ - public void setJsonContent(@Nullable Map jsonContent) { - this.jsonContent.clear(); - - if (jsonContent != null) { - this.jsonContent.putAll(jsonContent); - } - } - - /** - * Returns the Markdown content produced by the conversion. - * - * @return Markdown content, or {@code null} if not produced - */ @Nullable - public String getMarkdownContent() { - return markdownContent; - } - - /** - * Sets the Markdown content produced by the conversion. - * - * @param markdownContent the Markdown content; may be {@code null} - */ - public void setMarkdownContent(@Nullable String markdownContent) { - this.markdownContent = markdownContent; - } - - /** - * Returns the plain text content produced by the conversion. - * - * @return plain text content, or {@code null} if not produced - */ - @Nullable - public String getTextContent() { - return textContent; - } - - /** - * Sets the plain text content produced by the conversion. - * - * @param textContent the text content; may be {@code null} - */ - public void setTextContent(@Nullable String textContent) { - this.textContent = textContent; - } - - // Fluent builder-style methods - /** - * Fluent setter for {@link #setDoctagsContent(String)}. - * - * @param doctagsContent the DocTags content; may be {@code null} - * @return this instance for chaining - */ - public DocumentResponse withDoctagsContent(@Nullable String doctagsContent) { - setDoctagsContent(doctagsContent); - return this; - } - - /** - * Fluent setter for {@link #setFilename(String)}. - * - * @param filename the filename; may be {@code null} - * @return this instance for chaining - */ - public DocumentResponse withFilename(@Nullable String filename) { - setFilename(filename); - return this; - } - - /** - * Fluent setter for {@link #setHtmlContent(String)}. - * - * @param htmlContent the HTML content; may be {@code null} - * @return this instance for chaining - */ - public DocumentResponse withHtmlContent(@Nullable String htmlContent) { - setHtmlContent(htmlContent); - return this; - } - - /** - * Fluent setter for {@link #setJsonContent(Map)}. - * - * @param jsonContent the JSON content map; may be {@code null} - * @return this instance for chaining - */ - public DocumentResponse withJsonContent(@Nullable Map jsonContent) { - setJsonContent(jsonContent); - return this; - } - - /** - * Fluent setter for {@link #setMarkdownContent(String)}. - * - * @param markdownContent the Markdown content; may be {@code null} - * @return this instance for chaining - */ - public DocumentResponse withMarkdownContent(@Nullable String markdownContent) { - setMarkdownContent(markdownContent); - return this; - } - - /** - * Fluent setter for {@link #setTextContent(String)}. - * - * @param textContent the text content; may be {@code null} - * @return this instance for chaining - */ - public DocumentResponse withTextContent(@Nullable String textContent) { - setTextContent(textContent); - return this; - } + private String textContent; - /** - * Returns a string representation containing the actual values of all fields. - * - * @return string representation of this response - */ - @Override - public String toString() { - return "DocumentResponse{" + - "doctagsContent=" + (doctagsContent == null ? "null" : "'" + doctagsContent + "'") + - ", filename=" + (filename == null ? "null" : "'" + filename + "'") + - ", htmlContent=" + (htmlContent == null ? "null" : "'" + htmlContent + "'") + - ", jsonContent=" + (jsonContent == null ? "null" : jsonContent.toString()) + - ", markdownContent=" + (markdownContent == null ? "null" : "'" + markdownContent + "'") + - ", textContent=" + (textContent == null ? "null" : "'" + textContent + "'") + - '}'; - } + @tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "") + public static class Builder { } } diff --git a/docling-api/src/main/java/ai/docling/api/convert/response/ErrorItem.java b/docling-api/src/main/java/ai/docling/api/convert/response/ErrorItem.java index 363496f..36c4fee 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/response/ErrorItem.java +++ b/docling-api/src/main/java/ai/docling/api/convert/response/ErrorItem.java @@ -12,116 +12,24 @@ * strings are omitted from JSON output.

*/ @JsonInclude(JsonInclude.Include.NON_EMPTY) +@tools.jackson.databind.annotation.JsonDeserialize(builder = ErrorItem.Builder.class) +@lombok.extern.jackson.Jacksonized +@lombok.Builder(toBuilder = true) +@lombok.Getter +@lombok.ToString public class ErrorItem { - @JsonProperty("component_type") + @Nullable private String componentType; @JsonProperty("error_message") + @Nullable private String errorMessage; @JsonProperty("module_name") - private String moduleName; - - /** - * Returns the component type that produced the error. - * - * @return component type or {@code null} - */ - @Nullable - public String getComponentType() { - return componentType; - } - - /** - * Sets the component type that produced the error. - * - * @param componentType component type; may be {@code null} - */ - public void setComponentType(@Nullable String componentType) { - this.componentType = componentType; - } - - /** - * Fluent setter for {@link #setComponentType(String)}. - * - * @param componentType component type; may be {@code null} - * @return this for chaining - */ - public ErrorItem withComponentType(@Nullable String componentType) { - setComponentType(componentType); - return this; - } - - /** - * Returns the human-readable error message. - * - * @return error message or {@code null} - */ - @Nullable - public String getErrorMessage() { - return errorMessage; - } - - /** - * Sets the human-readable error message. - * - * @param errorMessage error message; may be {@code null} - */ - public void setErrorMessage(@Nullable String errorMessage) { - this.errorMessage = errorMessage; - } - - /** - * Fluent setter for {@link #setErrorMessage(String)}. - * - * @param errorMessage error message; may be {@code null} - * @return this for chaining - */ - public ErrorItem withErrorMessage(@Nullable String errorMessage) { - setErrorMessage(errorMessage); - return this; - } - - /** - * Returns the module name where the error originated. - * - * @return module name or {@code null} - */ @Nullable - public String getModuleName() { - return moduleName; - } - - /** - * Sets the module name where the error originated. - * - * @param moduleName module name; may be {@code null} - */ - public void setModuleName(@Nullable String moduleName) { - this.moduleName = moduleName; - } - - /** - * Fluent setter for {@link #setModuleName(String)}. - * - * @param moduleName module name; may be {@code null} - * @return this for chaining - */ - public ErrorItem withModuleName(@Nullable String moduleName) { - setModuleName(moduleName); - return this; - } + private String moduleName; - /** - * Returns a string representation with actual field values. - */ - @Override - public String toString() { - return "ErrorItem{" + - "componentType=" + (componentType == null ? "null" : "'" + componentType + "'") + - ", errorMessage=" + (errorMessage == null ? "null" : "'" + errorMessage + "'") + - ", moduleName=" + (moduleName == null ? "null" : "'" + moduleName + "'") + - '}'; - } + @tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "") + public static class Builder { } } diff --git a/docling-api/src/main/java/ai/docling/api/health/HealthCheckResponse.java b/docling-api/src/main/java/ai/docling/api/health/HealthCheckResponse.java index 5eef555..198b328 100644 --- a/docling-api/src/main/java/ai/docling/api/health/HealthCheckResponse.java +++ b/docling-api/src/main/java/ai/docling/api/health/HealthCheckResponse.java @@ -12,42 +12,16 @@ * are omitted from JSON.

*/ @JsonInclude(JsonInclude.Include.NON_EMPTY) +@tools.jackson.databind.annotation.JsonDeserialize(builder = HealthCheckResponse.Builder.class) +@lombok.extern.jackson.Jacksonized +@lombok.Builder(toBuilder = true) +@lombok.Getter +@lombok.ToString public class HealthCheckResponse { - @JsonProperty("status") - private String status; - - /** - * Returns the health status string. - * @return status or {@code null} - */ @Nullable - public String getStatus() { - return status; - } - - /** - * Sets the health status string. - * @param status may be {@code null} - */ - public void setStatus(@Nullable String status) { - this.status = status; - } - - /** - * Fluent setter for {@link #setStatus(String)}. - * @param status may be {@code null} - * @return this instance for chaining - */ - public HealthCheckResponse withStatus(@Nullable String status) { - setStatus(status); - return this; - } + private String status; - @Override - public String toString() { - return "HealthCheckResponse{" + - "status=" + (status == null ? "null" : "'" + status + "'") + - '}'; - } + @tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "") + public static class Builder { } } diff --git a/docling-api/src/test/java/ai/docling/api/convert/request/ConvertDocumentRequestTests.java b/docling-api/src/test/java/ai/docling/api/convert/request/ConvertDocumentRequestTests.java index 6e6d4ef..d31d987 100644 --- a/docling-api/src/test/java/ai/docling/api/convert/request/ConvertDocumentRequestTests.java +++ b/docling-api/src/test/java/ai/docling/api/convert/request/ConvertDocumentRequestTests.java @@ -23,26 +23,28 @@ class ConvertDocumentRequestTests { @Test void whenOptionsIsNullThenThrow() { var uri = URI.create("http://example.com"); - var httpSource = new HttpSource().withUrl(uri); + var httpSource = HttpSource.builder().url(uri).build(); - assertThatThrownBy(() -> new ConvertDocumentRequest().withSources(List.of(httpSource)).withOptions(null)) + assertThatThrownBy(() -> ConvertDocumentRequest.builder().options(null).source(httpSource).build()) .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("options cannot be null"); + .hasMessageContaining("options is marked non-null but is null"); } @Test void buildWithHttpSourcesAsList() { - ConvertDocumentRequest request = new ConvertDocumentRequest() - .withSources(List.of(new HttpSource().withUrl(URI.create("http://example.com")))) - .withTarget(new InBodyTarget()); + ConvertDocumentRequest request = ConvertDocumentRequest.builder() + .source(HttpSource.builder().url(URI.create("http://example.com")).build()) + .target(InBodyTarget.builder().build()) + .build(); assertThat(request.getSources()).hasSize(1); assertThat(request.getSources().get(0)).isInstanceOf(HttpSource.class); } @Test void buildWithHttpSourcesAsVarargs() { - ConvertDocumentRequest request = new ConvertDocumentRequest() - .withSources(List.of(new HttpSource().withUrl(URI.create("http://example.com")), new HttpSource().withUrl(URI.create("http://example.org")))); + ConvertDocumentRequest request = ConvertDocumentRequest.builder() + .sources(List.of(HttpSource.builder().url(URI.create("http://example.com")).build(), HttpSource.builder().url(URI.create("http://example.org")).build())) + .build(); assertThat(request.getSources()) .hasSize(2) @@ -51,8 +53,9 @@ void buildWithHttpSourcesAsVarargs() { @Test void buildWithFileSourcesAsList() { - ConvertDocumentRequest request = new ConvertDocumentRequest() - .withSources(List.of(new FileSource().withFilename("file:///path/to/file.txt").withBase64String("content"))); + ConvertDocumentRequest request = ConvertDocumentRequest.builder() + .sources(List.of(FileSource.builder().filename("file:///path/to/file.txt").base64String("content").build())) + .build(); assertThat(request.getSources()) .hasSize(1) @@ -61,44 +64,35 @@ void buildWithFileSourcesAsList() { @Test void buildWithFileSourcesAsVarargs() { - ConvertDocumentRequest request = new ConvertDocumentRequest() - .withSources(List.of( - new FileSource().withFilename("file1.txt").withBase64String("base64string1"), - new FileSource().withFilename("file2.txt").withBase64String("base64string2") + ConvertDocumentRequest request = ConvertDocumentRequest.builder() + .sources(List.of( + FileSource.builder().filename("file1.txt").base64String("base64string1").build(), + FileSource.builder().filename("file2.txt").base64String("base64string2").build() ) ) - .withTarget(new ZipTarget()); + .target(ZipTarget.builder().build()) + .build(); assertThat(request.getSources()) .hasSize(2) .allSatisfy(source -> assertThat(source).isInstanceOf(FileSource.class)); } - @Test - void whenMixedSourcesThenThrow() { - var sources = List.of( - new HttpSource().withUrl(URI.create("http://example.com")), - new FileSource().withFilename("file.txt").withBase64String("base64string") - ); - assertThatThrownBy(() -> new ConvertDocumentRequest().withSources(sources)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("All sources must be of the same type (HttpSource or FileSource)"); - } - @Test void convertDocumentRequestIsImmutable() { - List sources = new ArrayList<>(List.of(new FileSource().withFilename("test.txt").withBase64String("dGVzdCBjb250ZW50"))); + List sources = new ArrayList<>(List.of(FileSource.builder().filename("test.txt").base64String("dGVzdCBjb250ZW50").build())); - ConvertDocumentRequest request = new ConvertDocumentRequest() - .withSources(sources) - .withOptions(new ConvertDocumentOptions().withDoOcr(true)) - .withTarget(new InBodyTarget()); + ConvertDocumentRequest request = ConvertDocumentRequest.builder() + .sources(sources) + .options(ConvertDocumentOptions.builder().doOcr(true).build()) + .target(InBodyTarget.builder().build()) + .build(); assertThat(request.getSources()) .usingRecursiveFieldByFieldElementComparator() .containsExactlyInAnyOrderElementsOf(sources); - sources.add(new FileSource().withFilename("changed.txt").withBase64String("Y2hhbmdlZA==")); + sources.add(FileSource.builder().filename("changed.txt").base64String("Y2hhbmdlZA==").build()); assertThat(request.getSources()) .singleElement() diff --git a/docling-api/src/test/java/ai/docling/api/convert/request/options/ConvertDocumentOptionsTests.java b/docling-api/src/test/java/ai/docling/api/convert/request/options/ConvertDocumentOptionsTests.java index 3ab61eb..6779263 100644 --- a/docling-api/src/test/java/ai/docling/api/convert/request/options/ConvertDocumentOptionsTests.java +++ b/docling-api/src/test/java/ai/docling/api/convert/request/options/ConvertDocumentOptionsTests.java @@ -17,10 +17,11 @@ void convertDocumentOptionsIsImmutable() { List toFormats = new ArrayList<>(List.of(OutputFormat.MARKDOWN)); List ocrLang = new ArrayList<>(List.of("en", "de")); - ConvertDocumentOptions options = new ConvertDocumentOptions() - .withFromFormats(fromFormats) - .withToFormats(toFormats) - .withOcrLang(ocrLang); + ConvertDocumentOptions options = ConvertDocumentOptions.builder() + .fromFormat(InputFormat.PDF) + .toFormat(OutputFormat.MARKDOWN) + .ocrLang(ocrLang) + .build(); assertThat(options.getFromFormats()).isEqualTo(fromFormats); assertThat(options.getToFormats()).isEqualTo(toFormats); diff --git a/docling-api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionApiTests.java b/docling-api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionApiTests.java index aed4677..d87e761 100644 --- a/docling-api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionApiTests.java +++ b/docling-api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionApiTests.java @@ -24,13 +24,14 @@ void createApiWithAllFields() { String prompt = "Describe this image in detail"; Integer concurrency = 2; - PictureDescriptionApi api = new PictureDescriptionApi() - .withUrl(url) - .withHeaders(headers) - .withParams(params) - .withTimeout(timeout) - .withConcurrency(concurrency) - .withPrompt(prompt); + PictureDescriptionApi api = PictureDescriptionApi.builder() + .url(url) + .headers(headers) + .params(params) + .timeout(timeout) + .concurrency(concurrency) + .prompt(prompt) + .build(); assertThat(api.getUrl()).isEqualTo(url); assertThat(api.getHeaders()).isEqualTo(headers); @@ -44,8 +45,9 @@ void createApiWithAllFields() { void createApiWithOnlyRequiredFields() { URI url = URI.create("https://api.example.com/vision"); - PictureDescriptionApi api = new PictureDescriptionApi() - .withUrl(url); + PictureDescriptionApi api = PictureDescriptionApi.builder() + .url(url) + .build(); assertThat(api.getUrl()).isEqualTo(url); assertThat(api.getHeaders()).isEmpty(); @@ -56,9 +58,9 @@ void createApiWithOnlyRequiredFields() { @Test void createApiWithNullUrlThrowsException() { - assertThatThrownBy(() -> new PictureDescriptionApi().withUrl(null)) + assertThatThrownBy(() -> PictureDescriptionApi.builder().build()) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("url cannot be null"); + .hasMessage("url is marked non-null but is null"); } @Test @@ -67,13 +69,14 @@ void pictureDescriptionApiIsImmutable() { Map headers = new HashMap<>(Map.of("Authorization", "Bearer original")); Map params = new HashMap<>(Map.of("model", "original-model")); - PictureDescriptionApi api = new PictureDescriptionApi() - .withUrl(url) - .withHeaders(headers) - .withParams(params) - .withTimeout(Duration.ofSeconds(10)) - .withConcurrency(3) - .withPrompt("Original prompt"); + PictureDescriptionApi api = PictureDescriptionApi.builder() + .url(url) + .headers(headers) + .params(params) + .timeout(Duration.ofSeconds(10)) + .concurrency(3) + .prompt("Original prompt") + .build(); assertThat(api.getHeaders()).isEqualTo(headers); assertThat(api.getParams()).isEqualTo(params); diff --git a/docling-api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionLocalTests.java b/docling-api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionLocalTests.java index 84a5b98..0010382 100644 --- a/docling-api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionLocalTests.java +++ b/docling-api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionLocalTests.java @@ -22,10 +22,11 @@ void createLocalWithAllFields() { String prompt = "Describe this image in detail"; Map generationConfig = Map.of("max_length", 100, "temperature", 0.7); - PictureDescriptionLocal local = new PictureDescriptionLocal() - .withRepoId(repoId) - .withPrompt(prompt) - .withGenerationConfig(generationConfig); + PictureDescriptionLocal local = PictureDescriptionLocal.builder() + .repoId(repoId) + .prompt(prompt) + .generationConfig(generationConfig) + .build(); assertThat(local.getRepoId()).isEqualTo(repoId); assertThat(local.getPrompt()).isEqualTo(prompt); @@ -36,8 +37,9 @@ void createLocalWithAllFields() { void createLocalWithOnlyRequiredFields() { String repoId = "microsoft/Florence-2-large"; - PictureDescriptionLocal local = new PictureDescriptionLocal() - .withRepoId(repoId); + PictureDescriptionLocal local = PictureDescriptionLocal.builder() + .repoId(repoId) + .build(); assertThat(local.getRepoId()).isEqualTo(repoId); assertThat(local.getPrompt()).isNull(); @@ -48,9 +50,9 @@ void createLocalWithOnlyRequiredFields() { @NullAndEmptySource @ValueSource(strings = " ") void createLocalWithNullRepoIdThrowsException(String repoId) { - assertThatThrownBy(() -> new PictureDescriptionLocal().withRepoId(null)) + assertThatThrownBy(() -> PictureDescriptionLocal.builder().build()) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("repoId cannot be null or empty"); + .hasMessage("repoId is marked non-null but is null"); } @Test @@ -58,10 +60,11 @@ void pictureDescriptionLocalIsImmutable() { String repoId = "microsoft/Florence-2-large"; Map generationConfig = new HashMap<>(Map.of("max_length", 100)); - PictureDescriptionLocal local = new PictureDescriptionLocal() - .withRepoId(repoId) - .withPrompt("Original prompt") - .withGenerationConfig(generationConfig); + PictureDescriptionLocal local = PictureDescriptionLocal.builder() + .repoId(repoId) + .prompt("Original prompt") + .generationConfig(generationConfig) + .build(); assertThat(local.getGenerationConfig()).isEqualTo(generationConfig); diff --git a/docling-api/src/test/java/ai/docling/api/convert/request/source/FileSourceTests.java b/docling-api/src/test/java/ai/docling/api/convert/request/source/FileSourceTests.java index b92c5de..b560cba 100644 --- a/docling-api/src/test/java/ai/docling/api/convert/request/source/FileSourceTests.java +++ b/docling-api/src/test/java/ai/docling/api/convert/request/source/FileSourceTests.java @@ -12,44 +12,16 @@ class FileSourceTests { @Test void whenBase64StringIsNullThenThrow() { - assertThatThrownBy(() -> new FileSource().withFilename("test.txt").withBase64String(null)) + assertThatThrownBy(() -> FileSource.builder().filename("test.txt").build()) .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("base64String cannot be null or blank"); - } - - @Test - void whenBase64StringIsEmptyThenThrow() { - assertThatThrownBy(() -> new FileSource().withFilename("test.txt").withBase64String("")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("base64String cannot be null or blank"); - } - - @Test - void whenBase64StringIsBlankThenThrow() { - assertThatThrownBy(() -> new FileSource().withFilename("test.txt").withBase64String(" ")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("base64String cannot be null or blank"); + .hasMessageContaining("base64String is marked non-null but is nul"); } @Test void whenFilenameIsNullThenThrow() { - assertThatThrownBy(() -> new FileSource().withFilename(null).withBase64String("dGVzdCBjb250ZW50")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("filename cannot be null or blank"); - } - - @Test - void whenFilenameIsEmptyThenThrow() { - assertThatThrownBy(() -> new FileSource().withFilename("").withBase64String("dGVzdCBjb250ZW50")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("filename cannot be null or blank"); - } - - @Test - void whenFilenameIsBlankThenThrow() { - assertThatThrownBy(() -> new FileSource().withFilename(" ").withBase64String("dGVzdCBjb250ZW50")) + assertThatThrownBy(() -> FileSource.builder().base64String("dGVzdCBjb250ZW50").build()) .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("filename cannot be null or blank"); + .hasMessageContaining("filename is marked non-null but is nul"); } @Test @@ -57,28 +29,8 @@ void whenValidParametersThenCreateFileSource() { String base64String = "dGVzdCBjb250ZW50"; String filename = "test.txt"; - FileSource fileSource = new FileSource().withBase64String(base64String).withFilename(filename); - - assertThat(fileSource.getKind()).isEqualTo(Source.Kind.FILE); - assertThat(fileSource.getBase64String()).isEqualTo(base64String); - assertThat(fileSource.getFilename()).isEqualTo(filename); - } - - @Test - void kindIsAlwaysSetToFile() { - FileSource fileSource = new FileSource().withKind(Source.Kind.HTTP).withBase64String("dGVzdCBjb250ZW50").withFilename("test.txt"); - - assertThat(fileSource.getKind()).isEqualTo(Source.Kind.FILE); - } - - @Test - void fromStaticMethodCreatesFileSource() { - String filename = "document.pdf"; - String base64String = "dGVzdCBjb250ZW50"; - - FileSource fileSource = new FileSource().withFilename(filename).withBase64String(base64String); + FileSource fileSource = FileSource.builder().base64String(base64String).filename(filename).build(); - assertThat(fileSource.getKind()).isEqualTo(Source.Kind.FILE); assertThat(fileSource.getBase64String()).isEqualTo(base64String); assertThat(fileSource.getFilename()).isEqualTo(filename); } @@ -88,12 +40,11 @@ void builderCreatesFileSource() { String filename = "presentation.pptx"; String base64String = "dGVzdCBjb250ZW50"; - FileSource fileSource = new FileSource() - .withFilename(filename) - .withBase64String(base64String) - ; + FileSource fileSource = FileSource.builder() + .filename(filename) + .base64String(base64String) + .build(); - assertThat(fileSource.getKind()).isEqualTo(Source.Kind.FILE); assertThat(fileSource.getBase64String()).isEqualTo(base64String); assertThat(fileSource.getFilename()).isEqualTo(filename); } diff --git a/docling-api/src/test/java/ai/docling/api/convert/request/source/HttpSourceTests.java b/docling-api/src/test/java/ai/docling/api/convert/request/source/HttpSourceTests.java index b049276..3921d1b 100644 --- a/docling-api/src/test/java/ai/docling/api/convert/request/source/HttpSourceTests.java +++ b/docling-api/src/test/java/ai/docling/api/convert/request/source/HttpSourceTests.java @@ -9,8 +9,6 @@ import org.junit.jupiter.api.Test; -import ai.docling.api.convert.request.source.Source.Kind; - /** * Unit tests for {@link HttpSource}. */ @@ -18,9 +16,9 @@ class HttpSourceTests { @Test void whenUrlIsNullThenThrow() { - assertThatThrownBy(() -> new HttpSource().withUrl(null)) + assertThatThrownBy(() -> HttpSource.builder().build()) .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("url cannot be null"); + .hasMessageContaining("url is marked non-null but is nul"); } @Test @@ -28,9 +26,8 @@ void whenValidParametersThenCreateHttpSource() { URI url = URI.create("https://example.com/document.pdf"); Map headers = Map.of("Authorization", "Bearer token123"); - HttpSource httpSource = new HttpSource().withUrl(url).withHeaders(headers); + HttpSource httpSource = HttpSource.builder().url(url).headers(headers).build(); - assertThat(httpSource.getKind()).isEqualTo(Source.Kind.HTTP); assertThat(httpSource.getUrl()).isEqualTo(url); assertThat(httpSource.getHeaders()).isEqualTo(headers); } @@ -39,28 +36,8 @@ void whenValidParametersThenCreateHttpSource() { void whenRequiredParametersThenCreateHttpSource() { URI url = URI.create("https://example.com/document.pdf"); - HttpSource httpSource = new HttpSource().withUrl(url); - - assertThat(httpSource.getKind()).isEqualTo(Source.Kind.HTTP); - assertThat(httpSource.getUrl()).isEqualTo(url); - assertThat(httpSource.getHeaders()).isNotNull().isEmpty(); - } - - @Test - void kindIsAlwaysSetToHttp() { - URI url = URI.create("https://example.com/document.pdf"); - HttpSource httpSource = new HttpSource().withUrl(url); - - assertThat(httpSource.getKind()).isEqualTo(Kind.HTTP); - } - - @Test - void fromStaticMethodWithUriCreatesHttpSource() { - URI url = URI.create("https://example.com/document.pdf"); - - HttpSource httpSource = new HttpSource().withUrl(url); + HttpSource httpSource = HttpSource.builder().url(url).build(); - assertThat(httpSource.getKind()).isEqualTo(Source.Kind.HTTP); assertThat(httpSource.getUrl()).isEqualTo(url); assertThat(httpSource.getHeaders()).isNotNull().isEmpty(); } @@ -70,12 +47,11 @@ void builderCreatesHttpSource() { URI url = URI.create("https://example.com/presentation.pptx"); Map headers = Map.of("User-Agent", "test-agent"); - HttpSource httpSource = new HttpSource() - .withUrl(url) - .withHeaders(headers) - ; + HttpSource httpSource = HttpSource.builder() + .url(url) + .headers(headers) + .build(); - assertThat(httpSource.getKind()).isEqualTo(Source.Kind.HTTP); assertThat(httpSource.getUrl()).isEqualTo(url); assertThat(httpSource.getHeaders()).isEqualTo(headers); } @@ -84,9 +60,10 @@ void builderCreatesHttpSource() { void httpSourceIsImmutable() { Map headers = new HashMap<>(Map.of("Authorization", "Bearer token123")); - HttpSource httpSource = new HttpSource() - .withUrl(URI.create("https://example.com/test.pdf")) - .withHeaders(headers); + HttpSource httpSource = HttpSource.builder() + .url(URI.create("https://example.com/test.pdf")) + .headers(headers) + .build(); assertThat(httpSource.getHeaders()).isEqualTo(headers); diff --git a/docling-api/src/test/java/ai/docling/api/convert/request/target/InBodyTargetTests.java b/docling-api/src/test/java/ai/docling/api/convert/request/target/InBodyTargetTests.java deleted file mode 100644 index 184ed6f..0000000 --- a/docling-api/src/test/java/ai/docling/api/convert/request/target/InBodyTargetTests.java +++ /dev/null @@ -1,28 +0,0 @@ -package ai.docling.api.convert.request.target; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; - -import ai.docling.api.convert.request.target.Target.Kind; - -/** - * Unit tests for {@link InBodyTarget}. - */ -class InBodyTargetTests { - - @Test - void whenValidParametersThenCreateInBodyTarget() { - InBodyTarget inBodyTarget = new InBodyTarget(); - - assertThat(inBodyTarget.getKind()).isEqualTo(Kind.INBODY); - } - - @Test - void kindIsAlwaysSetToInBody() { - InBodyTarget inBodyTarget = new InBodyTarget().withKind(Kind.PUT); - - assertThat(inBodyTarget.getKind()).isEqualTo(Kind.INBODY); - } - -} diff --git a/docling-api/src/test/java/ai/docling/api/convert/request/target/PutTargetTests.java b/docling-api/src/test/java/ai/docling/api/convert/request/target/PutTargetTests.java index 6193959..b26ff81 100644 --- a/docling-api/src/test/java/ai/docling/api/convert/request/target/PutTargetTests.java +++ b/docling-api/src/test/java/ai/docling/api/convert/request/target/PutTargetTests.java @@ -7,8 +7,6 @@ import org.junit.jupiter.api.Test; -import ai.docling.api.convert.request.target.Target.Kind; - /** * Unit tests for {@link PutTarget}. */ @@ -16,28 +14,17 @@ class PutTargetTests { @Test void whenUriIsNullThenThrow() { - assertThatThrownBy(() -> new PutTarget().withUrl(null)) + assertThatThrownBy(() -> PutTarget.builder().build()) .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("url cannot be null"); + .hasMessageContaining("url is marked non-null but is nul"); } @Test void whenValidParametersThenCreatePutTarget() { URI uri = URI.create("https://example.com/upload"); - PutTarget putTarget = new PutTarget().withUrl(uri); + PutTarget putTarget = PutTarget.builder().url(uri).build(); - assertThat(putTarget.getKind()).isEqualTo(Target.Kind.PUT); assertThat(putTarget.getUrl()).isEqualTo(uri); } - - @Test - void kindIsAlwaysSetToPut() { - URI uri = URI.create("https://example.com/upload"); - - PutTarget putTarget = new PutTarget().withKind(Kind.INBODY).withUrl(uri); - - assertThat(putTarget.getKind()).isEqualTo(Kind.PUT); - } - } diff --git a/docling-api/src/test/java/ai/docling/api/convert/request/target/ZipTargetTests.java b/docling-api/src/test/java/ai/docling/api/convert/request/target/ZipTargetTests.java deleted file mode 100644 index 51c2ecf..0000000 --- a/docling-api/src/test/java/ai/docling/api/convert/request/target/ZipTargetTests.java +++ /dev/null @@ -1,26 +0,0 @@ -package ai.docling.api.convert.request.target; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.Test; - -/** - * Unit tests for {@link ZipTarget}. - */ -class ZipTargetTests { - - @Test - void whenValidParametersThenCreateZipTarget() { - ZipTarget zipTarget = new ZipTarget(); - - assertThat(zipTarget.getKind()).isEqualTo(Target.Kind.ZIP); - } - - @Test - void kindIsAlwaysSetToZip() { - ZipTarget zipTarget = new ZipTarget().withKind(Target.Kind.PUT); - - assertThat(zipTarget.getKind()).isEqualTo(Target.Kind.ZIP); - } - -} diff --git a/docling-api/src/test/java/ai/docling/api/convert/response/ConvertDocumentResponseTests.java b/docling-api/src/test/java/ai/docling/api/convert/response/ConvertDocumentResponseTests.java index 1f9cc90..3954c23 100644 --- a/docling-api/src/test/java/ai/docling/api/convert/response/ConvertDocumentResponseTests.java +++ b/docling-api/src/test/java/ai/docling/api/convert/response/ConvertDocumentResponseTests.java @@ -16,17 +16,18 @@ class ConvertDocumentResponseTests { @Test void createResponseWithAllFields() { - DocumentResponse document = new DocumentResponse() - .withDoctagsContent("doctags content") - .withFilename("test-file.pdf") - .withHtmlContent("content") - .withJsonContent(Map.of("key", "value")) - .withMarkdownContent("# Markdown content") - .withTextContent("Plain text content"); + DocumentResponse document = DocumentResponse.builder() + .doctagsContent("doctags content") + .filename("test-file.pdf") + .htmlContent("content") + .jsonContent("key", "value") + .markdownContent("# Markdown content") + .textContent("Plain text content") + .build(); List errors = List.of( - new ErrorItem().withComponentType("parser").withErrorMessage("Parse error").withModuleName("pdf_module"), - new ErrorItem().withComponentType("converter").withErrorMessage("Conversion warning").withModuleName("html_module") + ErrorItem.builder().componentType("parser").errorMessage("Parse error").moduleName("pdf_module").build(), + ErrorItem.builder().componentType("converter").errorMessage("Conversion warning").moduleName("html_module").build() ); Double processingTime = 1.5; @@ -36,12 +37,13 @@ void createResponseWithAllFields() { "convert_time", 0.7 ); - ConvertDocumentResponse response = new ConvertDocumentResponse() - .withDocument(document) - .withErrors(errors) - .withProcessingTime(processingTime) - .withStatus(status) - .withTimings(timings); + ConvertDocumentResponse response = ConvertDocumentResponse.builder() + .document(document) + .errors(errors) + .processingTime(processingTime) + .status(status) + .timings(timings) + .build(); assertThat(response.getDocument()).isEqualTo(document); assertThat(response.getErrors()).containsExactlyInAnyOrderElementsOf(errors); @@ -52,7 +54,7 @@ void createResponseWithAllFields() { @Test void createResponseWithNullFields() { - ConvertDocumentResponse response = new ConvertDocumentResponse(); + ConvertDocumentResponse response = ConvertDocumentResponse.builder().build(); assertThat(response.getDocument()).isNull(); assertThat(response.getErrors()).isNotNull().isEmpty(); @@ -63,14 +65,16 @@ void createResponseWithNullFields() { @Test void createResponseWithEmptyCollections() { - DocumentResponse document = new DocumentResponse() - .withFilename("empty-file.txt") - .withTextContent(""); + DocumentResponse document = DocumentResponse.builder() + .filename("empty-file.txt") + .textContent("") + .build(); - ConvertDocumentResponse response = new ConvertDocumentResponse() - .withDocument(document) - .withProcessingTime(0.1) - .withStatus("completed"); + ConvertDocumentResponse response = ConvertDocumentResponse.builder() + .document(document) + .processingTime(0.1) + .status("completed") + .build(); assertThat(response.getDocument()).isEqualTo(document); assertThat(response.getErrors()).isEmpty(); @@ -82,19 +86,20 @@ void createResponseWithEmptyCollections() { @Test void convertDocumentResponseIsImmutable() { List errors = new ArrayList<>(List.of( - new ErrorItem().withComponentType("original").withErrorMessage("Original error").withModuleName("original_module") + ErrorItem.builder().componentType("original").errorMessage("Original error").moduleName("original_module").build() )); Map timings = new HashMap<>(Map.of("original_time", 1.0)); - ConvertDocumentResponse response = new ConvertDocumentResponse() - .withErrors(errors) - .withTimings(timings); + ConvertDocumentResponse response = ConvertDocumentResponse.builder() + .errors(errors) + .timings(timings) + .build(); assertThat(response.getErrors()).containsExactlyInAnyOrderElementsOf(errors); assertThat(response.getTimings()).containsExactlyInAnyOrderEntriesOf(timings); - errors.add(new ErrorItem().withComponentType("modified").withErrorMessage("Modified error").withModuleName("modified_module")); + errors.add(ErrorItem.builder().componentType("modified").errorMessage("Modified error").moduleName("modified_module").build()); timings.put("modified_time", 3.0); assertThat(response.getErrors()).hasSize(1); diff --git a/docling-api/src/test/java/ai/docling/api/convert/response/DocumentResponseTests.java b/docling-api/src/test/java/ai/docling/api/convert/response/DocumentResponseTests.java index 659b551..fc5f250 100644 --- a/docling-api/src/test/java/ai/docling/api/convert/response/DocumentResponseTests.java +++ b/docling-api/src/test/java/ai/docling/api/convert/response/DocumentResponseTests.java @@ -24,13 +24,14 @@ void createResponseWithAllFields() { String markdownContent = "# Test Document\n\nThis is a test document."; String textContent = "Test Document\n\nThis is a test document."; - DocumentResponse response = new DocumentResponse() - .withDoctagsContent(doctagsContent) - .withFilename(filename) - .withHtmlContent(htmlContent) - .withJsonContent(new HashMap<>(jsonContent)) - .withMarkdownContent(markdownContent) - .withTextContent(textContent); + DocumentResponse response = DocumentResponse.builder() + .doctagsContent(doctagsContent) + .filename(filename) + .htmlContent(htmlContent) + .jsonContent(new HashMap<>(jsonContent)) + .markdownContent(markdownContent) + .textContent(textContent) + .build(); assertThat(response.getDoctagsContent()).isEqualTo(doctagsContent); assertThat(response.getFilename()).isEqualTo(filename); @@ -42,7 +43,7 @@ void createResponseWithAllFields() { @Test void createResponseWithNullFields() { - DocumentResponse response = new DocumentResponse(); + DocumentResponse response = DocumentResponse.builder().build(); assertThat(response.getDoctagsContent()).isNull(); assertThat(response.getFilename()).isNull(); @@ -58,10 +59,11 @@ void createResponseWithEmptyFields() { String markdownContent = ""; String textContent = ""; - DocumentResponse response = new DocumentResponse() - .withFilename(filename) - .withMarkdownContent(markdownContent) - .withTextContent(textContent); + DocumentResponse response = DocumentResponse.builder() + .filename(filename) + .markdownContent(markdownContent) + .textContent(textContent) + .build(); assertThat(response.getDoctagsContent()).isNull(); assertThat(response.getFilename()).isEqualTo(filename); @@ -78,8 +80,9 @@ void documentResponseJsonContentReflectsOriginalMapChanges() { "count", 1 )); - DocumentResponse response = new DocumentResponse(); - response.setJsonContent(jsonContent); + DocumentResponse response = DocumentResponse.builder() + .jsonContent(jsonContent) + .build(); assertThat(response.getJsonContent()).containsExactlyInAnyOrderEntriesOf(jsonContent); @@ -87,12 +90,11 @@ void documentResponseJsonContentReflectsOriginalMapChanges() { assertThat(response.getJsonContent()).hasSize(2); - response.setJsonContent(jsonContent); + response = response.toBuilder().jsonContent(jsonContent).build(); assertThat(response.getJsonContent()).hasSize(3); assertThat(response.getJsonContent().get("original")).isEqualTo("value"); assertThat(response.getJsonContent().get("count")).isEqualTo(1); assertThat(response.getJsonContent()).containsKey("modified"); } - } diff --git a/docling-api/src/test/java/ai/docling/api/convert/response/ErrorItemTests.java b/docling-api/src/test/java/ai/docling/api/convert/response/ErrorItemTests.java index 7d37b19..7ff7d6e 100644 --- a/docling-api/src/test/java/ai/docling/api/convert/response/ErrorItemTests.java +++ b/docling-api/src/test/java/ai/docling/api/convert/response/ErrorItemTests.java @@ -15,10 +15,11 @@ void createErrorItemWithAllFields() { String errorMessage = "Failed to parse document structure"; String moduleName = "docling.core.parser"; - ErrorItem errorItem = new ErrorItem() - .withComponentType(componentType) - .withErrorMessage(errorMessage) - .withModuleName(moduleName); + ErrorItem errorItem = ErrorItem.builder() + .componentType(componentType) + .errorMessage(errorMessage) + .moduleName(moduleName) + .build(); assertThat(errorItem.getComponentType()).isEqualTo(componentType); assertThat(errorItem.getErrorMessage()).isEqualTo(errorMessage); @@ -27,10 +28,7 @@ void createErrorItemWithAllFields() { @Test void createErrorItemWithNullFields() { - ErrorItem errorItem = new ErrorItem() - .withComponentType(null) - .withErrorMessage(null) - .withModuleName(null); + ErrorItem errorItem = ErrorItem.builder().build(); assertThat(errorItem.getComponentType()).isNull(); assertThat(errorItem.getErrorMessage()).isNull(); @@ -43,10 +41,11 @@ void createErrorItemWithEmptyFields() { String errorMessage = ""; String moduleName = ""; - ErrorItem errorItem = new ErrorItem() - .withComponentType(componentType) - .withErrorMessage(errorMessage) - .withModuleName(moduleName); + ErrorItem errorItem = ErrorItem.builder() + .componentType(componentType) + .errorMessage(errorMessage) + .moduleName(moduleName) + .build(); assertThat(errorItem.getComponentType()).isEmpty(); assertThat(errorItem.getErrorMessage()).isEmpty(); diff --git a/docling-api/src/test/java/ai/docling/api/health/HealthCheckResponseTests.java b/docling-api/src/test/java/ai/docling/api/health/HealthCheckResponseTests.java index 6f3d411..4619737 100644 --- a/docling-api/src/test/java/ai/docling/api/health/HealthCheckResponseTests.java +++ b/docling-api/src/test/java/ai/docling/api/health/HealthCheckResponseTests.java @@ -13,14 +13,16 @@ class HealthCheckResponseTests { void whenValidParametersThenCreateHealthCheckResponse() { String status = "healthy"; - HealthCheckResponse response = new HealthCheckResponse().withStatus(status); + HealthCheckResponse response = HealthCheckResponse.builder() + .status(status) + .build(); assertThat(response.getStatus()).isEqualTo(status); } @Test void whenStatusIsNullThenCreateHealthCheckResponse() { - HealthCheckResponse response = new HealthCheckResponse().withStatus(null); + HealthCheckResponse response = HealthCheckResponse.builder().build(); assertThat(response.getStatus()).isNull(); } @@ -29,7 +31,9 @@ void whenStatusIsNullThenCreateHealthCheckResponse() { void whenEmptyStatusThenCreateHealthCheckResponse() { String status = ""; - HealthCheckResponse response = new HealthCheckResponse().withStatus(status); + HealthCheckResponse response = HealthCheckResponse.builder() + .status(status) + .build(); assertThat(response.getStatus()).isEqualTo(status); } diff --git a/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeClient.java b/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeClient.java index 99cdd9c..2ecd663 100644 --- a/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeClient.java +++ b/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeClient.java @@ -6,9 +6,16 @@ import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.net.http.HttpRequest.BodyPublishers; import java.net.http.HttpResponse; import java.net.http.HttpResponse.BodyHandlers; +import java.nio.ByteBuffer; import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.Flow.Subscriber; +import java.util.logging.Level; +import java.util.logging.Logger; import ai.docling.api.DoclingApi; import ai.docling.api.convert.request.ConvertDocumentRequest; @@ -27,10 +34,13 @@ * {@link #writeValueAsString(Object)} for serialization and deserialization behavior. */ public abstract class DoclingServeClient implements DoclingApi { + private static final Logger LOG = Logger.getLogger(DoclingServeClient.class.getName()); protected static final URI DEFAULT_BASE_URL = URI.create("http://localhost:5001"); private final URI baseUrl; private final HttpClient httpClient; + private final boolean logRequests; + private final boolean logResponses; protected DoclingServeClient(DoclingServeClientBuilder builder) { this.baseUrl = ensureNotNull(builder.baseUrl, "baseUrl"); @@ -43,6 +53,8 @@ protected DoclingServeClient(DoclingServeClientBuilder builder) { } this.httpClient = ensureNotNull(builder.httpClientBuilder, "httpClientBuilder").build(); + this.logRequests = builder.logRequests; + this.logResponses = builder.logResponses; } /** @@ -66,6 +78,63 @@ protected DoclingServeClient(DoclingServeClientBuilder builder) { */ protected abstract String writeValueAsString(T value); + protected void logRequest(HttpRequest request) { + if (LOG.isLoggable(Level.INFO)) { + var stringBuilder = new StringBuilder(); + stringBuilder.append("\n→ REQUEST: %s %s\n".formatted(request.method(), request.uri())); + stringBuilder.append(" HEADERS:\n"); + + request.headers().map().forEach((key, values) -> + stringBuilder.append(" %s: %s\n".formatted(key, String.join(", ", values))) + ); + + LOG.info(stringBuilder.toString()); + } + } + + protected void logResponse(HttpResponse response, Optional responseBody) { + if (LOG.isLoggable(Level.INFO)) { + var stringBuilder = new StringBuilder(); + stringBuilder.append("\n← RESPONSE: %s\n".formatted(response.statusCode())); + stringBuilder.append(" HEADERS:\n"); + + response.headers().map().forEach((key, values) -> + stringBuilder.append(" %s: %s\n".formatted(key, String.join(", ", values))) + ); + + responseBody.ifPresent(body -> stringBuilder.append(" BODY:\n%s".formatted(body))); + LOG.info(stringBuilder.toString()); + } + } + + protected T execute(HttpRequest request, Class expectedValueType) { + if (this.logRequests) { + logRequest(request); + } + + long startTime = System.currentTimeMillis(); + + try { + return httpClient.sendAsync(request, BodyHandlers.ofString()) + .thenApply(response -> getResponse(response, expectedValueType)) + .join(); + } + finally { + long duration = System.currentTimeMillis() - startTime; + LOG.info(() -> "Request took %d ms".formatted(duration)); + } + } + + protected T getResponse(HttpResponse response, Class expectedReturnType) { + var body = response.body(); + + if (this.logResponses) { + logResponse(response, Optional.ofNullable(body)); + } + + return readValue(body, expectedReturnType); + } + @Override public HealthCheckResponse health() { var httpRequest = HttpRequest.newBuilder() @@ -74,10 +143,7 @@ public HealthCheckResponse health() { .GET() .build(); - return httpClient.sendAsync(httpRequest, BodyHandlers.ofString()) - .thenApply(HttpResponse::body) - .thenApply(json -> readValue(json, HealthCheckResponse.class)) - .join(); + return execute(httpRequest, HealthCheckResponse.class); } @Override @@ -86,13 +152,34 @@ public ConvertDocumentResponse convertSource(ConvertDocumentRequest request) { .uri(baseUrl.resolve("/v1/convert/source")) .header("Accept", "application/json") .header("Content-Type", "application/json") - .POST(HttpRequest.BodyPublishers.ofString(writeValueAsString(request))) + .POST(new LoggingBodyPublisher<>(request)) .build(); - return httpClient.sendAsync(httpRequest, BodyHandlers.ofString()) - .thenApply(HttpResponse::body) - .thenApply(json -> readValue(json, ConvertDocumentResponse.class)) - .join(); + return execute(httpRequest, ConvertDocumentResponse.class); + } + + private class LoggingBodyPublisher implements BodyPublisher { + private final BodyPublisher delegate; + private final String stringContent; + + private LoggingBodyPublisher(T content) { + this.stringContent = writeValueAsString(content); + this.delegate = BodyPublishers.ofString(this.stringContent); + } + + @Override + public long contentLength() { + return this.delegate.contentLength(); + } + + @Override + public void subscribe(Subscriber subscriber) { + if (logRequests) { + LOG.info(() -> "→ REQUEST BODY: %s".formatted(this.stringContent)); + } + + this.delegate.subscribe(subscriber); + } } /** @@ -108,6 +195,8 @@ public ConvertDocumentResponse convertSource(ConvertDocumentRequest request) { public abstract static class DoclingServeClientBuilder> implements DoclingApiBuilder { private URI baseUrl = DEFAULT_BASE_URL; private HttpClient.Builder httpClientBuilder = HttpClient.newBuilder(); + private boolean logRequests = false; + private boolean logResponses = false; /** * Protected constructor for use by subclasses of {@link DoclingServeClientBuilder}. @@ -174,5 +263,17 @@ public B httpClientBuilder(HttpClient.Builder httpClientBuilder) { this.httpClientBuilder = httpClientBuilder; return (B) this; } + + @Override + public B logRequests(boolean logRequests) { + this.logRequests = logRequests; + return (B) this; + } + + @Override + public B logResponses(boolean logResponses) { + this.logResponses = logResponses; + return (B) this; + } } } diff --git a/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeJackson2Client.java b/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeJackson2Client.java index dd5b653..96f76b6 100644 --- a/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeJackson2Client.java +++ b/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeJackson2Client.java @@ -71,7 +71,6 @@ protected String writeValueAsString(T value) { */ public static final class Builder extends DoclingServeClientBuilder { private JsonMapper.Builder jsonMapperBuilder = JsonMapper.builder(); - private Builder() { } diff --git a/docling-serve-client/src/test/java/ai/docling/client/serve/AbstractDoclingServeClientTests.java b/docling-serve-client/src/test/java/ai/docling/client/serve/AbstractDoclingServeClientTests.java index 335229f..182e84b 100644 --- a/docling-serve-client/src/test/java/ai/docling/client/serve/AbstractDoclingServeClientTests.java +++ b/docling-serve-client/src/test/java/ai/docling/client/serve/AbstractDoclingServeClientTests.java @@ -7,7 +7,6 @@ import java.net.URI; import java.time.Duration; import java.util.Base64; -import java.util.List; import java.util.Optional; import org.junit.jupiter.api.Test; @@ -50,8 +49,9 @@ void shouldSuccessfullyCallHealthEndpoint() { @Test void shouldConvertHttpSourceSuccessfully() { - ConvertDocumentRequest request = new ConvertDocumentRequest() - .withSources(List.of(new HttpSource().withUrl(URI.create("https://docs.arconia.io/arconia-cli/latest/development/dev/")))); + ConvertDocumentRequest request = ConvertDocumentRequest.builder() + .source(HttpSource.builder().url(URI.create("https://docs.arconia.io/arconia-cli/latest/development/dev/")).build()) + .build(); ConvertDocumentResponse response = getDoclingClient().convertSource(request); @@ -71,8 +71,13 @@ void shouldConvertHttpSourceSuccessfully() { @Test void shouldConvertFileSourceSuccessfully() throws IOException { var fileResource = readFileFromClasspath("story.pdf"); - ConvertDocumentRequest request = new ConvertDocumentRequest() - .withSources(List.of(new FileSource().withFilename("story.pdf").withBase64String(Base64.getEncoder().encodeToString(fileResource)))); + ConvertDocumentRequest request = ConvertDocumentRequest.builder() + .source(FileSource.builder() + .filename("story.pdf") + .base64String(Base64.getEncoder().encodeToString(fileResource)) + .build() + ) + .build(); ConvertDocumentResponse response = getDoclingClient().convertSource(request); @@ -90,14 +95,16 @@ void shouldConvertFileSourceSuccessfully() throws IOException { @Test void shouldHandleConversionWithDifferentDocumentOptions() { - ConvertDocumentOptions options = new ConvertDocumentOptions() - .withDoOcr(true) - .withIncludeImages(true) - .withTableMode(TableFormerMode.FAST); - - ConvertDocumentRequest request = new ConvertDocumentRequest() - .withSources(List.of(new HttpSource().withUrl(URI.create("https://docs.arconia.io/arconia-cli/latest/development/dev/")))) - .withOptions(options); + ConvertDocumentOptions options = ConvertDocumentOptions.builder() + .doOcr(true) + .includeImages(true) + .tableMode(TableFormerMode.FAST) + .build(); + + ConvertDocumentRequest request = ConvertDocumentRequest.builder() + .source(HttpSource.builder().url(URI.create("https://docs.arconia.io/arconia-cli/latest/development/dev/")).build()) + .options(options) + .build(); ConvertDocumentResponse response = getDoclingClient().convertSource(request); diff --git a/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson2ClientTests.java b/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson2ClientTests.java index 9cc0b1f..7393d85 100644 --- a/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson2ClientTests.java +++ b/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson2ClientTests.java @@ -15,6 +15,8 @@ class DoclingServeJackson2ClientTests extends AbstractDoclingServeClientTests { static void setUp() { doclingClient = DoclingServeJackson2Client.builder() .baseUrl("http://localhost:%s".formatted(doclingContainer.getMappedPort(DoclingServeContainerConfig.DEFAULT_DOCLING_SERVE_PORT))) + .logRequests() + .logResponses() .build(); } diff --git a/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson3ClientTests.java b/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson3ClientTests.java index 75a4a9b..3377461 100644 --- a/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson3ClientTests.java +++ b/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson3ClientTests.java @@ -14,6 +14,8 @@ class DoclingServeJackson3ClientTests extends AbstractDoclingServeClientTests { @BeforeAll static void setUp() { doclingClient = DoclingServeJackson3Client.builder() + .logRequests() + .logResponses() .baseUrl("http://localhost:%s".formatted(doclingContainer.getMappedPort(DoclingServeContainerConfig.DEFAULT_DOCLING_SERVE_PORT))) .build(); } diff --git a/gradle.properties b/gradle.properties index b640061..fa627fc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,5 @@ # This file was generated by the Gradle 'init' task. # https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties -org.gradle.configuration-cache=true java.version=17 version=0.0.1-SNAPSHOT diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 40f0975..8bb7e21 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,6 +4,8 @@ jackson2 = "2.20.0" jackson3 = "3.0.1" jspecify = "1.0.0" junit = "6.0.0" +lombok = "1.18.42" +lombok-gradle = "9.0.0" testcontainers = "2.0.1" [libraries] @@ -28,3 +30,6 @@ junit-platform = { group = "org.junit.platform", name = "junit-platform-launcher # Testcontainers testcontainers-bom = { group = "org.testcontainers", name = "testcontainers-bom", version.ref = "testcontainers"} testcontainers-junit-jupiter = { group = "org.testcontainers", name = "testcontainers-junit-jupiter" } + +[plugins] +lombok = { id = "io.freefair.lombok", version.ref = "lombok-gradle" } From dfa67558366a70bfb7e7256fb53ab65bbdd41fda Mon Sep 17 00:00:00 2001 From: Eric Deandrea Date: Mon, 3 Nov 2025 08:13:55 -0500 Subject: [PATCH 12/13] refactor docling-api into docling-serve-api --- .github/workflows/build.yml | 2 +- README.md | 14 ++++++------ .../docling-serve-api}/build.gradle.kts | 0 .../docling-serve-api}/src/lombok.config | 0 .../ai/docling/api/serve/DoclingServeApi.java | 20 +++++++++--------- .../request/ConvertDocumentRequest.java | 8 +++---- .../options/ConvertDocumentOptions.java | 2 +- .../convert/request/options/ImageRefMode.java | 2 +- .../convert/request/options/InputFormat.java | 2 +- .../convert/request/options/OcrEngine.java | 2 +- .../convert/request/options/OutputFormat.java | 2 +- .../convert/request/options/PdfBackend.java | 2 +- .../options/PictureDescriptionApi.java | 2 +- .../options/PictureDescriptionLocal.java | 2 +- .../request/options/ProcessingPipeline.java | 2 +- .../request/options/ResponseFormat.java | 2 +- .../request/options/TableFormerMode.java | 2 +- .../convert/request/options/VlmModelApi.java | 2 +- .../request/options/VlmModelLocal.java | 2 +- .../convert/request/options/VlmModelType.java | 2 +- .../request/options}/package-info.java | 2 +- .../serve/convert/request}/package-info.java | 2 +- .../convert/request/source/FileSource.java | 2 +- .../convert/request/source/HttpSource.java | 2 +- .../serve}/convert/request/source/Source.java | 2 +- .../convert/request/source}/package-info.java | 2 +- .../convert/request/target/InBodyTarget.java | 2 +- .../convert/request/target/PutTarget.java | 2 +- .../serve}/convert/request/target/Target.java | 2 +- .../convert/request/target/ZipTarget.java | 2 +- .../convert/request/target}/package-info.java | 2 +- .../response/ConvertDocumentResponse.java | 2 +- .../convert/response/DocumentResponse.java | 2 +- .../serve}/convert/response/ErrorItem.java | 2 +- .../serve/convert/response}/package-info.java | 2 +- .../serve}/health/HealthCheckResponse.java | 2 +- .../api/serve}/health/package-info.java | 2 +- .../ai/docling/api/serve}/package-info.java | 2 +- .../ai/docling/api/serve}/util/Utils.java | 4 ++-- .../api/serve}/util/ValidationUtils.java | 6 +++--- .../docling/api/serve}/util/package-info.java | 2 +- .../request/ConvertDocumentRequestTests.java | 12 +++++------ .../options/ConvertDocumentOptionsTests.java | 2 +- .../options/PictureDescriptionApiTests.java | 2 +- .../options/PictureDescriptionLocalTests.java | 2 +- .../request/source/FileSourceTests.java | 2 +- .../request/source/HttpSourceTests.java | 2 +- .../request/target/PutTargetTests.java | 2 +- .../ConvertDocumentResponseTests.java | 2 +- .../response/DocumentResponseTests.java | 2 +- .../convert/response/ErrorItemTests.java | 2 +- .../health/HealthCheckResponseTests.java | 2 +- .../docling/api/serve}/util/UtilsTests.java | 2 +- .../api/serve}/util/ValidationUtilsTests.java | 10 ++++----- .../docling-serve-client}/build.gradle.kts | 2 +- .../client/serve/DoclingServeClient.java | 14 ++++++------ .../DoclingServeClientBuilderFactory.java | 0 .../serve/DoclingServeJackson2Client.java | 2 +- .../serve/DoclingServeJackson3Client.java | 2 +- .../ai/docling/client/serve/package-info.java | 0 .../AbstractDoclingServeClientTests.java | 18 ++++++++-------- ...DoclingServeClientBuilderFactoryTests.java | 0 .../DoclingServeJackson2ClientTests.java | 6 +++--- .../DoclingServeJackson3ClientTests.java | 6 +++--- .../src/test/resources/story.pdf | Bin docs/build.gradle.kts | 2 +- docs/src/doc/docs/api.md | 1 - docs/src/doc/docs/docling-serve/serve-api.md | 1 + .../docs/{ => docling-serve}/serve-client.md | 0 docs/src/doc/mkdocs.yml | 5 +++-- settings.gradle.kts | 5 ++++- 71 files changed, 116 insertions(+), 112 deletions(-) rename {docling-api => docling-serve/docling-serve-api}/build.gradle.kts (100%) rename {docling-api => docling-serve/docling-serve-api}/src/lombok.config (100%) rename docling-api/src/main/java/ai/docling/api/DoclingApi.java => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/DoclingServeApi.java (83%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/convert/request/ConvertDocumentRequest.java (85%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/convert/request/options/ConvertDocumentOptions.java (98%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/convert/request/options/ImageRefMode.java (83%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/convert/request/options/InputFormat.java (92%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/convert/request/options/OcrEngine.java (85%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/convert/request/options/OutputFormat.java (86%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/convert/request/options/PdfBackend.java (84%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/convert/request/options/PictureDescriptionApi.java (96%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/convert/request/options/PictureDescriptionLocal.java (95%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/convert/request/options/ProcessingPipeline.java (82%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/convert/request/options/ResponseFormat.java (85%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/convert/request/options/TableFormerMode.java (78%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/convert/request/options/VlmModelApi.java (96%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/convert/request/options/VlmModelLocal.java (97%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/convert/request/options/VlmModelType.java (89%) rename {docling-api/src/main/java/ai/docling/api/convert/request/source => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options}/package-info.java (51%) rename {docling-api/src/main/java/ai/docling/api/convert/response => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request}/package-info.java (55%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/convert/request/source/FileSource.java (95%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/convert/request/source/HttpSource.java (96%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/convert/request/source/Source.java (95%) rename {docling-api/src/main/java/ai/docling/api/convert/request/options => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/source}/package-info.java (51%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/convert/request/target/InBodyTarget.java (94%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/convert/request/target/PutTarget.java (96%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/convert/request/target/Target.java (96%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/convert/request/target/ZipTarget.java (95%) rename {docling-api/src/main/java/ai/docling/api/convert/request => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/target}/package-info.java (51%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/convert/response/ConvertDocumentResponse.java (96%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/convert/response/DocumentResponse.java (97%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/convert/response/ErrorItem.java (95%) rename {docling-api/src/main/java/ai/docling/api/convert/request/target => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/response}/package-info.java (54%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/health/HealthCheckResponse.java (95%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/health/package-info.java (72%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/package-info.java (74%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/util/Utils.java (98%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/util/ValidationUtils.java (98%) rename {docling-api/src/main/java/ai/docling/api => docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve}/util/package-info.java (61%) rename {docling-api/src/test/java/ai/docling/api => docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve}/convert/request/ConvertDocumentRequestTests.java (90%) rename {docling-api/src/test/java/ai/docling/api => docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve}/convert/request/options/ConvertDocumentOptionsTests.java (96%) rename {docling-api/src/test/java/ai/docling/api => docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve}/convert/request/options/PictureDescriptionApiTests.java (98%) rename {docling-api/src/test/java/ai/docling/api => docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve}/convert/request/options/PictureDescriptionLocalTests.java (97%) rename {docling-api/src/test/java/ai/docling/api => docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve}/convert/request/source/FileSourceTests.java (96%) rename {docling-api/src/test/java/ai/docling/api => docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve}/convert/request/source/HttpSourceTests.java (97%) rename {docling-api/src/test/java/ai/docling/api => docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve}/convert/request/target/PutTargetTests.java (93%) rename {docling-api/src/test/java/ai/docling/api => docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve}/convert/response/ConvertDocumentResponseTests.java (98%) rename {docling-api/src/test/java/ai/docling/api => docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve}/convert/response/DocumentResponseTests.java (98%) rename {docling-api/src/test/java/ai/docling/api => docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve}/convert/response/ErrorItemTests.java (96%) rename {docling-api/src/test/java/ai/docling/api => docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve}/health/HealthCheckResponseTests.java (96%) rename {docling-api/src/test/java/ai/docling/api => docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve}/util/UtilsTests.java (99%) rename {docling-api/src/test/java/ai/docling/api => docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve}/util/ValidationUtilsTests.java (96%) rename {docling-serve-client => docling-serve/docling-serve-client}/build.gradle.kts (90%) rename {docling-serve-client => docling-serve/docling-serve-client}/src/main/java/ai/docling/client/serve/DoclingServeClient.java (95%) rename {docling-serve-client => docling-serve/docling-serve-client}/src/main/java/ai/docling/client/serve/DoclingServeClientBuilderFactory.java (100%) rename {docling-serve-client => docling-serve/docling-serve-client}/src/main/java/ai/docling/client/serve/DoclingServeJackson2Client.java (98%) rename {docling-serve-client => docling-serve/docling-serve-client}/src/main/java/ai/docling/client/serve/DoclingServeJackson3Client.java (98%) rename {docling-serve-client => docling-serve/docling-serve-client}/src/main/java/ai/docling/client/serve/package-info.java (100%) rename {docling-serve-client => docling-serve/docling-serve-client}/src/test/java/ai/docling/client/serve/AbstractDoclingServeClientTests.java (87%) rename {docling-serve-client => docling-serve/docling-serve-client}/src/test/java/ai/docling/client/serve/DoclingServeClientBuilderFactoryTests.java (100%) rename {docling-serve-client => docling-serve/docling-serve-client}/src/test/java/ai/docling/client/serve/DoclingServeJackson2ClientTests.java (82%) rename {docling-serve-client => docling-serve/docling-serve-client}/src/test/java/ai/docling/client/serve/DoclingServeJackson3ClientTests.java (81%) rename {docling-serve-client => docling-serve/docling-serve-client}/src/test/resources/story.pdf (100%) delete mode 100644 docs/src/doc/docs/api.md create mode 100644 docs/src/doc/docs/docling-serve/serve-api.md rename docs/src/doc/docs/{ => docling-serve}/serve-client.md (100%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 118d389..08c8bcc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,7 +28,7 @@ jobs: - 21 - 25 module: - - docling-api + - docling-serve-api - docling-serve-client - docling-testing - docling-testcontainers diff --git a/README.md b/README.md index d94a209..adb5649 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,8 @@ This is repository for Docling Java, a Java API for using [Docling](https://gith This project aims to provide the following artifacts: -* [`docling-api`](docling-api): The core API for interacting with Docling. Should be framework-agnostic. -* [`docling-serve-client`](docling-serve-client): A reference implementation of the [`docling-api`](docling-api) using Java's [`HttpClient`](https://openjdk.org/groups/net/httpclient/intro.html) and [Jackson](https://github.com/FasterXML/jackson) to connect to a [Docling Serve](https://github.com/docling-project/docling-serve) endpoint. +* [`docling-serve-api`](docling-serve/docling-serve-api): The core API for interacting with Docling. Should be framework-agnostic. +* [`docling-serve-client`](docling-serve/docling-serve-client): A reference implementation of the [`docling-serve-api`](docling-serve/docling-serve-api) using Java's [`HttpClient`](https://openjdk.org/groups/net/httpclient/intro.html) and [Jackson](https://github.com/FasterXML/jackson) to connect to a [Docling Serve](https://github.com/docling-project/docling-serve) endpoint. * [`docling-testing`](docling-testing): Utilities for testing Docling integration. * [`docling-testcontainers`](docling-testcontainers): A [Testcontainers module](https://testcontainers.com/) for running Docling in a Docker container. @@ -36,13 +36,13 @@ This project aims to provide the following artifacts: Use `DoclingApi.convertSource()` to convert individual documents. For example: ```java -import ai.docling.api.DoclingApi; -import ai.docling.api.convert.request.ConvertDocumentRequest; -import ai.docling.api.convert.request.source.HttpSource; -import ai.docling.api.convert.response.ConvertDocumentResponse; +import ai.docling.api.serve.DoclingServeApi; +import ai.docling.api.serve.convert.request.ConvertDocumentRequest; +import ai.docling.api.serve.convert.request.source.HttpSource; +import ai.docling.api.serve.convert.response.ConvertDocumentResponse; import ai.docling.client.serve.DoclingServeClientBuilderFactory; -DoclingApi doclingApi = DoclingServeClientBuilderFactory.newBuilder() +DoclingServeApi doclingServeApi = DoclingServeClientBuilderFactory.newBuilder() .baseUrl("") .build(); diff --git a/docling-api/build.gradle.kts b/docling-serve/docling-serve-api/build.gradle.kts similarity index 100% rename from docling-api/build.gradle.kts rename to docling-serve/docling-serve-api/build.gradle.kts diff --git a/docling-api/src/lombok.config b/docling-serve/docling-serve-api/src/lombok.config similarity index 100% rename from docling-api/src/lombok.config rename to docling-serve/docling-serve-api/src/lombok.config diff --git a/docling-api/src/main/java/ai/docling/api/DoclingApi.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/DoclingServeApi.java similarity index 83% rename from docling-api/src/main/java/ai/docling/api/DoclingApi.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/DoclingServeApi.java index 1f574e8..df222d9 100644 --- a/docling-api/src/main/java/ai/docling/api/DoclingApi.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/DoclingServeApi.java @@ -1,13 +1,13 @@ -package ai.docling.api; +package ai.docling.api.serve; -import ai.docling.api.convert.request.ConvertDocumentRequest; -import ai.docling.api.convert.response.ConvertDocumentResponse; -import ai.docling.api.health.HealthCheckResponse; +import ai.docling.api.serve.convert.request.ConvertDocumentRequest; +import ai.docling.api.serve.convert.response.ConvertDocumentResponse; +import ai.docling.api.serve.health.HealthCheckResponse; /** - * Docling API interface. + * Docling Serve API interface. */ -public interface DoclingApi { +public interface DoclingServeApi { /** * Executes a health check for the API and retrieves the health status of the service. @@ -31,16 +31,16 @@ public interface DoclingApi { * * @return a {@link DoclingApiBuilder} initialized with the state of the current API instance. */ - > DoclingApiBuilder toBuilder(); + > DoclingApiBuilder toBuilder(); /** - * A builder interface for constructing implementations of {@link DoclingApi}. This interface + * A builder interface for constructing implementations of {@link DoclingServeApi}. This interface * supports a fluent API for setting configuration properties before building an instance. * - * @param the type of the {@link DoclingApi} implementation being built. + * @param the type of the {@link DoclingServeApi} implementation being built. * @param the type of the concrete builder implementation. */ - interface DoclingApiBuilder> { + interface DoclingApiBuilder> { /** * Enables logging of requests for the API client being built. This can be useful for * debugging or monitoring the behavior of requests made to the API. diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/ConvertDocumentRequest.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/ConvertDocumentRequest.java similarity index 85% rename from docling-api/src/main/java/ai/docling/api/convert/request/ConvertDocumentRequest.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/ConvertDocumentRequest.java index caa0baa..31a383d 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/ConvertDocumentRequest.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/ConvertDocumentRequest.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request; +package ai.docling.api.serve.convert.request; import java.util.List; @@ -9,9 +9,9 @@ import com.fasterxml.jackson.annotation.JsonSetter; import com.fasterxml.jackson.annotation.Nulls; -import ai.docling.api.convert.request.options.ConvertDocumentOptions; -import ai.docling.api.convert.request.source.Source; -import ai.docling.api.convert.request.target.Target; +import ai.docling.api.serve.convert.request.options.ConvertDocumentOptions; +import ai.docling.api.serve.convert.request.source.Source; +import ai.docling.api.serve.convert.request.target.Target; /** * Represents a request to convert a document. The request includes the source(s) of the document, diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/options/ConvertDocumentOptions.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/ConvertDocumentOptions.java similarity index 98% rename from docling-api/src/main/java/ai/docling/api/convert/request/options/ConvertDocumentOptions.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/ConvertDocumentOptions.java index ff64a19..8463356 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/options/ConvertDocumentOptions.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/ConvertDocumentOptions.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.options; +package ai.docling.api.serve.convert.request.options; import java.time.Duration; import java.util.List; diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/options/ImageRefMode.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/ImageRefMode.java similarity index 83% rename from docling-api/src/main/java/ai/docling/api/convert/request/options/ImageRefMode.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/ImageRefMode.java index f39100a..84fa156 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/options/ImageRefMode.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/ImageRefMode.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.options; +package ai.docling.api.serve.convert.request.options; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/options/InputFormat.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/InputFormat.java similarity index 92% rename from docling-api/src/main/java/ai/docling/api/convert/request/options/InputFormat.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/InputFormat.java index a3b1ff7..56aa7c4 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/options/InputFormat.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/InputFormat.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.options; +package ai.docling.api.serve.convert.request.options; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/options/OcrEngine.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/OcrEngine.java similarity index 85% rename from docling-api/src/main/java/ai/docling/api/convert/request/options/OcrEngine.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/OcrEngine.java index 69ee359..df159d5 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/options/OcrEngine.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/OcrEngine.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.options; +package ai.docling.api.serve.convert.request.options; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/options/OutputFormat.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/OutputFormat.java similarity index 86% rename from docling-api/src/main/java/ai/docling/api/convert/request/options/OutputFormat.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/OutputFormat.java index 83a9c53..66099a7 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/options/OutputFormat.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/OutputFormat.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.options; +package ai.docling.api.serve.convert.request.options; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/options/PdfBackend.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/PdfBackend.java similarity index 84% rename from docling-api/src/main/java/ai/docling/api/convert/request/options/PdfBackend.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/PdfBackend.java index 36f776c..a4f90f9 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/options/PdfBackend.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/PdfBackend.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.options; +package ai.docling.api.serve.convert.request.options; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionApi.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/PictureDescriptionApi.java similarity index 96% rename from docling-api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionApi.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/PictureDescriptionApi.java index 369e26b..28fd92b 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionApi.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/PictureDescriptionApi.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.options; +package ai.docling.api.serve.convert.request.options; import java.net.URI; import java.time.Duration; diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionLocal.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/PictureDescriptionLocal.java similarity index 95% rename from docling-api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionLocal.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/PictureDescriptionLocal.java index c1a2dcf..19cc35f 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/options/PictureDescriptionLocal.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/PictureDescriptionLocal.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.options; +package ai.docling.api.serve.convert.request.options; import java.util.Map; diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/options/ProcessingPipeline.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/ProcessingPipeline.java similarity index 82% rename from docling-api/src/main/java/ai/docling/api/convert/request/options/ProcessingPipeline.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/ProcessingPipeline.java index 4681a61..3b49e6b 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/options/ProcessingPipeline.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/ProcessingPipeline.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.options; +package ai.docling.api.serve.convert.request.options; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/options/ResponseFormat.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/ResponseFormat.java similarity index 85% rename from docling-api/src/main/java/ai/docling/api/convert/request/options/ResponseFormat.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/ResponseFormat.java index 6d9767f..230fac6 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/options/ResponseFormat.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/ResponseFormat.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.options; +package ai.docling.api.serve.convert.request.options; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/options/TableFormerMode.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/TableFormerMode.java similarity index 78% rename from docling-api/src/main/java/ai/docling/api/convert/request/options/TableFormerMode.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/TableFormerMode.java index b12090f..0d50750 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/options/TableFormerMode.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/TableFormerMode.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.options; +package ai.docling.api.serve.convert.request.options; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/options/VlmModelApi.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/VlmModelApi.java similarity index 96% rename from docling-api/src/main/java/ai/docling/api/convert/request/options/VlmModelApi.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/VlmModelApi.java index a8382ba..f352e3a 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/options/VlmModelApi.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/VlmModelApi.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.options; +package ai.docling.api.serve.convert.request.options; import java.net.URI; import java.time.Duration; diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/options/VlmModelLocal.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/VlmModelLocal.java similarity index 97% rename from docling-api/src/main/java/ai/docling/api/convert/request/options/VlmModelLocal.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/VlmModelLocal.java index c836b72..294526d 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/options/VlmModelLocal.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/VlmModelLocal.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.options; +package ai.docling.api.serve.convert.request.options; import java.util.Map; diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/options/VlmModelType.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/VlmModelType.java similarity index 89% rename from docling-api/src/main/java/ai/docling/api/convert/request/options/VlmModelType.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/VlmModelType.java index afd30f8..fa5e732 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/options/VlmModelType.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/VlmModelType.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.options; +package ai.docling.api.serve.convert.request.options; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/source/package-info.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/package-info.java similarity index 51% rename from docling-api/src/main/java/ai/docling/api/convert/request/source/package-info.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/package-info.java index 59dd975..597b14b 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/source/package-info.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/options/package-info.java @@ -1,4 +1,4 @@ @NullMarked -package ai.docling.api.convert.request.source; +package ai.docling.api.serve.convert.request.options; import org.jspecify.annotations.NullMarked; diff --git a/docling-api/src/main/java/ai/docling/api/convert/response/package-info.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/package-info.java similarity index 55% rename from docling-api/src/main/java/ai/docling/api/convert/response/package-info.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/package-info.java index 0cbcd82..8a68e04 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/response/package-info.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/package-info.java @@ -1,4 +1,4 @@ @NullMarked -package ai.docling.api.convert.response; +package ai.docling.api.serve.convert.request; import org.jspecify.annotations.NullMarked; diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/source/FileSource.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/source/FileSource.java similarity index 95% rename from docling-api/src/main/java/ai/docling/api/convert/request/source/FileSource.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/source/FileSource.java index 97874c7..18bc70a 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/source/FileSource.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/source/FileSource.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.source; +package ai.docling.api.serve.convert.request.source; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/source/HttpSource.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/source/HttpSource.java similarity index 96% rename from docling-api/src/main/java/ai/docling/api/convert/request/source/HttpSource.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/source/HttpSource.java index 507b520..f22d5a5 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/source/HttpSource.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/source/HttpSource.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.source; +package ai.docling.api.serve.convert.request.source; import java.net.URI; import java.util.Map; diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/source/Source.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/source/Source.java similarity index 95% rename from docling-api/src/main/java/ai/docling/api/convert/request/source/Source.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/source/Source.java index 2247867..d194e42 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/source/Source.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/source/Source.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.source; +package ai.docling.api.serve.convert.request.source; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/options/package-info.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/source/package-info.java similarity index 51% rename from docling-api/src/main/java/ai/docling/api/convert/request/options/package-info.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/source/package-info.java index cc6c665..14112ef 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/options/package-info.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/source/package-info.java @@ -1,4 +1,4 @@ @NullMarked -package ai.docling.api.convert.request.options; +package ai.docling.api.serve.convert.request.source; import org.jspecify.annotations.NullMarked; diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/target/InBodyTarget.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/target/InBodyTarget.java similarity index 94% rename from docling-api/src/main/java/ai/docling/api/convert/request/target/InBodyTarget.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/target/InBodyTarget.java index 5779f66..8e82284 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/target/InBodyTarget.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/target/InBodyTarget.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.target; +package ai.docling.api.serve.convert.request.target; import com.fasterxml.jackson.annotation.JsonInclude; diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/target/PutTarget.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/target/PutTarget.java similarity index 96% rename from docling-api/src/main/java/ai/docling/api/convert/request/target/PutTarget.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/target/PutTarget.java index 5863452..6ba1896 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/target/PutTarget.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/target/PutTarget.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.target; +package ai.docling.api.serve.convert.request.target; import java.net.URI; diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/target/Target.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/target/Target.java similarity index 96% rename from docling-api/src/main/java/ai/docling/api/convert/request/target/Target.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/target/Target.java index b32eaae..c5939e7 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/target/Target.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/target/Target.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.target; +package ai.docling.api.serve.convert.request.target; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/target/ZipTarget.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/target/ZipTarget.java similarity index 95% rename from docling-api/src/main/java/ai/docling/api/convert/request/target/ZipTarget.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/target/ZipTarget.java index d6f1c2c..e6108c7 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/target/ZipTarget.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/target/ZipTarget.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.target; +package ai.docling.api.serve.convert.request.target; import com.fasterxml.jackson.annotation.JsonInclude; diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/package-info.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/target/package-info.java similarity index 51% rename from docling-api/src/main/java/ai/docling/api/convert/request/package-info.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/target/package-info.java index a65639c..ed1488f 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/package-info.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/request/target/package-info.java @@ -1,4 +1,4 @@ @NullMarked -package ai.docling.api.convert.request; +package ai.docling.api.serve.convert.request.target; import org.jspecify.annotations.NullMarked; diff --git a/docling-api/src/main/java/ai/docling/api/convert/response/ConvertDocumentResponse.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/response/ConvertDocumentResponse.java similarity index 96% rename from docling-api/src/main/java/ai/docling/api/convert/response/ConvertDocumentResponse.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/response/ConvertDocumentResponse.java index 63e0e44..f41da74 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/response/ConvertDocumentResponse.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/response/ConvertDocumentResponse.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.response; +package ai.docling.api.serve.convert.response; import java.util.List; import java.util.Map; diff --git a/docling-api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/response/DocumentResponse.java similarity index 97% rename from docling-api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/response/DocumentResponse.java index 02e8994..6dbcb84 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/response/DocumentResponse.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/response/DocumentResponse.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.response; +package ai.docling.api.serve.convert.response; import java.util.Map; diff --git a/docling-api/src/main/java/ai/docling/api/convert/response/ErrorItem.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/response/ErrorItem.java similarity index 95% rename from docling-api/src/main/java/ai/docling/api/convert/response/ErrorItem.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/response/ErrorItem.java index 36c4fee..e67f0a9 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/response/ErrorItem.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/response/ErrorItem.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.response; +package ai.docling.api.serve.convert.response; import org.jspecify.annotations.Nullable; diff --git a/docling-api/src/main/java/ai/docling/api/convert/request/target/package-info.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/response/package-info.java similarity index 54% rename from docling-api/src/main/java/ai/docling/api/convert/request/target/package-info.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/response/package-info.java index 66dfd27..0b83003 100644 --- a/docling-api/src/main/java/ai/docling/api/convert/request/target/package-info.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/convert/response/package-info.java @@ -1,4 +1,4 @@ @NullMarked -package ai.docling.api.convert.request.target; +package ai.docling.api.serve.convert.response; import org.jspecify.annotations.NullMarked; diff --git a/docling-api/src/main/java/ai/docling/api/health/HealthCheckResponse.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/health/HealthCheckResponse.java similarity index 95% rename from docling-api/src/main/java/ai/docling/api/health/HealthCheckResponse.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/health/HealthCheckResponse.java index 198b328..9496573 100644 --- a/docling-api/src/main/java/ai/docling/api/health/HealthCheckResponse.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/health/HealthCheckResponse.java @@ -1,4 +1,4 @@ -package ai.docling.api.health; +package ai.docling.api.serve.health; import org.jspecify.annotations.Nullable; diff --git a/docling-api/src/main/java/ai/docling/api/health/package-info.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/health/package-info.java similarity index 72% rename from docling-api/src/main/java/ai/docling/api/health/package-info.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/health/package-info.java index f439f96..b9667e4 100644 --- a/docling-api/src/main/java/ai/docling/api/health/package-info.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/health/package-info.java @@ -2,6 +2,6 @@ * Docling Serve - Health APIs. */ @NullMarked -package ai.docling.api.health; +package ai.docling.api.serve.health; import org.jspecify.annotations.NullMarked; diff --git a/docling-api/src/main/java/ai/docling/api/package-info.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/package-info.java similarity index 74% rename from docling-api/src/main/java/ai/docling/api/package-info.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/package-info.java index 13af60c..af2eca6 100644 --- a/docling-api/src/main/java/ai/docling/api/package-info.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/package-info.java @@ -2,6 +2,6 @@ * Docling Java API. */ @NullMarked -package ai.docling.api; +package ai.docling.api.serve; import org.jspecify.annotations.NullMarked; diff --git a/docling-api/src/main/java/ai/docling/api/util/Utils.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/util/Utils.java similarity index 98% rename from docling-api/src/main/java/ai/docling/api/util/Utils.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/util/Utils.java index 1b8a436..fd7c016 100644 --- a/docling-api/src/main/java/ai/docling/api/util/Utils.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/util/Utils.java @@ -1,6 +1,6 @@ -package ai.docling.api.util; +package ai.docling.api.serve.util; -import static ai.docling.api.util.ValidationUtils.ensureNotEmpty; +import static ai.docling.api.serve.util.ValidationUtils.ensureNotEmpty; import java.util.Collection; import java.util.List; diff --git a/docling-api/src/main/java/ai/docling/api/util/ValidationUtils.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/util/ValidationUtils.java similarity index 98% rename from docling-api/src/main/java/ai/docling/api/util/ValidationUtils.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/util/ValidationUtils.java index bcaec31..a4dcd76 100644 --- a/docling-api/src/main/java/ai/docling/api/util/ValidationUtils.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/util/ValidationUtils.java @@ -1,7 +1,7 @@ -package ai.docling.api.util; +package ai.docling.api.serve.util; -import static ai.docling.api.util.Utils.isNullOrBlank; -import static ai.docling.api.util.Utils.isNullOrEmpty; +import static ai.docling.api.serve.util.Utils.isNullOrBlank; +import static ai.docling.api.serve.util.Utils.isNullOrEmpty; import java.util.Collection; import java.util.Map; diff --git a/docling-api/src/main/java/ai/docling/api/util/package-info.java b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/util/package-info.java similarity index 61% rename from docling-api/src/main/java/ai/docling/api/util/package-info.java rename to docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/util/package-info.java index bad3294..ddf94a6 100644 --- a/docling-api/src/main/java/ai/docling/api/util/package-info.java +++ b/docling-serve/docling-serve-api/src/main/java/ai/docling/api/serve/util/package-info.java @@ -1,4 +1,4 @@ @NullMarked -package ai.docling.api.util; +package ai.docling.api.serve.util; import org.jspecify.annotations.NullMarked; diff --git a/docling-api/src/test/java/ai/docling/api/convert/request/ConvertDocumentRequestTests.java b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/request/ConvertDocumentRequestTests.java similarity index 90% rename from docling-api/src/test/java/ai/docling/api/convert/request/ConvertDocumentRequestTests.java rename to docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/request/ConvertDocumentRequestTests.java index d31d987..29cd9d4 100644 --- a/docling-api/src/test/java/ai/docling/api/convert/request/ConvertDocumentRequestTests.java +++ b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/request/ConvertDocumentRequestTests.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request; +package ai.docling.api.serve.convert.request; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -10,11 +10,11 @@ import org.junit.jupiter.api.Test; -import ai.docling.api.convert.request.options.ConvertDocumentOptions; -import ai.docling.api.convert.request.source.FileSource; -import ai.docling.api.convert.request.source.HttpSource; -import ai.docling.api.convert.request.target.InBodyTarget; -import ai.docling.api.convert.request.target.ZipTarget; +import ai.docling.api.serve.convert.request.options.ConvertDocumentOptions; +import ai.docling.api.serve.convert.request.source.FileSource; +import ai.docling.api.serve.convert.request.source.HttpSource; +import ai.docling.api.serve.convert.request.target.InBodyTarget; +import ai.docling.api.serve.convert.request.target.ZipTarget; /** * Unit tests for {@link ConvertDocumentRequest}. diff --git a/docling-api/src/test/java/ai/docling/api/convert/request/options/ConvertDocumentOptionsTests.java b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/request/options/ConvertDocumentOptionsTests.java similarity index 96% rename from docling-api/src/test/java/ai/docling/api/convert/request/options/ConvertDocumentOptionsTests.java rename to docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/request/options/ConvertDocumentOptionsTests.java index 6779263..84f26c5 100644 --- a/docling-api/src/test/java/ai/docling/api/convert/request/options/ConvertDocumentOptionsTests.java +++ b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/request/options/ConvertDocumentOptionsTests.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.options; +package ai.docling.api.serve.convert.request.options; import static org.assertj.core.api.Assertions.assertThat; diff --git a/docling-api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionApiTests.java b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/request/options/PictureDescriptionApiTests.java similarity index 98% rename from docling-api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionApiTests.java rename to docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/request/options/PictureDescriptionApiTests.java index d87e761..5b6fa67 100644 --- a/docling-api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionApiTests.java +++ b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/request/options/PictureDescriptionApiTests.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.options; +package ai.docling.api.serve.convert.request.options; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; diff --git a/docling-api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionLocalTests.java b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/request/options/PictureDescriptionLocalTests.java similarity index 97% rename from docling-api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionLocalTests.java rename to docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/request/options/PictureDescriptionLocalTests.java index 0010382..2d245c7 100644 --- a/docling-api/src/test/java/ai/docling/api/convert/request/options/PictureDescriptionLocalTests.java +++ b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/request/options/PictureDescriptionLocalTests.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.options; +package ai.docling.api.serve.convert.request.options; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; diff --git a/docling-api/src/test/java/ai/docling/api/convert/request/source/FileSourceTests.java b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/request/source/FileSourceTests.java similarity index 96% rename from docling-api/src/test/java/ai/docling/api/convert/request/source/FileSourceTests.java rename to docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/request/source/FileSourceTests.java index b560cba..46da35a 100644 --- a/docling-api/src/test/java/ai/docling/api/convert/request/source/FileSourceTests.java +++ b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/request/source/FileSourceTests.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.source; +package ai.docling.api.serve.convert.request.source; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; diff --git a/docling-api/src/test/java/ai/docling/api/convert/request/source/HttpSourceTests.java b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/request/source/HttpSourceTests.java similarity index 97% rename from docling-api/src/test/java/ai/docling/api/convert/request/source/HttpSourceTests.java rename to docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/request/source/HttpSourceTests.java index 3921d1b..8acb156 100644 --- a/docling-api/src/test/java/ai/docling/api/convert/request/source/HttpSourceTests.java +++ b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/request/source/HttpSourceTests.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.source; +package ai.docling.api.serve.convert.request.source; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; diff --git a/docling-api/src/test/java/ai/docling/api/convert/request/target/PutTargetTests.java b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/request/target/PutTargetTests.java similarity index 93% rename from docling-api/src/test/java/ai/docling/api/convert/request/target/PutTargetTests.java rename to docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/request/target/PutTargetTests.java index b26ff81..4be8386 100644 --- a/docling-api/src/test/java/ai/docling/api/convert/request/target/PutTargetTests.java +++ b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/request/target/PutTargetTests.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.request.target; +package ai.docling.api.serve.convert.request.target; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; diff --git a/docling-api/src/test/java/ai/docling/api/convert/response/ConvertDocumentResponseTests.java b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/response/ConvertDocumentResponseTests.java similarity index 98% rename from docling-api/src/test/java/ai/docling/api/convert/response/ConvertDocumentResponseTests.java rename to docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/response/ConvertDocumentResponseTests.java index 3954c23..4fb5e99 100644 --- a/docling-api/src/test/java/ai/docling/api/convert/response/ConvertDocumentResponseTests.java +++ b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/response/ConvertDocumentResponseTests.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.response; +package ai.docling.api.serve.convert.response; import static org.assertj.core.api.Assertions.assertThat; diff --git a/docling-api/src/test/java/ai/docling/api/convert/response/DocumentResponseTests.java b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/response/DocumentResponseTests.java similarity index 98% rename from docling-api/src/test/java/ai/docling/api/convert/response/DocumentResponseTests.java rename to docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/response/DocumentResponseTests.java index fc5f250..60a7c87 100644 --- a/docling-api/src/test/java/ai/docling/api/convert/response/DocumentResponseTests.java +++ b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/response/DocumentResponseTests.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.response; +package ai.docling.api.serve.convert.response; import static org.assertj.core.api.Assertions.assertThat; diff --git a/docling-api/src/test/java/ai/docling/api/convert/response/ErrorItemTests.java b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/response/ErrorItemTests.java similarity index 96% rename from docling-api/src/test/java/ai/docling/api/convert/response/ErrorItemTests.java rename to docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/response/ErrorItemTests.java index 7ff7d6e..db9d838 100644 --- a/docling-api/src/test/java/ai/docling/api/convert/response/ErrorItemTests.java +++ b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/convert/response/ErrorItemTests.java @@ -1,4 +1,4 @@ -package ai.docling.api.convert.response; +package ai.docling.api.serve.convert.response; import static org.assertj.core.api.Assertions.assertThat; diff --git a/docling-api/src/test/java/ai/docling/api/health/HealthCheckResponseTests.java b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/health/HealthCheckResponseTests.java similarity index 96% rename from docling-api/src/test/java/ai/docling/api/health/HealthCheckResponseTests.java rename to docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/health/HealthCheckResponseTests.java index 4619737..4f8bb38 100644 --- a/docling-api/src/test/java/ai/docling/api/health/HealthCheckResponseTests.java +++ b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/health/HealthCheckResponseTests.java @@ -1,4 +1,4 @@ -package ai.docling.api.health; +package ai.docling.api.serve.health; import static org.assertj.core.api.Assertions.assertThat; diff --git a/docling-api/src/test/java/ai/docling/api/util/UtilsTests.java b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/util/UtilsTests.java similarity index 99% rename from docling-api/src/test/java/ai/docling/api/util/UtilsTests.java rename to docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/util/UtilsTests.java index 972adf2..6ac5255 100644 --- a/docling-api/src/test/java/ai/docling/api/util/UtilsTests.java +++ b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/util/UtilsTests.java @@ -1,4 +1,4 @@ -package ai.docling.api.util; +package ai.docling.api.serve.util; import static java.util.Collections.emptyList; import static org.assertj.core.api.Assertions.assertThat; diff --git a/docling-api/src/test/java/ai/docling/api/util/ValidationUtilsTests.java b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/util/ValidationUtilsTests.java similarity index 96% rename from docling-api/src/test/java/ai/docling/api/util/ValidationUtilsTests.java rename to docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/util/ValidationUtilsTests.java index 52de035..6697a6f 100644 --- a/docling-api/src/test/java/ai/docling/api/util/ValidationUtilsTests.java +++ b/docling-serve/docling-serve-api/src/test/java/ai/docling/api/serve/util/ValidationUtilsTests.java @@ -1,9 +1,9 @@ -package ai.docling.api.util; +package ai.docling.api.serve.util; -import static ai.docling.api.util.ValidationUtils.ensureBetween; -import static ai.docling.api.util.ValidationUtils.ensureEq; -import static ai.docling.api.util.ValidationUtils.ensureGreaterThanZero; -import static ai.docling.api.util.ValidationUtils.ensureNotNegative; +import static ai.docling.api.serve.util.ValidationUtils.ensureBetween; +import static ai.docling.api.serve.util.ValidationUtils.ensureEq; +import static ai.docling.api.serve.util.ValidationUtils.ensureGreaterThanZero; +import static ai.docling.api.serve.util.ValidationUtils.ensureNotNegative; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatThrownBy; diff --git a/docling-serve-client/build.gradle.kts b/docling-serve/docling-serve-client/build.gradle.kts similarity index 90% rename from docling-serve-client/build.gradle.kts rename to docling-serve/docling-serve-client/build.gradle.kts index fa84ec0..6c158b0 100644 --- a/docling-serve-client/build.gradle.kts +++ b/docling-serve/docling-serve-client/build.gradle.kts @@ -3,7 +3,7 @@ plugins { } dependencies { - api(project(":docling-api")) + api(project(":docling-serve-api")) implementation(platform(libs.jackson.bom)) implementation(libs.jackson.databind) implementation(libs.jackson2.databind) diff --git a/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeClient.java b/docling-serve/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeClient.java similarity index 95% rename from docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeClient.java rename to docling-serve/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeClient.java index 2ecd663..4f4ea5f 100644 --- a/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeClient.java +++ b/docling-serve/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeClient.java @@ -1,7 +1,7 @@ package ai.docling.client.serve; -import static ai.docling.api.util.ValidationUtils.ensureNotBlank; -import static ai.docling.api.util.ValidationUtils.ensureNotNull; +import static ai.docling.api.serve.util.ValidationUtils.ensureNotBlank; +import static ai.docling.api.serve.util.ValidationUtils.ensureNotNull; import java.net.URI; import java.net.http.HttpClient; @@ -17,10 +17,10 @@ import java.util.logging.Level; import java.util.logging.Logger; -import ai.docling.api.DoclingApi; -import ai.docling.api.convert.request.ConvertDocumentRequest; -import ai.docling.api.convert.response.ConvertDocumentResponse; -import ai.docling.api.health.HealthCheckResponse; +import ai.docling.api.serve.DoclingServeApi; +import ai.docling.api.serve.convert.request.ConvertDocumentRequest; +import ai.docling.api.serve.convert.response.ConvertDocumentResponse; +import ai.docling.api.serve.health.HealthCheckResponse; /** * Abstract class representing a client for interacting with the Docling API. @@ -33,7 +33,7 @@ *

Concrete subclasses must implement {@link #readValue(String, Class)} and * {@link #writeValueAsString(Object)} for serialization and deserialization behavior. */ -public abstract class DoclingServeClient implements DoclingApi { +public abstract class DoclingServeClient implements DoclingServeApi { private static final Logger LOG = Logger.getLogger(DoclingServeClient.class.getName()); protected static final URI DEFAULT_BASE_URL = URI.create("http://localhost:5001"); diff --git a/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeClientBuilderFactory.java b/docling-serve/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeClientBuilderFactory.java similarity index 100% rename from docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeClientBuilderFactory.java rename to docling-serve/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeClientBuilderFactory.java diff --git a/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeJackson2Client.java b/docling-serve/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeJackson2Client.java similarity index 98% rename from docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeJackson2Client.java rename to docling-serve/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeJackson2Client.java index 96f76b6..1682dc7 100644 --- a/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeJackson2Client.java +++ b/docling-serve/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeJackson2Client.java @@ -1,6 +1,6 @@ package ai.docling.client.serve; -import static ai.docling.api.util.ValidationUtils.ensureNotNull; +import static ai.docling.api.serve.util.ValidationUtils.ensureNotNull; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.json.JsonMapper; diff --git a/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeJackson3Client.java b/docling-serve/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeJackson3Client.java similarity index 98% rename from docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeJackson3Client.java rename to docling-serve/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeJackson3Client.java index ae70f1f..d20c827 100644 --- a/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeJackson3Client.java +++ b/docling-serve/docling-serve-client/src/main/java/ai/docling/client/serve/DoclingServeJackson3Client.java @@ -1,6 +1,6 @@ package ai.docling.client.serve; -import static ai.docling.api.util.ValidationUtils.ensureNotNull; +import static ai.docling.api.serve.util.ValidationUtils.ensureNotNull; import tools.jackson.databind.json.JsonMapper; diff --git a/docling-serve-client/src/main/java/ai/docling/client/serve/package-info.java b/docling-serve/docling-serve-client/src/main/java/ai/docling/client/serve/package-info.java similarity index 100% rename from docling-serve-client/src/main/java/ai/docling/client/serve/package-info.java rename to docling-serve/docling-serve-client/src/main/java/ai/docling/client/serve/package-info.java diff --git a/docling-serve-client/src/test/java/ai/docling/client/serve/AbstractDoclingServeClientTests.java b/docling-serve/docling-serve-client/src/test/java/ai/docling/client/serve/AbstractDoclingServeClientTests.java similarity index 87% rename from docling-serve-client/src/test/java/ai/docling/client/serve/AbstractDoclingServeClientTests.java rename to docling-serve/docling-serve-client/src/test/java/ai/docling/client/serve/AbstractDoclingServeClientTests.java index 182e84b..d009c33 100644 --- a/docling-serve-client/src/test/java/ai/docling/client/serve/AbstractDoclingServeClientTests.java +++ b/docling-serve/docling-serve-client/src/test/java/ai/docling/client/serve/AbstractDoclingServeClientTests.java @@ -11,14 +11,14 @@ import org.junit.jupiter.api.Test; -import ai.docling.api.DoclingApi; -import ai.docling.api.convert.request.ConvertDocumentRequest; -import ai.docling.api.convert.request.options.ConvertDocumentOptions; -import ai.docling.api.convert.request.options.TableFormerMode; -import ai.docling.api.convert.request.source.FileSource; -import ai.docling.api.convert.request.source.HttpSource; -import ai.docling.api.convert.response.ConvertDocumentResponse; -import ai.docling.api.health.HealthCheckResponse; +import ai.docling.api.serve.DoclingServeApi; +import ai.docling.api.serve.convert.request.ConvertDocumentRequest; +import ai.docling.api.serve.convert.request.options.ConvertDocumentOptions; +import ai.docling.api.serve.convert.request.options.TableFormerMode; +import ai.docling.api.serve.convert.request.source.FileSource; +import ai.docling.api.serve.convert.request.source.HttpSource; +import ai.docling.api.serve.convert.response.ConvertDocumentResponse; +import ai.docling.api.serve.health.HealthCheckResponse; import ai.docling.testcontainers.serve.DoclingServeContainer; import ai.docling.testcontainers.serve.config.DoclingServeContainerConfig; @@ -35,7 +35,7 @@ abstract class AbstractDoclingServeClientTests { doclingContainer.start(); } - protected abstract DoclingApi getDoclingClient(); + protected abstract DoclingServeApi getDoclingClient(); @Test void shouldSuccessfullyCallHealthEndpoint() { diff --git a/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeClientBuilderFactoryTests.java b/docling-serve/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeClientBuilderFactoryTests.java similarity index 100% rename from docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeClientBuilderFactoryTests.java rename to docling-serve/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeClientBuilderFactoryTests.java diff --git a/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson2ClientTests.java b/docling-serve/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson2ClientTests.java similarity index 82% rename from docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson2ClientTests.java rename to docling-serve/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson2ClientTests.java index 7393d85..157018a 100644 --- a/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson2ClientTests.java +++ b/docling-serve/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson2ClientTests.java @@ -2,14 +2,14 @@ import org.junit.jupiter.api.BeforeAll; -import ai.docling.api.DoclingApi; +import ai.docling.api.serve.DoclingServeApi; import ai.docling.testcontainers.serve.config.DoclingServeContainerConfig; /** * Integration tests for {@link DoclingServeJackson2Client}. */ class DoclingServeJackson2ClientTests extends AbstractDoclingServeClientTests { - private static DoclingApi doclingClient; + private static DoclingServeApi doclingClient; @BeforeAll static void setUp() { @@ -21,7 +21,7 @@ static void setUp() { } @Override - protected DoclingApi getDoclingClient() { + protected DoclingServeApi getDoclingClient() { return doclingClient; } } diff --git a/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson3ClientTests.java b/docling-serve/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson3ClientTests.java similarity index 81% rename from docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson3ClientTests.java rename to docling-serve/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson3ClientTests.java index 3377461..19be7fc 100644 --- a/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson3ClientTests.java +++ b/docling-serve/docling-serve-client/src/test/java/ai/docling/client/serve/DoclingServeJackson3ClientTests.java @@ -2,14 +2,14 @@ import org.junit.jupiter.api.BeforeAll; -import ai.docling.api.DoclingApi; +import ai.docling.api.serve.DoclingServeApi; import ai.docling.testcontainers.serve.config.DoclingServeContainerConfig; /** * Integration tests for {@link DoclingServeClient}. */ class DoclingServeJackson3ClientTests extends AbstractDoclingServeClientTests { - private static DoclingApi doclingClient; + private static DoclingServeApi doclingClient; @BeforeAll static void setUp() { @@ -21,7 +21,7 @@ static void setUp() { } @Override - protected DoclingApi getDoclingClient() { + protected DoclingServeApi getDoclingClient() { return doclingClient; } } diff --git a/docling-serve-client/src/test/resources/story.pdf b/docling-serve/docling-serve-client/src/test/resources/story.pdf similarity index 100% rename from docling-serve-client/src/test/resources/story.pdf rename to docling-serve/docling-serve-client/src/test/resources/story.pdf diff --git a/docs/build.gradle.kts b/docs/build.gradle.kts index 5765e40..c4ffb9b 100644 --- a/docs/build.gradle.kts +++ b/docs/build.gradle.kts @@ -22,7 +22,7 @@ mkdocs { "project-version" to "${project.version}", "project-groupId" to "${project.group}", "project-artifactId" to "${rootProject.name}", - "api-artifactId" to project(":docling-api").name, + "api-artifactId" to project(":docling-serve-api").name, "serve-client-artifactId" to project(":docling-serve-client").name, "testing-artifactId" to project(":docling-testing").name, "testcontainers-artifactId" to project(":docling-testcontainers").name diff --git a/docs/src/doc/docs/api.md b/docs/src/doc/docs/api.md deleted file mode 100644 index 5932792..0000000 --- a/docs/src/doc/docs/api.md +++ /dev/null @@ -1 +0,0 @@ -# API diff --git a/docs/src/doc/docs/docling-serve/serve-api.md b/docs/src/doc/docs/docling-serve/serve-api.md new file mode 100644 index 0000000..c4ba30a --- /dev/null +++ b/docs/src/doc/docs/docling-serve/serve-api.md @@ -0,0 +1 @@ +# Docling Serve API diff --git a/docs/src/doc/docs/serve-client.md b/docs/src/doc/docs/docling-serve/serve-client.md similarity index 100% rename from docs/src/doc/docs/serve-client.md rename to docs/src/doc/docs/docling-serve/serve-client.md diff --git a/docs/src/doc/mkdocs.yml b/docs/src/doc/mkdocs.yml index 1654a90..1bf86a4 100644 --- a/docs/src/doc/mkdocs.yml +++ b/docs/src/doc/mkdocs.yml @@ -57,8 +57,9 @@ nav: - Getting started: - Getting started: getting-started.md - Artifacts: - - API: api.md - - Serve Client: serve-client.md + - Docling Serve: + - API: docling-serve/serve-api.md + - Client: docling-serve/serve-client.md - Testing: testing.md - Testcontainers: testcontainers.md diff --git a/settings.gradle.kts b/settings.gradle.kts index 8c306e3..02b46df 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -4,4 +4,7 @@ plugins { } rootProject.name = "docling-java" -include("docling-api", "docling-serve-client", "docs", "docling-testing", "docling-testcontainers") +include("docling-serve-api", "docling-serve-client", "docs", "docling-testing", "docling-testcontainers") + +project(":docling-serve-api").projectDir = file("docling-serve/docling-serve-api") +project(":docling-serve-client").projectDir = file("docling-serve/docling-serve-client") From db96e0737717159509cef35f8eeffb3510006bec Mon Sep 17 00:00:00 2001 From: Eric Deandrea Date: Mon, 3 Nov 2025 08:16:50 -0500 Subject: [PATCH 13/13] refactor docling-api into docling-serve-api --- docs/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/build.gradle.kts b/docs/build.gradle.kts index c4ffb9b..3029aee 100644 --- a/docs/build.gradle.kts +++ b/docs/build.gradle.kts @@ -22,7 +22,7 @@ mkdocs { "project-version" to "${project.version}", "project-groupId" to "${project.group}", "project-artifactId" to "${rootProject.name}", - "api-artifactId" to project(":docling-serve-api").name, + "serve-api-artifactId" to project(":docling-serve-api").name, "serve-client-artifactId" to project(":docling-serve-client").name, "testing-artifactId" to project(":docling-testing").name, "testcontainers-artifactId" to project(":docling-testcontainers").name