Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Declarative AI Services #12

Merged
merged 5 commits into from
Mar 25, 2024
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
79 changes: 79 additions & 0 deletions langchain4j-easy-rag-spring-boot-starter/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?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>dev.langchain4j</groupId>
<artifactId>langchain4j-spring</artifactId>
<version>0.29.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

<artifactId>langchain4j-easy-rag-spring-boot-starter</artifactId>
<name>Spring Boot starter for Easy RAG</name>

<dependencies>

<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-easy-rag</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
</dependency>

<!-- should be listed before spring-boot-configuration-processor -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>

<!-- needed to generate automatic metadata about available config properties -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>

</dependencies>

<licenses>
<license>
<name>Apache-2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
<comments>A business-friendly OSS license</comments>
</license>
</licenses>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package dev.langchain4j.rag.easy.spring;

import lombok.Getter;
import lombok.Setter;

import java.nio.file.Path;

@Getter
@Setter
class DocumentsProperties {

Path path;
String glob;
Boolean recursion;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package dev.langchain4j.rag.easy.spring;

import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.DocumentSplitter;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.document.splitter.DocumentSplitters;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.Tokenizer;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.embedding.HuggingFaceTokenizer;
import dev.langchain4j.model.embedding.bge.small.en.v15.BgeSmallEnV15QuantizedEmbeddingModel;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import org.apache.commons.lang3.NotImplementedException;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

import java.util.List;

import static dev.langchain4j.internal.Utils.isNotNullOrBlank;
import static dev.langchain4j.rag.easy.spring.EasyRagProperties.PREFIX;
import static java.util.Collections.singletonList;

@AutoConfiguration
@EnableConfigurationProperties(EasyRagProperties.class)
public class EasyRagAutoConfig {

@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(HuggingFaceTokenizer.class)
Tokenizer tokenizer() { // TODO bean name, type
return new HuggingFaceTokenizer();
}

@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(DocumentSplitters.class)
DocumentSplitter documentSplitter(Tokenizer tokenizer) { // TODO bean name, type
return DocumentSplitters.recursive(300, 30, tokenizer);
}

@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(BgeSmallEnV15QuantizedEmbeddingModel.class)
EmbeddingModel embeddingModel() { // TODO bean name, type
return new BgeSmallEnV15QuantizedEmbeddingModel();
}

@Bean
@ConditionalOnProperty(PREFIX + ".ingestion.documents.path")
ApplicationRunner easyRagDocumentIngestor(DocumentSplitter documentSplitter, // TODO should splitter be optional?
EmbeddingModel embeddingModel,
EmbeddingStore<TextSegment> embeddingStore,
EasyRagProperties easyRagProperties) { // TODO bean name, type
return args -> {

List<Document> documents = loadDocuments(easyRagProperties.ingestion.documents);

EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()
// TODO documentTransformer
.documentSplitter(documentSplitter)
// TODO textSegmentTransformer
.embeddingStore(embeddingStore)
.embeddingModel(embeddingModel)
.build();

ingestor.ingest(documents);
};
}

private static List<Document> loadDocuments(DocumentsProperties documentsProperties) {
if (documentsProperties.recursion != null && documentsProperties.recursion) {
throw new NotImplementedException(); // TODO
} else {
if (isNotNullOrBlank(documentsProperties.glob)) {
throw new NotImplementedException(); // TODO
} else {
if (documentsProperties.path.toFile().isDirectory()) {
return FileSystemDocumentLoader.loadDocuments(documentsProperties.path);
} else {
Document document = FileSystemDocumentLoader.loadDocument(documentsProperties.path);
return singletonList(document);
}
}
}
}

// TODO ITs
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package dev.langchain4j.rag.easy.spring;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;

import static dev.langchain4j.rag.easy.spring.EasyRagProperties.PREFIX;

@Getter
@Setter
@ConfigurationProperties(prefix = PREFIX)
public class EasyRagProperties {

static final String PREFIX = "langchain4j.easy-rag";

@NestedConfigurationProperty
IngestionProperties ingestion;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package dev.langchain4j.rag.easy.spring;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.NestedConfigurationProperty;

@Getter
@Setter
class IngestionProperties {

@NestedConfigurationProperty
DocumentsProperties documents;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=dev.langchain4j.rag.easy.spring.EasyRagAutoConfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dev.langchain4j.rag.easy.spring.EasyRagAutoConfig
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import dev.langchain4j.model.openai.*;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
Expand Down Expand Up @@ -159,4 +160,10 @@ OpenAiImageModel openAiImageModel(Properties properties) {
.persistTo(imageModelProperties.getPersistTo())
.build();
}

@Bean
@ConditionalOnMissingBean
OpenAiTokenizer openAiTokenizer() {
return new OpenAiTokenizer();
}
}
102 changes: 102 additions & 0 deletions langchain4j-spring-boot-starter/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?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>dev.langchain4j</groupId>
<artifactId>langchain4j-spring</artifactId>
<version>0.29.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

<artifactId>langchain4j-spring-boot-starter</artifactId>
<name>Spring Boot starter for LangChain4j</name>

<dependencies>

<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.10.2</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
</dependency>

<!-- should be listed before spring-boot-configuration-processor -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>

<!-- needed to generate automatic metadata about available config properties -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai-spring-boot-starter</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-core</artifactId>
<version>${project.version}</version>
<classifier>tests</classifier>
<type>test-jar</type>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.tinylog</groupId>
<artifactId>tinylog-impl</artifactId>
<version>2.6.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.tinylog</groupId>
<artifactId>slf4j-tinylog</artifactId>
<version>2.6.2</version>
<scope>test</scope>
</dependency>

</dependencies>

<licenses>
<license>
<name>Apache-2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
<comments>A business-friendly OSS license</comments>
</license>
</licenses>

</project>
Loading
Loading