diff --git a/bom/camel-bom/pom.xml b/bom/camel-bom/pom.xml index c1cfcf4f04bcf..6966d410ae5f4 100644 --- a/bom/camel-bom/pom.xml +++ b/bom/camel-bom/pom.xml @@ -2067,6 +2067,11 @@ camel-tooling-util 4.9.0-SNAPSHOT + + org.apache.camel + camel-torchserve + 4.9.0-SNAPSHOT + org.apache.camel camel-tracing diff --git a/catalog/camel-allcomponents/pom.xml b/catalog/camel-allcomponents/pom.xml index 71f3508855bf1..fcb5049fc8b7b 100644 --- a/catalog/camel-allcomponents/pom.xml +++ b/catalog/camel-allcomponents/pom.xml @@ -1821,6 +1821,11 @@ camel-timer ${project.version} + + org.apache.camel + camel-torchserve + ${project.version} + org.apache.camel camel-tracing diff --git a/components/camel-ai/camel-torchserve/pom.xml b/components/camel-ai/camel-torchserve/pom.xml new file mode 100644 index 0000000000000..c4519775e1095 --- /dev/null +++ b/components/camel-ai/camel-torchserve/pom.xml @@ -0,0 +1,196 @@ + + + + 4.0.0 + + + camel-ai-parent + org.apache.camel + 4.9.0-SNAPSHOT + + + camel-torchserve + jar + Camel :: AI :: TorchServe + Provide access to PyTorch TorchServe servers to run inference with PyTorch models remotely + + + + + + + + org.apache.camel + camel-support + + + + + + org.apache.httpcomponents.client5 + httpclient5 + ${httpclient-version} + + + + + com.fasterxml.jackson.core + jackson-core + ${jackson2-version} + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson2-version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson2-version} + + + com.fasterxml.jackson.jakarta.rs + jackson-jakarta-rs-json-provider + ${jackson2-version} + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson2-version} + + + org.openapitools + jackson-databind-nullable + ${jackson-databind-nullable-version} + + + jakarta.annotation + jakarta.annotation-api + ${jakarta-annotation-api-version} + + + + + org.apache.camel + camel-test-junit5 + test + + + org.wiremock + wiremock + ${wiremock-version} + test + + + + + + + org.openapitools + openapi-generator-maven-plugin + ${openapi-generator-version} + + + generate-inference + + generate + + + ${project.basedir}/src/main/resources/openapi/inference.json + java + org.apache.camel.component.torchserve.client.inference.api + org.apache.camel.component.torchserve.client.inference.model + org.apache.camel.component.torchserve.client.inference.invoker + + + true + apache-httpclient + + false + false + true + + + + generate-management + + generate + + + ${project.basedir}/src/main/resources/openapi/management.json + java + org.apache.camel.component.torchserve.client.management.api + org.apache.camel.component.torchserve.client.management.model + org.apache.camel.component.torchserve.client.management.invoker + + + true + apache-httpclient + + false + false + true + + + + generate-metrics + + generate + + + ${project.basedir}/src/main/resources/openapi/metrics.json + java + org.apache.camel.component.torchserve.client.metrics.api + org.apache.camel.component.torchserve.client.metrics.model + org.apache.camel.component.torchserve.client.metrics.invoker + + + true + apache-httpclient + + false + false + true + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-source + generate-sources + + add-source + + + + ${project.build.directory}/generated-sources/openapi/src/main/java + + + + + + + + + diff --git a/components/camel-ai/camel-torchserve/src/generated/java/org/apache/camel/component/torchserve/TorchServeComponentConfigurer.java b/components/camel-ai/camel-torchserve/src/generated/java/org/apache/camel/component/torchserve/TorchServeComponentConfigurer.java new file mode 100644 index 0000000000000..675b2e627f341 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/generated/java/org/apache/camel/component/torchserve/TorchServeComponentConfigurer.java @@ -0,0 +1,178 @@ +/* Generated by camel build tools - do NOT edit this file! */ +package org.apache.camel.component.torchserve; + +import javax.annotation.processing.Generated; +import java.util.Map; + +import org.apache.camel.CamelContext; +import org.apache.camel.spi.ExtendedPropertyConfigurerGetter; +import org.apache.camel.spi.PropertyConfigurerGetter; +import org.apache.camel.spi.ConfigurerStrategy; +import org.apache.camel.spi.GeneratedPropertyConfigurer; +import org.apache.camel.util.CaseInsensitiveMap; +import org.apache.camel.support.component.PropertyConfigurerSupport; + +/** + * Generated by camel build tools - do NOT edit this file! + */ +@Generated("org.apache.camel.maven.packaging.EndpointSchemaGeneratorMojo") +@SuppressWarnings("unchecked") +public class TorchServeComponentConfigurer extends PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter { + + private org.apache.camel.component.torchserve.TorchServeConfiguration getOrCreateConfiguration(TorchServeComponent target) { + if (target.getConfiguration() == null) { + target.setConfiguration(new org.apache.camel.component.torchserve.TorchServeConfiguration()); + } + return target.getConfiguration(); + } + + @Override + public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) { + TorchServeComponent target = (TorchServeComponent) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "autowiredenabled": + case "autowiredEnabled": target.setAutowiredEnabled(property(camelContext, boolean.class, value)); return true; + case "configuration": target.setConfiguration(property(camelContext, org.apache.camel.component.torchserve.TorchServeConfiguration.class, value)); return true; + case "healthcheckconsumerenabled": + case "healthCheckConsumerEnabled": target.setHealthCheckConsumerEnabled(property(camelContext, boolean.class, value)); return true; + case "healthcheckproducerenabled": + case "healthCheckProducerEnabled": target.setHealthCheckProducerEnabled(property(camelContext, boolean.class, value)); return true; + case "inferenceaddress": + case "inferenceAddress": getOrCreateConfiguration(target).setInferenceAddress(property(camelContext, java.lang.String.class, value)); return true; + case "inferencekey": + case "inferenceKey": getOrCreateConfiguration(target).setInferenceKey(property(camelContext, java.lang.String.class, value)); return true; + case "inferenceport": + case "inferencePort": getOrCreateConfiguration(target).setInferencePort(property(camelContext, int.class, value)); return true; + case "lazystartproducer": + case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true; + case "listlimit": + case "listLimit": getOrCreateConfiguration(target).setListLimit(property(camelContext, int.class, value)); return true; + case "listnextpagetoken": + case "listNextPageToken": getOrCreateConfiguration(target).setListNextPageToken(property(camelContext, java.lang.String.class, value)); return true; + case "managementaddress": + case "managementAddress": getOrCreateConfiguration(target).setManagementAddress(property(camelContext, java.lang.String.class, value)); return true; + case "managementkey": + case "managementKey": getOrCreateConfiguration(target).setManagementKey(property(camelContext, java.lang.String.class, value)); return true; + case "managementport": + case "managementPort": getOrCreateConfiguration(target).setManagementPort(property(camelContext, int.class, value)); return true; + case "metricsaddress": + case "metricsAddress": getOrCreateConfiguration(target).setMetricsAddress(property(camelContext, java.lang.String.class, value)); return true; + case "metricsname": + case "metricsName": getOrCreateConfiguration(target).setMetricsName(property(camelContext, java.lang.String.class, value)); return true; + case "metricsport": + case "metricsPort": getOrCreateConfiguration(target).setMetricsPort(property(camelContext, int.class, value)); return true; + case "modelname": + case "modelName": getOrCreateConfiguration(target).setModelName(property(camelContext, java.lang.String.class, value)); return true; + case "modelversion": + case "modelVersion": getOrCreateConfiguration(target).setModelVersion(property(camelContext, java.lang.String.class, value)); return true; + case "registeroptions": + case "registerOptions": getOrCreateConfiguration(target).setRegisterOptions(property(camelContext, org.apache.camel.component.torchserve.client.model.RegisterOptions.class, value)); return true; + case "scaleworkeroptions": + case "scaleWorkerOptions": getOrCreateConfiguration(target).setScaleWorkerOptions(property(camelContext, org.apache.camel.component.torchserve.client.model.ScaleWorkerOptions.class, value)); return true; + case "unregisteroptions": + case "unregisterOptions": getOrCreateConfiguration(target).setUnregisterOptions(property(camelContext, org.apache.camel.component.torchserve.client.model.UnregisterOptions.class, value)); return true; + case "url": getOrCreateConfiguration(target).setUrl(property(camelContext, java.lang.String.class, value)); return true; + default: return false; + } + } + + @Override + public Class getOptionType(String name, boolean ignoreCase) { + switch (ignoreCase ? name.toLowerCase() : name) { + case "autowiredenabled": + case "autowiredEnabled": return boolean.class; + case "configuration": return org.apache.camel.component.torchserve.TorchServeConfiguration.class; + case "healthcheckconsumerenabled": + case "healthCheckConsumerEnabled": return boolean.class; + case "healthcheckproducerenabled": + case "healthCheckProducerEnabled": return boolean.class; + case "inferenceaddress": + case "inferenceAddress": return java.lang.String.class; + case "inferencekey": + case "inferenceKey": return java.lang.String.class; + case "inferenceport": + case "inferencePort": return int.class; + case "lazystartproducer": + case "lazyStartProducer": return boolean.class; + case "listlimit": + case "listLimit": return int.class; + case "listnextpagetoken": + case "listNextPageToken": return java.lang.String.class; + case "managementaddress": + case "managementAddress": return java.lang.String.class; + case "managementkey": + case "managementKey": return java.lang.String.class; + case "managementport": + case "managementPort": return int.class; + case "metricsaddress": + case "metricsAddress": return java.lang.String.class; + case "metricsname": + case "metricsName": return java.lang.String.class; + case "metricsport": + case "metricsPort": return int.class; + case "modelname": + case "modelName": return java.lang.String.class; + case "modelversion": + case "modelVersion": return java.lang.String.class; + case "registeroptions": + case "registerOptions": return org.apache.camel.component.torchserve.client.model.RegisterOptions.class; + case "scaleworkeroptions": + case "scaleWorkerOptions": return org.apache.camel.component.torchserve.client.model.ScaleWorkerOptions.class; + case "unregisteroptions": + case "unregisterOptions": return org.apache.camel.component.torchserve.client.model.UnregisterOptions.class; + case "url": return java.lang.String.class; + default: return null; + } + } + + @Override + public Object getOptionValue(Object obj, String name, boolean ignoreCase) { + TorchServeComponent target = (TorchServeComponent) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "autowiredenabled": + case "autowiredEnabled": return target.isAutowiredEnabled(); + case "configuration": return target.getConfiguration(); + case "healthcheckconsumerenabled": + case "healthCheckConsumerEnabled": return target.isHealthCheckConsumerEnabled(); + case "healthcheckproducerenabled": + case "healthCheckProducerEnabled": return target.isHealthCheckProducerEnabled(); + case "inferenceaddress": + case "inferenceAddress": return getOrCreateConfiguration(target).getInferenceAddress(); + case "inferencekey": + case "inferenceKey": return getOrCreateConfiguration(target).getInferenceKey(); + case "inferenceport": + case "inferencePort": return getOrCreateConfiguration(target).getInferencePort(); + case "lazystartproducer": + case "lazyStartProducer": return target.isLazyStartProducer(); + case "listlimit": + case "listLimit": return getOrCreateConfiguration(target).getListLimit(); + case "listnextpagetoken": + case "listNextPageToken": return getOrCreateConfiguration(target).getListNextPageToken(); + case "managementaddress": + case "managementAddress": return getOrCreateConfiguration(target).getManagementAddress(); + case "managementkey": + case "managementKey": return getOrCreateConfiguration(target).getManagementKey(); + case "managementport": + case "managementPort": return getOrCreateConfiguration(target).getManagementPort(); + case "metricsaddress": + case "metricsAddress": return getOrCreateConfiguration(target).getMetricsAddress(); + case "metricsname": + case "metricsName": return getOrCreateConfiguration(target).getMetricsName(); + case "metricsport": + case "metricsPort": return getOrCreateConfiguration(target).getMetricsPort(); + case "modelname": + case "modelName": return getOrCreateConfiguration(target).getModelName(); + case "modelversion": + case "modelVersion": return getOrCreateConfiguration(target).getModelVersion(); + case "registeroptions": + case "registerOptions": return getOrCreateConfiguration(target).getRegisterOptions(); + case "scaleworkeroptions": + case "scaleWorkerOptions": return getOrCreateConfiguration(target).getScaleWorkerOptions(); + case "unregisteroptions": + case "unregisterOptions": return getOrCreateConfiguration(target).getUnregisterOptions(); + case "url": return getOrCreateConfiguration(target).getUrl(); + default: return null; + } + } +} + diff --git a/components/camel-ai/camel-torchserve/src/generated/java/org/apache/camel/component/torchserve/TorchServeConfigurationConfigurer.java b/components/camel-ai/camel-torchserve/src/generated/java/org/apache/camel/component/torchserve/TorchServeConfigurationConfigurer.java new file mode 100644 index 0000000000000..7566c1927b2e1 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/generated/java/org/apache/camel/component/torchserve/TorchServeConfigurationConfigurer.java @@ -0,0 +1,144 @@ +/* Generated by camel build tools - do NOT edit this file! */ +package org.apache.camel.component.torchserve; + +import javax.annotation.processing.Generated; +import java.util.Map; + +import org.apache.camel.CamelContext; +import org.apache.camel.spi.ExtendedPropertyConfigurerGetter; +import org.apache.camel.spi.PropertyConfigurerGetter; +import org.apache.camel.spi.ConfigurerStrategy; +import org.apache.camel.spi.GeneratedPropertyConfigurer; +import org.apache.camel.util.CaseInsensitiveMap; +import org.apache.camel.component.torchserve.TorchServeConfiguration; + +/** + * Generated by camel build tools - do NOT edit this file! + */ +@Generated("org.apache.camel.maven.packaging.GenerateConfigurerMojo") +@SuppressWarnings("unchecked") +public class TorchServeConfigurationConfigurer extends org.apache.camel.support.component.PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter { + + @Override + public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) { + org.apache.camel.component.torchserve.TorchServeConfiguration target = (org.apache.camel.component.torchserve.TorchServeConfiguration) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "inferenceaddress": + case "inferenceAddress": target.setInferenceAddress(property(camelContext, java.lang.String.class, value)); return true; + case "inferencekey": + case "inferenceKey": target.setInferenceKey(property(camelContext, java.lang.String.class, value)); return true; + case "inferenceport": + case "inferencePort": target.setInferencePort(property(camelContext, int.class, value)); return true; + case "listlimit": + case "listLimit": target.setListLimit(property(camelContext, int.class, value)); return true; + case "listnextpagetoken": + case "listNextPageToken": target.setListNextPageToken(property(camelContext, java.lang.String.class, value)); return true; + case "managementaddress": + case "managementAddress": target.setManagementAddress(property(camelContext, java.lang.String.class, value)); return true; + case "managementkey": + case "managementKey": target.setManagementKey(property(camelContext, java.lang.String.class, value)); return true; + case "managementport": + case "managementPort": target.setManagementPort(property(camelContext, int.class, value)); return true; + case "metricsaddress": + case "metricsAddress": target.setMetricsAddress(property(camelContext, java.lang.String.class, value)); return true; + case "metricsname": + case "metricsName": target.setMetricsName(property(camelContext, java.lang.String.class, value)); return true; + case "metricsport": + case "metricsPort": target.setMetricsPort(property(camelContext, int.class, value)); return true; + case "modelname": + case "modelName": target.setModelName(property(camelContext, java.lang.String.class, value)); return true; + case "modelversion": + case "modelVersion": target.setModelVersion(property(camelContext, java.lang.String.class, value)); return true; + case "registeroptions": + case "registerOptions": target.setRegisterOptions(property(camelContext, org.apache.camel.component.torchserve.client.model.RegisterOptions.class, value)); return true; + case "scaleworkeroptions": + case "scaleWorkerOptions": target.setScaleWorkerOptions(property(camelContext, org.apache.camel.component.torchserve.client.model.ScaleWorkerOptions.class, value)); return true; + case "unregisteroptions": + case "unregisterOptions": target.setUnregisterOptions(property(camelContext, org.apache.camel.component.torchserve.client.model.UnregisterOptions.class, value)); return true; + case "url": target.setUrl(property(camelContext, java.lang.String.class, value)); return true; + default: return false; + } + } + + @Override + public Class getOptionType(String name, boolean ignoreCase) { + switch (ignoreCase ? name.toLowerCase() : name) { + case "inferenceaddress": + case "inferenceAddress": return java.lang.String.class; + case "inferencekey": + case "inferenceKey": return java.lang.String.class; + case "inferenceport": + case "inferencePort": return int.class; + case "listlimit": + case "listLimit": return int.class; + case "listnextpagetoken": + case "listNextPageToken": return java.lang.String.class; + case "managementaddress": + case "managementAddress": return java.lang.String.class; + case "managementkey": + case "managementKey": return java.lang.String.class; + case "managementport": + case "managementPort": return int.class; + case "metricsaddress": + case "metricsAddress": return java.lang.String.class; + case "metricsname": + case "metricsName": return java.lang.String.class; + case "metricsport": + case "metricsPort": return int.class; + case "modelname": + case "modelName": return java.lang.String.class; + case "modelversion": + case "modelVersion": return java.lang.String.class; + case "registeroptions": + case "registerOptions": return org.apache.camel.component.torchserve.client.model.RegisterOptions.class; + case "scaleworkeroptions": + case "scaleWorkerOptions": return org.apache.camel.component.torchserve.client.model.ScaleWorkerOptions.class; + case "unregisteroptions": + case "unregisterOptions": return org.apache.camel.component.torchserve.client.model.UnregisterOptions.class; + case "url": return java.lang.String.class; + default: return null; + } + } + + @Override + public Object getOptionValue(Object obj, String name, boolean ignoreCase) { + org.apache.camel.component.torchserve.TorchServeConfiguration target = (org.apache.camel.component.torchserve.TorchServeConfiguration) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "inferenceaddress": + case "inferenceAddress": return target.getInferenceAddress(); + case "inferencekey": + case "inferenceKey": return target.getInferenceKey(); + case "inferenceport": + case "inferencePort": return target.getInferencePort(); + case "listlimit": + case "listLimit": return target.getListLimit(); + case "listnextpagetoken": + case "listNextPageToken": return target.getListNextPageToken(); + case "managementaddress": + case "managementAddress": return target.getManagementAddress(); + case "managementkey": + case "managementKey": return target.getManagementKey(); + case "managementport": + case "managementPort": return target.getManagementPort(); + case "metricsaddress": + case "metricsAddress": return target.getMetricsAddress(); + case "metricsname": + case "metricsName": return target.getMetricsName(); + case "metricsport": + case "metricsPort": return target.getMetricsPort(); + case "modelname": + case "modelName": return target.getModelName(); + case "modelversion": + case "modelVersion": return target.getModelVersion(); + case "registeroptions": + case "registerOptions": return target.getRegisterOptions(); + case "scaleworkeroptions": + case "scaleWorkerOptions": return target.getScaleWorkerOptions(); + case "unregisteroptions": + case "unregisterOptions": return target.getUnregisterOptions(); + case "url": return target.getUrl(); + default: return null; + } + } +} + diff --git a/components/camel-ai/camel-torchserve/src/generated/java/org/apache/camel/component/torchserve/TorchServeConverterLoader.java b/components/camel-ai/camel-torchserve/src/generated/java/org/apache/camel/component/torchserve/TorchServeConverterLoader.java new file mode 100644 index 0000000000000..dbc5163ba0bcb --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/generated/java/org/apache/camel/component/torchserve/TorchServeConverterLoader.java @@ -0,0 +1,49 @@ +/* Generated by camel build tools - do NOT edit this file! */ +package org.apache.camel.component.torchserve; + +import javax.annotation.processing.Generated; + +import org.apache.camel.CamelContext; +import org.apache.camel.CamelContextAware; +import org.apache.camel.DeferredContextBinding; +import org.apache.camel.Exchange; +import org.apache.camel.TypeConversionException; +import org.apache.camel.TypeConverterLoaderException; +import org.apache.camel.spi.TypeConverterLoader; +import org.apache.camel.spi.TypeConverterRegistry; +import org.apache.camel.support.SimpleTypeConverter; +import org.apache.camel.support.TypeConverterSupport; +import org.apache.camel.util.DoubleMap; + +/** + * Generated by camel build tools - do NOT edit this file! + */ +@Generated("org.apache.camel.maven.packaging.TypeConverterLoaderGeneratorMojo") +@SuppressWarnings("unchecked") +@DeferredContextBinding +public final class TorchServeConverterLoader implements TypeConverterLoader, CamelContextAware { + + private CamelContext camelContext; + + public TorchServeConverterLoader() { + } + + @Override + public void setCamelContext(CamelContext camelContext) { + this.camelContext = camelContext; + } + + @Override + public CamelContext getCamelContext() { + return camelContext; + } + + @Override + public void load(TypeConverterRegistry registry) throws TypeConverterLoaderException { + } + + + private static void addTypeConverter(TypeConverterRegistry registry, Class toType, Class fromType, boolean allowNull, SimpleTypeConverter.ConversionMethod method) { + registry.addTypeConverter(toType, fromType, new SimpleTypeConverter(allowNull, method)); + } +} diff --git a/components/camel-ai/camel-torchserve/src/generated/java/org/apache/camel/component/torchserve/TorchServeEndpointConfigurer.java b/components/camel-ai/camel-torchserve/src/generated/java/org/apache/camel/component/torchserve/TorchServeEndpointConfigurer.java new file mode 100644 index 0000000000000..6a53b3508f5fb --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/generated/java/org/apache/camel/component/torchserve/TorchServeEndpointConfigurer.java @@ -0,0 +1,150 @@ +/* Generated by camel build tools - do NOT edit this file! */ +package org.apache.camel.component.torchserve; + +import javax.annotation.processing.Generated; +import java.util.Map; + +import org.apache.camel.CamelContext; +import org.apache.camel.spi.ExtendedPropertyConfigurerGetter; +import org.apache.camel.spi.PropertyConfigurerGetter; +import org.apache.camel.spi.ConfigurerStrategy; +import org.apache.camel.spi.GeneratedPropertyConfigurer; +import org.apache.camel.util.CaseInsensitiveMap; +import org.apache.camel.support.component.PropertyConfigurerSupport; + +/** + * Generated by camel build tools - do NOT edit this file! + */ +@Generated("org.apache.camel.maven.packaging.EndpointSchemaGeneratorMojo") +@SuppressWarnings("unchecked") +public class TorchServeEndpointConfigurer extends PropertyConfigurerSupport implements GeneratedPropertyConfigurer, PropertyConfigurerGetter { + + @Override + public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) { + TorchServeEndpoint target = (TorchServeEndpoint) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "inferenceaddress": + case "inferenceAddress": target.getConfiguration().setInferenceAddress(property(camelContext, java.lang.String.class, value)); return true; + case "inferencekey": + case "inferenceKey": target.getConfiguration().setInferenceKey(property(camelContext, java.lang.String.class, value)); return true; + case "inferenceport": + case "inferencePort": target.getConfiguration().setInferencePort(property(camelContext, int.class, value)); return true; + case "lazystartproducer": + case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true; + case "listlimit": + case "listLimit": target.getConfiguration().setListLimit(property(camelContext, int.class, value)); return true; + case "listnextpagetoken": + case "listNextPageToken": target.getConfiguration().setListNextPageToken(property(camelContext, java.lang.String.class, value)); return true; + case "managementaddress": + case "managementAddress": target.getConfiguration().setManagementAddress(property(camelContext, java.lang.String.class, value)); return true; + case "managementkey": + case "managementKey": target.getConfiguration().setManagementKey(property(camelContext, java.lang.String.class, value)); return true; + case "managementport": + case "managementPort": target.getConfiguration().setManagementPort(property(camelContext, int.class, value)); return true; + case "metricsaddress": + case "metricsAddress": target.getConfiguration().setMetricsAddress(property(camelContext, java.lang.String.class, value)); return true; + case "metricsname": + case "metricsName": target.getConfiguration().setMetricsName(property(camelContext, java.lang.String.class, value)); return true; + case "metricsport": + case "metricsPort": target.getConfiguration().setMetricsPort(property(camelContext, int.class, value)); return true; + case "modelname": + case "modelName": target.getConfiguration().setModelName(property(camelContext, java.lang.String.class, value)); return true; + case "modelversion": + case "modelVersion": target.getConfiguration().setModelVersion(property(camelContext, java.lang.String.class, value)); return true; + case "registeroptions": + case "registerOptions": target.getConfiguration().setRegisterOptions(property(camelContext, org.apache.camel.component.torchserve.client.model.RegisterOptions.class, value)); return true; + case "scaleworkeroptions": + case "scaleWorkerOptions": target.getConfiguration().setScaleWorkerOptions(property(camelContext, org.apache.camel.component.torchserve.client.model.ScaleWorkerOptions.class, value)); return true; + case "unregisteroptions": + case "unregisterOptions": target.getConfiguration().setUnregisterOptions(property(camelContext, org.apache.camel.component.torchserve.client.model.UnregisterOptions.class, value)); return true; + case "url": target.getConfiguration().setUrl(property(camelContext, java.lang.String.class, value)); return true; + default: return false; + } + } + + @Override + public Class getOptionType(String name, boolean ignoreCase) { + switch (ignoreCase ? name.toLowerCase() : name) { + case "inferenceaddress": + case "inferenceAddress": return java.lang.String.class; + case "inferencekey": + case "inferenceKey": return java.lang.String.class; + case "inferenceport": + case "inferencePort": return int.class; + case "lazystartproducer": + case "lazyStartProducer": return boolean.class; + case "listlimit": + case "listLimit": return int.class; + case "listnextpagetoken": + case "listNextPageToken": return java.lang.String.class; + case "managementaddress": + case "managementAddress": return java.lang.String.class; + case "managementkey": + case "managementKey": return java.lang.String.class; + case "managementport": + case "managementPort": return int.class; + case "metricsaddress": + case "metricsAddress": return java.lang.String.class; + case "metricsname": + case "metricsName": return java.lang.String.class; + case "metricsport": + case "metricsPort": return int.class; + case "modelname": + case "modelName": return java.lang.String.class; + case "modelversion": + case "modelVersion": return java.lang.String.class; + case "registeroptions": + case "registerOptions": return org.apache.camel.component.torchserve.client.model.RegisterOptions.class; + case "scaleworkeroptions": + case "scaleWorkerOptions": return org.apache.camel.component.torchserve.client.model.ScaleWorkerOptions.class; + case "unregisteroptions": + case "unregisterOptions": return org.apache.camel.component.torchserve.client.model.UnregisterOptions.class; + case "url": return java.lang.String.class; + default: return null; + } + } + + @Override + public Object getOptionValue(Object obj, String name, boolean ignoreCase) { + TorchServeEndpoint target = (TorchServeEndpoint) obj; + switch (ignoreCase ? name.toLowerCase() : name) { + case "inferenceaddress": + case "inferenceAddress": return target.getConfiguration().getInferenceAddress(); + case "inferencekey": + case "inferenceKey": return target.getConfiguration().getInferenceKey(); + case "inferenceport": + case "inferencePort": return target.getConfiguration().getInferencePort(); + case "lazystartproducer": + case "lazyStartProducer": return target.isLazyStartProducer(); + case "listlimit": + case "listLimit": return target.getConfiguration().getListLimit(); + case "listnextpagetoken": + case "listNextPageToken": return target.getConfiguration().getListNextPageToken(); + case "managementaddress": + case "managementAddress": return target.getConfiguration().getManagementAddress(); + case "managementkey": + case "managementKey": return target.getConfiguration().getManagementKey(); + case "managementport": + case "managementPort": return target.getConfiguration().getManagementPort(); + case "metricsaddress": + case "metricsAddress": return target.getConfiguration().getMetricsAddress(); + case "metricsname": + case "metricsName": return target.getConfiguration().getMetricsName(); + case "metricsport": + case "metricsPort": return target.getConfiguration().getMetricsPort(); + case "modelname": + case "modelName": return target.getConfiguration().getModelName(); + case "modelversion": + case "modelVersion": return target.getConfiguration().getModelVersion(); + case "registeroptions": + case "registerOptions": return target.getConfiguration().getRegisterOptions(); + case "scaleworkeroptions": + case "scaleWorkerOptions": return target.getConfiguration().getScaleWorkerOptions(); + case "unregisteroptions": + case "unregisterOptions": return target.getConfiguration().getUnregisterOptions(); + case "url": return target.getConfiguration().getUrl(); + default: return null; + } + } +} + diff --git a/components/camel-ai/camel-torchserve/src/generated/java/org/apache/camel/component/torchserve/TorchServeEndpointUriFactory.java b/components/camel-ai/camel-torchserve/src/generated/java/org/apache/camel/component/torchserve/TorchServeEndpointUriFactory.java new file mode 100644 index 0000000000000..79ab5aed67bdc --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/generated/java/org/apache/camel/component/torchserve/TorchServeEndpointUriFactory.java @@ -0,0 +1,90 @@ +/* Generated by camel build tools - do NOT edit this file! */ +package org.apache.camel.component.torchserve; + +import javax.annotation.processing.Generated; +import java.net.URISyntaxException; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.camel.spi.EndpointUriFactory; + +/** + * Generated by camel build tools - do NOT edit this file! + */ +@Generated("org.apache.camel.maven.packaging.GenerateEndpointUriFactoryMojo") +public class TorchServeEndpointUriFactory extends org.apache.camel.support.component.EndpointUriFactorySupport implements EndpointUriFactory { + + private static final String BASE = ":api/operation"; + + private static final Set PROPERTY_NAMES; + private static final Set SECRET_PROPERTY_NAMES; + private static final Set MULTI_VALUE_PREFIXES; + static { + Set props = new HashSet<>(20); + props.add("api"); + props.add("inferenceAddress"); + props.add("inferenceKey"); + props.add("inferencePort"); + props.add("lazyStartProducer"); + props.add("listLimit"); + props.add("listNextPageToken"); + props.add("managementAddress"); + props.add("managementKey"); + props.add("managementPort"); + props.add("metricsAddress"); + props.add("metricsName"); + props.add("metricsPort"); + props.add("modelName"); + props.add("modelVersion"); + props.add("operation"); + props.add("registerOptions"); + props.add("scaleWorkerOptions"); + props.add("unregisterOptions"); + props.add("url"); + PROPERTY_NAMES = Collections.unmodifiableSet(props); + SECRET_PROPERTY_NAMES = Collections.emptySet(); + MULTI_VALUE_PREFIXES = Collections.emptySet(); + } + + @Override + public boolean isEnabled(String scheme) { + return "torchserve".equals(scheme); + } + + @Override + public String buildUri(String scheme, Map properties, boolean encode) throws URISyntaxException { + String syntax = scheme + BASE; + String uri = syntax; + + Map copy = new HashMap<>(properties); + + uri = buildPathParameter(syntax, uri, "api", null, true, copy); + uri = buildPathParameter(syntax, uri, "operation", null, true, copy); + uri = buildQueryParameters(uri, copy, encode); + return uri; + } + + @Override + public Set propertyNames() { + return PROPERTY_NAMES; + } + + @Override + public Set secretPropertyNames() { + return SECRET_PROPERTY_NAMES; + } + + @Override + public Set multiValuePrefixes() { + return MULTI_VALUE_PREFIXES; + } + + @Override + public boolean isLenientProperties() { + return false; + } +} + diff --git a/components/camel-ai/camel-torchserve/src/generated/resources/META-INF/org/apache/camel/component/torchserve/torchserve.json b/components/camel-ai/camel-torchserve/src/generated/resources/META-INF/org/apache/camel/component/torchserve/torchserve.json new file mode 100644 index 0000000000000..fddbde77aaac1 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/generated/resources/META-INF/org/apache/camel/component/torchserve/torchserve.json @@ -0,0 +1,79 @@ +{ + "component": { + "kind": "component", + "name": "torchserve", + "title": "TorchServe", + "description": "Provide access to PyTorch TorchServe servers to run inference with PyTorch models remotely", + "deprecated": false, + "firstVersion": "4.9.0", + "label": "ai", + "javaType": "org.apache.camel.component.torchserve.TorchServeComponent", + "supportLevel": "Preview", + "groupId": "org.apache.camel", + "artifactId": "camel-torchserve", + "version": "4.9.0-SNAPSHOT", + "scheme": "torchserve", + "extendsScheme": "", + "syntax": "torchserve:api\/operation", + "async": false, + "api": false, + "consumerOnly": false, + "producerOnly": true, + "lenientProperties": false, + "browsable": false, + "remote": true + }, + "componentProperties": { + "configuration": { "index": 0, "kind": "property", "displayName": "Configuration", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.component.torchserve.TorchServeConfiguration", "deprecated": false, "autowired": false, "secret": false, "description": "The configuration." }, + "modelName": { "index": 1, "kind": "property", "displayName": "Model Name", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "The name of model." }, + "modelVersion": { "index": 2, "kind": "property", "displayName": "Model Version", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "The version of model." }, + "lazyStartProducer": { "index": 3, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "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 producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." }, + "autowiredEnabled": { "index": 4, "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 type, which then gets configured on the component. This can be used for automatic configuring JDBC data sources, JMS connection factories, AWS Clients, etc." }, + "healthCheckConsumerEnabled": { "index": 5, "kind": "property", "displayName": "Health Check Consumer Enabled", "group": "health", "label": "health", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Used for enabling or disabling all consumer based health checks from this component" }, + "healthCheckProducerEnabled": { "index": 6, "kind": "property", "displayName": "Health Check Producer Enabled", "group": "health", "label": "health", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Used for enabling or disabling all producer based health checks from this component. Notice: Camel has by default disabled all producer based health-checks. You can turn on producer checks globally by setting camel.health.producersEnabled=true." }, + "inferenceAddress": { "index": 7, "kind": "property", "displayName": "Inference Address", "group": "inference", "label": "inference", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "The address of the inference API endpoint." }, + "inferencePort": { "index": 8, "kind": "property", "displayName": "Inference Port", "group": "inference", "label": "inference", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 8080, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "The port of the inference API endpoint." }, + "listLimit": { "index": 9, "kind": "property", "displayName": "List Limit", "group": "management", "label": "management", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 100, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "The maximum number of items to return for the list operation. When this value is present, TorchServe does not return more than the specified number of items, but it might return fewer. This value is optional. If you include a value, it must be between 1 and 1000, inclusive. If you do not include a value, it defaults to 100." }, + "listNextPageToken": { "index": 10, "kind": "property", "displayName": "List Next Page Token", "group": "management", "label": "management", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "The token to retrieve the next set of results for the list operation. TorchServe provides the token when the response from a previous call has more results than the maximum page size." }, + "managementAddress": { "index": 11, "kind": "property", "displayName": "Management Address", "group": "management", "label": "management", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "The address of the management API endpoint." }, + "managementPort": { "index": 12, "kind": "property", "displayName": "Management Port", "group": "management", "label": "management", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 8081, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "The port of the management API endpoint." }, + "registerOptions": { "index": 13, "kind": "property", "displayName": "Register Options", "group": "management", "label": "management", "required": false, "type": "object", "javaType": "org.apache.camel.component.torchserve.client.model.RegisterOptions", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "Additional options for the register operation." }, + "scaleWorkerOptions": { "index": 14, "kind": "property", "displayName": "Scale Worker Options", "group": "management", "label": "management", "required": false, "type": "object", "javaType": "org.apache.camel.component.torchserve.client.model.ScaleWorkerOptions", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "Additional options for the scale-worker operation." }, + "unregisterOptions": { "index": 15, "kind": "property", "displayName": "Unregister Options", "group": "management", "label": "management", "required": false, "type": "object", "javaType": "org.apache.camel.component.torchserve.client.model.UnregisterOptions", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "Additional options for the unregister operation." }, + "url": { "index": 16, "kind": "property", "displayName": "Url", "group": "management", "label": "management", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "Model archive download url, support local file or HTTP(s) protocol. For S3, consider using pre-signed url." }, + "metricsAddress": { "index": 17, "kind": "property", "displayName": "Metrics Address", "group": "metrics", "label": "metrics", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "The address of the metrics API endpoint." }, + "metricsName": { "index": 18, "kind": "property", "displayName": "Metrics Name", "group": "metrics", "label": "metrics", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "Names of metrics to filter." }, + "metricsPort": { "index": 19, "kind": "property", "displayName": "Metrics Port", "group": "metrics", "label": "metrics", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 8082, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "The port of the metrics API endpoint." }, + "inferenceKey": { "index": 20, "kind": "property", "displayName": "Inference Key", "group": "security", "label": "inference,security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "The token authorization key for accessing the inference API." }, + "managementKey": { "index": 21, "kind": "property", "displayName": "Management Key", "group": "security", "label": "management,security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "The token authorization key for accessing the management API." } + }, + "headers": { + "CamelTorchServeModelName": { "index": 0, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The name of model.", "constantName": "org.apache.camel.component.torchserve.TorchServeConstants#MODEL_NAME" }, + "CamelTorchServeModelVersion": { "index": 1, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The version of model.", "constantName": "org.apache.camel.component.torchserve.TorchServeConstants#MODEL_VERSION" }, + "CamelTorchServeRegisterOptions": { "index": 2, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "org.apache.camel.component.torchserve.client.model.RegisterOptions", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Additional options for the register operation.", "constantName": "org.apache.camel.component.torchserve.TorchServeConstants#REGISTER_OPTIONS" }, + "CamelTorchServeScaleWorkerOptions": { "index": 3, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "org.apache.camel.component.torchserve.client.model.ScaleWorkerOptions", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Additional options for the scale-worker operation.", "constantName": "org.apache.camel.component.torchserve.TorchServeConstants#SCALE_WORKER_OPTIONS" }, + "CamelTorchServeUnrsegisterOptions": { "index": 4, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "org.apache.camel.component.torchserve.client.model.UnregisterOptions", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Additional options for the unregister operation.", "constantName": "org.apache.camel.component.torchserve.TorchServeConstants#UNREGISTER_OPTIONS" } + }, + "properties": { + "api": { "index": 0, "kind": "path", "displayName": "Api", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "enum": [ "inference", "management", "metrics" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The TorchServe API" }, + "operation": { "index": 1, "kind": "path", "displayName": "Operation", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "enum": [ "ping", "predictions", "explanations", "register", "scale-worker", "describe", "unregister", "list", "set-default", "metrics" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The API operation" }, + "modelName": { "index": 2, "kind": "parameter", "displayName": "Model Name", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "The name of model." }, + "modelVersion": { "index": 3, "kind": "parameter", "displayName": "Model Version", "group": "common", "label": "common", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "The version of model." }, + "lazyStartProducer": { "index": 4, "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 producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." }, + "inferenceAddress": { "index": 5, "kind": "parameter", "displayName": "Inference Address", "group": "inference", "label": "inference", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "The address of the inference API endpoint." }, + "inferencePort": { "index": 6, "kind": "parameter", "displayName": "Inference Port", "group": "inference", "label": "inference", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 8080, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "The port of the inference API endpoint." }, + "listLimit": { "index": 7, "kind": "parameter", "displayName": "List Limit", "group": "management", "label": "management", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 100, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "The maximum number of items to return for the list operation. When this value is present, TorchServe does not return more than the specified number of items, but it might return fewer. This value is optional. If you include a value, it must be between 1 and 1000, inclusive. If you do not include a value, it defaults to 100." }, + "listNextPageToken": { "index": 8, "kind": "parameter", "displayName": "List Next Page Token", "group": "management", "label": "management", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "The token to retrieve the next set of results for the list operation. TorchServe provides the token when the response from a previous call has more results than the maximum page size." }, + "managementAddress": { "index": 9, "kind": "parameter", "displayName": "Management Address", "group": "management", "label": "management", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "The address of the management API endpoint." }, + "managementPort": { "index": 10, "kind": "parameter", "displayName": "Management Port", "group": "management", "label": "management", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 8081, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "The port of the management API endpoint." }, + "registerOptions": { "index": 11, "kind": "parameter", "displayName": "Register Options", "group": "management", "label": "management", "required": false, "type": "object", "javaType": "org.apache.camel.component.torchserve.client.model.RegisterOptions", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "Additional options for the register operation." }, + "scaleWorkerOptions": { "index": 12, "kind": "parameter", "displayName": "Scale Worker Options", "group": "management", "label": "management", "required": false, "type": "object", "javaType": "org.apache.camel.component.torchserve.client.model.ScaleWorkerOptions", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "Additional options for the scale-worker operation." }, + "unregisterOptions": { "index": 13, "kind": "parameter", "displayName": "Unregister Options", "group": "management", "label": "management", "required": false, "type": "object", "javaType": "org.apache.camel.component.torchserve.client.model.UnregisterOptions", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "Additional options for the unregister operation." }, + "url": { "index": 14, "kind": "parameter", "displayName": "Url", "group": "management", "label": "management", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "Model archive download url, support local file or HTTP(s) protocol. For S3, consider using pre-signed url." }, + "metricsAddress": { "index": 15, "kind": "parameter", "displayName": "Metrics Address", "group": "metrics", "label": "metrics", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "The address of the metrics API endpoint." }, + "metricsName": { "index": 16, "kind": "parameter", "displayName": "Metrics Name", "group": "metrics", "label": "metrics", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "Names of metrics to filter." }, + "metricsPort": { "index": 17, "kind": "parameter", "displayName": "Metrics Port", "group": "metrics", "label": "metrics", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 8082, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "The port of the metrics API endpoint." }, + "inferenceKey": { "index": 18, "kind": "parameter", "displayName": "Inference Key", "group": "security", "label": "inference,security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "The token authorization key for accessing the inference API." }, + "managementKey": { "index": 19, "kind": "parameter", "displayName": "Management Key", "group": "security", "label": "management,security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.torchserve.TorchServeConfiguration", "configurationField": "configuration", "description": "The token authorization key for accessing the management API." } + } +} diff --git a/components/camel-ai/camel-torchserve/src/generated/resources/META-INF/services/org/apache/camel/TypeConverterLoader b/components/camel-ai/camel-torchserve/src/generated/resources/META-INF/services/org/apache/camel/TypeConverterLoader new file mode 100644 index 0000000000000..1c8adb1830a1d --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/generated/resources/META-INF/services/org/apache/camel/TypeConverterLoader @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +org.apache.camel.component.torchserve.TorchServeConverterLoader diff --git a/components/camel-ai/camel-torchserve/src/generated/resources/META-INF/services/org/apache/camel/component.properties b/components/camel-ai/camel-torchserve/src/generated/resources/META-INF/services/org/apache/camel/component.properties new file mode 100644 index 0000000000000..e977e29ef9de4 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/generated/resources/META-INF/services/org/apache/camel/component.properties @@ -0,0 +1,7 @@ +# Generated by camel build tools - do NOT edit this file! +components=torchserve +groupId=org.apache.camel +artifactId=camel-torchserve +version=4.9.0-SNAPSHOT +projectName=Camel :: AI :: TorchServe +projectDescription=Provide access to PyTorch TorchServe servers to run inference with PyTorch models remotely diff --git a/components/camel-ai/camel-torchserve/src/generated/resources/META-INF/services/org/apache/camel/component/torchserve b/components/camel-ai/camel-torchserve/src/generated/resources/META-INF/services/org/apache/camel/component/torchserve new file mode 100644 index 0000000000000..b618071ec5c13 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/generated/resources/META-INF/services/org/apache/camel/component/torchserve @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.component.torchserve.TorchServeComponent diff --git a/components/camel-ai/camel-torchserve/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.component.torchserve.TorchServeConfiguration b/components/camel-ai/camel-torchserve/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.component.torchserve.TorchServeConfiguration new file mode 100644 index 0000000000000..00d58474fb51d --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.component.torchserve.TorchServeConfiguration @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.component.torchserve.TorchServeConfigurationConfigurer diff --git a/components/camel-ai/camel-torchserve/src/generated/resources/META-INF/services/org/apache/camel/configurer/torchserve-component b/components/camel-ai/camel-torchserve/src/generated/resources/META-INF/services/org/apache/camel/configurer/torchserve-component new file mode 100644 index 0000000000000..38a24c5111da0 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/generated/resources/META-INF/services/org/apache/camel/configurer/torchserve-component @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.component.torchserve.TorchServeComponentConfigurer diff --git a/components/camel-ai/camel-torchserve/src/generated/resources/META-INF/services/org/apache/camel/configurer/torchserve-endpoint b/components/camel-ai/camel-torchserve/src/generated/resources/META-INF/services/org/apache/camel/configurer/torchserve-endpoint new file mode 100644 index 0000000000000..0137c159d70ea --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/generated/resources/META-INF/services/org/apache/camel/configurer/torchserve-endpoint @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.component.torchserve.TorchServeEndpointConfigurer diff --git a/components/camel-ai/camel-torchserve/src/generated/resources/META-INF/services/org/apache/camel/urifactory/torchserve-endpoint b/components/camel-ai/camel-torchserve/src/generated/resources/META-INF/services/org/apache/camel/urifactory/torchserve-endpoint new file mode 100644 index 0000000000000..d7f59271b4eaf --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/generated/resources/META-INF/services/org/apache/camel/urifactory/torchserve-endpoint @@ -0,0 +1,2 @@ +# Generated by camel build tools - do NOT edit this file! +class=org.apache.camel.component.torchserve.TorchServeEndpointUriFactory diff --git a/components/camel-ai/camel-torchserve/src/main/docs/torchserve-component.adoc b/components/camel-ai/camel-torchserve/src/main/docs/torchserve-component.adoc new file mode 100644 index 0000000000000..6c169c8ebb0c8 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/docs/torchserve-component.adoc @@ -0,0 +1,236 @@ += TorchServe Component +:doctitle: TorchServe +:shortname: torchserve +:artifactid: camel-torchserve +:description: Provide access to PyTorch TorchServe servers to run inference with PyTorch models remotely +:since: 4.9 +:supportlevel: Preview +:tabs-sync-option: +:component-header: Only producer is supported +//Manually maintained attributes +:group: AI +:camel-spring-boot-name: torchserve + +*Since Camel {since}* + +*{component-header}* + +The TorchServe component provides support for invoking the https://pytorch.org/serve/rest_api.html[TorchServe REST API]. It enables Camel to access PyTorch TorchServe servers to run inference with PyTorch models remotely. + +To use the TorchServe component, Maven users will need to add the following dependency to their `pom.xml`: + +[source,xml] +---- + + org.apache.camel + camel-torchserve + x.x.x + + +---- + +== URI format + +---- +torchserve:api/operation[?options] +---- + +Where `api` represents one of the https://pytorch.org/serve/rest_api.html[TorchServe REST API], and `operation` represents a specific operation supported by the API. + +// component-configure options: START + +// component-configure options: END + +// component options: START +include::partial$component-configure-options.adoc[] +include::partial$component-endpoint-options.adoc[] +// component options: END + +// endpoint options: START + +// endpoint options: END + +// component headers: START +include::partial$component-endpoint-headers.adoc[] +// component headers: END + +== Usage + +Each API endpoint support the following operations. + +=== Inference API + +The Inference API provides the inference operations. + +---- +torchserve:inference/[?options] +---- + +[width="100%",cols="2,5,1,2",options="header"] +|=== +| Operation | Description | Options | Result + +| `ping` | Get TorchServe status. | - | `String` + +| `predictions` | Predictions entry point to get inference using a model. | +`modelName` + +`modelVersion` +| `Object` + +| `explanations` | Not supported yet. | - | `Object` +|=== + +=== Management API + +The Management API provides the operations to manage models at runtime. + +---- +torchserve:management/[?options] +---- + +[width="100%",cols="2,5,1,2",options="header"] +|=== +| Operation | Description | Options | Result + +| `register` | Register a new model in TorchServe. | +`url` + +`registerOptions` +| `String` + +| `scale-worker` +| Configure number of workers for a model. This is an asynchronous call by default. +Caller need to call `describe` to check if the model workers has been changed. | +`modelName` + +`modelVersion` + +`scaleWorkerOptions` +| `String` + +| `describe` +| Provides detailed information about a model. If "all" is specified as version, +returns the details about all the versions of the model. | +`modelName` + +`modelVersion` +| `List` footnote:[`org.apache.camel.component.torchserve.client.model.ModelDetail`] + +| `unregister` +| Unregister a model from TorchServe. This is an asynchronous call by default. +Caller can call `list` to confirm the model is unregistered. | +`modelName` + +`modelVersion` + +`unregisterOptions` +| `String` + +| `list` | List registered models in TorchServe. | +`listLimit` + +`listNextPageToken` +| `ModelList` footnote:[`org.apache.camel.component.torchserve.client.model.ModelList`] + +| `set-default` | Set default version of a model. | +`modelName` + +`modelVersion` +| `String` +|=== + +=== Metrics API + +The Metrics API provides the operations to fetch metrics in the Prometheus format. + +---- +torchserve:mertrics/[?options] +---- + +[width="100%",cols="2,5,1,2",options="header"] +|=== +| operation | Description | Headers | Result +| `metrics` | Get TorchServe application metrics in prometheus format. | `metricsName` | `String` +|=== + +== Examples + +=== Inference API + +.Health checking +[source,java] +---- +from("direct:ping") + .to("torchserve:inference/ping") + .log("Status: ${body}"); +---- + +.Prediction +[source,java] +---- +from("file:data/kitten.jpg") + .to("torchserve:inference/predictions?modelName=squeezenet1_1") + .log("Result: ${body}");; +---- + +=== Management API + +.Register a model +[source,java] +---- +from("direct:register") + .to("torchserve:management/register?url=https://torchserve.pytorch.org/mar_files/mnist_v2.mar") + .log("Status: ${body}"); +---- + +.Scale workers for a registered model +[source,java] +---- +from("direct:scale-worker") + .setHeader(TorchServeConstants.SCALE_WORKER_OPTIONS, + constant(ScaleWorkerOptions.builder().minWorker(1).maxWorker(2).build())) + .to("torchserve:management/scale-worker?modelName=mnist_v2") + .log("Status: ${body}"); +---- + +.Get the detailed information about a model +[source,java] +---- +from("direct:describe") + .to("torchserve:management/describe?modelName=mnist_v2") + .log("${body[0]}"); +---- + +.Unregister a model +[source,java] +---- +from("direct:register") + .to("torchserve:management/unregister?modelName=mnist_v2") + .log("Status: ${body}"); +---- + +.List models +[source,java] +---- +from("direct:list") + .to("torchserve:management/list") + .log("${body.models}"); +---- + +.Set the default version of a model +[source,java] +---- +from("direct:set-default") + .to("torchserve:management/set-default?modelName=mnist_v2&modelVersion=2.0") + .log("Status: ${body}"); +---- + +=== Metrics API + +.All metrics +[source,java] +---- +from("direct:metrics") + .to("torchserve:metrics/metrics"); +---- + +.`MemoryUsed` metrics only +[source,java] +---- +from("direct:metrics") + .to("torchserve:metrics/metrics?metricsName=MemoryUsed"); +---- + +include::spring-boot:partial$starter.adoc[] diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/TorchServeComponent.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/TorchServeComponent.java new file mode 100644 index 0000000000000..f1ba1c17bd9f9 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/TorchServeComponent.java @@ -0,0 +1,59 @@ +/* + * 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.torchserve; + +import java.util.Map; + +import org.apache.camel.CamelContext; +import org.apache.camel.Endpoint; +import org.apache.camel.spi.Metadata; +import org.apache.camel.spi.annotations.Component; +import org.apache.camel.support.HealthCheckComponent; + +@Component("torchserve") +public class TorchServeComponent extends HealthCheckComponent { + + @Metadata + private TorchServeConfiguration configuration = new TorchServeConfiguration(); + + public TorchServeComponent() { + super(); + } + + public TorchServeComponent(CamelContext context) { + super(context); + } + + protected Endpoint createEndpoint(String uri, String remaining, Map parameters) throws Exception { + TorchServeConfiguration configuration + = this.configuration != null ? this.configuration.copy() : new TorchServeConfiguration(); + Endpoint endpoint = new TorchServeEndpoint(uri, this, remaining, configuration); + setProperties(endpoint, parameters); + return endpoint; + } + + public TorchServeConfiguration getConfiguration() { + return configuration; + } + + /** + * The configuration. + */ + public void setConfiguration(TorchServeConfiguration configuration) { + this.configuration = configuration; + } +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/TorchServeConfiguration.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/TorchServeConfiguration.java new file mode 100644 index 0000000000000..6b70288604ac3 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/TorchServeConfiguration.java @@ -0,0 +1,279 @@ +/* + * 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.torchserve; + +import org.apache.camel.RuntimeCamelException; +import org.apache.camel.component.torchserve.client.model.RegisterOptions; +import org.apache.camel.component.torchserve.client.model.ScaleWorkerOptions; +import org.apache.camel.component.torchserve.client.model.UnregisterOptions; +import org.apache.camel.spi.Configurer; +import org.apache.camel.spi.UriParam; +import org.apache.camel.spi.UriParams; + +@UriParams +@Configurer +public class TorchServeConfiguration implements Cloneable { + + @UriParam(label = "inference,security") + private String inferenceKey; + + @UriParam(label = "inference") + private String inferenceAddress; + + @UriParam(label = "inference", defaultValue = "8080") + private int inferencePort = 8080; + + @UriParam(label = "management,security") + private String managementKey; + + @UriParam(label = "management") + private String managementAddress; + + @UriParam(label = "management", defaultValue = "8081") + private int managementPort = 8081; + + @UriParam(label = "metrics") + private String metricsAddress; + + @UriParam(label = "metrics", defaultValue = "8082") + private int metricsPort = 8082; + + @UriParam(label = "common") + private String modelName; + + @UriParam(label = "common") + private String modelVersion; + + @UriParam(label = "management") + private String url; + + @UriParam(label = "management") + private RegisterOptions registerOptions; + + @UriParam(label = "management") + private ScaleWorkerOptions scaleWorkerOptions; + + @UriParam(label = "management") + private UnregisterOptions unregisterOptions; + + @UriParam(label = "management", defaultValue = "100") + private int listLimit = 100; + + @UriParam(label = "management") + private String listNextPageToken; + + @UriParam(label = "metrics") + private String metricsName; + + public String getMetricsName() { + return metricsName; + } + + public String getInferenceKey() { + return inferenceKey; + } + + /** + * The token authorization key for accessing the inference API. + */ + public void setInferenceKey(String inferenceKey) { + this.inferenceKey = inferenceKey; + } + + public String getInferenceAddress() { + return inferenceAddress; + } + + /** + * The address of the inference API endpoint. + */ + public void setInferenceAddress(String inferenceAddress) { + this.inferenceAddress = inferenceAddress; + } + + public int getInferencePort() { + return inferencePort; + } + + /** + * The port of the inference API endpoint. + */ + public void setInferencePort(int inferencePort) { + this.inferencePort = inferencePort; + } + + public String getManagementKey() { + return managementKey; + } + + /** + * The token authorization key for accessing the management API. + */ + public void setManagementKey(String managementKey) { + this.managementKey = managementKey; + } + + public String getManagementAddress() { + return managementAddress; + } + + /** + * The address of the management API endpoint. + */ + public void setManagementAddress(String managementAddress) { + this.managementAddress = managementAddress; + } + + public int getManagementPort() { + return managementPort; + } + + /** + * The port of the management API endpoint. + */ + public void setManagementPort(int managementPort) { + this.managementPort = managementPort; + } + + public String getMetricsAddress() { + return metricsAddress; + } + + /** + * The address of the metrics API endpoint. + */ + public void setMetricsAddress(String metricsAddress) { + this.metricsAddress = metricsAddress; + } + + public int getMetricsPort() { + return metricsPort; + } + + /** + * The port of the metrics API endpoint. + */ + public void setMetricsPort(int metricsPort) { + this.metricsPort = metricsPort; + } + + public String getModelName() { + return modelName; + } + + /** + * The name of model. + */ + public void setModelName(String modelName) { + this.modelName = modelName; + } + + public String getModelVersion() { + return modelVersion; + } + + /** + * The version of model. + */ + public void setModelVersion(String modelVersion) { + this.modelVersion = modelVersion; + } + + public String getUrl() { + return url; + } + + /** + * Model archive download url, support local file or HTTP(s) protocol. For S3, consider using pre-signed url. + */ + public void setUrl(String url) { + this.url = url; + } + + public RegisterOptions getRegisterOptions() { + return registerOptions; + } + + /** + * Additional options for the register operation. + */ + public void setRegisterOptions(RegisterOptions registerOptions) { + this.registerOptions = registerOptions; + } + + public ScaleWorkerOptions getScaleWorkerOptions() { + return scaleWorkerOptions; + } + + /** + * Additional options for the scale-worker operation. + */ + public void setScaleWorkerOptions(ScaleWorkerOptions scaleWorkerOptions) { + this.scaleWorkerOptions = scaleWorkerOptions; + } + + public UnregisterOptions getUnregisterOptions() { + return unregisterOptions; + } + + /** + * Additional options for the unregister operation. + */ + public void setUnregisterOptions(UnregisterOptions unregisterOptions) { + this.unregisterOptions = unregisterOptions; + } + + public int getListLimit() { + return listLimit; + } + + /** + * The maximum number of items to return for the list operation. When this value is present, TorchServe does not + * return more than the specified number of items, but it might return fewer. This value is optional. If you include + * a value, it must be between 1 and 1000, inclusive. If you do not include a value, it defaults to 100. + */ + public void setListLimit(int listLimit) { + this.listLimit = listLimit; + } + + public String getListNextPageToken() { + return listNextPageToken; + } + + /** + * The token to retrieve the next set of results for the list operation. TorchServe provides the token when the + * response from a previous call has more results than the maximum page size. + */ + public void setListNextPageToken(String listNextPageToken) { + this.listNextPageToken = listNextPageToken; + } + + /** + * Names of metrics to filter. + */ + public void setMetricsName(String metricsName) { + this.metricsName = metricsName; + } + + public TorchServeConfiguration copy() { + try { + return (TorchServeConfiguration) clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeCamelException(e); + } + } +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/TorchServeConstants.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/TorchServeConstants.java new file mode 100644 index 0000000000000..152086ada7064 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/TorchServeConstants.java @@ -0,0 +1,44 @@ +/* + * 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.torchserve; + +import org.apache.camel.spi.Metadata; + +/** + * Constants used in Camel TorchServe component. + */ +public interface TorchServeConstants { + + @Metadata(description = "The name of model.", javaType = "String") + String MODEL_NAME = "CamelTorchServeModelName"; + + @Metadata(description = "The version of model.", javaType = "String") + String MODEL_VERSION = "CamelTorchServeModelVersion"; + + @Metadata(description = "Additional options for the register operation.", + javaType = "org.apache.camel.component.torchserve.client.model.RegisterOptions") + String REGISTER_OPTIONS = "CamelTorchServeRegisterOptions"; + + @Metadata(description = "Additional options for the scale-worker operation.", + javaType = "org.apache.camel.component.torchserve.client.model.ScaleWorkerOptions") + String SCALE_WORKER_OPTIONS = "CamelTorchServeScaleWorkerOptions"; + + @Metadata(description = "Additional options for the unregister operation.", + javaType = "org.apache.camel.component.torchserve.client.model.UnregisterOptions") + String UNREGISTER_OPTIONS = "CamelTorchServeUnrsegisterOptions"; + +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/TorchServeConverter.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/TorchServeConverter.java new file mode 100644 index 0000000000000..bc605a941b600 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/TorchServeConverter.java @@ -0,0 +1,26 @@ +/* + * 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.torchserve; + +import org.apache.camel.Converter; + +/** + * Converter methods to convert from / to TorchServe types. + */ +@Converter(generateLoader = true) +public class TorchServeConverter { +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/TorchServeEndpoint.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/TorchServeEndpoint.java new file mode 100644 index 0000000000000..b152b9934c393 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/TorchServeEndpoint.java @@ -0,0 +1,142 @@ +/* + * 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.torchserve; + +import org.apache.camel.Category; +import org.apache.camel.Consumer; +import org.apache.camel.Processor; +import org.apache.camel.Producer; +import org.apache.camel.component.torchserve.client.TorchServeClient; +import org.apache.camel.component.torchserve.producer.InferenceProducer; +import org.apache.camel.component.torchserve.producer.ManagementProducer; +import org.apache.camel.component.torchserve.producer.MetricsProducer; +import org.apache.camel.spi.Metadata; +import org.apache.camel.spi.UriEndpoint; +import org.apache.camel.spi.UriParam; +import org.apache.camel.spi.UriPath; +import org.apache.camel.support.DefaultEndpoint; + +@UriEndpoint(firstVersion = "4.9.0", scheme = "torchserve", title = "TorchServe", + syntax = "torchserve:api/operation", producerOnly = true, + category = { Category.AI }, headersClass = TorchServeConstants.class) +public class TorchServeEndpoint extends DefaultEndpoint { + + @UriPath(enums = "inference,management,metrics", description = "The TorchServe API") + @Metadata(required = true) + private final String api; + + @UriPath(enums = "ping,predictions,explanations,register,scale-worker,describe,unregister,list,set-default,metrics", + description = "The API operation") + @Metadata(required = true) + private final String operation; + + @UriParam + private TorchServeConfiguration configuration; + + private TorchServeClient client; + + public TorchServeEndpoint(String uri, TorchServeComponent component, String path, + TorchServeConfiguration configuration) { + super(uri, component); + String[] parts = extractPath(path); + this.api = parts[0]; + this.operation = parts[1]; + this.configuration = configuration; + } + + private static String[] extractPath(String path) { + if (path == null) { + throw new IllegalArgumentException("path must not be null"); + } + String[] parts = path.split("/"); + if (parts.length != 2) { + throw new IllegalArgumentException("path must be /: " + path); + } + return parts; + } + + @Override + protected void doInit() throws Exception { + super.doInit(); + + client = createClient(); + } + + private TorchServeClient createClient() { + TorchServeClient.Builder builder = TorchServeClient.builder(); + // Inference + if (configuration.getInferenceKey() != null) { + builder.inferenceKey(configuration.getInferenceKey()); + } + if (configuration.getInferenceAddress() != null) { + builder.inferenceAddress(configuration.getInferenceAddress()); + } else { + builder.inferencePort(configuration.getInferencePort()); + } + // Management + if (configuration.getManagementKey() != null) { + builder.managementKey(configuration.getManagementKey()); + } + if (configuration.getManagementAddress() != null) { + builder.managementAddress(configuration.getManagementAddress()); + } else { + builder.managementPort(configuration.getManagementPort()); + } + // Metrics + if (configuration.getMetricsAddress() != null) { + builder.metricsAddress(configuration.getMetricsAddress()); + } else { + builder.metricsPort(configuration.getMetricsPort()); + } + return builder.build(); + } + + @Override + public Producer createProducer() { + return switch (api) { + case "inference" -> new InferenceProducer(this); + case "management" -> new ManagementProducer(this); + case "metrics" -> new MetricsProducer(this); + default -> throw new IllegalArgumentException("Unknown API: " + api); + }; + } + + @Override + public Consumer createConsumer(Processor processor) { + throw new UnsupportedOperationException("Consumer not supported"); + } + + public String getApi() { + return api; + } + + public String getOperation() { + return operation; + } + + public TorchServeConfiguration getConfiguration() { + return configuration; + } + + public void setConfiguration(TorchServeConfiguration configuration) { + this.configuration = configuration; + } + + public TorchServeClient getClient() { + return client; + } +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/Inference.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/Inference.java new file mode 100644 index 0000000000000..e33245a0fe38b --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/Inference.java @@ -0,0 +1,53 @@ +/* + * 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.torchserve.client; + +import org.apache.camel.component.torchserve.client.model.Api; +import org.apache.camel.component.torchserve.client.model.ApiException; +import org.apache.camel.component.torchserve.client.model.Response; + +/** + * Inference API + */ +public interface Inference { + + /** + * Get openapi description. + */ + Api apiDescription() throws ApiException; + + /** + * Get TorchServe status. + */ + Response ping() throws ApiException; + + /** + * Predictions entry point to get inference using default model version. + */ + Object predictions(String modelName, Object body) throws ApiException; + + /** + * Predictions entry point to get inference using specific model version. + */ + Object predictions(String modelName, String modelVersion, Object body) throws ApiException; + + /** + * Not supported yet. + */ + Object explanations(String modelName) throws ApiException; + +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/Management.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/Management.java new file mode 100644 index 0000000000000..6629d512b7836 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/Management.java @@ -0,0 +1,95 @@ +/* + * 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.torchserve.client; + +import java.util.List; + +import org.apache.camel.component.torchserve.client.model.Api; +import org.apache.camel.component.torchserve.client.model.ApiException; +import org.apache.camel.component.torchserve.client.model.ModelDetail; +import org.apache.camel.component.torchserve.client.model.ModelList; +import org.apache.camel.component.torchserve.client.model.RegisterOptions; +import org.apache.camel.component.torchserve.client.model.Response; +import org.apache.camel.component.torchserve.client.model.ScaleWorkerOptions; +import org.apache.camel.component.torchserve.client.model.UnregisterOptions; + +/** + * Management API + */ +public interface Management { + + /** + * Register a new model in TorchServe. + */ + Response registerModel(String url, RegisterOptions options) throws ApiException; + + /** + * Configure number of workers for a default version of a model. This is an asynchronous call by default. Caller + * need to call describeModel to check if the model workers has been changed. + */ + Response setAutoScale(String modelName, ScaleWorkerOptions options) throws ApiException; + + /** + * Configure number of workers for a specified version of a model. This is an asynchronous call by default. Caller + * need to call describeModel to check if the model workers has been changed. + */ + Response setAutoScale(String modelName, String modelVersion, ScaleWorkerOptions options) throws ApiException; + + /** + * Provides detailed information about the default version of a model. + */ + List describeModel(String modelName) throws ApiException; + + /** + * Provides detailed information about the specified version of a model. If "all" is specified as version, returns + * the details about all the versions of the model. + */ + List describeModel(String modelName, String modelVersion) throws ApiException; + + /** + * Unregister the default version of a model from TorchServe if it is the only version available. This is an + * asynchronous call by default. Caller can call listModels to confirm model is unregistered. + */ + Response unregisterModel(String modelName, UnregisterOptions options) throws ApiException; + + /** + * Unregister the specified version of a model from TorchServe. This is an asynchronous call by default. Caller can + * call listModels to confirm model is unregistered. + */ + Response unregisterModel(String modelName, String modelVersion, UnregisterOptions options) throws ApiException; + + /** + * List registered models in TorchServe. + */ + ModelList listModels(Integer limit, String nextPageToken) throws ApiException; + + /** + * Set default version of a model. + */ + Response setDefault(String modelName, String modelVersion) throws ApiException; + + /** + * Get openapi description. + */ + Api apiDescription() throws ApiException; + + /** + * Not supported yet. + */ + Object token(String type) throws ApiException; + +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/Metrics.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/Metrics.java new file mode 100644 index 0000000000000..f31cc22445339 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/Metrics.java @@ -0,0 +1,36 @@ +/* + * 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.torchserve.client; + +import org.apache.camel.component.torchserve.client.model.ApiException; + +/** + * Metrics API + */ +public interface Metrics { + + /** + * Get TorchServe application metrics in prometheus format. + */ + String metrics() throws ApiException; + + /** + * Get TorchServe application metrics in prometheus format. + */ + String metrics(String name) throws ApiException; + +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/TorchServeClient.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/TorchServeClient.java new file mode 100644 index 0000000000000..f2634ac9f251f --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/TorchServeClient.java @@ -0,0 +1,127 @@ +/* + * 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.torchserve.client; + +import java.util.Optional; + +import org.apache.camel.component.torchserve.client.impl.DefaultInference; +import org.apache.camel.component.torchserve.client.impl.DefaultManagement; +import org.apache.camel.component.torchserve.client.impl.DefaultMetrics; + +public class TorchServeClient { + + private final Inference inference; + private final Management management; + private final Metrics metrics; + + private TorchServeClient(Inference inference, Management management, Metrics metrics) { + this.inference = inference; + this.management = management; + this.metrics = metrics; + } + + public static TorchServeClient newInstance() { + return builder().build(); + } + + public static Builder builder() { + return new Builder(); + } + + public Inference inference() { + return inference; + } + + public Management management() { + return management; + } + + public Metrics metrics() { + return metrics; + } + + public static class Builder { + + private Optional inferenceKey = Optional.empty(); + private Optional inferenceAddress = Optional.empty(); + private Optional inferencePort = Optional.empty(); + + private Optional managementKey = Optional.empty(); + private Optional managementAddress = Optional.empty(); + private Optional managementPort = Optional.empty(); + + private Optional metricsAddress = Optional.empty(); + private Optional metricsPort = Optional.empty(); + + public Builder inferenceKey(String key) { + this.inferenceKey = Optional.of(key); + return this; + } + + public Builder inferenceAddress(String address) { + this.inferenceAddress = Optional.of(address); + return this; + } + + public Builder inferencePort(int port) { + this.inferencePort = Optional.of(port); + return this; + } + + public Builder managementKey(String key) { + this.managementKey = Optional.of(key); + return this; + } + + public Builder managementAddress(String address) { + this.managementAddress = Optional.of(address); + return this; + } + + public Builder managementPort(int port) { + this.managementPort = Optional.of(port); + return this; + } + + public Builder metricsAddress(String address) { + this.metricsAddress = Optional.of(address); + return this; + } + + public Builder metricsPort(Integer port) { + this.metricsPort = Optional.of(port); + return this; + } + + public TorchServeClient build() { + DefaultInference inference = inferenceAddress.map(DefaultInference::new) + .or(() -> inferencePort.map(DefaultInference::new)) + .orElse(new DefaultInference()); + inferenceKey.ifPresent(inference::setAuthToken); + + DefaultManagement management = managementAddress.map(DefaultManagement::new) + .or(() -> managementPort.map(DefaultManagement::new)) + .orElse(new DefaultManagement()); + managementKey.ifPresent(management::setAuthToken); + + DefaultMetrics metrics = metricsAddress.map(DefaultMetrics::new) + .or(() -> metricsPort.map(DefaultMetrics::new)) + .orElse(new DefaultMetrics()); + return new TorchServeClient(inference, management, metrics); + } + } +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/impl/DefaultInference.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/impl/DefaultInference.java new file mode 100644 index 0000000000000..70deafcd6f202 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/impl/DefaultInference.java @@ -0,0 +1,97 @@ +/* + * 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.torchserve.client.impl; + +import java.util.Map; + +import org.apache.camel.component.torchserve.client.Inference; +import org.apache.camel.component.torchserve.client.inference.api.DefaultApi; +import org.apache.camel.component.torchserve.client.inference.invoker.ApiClient; +import org.apache.camel.component.torchserve.client.model.Api; +import org.apache.camel.component.torchserve.client.model.ApiException; +import org.apache.camel.component.torchserve.client.model.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DefaultInference implements Inference { + + private static final Logger LOG = LoggerFactory.getLogger(DefaultInference.class); + + private final DefaultApi api; + + public DefaultInference() { + this(8080); + } + + public DefaultInference(int port) { + this("http://localhost:" + port); + } + + public DefaultInference(String address) { + ApiClient client = new ApiClient().setBasePath(address); + this.api = new DefaultApi(client); + LOG.debug("Inference API address: {}", address); + } + + public void setAuthToken(String token) { + api.getApiClient().addDefaultHeader("Authorization", "Bearer " + token); + } + + @Override + public Api apiDescription() throws ApiException { + try { + // Workaround for HTTPClient 5.4 requiring content-type for OPTIONS requests + return Api.from(api.apiDescription(Map.of("Content-Type", "application/json"))); + } catch (org.apache.camel.component.torchserve.client.inference.invoker.ApiException e) { + throw new ApiException(e); + } + } + + @Override + public Response ping() throws ApiException { + try { + return Response.from(api.ping()); + } catch (org.apache.camel.component.torchserve.client.inference.invoker.ApiException e) { + throw new ApiException(e); + } + } + + @Override + public Object predictions(String modelName, Object body) throws ApiException { + try { + // /predictions/{model_name} + return api.predictions_1(modelName, body); + } catch (org.apache.camel.component.torchserve.client.inference.invoker.ApiException e) { + throw new ApiException(e); + } + } + + @Override + public Object predictions(String modelName, String modelVersion, Object body) throws ApiException { + try { + return api.versionPredictions(modelName, modelVersion, body); + } catch (org.apache.camel.component.torchserve.client.inference.invoker.ApiException e) { + throw new ApiException(e); + } + } + + @Override + public Object explanations(String modelName) { + throw new UnsupportedOperationException("Not supported yet"); + } + +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/impl/DefaultManagement.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/impl/DefaultManagement.java new file mode 100644 index 0000000000000..c57cd029fa94c --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/impl/DefaultManagement.java @@ -0,0 +1,183 @@ +/* + * 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.torchserve.client.impl; + +import java.util.List; +import java.util.Map; + +import org.apache.camel.component.torchserve.client.Management; +import org.apache.camel.component.torchserve.client.management.api.DefaultApi; +import org.apache.camel.component.torchserve.client.management.invoker.ApiClient; +import org.apache.camel.component.torchserve.client.management.model.DescribeModel200ResponseInner; +import org.apache.camel.component.torchserve.client.model.Api; +import org.apache.camel.component.torchserve.client.model.ApiException; +import org.apache.camel.component.torchserve.client.model.ModelDetail; +import org.apache.camel.component.torchserve.client.model.ModelList; +import org.apache.camel.component.torchserve.client.model.RegisterOptions; +import org.apache.camel.component.torchserve.client.model.Response; +import org.apache.camel.component.torchserve.client.model.ScaleWorkerOptions; +import org.apache.camel.component.torchserve.client.model.UnregisterOptions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DefaultManagement implements Management { + + private static final Logger LOG = LoggerFactory.getLogger(DefaultManagement.class); + + private final DefaultApi api; + + public DefaultManagement() { + this(8081); + } + + public DefaultManagement(int port) { + this("http://localhost:" + port); + } + + public DefaultManagement(String address) { + ApiClient client = new ApiClient().setBasePath(address); + this.api = new DefaultApi(client); + LOG.debug("Management API address: {}", address); + } + + public void setAuthToken(String token) { + api.getApiClient().addDefaultHeader("Authorization", "Bearer " + token); + } + + @Override + public Response registerModel(String url, RegisterOptions options) throws ApiException { + try { + return Response.from(api.registerModel(url, + options.getModelName(), + options.getHandler(), + options.getRuntime(), + options.getBatchSize(), + options.getMaxBatchDelay(), + options.getResponseTimeout(), + options.getInitialWorkers(), + options.getSynchronous(), + options.getS3SseKms(), + null)); + } catch (org.apache.camel.component.torchserve.client.management.invoker.ApiException e) { + throw new ApiException(e); + } + } + + @Override + public Response setAutoScale(String modelName, ScaleWorkerOptions options) throws ApiException { + try { + return Response.from(api.setAutoScale(modelName, + options.getMinWorker(), + options.getMaxWorker(), + options.getNumberGpu(), + options.getSynchronous(), + options.getTimeout())); + } catch (org.apache.camel.component.torchserve.client.management.invoker.ApiException e) { + throw new ApiException(e); + } + } + + @Override + public Response setAutoScale(String modelName, String modelVersion, ScaleWorkerOptions options) throws ApiException { + try { + return Response.from(api.versionSetAutoScale(modelName, modelVersion, + options.getMinWorker(), + options.getMaxWorker(), + options.getNumberGpu(), + options.getSynchronous(), + options.getTimeout())); + } catch (org.apache.camel.component.torchserve.client.management.invoker.ApiException e) { + throw new ApiException(e); + } + } + + @Override + public List describeModel(String modelName) throws ApiException { + try { + List response = api.describeModel(modelName); + return response.stream().map(ModelDetail::from).toList(); + } catch (org.apache.camel.component.torchserve.client.management.invoker.ApiException e) { + throw new ApiException(e); + } + } + + @Override + public List describeModel(String modelName, String modelVersion) throws ApiException { + try { + List response = api.versionDescribeModel(modelName, modelVersion); + return response.stream().map(ModelDetail::from).toList(); + } catch (org.apache.camel.component.torchserve.client.management.invoker.ApiException e) { + throw new ApiException(e); + } + } + + @Override + public Response unregisterModel(String modelName, UnregisterOptions options) throws ApiException { + try { + return Response.from(api.unregisterModel(modelName, + options.getSynchronous(), + options.getTimeout())); + } catch (org.apache.camel.component.torchserve.client.management.invoker.ApiException e) { + throw new ApiException(e); + } + } + + @Override + public Response unregisterModel(String modelName, String modelVersion, UnregisterOptions options) + throws ApiException { + try { + return Response.from(api.versionUnregisterModel(modelName, modelVersion, + options.getSynchronous(), + options.getTimeout())); + } catch (org.apache.camel.component.torchserve.client.management.invoker.ApiException e) { + throw new ApiException(e); + } + } + + @Override + public ModelList listModels(Integer limit, String nextPageToken) throws ApiException { + try { + return ModelList.from(api.listModels(limit, nextPageToken)); + } catch (org.apache.camel.component.torchserve.client.management.invoker.ApiException e) { + throw new ApiException(e); + } + } + + @Override + public Response setDefault(String modelName, String modelVersion) throws ApiException { + try { + return Response.from(api.setDefault(modelName, modelVersion)); + } catch (org.apache.camel.component.torchserve.client.management.invoker.ApiException e) { + throw new ApiException(e); + } + } + + @Override + public Api apiDescription() throws ApiException { + try { + // Workaround for HTTPClient 5.4 requiring content-type for OPTIONS requests + return Api.from(api.apiDescription(Map.of("Content-Type", "application/json"))); + } catch (org.apache.camel.component.torchserve.client.management.invoker.ApiException e) { + throw new ApiException(e); + } + } + + @Override + public Object token(String type) throws ApiException { + throw new UnsupportedOperationException("Not supported yet"); + } +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/impl/DefaultMetrics.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/impl/DefaultMetrics.java new file mode 100644 index 0000000000000..016c4930e36b4 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/impl/DefaultMetrics.java @@ -0,0 +1,60 @@ +/* + * 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.torchserve.client.impl; + +import org.apache.camel.component.torchserve.client.Metrics; +import org.apache.camel.component.torchserve.client.metrics.api.DefaultApi; +import org.apache.camel.component.torchserve.client.metrics.invoker.ApiClient; +import org.apache.camel.component.torchserve.client.model.ApiException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DefaultMetrics implements Metrics { + + private static final Logger LOG = LoggerFactory.getLogger(DefaultMetrics.class); + + private final DefaultApi api; + + public DefaultMetrics() { + this(8082); + } + + public DefaultMetrics(int port) { + this("http://localhost:" + port); + } + + public DefaultMetrics(String address) { + ApiClient client = new ApiClient().setBasePath(address); + this.api = new DefaultApi(client); + LOG.debug("Metrics API address: {}", address); + } + + @Override + public String metrics() throws ApiException { + return metrics(null); + } + + @Override + public String metrics(String name) throws ApiException { + try { + return api.metrics(name); + } catch (org.apache.camel.component.torchserve.client.metrics.invoker.ApiException e) { + throw new ApiException(e); + } + } + +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/Api.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/Api.java new file mode 100644 index 0000000000000..cf098a4b890ea --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/Api.java @@ -0,0 +1,81 @@ +/* + * 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.torchserve.client.model; + +import java.util.HashMap; +import java.util.Map; + +public class Api { + + private String openapi = null; + private Map info = new HashMap<>(); + private Map paths = new HashMap<>(); + + public Api() { + } + + @SuppressWarnings("unchecked") + public static Api from(org.apache.camel.component.torchserve.client.inference.model.ApiDescription200Response src) { + Api api = new Api(); + api.setOpenapi(src.getOpenapi()); + api.setInfo((Map) src.getInfo()); + api.setPaths((Map) src.getPaths()); + return api; + } + + @SuppressWarnings("unchecked") + public static Api from(org.apache.camel.component.torchserve.client.management.model.ApiDescription200Response src) { + Api api = new Api(); + api.setOpenapi(src.getOpenapi()); + api.setInfo((Map) src.getInfo()); + api.setPaths((Map) src.getPaths()); + return api; + } + + public String getOpenapi() { + return openapi; + } + + public void setOpenapi(String openapi) { + this.openapi = openapi; + } + + public Map getInfo() { + return info; + } + + public void setInfo(Map info) { + this.info = info; + } + + public Map getPaths() { + return paths; + } + + public void setPaths(Map paths) { + this.paths = paths; + } + + @Override + public String toString() { + return getClass().getSimpleName() + " {\n" + + " openapi: " + openapi + "\n" + + " info: " + info + "\n" + + " paths: " + paths + "\n" + + "}"; + } +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/ApiException.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/ApiException.java new file mode 100644 index 0000000000000..db1b7d53bc847 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/ApiException.java @@ -0,0 +1,60 @@ +/* + * 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.torchserve.client.model; + +import java.util.List; +import java.util.Map; + +public class ApiException extends Exception { + + private int code = 0; + private Map> responseHeaders = null; + private String responseBody = null; + + public ApiException(org.apache.camel.component.torchserve.client.inference.invoker.ApiException e) { + super(e.getResponseBody(), e); + this.code = e.getCode(); + this.responseHeaders = e.getResponseHeaders(); + this.responseBody = e.getResponseBody(); + } + + public ApiException(org.apache.camel.component.torchserve.client.management.invoker.ApiException e) { + super(e.getResponseBody(), e); + this.code = e.getCode(); + this.responseHeaders = e.getResponseHeaders(); + this.responseBody = e.getResponseBody(); + } + + public ApiException(org.apache.camel.component.torchserve.client.metrics.invoker.ApiException e) { + super(e.getResponseBody(), e); + this.code = e.getCode(); + this.responseHeaders = e.getResponseHeaders(); + this.responseBody = e.getResponseBody(); + } + + public int getCode() { + return code; + } + + public Map> getResponseHeaders() { + return responseHeaders; + } + + public String getResponseBody() { + return responseBody; + } +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/JobQueueStatus.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/JobQueueStatus.java new file mode 100644 index 0000000000000..98970b577097a --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/JobQueueStatus.java @@ -0,0 +1,63 @@ +/* + * 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.torchserve.client.model; + +import org.apache.camel.component.torchserve.client.management.model.DescribeModel200ResponseInnerJobQueueStatus; + +public class JobQueueStatus { + + private Integer remainingCapacity = null; + private Integer pendingRequests = null; + + public JobQueueStatus() { + } + + public static JobQueueStatus from(DescribeModel200ResponseInnerJobQueueStatus src) { + if (src == null) { + return null; + } + + JobQueueStatus status = new JobQueueStatus(); + status.setRemainingCapacity(src.getRemainingCapacity()); + status.setPendingRequests(src.getPendingRequests()); + return status; + } + + public Integer getRemainingCapacity() { + return remainingCapacity; + } + + public void setRemainingCapacity(Integer remainingCapacity) { + this.remainingCapacity = remainingCapacity; + } + + public Integer getPendingRequests() { + return pendingRequests; + } + + public void setPendingRequests(Integer pendingRequests) { + this.pendingRequests = pendingRequests; + } + + @Override + public String toString() { + return getClass().getSimpleName() + " {" + + " remainingCapacity: " + remainingCapacity + "," + + " pendingRequests: " + pendingRequests + " " + + "}"; + } +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/Metrics.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/Metrics.java new file mode 100644 index 0000000000000..ff08bc850caf4 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/Metrics.java @@ -0,0 +1,74 @@ +/* + * 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.torchserve.client.model; + +import org.apache.camel.component.torchserve.client.management.model.DescribeModel200ResponseInnerMetrics; + +public class Metrics { + + private Integer rejectedRequests = null; + private Integer waitingQueueSize = null; + private Integer requests = null; + + public Metrics() { + } + + public static Metrics from(DescribeModel200ResponseInnerMetrics src) { + if (src == null) { + return null; + } + + Metrics metrics = new Metrics(); + metrics.setRejectedRequests(src.getRejectedRequests()); + metrics.setWaitingQueueSize(src.getWaitingQueueSize()); + metrics.setRequests(src.getRequests()); + return metrics; + } + + public Integer getRejectedRequests() { + return rejectedRequests; + } + + public void setRejectedRequests(Integer rejectedRequests) { + this.rejectedRequests = rejectedRequests; + } + + public Integer getWaitingQueueSize() { + return waitingQueueSize; + } + + public void setWaitingQueueSize(Integer waitingQueueSize) { + this.waitingQueueSize = waitingQueueSize; + } + + public Integer getRequests() { + return requests; + } + + public void setRequests(Integer requests) { + this.requests = requests; + } + + @Override + public String toString() { + return getClass().getSimpleName() + " {" + + " rejectedRequests: " + rejectedRequests + "," + + " waitingQueueSize: " + waitingQueueSize + "," + + " requests: " + requests + " " + + "}"; + } +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/Model.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/Model.java new file mode 100644 index 0000000000000..641b403ba8d52 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/Model.java @@ -0,0 +1,63 @@ +/* + * 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.torchserve.client.model; + +import org.apache.camel.component.torchserve.client.management.model.ListModels200ResponseModelsInner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Model { + + private static final Logger LOG = LoggerFactory.getLogger(Model.class); + + protected String modelName = null; + protected String modelUrl = null; + + public Model() { + } + + public static Model from(ListModels200ResponseModelsInner src) { + Model model = new Model(); + model.setModelName(src.getModelName()); + model.setModelUrl(src.getModelUrl()); + return model; + } + + public String getModelName() { + return modelName; + } + + public void setModelName(String modelName) { + this.modelName = modelName; + } + + public String getModelUrl() { + return modelUrl; + } + + public void setModelUrl(String modelUrl) { + this.modelUrl = modelUrl; + } + + @Override + public String toString() { + return getClass().getSimpleName() + " {" + + " modelName: " + modelName + "," + + " modelUrl: " + modelUrl + " " + + "}"; + } +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/ModelDetail.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/ModelDetail.java new file mode 100644 index 0000000000000..d9946bf862a5f --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/ModelDetail.java @@ -0,0 +1,143 @@ +/* + * 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.torchserve.client.model; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.camel.component.torchserve.client.management.model.DescribeModel200ResponseInner; + +public class ModelDetail extends Model { + + private String modelVersion = null; + private Integer minWorkers = null; + private Integer maxWorkers = null; + private Integer batchSize = null; + private Integer maxBatchDelay = null; + private String status = null; + private List workers = new ArrayList<>(); + private Metrics metrics = null; + private JobQueueStatus jobQueueStatus = null; + + public ModelDetail() { + } + + public static ModelDetail from(DescribeModel200ResponseInner src) { + ModelDetail model = new ModelDetail(); + model.setModelName(src.getModelName()); + model.setModelVersion(src.getModelVersion()); + model.setModelUrl(src.getModelUrl()); + model.setMinWorkers(src.getMinWorkers()); + model.setMaxWorkers(src.getMaxWorkers()); + model.setBatchSize(src.getBatchSize()); + model.setMaxBatchDelay(src.getMaxBatchDelay()); + model.setStatus(src.getStatus()); + model.setWorkers(src.getWorkers().stream().map(Worker::from).toList()); + model.setMetrics(Metrics.from(src.getMetrics())); + model.setJobQueueStatus(JobQueueStatus.from(src.getJobQueueStatus())); + return model; + } + + public String getModelVersion() { + return modelVersion; + } + + public void setModelVersion(String modelVersion) { + this.modelVersion = modelVersion; + } + + public Integer getMinWorkers() { + return minWorkers; + } + + public void setMinWorkers(Integer minWorkers) { + this.minWorkers = minWorkers; + } + + public Integer getMaxWorkers() { + return maxWorkers; + } + + public void setMaxWorkers(Integer maxWorkers) { + this.maxWorkers = maxWorkers; + } + + public Integer getBatchSize() { + return batchSize; + } + + public void setBatchSize(Integer batchSize) { + this.batchSize = batchSize; + } + + public Integer getMaxBatchDelay() { + return maxBatchDelay; + } + + public void setMaxBatchDelay(Integer maxBatchDelay) { + this.maxBatchDelay = maxBatchDelay; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public List getWorkers() { + return workers; + } + + public void setWorkers(List workers) { + this.workers = workers; + } + + public Metrics getMetrics() { + return metrics; + } + + public void setMetrics(Metrics metrics) { + this.metrics = metrics; + } + + public JobQueueStatus getJobQueueStatus() { + return jobQueueStatus; + } + + public void setJobQueueStatus(JobQueueStatus jobQueueStatus) { + this.jobQueueStatus = jobQueueStatus; + } + + @Override + public String toString() { + return getClass().getSimpleName() + " {\n" + + " modelName: " + modelName + "\n" + + " modelVersion: " + modelVersion + "\n" + + " modelUrl: " + modelUrl + "\n" + + " minWorkers: " + minWorkers + "\n" + + " maxWorkers: " + maxWorkers + "\n" + + " batchSize: " + batchSize + "\n" + + " maxBatchDelay: " + maxBatchDelay + "\n" + + " status: " + status + "\n" + + " workers: " + workers + "\n" + + " metrics: " + metrics + "\n" + + " jobQueueStatus: " + jobQueueStatus + "\n" + + "}"; + } +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/ModelList.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/ModelList.java new file mode 100644 index 0000000000000..7e3252966a218 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/ModelList.java @@ -0,0 +1,62 @@ +/* + * 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.torchserve.client.model; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.camel.component.torchserve.client.management.model.ListModels200Response; + +public class ModelList { + + private String nextPageToken = null; + private List models = new ArrayList<>(); + + public ModelList() { + } + + public static ModelList from(ListModels200Response src) { + ModelList modelList = new ModelList(); + modelList.setNextPageToken(src.getNextPageToken()); + modelList.setModels(src.getModels().stream().map(Model::from).toList()); + return modelList; + } + + public String getNextPageToken() { + return nextPageToken; + } + + public void setNextPageToken(String nextPageToken) { + this.nextPageToken = nextPageToken; + } + + public List getModels() { + return models; + } + + public void setModels(List models) { + this.models = models; + } + + @Override + public String toString() { + return getClass().getSimpleName() + " {\n" + + " nextPageToken: " + nextPageToken + "\n" + + " models: " + models + "\n" + + "}"; + } +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/RegisterOptions.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/RegisterOptions.java new file mode 100644 index 0000000000000..b5adc2deb84f0 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/RegisterOptions.java @@ -0,0 +1,160 @@ +/* + * 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.torchserve.client.model; + +public class RegisterOptions { + + /** + * Name of model. This value will override modelName in MANIFEST.json if present. (optional) + */ + private String modelName = null; + /** + * Inference handler entry-point. This value will override handler in MANIFEST.json if present. (optional) + */ + private String handler = null; + /** + * Runtime for the model custom service code. This value will override runtime in MANIFEST.json if present. + * (optional) + */ + private String runtime = null; + /** + * Inference batch size, default: 1. (optional) + */ + private Integer batchSize = null; + /** + * Maximum delay for batch aggregation, default: 100. (optional) + */ + private Integer maxBatchDelay = null; + /** + * Maximum time, in seconds, the TorchServe waits for a response from the model inference code, default: 120. + * (optional) + */ + private Integer responseTimeout = null; + /** + * Number of initial workers, default: 0. (optional) + */ + private Integer initialWorkers = null; + /** + * Decides whether creation of worker synchronous or not, default: false. (optional, default to false) + */ + private Boolean synchronous = null; + /** + * Model mar file is S3 SSE KMS(server side encryption) enabled or not, default: false. (optional, default to false) + */ + private Boolean s3SseKms = null; + + private RegisterOptions() { + } + + public static RegisterOptions empty() { + return new RegisterOptions(); + } + + public static Builder builder() { + return new Builder(); + } + + public String getModelName() { + return modelName; + } + + public String getHandler() { + return handler; + } + + public String getRuntime() { + return runtime; + } + + public Integer getBatchSize() { + return batchSize; + } + + public Integer getMaxBatchDelay() { + return maxBatchDelay; + } + + public Integer getResponseTimeout() { + return responseTimeout; + } + + public Integer getInitialWorkers() { + return initialWorkers; + } + + public Boolean getSynchronous() { + return synchronous; + } + + public Boolean getS3SseKms() { + return s3SseKms; + } + + public static class Builder { + + private final RegisterOptions options = new RegisterOptions(); + + public Builder modelName(String modelName) { + options.modelName = modelName; + return this; + } + + public Builder handler(String handler) { + options.handler = handler; + return this; + } + + public Builder runtime(String runtime) { + options.runtime = runtime; + return this; + } + + public Builder batchSize(Integer batchSize) { + options.batchSize = batchSize; + return this; + } + + public Builder maxBatchDelay(Integer maxBatchDelay) { + options.maxBatchDelay = maxBatchDelay; + return this; + } + + public Builder responseTimeout(Integer responseTimeout) { + options.responseTimeout = responseTimeout; + return this; + } + + public Builder initialWorkers(Integer initialWorkers) { + options.initialWorkers = initialWorkers; + return this; + } + + public Builder synchronous(Boolean synchronous) { + options.synchronous = synchronous; + return this; + } + + public Builder s3SseKms(Boolean s3SseKms) { + options.s3SseKms = s3SseKms; + return this; + } + + public RegisterOptions build() { + return options; + } + } +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/Response.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/Response.java new file mode 100644 index 0000000000000..7ebd129a539c3 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/Response.java @@ -0,0 +1,55 @@ +/* + * 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.torchserve.client.model; + +import org.apache.camel.component.torchserve.client.inference.model.Ping200Response; +import org.apache.camel.component.torchserve.client.management.model.RegisterModel200Response; + +public class Response { + + private String status; + + public Response() { + } + + public static Response from(Ping200Response src) { + Response response = new Response(); + response.setStatus(src.getStatus()); + return response; + } + + public static Response from(RegisterModel200Response src) { + Response response = new Response(); + response.setStatus(src.getStatus()); + return response; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + @Override + public String toString() { + return getClass().getSimpleName() + " {\n" + + " status: " + status + "\n" + + "}"; + } +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/ScaleWorkerOptions.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/ScaleWorkerOptions.java new file mode 100644 index 0000000000000..0f9e51d6907ac --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/ScaleWorkerOptions.java @@ -0,0 +1,107 @@ +/* + * 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.torchserve.client.model; + +public class ScaleWorkerOptions { + + /** + * Minimum number of worker processes. (optional) + */ + private Integer minWorker = null; + /** + * Maximum number of worker processes. (optional) + */ + private Integer maxWorker = null; + /** + * Number of GPU worker processes to create. (optional) + */ + private Integer numberGpu = null; + /** + * Decides whether the call is synchronous or not, default: false. (optional, default to false) + */ + private Boolean synchronous = null; + /** + * Waiting up to the specified wait time if necessary for a worker to complete all pending requests. Use 0 to + * terminate backend worker process immediately. Use -1 for wait infinitely. (optional) + */ + private Integer timeout = null; + + private ScaleWorkerOptions() { + } + + public static ScaleWorkerOptions empty() { + return new ScaleWorkerOptions(); + } + + public static Builder builder() { + return new Builder(); + } + + public Integer getMinWorker() { + return minWorker; + } + + public Integer getMaxWorker() { + return maxWorker; + } + + public Integer getNumberGpu() { + return numberGpu; + } + + public Boolean getSynchronous() { + return synchronous; + } + + public Integer getTimeout() { + return timeout; + } + + public static class Builder { + + private final ScaleWorkerOptions options = new ScaleWorkerOptions(); + + public Builder minWorker(Integer minWorker) { + options.minWorker = minWorker; + return this; + } + + public Builder maxWorker(Integer maxWorker) { + options.maxWorker = maxWorker; + return this; + } + + public Builder numberGpu(Integer numberGpu) { + options.numberGpu = numberGpu; + return this; + } + + public Builder synchronous(Boolean synchronous) { + options.synchronous = synchronous; + return this; + } + + public Builder timeout(Integer timeout) { + options.timeout = timeout; + return this; + } + + public ScaleWorkerOptions build() { + return options; + } + } +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/UnregisterOptions.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/UnregisterOptions.java new file mode 100644 index 0000000000000..d3878c3895256 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/UnregisterOptions.java @@ -0,0 +1,68 @@ +/* + * 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.torchserve.client.model; + +public class UnregisterOptions { + + /** + * Decides whether the call is synchronous or not, default: false. (optional, default to false) + */ + private Boolean synchronous = null; + /** + * Waiting up to the specified wait time if necessary for a worker to complete all pending requests. Use 0 to + * terminate backend worker process immediately. Use -1 for wait infinitely. (optional) + */ + private Integer timeout = null; + + private UnregisterOptions() { + } + + public static UnregisterOptions empty() { + return new UnregisterOptions(); + } + + public static Builder builder() { + return new Builder(); + } + + public Boolean getSynchronous() { + return synchronous; + } + + public Integer getTimeout() { + return timeout; + } + + public static class Builder { + + private final UnregisterOptions options = new UnregisterOptions(); + + public Builder synchronous(Boolean synchronous) { + options.synchronous = synchronous; + return this; + } + + public Builder timeout(Integer timeout) { + options.timeout = timeout; + return this; + } + + public UnregisterOptions build() { + return options; + } + } +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/Worker.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/Worker.java new file mode 100644 index 0000000000000..ab6751daa83e6 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/client/model/Worker.java @@ -0,0 +1,99 @@ +/* + * 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.torchserve.client.model; + +import org.apache.camel.component.torchserve.client.management.model.DescribeModel200ResponseInnerWorkersInner; + +public class Worker { + + private String id = null; + private String startTime = null; + private Boolean gpu = null; + private Status status = null; + + public Worker() { + } + + public static Worker from(DescribeModel200ResponseInnerWorkersInner src) { + if (src == null) { + return null; + } + + Worker worker = new Worker(); + worker.setId(src.getId()); + worker.setStartTime(src.getStartTime()); + worker.setGpu(src.getGpu()); + worker.setStatus(Status.from(src.getStatus())); + return worker; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getStartTime() { + return startTime; + } + + public void setStartTime(String startTime) { + this.startTime = startTime; + } + + public Boolean getGpu() { + return gpu; + } + + public void setGpu(Boolean gpu) { + this.gpu = gpu; + } + + public Status getStatus() { + return status; + } + + public void setStatus(Status status) { + this.status = status; + } + + @Override + public String toString() { + return getClass().getSimpleName() + " {" + + " id: " + id + "," + + " startTime: " + startTime + "," + + " gpu: " + gpu + "," + + " status: " + status + " " + + "}"; + } + + public enum Status { + READY, + LOADING, + UNLOADING; + + public static Status from(DescribeModel200ResponseInnerWorkersInner.StatusEnum status) { + return switch (status) { + case READY -> READY; + case LOADING -> LOADING; + case UNLOADING -> UNLOADING; + }; + } + } +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/producer/InferenceProducer.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/producer/InferenceProducer.java new file mode 100644 index 0000000000000..6b0f1a0e52e59 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/producer/InferenceProducer.java @@ -0,0 +1,76 @@ +/* + * 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.torchserve.producer; + +import org.apache.camel.Exchange; +import org.apache.camel.component.torchserve.TorchServeEndpoint; +import org.apache.camel.component.torchserve.client.Inference; +import org.apache.camel.component.torchserve.client.model.ApiException; +import org.apache.camel.component.torchserve.client.model.Response; + +public class InferenceProducer extends TorchServeProducer { + + private final Inference inference; + private final String operation; + + public InferenceProducer(TorchServeEndpoint endpoint) { + super(endpoint); + this.inference = endpoint.getClient().inference(); + this.operation = endpoint.getOperation(); + } + + @Override + public void process(Exchange exchange) throws Exception { + switch (this.operation) { + case "ping": + ping(exchange); + break; + case "predictions": + predictions(exchange); + break; + case "explanations": + explanations(exchange); + break; + default: + throw new IllegalArgumentException("Unsupported operation: " + this.operation); + } + } + + private void ping(Exchange exchange) throws ApiException { + Response response = this.inference.ping(); + exchange.getMessage().setBody(response.getStatus()); + } + + private void predictions(Exchange exchange) throws ApiException { + String modelName = getEndpoint().getConfiguration().getModelName(); + String modelVersion = getEndpoint().getConfiguration().getModelVersion(); + Object body = exchange.getMessage().getBody(byte[].class); + Object response; + if (modelVersion == null) { + response = this.inference.predictions(modelName, body); + } else { + response = this.inference.predictions(modelName, modelVersion, body); + } + exchange.getMessage().setBody(response); + } + + private void explanations(Exchange exchange) throws ApiException { + String modelName = getEndpoint().getConfiguration().getModelName(); + Object response = this.inference.explanations(modelName); + exchange.getMessage().setBody(response); + } +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/producer/ManagementProducer.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/producer/ManagementProducer.java new file mode 100644 index 0000000000000..d9e2ae05b7817 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/producer/ManagementProducer.java @@ -0,0 +1,132 @@ +/* + * 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.torchserve.producer; + +import java.util.List; +import java.util.Optional; + +import org.apache.camel.Exchange; +import org.apache.camel.component.torchserve.TorchServeConstants; +import org.apache.camel.component.torchserve.TorchServeEndpoint; +import org.apache.camel.component.torchserve.client.Management; +import org.apache.camel.component.torchserve.client.model.ApiException; +import org.apache.camel.component.torchserve.client.model.ModelDetail; +import org.apache.camel.component.torchserve.client.model.ModelList; +import org.apache.camel.component.torchserve.client.model.RegisterOptions; +import org.apache.camel.component.torchserve.client.model.Response; +import org.apache.camel.component.torchserve.client.model.ScaleWorkerOptions; +import org.apache.camel.component.torchserve.client.model.UnregisterOptions; + +public class ManagementProducer extends TorchServeProducer { + + private final Management management; + private final String operation; + + public ManagementProducer(TorchServeEndpoint endpoint) { + super(endpoint); + this.management = endpoint.getClient().management(); + this.operation = endpoint.getOperation(); + } + + @Override + public void process(Exchange exchange) throws Exception { + switch (this.operation) { + case "register": + register(exchange); + break; + case "scale-worker": + scaleWorker(exchange); + break; + case "describe": + describe(exchange); + break; + case "unregister": + unregister(exchange); + break; + case "list": + list(exchange); + break; + case "set-default": + setDefault(exchange); + break; + default: + throw new IllegalArgumentException("Unsupported operation: " + this.operation); + } + } + + private void register(Exchange exchange) throws ApiException { + String url = getEndpoint().getConfiguration().getUrl(); + RegisterOptions options = Optional + .ofNullable(exchange.getMessage().getHeader(TorchServeConstants.REGISTER_OPTIONS, RegisterOptions.class)) + .or(() -> Optional.ofNullable(getEndpoint().getConfiguration().getRegisterOptions())) + .orElse(RegisterOptions.empty()); + Response response = this.management.registerModel(url, options); + exchange.getMessage().setBody(response.getStatus()); + } + + private void scaleWorker(Exchange exchange) throws ApiException { + String modelName = getEndpoint().getConfiguration().getModelName(); + ScaleWorkerOptions options = Optional + .ofNullable(exchange.getIn().getHeader(TorchServeConstants.SCALE_WORKER_OPTIONS, ScaleWorkerOptions.class)) + .or(() -> Optional.ofNullable(getEndpoint().getConfiguration().getScaleWorkerOptions())) + .orElse(ScaleWorkerOptions.empty()); + Response response = this.management.setAutoScale(modelName, options); + exchange.getMessage().setBody(response.getStatus()); + } + + private void describe(Exchange exchange) throws ApiException { + String modelName = getEndpoint().getConfiguration().getModelName(); + String modelVersion = getEndpoint().getConfiguration().getModelVersion(); + List response; + if (modelVersion == null) { + response = this.management.describeModel(modelName); + } else { + response = this.management.describeModel(modelName, modelVersion); + } + exchange.getMessage().setBody(response); + } + + private void unregister(Exchange exchange) throws ApiException { + String modelName = getEndpoint().getConfiguration().getModelName(); + String modelVersion = getEndpoint().getConfiguration().getModelVersion(); + UnregisterOptions options = Optional + .ofNullable(exchange.getMessage().getHeader(TorchServeConstants.UNREGISTER_OPTIONS, UnregisterOptions.class)) + .or(() -> Optional.ofNullable(getEndpoint().getConfiguration().getUnregisterOptions())) + .orElse(UnregisterOptions.empty()); + Response response; + if (modelVersion == null) { + response = this.management.unregisterModel(modelName, options); + } else { + response = this.management.unregisterModel(modelName, modelVersion, options); + } + exchange.getMessage().setBody(response.getStatus()); + } + + private void list(Exchange exchange) throws ApiException { + int limit = getEndpoint().getConfiguration().getListLimit(); + String nextPageToken = getEndpoint().getConfiguration().getListNextPageToken(); + ModelList response = this.management.listModels(limit, nextPageToken); + exchange.getMessage().setBody(response); + } + + private void setDefault(Exchange exchange) throws ApiException { + String modelName = getEndpoint().getConfiguration().getModelName(); + String modelVersion = getEndpoint().getConfiguration().getModelVersion(); + Response response = this.management.setDefault(modelName, modelVersion); + exchange.getMessage().setBody(response.getStatus()); + } +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/producer/MetricsProducer.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/producer/MetricsProducer.java new file mode 100644 index 0000000000000..e19c7fc0f5103 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/producer/MetricsProducer.java @@ -0,0 +1,49 @@ +/* + * 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.torchserve.producer; + +import org.apache.camel.Exchange; +import org.apache.camel.component.torchserve.TorchServeEndpoint; +import org.apache.camel.component.torchserve.client.Metrics; +import org.apache.camel.component.torchserve.client.model.ApiException; + +public class MetricsProducer extends TorchServeProducer { + + private final Metrics metrics; + private final String operation; + + public MetricsProducer(TorchServeEndpoint endpoint) { + super(endpoint); + this.metrics = endpoint.getClient().metrics(); + this.operation = endpoint.getOperation(); + } + + @Override + public void process(Exchange exchange) throws Exception { + if ("metrics".equals(this.operation)) { + metrics(exchange); + } else { + throw new IllegalArgumentException("Unsupported operation: " + this.operation); + } + } + + private void metrics(Exchange exchange) throws ApiException { + String metricsName = getEndpoint().getConfiguration().getMetricsName(); + String response = metricsName == null ? this.metrics.metrics() : this.metrics.metrics(metricsName); + exchange.getMessage().setBody(response); + } +} diff --git a/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/producer/TorchServeProducer.java b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/producer/TorchServeProducer.java new file mode 100644 index 0000000000000..737f37f4dd62b --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/java/org/apache/camel/component/torchserve/producer/TorchServeProducer.java @@ -0,0 +1,33 @@ +/* + * 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.torchserve.producer; + +import org.apache.camel.Endpoint; +import org.apache.camel.component.torchserve.TorchServeEndpoint; +import org.apache.camel.support.DefaultProducer; + +public abstract class TorchServeProducer extends DefaultProducer { + + public TorchServeProducer(Endpoint endpoint) { + super(endpoint); + } + + @Override + public TorchServeEndpoint getEndpoint() { + return (TorchServeEndpoint) super.getEndpoint(); + } +} diff --git a/components/camel-ai/camel-torchserve/src/main/resources/openapi/inference.json b/components/camel-ai/camel-torchserve/src/main/resources/openapi/inference.json new file mode 100644 index 0000000000000..dc8556e0e2e6a --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/resources/openapi/inference.json @@ -0,0 +1,790 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "TorchServe APIs", + "description": "TorchServe is a flexible and easy to use tool for serving deep learning models", + "version": "0.11.1" + }, + "paths": { + "/": { + "options": { + "description": "Get openapi description.", + "operationId": "apiDescription", + "parameters": [], + "responses": { + "200": { + "description": "A openapi 3.0.1 descriptor", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "openapi", + "info", + "paths" + ], + "properties": { + "openapi": { + "type": "string" + }, + "info": { + "type": "object" + }, + "paths": { + "type": "object" + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + } + } + } + }, + "/ping": { + "get": { + "description": "Get TorchServe status.", + "operationId": "ping", + "parameters": [], + "responses": { + "200": { + "description": "TorchServe status", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string", + "description": "Overall status of the TorchServe." + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + } + } + } + }, + "/v1/models/{model_name}:predict": { + "post": { + "description": "Predictions entry point to get inference using default model version.", + "operationId": "predictions", + "parameters": [ + { + "in": "path", + "name": "model_name", + "description": "Name of model.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Input data format is defined by each model.", + "content": { + "*/*": { + "schema": { + "AnyValue": {} + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Output data format is defined by each model.", + "content": { + "*/*": { + "schema": { + "AnyValue": {} + } + } + } + }, + "404": { + "description": "Model not found or Model Version not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + }, + "503": { + "description": "No worker is available to serve request", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + } + } + } + }, + "/v2/models/{model_name}/infer": { + "post": { + "description": "Predictions entry point to get inference using default model version.", + "operationId": "predictions", + "parameters": [ + { + "in": "path", + "name": "model_name", + "description": "Name of model.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Input data format is defined by each model.", + "content": { + "*/*": { + "schema": { + "AnyValue": {} + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Output data format is defined by each model.", + "content": { + "*/*": { + "schema": { + "AnyValue": {} + } + } + } + }, + "404": { + "description": "Model not found or Model Version not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + }, + "503": { + "description": "No worker is available to serve request", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + } + } + } + }, + "/predictions/{model_name}": { + "post": { + "description": "Predictions entry point to get inference using default model version.", + "operationId": "predictions", + "parameters": [ + { + "in": "path", + "name": "model_name", + "description": "Name of model.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Input data format is defined by each model.", + "content": { + "*/*": { + "schema": { + "AnyValue": {} + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Output data format is defined by each model.", + "content": { + "*/*": { + "schema": { + "AnyValue": {} + } + } + } + }, + "404": { + "description": "Model not found or Model Version not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + }, + "503": { + "description": "No worker is available to serve request", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + } + } + } + }, + "/predictions/{model_name}/{model_version}": { + "post": { + "description": "Predictions entry point to get inference using specific model version.", + "operationId": "version_predictions", + "parameters": [ + { + "in": "path", + "name": "model_name", + "description": "Name of model.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "model_version", + "description": "Name of model version.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Input data format is defined by each model.", + "content": { + "*/*": { + "schema": { + "AnyValue": {} + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Output data format is defined by each model.", + "content": { + "*/*": { + "schema": { + "AnyValue": {} + } + } + } + }, + "404": { + "description": "Model not found or Model Version not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + }, + "503": { + "description": "No worker is available to serve request", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + } + } + } + }, + "/api-description": { + "get": { + "description": "Get openapi description.", + "operationId": "api-description", + "parameters": [], + "responses": { + "200": { + "description": "A openapi 3.0.1 descriptor", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "openapi", + "info", + "paths" + ], + "properties": { + "openapi": { + "type": "string" + }, + "info": { + "type": "object" + }, + "paths": { + "type": "object" + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + } + }, + "deprecated": true + } + }, + "/metrics": { + "get": { + "description": "Get TorchServe application metrics in prometheus format.", + "operationId": "metrics", + "parameters": [ + { + "in": "query", + "name": "name[]", + "description": "Names of metrics to filter", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "TorchServe application metrics", + "content": { + "text/plain; version=0.0.4; charset=utf-8": { + "schema": { + "type": "object", + "required": [ + "# HELP", + "# TYPE", + "metric" + ], + "properties": { + "# HELP": { + "type": "string", + "description": "Help text for TorchServe metric." + }, + "# TYPE": { + "type": "string", + "description": "Type of TorchServe metric." + }, + "metric": { + "type": "string", + "description": "TorchServe application metric." + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + } + } + } + } + } +} diff --git a/components/camel-ai/camel-torchserve/src/main/resources/openapi/management.json b/components/camel-ai/camel-torchserve/src/main/resources/openapi/management.json new file mode 100644 index 0000000000000..d36df85a9c533 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/resources/openapi/management.json @@ -0,0 +1,1902 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "TorchServe APIs", + "description": "TorchServe is a flexible and easy to use tool for serving deep learning models", + "version": "0.11.1" + }, + "paths": { + "/": { + "options": { + "description": "Get openapi description.", + "operationId": "apiDescription", + "parameters": [], + "responses": { + "200": { + "description": "A openapi 3.0.1 descriptor", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "openapi", + "info", + "paths" + ], + "properties": { + "openapi": { + "type": "string" + }, + "info": { + "type": "object" + }, + "paths": { + "type": "object" + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + } + } + } + }, + "/models": { + "get": { + "description": "List registered models in TorchServe.", + "operationId": "listModels", + "parameters": [ + { + "in": "query", + "name": "limit", + "description": "Use this parameter to specify the maximum number of items to return. When this value is present, TorchServe does not return more than the specified number of items, but it might return fewer. This value is optional. If you include a value, it must be between 1 and 1000, inclusive. If you do not include a value, it defaults to 100.", + "required": false, + "schema": { + "type": "integer", + "default": "100" + } + }, + { + "in": "query", + "name": "next_page_token", + "description": "The token to retrieve the next set of results. TorchServe provides the token when the response from a previous call has more results than the maximum page size.", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "models" + ], + "properties": { + "nextPageToken": { + "type": "string", + "description": "Use this parameter in a subsequent request after you receive a response with truncated results. Set it to the value of NextMarker from the truncated response you just received." + }, + "models": { + "type": "array", + "items": { + "type": "object", + "required": [ + "modelName", + "modelUrl" + ], + "properties": { + "modelName": { + "type": "string", + "description": "Name of the model." + }, + "modelUrl": { + "type": "string", + "description": "URL of the model." + } + } + }, + "description": "A list of registered models." + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + } + } + }, + "post": { + "description": "Register a new model in TorchServe.", + "operationId": "registerModel", + "parameters": [ + { + "in": "query", + "name": "url", + "description": "Model archive download url, support local file or HTTP(s) protocol. For S3, consider use pre-signed url.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "model_name", + "description": "Name of model. This value will override modelName in MANIFEST.json if present.", + "required": false, + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "handler", + "description": "Inference handler entry-point. This value will override handler in MANIFEST.json if present.", + "required": false, + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "runtime", + "description": "Runtime for the model custom service code. This value will override runtime in MANIFEST.json if present.", + "required": false, + "schema": { + "type": "string", + "enum": [ + "PYTHON", + "PYTHON3", + "LSP" + ] + } + }, + { + "in": "query", + "name": "batch_size", + "description": "Inference batch size, default: 1.", + "required": false, + "schema": { + "type": "integer", + "default": "1" + } + }, + { + "in": "query", + "name": "max_batch_delay", + "description": "Maximum delay for batch aggregation, default: 100.", + "required": false, + "schema": { + "type": "integer", + "default": "100" + } + }, + { + "in": "query", + "name": "response_timeout", + "description": "Maximum time, in seconds, the TorchServe waits for a response from the model inference code, default: 120.", + "required": false, + "schema": { + "type": "integer", + "default": "2" + } + }, + { + "in": "query", + "name": "initial_workers", + "description": "Number of initial workers, default: 0.", + "required": false, + "schema": { + "type": "integer", + "default": "0" + } + }, + { + "in": "query", + "name": "synchronous", + "description": "Decides whether creation of worker synchronous or not, default: false.", + "required": false, + "schema": { + "type": "boolean", + "default": "false" + } + }, + { + "in": "query", + "name": "s3_sse_kms", + "description": "Model mar file is S3 SSE KMS(server side encryption) enabled or not, default: false.", + "required": false, + "schema": { + "type": "boolean", + "default": "false" + } + } + ], + "requestBody": { + "content": { + "text/plain": { + "schema": { + "AnyValue": {} + } + } + }, + "required": false + }, + "responses": { + "200": { + "description": "Model registered", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string", + "description": "Error type." + } + } + } + } + } + }, + "202": { + "description": "Accepted", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string", + "description": "Error type." + } + } + } + } + } + }, + "210": { + "description": "Partial Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string", + "description": "Error type." + } + } + } + } + } + }, + "400": { + "description": "Bad request", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + }, + "404": { + "description": "Model not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + }, + "409": { + "description": "Model already registered", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + } + } + } + }, + "/models/{model_name}": { + "get": { + "description": "Provides detailed information about the default version of a model.", + "operationId": "describeModel", + "parameters": [ + { + "in": "path", + "name": "model_name", + "description": "Name of model to describe.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "required": [ + "modelName", + "modelVersion", + "modelUrl", + "minWorkers", + "maxWorkers", + "status", + "workers", + "metrics", + "jobQueueStatus" + ], + "properties": { + "modelName": { + "type": "string", + "description": "Name of the model." + }, + "modelVersion": { + "type": "string", + "description": "Version of the model." + }, + "modelUrl": { + "type": "string", + "description": "URL of the model." + }, + "minWorkers": { + "type": "integer", + "description": "Configured minimum number of worker." + }, + "maxWorkers": { + "type": "integer", + "description": "Configured maximum number of worker." + }, + "batchSize": { + "type": "integer", + "description": "Configured batch size." + }, + "maxBatchDelay": { + "type": "integer", + "description": "Configured maximum batch delay in ms." + }, + "status": { + "type": "string", + "description": "Overall health status of the model" + }, + "workers": { + "type": "array", + "items": { + "type": "object", + "required": [ + "id", + "startTime", + "status" + ], + "properties": { + "id": { + "type": "string", + "description": "Worker id" + }, + "startTime": { + "type": "string", + "description": "Worker start time" + }, + "gpu": { + "type": "boolean", + "description": "If running on GPU" + }, + "status": { + "type": "string", + "description": "Worker status", + "enum": [ + "READY", + "LOADING", + "UNLOADING" + ] + } + } + }, + "description": "A list of active backend workers." + }, + "metrics": { + "type": "object", + "required": [ + "rejectedRequests", + "waitingQueueSize", + "requests" + ], + "properties": { + "rejectedRequests": { + "type": "integer", + "description": "Number requests has been rejected in last 10 minutes." + }, + "waitingQueueSize": { + "type": "integer", + "description": "Number requests waiting in the queue." + }, + "requests": { + "type": "integer", + "description": "Number requests processed in last 10 minutes." + } + } + }, + "jobQueueStatus": { + "type": "object", + "required": [ + "remainingCapacity", + "pendingRequests" + ], + "properties": { + "remainingCapacity": { + "type": "integer", + "description": "Number of new requests that can be queued." + }, + "pendingRequests": { + "type": "integer", + "description": "Number of requests waiting in the queue." + } + } + } + } + } + } + } + } + }, + "404": { + "description": "Model not found or Model version not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + } + } + }, + "put": { + "description": "Configure number of workers for a default version of a model.This is a asynchronous call by default. Caller need to call describeModel to check if the model workers has been changed.", + "operationId": "setAutoScale", + "parameters": [ + { + "in": "path", + "name": "model_name", + "description": "Name of model to scale workers.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "min_worker", + "description": "Minimum number of worker processes.", + "required": false, + "schema": { + "type": "integer", + "default": "1" + } + }, + { + "in": "query", + "name": "max_worker", + "description": "Maximum number of worker processes.", + "required": false, + "schema": { + "type": "integer", + "default": "1" + } + }, + { + "in": "query", + "name": "number_gpu", + "description": "Number of GPU worker processes to create.", + "required": false, + "schema": { + "type": "integer", + "default": "0" + } + }, + { + "in": "query", + "name": "synchronous", + "description": "Decides whether the call is synchronous or not, default: false.", + "required": false, + "schema": { + "type": "boolean", + "default": "false" + } + }, + { + "in": "query", + "name": "timeout", + "description": "Waiting up to the specified wait time if necessary for a worker to complete all pending requests. Use 0 to terminate backend worker process immediately. Use -1 for wait infinitely.", + "required": false, + "schema": { + "type": "integer", + "default": "-1" + } + } + ], + "responses": { + "200": { + "description": "Model workers updated", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string", + "description": "Error type." + } + } + } + } + } + }, + "202": { + "description": "Accepted", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string", + "description": "Error type." + } + } + } + } + } + }, + "210": { + "description": "Partial Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string", + "description": "Error type." + } + } + } + } + } + }, + "400": { + "description": "Bad request", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + }, + "404": { + "description": "Model not found or Model version not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + } + } + }, + "delete": { + "description": "Unregister the default version of a model from TorchServe if it is the only version available.This is a asynchronous call by default. Caller can call listModels to confirm model is unregistered", + "operationId": "unregisterModel", + "parameters": [ + { + "in": "path", + "name": "model_name", + "description": "Name of model to unregister.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "synchronous", + "description": "Decides whether the call is synchronous or not, default: false.", + "required": false, + "schema": { + "type": "boolean", + "default": "false" + } + }, + { + "in": "query", + "name": "timeout", + "description": "Waiting up to the specified wait time if necessary for a worker to complete all pending requests. Use 0 to terminate backend worker process immediately. Use -1 for wait infinitely.", + "required": false, + "schema": { + "type": "integer", + "default": "-1" + } + } + ], + "responses": { + "200": { + "description": "Model unregistered", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string", + "description": "Error type." + } + } + } + } + } + }, + "202": { + "description": "Accepted", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string", + "description": "Error type." + } + } + } + } + } + }, + "404": { + "description": "Model not found or Model version not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + }, + "408": { + "description": "Request Timeout Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + } + } + } + }, + "/models/{model_name}/{model_version}": { + "get": { + "description": "Provides detailed information about the specified version of a model.If \"all\" is specified as version, returns the details about all the versions of the model.", + "operationId": "version_describeModel", + "parameters": [ + { + "in": "path", + "name": "model_name", + "description": "Name of model to describe.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "model_version", + "description": "Version of model to describe.", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "required": [ + "modelName", + "modelVersion", + "modelUrl", + "minWorkers", + "maxWorkers", + "status", + "workers", + "metrics", + "jobQueueStatus" + ], + "properties": { + "modelName": { + "type": "string", + "description": "Name of the model." + }, + "modelVersion": { + "type": "string", + "description": "Version of the model." + }, + "modelUrl": { + "type": "string", + "description": "URL of the model." + }, + "minWorkers": { + "type": "integer", + "description": "Configured minimum number of worker." + }, + "maxWorkers": { + "type": "integer", + "description": "Configured maximum number of worker." + }, + "batchSize": { + "type": "integer", + "description": "Configured batch size." + }, + "maxBatchDelay": { + "type": "integer", + "description": "Configured maximum batch delay in ms." + }, + "status": { + "type": "string", + "description": "Overall health status of the model" + }, + "workers": { + "type": "array", + "items": { + "type": "object", + "required": [ + "id", + "startTime", + "status" + ], + "properties": { + "id": { + "type": "string", + "description": "Worker id" + }, + "startTime": { + "type": "string", + "description": "Worker start time" + }, + "gpu": { + "type": "boolean", + "description": "If running on GPU" + }, + "status": { + "type": "string", + "description": "Worker status", + "enum": [ + "READY", + "LOADING", + "UNLOADING" + ] + } + } + }, + "description": "A list of active backend workers." + }, + "metrics": { + "type": "object", + "required": [ + "rejectedRequests", + "waitingQueueSize", + "requests" + ], + "properties": { + "rejectedRequests": { + "type": "integer", + "description": "Number requests has been rejected in last 10 minutes." + }, + "waitingQueueSize": { + "type": "integer", + "description": "Number requests waiting in the queue." + }, + "requests": { + "type": "integer", + "description": "Number requests processed in last 10 minutes." + } + } + }, + "jobQueueStatus": { + "type": "object", + "required": [ + "remainingCapacity", + "pendingRequests" + ], + "properties": { + "remainingCapacity": { + "type": "integer", + "description": "Number of new requests that can be queued." + }, + "pendingRequests": { + "type": "integer", + "description": "Number of requests waiting in the queue." + } + } + } + } + } + } + } + } + }, + "404": { + "description": "Model not found or Model version not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + } + } + }, + "put": { + "description": "Configure number of workers for a specified version of a model. This is a asynchronous call by default. Caller need to call describeModel to check if the model workers has been changed.", + "operationId": "version_setAutoScale", + "parameters": [ + { + "in": "path", + "name": "model_name", + "description": "Name of model to scale workers.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "model_version", + "description": "Version of model to scale workers.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "min_worker", + "description": "Minimum number of worker processes.", + "required": false, + "schema": { + "type": "integer", + "default": "1" + } + }, + { + "in": "query", + "name": "max_worker", + "description": "Maximum number of worker processes.", + "required": false, + "schema": { + "type": "integer", + "default": "1" + } + }, + { + "in": "query", + "name": "number_gpu", + "description": "Number of GPU worker processes to create.", + "required": false, + "schema": { + "type": "integer", + "default": "0" + } + }, + { + "in": "query", + "name": "synchronous", + "description": "Decides whether the call is synchronous or not, default: false.", + "required": false, + "schema": { + "type": "boolean", + "default": "false" + } + }, + { + "in": "query", + "name": "timeout", + "description": "Waiting up to the specified wait time if necessary for a worker to complete all pending requests. Use 0 to terminate backend worker process immediately. Use -1 for wait infinitely.", + "required": false, + "schema": { + "type": "integer", + "default": "-1" + } + } + ], + "responses": { + "200": { + "description": "Model workers updated", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string", + "description": "Error type." + } + } + } + } + } + }, + "202": { + "description": "Accepted", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string", + "description": "Error type." + } + } + } + } + } + }, + "210": { + "description": "Partial Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string", + "description": "Error type." + } + } + } + } + } + }, + "400": { + "description": "Bad request", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + }, + "404": { + "description": "Model not found or Model version not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + } + } + }, + "delete": { + "description": "Unregister the specified version of a model from TorchServe. This is an asynchronous call by default. Caller can call listModels to confirm model is unregistered", + "operationId": "version_unregisterModel", + "parameters": [ + { + "in": "path", + "name": "model_name", + "description": "Name of model to unregister.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "model_version", + "description": "Version of model to unregister.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "synchronous", + "description": "Decides whether the call is synchronous or not, default: false.", + "required": false, + "schema": { + "type": "boolean", + "default": "false" + } + }, + { + "in": "query", + "name": "timeout", + "description": "Waiting up to the specified wait time if necessary for a worker to complete all pending requests. Use 0 to terminate backend worker process immediately. Use -1 for wait infinitely.", + "required": false, + "schema": { + "type": "integer", + "default": "-1" + } + } + ], + "responses": { + "200": { + "description": "Model unregistered", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string", + "description": "Error type." + } + } + } + } + } + }, + "202": { + "description": "Accepted", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string", + "description": "Error type." + } + } + } + } + } + }, + "404": { + "description": "Model not found or Model version not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + }, + "408": { + "description": "Request Timeout Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + } + } + } + }, + "/models/{model_name}/{model_version}/set-default": { + "put": { + "description": "Set default version of a model", + "operationId": "setDefault", + "parameters": [ + { + "in": "path", + "name": "model_name", + "description": "Name of model whose default version needs to be updated.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "model_version", + "description": "Version of model to be set as default version for the model", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Default vesion succsesfully updated for model", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string", + "description": "Error type." + } + } + } + } + } + }, + "404": { + "description": "Model not found or Model version not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + } + } + } + }, + "/api-description": { + "get": { + "description": "Get openapi description.", + "operationId": "api-description", + "parameters": [], + "responses": { + "200": { + "description": "A openapi 3.0.1 descriptor", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "openapi", + "info", + "paths" + ], + "properties": { + "openapi": { + "type": "string" + }, + "info": { + "type": "object" + }, + "paths": { + "type": "object" + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + } + }, + "deprecated": true + } + }, + "/metrics": { + "get": { + "description": "Get TorchServe application metrics in prometheus format.", + "operationId": "metrics", + "parameters": [ + { + "in": "query", + "name": "name[]", + "description": "Names of metrics to filter", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "TorchServe application metrics", + "content": { + "text/plain; version=0.0.4; charset=utf-8": { + "schema": { + "type": "object", + "required": [ + "# HELP", + "# TYPE", + "metric" + ], + "properties": { + "# HELP": { + "type": "string", + "description": "Help text for TorchServe metric." + }, + "# TYPE": { + "type": "string", + "description": "Type of TorchServe metric." + }, + "metric": { + "type": "string", + "description": "TorchServe application metric." + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + } + } + } + } + } +} diff --git a/components/camel-ai/camel-torchserve/src/main/resources/openapi/metrics.json b/components/camel-ai/camel-torchserve/src/main/resources/openapi/metrics.json new file mode 100644 index 0000000000000..02bec64ec2b01 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/main/resources/openapi/metrics.json @@ -0,0 +1,68 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "TorchServe APIs", + "description": "TorchServe is a flexible and easy to use tool for serving deep learning models", + "version": "0.11.1" + }, + "paths": { + "/metrics": { + "get": { + "description": "Get TorchServe application metrics in prometheus format.", + "operationId": "metrics", + "parameters": [ + { + "in": "query", + "name": "name[]", + "description": "Names of metrics to filter", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "TorchServe application metrics", + "content": { + "text/plain; version=0.0.4; charset=utf-8": { + "schema": { + "type": "string" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "code", + "type", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "Error code." + }, + "type": { + "type": "string", + "description": "Error type." + }, + "message": { + "type": "string", + "description": "Error message." + } + } + } + } + } + } + } + } + } + } +} diff --git a/components/camel-ai/camel-torchserve/src/test/java/org/apache/camel/component/torchserve/InferenceTest.java b/components/camel-ai/camel-torchserve/src/test/java/org/apache/camel/component/torchserve/InferenceTest.java new file mode 100644 index 0000000000000..0312c1bb174d8 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/test/java/org/apache/camel/component/torchserve/InferenceTest.java @@ -0,0 +1,110 @@ +/* + * 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.torchserve; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.apache.camel.CamelContext; +import org.apache.camel.RoutesBuilder; +import org.apache.camel.builder.RouteBuilder; +import org.junit.jupiter.api.Test; + +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.okJson; +import static com.github.tomakehurst.wiremock.client.WireMock.post; + +class InferenceTest extends TorchServeTestSupport { + + private static final String TEST_MODEL = "squeezenet1_1"; + private static final String TEST_MODEL_VERSION = "1.0"; + private static final String TEST_DATA = "src/test/resources/data/kitten.jpg"; + + @Override + protected CamelContext createCamelContext() throws Exception { + var context = super.createCamelContext(); + var component = context.getComponent("torchserve", TorchServeComponent.class); + var configuration = component.getConfiguration(); + configuration.setInferenceAddress(mockServer.baseUrl()); + return context; + } + + @Test + void testPing() throws Exception { + mockServer.stubFor(get("/ping") + .willReturn(okJson("{ \"status\": \"Healthy\" }"))); + var mock = getMockEndpoint("mock:result"); + mock.expectedBodyReceived().constant("Healthy"); + + template.sendBody("direct:ping", null); + + mock.await(1, TimeUnit.SECONDS); + mock.assertIsSatisfied(); + } + + @Test + void testPredictions() throws Exception { + mockServer.stubFor(post("/predictions/" + TEST_MODEL) + .willReturn(okJson("{ \"cat\": 1.0 }"))); + var mock = getMockEndpoint("mock:result"); + mock.expectedBodyReceived() + .body(Map.class) + .isEqualTo(Map.of("cat", 1.0)); + + var body = Files.readAllBytes(Path.of(TEST_DATA)); + template.sendBody("direct:predictions", body); + + mock.await(1, TimeUnit.SECONDS); + mock.assertIsSatisfied(); + } + + @Test + void testPredictions_version() throws Exception { + mockServer.stubFor(post("/predictions/" + TEST_MODEL + "/" + TEST_MODEL_VERSION) + .willReturn(okJson("{ \"cat\": 1.0 }"))); + var mock = getMockEndpoint("mock:result"); + mock.expectedBodyReceived() + .body(Map.class) + .isEqualTo(Map.of("cat", 1.0)); + + var body = Files.readAllBytes(Path.of(TEST_DATA)); + template.sendBody("direct:predictions_version", body); + + mock.await(1, TimeUnit.SECONDS); + mock.assertIsSatisfied(); + } + + @Override + protected RoutesBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from("direct:ping") + .to("torchserve:inference/ping") + .to("mock:result"); + from("direct:predictions") + .toF("torchserve:inference/predictions?modelName=%s", TEST_MODEL) + .to("mock:result"); + from("direct:predictions_version") + .toF("torchserve:inference/predictions?modelName=%s&modelVersion=%s", TEST_MODEL, TEST_MODEL_VERSION) + .to("mock:result"); + } + }; + } +} diff --git a/components/camel-ai/camel-torchserve/src/test/java/org/apache/camel/component/torchserve/ManagementTest.java b/components/camel-ai/camel-torchserve/src/test/java/org/apache/camel/component/torchserve/ManagementTest.java new file mode 100644 index 0000000000000..d71cca0c6c2a7 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/test/java/org/apache/camel/component/torchserve/ManagementTest.java @@ -0,0 +1,192 @@ +/* + * 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.torchserve; + +import java.util.concurrent.TimeUnit; + +import org.apache.camel.CamelContext; +import org.apache.camel.RoutesBuilder; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.torchserve.client.model.ModelList; +import org.junit.jupiter.api.Test; + +import static com.github.tomakehurst.wiremock.client.WireMock.delete; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.okJson; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.put; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; + +class ManagementTest extends TorchServeTestSupport { + + private static final String TEST_MODEL = "squeezenet1_1"; + private static final String TEST_MODEL_VERSION = "1.0"; + private static final String ADDED_MODEL_URL = "https://torchserve.pytorch.org/mar_files/mnist_v2.mar"; + private static final String ADDED_MODEL = "mnist_v2"; + private static final String ADDED_MODEL_VERSION = "2.0"; + + @Override + protected CamelContext createCamelContext() throws Exception { + var context = super.createCamelContext(); + var component = context.getComponent("torchserve", TorchServeComponent.class); + var configuration = component.getConfiguration(); + configuration.setManagementAddress(mockServer.baseUrl()); + return context; + } + + @Test + void testRegister() throws Exception { + mockServer.stubFor(post(urlPathEqualTo("/models")) + .willReturn(okJson("{ \"status\": \"registered\" }"))); + var mock = getMockEndpoint("mock:result"); + mock.expectedBodyReceived().constant("registered"); + + template.sendBody("direct:register", null); + + mock.await(1, TimeUnit.SECONDS); + mock.assertIsSatisfied(); + } + + @Test + void testScaleWorker() throws Exception { + mockServer.stubFor(put(urlPathEqualTo("/models/" + ADDED_MODEL)) + .willReturn(okJson("{ \"status\": \"processing\" }"))); + var mock = getMockEndpoint("mock:result"); + mock.expectedBodyReceived().constant("processing"); + + template.sendBody("direct:scale-worker", null); + + mock.await(1, TimeUnit.SECONDS); + mock.assertIsSatisfied(); + } + + @Test + void testScaleWorker_version() throws Exception { + var mock = getMockEndpoint("mock:result"); + mock.expectedMinimumMessageCount(1); + + template.sendBody("direct:list", null); + mock.await(1, TimeUnit.SECONDS); + + mock.assertIsSatisfied(); + } + + @Test + void testDescribe() throws Exception { + var mock = getMockEndpoint("mock:result"); + mock.expectedMinimumMessageCount(1); + + template.sendBody("direct:list", null); + mock.await(1, TimeUnit.SECONDS); + + mock.assertIsSatisfied(); + } + + @Test + void testDescribe_version() throws Exception { + var mock = getMockEndpoint("mock:result"); + mock.expectedMinimumMessageCount(1); + + template.sendBody("direct:list", null); + mock.await(1, TimeUnit.SECONDS); + + mock.assertIsSatisfied(); + } + + @Test + void testUnregister() throws Exception { + mockServer.stubFor(delete("/models/" + ADDED_MODEL) + .willReturn(okJson("{ \"status\": \"unregistered\" }"))); + var mock = getMockEndpoint("mock:result"); + mock.expectedBodyReceived().constant("unregistered"); + + template.sendBody("direct:unregister", null); + + mock.await(1, TimeUnit.SECONDS); + mock.assertIsSatisfied(); + } + + @Test + void testUnregister_version() throws Exception { + mockServer.stubFor(delete("/models/" + ADDED_MODEL + "/" + ADDED_MODEL_VERSION) + .willReturn(okJson("{ \"status\": \"unregistered\" }"))); + var mock = getMockEndpoint("mock:result"); + mock.expectedBodyReceived().constant("unregistered"); + + template.sendBody("direct:unregister_version", null); + + mock.await(1, TimeUnit.SECONDS); + mock.assertIsSatisfied(); + } + + @Test + void testList() throws Exception { + mockServer.stubFor(get(urlPathEqualTo("/models")) + .willReturn(okJson( + "{ \"models\": [ { \"modelName\": \"squeezenet1_1\", \"modelUrl\": \"squeezenet1_1.mar\" } ] }"))); + var mock = getMockEndpoint("mock:result"); + mock.expectedBodyReceived().body(ModelList.class); + + template.sendBody("direct:list", null); + + mock.await(1, TimeUnit.SECONDS); + mock.assertIsSatisfied(); + } + + @Test + void testSetDefault() throws Exception { + mockServer.stubFor(put("/models/" + TEST_MODEL + "/" + TEST_MODEL_VERSION + "/set-default") + .willReturn(okJson("{ \"status\": \"Default vesion succsesfully updated\" }"))); + var mock = getMockEndpoint("mock:result"); + mock.expectedBodyReceived().constant("Default vesion succsesfully updated"); + + template.sendBody("direct:set-default", null); + + mock.await(1, TimeUnit.SECONDS); + mock.assertIsSatisfied(); + } + + @Override + protected RoutesBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from("direct:register") + .toF("torchserve:management/register?url=%s", ADDED_MODEL_URL) + .to("mock:result"); + from("direct:scale-worker") + .toF("torchserve:management/scale-worker?modelName=%s", ADDED_MODEL) + .to("mock:result"); + from("direct:describe") + .to("torchserve:management/describe") + .to("mock:result"); + from("direct:unregister") + .toF("torchserve:management/unregister?modelName=%s", ADDED_MODEL) + .to("mock:result"); + from("direct:unregister_version") + .toF("torchserve:management/unregister?modelName=%s&modelVersion=%s", ADDED_MODEL, ADDED_MODEL_VERSION) + .to("mock:result"); + from("direct:list") + .to("torchserve:management/list") + .to("mock:result"); + from("direct:set-default") + .toF("torchserve:management/set-default?modelName=%s&modelVersion=%s", TEST_MODEL, TEST_MODEL_VERSION) + .to("mock:result"); + } + }; + } +} diff --git a/components/camel-ai/camel-torchserve/src/test/java/org/apache/camel/component/torchserve/MetricsTest.java b/components/camel-ai/camel-torchserve/src/test/java/org/apache/camel/component/torchserve/MetricsTest.java new file mode 100644 index 0000000000000..71ca76e7d7cc5 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/test/java/org/apache/camel/component/torchserve/MetricsTest.java @@ -0,0 +1,64 @@ +/* + * 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.torchserve; + +import java.util.concurrent.TimeUnit; + +import org.apache.camel.CamelContext; +import org.apache.camel.RoutesBuilder; +import org.apache.camel.builder.RouteBuilder; +import org.junit.jupiter.api.Test; + +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.okForContentType; + +class MetricsTest extends TorchServeTestSupport { + + @Override + protected CamelContext createCamelContext() throws Exception { + var context = super.createCamelContext(); + var component = context.getComponent("torchserve", TorchServeComponent.class); + var configuration = component.getConfiguration(); + configuration.setMetricsAddress(mockServer.baseUrl()); + return context; + } + + @Test + void testMetrics() throws Exception { + mockServer.stubFor(get("/metrics") + .willReturn(okForContentType("text/plain", "# HELP test\n# TYPE test counter\n"))); + var mock = getMockEndpoint("mock:result"); + mock.expectedBodyReceived().constant("# HELP test\n# TYPE test counter\n"); + + template.sendBody("direct:metrics", null); + + mock.await(1, TimeUnit.SECONDS); + mock.assertIsSatisfied(); + } + + @Override + protected RoutesBuilder createRouteBuilder() throws Exception { + return new RouteBuilder() { + @Override + public void configure() { + from("direct:metrics") + .to("torchserve:metrics/metrics") + .to("mock:result"); + } + }; + } +} diff --git a/components/camel-ai/camel-torchserve/src/test/java/org/apache/camel/component/torchserve/TorchServeTestSupport.java b/components/camel-ai/camel-torchserve/src/test/java/org/apache/camel/component/torchserve/TorchServeTestSupport.java new file mode 100644 index 0000000000000..5b9ea09735164 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/test/java/org/apache/camel/component/torchserve/TorchServeTestSupport.java @@ -0,0 +1,45 @@ +/* + * 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.torchserve; + +import com.github.tomakehurst.wiremock.WireMockServer; +import org.apache.camel.test.junit5.CamelTestSupport; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; + +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; + +class TorchServeTestSupport extends CamelTestSupport { + + protected static WireMockServer mockServer = new WireMockServer(options().dynamicPort()); + + @BeforeAll + static void startMockServer() { + mockServer.start(); + } + + @AfterAll + static void stopMockServer() { + mockServer.stop(); + } + + @BeforeEach + void resetMockServer() { + mockServer.resetRequests(); + } +} diff --git a/components/camel-ai/camel-torchserve/src/test/resources/data/0.png b/components/camel-ai/camel-torchserve/src/test/resources/data/0.png new file mode 100644 index 0000000000000..a193c47ba45d8 Binary files /dev/null and b/components/camel-ai/camel-torchserve/src/test/resources/data/0.png differ diff --git a/components/camel-ai/camel-torchserve/src/test/resources/data/1.png b/components/camel-ai/camel-torchserve/src/test/resources/data/1.png new file mode 100644 index 0000000000000..765d4e29a93b4 Binary files /dev/null and b/components/camel-ai/camel-torchserve/src/test/resources/data/1.png differ diff --git a/components/camel-ai/camel-torchserve/src/test/resources/data/kitten.jpg b/components/camel-ai/camel-torchserve/src/test/resources/data/kitten.jpg new file mode 100644 index 0000000000000..3353026abb02e Binary files /dev/null and b/components/camel-ai/camel-torchserve/src/test/resources/data/kitten.jpg differ diff --git a/components/camel-ai/camel-torchserve/src/test/resources/log4j2.properties b/components/camel-ai/camel-torchserve/src/test/resources/log4j2.properties new file mode 100644 index 0000000000000..5b1b5aa1ba47a --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/test/resources/log4j2.properties @@ -0,0 +1,28 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- + +appender.file.type = File +appender.file.name = file +appender.file.fileName = target/camel-torchserve-test.log +appender.file.layout.type = PatternLayout +appender.file.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n +appender.out.type = Console +appender.out.name = out +appender.out.layout.type = PatternLayout +appender.out.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n +rootLogger.level = INFO +rootLogger.appenderRef.file.ref = file diff --git a/components/camel-ai/camel-torchserve/src/test/resources/models/squeezenet1_1.mar b/components/camel-ai/camel-torchserve/src/test/resources/models/squeezenet1_1.mar new file mode 100644 index 0000000000000..3f9d563831f37 Binary files /dev/null and b/components/camel-ai/camel-torchserve/src/test/resources/models/squeezenet1_1.mar differ diff --git a/components/camel-ai/camel-torchserve/src/test/resources/torchserve/config.properties b/components/camel-ai/camel-torchserve/src/test/resources/torchserve/config.properties new file mode 100644 index 0000000000000..ea28f45a50148 --- /dev/null +++ b/components/camel-ai/camel-torchserve/src/test/resources/torchserve/config.properties @@ -0,0 +1,26 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- +inference_address=http://0.0.0.0:8080 +management_address=http://0.0.0.0:8081 +metrics_address=http://0.0.0.0:8082 +grpc_inference_address=0.0.0.0 +grpc_management_address=0.0.0.0 +number_of_netty_threads=32 +job_queue_size=1000 +model_store=/home/model-server/model-store +workflow_store=/home/model-server/wf-store +metrics_mode=prometheus diff --git a/components/camel-ai/pom.xml b/components/camel-ai/pom.xml index 1262e03b4fa2d..a05819ff04dc5 100644 --- a/components/camel-ai/pom.xml +++ b/components/camel-ai/pom.xml @@ -36,15 +36,16 @@ camel-chatscript camel-djl - camel-langchain4j-core camel-langchain4j-chat + camel-langchain4j-core camel-langchain4j-embeddings + camel-langchain4j-tokenizer + camel-langchain4j-tools camel-langchain4j-web-search camel-milvus camel-pinecone camel-qdrant - camel-langchain4j-tokenizer - camel-langchain4j-tools + camel-torchserve diff --git a/docs/components/modules/ROOT/examples/json/torchserve.json b/docs/components/modules/ROOT/examples/json/torchserve.json new file mode 120000 index 0000000000000..0671f9519cbde --- /dev/null +++ b/docs/components/modules/ROOT/examples/json/torchserve.json @@ -0,0 +1 @@ +../../../../../../components/camel-ai/camel-torchserve/src/generated/resources/META-INF/org/apache/camel/component/torchserve/torchserve.json \ No newline at end of file diff --git a/docs/components/modules/ROOT/nav.adoc b/docs/components/modules/ROOT/nav.adoc index cdbb87cbccc7c..d95c26c9b494e 100644 --- a/docs/components/modules/ROOT/nav.adoc +++ b/docs/components/modules/ROOT/nav.adoc @@ -14,6 +14,7 @@ *** xref:milvus-component.adoc[Milvus] *** xref:pinecone-component.adoc[Pinecone] *** xref:qdrant-component.adoc[Qdrant] +*** xref:torchserve-component.adoc[TorchServe] ** xref:amqp-component.adoc[AMQP] ** xref:arangodb-component.adoc[ArangoDb] ** xref:as2-component.adoc[AS2] diff --git a/docs/components/modules/ROOT/pages/torchserve-component.adoc b/docs/components/modules/ROOT/pages/torchserve-component.adoc new file mode 120000 index 0000000000000..d37d4cdcb69fc --- /dev/null +++ b/docs/components/modules/ROOT/pages/torchserve-component.adoc @@ -0,0 +1 @@ +../../../../../components/camel-ai/camel-torchserve/src/main/docs/torchserve-component.adoc \ No newline at end of file diff --git a/dsl/camel-jbang/camel-jbang-plugin-generate/pom.xml b/dsl/camel-jbang/camel-jbang-plugin-generate/pom.xml index 0f2929fd70ec8..77c7c0be40f90 100644 --- a/dsl/camel-jbang/camel-jbang-plugin-generate/pom.xml +++ b/dsl/camel-jbang/camel-jbang-plugin-generate/pom.xml @@ -71,7 +71,7 @@ org.openapitools openapi-generator - ${openapi-generator} + ${openapi-generator-version} org.slf4j diff --git a/parent/pom.xml b/parent/pom.xml index 5849675069de6..c04c5ba070e3c 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -232,6 +232,7 @@ 1.10 3.0.5 2.5.2 + 0.2.6 1.0.0 2.17.2 2.22.0 @@ -382,7 +383,7 @@ 2.0.13 5.0.0 3.4.3 - 7.8.0 + 7.8.0 4.0.1 2.17.1 2.14.0 @@ -2454,6 +2455,11 @@ camel-timer ${project.version} + + org.apache.camel + camel-torchserve + ${project.version} + org.apache.camel camel-tracing diff --git a/pom.xml b/pom.xml index 6f5fc7d174eda..16f1d939260de 100644 --- a/pom.xml +++ b/pom.xml @@ -265,6 +265,7 @@ **/*.jslt **/*.key **/*.keystore + **/*.mar **/*.model **/*.mp3 **/*.mp4