Skip to content

Commit f5103e1

Browse files
authored
Merge pull request #12 from arey/feature/spring-ai-1.0.0-M6
Update Spring AI to 1.0.0-M6 and uses ToolCallback API
2 parents 94f405d + 78fee9d commit f5103e1

File tree

7 files changed

+107
-126
lines changed

7 files changed

+107
-126
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ ext.webjarsLocatorLiteVersion = "1.0.1"
3232
ext.webjarsFontawesomeVersion = "4.7.0"
3333
ext.webjarsBootstrapVersion = "5.3.3"
3434
ext.webjarsMarkedVersion = "14.1.2"
35-
ext.springAiVersion = "1.0.0-M5"
35+
ext.springAiVersion = "1.0.0-M6"
3636

3737
dependencies {
3838
// Workaround for AOT issue (https://github.com/spring-projects/spring-framework/pull/33949) -->

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
<maven-checkstyle.version>3.6.0</maven-checkstyle.version>
3939
<nohttp-checkstyle.version>0.0.11</nohttp-checkstyle.version>
4040
<spring-format.version>0.0.43</spring-format.version>
41-
<spring-ai.version>1.0.0-M5</spring-ai.version>
41+
<spring-ai.version>1.0.0-M6</spring-ai.version>
4242
</properties>
4343

4444
<dependencies>

src/main/java/org/springframework/samples/petclinic/genai/AIBeanConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public class AIBeanConfiguration {
3232

3333
@Bean
3434
VectorStore vectorStore(EmbeddingModel embeddingModel) {
35-
return new SimpleVectorStore(embeddingModel);
35+
return SimpleVectorStore.builder(embeddingModel).build();
3636
}
3737

3838
}

src/main/java/org/springframework/samples/petclinic/genai/AIDataProvider.java

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,18 @@
2626
import org.springframework.data.domain.Pageable;
2727
import org.springframework.samples.petclinic.owner.Owner;
2828
import org.springframework.samples.petclinic.owner.OwnerRepository;
29+
import org.springframework.samples.petclinic.owner.Pet;
30+
import org.springframework.samples.petclinic.vet.Vet;
2931
import org.springframework.stereotype.Service;
3032

3133
import java.util.List;
32-
import java.util.Optional;
3334

3435
/**
3536
* Functions that are invoked by the LLM will use this bean to query the system of record
3637
* for information such as listing owners and vets, or adding pets to an owner.
3738
*
3839
* @author Oded Shopen
40+
* @author Antoine Rey
3941
*/
4042
@Service
4143
public class AIDataProvider {
@@ -49,36 +51,34 @@ public AIDataProvider(OwnerRepository ownerRepository, VectorStore vectorStore)
4951
this.vectorStore = vectorStore;
5052
}
5153

52-
public OwnersResponse getAllOwners() {
54+
public List<Owner> getAllOwners() {
5355
Pageable pageable = PageRequest.of(0, 100);
5456
Page<Owner> ownerPage = ownerRepository.findAll(pageable);
55-
return new OwnersResponse(ownerPage.getContent());
57+
return ownerPage.getContent();
5658
}
5759

58-
public VetResponse getVets(VetRequest request) throws JsonProcessingException {
60+
public List<String> getVets(Vet vet) throws JsonProcessingException {
5961
ObjectMapper objectMapper = new ObjectMapper();
60-
String vetAsJson = objectMapper.writeValueAsString(request.vet());
62+
String vetAsJson = objectMapper.writeValueAsString(vet);
6163

6264
// Provide a limit of 50 results when zero parameters are sent
63-
int topK = (request.vet() == null) ? 50 : 20;
65+
int topK = (vet == null) ? 50 : 20;
6466
SearchRequest sr = SearchRequest.builder().query(vetAsJson).topK(topK).build();
6567

6668
List<Document> topMatches = this.vectorStore.similaritySearch(sr);
67-
List<String> results = topMatches.stream().map(Document::getContent).toList();
68-
return new VetResponse(results);
69+
return topMatches.stream().map(Document::getText).toList();
6970
}
7071

71-
public AddedPetResponse addPetToOwner(AddPetRequest request) {
72-
var ownerWithPet = ownerRepository.findById(request.ownerId()).map(existingOwner -> {
73-
existingOwner.addPet(request.pet());
72+
public Owner addPetToOwner(int ownerId, Pet pet) {
73+
pet.setId(null); // Non persistent Pet
74+
return ownerRepository.findById(ownerId).map(existingOwner -> {
75+
existingOwner.addPet(pet);
7476
return ownerRepository.save(existingOwner);
7577
}).orElse(null);
76-
return new AddedPetResponse(ownerWithPet);
7778
}
7879

79-
public OwnerResponse addOwnerToPetclinic(OwnerRequest ownerRequest) {
80-
ownerRepository.save(ownerRequest.owner());
81-
return new OwnerResponse(ownerRequest.owner());
80+
public Owner addOwnerToPetclinic(Owner owner) {
81+
return ownerRepository.save(owner);
8282
}
8383

8484
}

src/main/java/org/springframework/samples/petclinic/genai/AIFunctionConfiguration.java

Lines changed: 0 additions & 106 deletions
This file was deleted.

src/main/java/org/springframework/samples/petclinic/genai/ChatConfiguration.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class ChatConfiguration {
3535
private Resource systemResource;
3636

3737
@Bean
38-
ChatClient chatClient(ChatClient.Builder chatClientBuilder, ChatMemory chatMemory) {
38+
ChatClient chatClient(ChatClient.Builder chatClientBuilder, ChatMemory chatMemory, PetclinicTools petclinicTools) {
3939
// @formatter:off
4040
return chatClientBuilder
4141
.defaultAdvisors(
@@ -44,7 +44,7 @@ ChatClient chatClient(ChatClient.Builder chatClientBuilder, ChatMemory chatMemor
4444
new SimpleLoggerAdvisor()
4545
)
4646
.defaultSystem(systemResource)
47-
.defaultFunctions("listOwners", "listVets", "addPetToOwner", "addOwnerToPetclinic")
47+
.defaultTools(petclinicTools)
4848
.build();
4949
// @formatter:on
5050
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright 2012-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.samples.petclinic.genai;
18+
19+
import com.fasterxml.jackson.core.JsonProcessingException;
20+
import org.slf4j.Logger;
21+
import org.slf4j.LoggerFactory;
22+
import org.springframework.ai.tool.annotation.Tool;
23+
import org.springframework.samples.petclinic.owner.Owner;
24+
import org.springframework.samples.petclinic.owner.Pet;
25+
import org.springframework.samples.petclinic.vet.Vet;
26+
import org.springframework.stereotype.Component;
27+
28+
import java.util.List;
29+
30+
/**
31+
* This class defines the tools (also known as function) that the LLM provider will invoke
32+
* when it requires more Information on a given topic. The currently available tools
33+
* enable the LLM to get the list of owners and their pets, get information about the
34+
* veterinarians, and add a pet to an owner.
35+
*
36+
* @author Oded Shopen
37+
* @author Antoine Rey
38+
*/
39+
@Component
40+
class PetclinicTools {
41+
42+
private final static Logger LOGGER = LoggerFactory.getLogger(PetclinicTools.class);
43+
44+
private final AIDataProvider petclinicAiProvider;
45+
46+
PetclinicTools(AIDataProvider petclinicAiProvider) {
47+
this.petclinicAiProvider = petclinicAiProvider;
48+
}
49+
50+
@Tool(description = "List the owners that the pet clinic has")
51+
public List<Owner> listOwners() {
52+
return petclinicAiProvider.getAllOwners();
53+
}
54+
55+
@Tool(description = "List the veterinarians that the pet clinic has")
56+
public List<String> listVets(Vet vet) {
57+
try {
58+
return petclinicAiProvider.getVets(vet);
59+
}
60+
catch (JsonProcessingException e) {
61+
LOGGER.error("Listing Veterinarians failed", e);
62+
return null;
63+
}
64+
}
65+
66+
@Tool(description = ("""
67+
Add a pet with the specified petTypeId, to an owner identified by the ownerId. \
68+
The allowed Pet types IDs are only: \
69+
1 - cat \
70+
2 - dog \
71+
3 - lizard \
72+
4 - snake \
73+
5 - bird \
74+
6 - hamster"""))
75+
public Owner addPetToOwner(Pet pet, Integer ownerId) {
76+
return petclinicAiProvider.addPetToOwner(ownerId, pet);
77+
}
78+
79+
@Tool(description = ("""
80+
Add a new pet owner to the pet clinic. \
81+
The Owner must include a first name and a last name as two separate words, \
82+
plus an address and a 10-digit phone number"""))
83+
public Owner addOwnerToPetclinic(Owner owner) {
84+
return petclinicAiProvider.addOwnerToPetclinic(owner);
85+
}
86+
87+
}

0 commit comments

Comments
 (0)