This is an automated email from the ASF dual-hosted git repository. acosentino pushed a commit to branch CAMEL-22547 in repository https://gitbox.apache.org/repos/asf/camel.git
commit 1b6130bfcdb1e7b1dcaa7eaf4363bcaf33e51fa3 Author: Andrea Cosentino <[email protected]> AuthorDate: Wed Oct 15 09:21:03 2025 +0200 CAMEL-22547 - Camel-Docling: Support Async operation in docling-server Signed-off-by: Andrea Cosentino <[email protected]> --- .../docling/DoclingComponentConfigurer.java | 24 ++ .../docling/DoclingConfigurationConfigurer.java | 24 ++ .../docling/DoclingEndpointConfigurer.java | 24 ++ .../docling/DoclingEndpointUriFactory.java | 6 +- .../apache/camel/component/docling/docling.json | 48 ++-- .../src/main/docs/docling-component.adoc | 139 ++++++++++ .../camel/component/docling/ConversionStatus.java | 91 +++++++ .../component/docling/DoclingConfiguration.java | 48 ++++ .../camel/component/docling/DoclingHeaders.java | 12 + .../camel/component/docling/DoclingProducer.java | 42 ++- .../component/docling/DoclingServeClient.java | 292 ++++++++++++++++++++- .../component/docling/DoclingServeProducerIT.java | 108 ++++++++ 12 files changed, 829 insertions(+), 29 deletions(-) diff --git a/components/camel-ai/camel-docling/src/generated/java/org/apache/camel/component/docling/DoclingComponentConfigurer.java b/components/camel-ai/camel-docling/src/generated/java/org/apache/camel/component/docling/DoclingComponentConfigurer.java index 270856eb5ef8..8162ee261074 100644 --- a/components/camel-ai/camel-docling/src/generated/java/org/apache/camel/component/docling/DoclingComponentConfigurer.java +++ b/components/camel-ai/camel-docling/src/generated/java/org/apache/camel/component/docling/DoclingComponentConfigurer.java @@ -32,6 +32,12 @@ public class DoclingComponentConfigurer extends PropertyConfigurerSupport implem switch (ignoreCase ? name.toLowerCase() : name) { case "apikeyheader": case "apiKeyHeader": getOrCreateConfiguration(target).setApiKeyHeader(property(camelContext, java.lang.String.class, value)); return true; + case "apitimeout": + case "apiTimeout": getOrCreateConfiguration(target).setApiTimeout(property(camelContext, long.class, value)); return true; + case "asyncpollinterval": + case "asyncPollInterval": getOrCreateConfiguration(target).setAsyncPollInterval(property(camelContext, long.class, value)); return true; + case "asynctimeout": + case "asyncTimeout": getOrCreateConfiguration(target).setAsyncTimeout(property(camelContext, long.class, value)); return true; case "authenticationscheme": case "authenticationScheme": getOrCreateConfiguration(target).setAuthenticationScheme(property(camelContext, org.apache.camel.component.docling.AuthenticationScheme.class, value)); return true; case "authenticationtoken": @@ -62,6 +68,8 @@ public class DoclingComponentConfigurer extends PropertyConfigurerSupport implem case "outputFormat": getOrCreateConfiguration(target).setOutputFormat(property(camelContext, java.lang.String.class, value)); return true; case "processtimeout": case "processTimeout": getOrCreateConfiguration(target).setProcessTimeout(property(camelContext, long.class, value)); return true; + case "useasyncmode": + case "useAsyncMode": getOrCreateConfiguration(target).setUseAsyncMode(property(camelContext, boolean.class, value)); return true; case "usedoclingserve": case "useDoclingServe": getOrCreateConfiguration(target).setUseDoclingServe(property(camelContext, boolean.class, value)); return true; case "workingdirectory": @@ -75,6 +83,12 @@ public class DoclingComponentConfigurer extends PropertyConfigurerSupport implem switch (ignoreCase ? name.toLowerCase() : name) { case "apikeyheader": case "apiKeyHeader": return java.lang.String.class; + case "apitimeout": + case "apiTimeout": return long.class; + case "asyncpollinterval": + case "asyncPollInterval": return long.class; + case "asynctimeout": + case "asyncTimeout": return long.class; case "authenticationscheme": case "authenticationScheme": return org.apache.camel.component.docling.AuthenticationScheme.class; case "authenticationtoken": @@ -105,6 +119,8 @@ public class DoclingComponentConfigurer extends PropertyConfigurerSupport implem case "outputFormat": return java.lang.String.class; case "processtimeout": case "processTimeout": return long.class; + case "useasyncmode": + case "useAsyncMode": return boolean.class; case "usedoclingserve": case "useDoclingServe": return boolean.class; case "workingdirectory": @@ -119,6 +135,12 @@ public class DoclingComponentConfigurer extends PropertyConfigurerSupport implem switch (ignoreCase ? name.toLowerCase() : name) { case "apikeyheader": case "apiKeyHeader": return getOrCreateConfiguration(target).getApiKeyHeader(); + case "apitimeout": + case "apiTimeout": return getOrCreateConfiguration(target).getApiTimeout(); + case "asyncpollinterval": + case "asyncPollInterval": return getOrCreateConfiguration(target).getAsyncPollInterval(); + case "asynctimeout": + case "asyncTimeout": return getOrCreateConfiguration(target).getAsyncTimeout(); case "authenticationscheme": case "authenticationScheme": return getOrCreateConfiguration(target).getAuthenticationScheme(); case "authenticationtoken": @@ -149,6 +171,8 @@ public class DoclingComponentConfigurer extends PropertyConfigurerSupport implem case "outputFormat": return getOrCreateConfiguration(target).getOutputFormat(); case "processtimeout": case "processTimeout": return getOrCreateConfiguration(target).getProcessTimeout(); + case "useasyncmode": + case "useAsyncMode": return getOrCreateConfiguration(target).isUseAsyncMode(); case "usedoclingserve": case "useDoclingServe": return getOrCreateConfiguration(target).isUseDoclingServe(); case "workingdirectory": diff --git a/components/camel-ai/camel-docling/src/generated/java/org/apache/camel/component/docling/DoclingConfigurationConfigurer.java b/components/camel-ai/camel-docling/src/generated/java/org/apache/camel/component/docling/DoclingConfigurationConfigurer.java index 585af1165493..b9ad1b2c73f5 100644 --- a/components/camel-ai/camel-docling/src/generated/java/org/apache/camel/component/docling/DoclingConfigurationConfigurer.java +++ b/components/camel-ai/camel-docling/src/generated/java/org/apache/camel/component/docling/DoclingConfigurationConfigurer.java @@ -25,6 +25,12 @@ public class DoclingConfigurationConfigurer extends org.apache.camel.support.com switch (ignoreCase ? name.toLowerCase() : name) { case "apikeyheader": case "apiKeyHeader": target.setApiKeyHeader(property(camelContext, java.lang.String.class, value)); return true; + case "apitimeout": + case "apiTimeout": target.setApiTimeout(property(camelContext, long.class, value)); return true; + case "asyncpollinterval": + case "asyncPollInterval": target.setAsyncPollInterval(property(camelContext, long.class, value)); return true; + case "asynctimeout": + case "asyncTimeout": target.setAsyncTimeout(property(camelContext, long.class, value)); return true; case "authenticationscheme": case "authenticationScheme": target.setAuthenticationScheme(property(camelContext, org.apache.camel.component.docling.AuthenticationScheme.class, value)); return true; case "authenticationtoken": @@ -50,6 +56,8 @@ public class DoclingConfigurationConfigurer extends org.apache.camel.support.com case "outputFormat": target.setOutputFormat(property(camelContext, java.lang.String.class, value)); return true; case "processtimeout": case "processTimeout": target.setProcessTimeout(property(camelContext, long.class, value)); return true; + case "useasyncmode": + case "useAsyncMode": target.setUseAsyncMode(property(camelContext, boolean.class, value)); return true; case "usedoclingserve": case "useDoclingServe": target.setUseDoclingServe(property(camelContext, boolean.class, value)); return true; case "workingdirectory": @@ -63,6 +71,12 @@ public class DoclingConfigurationConfigurer extends org.apache.camel.support.com switch (ignoreCase ? name.toLowerCase() : name) { case "apikeyheader": case "apiKeyHeader": return java.lang.String.class; + case "apitimeout": + case "apiTimeout": return long.class; + case "asyncpollinterval": + case "asyncPollInterval": return long.class; + case "asynctimeout": + case "asyncTimeout": return long.class; case "authenticationscheme": case "authenticationScheme": return org.apache.camel.component.docling.AuthenticationScheme.class; case "authenticationtoken": @@ -88,6 +102,8 @@ public class DoclingConfigurationConfigurer extends org.apache.camel.support.com case "outputFormat": return java.lang.String.class; case "processtimeout": case "processTimeout": return long.class; + case "useasyncmode": + case "useAsyncMode": return boolean.class; case "usedoclingserve": case "useDoclingServe": return boolean.class; case "workingdirectory": @@ -102,6 +118,12 @@ public class DoclingConfigurationConfigurer extends org.apache.camel.support.com switch (ignoreCase ? name.toLowerCase() : name) { case "apikeyheader": case "apiKeyHeader": return target.getApiKeyHeader(); + case "apitimeout": + case "apiTimeout": return target.getApiTimeout(); + case "asyncpollinterval": + case "asyncPollInterval": return target.getAsyncPollInterval(); + case "asynctimeout": + case "asyncTimeout": return target.getAsyncTimeout(); case "authenticationscheme": case "authenticationScheme": return target.getAuthenticationScheme(); case "authenticationtoken": @@ -127,6 +149,8 @@ public class DoclingConfigurationConfigurer extends org.apache.camel.support.com case "outputFormat": return target.getOutputFormat(); case "processtimeout": case "processTimeout": return target.getProcessTimeout(); + case "useasyncmode": + case "useAsyncMode": return target.isUseAsyncMode(); case "usedoclingserve": case "useDoclingServe": return target.isUseDoclingServe(); case "workingdirectory": diff --git a/components/camel-ai/camel-docling/src/generated/java/org/apache/camel/component/docling/DoclingEndpointConfigurer.java b/components/camel-ai/camel-docling/src/generated/java/org/apache/camel/component/docling/DoclingEndpointConfigurer.java index 88e14d5862c2..633d25a55840 100644 --- a/components/camel-ai/camel-docling/src/generated/java/org/apache/camel/component/docling/DoclingEndpointConfigurer.java +++ b/components/camel-ai/camel-docling/src/generated/java/org/apache/camel/component/docling/DoclingEndpointConfigurer.java @@ -25,6 +25,12 @@ public class DoclingEndpointConfigurer extends PropertyConfigurerSupport impleme switch (ignoreCase ? name.toLowerCase() : name) { case "apikeyheader": case "apiKeyHeader": target.getConfiguration().setApiKeyHeader(property(camelContext, java.lang.String.class, value)); return true; + case "apitimeout": + case "apiTimeout": target.getConfiguration().setApiTimeout(property(camelContext, long.class, value)); return true; + case "asyncpollinterval": + case "asyncPollInterval": target.getConfiguration().setAsyncPollInterval(property(camelContext, long.class, value)); return true; + case "asynctimeout": + case "asyncTimeout": target.getConfiguration().setAsyncTimeout(property(camelContext, long.class, value)); return true; case "authenticationscheme": case "authenticationScheme": target.getConfiguration().setAuthenticationScheme(property(camelContext, org.apache.camel.component.docling.AuthenticationScheme.class, value)); return true; case "authenticationtoken": @@ -52,6 +58,8 @@ public class DoclingEndpointConfigurer extends PropertyConfigurerSupport impleme case "outputFormat": target.getConfiguration().setOutputFormat(property(camelContext, java.lang.String.class, value)); return true; case "processtimeout": case "processTimeout": target.getConfiguration().setProcessTimeout(property(camelContext, long.class, value)); return true; + case "useasyncmode": + case "useAsyncMode": target.getConfiguration().setUseAsyncMode(property(camelContext, boolean.class, value)); return true; case "usedoclingserve": case "useDoclingServe": target.getConfiguration().setUseDoclingServe(property(camelContext, boolean.class, value)); return true; case "workingdirectory": @@ -65,6 +73,12 @@ public class DoclingEndpointConfigurer extends PropertyConfigurerSupport impleme switch (ignoreCase ? name.toLowerCase() : name) { case "apikeyheader": case "apiKeyHeader": return java.lang.String.class; + case "apitimeout": + case "apiTimeout": return long.class; + case "asyncpollinterval": + case "asyncPollInterval": return long.class; + case "asynctimeout": + case "asyncTimeout": return long.class; case "authenticationscheme": case "authenticationScheme": return org.apache.camel.component.docling.AuthenticationScheme.class; case "authenticationtoken": @@ -92,6 +106,8 @@ public class DoclingEndpointConfigurer extends PropertyConfigurerSupport impleme case "outputFormat": return java.lang.String.class; case "processtimeout": case "processTimeout": return long.class; + case "useasyncmode": + case "useAsyncMode": return boolean.class; case "usedoclingserve": case "useDoclingServe": return boolean.class; case "workingdirectory": @@ -106,6 +122,12 @@ public class DoclingEndpointConfigurer extends PropertyConfigurerSupport impleme switch (ignoreCase ? name.toLowerCase() : name) { case "apikeyheader": case "apiKeyHeader": return target.getConfiguration().getApiKeyHeader(); + case "apitimeout": + case "apiTimeout": return target.getConfiguration().getApiTimeout(); + case "asyncpollinterval": + case "asyncPollInterval": return target.getConfiguration().getAsyncPollInterval(); + case "asynctimeout": + case "asyncTimeout": return target.getConfiguration().getAsyncTimeout(); case "authenticationscheme": case "authenticationScheme": return target.getConfiguration().getAuthenticationScheme(); case "authenticationtoken": @@ -133,6 +155,8 @@ public class DoclingEndpointConfigurer extends PropertyConfigurerSupport impleme case "outputFormat": return target.getConfiguration().getOutputFormat(); case "processtimeout": case "processTimeout": return target.getConfiguration().getProcessTimeout(); + case "useasyncmode": + case "useAsyncMode": return target.getConfiguration().isUseAsyncMode(); case "usedoclingserve": case "useDoclingServe": return target.getConfiguration().isUseDoclingServe(); case "workingdirectory": diff --git a/components/camel-ai/camel-docling/src/generated/java/org/apache/camel/component/docling/DoclingEndpointUriFactory.java b/components/camel-ai/camel-docling/src/generated/java/org/apache/camel/component/docling/DoclingEndpointUriFactory.java index 257c975eafa8..7ab76504c522 100644 --- a/components/camel-ai/camel-docling/src/generated/java/org/apache/camel/component/docling/DoclingEndpointUriFactory.java +++ b/components/camel-ai/camel-docling/src/generated/java/org/apache/camel/component/docling/DoclingEndpointUriFactory.java @@ -23,8 +23,11 @@ public class DoclingEndpointUriFactory extends org.apache.camel.support.componen private static final Set<String> SECRET_PROPERTY_NAMES; private static final Map<String, String> MULTI_VALUE_PREFIXES; static { - Set<String> props = new HashSet<>(18); + Set<String> props = new HashSet<>(22); props.add("apiKeyHeader"); + props.add("apiTimeout"); + props.add("asyncPollInterval"); + props.add("asyncTimeout"); props.add("authenticationScheme"); props.add("authenticationToken"); props.add("contentInBody"); @@ -40,6 +43,7 @@ public class DoclingEndpointUriFactory extends org.apache.camel.support.componen props.add("operationId"); props.add("outputFormat"); props.add("processTimeout"); + props.add("useAsyncMode"); props.add("useDoclingServe"); props.add("workingDirectory"); PROPERTY_NAMES = Collections.unmodifiableSet(props); diff --git a/components/camel-ai/camel-docling/src/generated/resources/META-INF/org/apache/camel/component/docling/docling.json b/components/camel-ai/camel-docling/src/generated/resources/META-INF/org/apache/camel/component/docling/docling.json index c04a0fe85843..f402df50e1e7 100644 --- a/components/camel-ai/camel-docling/src/generated/resources/META-INF/org/apache/camel/component/docling/docling.json +++ b/components/camel-ai/camel-docling/src/generated/resources/META-INF/org/apache/camel/component/docling/docling.json @@ -34,15 +34,19 @@ "operation": { "index": 7, "kind": "property", "displayName": "Operation", "group": "producer", "label": "", "required": true, "type": "enum", "javaType": "org.apache.camel.component.docling.DoclingOperations", "enum": [ "CONVERT_TO_MARKDOWN", "CONVERT_TO_HTML", "CONVERT_TO_JSON", "EXTRACT_TEXT", "EXTRACT_STRUCTURED_DATA" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "CONVERT_TO_MARKDOWN", "configurationClass": "org.apache.camel.c [...] "outputFormat": { "index": 8, "kind": "property", "displayName": "Output Format", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "markdown", "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Output format for document conversion" }, "useDoclingServe": { "index": 9, "kind": "property", "displayName": "Use Docling Serve", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Use docling-serve API instead of CLI command" }, - "autowiredEnabled": { "index": 10, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching [...] - "convertEndpoint": { "index": 11, "kind": "property", "displayName": "Convert Endpoint", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "\/v1\/convert\/source", "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Docling-serve API convert endp [...] - "doclingCommand": { "index": 12, "kind": "property", "displayName": "Docling Command", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Path to Docling Python executable or command" }, - "processTimeout": { "index": 13, "kind": "property", "displayName": "Process Timeout", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 30000, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Timeout for Docling process execution in milliseconds" }, - "workingDirectory": { "index": 14, "kind": "property", "displayName": "Working Directory", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Working directory for Docling execution" }, - "apiKeyHeader": { "index": 15, "kind": "property", "displayName": "Api Key Header", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "X-API-Key", "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Header name for API key authentication" }, - "authenticationScheme": { "index": 16, "kind": "property", "displayName": "Authentication Scheme", "group": "security", "label": "security", "required": false, "type": "enum", "javaType": "org.apache.camel.component.docling.AuthenticationScheme", "enum": [ "NONE", "BEARER", "API_KEY" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "NONE", "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configuration [...] - "authenticationToken": { "index": 17, "kind": "property", "displayName": "Authentication Token", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": true, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Authentication token for docling-serve API (Bearer token or API [...] - "maxFileSize": { "index": 18, "kind": "property", "displayName": "Max File Size", "group": "security", "label": "security", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 52428800, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Maximum file size in bytes for processing" } + "apiTimeout": { "index": 10, "kind": "property", "displayName": "Api Timeout", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 60000, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "API request timeout in milliseconds" }, + "asyncPollInterval": { "index": 11, "kind": "property", "displayName": "Async Poll Interval", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 2000, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Polling interval for async conversion status in millise [...] + "asyncTimeout": { "index": 12, "kind": "property", "displayName": "Async Timeout", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 300000, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Maximum time to wait for async conversion completion in millisec [...] + "autowiredEnabled": { "index": 13, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching [...] + "convertEndpoint": { "index": 14, "kind": "property", "displayName": "Convert Endpoint", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "\/v1\/convert\/source", "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Docling-serve API convert endp [...] + "doclingCommand": { "index": 15, "kind": "property", "displayName": "Docling Command", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Path to Docling Python executable or command" }, + "processTimeout": { "index": 16, "kind": "property", "displayName": "Process Timeout", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 30000, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Timeout for Docling process execution in milliseconds" }, + "useAsyncMode": { "index": 17, "kind": "property", "displayName": "Use Async Mode", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Use asynchronous conversion mode (docling-serve API only)" }, + "workingDirectory": { "index": 18, "kind": "property", "displayName": "Working Directory", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Working directory for Docling execution" }, + "apiKeyHeader": { "index": 19, "kind": "property", "displayName": "Api Key Header", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "X-API-Key", "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Header name for API key authentication" }, + "authenticationScheme": { "index": 20, "kind": "property", "displayName": "Authentication Scheme", "group": "security", "label": "security", "required": false, "type": "enum", "javaType": "org.apache.camel.component.docling.AuthenticationScheme", "enum": [ "NONE", "BEARER", "API_KEY" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "NONE", "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configuration [...] + "authenticationToken": { "index": 21, "kind": "property", "displayName": "Authentication Token", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": true, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Authentication token for docling-serve API (Bearer token or API [...] + "maxFileSize": { "index": 22, "kind": "property", "displayName": "Max File Size", "group": "security", "label": "security", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 52428800, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Maximum file size in bytes for processing" } }, "headers": { "CamelDoclingOperation": { "index": 0, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "DoclingOperations", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The operation to perform", "constantName": "org.apache.camel.component.docling.DoclingHeaders#OPERATION" }, @@ -52,7 +56,11 @@ "CamelDoclingProcessingOptions": { "index": 4, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Map<String, Object>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Additional processing options", "constantName": "org.apache.camel.component.docling.DoclingHeaders#PROCESSING_OPTIONS" }, "CamelDoclingEnableOCR": { "index": 5, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Whether to include OCR processing", "constantName": "org.apache.camel.component.docling.DoclingHeaders#ENABLE_OCR" }, "CamelDoclingOCRLanguage": { "index": 6, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Language for OCR processing", "constantName": "org.apache.camel.component.docling.DoclingHeaders#OCR_LANGUAGE" }, - "CamelDoclingCustomArguments": { "index": 7, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "List<String>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Custom command line arguments to pass to Docling", "constantName": "org.apache.camel.component.docling.DoclingHeaders#CUSTOM_ARGUMENTS" } + "CamelDoclingCustomArguments": { "index": 7, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "List<String>", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Custom command line arguments to pass to Docling", "constantName": "org.apache.camel.component.docling.DoclingHeaders#CUSTOM_ARGUMENTS" }, + "CamelDoclingUseAsyncMode": { "index": 8, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Use asynchronous conversion mode (overrides endpoint configuration)", "constantName": "org.apache.camel.component.docling.DoclingHeaders#USE_ASYNC_MODE" }, + "CamelDoclingAsyncPollInterval": { "index": 9, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Polling interval for async conversion status in milliseconds", "constantName": "org.apache.camel.component.docling.DoclingHeaders#ASYNC_POLL_INTERVAL" }, + "CamelDoclingAsyncTimeout": { "index": 10, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Maximum time to wait for async conversion completion in milliseconds", "constantName": "org.apache.camel.component.docling.DoclingHeaders#ASYNC_TIMEOUT" }, + "CamelDoclingTaskId": { "index": 11, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Task ID for checking async conversion status", "constantName": "org.apache.camel.component.docling.DoclingHeaders#TASK_ID" } }, "properties": { "operationId": { "index": 0, "kind": "path", "displayName": "Operation Id", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The operation identifier" }, @@ -65,13 +73,17 @@ "outputFormat": { "index": 7, "kind": "parameter", "displayName": "Output Format", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "markdown", "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Output format for document conversion" }, "useDoclingServe": { "index": 8, "kind": "parameter", "displayName": "Use Docling Serve", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Use docling-serve API instead of CLI command" }, "lazyStartProducer": { "index": 9, "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a produc [...] - "convertEndpoint": { "index": 10, "kind": "parameter", "displayName": "Convert Endpoint", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "\/v1\/convert\/source", "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Docling-serve API convert end [...] - "doclingCommand": { "index": 11, "kind": "parameter", "displayName": "Docling Command", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Path to Docling Python executable or command" }, - "processTimeout": { "index": 12, "kind": "parameter", "displayName": "Process Timeout", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 30000, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Timeout for Docling process execution in milliseconds" }, - "workingDirectory": { "index": 13, "kind": "parameter", "displayName": "Working Directory", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Working directory for Docling execution" }, - "apiKeyHeader": { "index": 14, "kind": "parameter", "displayName": "Api Key Header", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "X-API-Key", "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Header name for API key authentication" }, - "authenticationScheme": { "index": 15, "kind": "parameter", "displayName": "Authentication Scheme", "group": "security", "label": "security", "required": false, "type": "enum", "javaType": "org.apache.camel.component.docling.AuthenticationScheme", "enum": [ "NONE", "BEARER", "API_KEY" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "NONE", "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configuratio [...] - "authenticationToken": { "index": 16, "kind": "parameter", "displayName": "Authentication Token", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": true, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Authentication token for docling-serve API (Bearer token or API [...] - "maxFileSize": { "index": 17, "kind": "parameter", "displayName": "Max File Size", "group": "security", "label": "security", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 52428800, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Maximum file size in bytes for processing" } + "apiTimeout": { "index": 10, "kind": "parameter", "displayName": "Api Timeout", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 60000, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "API request timeout in milliseconds" }, + "asyncPollInterval": { "index": 11, "kind": "parameter", "displayName": "Async Poll Interval", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 2000, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Polling interval for async conversion status in millis [...] + "asyncTimeout": { "index": 12, "kind": "parameter", "displayName": "Async Timeout", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 300000, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Maximum time to wait for async conversion completion in millise [...] + "convertEndpoint": { "index": 13, "kind": "parameter", "displayName": "Convert Endpoint", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "\/v1\/convert\/source", "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Docling-serve API convert end [...] + "doclingCommand": { "index": 14, "kind": "parameter", "displayName": "Docling Command", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Path to Docling Python executable or command" }, + "processTimeout": { "index": 15, "kind": "parameter", "displayName": "Process Timeout", "group": "advanced", "label": "advanced", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 30000, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Timeout for Docling process execution in milliseconds" }, + "useAsyncMode": { "index": 16, "kind": "parameter", "displayName": "Use Async Mode", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": false, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Use asynchronous conversion mode (docling-serve API only)" }, + "workingDirectory": { "index": 17, "kind": "parameter", "displayName": "Working Directory", "group": "advanced", "label": "advanced", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Working directory for Docling execution" }, + "apiKeyHeader": { "index": 18, "kind": "parameter", "displayName": "Api Key Header", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "X-API-Key", "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Header name for API key authentication" }, + "authenticationScheme": { "index": 19, "kind": "parameter", "displayName": "Authentication Scheme", "group": "security", "label": "security", "required": false, "type": "enum", "javaType": "org.apache.camel.component.docling.AuthenticationScheme", "enum": [ "NONE", "BEARER", "API_KEY" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": "NONE", "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configuratio [...] + "authenticationToken": { "index": 20, "kind": "parameter", "displayName": "Authentication Token", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": true, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Authentication token for docling-serve API (Bearer token or API [...] + "maxFileSize": { "index": 21, "kind": "parameter", "displayName": "Max File Size", "group": "security", "label": "security", "required": false, "type": "integer", "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "defaultValue": 52428800, "configurationClass": "org.apache.camel.component.docling.DoclingConfiguration", "configurationField": "configuration", "description": "Maximum file size in bytes for processing" } } } diff --git a/components/camel-ai/camel-docling/src/main/docs/docling-component.adoc b/components/camel-ai/camel-docling/src/main/docs/docling-component.adoc index fb2a9d4cb4a9..f2121f71cece 100644 --- a/components/camel-ai/camel-docling/src/main/docs/docling-component.adoc +++ b/components/camel-ai/camel-docling/src/main/docs/docling-component.adoc @@ -459,6 +459,142 @@ public class CustomArgsBean { } ---- +== Asynchronous Processing + +The component supports asynchronous document conversion when using docling-serve API mode. This is particularly useful for: +- Large documents that take a long time to process +- High-volume batch processing scenarios +- Better resource utilization on the server side + +=== Enabling Async Mode + +[tabs] +==== +Java:: ++ +[source,java] +---- +from("file:///data/documents?include=.*\\.pdf") + .to("docling:CONVERT_TO_MARKDOWN?" + + "useDoclingServe=true&" + + "useAsyncMode=true&" + + "asyncPollInterval=2000&" + + "asyncTimeout=300000&" + + "contentInBody=true") + .to("file:///data/output"); +---- + +YAML:: ++ +[source,yaml] +---- +- route: + from: + uri: "file:///data/documents" + parameters: + include: ".*\\.pdf" + steps: + - to: + uri: "docling:CONVERT_TO_MARKDOWN" + parameters: + useDoclingServe: true + useAsyncMode: true + asyncPollInterval: 2000 + asyncTimeout: 300000 + contentInBody: true + - to: + uri: "file:///data/output" +---- +==== + +=== Async Processing with Custom Timeout + +For very large documents, you may need to increase the timeout: + +[tabs] +==== +Java:: ++ +[source,java] +---- +from("file:///data/large-documents?include=.*\\.pdf") + .to("docling:CONVERT_TO_MARKDOWN?" + + "useDoclingServe=true&" + + "useAsyncMode=true&" + + "asyncPollInterval=5000&" + + "asyncTimeout=600000&" + // 10 minutes + "contentInBody=true") + .to("file:///data/output"); +---- + +YAML:: ++ +[source,yaml] +---- +- route: + from: + uri: "file:///data/large-documents" + parameters: + include: ".*\\.pdf" + steps: + - to: + uri: "docling:CONVERT_TO_MARKDOWN" + parameters: + useDoclingServe: true + useAsyncMode: true + asyncPollInterval: 5000 + asyncTimeout: 600000 + contentInBody: true + - to: + uri: "file:///data/output" +---- +==== + +=== Using Headers to Control Async Behavior + +You can override async settings per-message using headers: + +[tabs] +==== +Java:: ++ +[source,java] +---- +from("file:///data/documents?include=.*\\.pdf") + .process(exchange -> { + File file = exchange.getIn().getBody(File.class); + // Use async mode only for large files + if (file.length() > 10 * 1024 * 1024) { // > 10MB + exchange.getIn().setHeader("CamelDoclingUseAsyncMode", true); + exchange.getIn().setHeader("CamelDoclingAsyncTimeout", 600000L); + } + }) + .to("docling:CONVERT_TO_MARKDOWN?useDoclingServe=true&contentInBody=true") + .to("file:///data/output"); +---- + +YAML:: ++ +[source,yaml] +---- +- route: + from: + uri: "file:///data/documents" + parameters: + include: ".*\\.pdf" + steps: + - process: + ref: "asyncDecisionProcessor" + - to: + uri: "docling:CONVERT_TO_MARKDOWN" + parameters: + useDoclingServe: true + contentInBody: true + - to: + uri: "file:///data/output" +---- +==== + == Using Docling-Serve API === Basic usage with docling-serve @@ -773,5 +909,8 @@ The component handles various error scenarios: - Consider using `contentInBody=true` when using docling-serve API mode to get results directly in the body - The `maxFileSize` setting helps prevent resource exhaustion - **API Mode vs CLI Mode**: The docling-serve API mode typically offers better performance and resource utilization for high-volume document processing, as it maintains a persistent server instance +- **Async Mode**: For large documents or high-volume processing, enable `useAsyncMode=true` to prevent blocking the Camel thread pool. The component will poll the docling-serve API for completion status while freeing up processing threads +- **Async Configuration**: Adjust `asyncPollInterval` (default 2000ms) and `asyncTimeout` (default 300000ms/5 minutes) based on your document size and processing requirements +- **Batch Processing**: When processing multiple documents, async mode allows better parallelization as the docling-serve instance can process multiple documents concurrently while Camel polls for results include::spring-boot:partial$starter.adoc[] diff --git a/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/ConversionStatus.java b/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/ConversionStatus.java new file mode 100644 index 000000000000..f5a1e8d03b44 --- /dev/null +++ b/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/ConversionStatus.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.component.docling; + +/** + * Represents the status of an async document conversion task. + */ +public class ConversionStatus { + + public enum Status { + PENDING, + IN_PROGRESS, + COMPLETED, + FAILED, + UNKNOWN + } + + private final String taskId; + private final Status status; + private final String result; + private final String errorMessage; + private final Integer progress; + + public ConversionStatus(String taskId, Status status) { + this(taskId, status, null, null, null); + } + + public ConversionStatus(String taskId, Status status, String result, String errorMessage, Integer progress) { + this.taskId = taskId; + this.status = status; + this.result = result; + this.errorMessage = errorMessage; + this.progress = progress; + } + + public String getTaskId() { + return taskId; + } + + public Status getStatus() { + return status; + } + + public String getResult() { + return result; + } + + public String getErrorMessage() { + return errorMessage; + } + + public Integer getProgress() { + return progress; + } + + public boolean isCompleted() { + return status == Status.COMPLETED; + } + + public boolean isFailed() { + return status == Status.FAILED; + } + + public boolean isInProgress() { + return status == Status.IN_PROGRESS || status == Status.PENDING; + } + + @Override + public String toString() { + return "ConversionStatus{" + + "taskId='" + taskId + '\'' + + ", status=" + status + + ", progress=" + progress + + ", errorMessage='" + errorMessage + '\'' + + '}'; + } +} diff --git a/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/DoclingConfiguration.java b/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/DoclingConfiguration.java index b0bc2418e708..600f958a7220 100644 --- a/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/DoclingConfiguration.java +++ b/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/DoclingConfiguration.java @@ -98,6 +98,22 @@ public class DoclingConfiguration implements Cloneable { @Metadata(description = "Docling-serve API convert endpoint path", defaultValue = "/v1/convert/source") private String convertEndpoint = "/v1/convert/source"; + @UriParam(label = "advanced") + @Metadata(description = "Use asynchronous conversion mode (docling-serve API only)", defaultValue = "false") + private boolean useAsyncMode = false; + + @UriParam(label = "advanced") + @Metadata(description = "Polling interval for async conversion status in milliseconds", defaultValue = "2000") + private long asyncPollInterval = 2000; + + @UriParam(label = "advanced") + @Metadata(description = "Maximum time to wait for async conversion completion in milliseconds", defaultValue = "300000") + private long asyncTimeout = 300000; // 5 minutes + + @UriParam(label = "advanced") + @Metadata(description = "API request timeout in milliseconds", defaultValue = "60000") + private long apiTimeout = 60000; + public DoclingOperations getOperation() { return operation; } @@ -226,6 +242,38 @@ public class DoclingConfiguration implements Cloneable { this.convertEndpoint = convertEndpoint; } + public boolean isUseAsyncMode() { + return useAsyncMode; + } + + public void setUseAsyncMode(boolean useAsyncMode) { + this.useAsyncMode = useAsyncMode; + } + + public long getAsyncPollInterval() { + return asyncPollInterval; + } + + public void setAsyncPollInterval(long asyncPollInterval) { + this.asyncPollInterval = asyncPollInterval; + } + + public long getAsyncTimeout() { + return asyncTimeout; + } + + public void setAsyncTimeout(long asyncTimeout) { + this.asyncTimeout = asyncTimeout; + } + + public long getApiTimeout() { + return apiTimeout; + } + + public void setApiTimeout(long apiTimeout) { + this.apiTimeout = apiTimeout; + } + public DoclingConfiguration copy() { try { return (DoclingConfiguration) super.clone(); diff --git a/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/DoclingHeaders.java b/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/DoclingHeaders.java index f8f96c9b8c26..74aa88afe9a0 100644 --- a/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/DoclingHeaders.java +++ b/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/DoclingHeaders.java @@ -44,6 +44,18 @@ public final class DoclingHeaders { @Metadata(description = "Custom command line arguments to pass to Docling", javaType = "List<String>") public static final String CUSTOM_ARGUMENTS = "CamelDoclingCustomArguments"; + @Metadata(description = "Use asynchronous conversion mode (overrides endpoint configuration)", javaType = "Boolean") + public static final String USE_ASYNC_MODE = "CamelDoclingUseAsyncMode"; + + @Metadata(description = "Polling interval for async conversion status in milliseconds", javaType = "Long") + public static final String ASYNC_POLL_INTERVAL = "CamelDoclingAsyncPollInterval"; + + @Metadata(description = "Maximum time to wait for async conversion completion in milliseconds", javaType = "Long") + public static final String ASYNC_TIMEOUT = "CamelDoclingAsyncTimeout"; + + @Metadata(description = "Task ID for checking async conversion status", javaType = "String") + public static final String TASK_ID = "CamelDoclingTaskId"; + private DoclingHeaders() { } diff --git a/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/DoclingProducer.java b/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/DoclingProducer.java index 3a440b7d349d..a15bcf01c8d2 100644 --- a/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/DoclingProducer.java +++ b/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/DoclingProducer.java @@ -59,11 +59,14 @@ public class DoclingProducer extends DefaultProducer { configuration.getAuthenticationScheme(), configuration.getAuthenticationToken(), configuration.getApiKeyHeader(), - configuration.getConvertEndpoint()); - LOG.info("DoclingProducer configured to use docling-serve API at: {}{} with authentication: {}", + configuration.getConvertEndpoint(), + configuration.getAsyncPollInterval(), + configuration.getAsyncTimeout()); + LOG.info("DoclingProducer configured to use docling-serve API at: {}{} with authentication: {} (async mode: {})", configuration.getDoclingServeUrl(), configuration.getConvertEndpoint(), - configuration.getAuthenticationScheme()); + configuration.getAuthenticationScheme(), + configuration.isUseAsyncMode()); } else { LOG.info("DoclingProducer configured to use docling CLI command"); } @@ -117,7 +120,7 @@ public class DoclingProducer extends DefaultProducer { LOG.debug("DoclingProducer converting to markdown"); if (configuration.isUseDoclingServe()) { String inputPath = getInputPath(exchange); - String result = doclingServeClient.convertDocument(inputPath, "markdown"); + String result = convertUsingDoclingServe(inputPath, "markdown", exchange); exchange.getIn().setBody(result); } else { String inputPath = getInputPath(exchange); @@ -129,7 +132,7 @@ public class DoclingProducer extends DefaultProducer { LOG.debug("DoclingProducer converting to HTML"); if (configuration.isUseDoclingServe()) { String inputPath = getInputPath(exchange); - String result = doclingServeClient.convertDocument(inputPath, "html"); + String result = convertUsingDoclingServe(inputPath, "html", exchange); exchange.getIn().setBody(result); } else { String inputPath = getInputPath(exchange); @@ -140,7 +143,7 @@ public class DoclingProducer extends DefaultProducer { private void processConvertToJSON(Exchange exchange) throws Exception { if (configuration.isUseDoclingServe()) { String inputPath = getInputPath(exchange); - String result = doclingServeClient.convertDocument(inputPath, "json"); + String result = convertUsingDoclingServe(inputPath, "json", exchange); exchange.getIn().setBody(result); } else { String inputPath = getInputPath(exchange); @@ -151,7 +154,7 @@ public class DoclingProducer extends DefaultProducer { private void processExtractText(Exchange exchange) throws Exception { if (configuration.isUseDoclingServe()) { String inputPath = getInputPath(exchange); - String result = doclingServeClient.convertDocument(inputPath, "text"); + String result = convertUsingDoclingServe(inputPath, "text", exchange); exchange.getIn().setBody(result); } else { String inputPath = getInputPath(exchange); @@ -162,7 +165,7 @@ public class DoclingProducer extends DefaultProducer { private void processExtractStructuredData(Exchange exchange) throws Exception { if (configuration.isUseDoclingServe()) { String inputPath = getInputPath(exchange); - String result = doclingServeClient.convertDocument(inputPath, "json"); + String result = convertUsingDoclingServe(inputPath, "json", exchange); exchange.getIn().setBody(result); } else { String inputPath = getInputPath(exchange); @@ -170,6 +173,29 @@ public class DoclingProducer extends DefaultProducer { } } + private String convertUsingDoclingServe(String inputPath, String outputFormat) throws Exception { + return convertUsingDoclingServe(inputPath, outputFormat, null); + } + + private String convertUsingDoclingServe(String inputPath, String outputFormat, Exchange exchange) throws Exception { + // Check for header override + boolean useAsync = configuration.isUseAsyncMode(); + if (exchange != null) { + Boolean asyncModeHeader = exchange.getIn().getHeader(DoclingHeaders.USE_ASYNC_MODE, Boolean.class); + if (asyncModeHeader != null) { + useAsync = asyncModeHeader; + } + } + + if (useAsync) { + LOG.debug("Using async mode for conversion"); + return doclingServeClient.convertDocumentAsyncAndWait(inputPath, outputFormat); + } else { + LOG.debug("Using sync mode for conversion"); + return doclingServeClient.convertDocument(inputPath, outputFormat); + } + } + private String getInputPath(Exchange exchange) throws InvalidPayloadException, IOException { String inputPath = exchange.getIn().getHeader(DoclingHeaders.INPUT_FILE_PATH, String.class); diff --git a/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/DoclingServeClient.java b/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/DoclingServeClient.java index 9da6af787708..615c5fac6ea6 100644 --- a/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/DoclingServeClient.java +++ b/components/camel-ai/camel-docling/src/main/java/org/apache/camel/component/docling/DoclingServeClient.java @@ -26,6 +26,7 @@ import java.util.Map; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; @@ -43,6 +44,7 @@ public class DoclingServeClient { private static final Logger LOG = LoggerFactory.getLogger(DoclingServeClient.class); private static final String DEFAULT_CONVERT_ENDPOINT = "/v1/convert/source"; + private static final String DEFAULT_ASYNC_CONVERT_ENDPOINT = "/v1/convert/source/async"; private final String baseUrl; private final ObjectMapper objectMapper; @@ -51,20 +53,28 @@ public class DoclingServeClient { private final String authenticationToken; private final String apiKeyHeader; private final String convertEndpoint; + private final long asyncPollInterval; + private final long asyncTimeout; public DoclingServeClient(String baseUrl) { - this(baseUrl, AuthenticationScheme.NONE, null, "X-API-Key", DEFAULT_CONVERT_ENDPOINT); + this(baseUrl, AuthenticationScheme.NONE, null, "X-API-Key", DEFAULT_CONVERT_ENDPOINT, 2000, 300000); } public DoclingServeClient( String baseUrl, AuthenticationScheme authenticationScheme, String authenticationToken, String apiKeyHeader) { - this(baseUrl, authenticationScheme, authenticationToken, apiKeyHeader, DEFAULT_CONVERT_ENDPOINT); + this(baseUrl, authenticationScheme, authenticationToken, apiKeyHeader, DEFAULT_CONVERT_ENDPOINT, 2000, 300000); } public DoclingServeClient( String baseUrl, AuthenticationScheme authenticationScheme, String authenticationToken, String apiKeyHeader, String convertEndpoint) { + this(baseUrl, authenticationScheme, authenticationToken, apiKeyHeader, convertEndpoint, 2000, 300000); + } + + public DoclingServeClient( + String baseUrl, AuthenticationScheme authenticationScheme, String authenticationToken, + String apiKeyHeader, String convertEndpoint, long asyncPollInterval, long asyncTimeout) { this.baseUrl = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl; this.objectMapper = new ObjectMapper(); this.httpClient = HttpClients.createDefault(); @@ -72,6 +82,8 @@ public class DoclingServeClient { this.authenticationToken = authenticationToken; this.apiKeyHeader = apiKeyHeader != null ? apiKeyHeader : "X-API-Key"; this.convertEndpoint = convertEndpoint != null ? convertEndpoint : DEFAULT_CONVERT_ENDPOINT; + this.asyncPollInterval = asyncPollInterval; + this.asyncTimeout = asyncTimeout; } /** @@ -267,6 +279,282 @@ public class DoclingServeClient { } } + /** + * Apply authentication headers to the HTTP GET request based on the configured authentication scheme. + * + * @param httpGet The HTTP GET request to add authentication to + */ + private void applyAuthenticationGet(HttpGet httpGet) { + if (authenticationScheme == null || authenticationScheme == AuthenticationScheme.NONE) { + return; + } + + if (authenticationToken == null || authenticationToken.isEmpty()) { + LOG.warn("Authentication scheme is set to {} but no authentication token provided", authenticationScheme); + return; + } + + switch (authenticationScheme) { + case BEARER: + httpGet.setHeader("Authorization", "Bearer " + authenticationToken); + LOG.debug("Applied Bearer token authentication"); + break; + case API_KEY: + httpGet.setHeader(apiKeyHeader, authenticationToken); + LOG.debug("Applied API Key authentication with header: {}", apiKeyHeader); + break; + default: + LOG.warn("Unknown authentication scheme: {}", authenticationScheme); + } + } + + /** + * Convert a document using the docling-serve async API and return the task ID. + * + * @param inputSource File path or URL to the document + * @param outputFormat Output format (md, json, html, text) + * @return Task ID for the async conversion + * @throws IOException If the API call fails + */ + public String convertDocumentAsync(String inputSource, String outputFormat) throws IOException { + LOG.debug("Starting async document conversion using docling-serve API: {}", inputSource); + + String asyncEndpoint = convertEndpoint.replace("/v1/convert/source", DEFAULT_ASYNC_CONVERT_ENDPOINT); + + // Check if input is a URL or file path + Map<String, Object> requestBody = buildRequestBody(inputSource, outputFormat); + + String jsonRequest = objectMapper.writeValueAsString(requestBody); + LOG.debug("Async request body: {}", jsonRequest); + + HttpPost httpPost = new HttpPost(baseUrl + asyncEndpoint); + httpPost.setEntity(new StringEntity(jsonRequest, ContentType.APPLICATION_JSON)); + httpPost.setHeader("Accept", "application/json"); + applyAuthentication(httpPost); + + try (CloseableHttpResponse response = httpClient.execute(httpPost)) { + int statusCode = response.getCode(); + String responseBody; + try { + responseBody = EntityUtils.toString(response.getEntity()); + } catch (org.apache.hc.core5.http.ParseException e) { + throw new IOException("Failed to parse response from docling-serve API", e); + } + + if (statusCode >= 200 && statusCode < 300) { + // Extract task ID from response + JsonNode rootNode = objectMapper.readTree(responseBody); + if (rootNode.has("task_id")) { + return rootNode.get("task_id").asText(); + } else if (rootNode.has("id")) { + return rootNode.get("id").asText(); + } else { + throw new IOException("No task ID found in async conversion response: " + responseBody); + } + } else { + throw new IOException( + "Docling-serve async API request failed with status " + statusCode + ": " + responseBody); + } + } + } + + /** + * Check the status of an async conversion task. + * + * @param taskId The task ID returned from convertDocumentAsync + * @return ConversionStatus object with current status + * @throws IOException If the API call fails + */ + public ConversionStatus checkConversionStatus(String taskId) throws IOException { + LOG.debug("Checking status for task: {}", taskId); + + String statusEndpoint = "/v1/status/poll/" + taskId; + HttpGet httpGet = new HttpGet(baseUrl + statusEndpoint); + httpGet.setHeader("Accept", "application/json"); + applyAuthenticationGet(httpGet); + + try (CloseableHttpResponse response = httpClient.execute(httpGet)) { + int statusCode = response.getCode(); + String responseBody; + try { + responseBody = EntityUtils.toString(response.getEntity()); + } catch (org.apache.hc.core5.http.ParseException e) { + throw new IOException("Failed to parse response from docling-serve API", e); + } + + if (statusCode >= 200 && statusCode < 300) { + JsonNode rootNode = objectMapper.readTree(responseBody); + return parseConversionStatus(taskId, rootNode); + } else { + throw new IOException( + "Failed to check task status. Status code: " + statusCode + ", Response: " + responseBody); + } + } + } + + /** + * Convert a document asynchronously and wait for completion by polling. + * + * @param inputSource File path or URL to the document + * @param outputFormat Output format (md, json, html, text) + * @return Converted document content + * @throws IOException If the API call fails or timeout occurs + */ + public String convertDocumentAsyncAndWait(String inputSource, String outputFormat) throws IOException { + String taskId = convertDocumentAsync(inputSource, outputFormat); + LOG.debug("Started async conversion with task ID: {}", taskId); + + long startTime = System.currentTimeMillis(); + long deadline = startTime + asyncTimeout; + + while (System.currentTimeMillis() < deadline) { + ConversionStatus status = checkConversionStatus(taskId); + LOG.debug("Task {} status: {}", taskId, status.getStatus()); + + if (status.isCompleted()) { + LOG.debug("Task {} completed successfully", taskId); + return status.getResult(); + } else if (status.isFailed()) { + throw new IOException("Async conversion failed: " + status.getErrorMessage()); + } + + // Wait before next poll + try { + Thread.sleep(asyncPollInterval); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException("Async conversion interrupted", e); + } + } + + throw new IOException( + "Async conversion timed out after " + asyncTimeout + "ms for task: " + taskId); + } + + private Map<String, Object> buildRequestBody(String inputSource, String outputFormat) { + Map<String, Object> requestBody = new HashMap<>(); + + if (inputSource.startsWith("http://") || inputSource.startsWith("https://")) { + Map<String, String> source = new HashMap<>(); + source.put("kind", "http"); + source.put("url", inputSource); + requestBody.put("sources", Collections.singletonList(source)); + } else { + try { + File file = new File(inputSource); + byte[] fileBytes = Files.readAllBytes(file.toPath()); + String base64Content = Base64.getEncoder().encodeToString(fileBytes); + + Map<String, String> source = new HashMap<>(); + source.put("kind", "file"); + source.put("base64_string", base64Content); + source.put("filename", file.getName()); + requestBody.put("sources", Collections.singletonList(source)); + } catch (IOException e) { + throw new RuntimeException("Failed to read file: " + inputSource, e); + } + } + + // Add output format if specified + if (outputFormat != null && !outputFormat.isEmpty()) { + Map<String, Object> options = new HashMap<>(); + options.put("to_formats", Collections.singletonList(mapOutputFormat(outputFormat))); + requestBody.put("options", options); + } + + return requestBody; + } + + private ConversionStatus parseConversionStatus(String taskId, JsonNode statusNode) { + // Docling-serve uses "task_status" field with values: pending, started, success, failure + String statusStr = statusNode.has("task_status") ? statusNode.get("task_status").asText() : "unknown"; + ConversionStatus.Status status; + + switch (statusStr.toLowerCase()) { + case "pending": + status = ConversionStatus.Status.PENDING; + break; + case "started": + case "in_progress": + case "running": + case "processing": + status = ConversionStatus.Status.IN_PROGRESS; + break; + case "success": + case "completed": + status = ConversionStatus.Status.COMPLETED; + break; + case "failure": + case "failed": + case "error": + status = ConversionStatus.Status.FAILED; + break; + default: + LOG.warn("Unknown task status: {}", statusStr); + status = ConversionStatus.Status.UNKNOWN; + } + + String result = null; + String errorMessage = null; + + // For completed tasks, fetch the result + if (status == ConversionStatus.Status.COMPLETED) { + try { + result = fetchTaskResult(taskId); + } catch (IOException e) { + LOG.warn("Failed to fetch result for completed task: {}", taskId, e); + } + } + + // For failed tasks, extract error message + if (status == ConversionStatus.Status.FAILED) { + if (statusNode.has("task_meta") && statusNode.get("task_meta").has("error")) { + errorMessage = statusNode.get("task_meta").get("error").asText(); + } else if (statusNode.has("error")) { + errorMessage = statusNode.get("error").asText(); + } else { + errorMessage = "Task failed without error message"; + } + } + + Integer progress = statusNode.has("task_position") ? statusNode.get("task_position").asInt() : null; + + return new ConversionStatus(taskId, status, result, errorMessage, progress); + } + + /** + * Fetch the result of a completed async conversion task. + * + * @param taskId The task ID + * @return Converted document content + * @throws IOException If the API call fails + */ + private String fetchTaskResult(String taskId) throws IOException { + LOG.debug("Fetching result for task: {}", taskId); + + String resultEndpoint = "/v1/result/" + taskId; + HttpGet httpGet = new HttpGet(baseUrl + resultEndpoint); + httpGet.setHeader("Accept", "application/json"); + applyAuthenticationGet(httpGet); + + try (CloseableHttpResponse response = httpClient.execute(httpGet)) { + int statusCode = response.getCode(); + String responseBody; + try { + responseBody = EntityUtils.toString(response.getEntity()); + } catch (org.apache.hc.core5.http.ParseException e) { + throw new IOException("Failed to parse response from docling-serve API", e); + } + + if (statusCode >= 200 && statusCode < 300) { + return extractConvertedContent(responseBody, null); + } else { + throw new IOException( + "Failed to fetch task result. Status code: " + statusCode + ", Response: " + responseBody); + } + } + } + public void close() throws IOException { if (httpClient != null) { httpClient.close(); diff --git a/components/camel-ai/camel-docling/src/test/java/org/apache/camel/component/docling/DoclingServeProducerIT.java b/components/camel-ai/camel-docling/src/test/java/org/apache/camel/component/docling/DoclingServeProducerIT.java index a713e72806f1..9d12251b2b0f 100644 --- a/components/camel-ai/camel-docling/src/test/java/org/apache/camel/component/docling/DoclingServeProducerIT.java +++ b/components/camel-ai/camel-docling/src/test/java/org/apache/camel/component/docling/DoclingServeProducerIT.java @@ -144,6 +144,97 @@ public class DoclingServeProducerIT extends CamelTestSupport { LOG.info("Output file size: {} bytes", outputFile.length()); } + @Test + public void testAsyncMarkdownConversion() throws Exception { + Path testFile = createTestFile(); + + String result = template.requestBodyAndHeader("direct:convert-async-markdown", + testFile.toString(), + DoclingHeaders.INPUT_FILE_PATH, testFile.toString(), String.class); + + assertNotNull(result, "Async conversion result should not be null"); + assertTrue(result.length() > 0, "Async conversion result should not be empty"); + + LOG.info("Successfully converted document to Markdown using async mode"); + } + + @Test + public void testAsyncHtmlConversion() throws Exception { + Path testFile = createTestFile(); + + String result = template.requestBodyAndHeader("direct:convert-async-html", + testFile.toString(), + DoclingHeaders.OPERATION, DoclingOperations.CONVERT_TO_HTML, String.class); + + assertNotNull(result, "Async HTML conversion result should not be null"); + assertTrue(result.length() > 0, "Async HTML conversion result should not be empty"); + + LOG.info("Successfully converted document to HTML using async mode"); + } + + @Test + public void testAsyncJsonConversion() throws Exception { + Path testFile = createTestFile(); + + String result = template.requestBodyAndHeader("direct:convert-async-json", + testFile.toString(), + DoclingHeaders.INPUT_FILE_PATH, testFile.toString(), String.class); + + assertNotNull(result, "Async JSON conversion result should not be null"); + assertTrue(result.length() > 0, "Async JSON conversion result should not be empty"); + assertTrue(result.contains("{") || result.contains("["), "JSON result should contain JSON structure"); + + LOG.info("Successfully converted document to JSON using async mode"); + } + + @Test + public void testAsyncUrlConversion() throws Exception { + String url = "https://arxiv.org/pdf/2501.17887"; + + String result = template.requestBody("direct:convert-async-url", url, String.class); + + assertNotNull(result, "Async URL conversion result should not be null"); + assertTrue(result.length() > 0, "Async URL conversion result should not be empty"); + + LOG.info("Successfully converted document from URL using async mode"); + } + + @Test + public void testAsyncConversionWithCustomTimeout() throws Exception { + Path testFile = createTestFile(); + + String result = template.requestBodyAndHeader("direct:convert-async-custom-timeout", + testFile.toString(), + DoclingHeaders.INPUT_FILE_PATH, testFile.toString(), String.class); + + assertNotNull(result, "Async conversion with custom timeout should not be null"); + assertTrue(result.length() > 0, "Async conversion with custom timeout should not be empty"); + + LOG.info("Successfully converted document using async mode with custom timeout"); + } + + @Test + public void testAsyncConversionWithHeaderOverride() throws Exception { + Path testFile = createTestFile(); + + // Use sync endpoint but override with async header + String result = template.requestBodyAndHeaders("direct:convert-markdown-serve", + testFile.toString(), + new java.util.HashMap<String, Object>() { + { + put(DoclingHeaders.INPUT_FILE_PATH, testFile.toString()); + put(DoclingHeaders.USE_ASYNC_MODE, true); + put(DoclingHeaders.ASYNC_POLL_INTERVAL, 1000L); + put(DoclingHeaders.ASYNC_TIMEOUT, 120000L); + } + }, String.class); + + assertNotNull(result, "Async conversion via header override should not be null"); + assertTrue(result.length() > 0, "Async conversion via header override should not be empty"); + + LOG.info("Successfully converted document using async mode via header override"); + } + private Path createTestFile() throws Exception { Path tempFile = Files.createTempFile("docling-serve-test", ".md"); Files.write(tempFile, @@ -157,6 +248,7 @@ public class DoclingServeProducerIT extends CamelTestSupport { return new RouteBuilder() { @Override public void configure() throws Exception { + // Synchronous conversion routes from("direct:convert-markdown-serve") .to("docling:convert?operation=CONVERT_TO_MARKDOWN&contentInBody=true"); @@ -172,6 +264,22 @@ public class DoclingServeProducerIT extends CamelTestSupport { from("direct:convert-and-write") .to("docling:convert?operation=CONVERT_TO_MARKDOWN&contentInBody=true") .to("file:" + outputDir.toString() + "?fileName=converted-output.md"); + + // Asynchronous conversion routes + from("direct:convert-async-markdown") + .to("docling:convert?operation=CONVERT_TO_MARKDOWN&contentInBody=true&useAsyncMode=true&asyncPollInterval=1000&asyncTimeout=120000"); + + from("direct:convert-async-html") + .to("docling:convert?operation=CONVERT_TO_HTML&contentInBody=true&useAsyncMode=true&asyncPollInterval=1000&asyncTimeout=120000"); + + from("direct:convert-async-json") + .to("docling:convert?operation=CONVERT_TO_JSON&contentInBody=true&useAsyncMode=true&asyncPollInterval=1000&asyncTimeout=120000"); + + from("direct:convert-async-url") + .to("docling:convert?operation=CONVERT_TO_MARKDOWN&contentInBody=true&useAsyncMode=true&asyncPollInterval=1000&asyncTimeout=120000"); + + from("direct:convert-async-custom-timeout") + .to("docling:convert?operation=CONVERT_TO_MARKDOWN&contentInBody=true&useAsyncMode=true&asyncPollInterval=500&asyncTimeout=300000"); } }; }
