diff --git a/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/ChatAgentApplication.java b/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/ChatAgentApplication.java index a4b01a2..0a022b4 100644 --- a/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/ChatAgentApplication.java +++ b/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/ChatAgentApplication.java @@ -2,7 +2,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ImportRuntimeHints; +import org.springframework.web.client.RestTemplate; @SpringBootApplication @ImportRuntimeHints(PetClinicRuntimeHints.class) @@ -12,4 +14,8 @@ public static void main(String[] args) { SpringApplication.run(ChatAgentApplication.class, args); } + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } } diff --git a/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/chat/AgentConfig.java b/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/chat/AgentConfig.java index 69cf47f..1f06564 100644 --- a/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/chat/AgentConfig.java +++ b/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/chat/AgentConfig.java @@ -5,6 +5,7 @@ import com.azure.identity.DefaultAzureCredentialBuilder; import org.springframework.ai.azure.openai.AzureOpenAiChatModel; import org.springframework.ai.azure.openai.AzureOpenAiChatOptions; +import org.springframework.ai.azure.openai.AzureOpenAiResponseFormat; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.ChatClientCustomizer; import org.springframework.ai.chat.client.advisor.PromptChatMemoryAdvisor; @@ -49,6 +50,7 @@ public AzureOpenAiChatModel chatModel(OpenAIClient openAIClient, ChatOptionsProp var openAIChatOptions = AzureOpenAiChatOptions.builder() .withDeploymentName(properties.getDeploymentName()) .withTemperature(properties.getTemperature()) + .withResponseFormat(AzureOpenAiResponseFormat.TEXT) .build(); // provide Context to load function callbacks diff --git a/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/chat/ChatOptionsProperties.java b/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/chat/ChatOptionsProperties.java index b55c9e2..a7baac8 100644 --- a/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/chat/ChatOptionsProperties.java +++ b/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/chat/ChatOptionsProperties.java @@ -7,7 +7,7 @@ @Getter @Setter @ConfigurationProperties(prefix = ChatOptionsProperties.PREFIX) -class ChatOptionsProperties { +public class ChatOptionsProperties { static final String PREFIX = "spring.ai.azure.openai.chat.options"; diff --git a/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/chat/OwnerTools.java b/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/chat/OwnerTools.java index 91d54c7..4e21380 100644 --- a/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/chat/OwnerTools.java +++ b/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/chat/OwnerTools.java @@ -7,10 +7,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Description; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.samples.petclinic.agent.owner.Owner; -import org.springframework.samples.petclinic.agent.owner.OwnerRepository; +import org.springframework.samples.petclinic.agent.dto.OwnerDto; +import org.springframework.samples.petclinic.agent.service.OwnerService; import java.util.List; import java.util.function.Function; @@ -18,43 +16,42 @@ @Configuration public class OwnerTools { - private final OwnerRepository owners; + private final OwnerService ownerService; - public OwnerTools(OwnerRepository ownerRepository) { - this.owners = ownerRepository; + public OwnerTools(OwnerService ownerService) { + this.ownerService = ownerService; } @Bean - @Description("Query the owners by last name, the owner information include owner id, address, telephone, city, first name and last name" + @Description("Query the owners by first name, the owner information include owner id, address, telephone, city, first name and last name" + "\n The owner also include the pets information, include the pet name, pet type and birth" - + "\n The pet include serveral visit record, include the visit name and visit date") - public Function> queryOwners() { + + "\n The pet include several visit records, include the visit name and visit date") + public Function> queryOwners() { return name -> { - Pageable pageable = PageRequest.of(0, 10); - return owners.findByLastName(name.lastName, pageable).toList(); + return ownerService.findByFirstName(name.firstName); }; } @Bean @Description("Create a new owner by providing the owner's firstName, lastName, address, telephone and city") - public Function addOwner() { + public Function addOwner() { return request -> { - Owner owner = new Owner(); + OwnerDto owner = new OwnerDto(); owner.setAddress(request.address); owner.setTelephone(request.telephone); owner.setCity(request.city); owner.setLastName(request.lastName); owner.setFirstName(request.firstName); - this.owners.save(owner); + ownerService.save(owner); return owner; }; } @Bean @Description("update a owner's firstName, lastName, address, telephone and city by providing the owner id\"") - public Function updateOwner() { + public Function updateOwner() { return request -> { - Owner owner = owners.findById(Integer.parseInt(request.ownerId)); + OwnerDto owner = ownerService.findById(Integer.parseInt(request.ownerId)); if (request.address != null) { owner.setAddress(request.address); } @@ -70,7 +67,7 @@ public Function updateOwner() { if (request.firstName != null) { owner.setFirstName(request.firstName); } - this.owners.save(owner); + ownerService.save(owner); return owner; }; } @@ -78,7 +75,7 @@ public Function updateOwner() { @JsonInclude(JsonInclude.Include.NON_NULL) @JsonClassDescription("Owner Query Request") public record OwnerQueryRequest(@JsonProperty(required = false, - value = "lastName") @JsonPropertyDescription("The Owner last name") String lastName) { + value = "firstName") @JsonPropertyDescription("The Owner first name") String firstName) { } @JsonInclude(JsonInclude.Include.NON_NULL) diff --git a/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/chat/VetTools.java b/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/chat/VetTools.java index 99ec15d..10e1957 100644 --- a/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/chat/VetTools.java +++ b/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/chat/VetTools.java @@ -7,8 +7,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Description; -import org.springframework.samples.petclinic.agent.vet.Vet; -import org.springframework.samples.petclinic.agent.vet.VetRepository; +import org.springframework.samples.petclinic.agent.dto.VetDto; +import org.springframework.samples.petclinic.agent.service.VetService; import java.util.Collection; import java.util.function.Function; @@ -16,17 +16,17 @@ @Configuration public class VetTools { - private final VetRepository vetRepository; + private final VetService vetService; - public VetTools(VetRepository vetRepository) { - this.vetRepository = vetRepository; + public VetTools(VetService vetService) { + this.vetService = vetService; } @Bean @Description("return list of Vets, include their specialties") - public Function> queryVets() { + public Function> queryVets() { return request -> { - return vetRepository.findAll(); + return vetService.findAll(); }; } diff --git a/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/dto/OwnerDto.java b/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/dto/OwnerDto.java new file mode 100644 index 0000000..d9ced25 --- /dev/null +++ b/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/dto/OwnerDto.java @@ -0,0 +1,23 @@ +package org.springframework.samples.petclinic.agent.dto; + +import lombok.Data; + +import java.util.Set; + +@Data +public class OwnerDto { + + private Integer id; + + private String firstName; + + private String lastName; + + private String address; + + private String city; + + private String telephone; + + private Set pets; +} diff --git a/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/dto/PetDto.java b/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/dto/PetDto.java new file mode 100644 index 0000000..b651067 --- /dev/null +++ b/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/dto/PetDto.java @@ -0,0 +1,19 @@ +package org.springframework.samples.petclinic.agent.dto; + +import lombok.Data; + +import java.util.Date; + +@Data +public class PetDto { + + private Integer id; + + private String name; + + private Date birthDate; + + private PetTypeDto type; + + private OwnerDto owner; +} diff --git a/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/dto/PetTypeDto.java b/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/dto/PetTypeDto.java new file mode 100644 index 0000000..f70451a --- /dev/null +++ b/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/dto/PetTypeDto.java @@ -0,0 +1,12 @@ +package org.springframework.samples.petclinic.agent.dto; + +import lombok.Data; + +@Data +public class PetTypeDto { + + private Integer id; + + private String name; + +} diff --git a/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/dto/SpecialtyDto.java b/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/dto/SpecialtyDto.java new file mode 100644 index 0000000..4c4df6e --- /dev/null +++ b/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/dto/SpecialtyDto.java @@ -0,0 +1,12 @@ +package org.springframework.samples.petclinic.agent.dto; + +import lombok.Data; + +@Data +public class SpecialtyDto { + + private Integer id; + + private String name; + +} diff --git a/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/dto/VetDto.java b/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/dto/VetDto.java new file mode 100644 index 0000000..ccdcaec --- /dev/null +++ b/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/dto/VetDto.java @@ -0,0 +1,18 @@ +package org.springframework.samples.petclinic.agent.dto; + +import lombok.Data; + +import java.util.Set; + +@Data +public class VetDto { + + private Integer id; + + private String firstName; + + private String lastName; + + private Set specialties; + +} diff --git a/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/service/OwnerService.java b/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/service/OwnerService.java new file mode 100644 index 0000000..4525338 --- /dev/null +++ b/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/service/OwnerService.java @@ -0,0 +1,37 @@ +package org.springframework.samples.petclinic.agent.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.samples.petclinic.agent.dto.OwnerDto; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.util.ArrayList; +import java.util.List; + +@Service +public class OwnerService { + + private final RestTemplate restTemplate; + + @Autowired + public OwnerService(RestTemplate restTemplate) { + this.restTemplate = restTemplate; + } + + public List findByFirstName(String firstName) { + var owners = restTemplate.getForObject("http://customers-service/owners/firstname/{firstName}", OwnerDto[].class, firstName); + if (owners != null) { + return List.of(owners); + } else { + return new ArrayList<>(); + } + } + + public OwnerDto findById(int ownerId) { + return restTemplate.getForObject("http://customers-service/owners/{ownerId}", OwnerDto.class, ownerId); + } + + public void save(OwnerDto owner) { + restTemplate.postForEntity("http://customers-service/owners", owner, OwnerDto.class); + } +} diff --git a/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/service/VetService.java b/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/service/VetService.java new file mode 100644 index 0000000..5569bd6 --- /dev/null +++ b/src/spring-petclinic-chat-agent/src/main/java/org/springframework/samples/petclinic/agent/service/VetService.java @@ -0,0 +1,30 @@ +package org.springframework.samples.petclinic.agent.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.samples.petclinic.agent.dto.VetDto; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.util.ArrayList; +import java.util.List; + +@Service +public class VetService { + + private final RestTemplate restTemplate; + + @Autowired + public VetService(RestTemplate restTemplate) { + this.restTemplate = restTemplate; + } + + public List findAll() { + var vets = restTemplate.getForObject("http://vets-service/vets", VetDto[].class); + if (vets != null) { + return List.of(vets); + } else { + return new ArrayList<>(); + } + } + +} diff --git a/src/spring-petclinic-chat-agent/src/main/resources/application.yml b/src/spring-petclinic-chat-agent/src/main/resources/application.yml index cddd41f..465cf7f 100644 --- a/src/spring-petclinic-chat-agent/src/main/resources/application.yml +++ b/src/spring-petclinic-chat-agent/src/main/resources/application.yml @@ -37,11 +37,11 @@ spring: ai: azure: openai: - endpoint: ${AZURE_OPENAI_ENDPOINT} - client-id: ${CLIENT_ID} + endpoint: + client-id: chat: options: - deployment-name: gpt-4 + deployment-name: gpt-4o temperature: 0.8 embedding: options: diff --git a/src/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/model/OwnerRepository.java b/src/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/model/OwnerRepository.java index aa720f2..be64170 100644 --- a/src/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/model/OwnerRepository.java +++ b/src/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/model/OwnerRepository.java @@ -16,6 +16,10 @@ package org.springframework.samples.petclinic.customers.model; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; /** * Repository class for Owner domain objects All method names are compliant with Spring Data naming @@ -27,4 +31,8 @@ * @author Michael Isvy * @author Maciej Szarlinski */ -public interface OwnerRepository extends JpaRepository { } +public interface OwnerRepository extends JpaRepository { + + @Query("SELECT o FROM Owner o WHERE LOWER(o.firstName) = LOWER(:firstName)") + List findByFirstName(@Param("firstName") String firstName); +} diff --git a/src/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/OwnerResource.java b/src/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/OwnerResource.java index 72c375a..a13fcc5 100644 --- a/src/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/OwnerResource.java +++ b/src/spring-petclinic-customers-service/src/main/java/org/springframework/samples/petclinic/customers/web/OwnerResource.java @@ -61,6 +61,14 @@ public Optional findOwner(@PathVariable("ownerId") @Min(1) int ownerId) { return ownerRepository.findById(ownerId); } + /** + * Find owners by first name + */ + @GetMapping(value = "/firstname/{firstName}") + public List findOwnersByFirstName(@PathVariable("firstName") String firstName) { + return ownerRepository.findByFirstName(firstName); + } + /** * Read List of Owners */