Skip to content

Commit 0629878

Browse files
committed
feat: Spring AI update
1 parent 22bc1d6 commit 0629878

File tree

14 files changed

+98
-1750
lines changed

14 files changed

+98
-1750
lines changed

backend/build.gradle

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,21 @@ dependencies {
3333
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
3434
implementation 'org.springframework.boot:spring-boot-starter-security'
3535
implementation 'org.springframework.boot:spring-boot-starter-web'
36-
implementation 'org.springframework.ai:spring-ai-tika-document-reader:0.8.1-SNAPSHOT'
36+
implementation 'org.springframework.ai:spring-ai-tika-document-reader:1.0.0-SNAPSHOT'
3737
implementation 'org.liquibase:liquibase-core'
3838
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-csv'
3939
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
4040
implementation 'net.javacrumbs.shedlock:shedlock-spring:5.2.0'
4141
implementation 'net.javacrumbs.shedlock:shedlock-provider-jdbc-template:5.2.0'
42-
implementation 'org.springframework.ai:spring-ai-pgvector-store-spring-boot-starter:0.8.1-SNAPSHOT'
43-
implementation 'org.springframework.ai:spring-ai-transformers-spring-boot-starter:0.8.1-SNAPSHOT'
42+
implementation 'org.springframework.ai:spring-ai-pgvector-store-spring-boot-starter:1.0.0-SNAPSHOT'
43+
implementation 'org.springframework.ai:spring-ai-transformers-spring-boot-starter:1.0.0-SNAPSHOT'
4444
testImplementation 'org.springframework.boot:spring-boot-starter-test'
4545
testImplementation 'org.springframework.security:spring-security-test'
4646
testImplementation 'com.tngtech.archunit:archunit-junit5:1.1.0'
4747
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
4848

4949
// if(project.hasProperty('useOllama')) {
50-
implementation 'org.springframework.ai:spring-ai-ollama-spring-boot-starter:0.8.1-SNAPSHOT'
50+
implementation 'org.springframework.ai:spring-ai-ollama-spring-boot-starter:1.0.0-SNAPSHOT'
5151
// } else {
5252
// implementation 'org.springframework.ai:spring-ai-openai-spring-boot-starter:0.8.1-SNAPSHOT'
5353
// }

backend/src/main/java/ch/xxx/aidoclibchat/adapter/repository/DocumentVSRepositoryBean.java

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,10 @@
1616
import java.sql.SQLException;
1717
import java.util.List;
1818
import java.util.Map;
19-
import java.util.stream.IntStream;
2019

2120
import org.postgresql.util.PGobject;
2221
import org.springframework.ai.document.Document;
23-
import org.springframework.ai.embedding.EmbeddingClient;
22+
import org.springframework.ai.embedding.EmbeddingModel;
2423
import org.springframework.ai.vectorstore.PgVectorStore;
2524
import org.springframework.ai.vectorstore.SearchRequest;
2625
import org.springframework.ai.vectorstore.VectorStore;
@@ -49,12 +48,13 @@ public class DocumentVSRepositoryBean implements DocumentVsRepository {
4948
private final ObjectMapper objectMapper;
5049
private final FilterExpressionConverter filterExpressionConverter;
5150

52-
public DocumentVSRepositoryBean(JdbcTemplate jdbcTemplate, EmbeddingClient embeddingClient, ObjectMapper objectMapper) {
51+
public DocumentVSRepositoryBean(JdbcTemplate jdbcTemplate, EmbeddingModel embeddingClient,
52+
ObjectMapper objectMapper) {
5353
this.jdbcTemplate = jdbcTemplate;
5454
this.objectMapper = objectMapper;
5555
this.vectorStore = new PgVectorStore(jdbcTemplate, embeddingClient);
5656
this.filterExpressionConverter = ((PgVectorStore) this.vectorStore).filterExpressionConverter;
57-
this.vectorTableName = PgVectorStore.VECTOR_TABLE_NAME;
57+
this.vectorTableName = PgVectorStore.DEFAULT_TABLE_NAME;
5858
}
5959

6060
@Override
@@ -86,21 +86,21 @@ public List<Document> retrieve(String query, DataType dataType) {
8686

8787
@Override
8888
public List<Document> findAllTableDocuments() {
89-
String nativeFilterExpression = this.filterExpressionConverter.convertExpression(new Filter.Expression(ExpressionType.NE,
90-
new Key(MetaData.DATATYPE), new Value(DataType.DOCUMENT.toString())));
89+
String nativeFilterExpression = this.filterExpressionConverter.convertExpression(new Filter.Expression(
90+
ExpressionType.NE, new Key(MetaData.DATATYPE), new Value(DataType.DOCUMENT.toString())));
9191

9292
String jsonPathFilter = " WHERE metadata::jsonb @@ '" + nativeFilterExpression + "'::jsonpath ";
9393

9494
return this.jdbcTemplate.query(
9595
String.format("SELECT * FROM %s %s LIMIT ? ", this.vectorTableName, jsonPathFilter),
96-
new DocumentRowMapper(this.objectMapper), 100000);
96+
new DocumentRowMapper(this.objectMapper), 100000);
9797
}
9898

9999
@Override
100100
public void deleteByIds(List<String> ids) {
101101
this.vectorStore.delete(ids);
102102
}
103-
103+
104104
private static class DocumentRowMapper implements RowMapper<Document> {
105105

106106
private static final String COLUMN_EMBEDDING = "embedding";
@@ -127,14 +127,13 @@ public Document mapRow(ResultSet rs, int rowNum) throws SQLException {
127127
Map<String, Object> metadata = toMap(pgMetadata);
128128

129129
Document document = new Document(id, content, metadata);
130-
document.setEmbedding(toDoubleList(embedding));
130+
document.setEmbedding(toDoubleArray(embedding));
131131

132132
return document;
133133
}
134134

135-
private List<Double> toDoubleList(PGobject embedding) throws SQLException {
136-
float[] floatArray = new PGvector(embedding.getValue()).toArray();
137-
return IntStream.range(0, floatArray.length).mapToDouble(i -> floatArray[i]).boxed().toList();
135+
private float[] toDoubleArray(PGobject embedding) throws SQLException {
136+
return new PGvector(embedding.getValue()).toArray();
138137
}
139138

140139
@SuppressWarnings("unchecked")
@@ -143,8 +142,7 @@ private Map<String, Object> toMap(PGobject pgObject) {
143142
String source = pgObject.getValue();
144143
try {
145144
return (Map<String, Object>) objectMapper.readValue(source, Map.class);
146-
}
147-
catch (JsonProcessingException e) {
145+
} catch (JsonProcessingException e) {
148146
throw new RuntimeException(e);
149147
}
150148
}

backend/src/main/java/ch/xxx/aidoclibchat/domain/model/dto/AiDocumentResult.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
import java.util.List;
1616

17-
import org.springframework.ai.chat.Generation;
17+
import org.springframework.ai.chat.model.Generation;
1818

1919
import ch.xxx.aidoclibchat.domain.model.entity.Document;
2020

backend/src/main/java/ch/xxx/aidoclibchat/usecase/service/CodeGenerationService.java

Lines changed: 40 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@
2121

2222
import org.slf4j.Logger;
2323
import org.slf4j.LoggerFactory;
24-
import org.springframework.ai.chat.ChatClient;
2524
import org.springframework.ai.chat.prompt.PromptTemplate;
2625
import org.springframework.beans.factory.annotation.Value;
2726
import org.springframework.stereotype.Service;
27+
import org.springframework.ai.chat.client.ChatClient.Builder;
2828

2929
import ch.xxx.aidoclibchat.domain.model.dto.GithubClient;
3030
import ch.xxx.aidoclibchat.domain.model.dto.GithubSource;
@@ -33,73 +33,72 @@
3333
public class CodeGenerationService {
3434
private static final Logger LOGGER = LoggerFactory.getLogger(CodeGenerationService.class);
3535
private final GithubClient githubClient;
36-
private final ChatClient chatClient;
36+
private final org.springframework.ai.chat.client.ChatClient chatClient;
3737
private final String ollamaPrompt = """
38-
You are an assistant to generate spring tests for the class under test.
38+
You are an assistant to generate spring tests for the class under test.
3939
Analyse the classes provided and generate tests for all methods. Base your tests on the example.
4040
Generate and implement the test methods. Generate and implement complete tests methods.
4141
Generate the complete source of the test class.
4242
43-
Your additional guidelines:
44-
1.Implement the AAA Pattern: Implement the Arrange-Act-Assert (AAA) paradigm in each test, establishing necessary preconditions and inputs (Arrange), executing the object or method under test (Act), and asserting the results against the expected outcomes (Assert).
45-
2.Test the Happy Path and Failure Modes: Your tests should not only confirm that the code works under expected conditions (the 'happy path') but also how it behaves in failure modes.
46-
3.Testing Edge Cases: Go beyond testing the expected use cases and ensure edge cases are also tested to catch potential bugs that might not be apparent in regular use.
47-
4.Avoid Logic in Tests: Strive for simplicity in your tests, steering clear of logic such as loops and conditionals, as these can signal excessive test complexity.
48-
5.Leverage TypeScript's Type System: Leverage static typing to catch potential bugs before they occur, potentially reducing the number of tests needed.
49-
6.Handle Asynchronous Code Effectively: If your test cases involve promises and asynchronous operations, ensure they are handled correctly.
50-
7.Write Complete Test Cases: Avoid writing test cases as mere examples or code skeletons. You have to write a complete set of tests. They should effectively validate the functionality under test.
51-
43+
Your additional guidelines:
44+
1.Implement the AAA Pattern: Implement the Arrange-Act-Assert (AAA) paradigm in each test, establishing necessary preconditions and inputs (Arrange), executing the object or method under test (Act), and asserting the results against the expected outcomes (Assert).
45+
2.Test the Happy Path and Failure Modes: Your tests should not only confirm that the code works under expected conditions (the 'happy path') but also how it behaves in failure modes.
46+
3.Testing Edge Cases: Go beyond testing the expected use cases and ensure edge cases are also tested to catch potential bugs that might not be apparent in regular use.
47+
4.Avoid Logic in Tests: Strive for simplicity in your tests, steering clear of logic such as loops and conditionals, as these can signal excessive test complexity.
48+
5.Leverage TypeScript's Type System: Leverage static typing to catch potential bugs before they occur, potentially reducing the number of tests needed.
49+
6.Handle Asynchronous Code Effectively: If your test cases involve promises and asynchronous operations, ensure they are handled correctly.
50+
7.Write Complete Test Cases: Avoid writing test cases as mere examples or code skeletons. You have to write a complete set of tests. They should effectively validate the functionality under test.
51+
5252
Generate tests for this class:
5353
{classToTest}
5454
5555
Use these classes as context for the tests:
5656
{contextClasses}
5757
5858
{testExample}
59-
""";
59+
""";
6060
private final String ollamaPrompt1 = """
6161
You are an assistant to generate a spring test class for the source class.
6262
1. Analyse the source class
6363
2. Analyse the context classes for the classes used by the source class
6464
3. Analyse the class in test example to base the code of the generated test class on it.
65-
4. Generate a test class for the source class and use the context classes as sources for creating the test class.
65+
4. Generate a test class for the source class and use the context classes as sources for creating the test class.
6666
5. Use the code of the test class as test example.
67-
6. Generate tests for each of the public methods of the source class.
68-
69-
Your additional guidelines:
70-
1.Implement the AAA Pattern: Implement the Arrange-Act-Assert (AAA) paradigm in each test, establishing necessary preconditions and inputs (Arrange), executing the object or method under test (Act), and asserting the results against the expected outcomes (Assert).
71-
2.Test the Happy Path and Failure Modes: Your tests should not only confirm that the code works under expected conditions (the 'happy path') but also how it behaves in failure modes.
72-
3.Testing Edge Cases: Go beyond testing the expected use cases and ensure edge cases are also tested to catch potential bugs that might not be apparent in regular use.
73-
4.Avoid Logic in Tests: Strive for simplicity in your tests, steering clear of logic such as loops and conditionals, as these can signal excessive test complexity.
74-
5.Leverage Java's Type System: Leverage static typing to catch potential bugs before they occur, potentially reducing the number of tests needed.
75-
6.Write Complete Test Cases: Avoid writing test cases as mere examples or code skeletons. You have to write a complete set of tests. They should effectively validate the functionality under test.
76-
77-
Generate the complete source code of the test class implementing the tests.
67+
6. Generate tests for each of the public methods of the source class.
68+
69+
Your additional guidelines:
70+
1.Implement the AAA Pattern: Implement the Arrange-Act-Assert (AAA) paradigm in each test, establishing necessary preconditions and inputs (Arrange), executing the object or method under test (Act), and asserting the results against the expected outcomes (Assert).
71+
2.Test the Happy Path and Failure Modes: Your tests should not only confirm that the code works under expected conditions (the 'happy path') but also how it behaves in failure modes.
72+
3.Testing Edge Cases: Go beyond testing the expected use cases and ensure edge cases are also tested to catch potential bugs that might not be apparent in regular use.
73+
4.Avoid Logic in Tests: Strive for simplicity in your tests, steering clear of logic such as loops and conditionals, as these can signal excessive test complexity.
74+
5.Leverage Java's Type System: Leverage static typing to catch potential bugs before they occur, potentially reducing the number of tests needed.
75+
6.Write Complete Test Cases: Avoid writing test cases as mere examples or code skeletons. You have to write a complete set of tests. They should effectively validate the functionality under test.
76+
77+
Generate the complete source code of the test class implementing the tests.
7878
7979
{testExample}
8080
8181
Use these context classes as extension for the source class:
8282
{contextClasses}
83-
83+
8484
Generate the complete source code of the test class implementing the tests.
8585
Generate tests for this source class:
86-
{classToTest}
86+
{classToTest}
8787
""";
8888
@Value("${spring.ai.ollama.chat.options.num-ctx:0}")
8989
private Long contextWindowSize;
9090

91-
public CodeGenerationService(GithubClient githubClient, ChatClient chatClient) {
91+
public CodeGenerationService(GithubClient githubClient, Builder builder) {
9292
this.githubClient = githubClient;
93-
this.chatClient = chatClient;
93+
this.chatClient = builder.build();
9494
}
9595

9696
public String generateTest(String url, Optional<String> testUrlOpt) {
9797
var start = Instant.now();
9898
var githubSource = this.createTestSources(url, true);
9999
var githubTestSource = testUrlOpt.map(testUrl -> this.createTestSources(testUrl, false))
100100
.orElse(new GithubSource(null, null, List.of(), List.of()));
101-
String contextClasses = githubSource.dependencies().stream()
102-
.filter(x -> this.contextWindowSize >= 16 * 1024)
101+
String contextClasses = githubSource.dependencies().stream().filter(x -> this.contextWindowSize >= 16 * 1024)
103102
.map(myGithubSource -> myGithubSource.sourceName() + ":" + System.getProperty("line.separator")
104103
+ myGithubSource.lines().stream()
105104
.collect(Collectors.joining(System.getProperty("line.separator"))))
@@ -113,11 +112,15 @@ public String generateTest(String url, Optional<String> testUrlOpt) {
113112
String classToTest = githubSource.lines().stream()
114113
.collect(Collectors.joining(System.getProperty("line.separator")));
115114
LOGGER.debug(new PromptTemplate(this.contextWindowSize >= 16 * 1024 ? this.ollamaPrompt1 : this.ollamaPrompt,
116-
Map.of("classToTest", classToTest, "contextClasses", contextClasses, "testExample", testExample)).createMessage().getContent());
115+
Map.of("classToTest", classToTest, "contextClasses", contextClasses, "testExample", testExample))
116+
.createMessage().getContent());
117117
LOGGER.info("Generation started with context window: {}", this.contextWindowSize);
118-
var response = chatClient.call(new PromptTemplate(this.contextWindowSize >= 16 * 1024 ? this.ollamaPrompt1 : this.ollamaPrompt,
119-
Map.of("classToTest", classToTest, "contextClasses", contextClasses, "testExample", testExample)).create());
120-
if((Instant.now().getEpochSecond() - start.getEpochSecond()) >= 300) {
118+
var response = chatClient.prompt()
119+
.user(u -> u.text(this.contextWindowSize >= 16 * 1024 ? this.ollamaPrompt1 : this.ollamaPrompt)
120+
.params(Map.of("classToTest", classToTest, "contextClasses", contextClasses, "testExample",
121+
testExample)))
122+
.call().chatResponse();
123+
if ((Instant.now().getEpochSecond() - start.getEpochSecond()) >= 300) {
121124
LOGGER.info(response.getResult().getOutput().getContent());
122125
}
123126
LOGGER.info("Prompt tokens: " + response.getMetadata().getUsage().getPromptTokens());
@@ -144,7 +147,8 @@ private List<GithubSource> createDependencies(final boolean referencedSources, f
144147
return sourceLines.stream().filter(x -> referencedSources).filter(myLine -> myLine.contains("import"))
145148
.filter(myLine -> myLine.contains(basePackage))
146149
.map(myLine -> String.format("%s%s%s", myUrl.split(basePackage.replace(".", "/"))[0].trim(),
147-
myLine.split("import")[1].split(";")[0].replaceAll("\\.", "/").trim(), myUrl.substring(myUrl.lastIndexOf('.'))))
150+
myLine.split("import")[1].split(";")[0].replaceAll("\\.", "/").trim(),
151+
myUrl.substring(myUrl.lastIndexOf('.'))))
148152
.map(myLine -> this.createTestSources(myLine, false)).toList();
149153
}
150154

0 commit comments

Comments
 (0)