From 014033e7e1ac7609f711a652f22074e7605f9364 Mon Sep 17 00:00:00 2001 From: mmeckes Date: Tue, 3 Jun 2025 17:09:09 +0100 Subject: [PATCH 1/3] Created ui project and added thymeleaf frontend. Moved APIs under /api path. Added web controller for 2 chat routes. One standard HTTP and one streaming. --- .../README.md | 70 +++++++++++++++++++ .../pom.xml | 61 ++++++++++++++++ .../java/com/unicorn/AgentsApplication.java | 13 ++++ .../com/unicorn/agents/ChatController.java | 28 ++++++++ .../com/unicorn/agents/DateTimeTools.java | 14 ++++ .../com/unicorn/agents/PromptRequest.java | 3 + .../src/main/resources/application.properties | 6 ++ .../agents/AgentsApplicationTests.java | 13 ++++ 8 files changed, 208 insertions(+) create mode 100644 samples/spring-ai-simple-chat-client-with-ui/README.md create mode 100644 samples/spring-ai-simple-chat-client-with-ui/pom.xml create mode 100644 samples/spring-ai-simple-chat-client-with-ui/src/main/java/com/unicorn/AgentsApplication.java create mode 100644 samples/spring-ai-simple-chat-client-with-ui/src/main/java/com/unicorn/agents/ChatController.java create mode 100644 samples/spring-ai-simple-chat-client-with-ui/src/main/java/com/unicorn/agents/DateTimeTools.java create mode 100644 samples/spring-ai-simple-chat-client-with-ui/src/main/java/com/unicorn/agents/PromptRequest.java create mode 100644 samples/spring-ai-simple-chat-client-with-ui/src/main/resources/application.properties create mode 100644 samples/spring-ai-simple-chat-client-with-ui/src/test/java/com/unicorn/agents/AgentsApplicationTests.java diff --git a/samples/spring-ai-simple-chat-client-with-ui/README.md b/samples/spring-ai-simple-chat-client-with-ui/README.md new file mode 100644 index 00000000..176ba17f --- /dev/null +++ b/samples/spring-ai-simple-chat-client-with-ui/README.md @@ -0,0 +1,70 @@ +# Spring AI Simple Chat Client + +A lightweight Spring Boot application demonstrating the integration of Spring AI with Amazon Bedrock to create a simple chat API. + +## Features + +- REST API endpoints for AI chat interactions +- Support for both synchronous and streaming responses +- Custom tool integration +- Integration with Amazon Bedrock's Claude 3 Sonnet model + +## Prerequisites + +- Java 21 +- Maven +- AWS account with access to Amazon Bedrock +- AWS credentials configured locally + +## Configuration + +The application is configured to use Amazon Bedrock's Claude 3 Sonnet model in the EU West 1 region. You can modify these settings in `application.properties`: + +```properties +spring.application.name=agents +spring.ai.bedrock.aws.region=eu-west-1 +spring.ai.bedrock.converse.chat.options.model=eu.anthropic.claude-3-7-sonnet-20250219-v1:0 +``` + +## Building and Running + +```bash +mvn spring-boot:run +``` + +The application will start on port 8080 by default. + +## API Endpoints + +### 1. Synchronous Chat Endpoint + +Send a prompt and receive a complete response: + +```bash +curl -XPOST 'http://localhost:8080/ai' \ + -H "Content-Type: application/json" \ + -d '{"prompt":"Tell me about Spring AI in 2 sentences."}' +``` + +### 2. Streaming Chat Endpoint + +Send a prompt and receive the response as a stream (useful for real-time UI updates): + +```bash +curl -XPOST -N 'http://localhost:8080/ai/stream' \ + -H "Content-Type: application/json" \ + -d '{"prompt":"Who is George Mallory?"}' +``` + +### 3. With tool use + +curl -XPOST -N 'http://localhost:8080/ai/stream' \ + -H "Content-Type: application/json" \ + -d '{"prompt":"What is the current date and time?"}' + +## Project Structure + +- `ChatController.java`: Defines the REST endpoints for chat interactions +- `PromptRequest.java`: Simple record class for the request payload +- `DateTimeTools.java`: Custom tool that provides date/time information to the AI +- `AgentsApplication.java`: Spring Boot application entry point diff --git a/samples/spring-ai-simple-chat-client-with-ui/pom.xml b/samples/spring-ai-simple-chat-client-with-ui/pom.xml new file mode 100644 index 00000000..79d40658 --- /dev/null +++ b/samples/spring-ai-simple-chat-client-with-ui/pom.xml @@ -0,0 +1,61 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.4.5 + + + com.unicorn + agents + 0.0.1-SNAPSHOT + agents + Simple Bedrock Chat Client for Spring AI + + 21 + 1.0.0 + + + + org.springframework.ai + spring-ai-starter-model-bedrock-converse + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + + + org.springframework.ai + spring-ai-bom + ${spring-ai.version} + pom + import + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/samples/spring-ai-simple-chat-client-with-ui/src/main/java/com/unicorn/AgentsApplication.java b/samples/spring-ai-simple-chat-client-with-ui/src/main/java/com/unicorn/AgentsApplication.java new file mode 100644 index 00000000..a8288708 --- /dev/null +++ b/samples/spring-ai-simple-chat-client-with-ui/src/main/java/com/unicorn/AgentsApplication.java @@ -0,0 +1,13 @@ +package com.unicorn; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class AgentsApplication { + + public static void main(String[] args) { + SpringApplication.run(AgentsApplication.class, args); + } + +} diff --git a/samples/spring-ai-simple-chat-client-with-ui/src/main/java/com/unicorn/agents/ChatController.java b/samples/spring-ai-simple-chat-client-with-ui/src/main/java/com/unicorn/agents/ChatController.java new file mode 100644 index 00000000..8a9dab60 --- /dev/null +++ b/samples/spring-ai-simple-chat-client-with-ui/src/main/java/com/unicorn/agents/ChatController.java @@ -0,0 +1,28 @@ +package com.unicorn.agents; + +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.web.bind.annotation.*; +import reactor.core.publisher.Flux; + +@RestController +@RequestMapping("/api") +public class ChatController { + + private final ChatClient chatClient; + + public ChatController (ChatClient.Builder chatClient){ + this.chatClient = chatClient + .defaultTools(new DateTimeTools()) + .build(); + } + + @PostMapping("/ai") + public String myAgent(@RequestBody PromptRequest promptRequest){ + return chatClient.prompt().user(promptRequest.prompt()).call().chatResponse().getResult().getOutput().getText(); + } + + @PostMapping("/ai/stream") + public Flux myStreamingAgent(@RequestBody PromptRequest promptRequest){ + return chatClient.prompt().user(promptRequest.prompt()).stream().content(); + } +} diff --git a/samples/spring-ai-simple-chat-client-with-ui/src/main/java/com/unicorn/agents/DateTimeTools.java b/samples/spring-ai-simple-chat-client-with-ui/src/main/java/com/unicorn/agents/DateTimeTools.java new file mode 100644 index 00000000..7d3c3789 --- /dev/null +++ b/samples/spring-ai-simple-chat-client-with-ui/src/main/java/com/unicorn/agents/DateTimeTools.java @@ -0,0 +1,14 @@ +package com.unicorn.agents; + +import java.time.LocalDateTime; +import org.springframework.ai.tool.annotation.Tool; +import org.springframework.context.i18n.LocaleContextHolder; + +class DateTimeTools { + + @Tool(description = "Get the current date and time in the user's timezone") + String getCurrentDateTime(String param) { + return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString(); + } + +} \ No newline at end of file diff --git a/samples/spring-ai-simple-chat-client-with-ui/src/main/java/com/unicorn/agents/PromptRequest.java b/samples/spring-ai-simple-chat-client-with-ui/src/main/java/com/unicorn/agents/PromptRequest.java new file mode 100644 index 00000000..3132b077 --- /dev/null +++ b/samples/spring-ai-simple-chat-client-with-ui/src/main/java/com/unicorn/agents/PromptRequest.java @@ -0,0 +1,3 @@ +package com.unicorn.agents; + +public record PromptRequest(String prompt){}; \ No newline at end of file diff --git a/samples/spring-ai-simple-chat-client-with-ui/src/main/resources/application.properties b/samples/spring-ai-simple-chat-client-with-ui/src/main/resources/application.properties new file mode 100644 index 00000000..7de8da22 --- /dev/null +++ b/samples/spring-ai-simple-chat-client-with-ui/src/main/resources/application.properties @@ -0,0 +1,6 @@ +spring.application.name=unicorn-store +spring.ai.bedrock.aws.region=eu-west-1 +spring.ai.bedrock.converse.chat.options.model=eu.anthropic.claude-3-7-sonnet-20250219-v1:0 +spring.thymeleaf.cache=false +spring.thymeleaf.prefix=classpath:/templates/ +spring.thymeleaf.suffix=.html diff --git a/samples/spring-ai-simple-chat-client-with-ui/src/test/java/com/unicorn/agents/AgentsApplicationTests.java b/samples/spring-ai-simple-chat-client-with-ui/src/test/java/com/unicorn/agents/AgentsApplicationTests.java new file mode 100644 index 00000000..a2a7766a --- /dev/null +++ b/samples/spring-ai-simple-chat-client-with-ui/src/test/java/com/unicorn/agents/AgentsApplicationTests.java @@ -0,0 +1,13 @@ +package com.unicorn.agents; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class AgentsApplicationTests { + + @Test + void contextLoads() { + } + +} From 36705253be1db532919386145e7cc56522bd7dc5 Mon Sep 17 00:00:00 2001 From: mmeckes Date: Tue, 3 Jun 2025 17:12:52 +0100 Subject: [PATCH 2/3] Created ui project and added thymeleaf frontend. Moved APIs under /api path. Added web controller for 2 chat routes. One standard HTTP and one streaming. --- .../com/unicorn/agents/WebController.java | 23 ++ .../src/main/resources/static/css/main.css | 10 + .../resources/static/images/unicorn-logo.svg | 9 + .../src/main/resources/templates/chat.html | 174 ++++++++++++++++ .../main/resources/templates/stream-chat.html | 196 ++++++++++++++++++ 5 files changed, 412 insertions(+) create mode 100644 samples/spring-ai-simple-chat-client-with-ui/src/main/java/com/unicorn/agents/WebController.java create mode 100644 samples/spring-ai-simple-chat-client-with-ui/src/main/resources/static/css/main.css create mode 100644 samples/spring-ai-simple-chat-client-with-ui/src/main/resources/static/images/unicorn-logo.svg create mode 100644 samples/spring-ai-simple-chat-client-with-ui/src/main/resources/templates/chat.html create mode 100644 samples/spring-ai-simple-chat-client-with-ui/src/main/resources/templates/stream-chat.html diff --git a/samples/spring-ai-simple-chat-client-with-ui/src/main/java/com/unicorn/agents/WebController.java b/samples/spring-ai-simple-chat-client-with-ui/src/main/java/com/unicorn/agents/WebController.java new file mode 100644 index 00000000..492c4b0a --- /dev/null +++ b/samples/spring-ai-simple-chat-client-with-ui/src/main/java/com/unicorn/agents/WebController.java @@ -0,0 +1,23 @@ +package com.unicorn.agents; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class WebController { + + @GetMapping("/") + public String home() { + return "redirect:/chat"; + } + + @GetMapping("/chat") + public String chatPage() { + return "chat"; + } + + @GetMapping("/stream-chat") + public String streamChatPage() { + return "stream-chat"; + } +} diff --git a/samples/spring-ai-simple-chat-client-with-ui/src/main/resources/static/css/main.css b/samples/spring-ai-simple-chat-client-with-ui/src/main/resources/static/css/main.css new file mode 100644 index 00000000..b1bb673b --- /dev/null +++ b/samples/spring-ai-simple-chat-client-with-ui/src/main/resources/static/css/main.css @@ -0,0 +1,10 @@ +/* Custom styles for Unicorn Store */ +.unicorn-gradient { + background: linear-gradient(135deg, #f5f3ff, #fce7f3); +} + +.unicorn-shadow { + box-shadow: 0 4px 14px rgba(156, 39, 176, 0.1); +} + +/* Any additional custom styles can be added here */ diff --git a/samples/spring-ai-simple-chat-client-with-ui/src/main/resources/static/images/unicorn-logo.svg b/samples/spring-ai-simple-chat-client-with-ui/src/main/resources/static/images/unicorn-logo.svg new file mode 100644 index 00000000..108f881c --- /dev/null +++ b/samples/spring-ai-simple-chat-client-with-ui/src/main/resources/static/images/unicorn-logo.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/samples/spring-ai-simple-chat-client-with-ui/src/main/resources/templates/chat.html b/samples/spring-ai-simple-chat-client-with-ui/src/main/resources/templates/chat.html new file mode 100644 index 00000000..99f2b20d --- /dev/null +++ b/samples/spring-ai-simple-chat-client-with-ui/src/main/resources/templates/chat.html @@ -0,0 +1,174 @@ + + + + + + Unicorn Store Chat + + + + +
+ +
+
+
+

🦄 Unicorn Store

+
+ +
+

Chat with our magical assistant to find your perfect unicorn!

+
+ + +
+
+
+
+ 🦄 +
+
+

Welcome to the Unicorn Store! How can I help you find your magical companion today?

+
+
+ +
+ + +
+
+ + +
+
+
+
+ + + + diff --git a/samples/spring-ai-simple-chat-client-with-ui/src/main/resources/templates/stream-chat.html b/samples/spring-ai-simple-chat-client-with-ui/src/main/resources/templates/stream-chat.html new file mode 100644 index 00000000..39e66664 --- /dev/null +++ b/samples/spring-ai-simple-chat-client-with-ui/src/main/resources/templates/stream-chat.html @@ -0,0 +1,196 @@ + + + + + + Unicorn Store Streaming Chat + + + + +
+ +
+
+
+

🦄 Unicorn Store

+
+ +
+

Experience real-time streaming responses from our magical assistant!

+
+ + +
+
+
+
+ 🦄 +
+
+

Welcome to the Unicorn Store streaming chat! Watch as responses appear in real-time. How can I help you find your magical companion today?

+
+
+ +
+ + +
+
+ + +
+
+
+
+ + + + From 00d3d58e74e7e244d49221d75c72d37617b8d370 Mon Sep 17 00:00:00 2001 From: mmeckes Date: Thu, 5 Jun 2025 09:19:58 +0100 Subject: [PATCH 3/3] Updated frontend to use a dark mode theme --- .../src/main/resources/templates/chat.html | 80 +++++++++++++------ .../main/resources/templates/stream-chat.html | 72 ++++++++++++----- 2 files changed, 110 insertions(+), 42 deletions(-) diff --git a/samples/spring-ai-simple-chat-client-with-ui/src/main/resources/templates/chat.html b/samples/spring-ai-simple-chat-client-with-ui/src/main/resources/templates/chat.html index 99f2b20d..2b533b9b 100644 --- a/samples/spring-ai-simple-chat-client-with-ui/src/main/resources/templates/chat.html +++ b/samples/spring-ai-simple-chat-client-with-ui/src/main/resources/templates/chat.html @@ -5,6 +5,26 @@ Unicorn Store Chat + - +
-

🦄 Unicorn Store

+

🦄 Unicorn Store

-

Chat with our magical assistant to find your perfect unicorn!

+

Chat with our magical assistant to find your perfect unicorn!

-
+
-
+
🦄
-
-

Welcome to the Unicorn Store! How can I help you find your magical companion today?

+
+

Welcome to the Unicorn Store! How can I help you find your magical companion today?

-
+
- - + +
@@ -108,21 +142,21 @@

🦄 Unicorn Store

if (sender === 'user') { messageDiv.innerHTML = `
-
-

${escapeHtml(content)}

+
+

${escapeHtml(content)}

-
+
👤
`; } else { messageDiv.innerHTML = ` -
+
🦄
-
-

${escapeHtml(content)}

+
+

${escapeHtml(content)}

`; } @@ -137,14 +171,14 @@

🦄 Unicorn Store

loadingDiv.id = loadingId; loadingDiv.className = 'flex mb-4'; loadingDiv.innerHTML = ` -
+
🦄
-
+
-
-
-
+
+
+
`; diff --git a/samples/spring-ai-simple-chat-client-with-ui/src/main/resources/templates/stream-chat.html b/samples/spring-ai-simple-chat-client-with-ui/src/main/resources/templates/stream-chat.html index 39e66664..32db8a49 100644 --- a/samples/spring-ai-simple-chat-client-with-ui/src/main/resources/templates/stream-chat.html +++ b/samples/spring-ai-simple-chat-client-with-ui/src/main/resources/templates/stream-chat.html @@ -5,6 +5,26 @@ Unicorn Store Streaming Chat + - +
-

🦄 Unicorn Store

+

🦄 Unicorn Store

-

Experience real-time streaming responses from our magical assistant!

+

Experience real-time streaming responses from our magical assistant!

-
+
-
+
🦄
-
-

Welcome to the Unicorn Store streaming chat! Watch as responses appear in real-time. How can I help you find your magical companion today?

+
+

Welcome to the Unicorn Store streaming chat! Watch as responses appear in real-time. How can I help you find your magical companion today?

-
+
- - + +
@@ -140,10 +174,10 @@

🦄 Unicorn Store

if (sender === 'user') { messageDiv.innerHTML = `
-
-

${escapeHtml(content)}

+
+

${escapeHtml(content)}

-
+
👤
@@ -158,11 +192,11 @@

🦄 Unicorn Store

const responseDiv = document.createElement('div'); responseDiv.className = 'flex mb-4'; responseDiv.innerHTML = ` -
+
🦄
-
-

+
+

`;