Skip to content

Commit 79b527a

Browse files
odediaarey
andauthored
Generative AI support for Spring Petclinic Microservices (#281)
* updated git ignore * New microservice for generative ai chatbot based on Spring AI. Supports listing vets, listing owners, adding owners and adding pets to owners * Update README.md * Use webjar as webjar * Externalise JavaScript for handling chatbox interaction * Use @PostMapping for the /fallback endpoint * Configure the /fallback uri for the POST verb only * Move the spring-ai.png to the docs directory * Switch by default to the openai demo account * Use lambda expression * Remove Lombok from the genai-service * Remove creds.yaml * Fix SonarQube issues * Fix SonarQube issues (second attempt) * Add the genai-service * Use lambda expression * Fix Docker warnings * Setting the GenAI environment variables --------- Co-authored-by: Antoine Rey <[email protected]>
1 parent f2c1422 commit 79b527a

File tree

40 files changed

+1371
-22
lines changed

40 files changed

+1371
-22
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,5 @@ target/
1919

2020
# Branch switching
2121
generated/
22+
23+
**/.DS_Store

README.md

+39-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Distributed version of the Spring PetClinic Sample Application built with Spring Cloud
1+
# Distributed version of the Spring PetClinic Sample Application built with Spring Cloud and Spring AI
22

33
[![Build Status](https://github.com/spring-petclinic/spring-petclinic-microservices/actions/workflows/maven-build.yml/badge.svg)](https://github.com/spring-petclinic/spring-petclinic-microservices/actions/workflows/maven-build.yml)
44
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
@@ -17,7 +17,7 @@ If everything goes well, you can access the following services at given location
1717
* Discovery Server - http://localhost:8761
1818
* Config Server - http://localhost:8888
1919
* AngularJS frontend (API Gateway) - http://localhost:8080
20-
* Customers, Vets and Visits Services - random port, check Eureka Dashboard
20+
* Customers, Vets, Visits and GenAI Services - random port, check Eureka Dashboard
2121
* Tracing Server (Zipkin) - http://localhost:9411/zipkin/ (we use [openzipkin](https://github.com/openzipkin/zipkin/tree/main/zipkin-server))
2222
* Admin Server (Spring Boot Admin) - http://localhost:9090
2323
* Grafana Dashboards - http://localhost:3000
@@ -46,7 +46,7 @@ For instance, if you target container images for an Apple M2, you could use the
4646
```
4747

4848
Once images are ready, you can start them with a single command
49-
`docker-compose up` or `podman-compose up`.
49+
`docker compose up` or `podman-compose up`.
5050

5151
Containers startup order is coordinated with the `service_healthy` condition of the Docker Compose [depends-on](https://github.com/compose-spec/compose-spec/blob/main/spec.md#depends_on) expression
5252
and the [healthcheck](https://github.com/compose-spec/compose-spec/blob/main/spec.md#healthcheck) of the service containers.
@@ -79,6 +79,7 @@ This project consists of several microservices:
7979
- **Customers Service**: Manages customer data.
8080
- **Vets Service**: Handles information about veterinarians.
8181
- **Visits Service**: Manages pet visit records.
82+
- **GenAI Service**: Provides a chatbot interface to the application.
8283
- **API Gateway**: Routes client requests to the appropriate services.
8384
- **Config Server**: Centralized configuration management for all services.
8485
- **Discovery Server**: Eureka-based service registry.
@@ -93,6 +94,41 @@ Each service has its own specific role and communicates via REST APIs.
9394

9495
![Spring Petclinic Microservices architecture](docs/microservices-architecture-diagram.jpg)
9596

97+
## Integrating the Spring AI Chatbot
98+
99+
Spring Petclinic integrates a Chatbot that allows you to interact with the application in a natural language. Here are some examples of what you could ask:
100+
101+
1. Please list the owners that come to the clinic.
102+
2. Are there any vets that specialize in surgery?
103+
3. Is there an owner named Betty?
104+
4. Which owners have dogs?
105+
5. Add a dog for Betty. Its name is Moopsie.
106+
6. Create a new owner.
107+
108+
![Screenshot of the chat dialog](docs/spring-ai.png)
109+
110+
This `spring-petlinic-genai-service` microservice currently supports **OpenAI** (default) or **Azure's OpenAI** as the LLM provider.
111+
In order to start the microservice, perform the following steps:
112+
113+
1. Decide which provider you want to use. By default, the `spring-ai-openai-spring-boot-starter` dependency is enabled.
114+
You can change it to `spring-ai-azure-openai-spring-boot-starter`in the `pom.xml`.
115+
2. Create an OpenAI API key or a Azure OpenAI resource in your Azure Portal.
116+
Refer to the [OpenAI's quickstart](https://platform.openai.com/docs/quickstart) or [Azure's documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/) for further information on how to obtain these.
117+
You only need to populate the provider you're using - either openai, or azure-openai.
118+
If you don't have your own OpenAI API key, don't worry!
119+
You can temporarily use the `demo` key, which OpenAI provides free of charge for demonstration purposes.
120+
This `demo` key has a quota, is limited to the `gpt-4o-mini` model, and is intended solely for demonstration use.
121+
With your own OpenAI account, you can test the `gpt-4o` model by modifying the `deployment-name` property of the `application.yml` file.
122+
3. Export your API keys and endpoint as environment variables:
123+
* either OpenAI:
124+
```bash
125+
export OPENAI_API_KEY="your_api_key_here"
126+
```
127+
* or Azure OpenAI:
128+
```bash
129+
export AZURE_OPENAI_ENDPOINT="https://your_resource.openai.azure.com"
130+
export AZURE_OPENAI_KEY="your_api_key_here"
131+
```
96132

97133
## In case you find a bug/suggested improvement for Spring Petclinic Microservices
98134

docker-compose.yml

+22-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
version: '3'
2-
31
services:
42
config-server:
53
image: springcommunity/spring-petclinic-config-server
@@ -79,6 +77,27 @@ services:
7977
ports:
8078
- 8083:8083
8179

80+
81+
genai-service:
82+
image: springcommunity/spring-petclinic-genai-service
83+
container_name: genai-service
84+
environment:
85+
- OPENAI_API_KEY=${OPENAI_API_KEY}
86+
- AZURE_OPENAI_KEY=${AZURE_OPENAI_KEY}
87+
- AZURE_OPENAI_ENDPOINT=${AZURE_OPENAI_ENDPOINT}
88+
deploy:
89+
resources:
90+
limits:
91+
memory: 512M
92+
depends_on:
93+
config-server:
94+
condition: service_healthy
95+
discovery-server:
96+
condition: service_healthy
97+
ports:
98+
- 8084:8084
99+
100+
82101
api-gateway:
83102
image: springcommunity/spring-petclinic-api-gateway
84103
container_name: api-gateway
@@ -131,7 +150,7 @@ services:
131150
limits:
132151
memory: 256M
133152
ports:
134-
- 3000:3000
153+
- 3030:3030
135154

136155
prometheus-server:
137156
build: ./docker/prometheus

docker/Dockerfile

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM eclipse-temurin:17 as builder
1+
FROM eclipse-temurin:17 AS builder
22
WORKDIR application
33
ARG ARTIFACT_NAME
44
COPY ${ARTIFACT_NAME}.jar application.jar
@@ -11,7 +11,7 @@ WORKDIR application
1111
ARG EXPOSED_PORT
1212
EXPOSE ${EXPOSED_PORT}
1313

14-
ENV SPRING_PROFILES_ACTIVE docker
14+
ENV SPRING_PROFILES_ACTIVE=docker
1515

1616
COPY --from=builder application/dependencies/ ./
1717

46.3 KB
Loading

docs/spring-ai.png

107 KB
Loading

pom.xml

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
<parent>
77
<groupId>org.springframework.boot</groupId>
88
<artifactId>spring-boot-starter-parent</artifactId>
9-
<version>3.2.7</version>
9+
<version>3.3.4</version>
1010
</parent>
1111

1212
<groupId>org.springframework.samples</groupId>
1313
<artifactId>spring-petclinic-microservices</artifactId>
14-
<version>3.2.7</version>
14+
<version>3.3.4</version>
1515
<name>${project.artifactId}</name>
1616
<packaging>pom</packaging>
1717

@@ -20,6 +20,7 @@
2020
<module>spring-petclinic-customers-service</module>
2121
<module>spring-petclinic-vets-service</module>
2222
<module>spring-petclinic-visits-service</module>
23+
<module>spring-petclinic-genai-service</module>
2324
<module>spring-petclinic-config-server</module>
2425
<module>spring-petclinic-discovery-server</module>
2526
<module>spring-petclinic-api-gateway</module>

scripts/run_all.sh

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ set -o pipefail
77

88
pkill -9 -f spring-petclinic || echo "Failed to kill any apps"
99

10-
docker-compose kill || echo "No docker containers are running"
10+
docker compose kill || echo "No docker containers are running"
1111

1212
echo "Running infra"
13-
docker-compose up -d grafana-server prometheus-server tracing-server
13+
docker compose up -d grafana-server prometheus-server tracing-server
1414

1515
echo "Running apps"
1616
mkdir -p target
@@ -23,6 +23,7 @@ sleep 20
2323
nohup java -jar spring-petclinic-customers-service/target/*.jar --server.port=8081 --spring.profiles.active=chaos-monkey > target/customers-service.log 2>&1 &
2424
nohup java -jar spring-petclinic-visits-service/target/*.jar --server.port=8082 --spring.profiles.active=chaos-monkey > target/visits-service.log 2>&1 &
2525
nohup java -jar spring-petclinic-vets-service/target/*.jar --server.port=8083 --spring.profiles.active=chaos-monkey > target/vets-service.log 2>&1 &
26+
nohup java -jar spring-petclinic-genai-service/target/*.jar --server.port=8084 --spring.profiles.active=chaos-monkey > target/genai-service.log 2>&1 &
2627
nohup java -jar spring-petclinic-api-gateway/target/*.jar --server.port=8080 --spring.profiles.active=chaos-monkey > target/gateway-service.log 2>&1 &
2728
nohup java -jar spring-petclinic-admin-server/target/*.jar --server.port=9090 --spring.profiles.active=chaos-monkey > target/admin-server.log 2>&1 &
2829
echo "Waiting for apps to start"

spring-petclinic-admin-server/pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<parent>
1313
<groupId>org.springframework.samples</groupId>
1414
<artifactId>spring-petclinic-microservices</artifactId>
15-
<version>3.2.7</version>
15+
<version>3.3.4</version>
1616
</parent>
1717

1818
<properties>

spring-petclinic-api-gateway/pom.xml

+8-1
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@
1111
<parent>
1212
<groupId>org.springframework.samples</groupId>
1313
<artifactId>spring-petclinic-microservices</artifactId>
14-
<version>3.2.7</version>
14+
<version>3.3.4</version>
1515
</parent>
1616

1717
<properties>
1818
<webjars-bootstrap.version>5.3.3</webjars-bootstrap.version>
1919
<webjars-font-awesome.version>4.7.0</webjars-font-awesome.version>
2020
<webjars-angular.version>1.8.3</webjars-angular.version>
2121
<webjars-angular-ui-router.version>1.0.30</webjars-angular-ui-router.version>
22+
<webjars-marked.version>14.1.2</webjars-marked.version>
23+
2224
<libsass-maven-plugin.version>0.2.29</libsass-maven-plugin.version>
2325
<docker.image.exposed.port>8081</docker.image.exposed.port>
2426
<docker.image.dockerfile.dir>${basedir}/../docker</docker.image.dockerfile.dir>
@@ -124,6 +126,11 @@
124126
<groupId>org.webjars</groupId>
125127
<artifactId>webjars-locator-core</artifactId>
126128
</dependency>
129+
<dependency>
130+
<groupId>org.webjars.npm</groupId>
131+
<artifactId>marked</artifactId>
132+
<version>${webjars-marked.version}</version>
133+
</dependency>
127134

128135
<!-- Testing -->
129136
<dependency>

spring-petclinic-api-gateway/src/main/java/org/springframework/samples/petclinic/api/ApiGatewayApplication.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ RouterFunction<?> routerFunction() {
8484
public Customizer<ReactiveResilience4JCircuitBreakerFactory> defaultCustomizer() {
8585
return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
8686
.circuitBreakerConfig(CircuitBreakerConfig.ofDefaults())
87-
.timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration.ofSeconds(4)).build())
87+
.timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration.ofSeconds(10)).build())
8888
.build());
8989
}
9090
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.springframework.samples.petclinic.api.boundary.web;
2+
3+
import org.apache.http.HttpStatus;
4+
import org.springframework.http.ResponseEntity;
5+
import org.springframework.web.bind.annotation.PostMapping;
6+
import org.springframework.web.bind.annotation.RestController;
7+
8+
@RestController
9+
public class FallbackController {
10+
11+
@PostMapping("/fallback")
12+
public ResponseEntity<String> fallback() {
13+
return ResponseEntity.status(HttpStatus.SC_SERVICE_UNAVAILABLE)
14+
.body("Chat is currently unavailable. Please try again later.");
15+
}
16+
}

spring-petclinic-api-gateway/src/main/resources/application.yml

+17-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ spring:
55
import: optional:configserver:${CONFIG_SERVER_URL:http://localhost:8888/}
66
cloud:
77
gateway:
8+
default-filters:
9+
- name: CircuitBreaker
10+
args:
11+
name: defaultCircuitBreaker
12+
fallbackUri: forward:/fallback
13+
- name: Retry
14+
args:
15+
retries: 1
16+
statuses: SERVICE_UNAVAILABLE
17+
methods: POST
818
routes:
919
- id: vets-service
1020
uri: lb://vets-service
@@ -24,8 +34,13 @@ spring:
2434
- Path=/api/customer/**
2535
filters:
2636
- StripPrefix=2
27-
28-
37+
- id: genai-service
38+
uri: lb://genai-service
39+
predicates:
40+
- Path=/api/genai/**
41+
filters:
42+
- StripPrefix=2
43+
- CircuitBreaker=name=genaiCircuitBreaker,fallbackUri=/fallback
2944

3045
---
3146
spring:

spring-petclinic-api-gateway/src/main/resources/static/css/petclinic.css

+93
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)