rag servcie impl
This commit is contained in:
parent
36f06a6d6a
commit
b4c4cd49f6
7 changed files with 179 additions and 71 deletions
|
|
@ -272,7 +272,6 @@
|
|||
<version>${apache.pdfbox.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Imixs-AI -->
|
||||
<dependency>
|
||||
<groupId>org.imixs.workflow</groupId>
|
||||
|
|
@ -280,24 +279,6 @@
|
|||
<version>${org.imixs.ai.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- DaraStax Java Driver -->
|
||||
<dependency>
|
||||
<groupId>com.datastax.oss</groupId>
|
||||
<artifactId>java-driver-core</artifactId>
|
||||
<version>${apache.cassandra.driver.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.datastax.oss</groupId>
|
||||
<artifactId>java-driver-query-builder</artifactId>
|
||||
<version>${apache.cassandra.driver.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.datastax.oss</groupId>
|
||||
<artifactId>java-driver-mapper-runtime</artifactId>
|
||||
<version>${apache.cassandra.driver.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Imixs-QR-Code -->
|
||||
<dependency>
|
||||
<groupId>org.imixs.workflow</groupId>
|
||||
|
|
|
|||
|
|
@ -166,17 +166,17 @@
|
|||
|
||||
<!-- DaraStax Java Driver -->
|
||||
<dependency>
|
||||
<groupId>com.datastax.oss</groupId>
|
||||
<groupId>org.apache.cassandra</groupId>
|
||||
<artifactId>java-driver-core</artifactId>
|
||||
<version>${apache.cassandra.driver.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.datastax.oss</groupId>
|
||||
<groupId>org.apache.cassandra</groupId>
|
||||
<artifactId>java-driver-query-builder</artifactId>
|
||||
<version>${apache.cassandra.driver.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.datastax.oss</groupId>
|
||||
<groupId>org.apache.cassandra</groupId>
|
||||
<artifactId>java-driver-mapper-runtime</artifactId>
|
||||
<version>${apache.cassandra.driver.version}</version>
|
||||
</dependency>
|
||||
|
|
|
|||
|
|
@ -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<String> 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 <FLOAT, 768>, 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 <FLOAT, " + DIMENSIONS + ">,\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<Float> 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<Float> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -200,13 +200,23 @@ public class RAGAdapter implements SignalAdapter {
|
|||
logger.info(llmPrompt);
|
||||
}
|
||||
|
||||
float[] indexResult = llmService.postEmbedding(llmPrompt, llmAPIEndpoint, llmAPIDebug);
|
||||
|
||||
// chunk text....
|
||||
List<String> chunk_list = RAGUtil.chunkMarkupDocument(llmPrompt);
|
||||
for (String chunk : chunk_list) {
|
||||
if (llmAPIDebug) {
|
||||
logger.info("├── 🔸 chunk: ");
|
||||
logger.info(chunk);
|
||||
}
|
||||
List<Float> indexResult = llmService.postEmbedding(chunk, llmAPIEndpoint, llmAPIDebug);
|
||||
// write to cassandra
|
||||
clusterService.insertVector(promptTemplate, indexResult);
|
||||
clusterService.insertVector(workitem.getUniqueID(), chunk, indexResult);
|
||||
if (llmAPIDebug) {
|
||||
logger.info("├── ⇨ " + indexResult.size() + " floats stored in RAG db.");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (llmAPIDebug) {
|
||||
logger.info("├── ✅ " + indexResult.length + " floats stored in RAG db.");
|
||||
logger.info(
|
||||
"├── ✅ Total processing time: " + (System.currentTimeMillis() - processingTime) + "ms");
|
||||
}
|
||||
|
|
|
|||
101
dynamixs-ai-rag/src/main/java/ai/dynamixs/rag/RAGUtil.java
Normal file
101
dynamixs-ai-rag/src/main/java/ai/dynamixs/rag/RAGUtil.java
Normal file
|
|
@ -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<String> chunkMarkupDocument(String markupText) {
|
||||
List<String> 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<String> chunks, StringBuilder currentChunk) {
|
||||
if (currentChunk.length() > 0) {
|
||||
chunks.add(currentChunk.toString().trim());
|
||||
currentChunk.setLength(0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
6
pom.xml
6
pom.xml
|
|
@ -70,7 +70,7 @@
|
|||
<org.imixs.signature.version>1.0.0</org.imixs.signature.version>
|
||||
<net.sf.saxon.version>9.9.1-4</net.sf.saxon.version>
|
||||
<apache.pdfbox.version>2.0.24</apache.pdfbox.version>
|
||||
<apache.cassandra.driver.version>4.17.0</apache.cassandra.driver.version>
|
||||
<apache.cassandra.driver.version>4.19.0</apache.cassandra.driver.version>
|
||||
<!-- test dependencies -->
|
||||
<junit.jupiter.version>5.9.2</junit.jupiter.version>
|
||||
<mockito.version>5.8.0</mockito.version>
|
||||
|
|
@ -273,7 +273,7 @@
|
|||
<version>${org.imixs.ai.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- DaraStax Java Driver -->
|
||||
<!-- DaraStax Java Driver
|
||||
<dependency>
|
||||
<groupId>com.datastax.oss</groupId>
|
||||
<artifactId>java-driver-core</artifactId>
|
||||
|
|
@ -289,7 +289,7 @@
|
|||
<artifactId>java-driver-mapper-runtime</artifactId>
|
||||
<version>${apache.cassandra.driver.version}</version>
|
||||
</dependency>
|
||||
|
||||
-->
|
||||
|
||||
<!-- Imixs-QR-Code -->
|
||||
<dependency>
|
||||
|
|
|
|||
|
|
@ -2115,7 +2115,7 @@ result.isValid=true;
|
|||
</imixs:item>
|
||||
<imixs:item name="txtactivityresult" type="xs:string">
|
||||
<imixs:value><![CDATA[<imixs-ai name="INDEX">
|
||||
<endpoint><propertyvalue>llm.service.endpoint</propertyvalue></endpoint>
|
||||
<endpoint>https://llama.cpp2.imixs.com/</endpoint>
|
||||
<debug>true</debug>
|
||||
</imixs-ai>
|
||||
]]></imixs:value>
|
||||
|
|
|
|||
Loading…
Reference in a new issue