这是indexloc提供的服务,不要输入任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions messaging-modules/apache-camel-kserve/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
target/
dependency-reduced-pom.xml

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr

### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/

### VS Code ###
.vscode/

### Mac OS ###
.DS_Store
15 changes: 15 additions & 0 deletions messaging-modules/apache-camel-kserve/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
This module contains 2 sub-modules:

1) triton server with pre-loaded model(should be downloaded)
2) the sentiment-service in java with apache-camel-kserve

The modules both contain a Dockerfile and can be easily deployed locally using docker-compose.yml

First, you need to download the model from [huggingface](https://huggingface.co/pjxcharya/onnx-sentiment-model/tree/main) and place it in triton-server/models/sentiment/1.
Then execute:

```bash
docker-compose up --build
```

The endpoint to test everything works is: `http://localhost:8080/sentiments?sentence=i probably like you`
16 changes: 16 additions & 0 deletions messaging-modules/apache-camel-kserve/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
version: '3.8'

services:
triton-server:
build: ./triton-server
environment:
- NVIDIA_VISIBLE_DEVICES=all
ports:
- "8000:8000" # HTTP
- "8001:8001" # gRPC
- "8002:8002" # Metrics
sentiment-service:
build: ./sentiment-service
ports:
- "8080:8080"
restart: unless-stopped
19 changes: 19 additions & 0 deletions messaging-modules/apache-camel-kserve/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>

<parent>
<groupId>com.baeldung</groupId>
<artifactId>messaging-modules</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>

<artifactId>sentiment-parent-pom</artifactId>

<modules>
<module>sentiment-service</module>
</modules>
</project>
12 changes: 12 additions & 0 deletions messaging-modules/apache-camel-kserve/sentiment-service/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM eclipse-temurin:21-jre

WORKDIR /app

# Copy the fat JAR from the builder stage
COPY target/sentiment-service-1.0-SNAPSHOT.jar app.jar

# Expose HTTP port
EXPOSE 8080

# Run the app
ENTRYPOINT ["java", "-jar", "app.jar"]
108 changes: 108 additions & 0 deletions messaging-modules/apache-camel-kserve/sentiment-service/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>com.baeldung</groupId>
<artifactId>sentiment-parent-pom</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>

<name>Sentiment System - Service</name>
<description>This is the main service of the system, that uses Apache Camel to integrate with Triton server and
use an AI model for inference</description>
<artifactId>sentiment-service</artifactId>

<properties>
<java.version>21</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.release>${java.version}</maven.compiler.release>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<camel.version>4.13.0</camel.version>
<jackson-databind.version>2.19.2</jackson-databind.version>
<tokenizers.version>0.21.0</tokenizers.version>
<maven-shade-plugin.version>3.6.0</maven-shade-plugin.version>
</properties>

<dependencies>
<!-- Core Camel -->
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-main</artifactId>
<version>${camel.version}</version>
</dependency>

<!-- REST support via Undertow -->
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-undertow</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-rest</artifactId>
<version>${camel.version}</version>
</dependency>

<!-- KServe inference component -->
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-kserve</artifactId>
<version>${camel.version}</version>
</dependency>

<!-- JSON support -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson-databind.version}</version>
</dependency>

<!-- lib to use for tokenizer -->
<dependency>
<groupId>ai.djl.huggingface</groupId>
<artifactId>tokenizers</artifactId>
<version>${tokenizers.version}</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>org.learnings.aimodels.sentiments.Application</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>${maven-shade-plugin.version}</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>shade</goal></goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.learnings.aimodels.sentiments.Application</mainClass>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.learnings.aimodels.sentiments;

import org.apache.camel.CamelContext;
import org.apache.camel.impl.DefaultCamelContext;
import org.learnings.aimodels.sentiments.web.api.SentimentsRoute;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Application {

private static final Logger log = LoggerFactory.getLogger(Application.class);

public static void main(String[] args) throws Exception {
CamelContext context = new DefaultCamelContext();
context.addRoutes(new SentimentsRoute());

context.start();
log.info("🚀 Sentiment service running on http://localhost:8080/sentiments");
Thread.sleep(Long.MAX_VALUE);
context.stop();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package org.learnings.aimodels.sentiments.web.api;

import ai.djl.huggingface.tokenizers.Encoding;
import ai.djl.huggingface.tokenizers.HuggingFaceTokenizer;
import com.google.protobuf.ByteString;
import inference.GrpcPredictV2.InferTensorContents;
import inference.GrpcPredictV2.ModelInferRequest;
import inference.GrpcPredictV2.ModelInferResponse;
import org.apache.camel.Exchange;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.model.rest.RestBindingMode;
import org.apache.camel.model.rest.RestParamType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class SentimentsRoute extends RouteBuilder {

private static final Logger log = LoggerFactory.getLogger(SentimentsRoute.class);
private final HuggingFaceTokenizer tokenizer = HuggingFaceTokenizer.newInstance("distilbert-base-uncased");

@Override
public void configure() {
// Configure REST via Undertow
restConfiguration()
.component("undertow")
.host("0.0.0.0")
.port(8080)
.bindingMode(RestBindingMode.off);

// REST GET endpoint
rest("/sentiments")
.get()
.param().name("sentence").required(true).type(RestParamType.query).endParam()
.outType(String[].class)
.responseMessage().code(200).message("the sentence is.. ").endResponseMessage()
.to("direct:classify");

// Main route
from("direct:classify")
.routeId("sentiment-inference")
.setBody(this::createRequest)
.setHeader("Content-Type", constant("application/json"))
.to("kserve:infer?modelName=sentiment&target=host.docker.internal:8001")
// .to("kserve:infer?modelName=sentiment&target=localhost:8001")
.process(this::postProcess);
}

private ModelInferRequest createRequest(Exchange exchange) {
String sentence = exchange.getIn().getHeader("sentence", String.class);
Encoding encoding = tokenizer.encode(sentence);
List<Long> inputIds = Arrays.stream(encoding.getIds()).boxed().collect(Collectors.toList());
List<Long> attentionMask = Arrays.stream(encoding.getAttentionMask()).boxed().collect(Collectors.toList());

var content0 = InferTensorContents.newBuilder().addAllInt64Contents(inputIds);
var input0 = ModelInferRequest.InferInputTensor.newBuilder()
.setName("input_ids").setDatatype("INT64").addShape(1).addShape(inputIds.size())
.setContents(content0);

var content1 = InferTensorContents.newBuilder().addAllInt64Contents(attentionMask);
var input1 = ModelInferRequest.InferInputTensor.newBuilder()
.setName("attention_mask").setDatatype("INT64").addShape(1).addShape(attentionMask.size())
.setContents(content1);

ModelInferRequest requestBody = ModelInferRequest.newBuilder()
.addInputs(0, input0).addInputs(1, input1)
.build();
log.debug("-- payload: [{}]", requestBody);

return requestBody;
}

private void postProcess(Exchange exchange) {
log.debug("-- in response");
ModelInferResponse response = exchange.getMessage().getBody(ModelInferResponse.class);

List<List<Float>> logits = response.getRawOutputContentsList().stream()
.map(ByteString::asReadOnlyByteBuffer)
.map(buf -> buf.order(ByteOrder.LITTLE_ENDIAN).asFloatBuffer())
.map(buf -> {
List<Float> longs = new ArrayList<>(buf.remaining());
while (buf.hasRemaining()) {
longs.add(buf.get());
}
return longs;
})
.toList();

log.debug("-- logits: [{}]", logits);
String result = Math.abs(logits.getFirst().getFirst()) < logits.getFirst().getLast() ? "good" : "bad";

exchange.getMessage().setBody(result);
}
}
10 changes: 10 additions & 0 deletions messaging-modules/apache-camel-kserve/triton-server/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM nvcr.io/nvidia/tritonserver:25.02-py3

# Copy the model repository into the container
COPY models/ /models/

# Expose default Triton ports
EXPOSE 8000 8001 8002

# Set entrypoint to run Triton with your model repo
CMD ["tritonserver", "--model-repository=/models"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: "sentiment"
platform: "onnxruntime_onnx"
max_batch_size: 8

input [
{
name: "input_ids"
data_type: TYPE_INT64
dims: [ -1 ]
},
{
name: "attention_mask"
data_type: TYPE_INT64
dims: [ -1 ]
}
]

output [
{
name: "logits"
data_type: TYPE_FP32
dims: [ 2 ]
}
]
1 change: 1 addition & 0 deletions messaging-modules/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

<modules>
<module>apache-camel</module>
<module>apache-camel-kserve</module>
<module>apache-rocketmq</module>
<module>automq</module>
<module>jgroups</module>
Expand Down