Skip to content
Open
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
15 changes: 10 additions & 5 deletions bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,16 @@
<artifactId>system-x-docling</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>software.tnb</groupId>
<artifactId>system-x-ollama</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>software.tnb</groupId>
<artifactId>system-x-qdrant</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>software.tnb</groupId>
<artifactId>system-x-elasticsearch</artifactId>
Expand Down Expand Up @@ -419,11 +429,6 @@
<artifactId>system-x-observability</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>software.tnb</groupId>
<artifactId>system-x-ollama</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>software.tnb</groupId>
<artifactId>system-x-opensearch</artifactId>
Expand Down
1 change: 1 addition & 0 deletions system-x/services/ai/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@
<module>docling</module>
<module>milvus</module>
<module>ollama</module>
<module>qdrant</module>
</modules>
</project>
18 changes: 18 additions & 0 deletions system-x/services/ai/qdrant/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?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">
<parent>
<artifactId>system-x-ai</artifactId>
<groupId>software.tnb</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>system-x-qdrant</artifactId>
<version>1.0-SNAPSHOT</version>
<name>TNB :: System-X :: Services :: Qdrant</name>

<dependencies>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package software.tnb.qdrant.resource.local;

import software.tnb.common.deployment.ContainerDeployable;
import software.tnb.qdrant.service.Qdrant;

import com.google.auto.service.AutoService;

@AutoService(Qdrant.class)
public class LocalQdrant extends Qdrant implements ContainerDeployable<QdrantContainer> {

private final QdrantContainer container = new QdrantContainer(defaultImage(), PORT);

@Override
public String host() {
return container.getHost();
}

@Override
public int port() {
return container.getMappedPort(PORT);
}

@Override
public void openResources() {
}

@Override
public void closeResources() {
}

@Override
public QdrantContainer container() {
return container;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package software.tnb.qdrant.resource.local;

import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;

public class QdrantContainer extends GenericContainer<QdrantContainer> {

public QdrantContainer(String image, int port) {
super(image);
this.withExposedPorts(port);
this.waitingFor(Wait.forLogMessage(".*starting in Actix runtime.*", 1));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package software.tnb.qdrant.resource.openshift;

import software.tnb.common.config.OpenshiftConfiguration;
import software.tnb.common.deployment.OpenshiftDeployable;
import software.tnb.common.deployment.WithName;
import software.tnb.common.openshift.OpenshiftClient;
import software.tnb.common.utils.NetworkUtils;
import software.tnb.common.utils.WaitUtils;
import software.tnb.common.utils.waiter.Waiter;
import software.tnb.qdrant.service.Qdrant;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.auto.service.AutoService;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;

import io.fabric8.kubernetes.api.model.ContainerPort;
import io.fabric8.kubernetes.api.model.ContainerPortBuilder;
import io.fabric8.kubernetes.api.model.IntOrString;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.ServiceBuilder;
import io.fabric8.kubernetes.api.model.Volume;
import io.fabric8.kubernetes.api.model.VolumeBuilder;
import io.fabric8.kubernetes.api.model.VolumeMount;
import io.fabric8.kubernetes.api.model.VolumeMountBuilder;
import io.fabric8.kubernetes.client.PortForward;
import io.fabric8.openshift.api.model.RouteBuilder;

@AutoService(Qdrant.class)
public class OpenshiftQdrant extends Qdrant implements OpenshiftDeployable, WithName {
private static final Logger LOG = LoggerFactory.getLogger(OpenshiftQdrant.class);

private PortForward portForward;
private int localPort;

@Override
public void undeploy() {
OpenshiftClient.get().apps().deployments().withName(name()).delete();
OpenshiftClient.get().services().withLabel(OpenshiftConfiguration.openshiftDeploymentLabel(), name()).delete();
OpenshiftClient.get().routes().withLabel(OpenshiftConfiguration.openshiftDeploymentLabel(), name()).delete();
WaitUtils.waitFor(new Waiter(() -> servicePod() == null, "Waiting until the pod is removed"));
}

@Override
public void openResources() {
localPort = NetworkUtils.getFreePort();
portForward = OpenshiftClient.get().services().withName(name()).portForward(PORT, localPort);
}

@Override
public void closeResources() {
NetworkUtils.releasePort(localPort);
if (portForward != null) {
try {
portForward.close();
} catch (Exception e) {
LOG.warn("Unable to close Qdrant port forward", e);
}
}
}

@Override
public void create() {
List<ContainerPort> ports = new LinkedList<>();
ports.add(new ContainerPortBuilder()
.withName("http")
.withProtocol("TCP")
.withContainerPort(PORT)
.build());

List<Volume> volumes = new LinkedList<>();
volumes.add(new VolumeBuilder()
.withName("qdrant-storage")
.withNewEmptyDir()
.endEmptyDir()
.build());
volumes.add(new VolumeBuilder()
.withName("qdrant-snapshots")
.withNewEmptyDir()
.endEmptyDir()
.build());

List<VolumeMount> volumeMounts = new LinkedList<>();
volumeMounts.add(new VolumeMountBuilder()
.withName("qdrant-storage")
.withMountPath("/qdrant/storage")
.build());
volumeMounts.add(new VolumeMountBuilder()
.withName("qdrant-snapshots")
.withMountPath("/qdrant/snapshots")
.build());

// @formatter:off
OpenshiftClient.get().createDeployment(Map.of(
"name", name(),
"image", image(),
"ports", ports,
"volumes", volumes,
"volumeMounts", volumeMounts
));

OpenshiftClient.get().services().resource(new ServiceBuilder()
.editOrNewMetadata()
.withName(name())
.addToLabels(OpenshiftConfiguration.openshiftDeploymentLabel(), name())
.endMetadata()
.editOrNewSpec()
.addToSelector(OpenshiftConfiguration.openshiftDeploymentLabel(), name())
.addNewPort()
.withName("http")
.withProtocol("TCP")
.withPort(PORT)
.withTargetPort(new IntOrString(PORT))
.endPort()
.endSpec()
.build()
).serverSideApply();

OpenshiftClient.get().routes().resource(new RouteBuilder()
.editOrNewMetadata()
.withName(name())
.addToLabels(OpenshiftConfiguration.openshiftDeploymentLabel(), name())
.endMetadata()
.editOrNewSpec()
.withNewTo()
.withKind("Service")
.withName(name())
.endTo()
.withNewPort()
.withNewTargetPort(PORT)
.endPort()
.withNewTls()
.withTermination("edge")
.withInsecureEdgeTerminationPolicy("Redirect")
.endTls()
.endSpec()
.build()
).serverSideApply();
// @formatter:on
}

@Override
public boolean isDeployed() {
return WithName.super.isDeployed();
}

@Override
public Predicate<Pod> podSelector() {
return WithName.super.podSelector();
}

@Override
public String host() {
return "localhost";
}

@Override
public int port() {
return localPort;
}

@Override
public String getLogs() {
return OpenshiftDeployable.super.getLogs();
}

@Override
public String name() {
return "qdrant";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package software.tnb.qdrant.service;

import software.tnb.common.account.NoAccount;
import software.tnb.common.client.NoClient;
import software.tnb.common.deployment.WithDockerImage;
import software.tnb.common.service.Service;
import software.tnb.qdrant.validation.QdrantValidation;

public abstract class Qdrant extends Service<NoAccount, NoClient, QdrantValidation> implements WithDockerImage {

protected static final int PORT = 6333;

public abstract String host();

public abstract int port();

public String url() {
return String.format("http://%s:%d", host(), port());
}

public QdrantValidation validation() {
if (validation == null) {
validation = new QdrantValidation(url());
}
return validation;
}

@Override
public String defaultImage() {
return "quay.io/fuse_qe/qdrant:1.16.3";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this image does not have a description in quay

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package software.tnb.qdrant.validation;

import software.tnb.common.utils.HTTPUtils;
import software.tnb.common.validation.Validation;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import okhttp3.MediaType;
import okhttp3.RequestBody;

public class QdrantValidation implements Validation {

private static final Logger LOG = LoggerFactory.getLogger(QdrantValidation.class);
private static final MediaType JSON = MediaType.parse("application/json");

private final String baseUrl;

public QdrantValidation(String baseUrl) {
this.baseUrl = baseUrl;
}

public String createCollection(String name, int vectorSize, String distance) {
LOG.debug("Creating collection '{}' with vectorSize={} and distance={}", name, vectorSize, distance);
String body = String.format("{\"vectors\":{\"size\":%d,\"distance\":\"%s\"}}", vectorSize, distance);
HTTPUtils.Response response = HTTPUtils.getInstance()
.put(baseUrl + "/collections/" + name, RequestBody.create(body, JSON));
return response.getBody();
}

public String deleteCollection(String name) {
LOG.debug("Deleting collection '{}'", name);
HTTPUtils.getInstance().delete(baseUrl + "/collections/" + name);
return "";
}

public String upsert(String collectionName, String pointsJson) {
LOG.debug("Upserting points into collection '{}'", collectionName);
String body = String.format("{\"points\":%s}", pointsJson);
HTTPUtils.Response response = HTTPUtils.getInstance()
.put(baseUrl + "/collections/" + collectionName + "/points", RequestBody.create(body, JSON));
return response.getBody();
}

public String query(String collectionName, String queryJson) {
LOG.debug("Querying collection '{}'", collectionName);
HTTPUtils.Response response = HTTPUtils.getInstance()
.post(baseUrl + "/collections/" + collectionName + "/points/query", RequestBody.create(queryJson, JSON));
return response.getBody();
}
}
12 changes: 8 additions & 4 deletions system-x/services/all/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,14 @@
<groupId>software.tnb</groupId>
<artifactId>system-x-docling</artifactId>
</dependency>
<dependency>
<groupId>software.tnb</groupId>
<artifactId>system-x-ollama</artifactId>
</dependency>
<dependency>
<groupId>software.tnb</groupId>
<artifactId>system-x-qdrant</artifactId>
</dependency>
<dependency>
<groupId>software.tnb</groupId>
<artifactId>system-x-elasticsearch</artifactId>
Expand Down Expand Up @@ -301,10 +309,6 @@
<groupId>software.tnb</groupId>
<artifactId>system-x-observability</artifactId>
</dependency>
<dependency>
<groupId>software.tnb</groupId>
<artifactId>system-x-ollama</artifactId>
</dependency>
<dependency>
<groupId>software.tnb</groupId>
<artifactId>system-x-opensearch</artifactId>
Expand Down
Loading