diff --git a/dynamixs-ai-app/pom.xml b/dynamixs-ai-app/pom.xml
index 5e679e1..1b9a0da 100644
--- a/dynamixs-ai-app/pom.xml
+++ b/dynamixs-ai-app/pom.xml
@@ -272,7 +272,6 @@
${apache.pdfbox.version}
-
org.imixs.workflow
@@ -280,24 +279,6 @@
${org.imixs.ai.version}
-
-
- com.datastax.oss
- java-driver-core
- ${apache.cassandra.driver.version}
-
-
- com.datastax.oss
- java-driver-query-builder
- ${apache.cassandra.driver.version}
-
-
- com.datastax.oss
- java-driver-mapper-runtime
- ${apache.cassandra.driver.version}
-
-
-
org.imixs.workflow
diff --git a/dynamixs-ai-rag/pom.xml b/dynamixs-ai-rag/pom.xml
index f3b0c74..1a826b4 100644
--- a/dynamixs-ai-rag/pom.xml
+++ b/dynamixs-ai-rag/pom.xml
@@ -166,17 +166,17 @@
- com.datastax.oss
+ org.apache.cassandra
java-driver-core
${apache.cassandra.driver.version}
- com.datastax.oss
+ org.apache.cassandra
java-driver-query-builder
${apache.cassandra.driver.version}
- com.datastax.oss
+ org.apache.cassandra
java-driver-mapper-runtime
${apache.cassandra.driver.version}
diff --git a/dynamixs-ai-rag/src/main/java/ai/dynamixs/rag/ClusterService.java b/dynamixs-ai-rag/src/main/java/ai/dynamixs/rag/ClusterService.java
index 0c259b1..cdb50ab 100644
--- a/dynamixs-ai-rag/src/main/java/ai/dynamixs/rag/ClusterService.java
+++ b/dynamixs-ai-rag/src/main/java/ai/dynamixs/rag/ClusterService.java
@@ -1,7 +1,7 @@
package ai.dynamixs.rag;
import java.net.InetSocketAddress;
-import java.time.Instant;
+import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.logging.Level;
@@ -15,6 +15,7 @@ import com.datastax.oss.driver.api.core.CqlSessionBuilder;
import com.datastax.oss.driver.api.core.InvalidKeyspaceException;
import com.datastax.oss.driver.api.core.cql.BoundStatement;
import com.datastax.oss.driver.api.core.cql.PreparedStatement;
+import com.datastax.oss.driver.api.core.data.CqlVector;
import com.datastax.oss.driver.api.core.servererrors.InvalidQueryException;
import jakarta.annotation.PostConstruct;
@@ -53,6 +54,9 @@ public class ClusterService {
public static final String ENV_EMBEDDINGS_CLUSTER_CONTACTPOINTS = "EMBEDDINGS_CLUSTER_CONTACTPOINTS";
public static final String ENV_EMBEDDINGS_CLUSTER_KEYSPACE = "EMBEDDINGS_CLUSTER_KEYSPACE";
+ // public static final int DIMENSIONS=768;
+ public static final int DIMENSIONS = 384;
+
// optional environment settings
public static final String ENV_EMBEDDINGS_CLUSTER_AUTH_USER = "EMBEDDINGS_CLUSTER_AUTH_USER";
public static final String ENV_EMBEDDINGS_CLUSTER_AUTH_PASSWORD = "EMBEDDINGS_CLUSTER_AUTH_PASSWORD";
@@ -118,6 +122,7 @@ public class ClusterService {
Optional keystorePwd;
private CqlSession session;
+ private PreparedStatement insertVectorStmt;
@Resource
private TimerService timerService;
@@ -178,6 +183,7 @@ public class ClusterService {
logger.info("├── initializing cluster and keyspace...");
session = initSessionWithKeyspace();
logger.info("│ ├── ✅ schema status = OK");
+
} catch (Exception e) {
throw new ClusterException(ClusterException.CLUSTER_ERROR,
"Failed to init cassandra session: " + e.getMessage(), e);
@@ -303,11 +309,61 @@ public class ClusterService {
/**
* This method creates the keySpace schema.
*
+ * CREATE TABLE embeddings.document_vectors ( business_document_id uuid,
+ * chunk_id text, chunk_text text, content_vector VECTOR , PRIMARY
+ * KEY (business_document_id, chunk_id) ); );
+ *
* @param cluster
* @throws ArchiveException
*/
private void createSchema() throws ClusterException {
+ logger.info("│ ├── create schema...");
+ // now create table schemas
+ String query = "CREATE TABLE IF NOT EXISTS document_vectors (\n" + //
+ " id text,\n" + //
+ " chunk_id uuid,\n" + //
+ " content_chunk text,\n" + //
+ " content_vector VECTOR ,\n" + //
+ " PRIMARY KEY (id, chunk_id)\n" + //
+ ");";
+ session.execute(query);
+
+ logger.info("│ ├── create index...");
+ query = "CREATE INDEX IF NOT EXISTS edv_ann_index\n" + //
+ " ON document_vectors(content_vector) USING 'sai';";
+
+ session.execute(query);
+ logger.info("│ ├── ✅ database schema created.");
+ }
+
+ public void insertVector(String uniqueID, String content, List vector) throws ClusterException {
+
+ // Prepare statement?
+ if (insertVectorStmt == null) {
+ String insertQuery = "INSERT INTO document_vectors "
+ + "(id, chunk_id, content_chunk, content_vector) VALUES (?, ?, ?, ?)";
+ insertVectorStmt = session.prepare(insertQuery);
+ }
+
+ try {
+ // Generiere UUID und aktuelle Zeit
+ UUID chunk_id = UUID.randomUUID();
+
+ // CqlVector verwenden
+ CqlVector cqlVector = CqlVector.newInstance(vector);
+
+ BoundStatement boundStmt = insertVectorStmt.bind(uniqueID, chunk_id, content, cqlVector);
+ session.execute(boundStmt);
+
+ } catch (Exception e) {
+ throw new ClusterException(ClusterException.CLUSTER_ERROR,
+ "Failed to insert embeddings into keyspace: " + e.getMessage(), e);
+ }
+ }
+
+ private void oldcreateSchema() throws ClusterException {
+
logger.info("│ ├── create schema...");
// now create table schemas
String query = "CREATE TABLE IF NOT EXISTS document_vectors (\n" + //
@@ -328,44 +384,4 @@ public class ClusterService {
session.execute(query);
logger.info("│ ├── ✅ database schema created.");
}
-
- public void insertVector(String content, float[] vector) throws ClusterException {
- String keySpacename = keySpace.orElse(null);
- try {
- // Generiere UUID und aktuelle Zeit
- UUID id = UUID.randomUUID();
- Instant createdAt = Instant.now();
-
- // CQL-Query mit Platzhaltern
- String insertQuery = "INSERT INTO " + keySpacename + ".document_vectors "
- + "(record_id, id, content_chunk, content_vector, created_at) VALUES (now(), ?, ?, ?, ?)";
-
- // Vorbereiten und Ausführen der Query
- PreparedStatement preparedStmt = session.prepare(insertQuery);
- BoundStatement boundStmt = preparedStmt.bind(id, content, vector, createdAt);
- session.execute(boundStmt);
-
- // id = uuid.uuid4()
- // content_chunk = content
- // content_vector = vector
- // created_at = datetime.datetime.now()
- // insert_query = """
- // INSERT INTO embeddings.document_vectors (record_id, id, content_chunk,
- // content_vector, created_at)
- // VALUES (now(), ?, ?, ?, ?)
- // """
- // prepared_stmt = session.prepare(insert_query)
- // session.execute(prepared_stmt, [
- // id,
- // content_chunk,
- // content_vector,
- // created_at
- // ])
-
- } catch (Exception e) {
- throw new ClusterException(ClusterException.CLUSTER_ERROR,
- "Failed to insert embeddings into keyspace: " + e.getMessage(), e);
- }
- }
-
}
diff --git a/dynamixs-ai-rag/src/main/java/ai/dynamixs/rag/RAGAdapter.java b/dynamixs-ai-rag/src/main/java/ai/dynamixs/rag/RAGAdapter.java
index 17b7e89..59b80be 100644
--- a/dynamixs-ai-rag/src/main/java/ai/dynamixs/rag/RAGAdapter.java
+++ b/dynamixs-ai-rag/src/main/java/ai/dynamixs/rag/RAGAdapter.java
@@ -200,13 +200,23 @@ public class RAGAdapter implements SignalAdapter {
logger.info(llmPrompt);
}
- float[] indexResult = llmService.postEmbedding(llmPrompt, llmAPIEndpoint, llmAPIDebug);
+ // chunk text....
+ List chunk_list = RAGUtil.chunkMarkupDocument(llmPrompt);
+ for (String chunk : chunk_list) {
+ if (llmAPIDebug) {
+ logger.info("├── 🔸 chunk: ");
+ logger.info(chunk);
+ }
+ List indexResult = llmService.postEmbedding(chunk, llmAPIEndpoint, llmAPIDebug);
+ // write to cassandra
+ clusterService.insertVector(workitem.getUniqueID(), chunk, indexResult);
+ if (llmAPIDebug) {
+ logger.info("├── ⇨ " + indexResult.size() + " floats stored in RAG db.");
- // write to cassandra
- clusterService.insertVector(promptTemplate, indexResult);
+ }
+ }
if (llmAPIDebug) {
- logger.info("├── ✅ " + indexResult.length + " floats stored in RAG db.");
logger.info(
"├── ✅ Total processing time: " + (System.currentTimeMillis() - processingTime) + "ms");
}
diff --git a/dynamixs-ai-rag/src/main/java/ai/dynamixs/rag/RAGUtil.java b/dynamixs-ai-rag/src/main/java/ai/dynamixs/rag/RAGUtil.java
new file mode 100644
index 0000000..4bd341a
--- /dev/null
+++ b/dynamixs-ai-rag/src/main/java/ai/dynamixs/rag/RAGUtil.java
@@ -0,0 +1,101 @@
+/*
+ * Imixs-Workflow
+ *
+ * Copyright (C) 2001-2020 Imixs Software Solutions GmbH,
+ * http://www.imixs.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You can receive a copy of the GNU General Public
+ * License at http://www.gnu.org/licenses/gpl.html
+ *
+ * Project:
+ * https://www.imixs.org
+ * https://github.com/imixs/imixs-workflow
+ *
+ * Contributors:
+ * Imixs Software Solutions GmbH - Project Management
+ * Ralph Soika - Software Developer
+ */
+
+package ai.dynamixs.rag;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * The RAGUtil provides methods to chunk markup text
+ *
+ * @author Ralph Soika
+ * @version 1.0
+ *
+ */
+public class RAGUtil {
+
+ private static Logger logger = Logger.getLogger(RAGAdapter.class.getName());
+
+ /**
+ * This helper method creates smaller text chunks from a large markup text.
+ */
+ public static List chunkMarkupDocument(String markupText) {
+ List chunks = new ArrayList<>();
+ StringBuilder currentChunk = new StringBuilder();
+
+ // Regex für Abschnitts-Header (# oder **)
+ Pattern sectionPattern = Pattern.compile("^(# .+|\\*\\*.+\\*\\*)", Pattern.MULTILINE);
+ Matcher matcher = sectionPattern.matcher(markupText);
+
+ int lastEnd = 0;
+ while (matcher.find()) {
+ // Text zwischen den Headern
+ String sectionContent = markupText.substring(lastEnd, matcher.start()).trim();
+
+ if (!sectionContent.isEmpty()) {
+ // Füge Inhalt zum aktuellen Chunk hinzu
+ if (currentChunk.length() + sectionContent.length() > 500) {
+ flushChunk(chunks, currentChunk);
+ }
+ currentChunk.append(sectionContent).append("\n");
+ }
+
+ // Header selbst behandeln
+ String header = matcher.group(1);
+ if (currentChunk.length() + header.length() > 300) {
+ flushChunk(chunks, currentChunk);
+ }
+ currentChunk.append(header).append("\n");
+
+ lastEnd = matcher.end();
+ }
+
+ // Restlichen Text nach dem letzten Header
+ String remainingContent = markupText.substring(lastEnd).trim();
+ if (!remainingContent.isEmpty()) {
+ currentChunk.append(remainingContent);
+ flushChunk(chunks, currentChunk);
+ } else if (currentChunk.length() > 0) {
+ chunks.add(currentChunk.toString().trim());
+ }
+
+ return chunks;
+ }
+
+ private static void flushChunk(List chunks, StringBuilder currentChunk) {
+ if (currentChunk.length() > 0) {
+ chunks.add(currentChunk.toString().trim());
+ currentChunk.setLength(0);
+ }
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index 4f96194..31c5e02 100644
--- a/pom.xml
+++ b/pom.xml
@@ -70,7 +70,7 @@
1.0.0
9.9.1-4
2.0.24
- 4.17.0
+ 4.19.0
5.9.2
5.8.0
@@ -273,7 +273,7 @@
${org.imixs.ai.version}
-
+
diff --git a/workflow/bedarfsmeldung-de-1.0.0.bpmn b/workflow/bedarfsmeldung-de-1.0.0.bpmn
index 42936fe..dcddb90 100644
--- a/workflow/bedarfsmeldung-de-1.0.0.bpmn
+++ b/workflow/bedarfsmeldung-de-1.0.0.bpmn
@@ -2115,7 +2115,7 @@ result.isValid=true;
- llm.service.endpoint
+ https://llama.cpp2.imixs.com/
true
]]>