forked from gothinkster/spring-boot-realworld-example-app
-
Notifications
You must be signed in to change notification settings - Fork 0
feat: Spring AI Application with Chat, Image Generation, and Web UI #138
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
devin-ai-integration
wants to merge
1
commit into
main
Choose a base branch
from
devin/1776086064-spring-ai-application
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,143 @@ | ||
| # Spring AI Application | ||
|
|
||
| A Spring Boot application powered by **Spring AI** that provides AI-driven capabilities including chat completion, text summarization, translation, code analysis, and image generation via OpenAI. | ||
|
|
||
| ## Features | ||
|
|
||
| - **Chat Completion** - Conversational AI with streaming support | ||
| - **Text Summarization** - Condense long text into concise summaries | ||
| - **Translation** - Translate text between 10+ languages | ||
| - **Code Analysis** - Analyze code for descriptions, issues, and complexity | ||
| - **Image Generation** - Generate images from text prompts using DALL-E 3 | ||
| - **Web UI** - Clean, tabbed browser interface for all features | ||
|
|
||
| ## Tech Stack | ||
|
|
||
| - Java 17 | ||
| - Spring Boot 3.4.1 | ||
| - Spring AI 1.0.0-M5 (OpenAI) | ||
| - Thymeleaf (Web UI) | ||
| - Maven | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - Java 17+ | ||
| - Maven 3.6+ | ||
| - OpenAI API key | ||
|
|
||
| ## Getting Started | ||
|
|
||
| ### 1. Navigate to the spring-ai directory | ||
|
|
||
| ```bash | ||
| cd spring-ai | ||
| ``` | ||
|
|
||
| ### 2. Set your OpenAI API key | ||
|
|
||
| ```bash | ||
| export SPRING_AI_OPENAI_API_KEY=your-api-key-here | ||
| ``` | ||
|
|
||
| ### 3. Build and run | ||
|
|
||
| ```bash | ||
| mvn clean install | ||
| mvn spring-boot:run | ||
| ``` | ||
|
|
||
| ### 4. Access the application | ||
|
|
||
| Open [http://localhost:8080](http://localhost:8080) in your browser. | ||
|
|
||
| ## API Endpoints | ||
|
|
||
| ### Chat | ||
|
|
||
| | Method | Endpoint | Description | | ||
| |--------|----------|-------------| | ||
| | `GET` | `/api/chat?message=...` | Simple chat | | ||
| | `POST` | `/api/chat` | Chat with options (model, temperature) | | ||
| | `GET` | `/api/chat/stream?message=...` | Streaming chat (SSE) | | ||
| | `POST` | `/api/chat/summarize` | Summarize text | | ||
| | `POST` | `/api/chat/translate` | Translate text | | ||
| | `POST` | `/api/chat/analyze-code` | Analyze code | | ||
|
|
||
| ### Image | ||
|
|
||
| | Method | Endpoint | Description | | ||
| |--------|----------|-------------| | ||
| | `POST` | `/api/image/generate` | Generate image from prompt | | ||
|
|
||
| ### Example Requests | ||
|
|
||
| **Chat:** | ||
| ```bash | ||
| curl http://localhost:8080/api/chat?message=Hello | ||
|
|
||
| curl -X POST http://localhost:8080/api/chat \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"message": "Explain Spring AI", "model": "gpt-4o-mini", "temperature": 0.7}' | ||
| ``` | ||
|
|
||
| **Summarize:** | ||
| ```bash | ||
| curl -X POST http://localhost:8080/api/chat/summarize \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"text": "Long text to summarize..."}' | ||
| ``` | ||
|
|
||
| **Translate:** | ||
| ```bash | ||
| curl -X POST http://localhost:8080/api/chat/translate \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"text": "Hello world", "targetLanguage": "Spanish"}' | ||
| ``` | ||
|
|
||
| **Image Generation:** | ||
| ```bash | ||
| curl -X POST http://localhost:8080/api/image/generate \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"prompt": "A sunset over mountains", "width": 1024, "height": 1024}' | ||
| ``` | ||
|
|
||
| ## Configuration | ||
|
|
||
| Key properties in `application.properties`: | ||
|
|
||
| | Property | Default | Description | | ||
| |----------|---------|-------------| | ||
| | `spring.ai.openai.api-key` | - | Your OpenAI API key | | ||
| | `spring.ai.openai.chat.options.model` | `gpt-4o-mini` | Chat model | | ||
| | `spring.ai.openai.chat.options.temperature` | `0.7` | Response creativity | | ||
| | `spring.ai.openai.image.options.model` | `dall-e-3` | Image model | | ||
| | `server.port` | `8080` | Server port | | ||
|
|
||
| ## Project Structure | ||
|
|
||
| ``` | ||
| spring-ai/ | ||
| src/main/java/com/example/springai/ | ||
| ├── SpringAiApplication.java # Main application entry point | ||
| ├── config/ | ||
| │ └── AiConfig.java # CORS and web configuration | ||
| ├── controller/ | ||
| │ ├── ChatController.java # Chat REST API endpoints | ||
| │ ├── ImageController.java # Image generation endpoint | ||
| │ └── WebController.java # Web UI controller | ||
| ├── model/ | ||
| │ ├── ChatRequest.java # Chat request DTO | ||
| │ ├── ChatResponse.java # Chat response DTO | ||
| │ ├── ImageRequest.java # Image request DTO | ||
| │ └── ImageResponse.java # Image response DTO | ||
| └── service/ | ||
| ├── ChatService.java # Chat/AI business logic | ||
| └── ImageService.java # Image generation logic | ||
| ``` | ||
|
|
||
| ## Running Tests | ||
|
|
||
| ```bash | ||
| cd spring-ai | ||
| mvn test | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| <modelVersion>4.0.0</modelVersion> | ||
|
|
||
| <parent> | ||
| <groupId>org.springframework.boot</groupId> | ||
| <artifactId>spring-boot-starter-parent</artifactId> | ||
| <version>3.4.1</version> | ||
| <relativePath/> | ||
| </parent> | ||
|
|
||
| <groupId>com.example</groupId> | ||
| <artifactId>spring-ai-app</artifactId> | ||
| <version>0.0.1-SNAPSHOT</version> | ||
| <name>Spring AI Application</name> | ||
| <description>A Spring Boot application with Spring AI integration for chat, image generation, and embeddings</description> | ||
|
|
||
| <properties> | ||
| <java.version>17</java.version> | ||
| <spring-ai.version>1.0.0-M5</spring-ai.version> | ||
| </properties> | ||
|
|
||
| <dependencies> | ||
| <!-- Spring Boot Starters --> | ||
| <dependency> | ||
| <groupId>org.springframework.boot</groupId> | ||
| <artifactId>spring-boot-starter-web</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.springframework.boot</groupId> | ||
| <artifactId>spring-boot-starter-thymeleaf</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.springframework.boot</groupId> | ||
| <artifactId>spring-boot-starter-validation</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.springframework.boot</groupId> | ||
| <artifactId>spring-boot-starter-actuator</artifactId> | ||
| </dependency> | ||
|
|
||
| <!-- Spring AI - OpenAI --> | ||
| <dependency> | ||
| <groupId>org.springframework.ai</groupId> | ||
| <artifactId>spring-ai-openai-spring-boot-starter</artifactId> | ||
| <version>${spring-ai.version}</version> | ||
| </dependency> | ||
|
|
||
| <!-- Spring Boot DevTools --> | ||
| <dependency> | ||
| <groupId>org.springframework.boot</groupId> | ||
| <artifactId>spring-boot-devtools</artifactId> | ||
| <scope>runtime</scope> | ||
| <optional>true</optional> | ||
| </dependency> | ||
|
|
||
| <!-- Testing --> | ||
| <dependency> | ||
| <groupId>org.springframework.boot</groupId> | ||
| <artifactId>spring-boot-starter-test</artifactId> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| </dependencies> | ||
|
|
||
| <repositories> | ||
| <repository> | ||
| <id>spring-milestones</id> | ||
| <name>Spring Milestones</name> | ||
| <url>https://repo.spring.io/milestone</url> | ||
| <snapshots> | ||
| <enabled>false</enabled> | ||
| </snapshots> | ||
| </repository> | ||
| </repositories> | ||
|
|
||
| <build> | ||
| <plugins> | ||
| <plugin> | ||
| <groupId>org.springframework.boot</groupId> | ||
| <artifactId>spring-boot-maven-plugin</artifactId> | ||
| </plugin> | ||
| </plugins> | ||
| </build> | ||
| </project> |
12 changes: 12 additions & 0 deletions
12
spring-ai/src/main/java/com/example/springai/SpringAiApplication.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package com.example.springai; | ||
|
|
||
| import org.springframework.boot.SpringApplication; | ||
| import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
|
|
||
| @SpringBootApplication | ||
| public class SpringAiApplication { | ||
|
|
||
| public static void main(String[] args) { | ||
| SpringApplication.run(SpringAiApplication.class, args); | ||
| } | ||
| } |
17 changes: 17 additions & 0 deletions
17
spring-ai/src/main/java/com/example/springai/config/AiConfig.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package com.example.springai.config; | ||
|
|
||
| import org.springframework.context.annotation.Configuration; | ||
| import org.springframework.web.servlet.config.annotation.CorsRegistry; | ||
| import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | ||
|
|
||
| @Configuration | ||
| public class AiConfig implements WebMvcConfigurer { | ||
|
|
||
| @Override | ||
| public void addCorsMappings(CorsRegistry registry) { | ||
| registry.addMapping("/api/**") | ||
| .allowedOrigins("*") | ||
| .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") | ||
| .allowedHeaders("*"); | ||
| } | ||
| } |
61 changes: 61 additions & 0 deletions
61
spring-ai/src/main/java/com/example/springai/controller/ChatController.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| package com.example.springai.controller; | ||
|
|
||
| import com.example.springai.model.ChatRequest; | ||
| import com.example.springai.model.ChatResponse; | ||
| import com.example.springai.service.ChatService; | ||
| import jakarta.validation.Valid; | ||
| import java.util.Map; | ||
| import org.springframework.http.MediaType; | ||
| import org.springframework.web.bind.annotation.GetMapping; | ||
| import org.springframework.web.bind.annotation.PostMapping; | ||
| import org.springframework.web.bind.annotation.RequestBody; | ||
| import org.springframework.web.bind.annotation.RequestMapping; | ||
| import org.springframework.web.bind.annotation.RequestParam; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
| import reactor.core.publisher.Flux; | ||
|
|
||
| @RestController | ||
| @RequestMapping("/api/chat") | ||
| public class ChatController { | ||
|
|
||
| private final ChatService chatService; | ||
|
|
||
| public ChatController(ChatService chatService) { | ||
| this.chatService = chatService; | ||
| } | ||
|
|
||
| @GetMapping | ||
| public ChatResponse chat(@RequestParam String message) { | ||
| return chatService.chat(message); | ||
| } | ||
|
|
||
| @PostMapping | ||
| public ChatResponse chatPost(@Valid @RequestBody ChatRequest request) { | ||
| return chatService.chatWithOptions( | ||
| request.getMessage(), request.getModel(), request.getTemperature()); | ||
| } | ||
|
|
||
| @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) | ||
| public Flux<String> chatStream(@RequestParam String message) { | ||
| return chatService.chatStream(message); | ||
| } | ||
|
|
||
| @PostMapping("/summarize") | ||
| public ChatResponse summarize(@RequestBody Map<String, String> request) { | ||
| String text = request.get("text"); | ||
| return chatService.summarize(text); | ||
| } | ||
|
|
||
| @PostMapping("/translate") | ||
| public ChatResponse translate(@RequestBody Map<String, String> request) { | ||
| String text = request.get("text"); | ||
| String targetLanguage = request.getOrDefault("targetLanguage", "English"); | ||
| return chatService.translate(text, targetLanguage); | ||
| } | ||
|
|
||
| @PostMapping("/analyze-code") | ||
| public ChatResponse analyzeCode(@RequestBody Map<String, String> request) { | ||
| String code = request.get("code"); | ||
| return chatService.analyzeCode(code); | ||
| } | ||
| } | ||
27 changes: 27 additions & 0 deletions
27
spring-ai/src/main/java/com/example/springai/controller/ImageController.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| package com.example.springai.controller; | ||
|
|
||
| import com.example.springai.model.ImageRequest; | ||
| import com.example.springai.model.ImageResponse; | ||
| import com.example.springai.service.ImageService; | ||
| import jakarta.validation.Valid; | ||
| import org.springframework.web.bind.annotation.PostMapping; | ||
| import org.springframework.web.bind.annotation.RequestBody; | ||
| import org.springframework.web.bind.annotation.RequestMapping; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
|
|
||
| @RestController | ||
| @RequestMapping("/api/image") | ||
| public class ImageController { | ||
|
|
||
| private final ImageService imageService; | ||
|
|
||
| public ImageController(ImageService imageService) { | ||
| this.imageService = imageService; | ||
| } | ||
|
|
||
| @PostMapping("/generate") | ||
| public ImageResponse generateImage(@Valid @RequestBody ImageRequest request) { | ||
| return imageService.generateImage( | ||
| request.getPrompt(), request.getWidth(), request.getHeight()); | ||
| } | ||
| } |
13 changes: 13 additions & 0 deletions
13
spring-ai/src/main/java/com/example/springai/controller/WebController.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package com.example.springai.controller; | ||
|
|
||
| import org.springframework.stereotype.Controller; | ||
| import org.springframework.web.bind.annotation.GetMapping; | ||
|
|
||
| @Controller | ||
| public class WebController { | ||
|
|
||
| @GetMapping("/") | ||
| public String index() { | ||
| return "index"; | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔴 Missing null validation on
/summarize,/translate,/analyze-codeendpoints allows null to propagate to AI APIThe
summarize,translate, andanalyzeCodecontroller methods extract values from a rawMap<String, String>usingrequest.get("text")/request.get("code")without null checks. If a client sends a request with a missing or misnamed key (e.g.,{}or{"txt": "hello"}), the value isnull. This null propagates throughChatService.chatWithSystemPrompt()→new UserMessage(null)→chatModel.call(), which will likely throw aNullPointerExceptionor an uninformative 500 error, rather than a proper 400 Bad Request. This contrasts with thechatPostendpoint which properly uses@Valid @RequestBody ChatRequestwith@NotBlankvalidation.Prompt for agents
Was this helpful? React with 👍 or 👎 to provide feedback.