├── .gitignore ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── README.md ├── ai ├── chat-ui-with-rag │ ├── README.md │ ├── pom.xml │ └── src │ │ └── main │ │ ├── java │ │ └── com │ │ │ └── howtodoinjava │ │ │ └── ai │ │ │ └── demo │ │ │ ├── ChatUiWithRagApplication.java │ │ │ ├── config │ │ │ ├── SecurityConfig.java │ │ │ ├── VectorStoreConfig.java │ │ │ └── WebConfig.java │ │ │ ├── rag │ │ │ └── UploadController.java │ │ │ └── web │ │ │ ├── ChatController.java │ │ │ └── model │ │ │ ├── Answer.java │ │ │ └── Question.java │ │ └── resources │ │ ├── application.properties │ │ ├── static │ │ ├── AI.png │ │ ├── File.png │ │ ├── User.png │ │ ├── script.js │ │ └── style.css │ │ └── templates │ │ ├── home.html │ │ └── login.html ├── embedding-model │ ├── README.md │ ├── openai │ │ ├── pom.xml │ │ └── src │ │ │ └── main │ │ │ ├── java │ │ │ └── com │ │ │ │ └── howtodoinjava │ │ │ │ └── demo │ │ │ │ ├── App.java │ │ │ │ └── EmbeddingController.java │ │ │ └── resources │ │ │ └── application.properties │ └── pom.xml ├── embedding-transformer │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── howtodoinjava │ │ │ │ └── ai │ │ │ │ └── demo │ │ │ │ ├── App.java │ │ │ │ └── EmbeddingController.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── howtodoinjava │ │ └── ai │ │ └── demo │ │ └── AppTest.java ├── function-callback │ ├── README.md │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── howtodoinjava │ │ │ │ └── ai │ │ │ │ └── demo │ │ │ │ ├── ChatController.java │ │ │ │ ├── ChatService.java │ │ │ │ ├── Functions.java │ │ │ │ ├── SpringAiFunctionCallbackApplication.java │ │ │ │ └── StockPriceService.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── howtodoinjava │ │ └── ai │ │ └── demo │ │ ├── SpringAiFunctionCallbackApplicationTest.java │ │ └── TestFunctionCalling.java ├── image-gen │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── howtodoinjava │ │ │ │ └── ai │ │ │ │ └── demo │ │ │ │ ├── AppConfiguration.java │ │ │ │ ├── ImageGenController.java │ │ │ │ ├── ImageGenRequest.java │ │ │ │ ├── ImageGenService.java │ │ │ │ └── SpringAiImageGenApplication.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── howtodoinjava │ │ └── ai │ │ └── demo │ │ ├── ImageGenControllerTest.java │ │ └── SpringAiImageGenApplicationTest.java ├── multi-model │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── howtodoinjava │ │ │ │ └── ai │ │ │ │ └── demo │ │ │ │ ├── AiController.java │ │ │ │ ├── MultiModelApplication.java │ │ │ │ ├── Request.java │ │ │ │ └── Response.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── howtodoinjava │ │ └── ai │ │ └── demo │ │ └── MultiModelApplicationTest.java ├── ollama-local-setup │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── howtodoinjava │ │ │ │ └── ai │ │ │ │ └── demo │ │ │ │ └── App.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── howtodoinjava │ │ └── ai │ │ └── demo │ │ └── AppTest.java ├── output-converters │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── howtodoinjava │ │ │ │ └── ai │ │ │ │ └── demo │ │ │ │ ├── App.java │ │ │ │ ├── ChatController.java │ │ │ │ ├── ChatService.java │ │ │ │ ├── PlayerInfo.java │ │ │ │ └── Question.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── howtodoinjava │ │ └── ai │ │ └── demo │ │ └── AppTest.java ├── pom.xml ├── prompt-template │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── howtodoinjava │ │ │ │ └── ai │ │ │ │ └── demo │ │ │ │ └── demo │ │ │ │ ├── App.java │ │ │ │ ├── QueryController.java │ │ │ │ ├── QueryResponse.java │ │ │ │ └── QueryService.java │ │ └── resources │ │ │ ├── application.properties │ │ │ └── query-template.st │ │ └── test │ │ └── java │ │ └── com │ │ └── howtodoinjava │ │ └── ai │ │ └── demo │ │ └── demo │ │ ├── AppTest.java │ │ └── QueryControllerTest.java ├── qa-advisor │ ├── README.md │ ├── docker-compose.yaml │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── howtodoinjava │ │ │ │ └── ai │ │ │ │ └── demo │ │ │ │ ├── Answer.java │ │ │ │ ├── App.java │ │ │ │ ├── QAController.java │ │ │ │ ├── QAService.java │ │ │ │ ├── Question.java │ │ │ │ └── VectorStoreLoader.java │ │ └── resources │ │ │ ├── CallingRates.pdf │ │ │ ├── application.properties │ │ │ ├── story.md │ │ │ └── story.txt │ │ └── test │ │ └── java │ │ └── com │ │ └── howtodoinjava │ │ └── ai │ │ └── demo │ │ ├── AppTest.java │ │ └── EvaluatorTests.java ├── similarity-search │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── howtodoinjava │ │ │ │ └── ai │ │ │ │ └── demo │ │ │ │ ├── App.java │ │ │ │ ├── AppConfig.java │ │ │ │ ├── IngestionPipeline.java │ │ │ │ ├── LogRecord.java │ │ │ │ ├── SemanticSearchController.java │ │ │ │ └── SemanticSearchService.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── howtodoinjava │ │ └── ai │ │ └── demo │ │ └── AppTest.java ├── speech-to-text │ ├── README.md │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── howtodoinjava │ │ │ │ └── ai │ │ │ │ └── demo │ │ │ │ ├── App.java │ │ │ │ └── TranscriptionController.java │ │ └── resources │ │ │ ├── application.properties │ │ │ └── speech.mp3 │ │ └── test │ │ └── java │ │ └── com │ │ └── howtodoinjava │ │ └── ai │ │ └── demo │ │ └── AppTest.java ├── structured-data-extraction │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── howtodoinjava │ │ │ │ └── ai │ │ │ │ └── demo │ │ │ │ ├── App.java │ │ │ │ ├── HealthRecord.java │ │ │ │ ├── HealthRecordController.java │ │ │ │ └── HealthRecordService.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── howtodoinjava │ │ └── ai │ │ └── demo │ │ └── AppTest.java ├── text-to-speech │ ├── README.md │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── howtodoinjava │ │ │ │ └── ai │ │ │ │ └── demo │ │ │ │ ├── SpeechController.java │ │ │ │ └── SpringAiTextToSpeechApp.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── howtodoinjava │ │ └── ai │ │ └── demo │ │ └── SpringAiTextToSpeechAppTest.java ├── usecases │ ├── etp-pipeline-spring-ai │ │ ├── README.md │ │ ├── docker-compose.yaml │ │ ├── pom.xml │ │ └── src │ │ │ ├── main │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── howtodoinjava │ │ │ │ │ └── ai │ │ │ │ │ └── demo │ │ │ │ │ ├── App.java │ │ │ │ │ ├── AppConfiguration.java │ │ │ │ │ ├── CustomDocumentReader.java │ │ │ │ │ ├── EtlPipeline.java │ │ │ │ │ └── IngestionController.java │ │ │ └── resources │ │ │ │ └── application.properties │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── howtodoinjava │ │ │ └── ai │ │ │ └── demo │ │ │ └── AppTest.java │ ├── generate-sql-queries │ │ ├── README.md │ │ ├── pom.xml │ │ └── src │ │ │ ├── main │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── howtodoinjava │ │ │ │ │ └── ai │ │ │ │ │ └── demo │ │ │ │ │ ├── AiException.java │ │ │ │ │ ├── AiRequest.java │ │ │ │ │ ├── AiResponse.java │ │ │ │ │ ├── CustomExceptionHandler.java │ │ │ │ │ ├── GenerateSqlWithAiApplication.java │ │ │ │ │ └── SqlController.java │ │ │ └── resources │ │ │ │ ├── application.properties │ │ │ │ ├── data.sql │ │ │ │ ├── schema.sql │ │ │ │ └── sql-prompt-template.st │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── howtodoinjava │ │ │ └── ai │ │ │ └── demo │ │ │ └── GenerateSqlWithAiApplicationTest.java │ ├── pom.xml │ ├── text-classifier │ │ ├── pom.xml │ │ └── src │ │ │ ├── main │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── howtodoinjava │ │ │ │ │ └── ai │ │ │ │ │ └── demo │ │ │ │ │ ├── App.java │ │ │ │ │ ├── ClassificationController.java │ │ │ │ │ ├── ClassificationType.java │ │ │ │ │ └── TextClassifierService.java │ │ │ └── resources │ │ │ │ └── application.properties │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── howtodoinjava │ │ │ └── ai │ │ │ └── demo │ │ │ └── AppTest.java │ └── vector-store-pipeline │ │ ├── README.md │ │ ├── docker-compose.yaml │ │ ├── pom.xml │ │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── howtodoinjava │ │ │ │ └── ai │ │ │ │ └── demo │ │ │ │ ├── App.java │ │ │ │ ├── CloudFunctionConfig.java │ │ │ │ ├── IngestionController.java │ │ │ │ └── IngestionService.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── howtodoinjava │ │ └── ai │ │ └── demo │ │ └── AppTest.java └── vector-stores │ ├── README.md │ ├── pgvector-ollama │ ├── compose.yaml │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── howtodoinjava │ │ │ │ └── ai │ │ │ │ └── demo │ │ │ │ └── PgVectorOllamaApp.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── howtodoinjava │ │ └── ai │ │ └── demo │ │ └── PgVectorOllamaAppTest.java │ ├── pgvector-openai │ ├── README.md │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── howtodoinjava │ │ │ │ └── ai │ │ │ │ └── demo │ │ │ │ └── App.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── howtodoinjava │ │ └── ai │ │ └── demo │ │ └── AppTest.java │ └── pom.xml ├── mvnw ├── mvnw.cmd ├── openai-quickstart ├── README.md ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── howtodoinjava │ │ └── ai │ │ ├── chat │ │ ├── AppConfiguration.java │ │ ├── Application.java │ │ ├── JokeResponse.java │ │ ├── OpenAiChatController.java │ │ └── Pair.java │ │ ├── image │ │ ├── AppConfiguration.java │ │ ├── Application.java │ │ └── OpenAiImageController.java │ │ ├── promptTemplate │ │ └── PromptTemplateApplication.java │ │ └── structuredOutput │ │ └── StructuredOutputDemoApplication.java │ └── resources │ ├── application.properties │ └── prompts │ ├── get-list-message.st │ ├── system-message.st │ └── user-message.st ├── pom.xml └── rag-examples ├── docker-compose.yml ├── pom.xml └── src └── main ├── java ├── com │ └── howtodoinjava │ │ └── ai │ │ ├── chat │ │ └── redis │ │ │ ├── RagWithRedisAndOpenAI.java │ │ │ ├── controller │ │ │ └── AiChatController.java │ │ │ └── service │ │ │ ├── DataIngestionService.java │ │ │ └── SimilaritySearchService.java │ │ └── chromaWithOpenAI │ │ └── Application.java └── elasticSearch │ └── RagWithElasticSearchAndOpenAI.java └── resources ├── application.properties └── data ├── current-affairs.pdf └── vectorStoreRawData.json /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | **/data 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | 23 | ### NetBeans ### 24 | /nbproject/private/ 25 | /nbbuild/ 26 | /dist/ 27 | /nbdist/ 28 | /.nb-gradle/ 29 | build/ 30 | !**/src/main/**/build/ 31 | !**/src/test/**/build/ 32 | 33 | ### VS Code ### 34 | .vscode/ 35 | /.mvn/wrapper/maven-wrapper.jar 36 | /.mvn/wrapper/maven-wrapper.properties 37 | /mvnw 38 | /mvnw.cmd 39 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokeshgupta1981/spring-ai-examples/182edc364610399dc41508e99bb605ba642b451f/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spring AI Examples 2 | 3 | ## Prerequisites 4 | 5 | Create an account at OpenAI Signup and generate the security token at [API Keys](https://platform.openai.com/api-keys). 6 | 7 | Spring AI (for OpenAI integration) expects a configuration property named 'spring.ai.openai.api-key' that we should set to the value of the API Key obtained in teh above step. 8 | 9 | ``` 10 | export SPRING_AI_OPENAI_API_KEY= 11 | ``` 12 | 13 | ## Run the Project 14 | 15 | ``` 16 | ./mvnw spring-boot:run 17 | ``` -------------------------------------------------------------------------------- /ai/chat-ui-with-rag/README.md: -------------------------------------------------------------------------------- 1 | # Spring AI Chat Application with RAG 2 | 3 | This is a demo application contains the Chat application's UI (thymeleaf) and backend (Spring AI, Spring Web and Spring Security). 4 | 5 | You can use either the default ```SimpleVectorStore``` that stores the embeddings in memory. 6 | 7 | Or, you can use Postgres vector store by passing a runtime argument: 8 | 9 | ```bash 10 | -DvectorStoreType=pgvector 11 | ``` 12 | Or define this property as environment variable. 13 | 14 | ## Using Postgres Vector Store Database 15 | 16 | Make sure the Postgres Vector Store database is up and running. To run the database as docker container, run the following commands. 17 | 18 | Pull the image: 19 | 20 | ``` 21 | docker pull pgvector/pgvector:pg16 22 | ``` 23 | 24 | Run the server: 25 | 26 | ``` 27 | docker run -it --rm --name postgres -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres pgvector/pgvector:pg16 28 | ``` 29 | 30 | ## Demo 31 | 32 | Once the application is started, you can access the application in URL http://localhost:8081/. 33 | 34 | By default a login page is shown. Enter the following username/password. 35 | 36 | ``` 37 | Username: howtodoinjava 38 | Password: password 39 | ``` 40 | 41 | After successful login, you will be redirected to chat application page. Feel free to upload any file, and ask AI any question related to the document content. 42 | -------------------------------------------------------------------------------- /ai/chat-ui-with-rag/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.howtodoinjava.ai.demo 7 | ai 8 | 1.0.0 9 | 10 | 11 | chat-ui-with-rag 12 | jar 13 | chat-ui-with-rag 14 | http://maven.apache.org 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-starter-web 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-thymeleaf 24 | 25 | 26 | org.thymeleaf.extras 27 | thymeleaf-extras-springsecurity6 28 | 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-security 33 | 34 | 35 | 36 | org.springframework.ai 37 | spring-ai-openai-spring-boot-starter 38 | 39 | 40 | org.springframework.ai 41 | spring-ai-tika-document-reader 42 | 43 | 44 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /ai/chat-ui-with-rag/src/main/java/com/howtodoinjava/ai/demo/ChatUiWithRagApplication.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class ChatUiWithRagApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(ChatUiWithRagApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ai/chat-ui-with-rag/src/main/java/com/howtodoinjava/ai/demo/config/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo.config; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 9 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 10 | import org.springframework.security.core.userdetails.User; 11 | import org.springframework.security.core.userdetails.UserDetails; 12 | import org.springframework.security.core.userdetails.UserDetailsService; 13 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 14 | import org.springframework.security.crypto.password.PasswordEncoder; 15 | import org.springframework.security.provisioning.InMemoryUserDetailsManager; 16 | import org.springframework.security.web.SecurityFilterChain; 17 | 18 | @Configuration 19 | public class SecurityConfig { 20 | 21 | @Bean 22 | SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { 23 | return http 24 | .authorizeRequests(authorize -> authorize 25 | .requestMatchers("/login").permitAll() 26 | .anyRequest().authenticated() 27 | ) 28 | .formLogin(formLogin -> formLogin 29 | .loginPage("/login") 30 | .defaultSuccessUrl("/", true) 31 | ) 32 | .csrf(csrf -> csrf.disable()) 33 | .headers(headers -> headers.frameOptions(frameOptions -> frameOptions.sameOrigin())) 34 | .build(); 35 | } 36 | 37 | @Bean 38 | UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) { 39 | List usersList = new ArrayList<>(); 40 | usersList.add(new User( 41 | "howtodoinjava", passwordEncoder.encode("password"), 42 | Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")))); 43 | return new InMemoryUserDetailsManager(usersList); 44 | } 45 | 46 | @Bean 47 | PasswordEncoder passwordEncoder() { 48 | return new BCryptPasswordEncoder(); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /ai/chat-ui-with-rag/src/main/java/com/howtodoinjava/ai/demo/config/VectorStoreConfig.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo.config; 2 | 3 | import org.springframework.ai.embedding.EmbeddingModel; 4 | import org.springframework.ai.vectorstore.SimpleVectorStore; 5 | import org.springframework.ai.vectorstore.VectorStore; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | 9 | @Configuration 10 | public class VectorStoreConfig { 11 | 12 | @Bean 13 | VectorStore simleVectorStore(EmbeddingModel embeddingModel) { 14 | return new SimpleVectorStore(embeddingModel); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ai/chat-ui-with-rag/src/main/java/com/howtodoinjava/ai/demo/config/WebConfig.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo.config; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 6 | 7 | @Configuration 8 | public class WebConfig implements WebMvcConfigurer { 9 | 10 | @Override 11 | public void addViewControllers(ViewControllerRegistry registry) { 12 | 13 | registry.addViewController("/").setViewName("home"); 14 | registry.addViewController("/login").setViewName("login"); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ai/chat-ui-with-rag/src/main/java/com/howtodoinjava/ai/demo/rag/UploadController.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo.rag; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.nio.file.Files; 6 | import java.nio.file.Path; 7 | import java.nio.file.Paths; 8 | import java.nio.file.StandardCopyOption; 9 | import java.util.List; 10 | import org.slf4j.Logger; 11 | import org.springframework.ai.document.Document; 12 | import org.springframework.ai.reader.tika.TikaDocumentReader; 13 | import org.springframework.ai.transformer.splitter.TokenTextSplitter; 14 | import org.springframework.ai.vectorstore.VectorStore; 15 | import org.springframework.web.bind.annotation.PostMapping; 16 | import org.springframework.web.bind.annotation.RequestParam; 17 | import org.springframework.web.bind.annotation.RestController; 18 | import org.springframework.web.multipart.MultipartFile; 19 | 20 | @RestController 21 | public class UploadController { 22 | 23 | private static final Logger LOG = org.slf4j.LoggerFactory.getLogger(UploadController.class); 24 | 25 | private final VectorStore vectorStore; 26 | 27 | public UploadController(VectorStore vectorStore) { 28 | this.vectorStore = vectorStore; 29 | } 30 | 31 | @PostMapping("/upload") 32 | public UploadResponse upload(@RequestParam("file") MultipartFile file) throws IOException { 33 | 34 | Path destinationFile = Paths.get("/temp").resolve( 35 | Paths.get(file.getOriginalFilename())).normalize().toAbsolutePath(); 36 | 37 | try (InputStream inputStream = file.getInputStream()) { 38 | Files.copy(inputStream, destinationFile, StandardCopyOption.REPLACE_EXISTING); 39 | } 40 | 41 | TikaDocumentReader documentReader = new TikaDocumentReader(destinationFile.toUri().toString()); 42 | List documents = documentReader.get(); 43 | List splitDocuments = new TokenTextSplitter().apply(documents); 44 | 45 | vectorStore.add(splitDocuments); 46 | 47 | return new UploadResponse(file.getOriginalFilename(), file.getContentType(), file.getSize()); 48 | } 49 | 50 | private static record UploadResponse(String fileName, String fileType, long fileSize) { 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ai/chat-ui-with-rag/src/main/java/com/howtodoinjava/ai/demo/web/ChatController.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo.web; 2 | 3 | import static org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY; 4 | 5 | import com.howtodoinjava.ai.demo.web.model.Answer; 6 | import com.howtodoinjava.ai.demo.web.model.Question; 7 | import org.springframework.ai.chat.client.ChatClient; 8 | import org.springframework.ai.chat.client.advisor.PromptChatMemoryAdvisor; 9 | import org.springframework.ai.chat.client.advisor.QuestionAnswerAdvisor; 10 | import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor; 11 | import org.springframework.ai.chat.memory.InMemoryChatMemory; 12 | import org.springframework.ai.vectorstore.SearchRequest; 13 | import org.springframework.ai.vectorstore.VectorStore; 14 | import org.springframework.security.core.Authentication; 15 | import org.springframework.web.bind.annotation.PostMapping; 16 | import org.springframework.web.bind.annotation.RequestBody; 17 | import org.springframework.web.bind.annotation.RequestMapping; 18 | import org.springframework.web.bind.annotation.RestController; 19 | 20 | @RestController 21 | @RequestMapping("/chat") 22 | public class ChatController { 23 | 24 | private final ChatClient chatClient; 25 | 26 | public ChatController(ChatClient.Builder chatClientBuilder, VectorStore vectorStore) { 27 | 28 | this.chatClient = chatClientBuilder 29 | .defaultAdvisors( 30 | new SimpleLoggerAdvisor(), 31 | new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults()), 32 | new PromptChatMemoryAdvisor(new InMemoryChatMemory())) 33 | .build(); 34 | } 35 | 36 | @PostMapping 37 | public Answer chat(@RequestBody Question question, Authentication user) { 38 | return chatClient.prompt() 39 | .user(question.question()) 40 | .advisors( 41 | advisorSpec -> advisorSpec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, user.getPrincipal())) 42 | .call() 43 | .entity(Answer.class); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ai/chat-ui-with-rag/src/main/java/com/howtodoinjava/ai/demo/web/model/Answer.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo.web.model; 2 | 3 | public record Answer(String answer) { 4 | } 5 | -------------------------------------------------------------------------------- /ai/chat-ui-with-rag/src/main/java/com/howtodoinjava/ai/demo/web/model/Question.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo.web.model; 2 | 3 | public record Question(String question) { 4 | } 5 | -------------------------------------------------------------------------------- /ai/chat-ui-with-rag/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | #server.port=8081 2 | 3 | spring.ai.openai.api-key=${OPENAI_API_KEY} 4 | spring.ai.openai.model=gpt-3.5-turbo 5 | 6 | spring.servlet.multipart.max-file-size=50MB 7 | spring.servlet.multipart.max-request-size=50MB 8 | 9 | spring.ai.openai.embedding.enabled=true 10 | spring.ai.openai.embedding.model=text-embedding-ada-002 11 | 12 | #spring.datasource.url=jdbc:postgresql://localhost:5432/postgres 13 | #spring.datasource.username=postgres 14 | #spring.datasource.password=postgres 15 | 16 | #spring.ai.vectorstore.pgvector.index-type=HNSW 17 | #spring.ai.vectorstore.pgvector.distance-type=COSINE_DISTANCE 18 | #spring.ai.vectorstore.pgvector.dimension=1536 19 | 20 | logging.level.org.springframework.ai.chat.client.advisor=DEBUG 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ai/chat-ui-with-rag/src/main/resources/static/AI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokeshgupta1981/spring-ai-examples/182edc364610399dc41508e99bb605ba642b451f/ai/chat-ui-with-rag/src/main/resources/static/AI.png -------------------------------------------------------------------------------- /ai/chat-ui-with-rag/src/main/resources/static/File.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokeshgupta1981/spring-ai-examples/182edc364610399dc41508e99bb605ba642b451f/ai/chat-ui-with-rag/src/main/resources/static/File.png -------------------------------------------------------------------------------- /ai/chat-ui-with-rag/src/main/resources/static/User.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokeshgupta1981/spring-ai-examples/182edc364610399dc41508e99bb605ba642b451f/ai/chat-ui-with-rag/src/main/resources/static/User.png -------------------------------------------------------------------------------- /ai/chat-ui-with-rag/src/main/resources/static/script.js: -------------------------------------------------------------------------------- 1 | const addToTranscript = (who, text) => { 2 | let b = document.querySelector('#transcript'); 3 | let name = (who === "User") ? username : who; 4 | b.innerHTML += createTranscriptEntry(who, name, text); 5 | b.scrollTop = b.scrollHeight; 6 | console.log(text); 7 | }; 8 | 9 | const createTranscriptEntry = (who, name, text) => { 10 | return ` 11 |
12 |
${name}: ${text}
13 |
` 14 | }; 15 | 16 | const handleResponse = (response) => { 17 | addToTranscript("AI", response.answer); 18 | }; 19 | 20 | const postQuestion = (question) => { 21 | let data = { 22 | question: question 23 | }; 24 | 25 | fetch("/chat", { 26 | method: "POST", 27 | headers: { 28 | "Content-Type": "application/json" 29 | }, 30 | body: JSON.stringify(data) 31 | }) 32 | .then(res => res.json()) 33 | .then(handleResponse); 34 | }; 35 | 36 | const submitTypedText = (event) => { 37 | let typedTextInput = document.querySelector('#userInput'); 38 | let typedText = typedTextInput.value; 39 | 40 | // don't submit if empty 41 | if (typedText.trim().length === 0) { 42 | return false; 43 | } 44 | // submit it here 45 | addToTranscript("User", typedText); 46 | postQuestion(typedText); 47 | typedTextInput.value = ''; 48 | return false; 49 | }; 50 | 51 | const initUIEvents = () => { 52 | let t = document.querySelector('#typedTextSubmit'); 53 | t.addEventListener('click', submitTypedText); 54 | var textarea = document.querySelector('textarea#userInput'); 55 | textarea.addEventListener('keydown', e => { 56 | if (e.key === "Enter" && !e.shiftKey) { 57 | e.preventDefault(); 58 | submitTypedText(e); 59 | } 60 | }); 61 | var modal = document.getElementById("uploadModal"); 62 | var openModalBtn = document.getElementById("uploadFile"); 63 | openModalBtn.addEventListener('click', () => { 64 | modal.style.display = "block"; 65 | }); 66 | var closeModalSpan = document.getElementsByClassName("closeModalSpan")[0]; 67 | closeModalSpan.addEventListener('click', () => { 68 | modal.style.display = "none"; 69 | }); 70 | uploadForm.addEventListener('submit', () => { 71 | var uploadForm = document.getElementById("uploadForm"); 72 | var filename = uploadForm.elements[0].value; 73 | if (filename && filename.length > 0) { 74 | var loader = document.getElementById("loader"); 75 | loader.style.visibility = "visible"; 76 | } 77 | }); 78 | var hiddenUploadFrame = document.getElementById("hiddenUploadFrame"); 79 | hiddenUploadFrame.addEventListener('load', () => { 80 | var hiddenUploadFrame = document.getElementById("hiddenUploadFrame"); 81 | var json = JSON.parse(hiddenUploadFrame.contentDocument.body.innerText); 82 | var fileName = json.fileName; 83 | var loader = document.getElementById("loader"); 84 | loader.style.visibility = "hidden"; 85 | modal.style.display = "none"; 86 | addToTranscript("File", "Uploaded file : " + fileName + " ("+ json.fileSize + " bytes)"); 87 | }); 88 | }; 89 | 90 | window.addEventListener('load', initUIEvents); -------------------------------------------------------------------------------- /ai/chat-ui-with-rag/src/main/resources/static/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: white; 3 | color: black; 4 | font-family:Arial, Helvetica, sans-serif; 5 | } 6 | 7 | div.loader{ 8 | position: absolute; 9 | width: 100px; 10 | height: 100px; 11 | top: 50%; 12 | left: 50%; 13 | transform: translate(-50%,-50%); 14 | visibility: hidden; 15 | } 16 | 17 | svg.circular{ 18 | animation: rotate 2s linear infinite; 19 | height: 100px; 20 | position: relative; 21 | width: 100px; 22 | } 23 | 24 | circle.path { 25 | stroke-dasharray: 1,200; 26 | stroke-dashoffset: 0; 27 | stroke:#B6463A; 28 | animation: 29 | dash 1.5s ease-in-out infinite, 30 | color 6s ease-in-out infinite; 31 | stroke-linecap: round; 32 | } 33 | 34 | @keyframes rotate{ 35 | 100%{ 36 | transform: rotate(360deg); 37 | } 38 | } 39 | @keyframes dash{ 40 | 0%{ 41 | stroke-dasharray: 1,200; 42 | stroke-dashoffset: 0; 43 | } 44 | 50%{ 45 | stroke-dasharray: 89,200; 46 | stroke-dashoffset: -35; 47 | } 48 | 100%{ 49 | stroke-dasharray: 89,200; 50 | stroke-dashoffset: -124; 51 | } 52 | } 53 | @keyframes color{ 54 | 100%, 0%{ 55 | stroke: red; 56 | } 57 | 40%{ 58 | stroke: blue; 59 | } 60 | 66%{ 61 | stroke: green; 62 | } 63 | 80%, 90%{ 64 | stroke: yellow; 65 | } 66 | } 67 | 68 | 69 | div#chatArea { 70 | position:absolute; 71 | top: 0px; 72 | bottom: 0px; 73 | width: 90%; 74 | display: table; 75 | } 76 | 77 | div#header { 78 | display: table-row; 79 | height: 50px; 80 | } 81 | 82 | div.chatDiv { 83 | display: table-row; 84 | border: 1px solid green; 85 | } 86 | 87 | textarea#userInput { 88 | /* position: absolute; */ 89 | max-height: 20vh; 90 | height: 20vh; 91 | width: 90%; 92 | border: 2px solid forestgreen; 93 | border-radius: 25px; 94 | font-size: 1rem; 95 | margin-top: 10px; 96 | padding-left: 20px; 97 | padding-right: 100px; 98 | resize: none; 99 | } 100 | 101 | textarea#userInput:focus { 102 | outline: none; 103 | } 104 | 105 | div#transcript { 106 | padding-left: 20px; 107 | padding-right: 20px; 108 | margin-left: 10px; 109 | margin-right:20px; 110 | background-color: #f3f6fb; 111 | max-height: 53vh; 112 | height: 53vh; 113 | border-radius: 25px; 114 | font-size: 1rem; 115 | overflow-y: scroll; 116 | text-transform: capitalize; 117 | } 118 | 119 | div.UserEntry { 120 | padding-top: 10px; 121 | padding-bottom: 10px; 122 | } 123 | 124 | div.AIEntry { 125 | padding-top: 10px; 126 | padding-bottom: 10px; 127 | } 128 | 129 | div.FileEntry { 130 | padding-top: 10px; 131 | padding-bottom: 10px; 132 | padding-left: 20px; 133 | padding-right: 20px; 134 | background-color: lightgray; 135 | border-radius: 20px; 136 | margin-left: 50vw; 137 | margin-right: 30px; 138 | } 139 | 140 | button { 141 | background-color: #f3f6fb; 142 | border: 2px solid forestgreen; 143 | font-size: 17px; 144 | border-radius: 25px; 145 | margin-top: 20px; 146 | } 147 | 148 | div.modal { 149 | display: none; 150 | position: fixed; 151 | z-index: 1; 152 | padding-top: 100px; 153 | left: 0; 154 | top: 0; 155 | width: 100%; 156 | height: 100%; 157 | overflow: auto; 158 | background-color: rgb(0,0,0); 159 | background-color: rgba(0,0,0,0.4); 160 | } 161 | 162 | div.modal-content { 163 | background-color: #fefefe; 164 | margin: auto; 165 | padding: 20px; 166 | border: 1px solid #888; 167 | width: 80%; 168 | } 169 | 170 | .closeModalSpan { 171 | color: #aaa; 172 | float: right; 173 | font-size: 28px; 174 | font-weight: bold; 175 | } 176 | 177 | .closeModalSpan:hover, 178 | .closeModalSpan:focus { 179 | color: black; 180 | text-decoration: none; 181 | cursor: pointer; 182 | } -------------------------------------------------------------------------------- /ai/chat-ui-with-rag/src/main/resources/templates/home.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | AI Chat Demo 6 | 7 | 8 | 11 | 12 | 13 | 14 | 15 | 30 |
31 | 34 | 35 |
36 |
37 | 38 |
39 | 40 |
41 | 42 | 43 | 44 |
45 | 46 | -------------------------------------------------------------------------------- /ai/chat-ui-with-rag/src/main/resources/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | AI Chat Demo 6 | 7 | 8 | 9 |

Login

10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
User:
Password:
24 |
25 | 26 | -------------------------------------------------------------------------------- /ai/embedding-model/README.md: -------------------------------------------------------------------------------- 1 | # Spring AI EmbeddingModel 2 | 3 | In Spring AI Vector Embedding tutorial, learn what is a vector or embedding, how it helps in semantic searches, and how to generate embeddings using popular LLM models such as OpenAI and Mistral. 4 | 5 | ## Related Tutorials 6 | 7 | - [Spring AI Vector Embedding Example](https://howtodoinjava.com/spring-ai/vector-embedding-example/) -------------------------------------------------------------------------------- /ai/embedding-model/openai/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.howtodoinjava.ai.demo 7 | embedding-model 8 | 1.0.0 9 | 10 | 11 | openai 12 | jar 13 | openai 14 | http://maven.apache.org 15 | 16 | 17 | 18 | 19 | org.springframework.ai 20 | spring-ai-openai-spring-boot-starter 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ai/embedding-model/openai/src/main/java/com/howtodoinjava/demo/App.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class App { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(App.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ai/embedding-model/openai/src/main/java/com/howtodoinjava/demo/EmbeddingController.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.demo; 2 | 3 | import java.util.Arrays; 4 | import org.springframework.ai.embedding.EmbeddingModel; 5 | import org.springframework.ai.embedding.EmbeddingRequest; 6 | import org.springframework.ai.openai.OpenAiEmbeddingOptions; 7 | import org.springframework.web.bind.annotation.PostMapping; 8 | import org.springframework.web.bind.annotation.RequestBody; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | import java.util.List; 12 | 13 | @RestController 14 | class EmbeddingController { 15 | 16 | private final EmbeddingModel embeddingModel; 17 | 18 | EmbeddingController(EmbeddingModel embeddingModel) { 19 | this.embeddingModel = embeddingModel; 20 | } 21 | 22 | @PostMapping("/embed") 23 | Response embed(@RequestBody String message) { 24 | 25 | var embeddings = embeddingModel.embed(message); 26 | return new Response(embeddings.size(), embeddings); 27 | } 28 | 29 | @PostMapping("/embed/openai-options") 30 | String embedWithOpenAiOptions(@RequestBody String message) { 31 | 32 | if (message == null || message.length() == 0) { 33 | return "Empty content for embedding"; 34 | } 35 | 36 | var embeddings = embeddingModel.call( 37 | new EmbeddingRequest(List.of(message), OpenAiEmbeddingOptions.builder() 38 | .withModel("text-embedding-3-small") 39 | .withUser("jon.snow") 40 | .build())) 41 | .getResult().getOutput(); 42 | return "Size of the embedding vector: " + embeddings.size(); 43 | } 44 | 45 | /*@PostMapping("/embed/ollama-options") 46 | String embedWithOllamaOptions(@RequestBody String message) { 47 | 48 | if (message == null || message.length() == 0) { 49 | return "Empty content for embedding"; 50 | } 51 | 52 | var embeddings = embeddingModel.call( 53 | new EmbeddingRequest(List.of(message), OllamaOptions.create() 54 | .withModel("mistral"))) 55 | .getResult().getOutput(); 56 | return "Size of the embedding vector: " + embeddings.size(); 57 | }*/ 58 | 59 | /*@PostMapping("/embed/ollama-options") 60 | String embedWithMistralAiOptions(@RequestBody String message) { 61 | 62 | if (message == null || message.length() == 0) { 63 | return "Empty content for embedding"; 64 | } 65 | 66 | var embeddings = embeddingModel.call( 67 | new EmbeddingRequest(List.of(message), MistralAiEmbeddingOptions.builder() 68 | .withModel("mistral-embed").build())) 69 | .getResult().getOutput(); 70 | return "Size of the embedding vector: " + embeddings.size(); 71 | }*/ 72 | 73 | } 74 | 75 | record Response(int dimension, List vector){ 76 | } 77 | -------------------------------------------------------------------------------- /ai/embedding-model/openai/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.ai.openai.api-key=${OPENAI_API_KEY} 2 | -------------------------------------------------------------------------------- /ai/embedding-model/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.howtodoinjava.ai.demo 7 | ai 8 | 1.0.0 9 | 10 | 11 | embedding-model 12 | pom 13 | embedding-model 14 | 15 | 16 | openai 17 | 18 | 19 | 20 | 21 | org.springframework.ai 22 | spring-ai-openai-spring-boot-starter 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ai/embedding-transformer/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.howtodoinjava.ai.demo 7 | ai 8 | 1.0.0 9 | 10 | 11 | embedding-transformer 12 | jar 13 | embedding-transformer 14 | 15 | 16 | 17 | org.springframework.ai 18 | spring-ai-transformers-spring-boot-starter 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /ai/embedding-transformer/src/main/java/com/howtodoinjava/ai/demo/App.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class App { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(App.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ai/embedding-transformer/src/main/java/com/howtodoinjava/ai/demo/EmbeddingController.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.ai.embedding.EmbeddingModel; 4 | import org.springframework.web.bind.annotation.PostMapping; 5 | import org.springframework.web.bind.annotation.RequestBody; 6 | import org.springframework.web.bind.annotation.RestController; 7 | 8 | @RestController 9 | class EmbeddingController { 10 | 11 | private final EmbeddingModel embeddingModel; 12 | 13 | EmbeddingController(EmbeddingModel embeddingModel) { 14 | this.embeddingModel = embeddingModel; 15 | } 16 | 17 | @PostMapping("/embed") 18 | String embed(@RequestBody String message) { 19 | var embeddings = embeddingModel.embed(message); 20 | return "Size of the embedding vector: " + embeddings.size(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ai/embedding-transformer/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.ai.openai.api-key=${OPENAI_API_KEY} 2 | spring.ai.embedding.transformer.onnx.modelUri=https://huggingface.co/intfloat/e5-small-v2/resolve/main/model.onnx 3 | spring.ai.embedding.transformer.tokenizer.uri=https://huggingface.co/intfloat/e5-small-v2/raw/main/tokenizer.json -------------------------------------------------------------------------------- /ai/embedding-transformer/src/test/java/com/howtodoinjava/ai/demo/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class AppTest { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | } -------------------------------------------------------------------------------- /ai/function-callback/README.md: -------------------------------------------------------------------------------- 1 | # Function Calling with Spring AI 2 | 3 | ## Related Tutorials 4 | 5 | - [Spring AI Function Calling with Spring Boot](https://howtodoinjava.com/spring-ai/function-calling-example/) -------------------------------------------------------------------------------- /ai/function-callback/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | 7 | com.howtodoinjava.ai.demo 8 | ai 9 | 1.0.0 10 | 11 | 12 | function-callback 13 | jar 14 | function-callback 15 | http://maven.apache.org 16 | 17 | 18 | 19 | org.springframework.ai 20 | spring-ai-openai-spring-boot-starter 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /ai/function-callback/src/main/java/com/howtodoinjava/ai/demo/ChatController.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.web.bind.annotation.GetMapping; 4 | import org.springframework.web.bind.annotation.RequestParam; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | @RestController 8 | public class ChatController { 9 | 10 | private final ChatService chatService; 11 | 12 | ChatController(ChatService chatService) { 13 | this.chatService = chatService; 14 | } 15 | 16 | @GetMapping("/chat/function") 17 | String chat(@RequestParam String stockName) { 18 | return chatService.getPriceByStockName(stockName); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ai/function-callback/src/main/java/com/howtodoinjava/ai/demo/ChatService.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import org.springframework.ai.chat.client.ChatClient; 6 | import org.springframework.ai.chat.messages.Message; 7 | import org.springframework.ai.chat.model.ChatModel; 8 | import org.springframework.ai.chat.model.ChatResponse; 9 | import org.springframework.ai.chat.prompt.Prompt; 10 | import org.springframework.ai.chat.prompt.PromptTemplate; 11 | import org.springframework.ai.openai.OpenAiChatOptions; 12 | import org.springframework.stereotype.Service; 13 | 14 | @Service 15 | public class ChatService { 16 | 17 | private final ChatClient chatClient; 18 | private final ChatModel chatModel; 19 | 20 | 21 | ChatService(ChatClient.Builder chatClientBuilder, 22 | ChatModel chatModel) { 23 | this.chatClient = chatClientBuilder.build(); 24 | this.chatModel = chatModel; 25 | } 26 | 27 | String getPriceByStockName(String stockName) { 28 | var userPromptTemplate = "Get the latest price for {stockName}."; 29 | 30 | PromptTemplate promptTemplate = new PromptTemplate(userPromptTemplate); 31 | Message message = promptTemplate.createMessage(Map.of("stockName", stockName)); 32 | 33 | ChatResponse response = chatModel.call( 34 | new Prompt(List.of(message), OpenAiChatOptions.builder() 35 | .withFunction("priceByStockNameFunction").build())); 36 | 37 | return response.getResult().getOutput().getContent(); 38 | 39 | /*return chatClient.prompt() 40 | .user(userSpec -> userSpec 41 | .text(userPromptTemplate) 42 | .param("stockName", stockName) 43 | ) 44 | .functions("priceByStockNameFunction") 45 | .call() 46 | .content();*/ 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ai/function-callback/src/main/java/com/howtodoinjava/ai/demo/Functions.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import com.howtodoinjava.ai.demo.StockPriceService.Stock; 4 | import java.util.function.Function; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.context.annotation.Description; 8 | 9 | @Configuration(proxyBeanMethods = false) 10 | public class Functions { 11 | 12 | @Bean 13 | @Description("Get quote by stock name") 14 | public Function priceByStockNameFunction(StockPriceService stockPriceService) { 15 | return stockPriceService::getStockPrice; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ai/function-callback/src/main/java/com/howtodoinjava/ai/demo/SpringAiFunctionCallbackApplication.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringAiFunctionCallbackApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringAiFunctionCallbackApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ai/function-callback/src/main/java/com/howtodoinjava/ai/demo/StockPriceService.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import java.util.Map; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | import org.springframework.stereotype.Service; 6 | 7 | @Service 8 | public class StockPriceService { 9 | 10 | private static final Map data = new ConcurrentHashMap<>(); 11 | 12 | static { 13 | data.put(new Stock("Google"), 101.00); 14 | data.put(new Stock("Microsoft"), 100.00); 15 | data.put(new Stock("Tesla"), 103.00); 16 | data.put(new Stock("OpenAI"), 104.00); 17 | data.put(new Stock("HDFC"), 105.00); 18 | } 19 | 20 | Double getStockPrice(Stock stock) { 21 | return data.keySet().stream() 22 | .filter(s -> s.name().equalsIgnoreCase(stock.name())) 23 | .map(s -> data.get(s)) 24 | .findFirst() 25 | .orElse(-1.0); 26 | } 27 | 28 | public record Stock(String name) { 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ai/function-callback/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.ai.openai.api-key=${OPENAI_API_KEY} 2 | -------------------------------------------------------------------------------- /ai/function-callback/src/test/java/com/howtodoinjava/ai/demo/SpringAiFunctionCallbackApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | public class SpringAiFunctionCallbackApplicationTest { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ai/function-callback/src/test/java/com/howtodoinjava/ai/demo/TestFunctionCalling.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.test.context.TestConfiguration; 5 | 6 | @TestConfiguration(proxyBeanMethods = false) 7 | public class TestFunctionCalling { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.from(SpringAiFunctionCallbackApplication::main) 11 | .with(SpringAiFunctionCallbackApplicationTest.class).run(args); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ai/image-gen/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.howtodoinjava.ai.demo 7 | ai 8 | 1.0.0 9 | 10 | 11 | image-gen 12 | jar 13 | image-gen 14 | 15 | 16 | 17 | org.springframework.ai 18 | spring-ai-openai-spring-boot-starter 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /ai/image-gen/src/main/java/com/howtodoinjava/ai/demo/AppConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.ai.image.ImageModel; 4 | import org.springframework.ai.openai.OpenAiImageModel; 5 | import org.springframework.ai.openai.api.OpenAiImageApi; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | @Configuration 11 | public class AppConfiguration { 12 | 13 | @Value("${spring.ai.openai.api-key}") 14 | String apiKey; 15 | 16 | @Bean 17 | ImageModel imageModel() { 18 | return new OpenAiImageModel(new OpenAiImageApi(apiKey)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ai/image-gen/src/main/java/com/howtodoinjava/ai/demo/ImageGenController.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.PostMapping; 5 | import org.springframework.web.bind.annotation.RequestBody; 6 | 7 | @Controller 8 | public class ImageGenController { 9 | 10 | private final ImageGenService imageGenService; 11 | 12 | public ImageGenController(ImageGenService imageGenService) { 13 | this.imageGenService = imageGenService; 14 | } 15 | 16 | @PostMapping("/generate-image") 17 | public String imageGen(@RequestBody ImageGenRequest request) { 18 | 19 | String imageUrl = imageGenService.imageGen(request); 20 | return "redirect:" + imageUrl; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ai/image-gen/src/main/java/com/howtodoinjava/ai/demo/ImageGenRequest.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | public record ImageGenRequest(String prompt) { 4 | } 5 | -------------------------------------------------------------------------------- /ai/image-gen/src/main/java/com/howtodoinjava/ai/demo/ImageGenService.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.ai.image.ImageModel; 4 | import org.springframework.ai.image.ImageOptions; 5 | import org.springframework.ai.image.ImageOptionsBuilder; 6 | import org.springframework.ai.image.ImagePrompt; 7 | import org.springframework.ai.image.ImageResponse; 8 | import org.springframework.stereotype.Service; 9 | 10 | @Service 11 | public class ImageGenService { 12 | 13 | private final ImageModel imageModel; 14 | 15 | public ImageGenService(ImageModel imageModel) { 16 | this.imageModel = imageModel; 17 | } 18 | 19 | public String imageGen(ImageGenRequest request) { 20 | ImageOptions options = ImageOptionsBuilder.builder().withModel("dall-e-3").build(); 21 | 22 | ImagePrompt imagePrompt = new ImagePrompt(request.prompt(), options); 23 | ImageResponse response = imageModel.call(imagePrompt); 24 | String imageUrl = response.getResult().getOutput().getUrl(); 25 | return imageUrl; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ai/image-gen/src/main/java/com/howtodoinjava/ai/demo/SpringAiImageGenApplication.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringAiImageGenApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringAiImageGenApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ai/image-gen/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.ai.openai.api-key=${OPENAI_API_KEY} 2 | -------------------------------------------------------------------------------- /ai/image-gen/src/test/java/com/howtodoinjava/ai/demo/ImageGenControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import static org.mockito.Mockito.when; 4 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; 5 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; 6 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 7 | 8 | import org.junit.jupiter.api.Test; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 11 | import org.springframework.boot.test.mock.mockito.MockBean; 12 | import org.springframework.http.MediaType; 13 | import org.springframework.test.web.servlet.MockMvc; 14 | 15 | @WebMvcTest(ImageGenController.class) 16 | class ImageGenControllerTest { 17 | 18 | @Autowired 19 | private MockMvc mockMvc; 20 | 21 | @MockBean 22 | private ImageGenService imageGenService; 23 | 24 | @Test 25 | void testImageGen() throws Exception { 26 | 27 | String prompt = "test-prompt"; 28 | String imageUrl = "test-url"; 29 | ImageGenRequest request = new ImageGenRequest(prompt); 30 | when(imageGenService.imageGen(request)).thenReturn(imageUrl); 31 | 32 | mockMvc.perform(post("/generate-image") 33 | .contentType(MediaType.APPLICATION_JSON) 34 | .content("{\"prompt\": \"test-prompt\"}")) 35 | .andExpect(status().is3xxRedirection()) 36 | .andExpect(redirectedUrl("test-url")); 37 | } 38 | } -------------------------------------------------------------------------------- /ai/image-gen/src/test/java/com/howtodoinjava/ai/demo/SpringAiImageGenApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | public class SpringAiImageGenApplicationTest { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ai/multi-model/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.howtodoinjava.ai.demo 7 | ai 8 | 1.0.0 9 | 10 | 11 | multi-model 12 | jar 13 | multi-model 14 | http://maven.apache.org 15 | 16 | 17 | UTF-8 18 | 19 | 20 | 21 | 22 | org.springframework.ai 23 | spring-ai-openai-spring-boot-starter 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ai/multi-model/src/main/java/com/howtodoinjava/ai/demo/AiController.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.ai.chat.client.ChatClient; 4 | import org.springframework.core.io.ByteArrayResource; 5 | import org.springframework.util.MimeTypeUtils; 6 | import org.springframework.web.bind.annotation.PostMapping; 7 | import org.springframework.web.bind.annotation.RequestBody; 8 | import org.springframework.web.bind.annotation.RequestParam; 9 | import org.springframework.web.bind.annotation.RestController; 10 | import org.springframework.web.multipart.MultipartFile; 11 | 12 | @RestController 13 | public class AiController { 14 | 15 | private final ChatClient chatClient; 16 | 17 | public AiController(ChatClient.Builder chatClientBuilder) { 18 | this.chatClient = chatClientBuilder.build(); 19 | } 20 | 21 | @PostMapping("/ask") 22 | public Response ask(@RequestParam("image") MultipartFile imageFile, 23 | @RequestBody Request request) throws Exception { 24 | 25 | ByteArrayResource imageResource = new ByteArrayResource(imageFile.getBytes()); 26 | 27 | var answer = chatClient.prompt() 28 | .user(userSpec -> userSpec 29 | .text(request.query()) 30 | .media(MimeTypeUtils.IMAGE_JPEG, imageResource)) 31 | .call() 32 | .content(); 33 | return new Response(answer); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ai/multi-model/src/main/java/com/howtodoinjava/ai/demo/MultiModelApplication.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class MultiModelApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(MultiModelApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ai/multi-model/src/main/java/com/howtodoinjava/ai/demo/Request.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | public record Request(String query) { 4 | } 5 | -------------------------------------------------------------------------------- /ai/multi-model/src/main/java/com/howtodoinjava/ai/demo/Response.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | public record Response(String answer) { 4 | } 5 | -------------------------------------------------------------------------------- /ai/multi-model/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.ai.openai.api-key=${OPENAI_API_KEY} 2 | spring.ai.openai.chat.options.model=gpt-4o 3 | -------------------------------------------------------------------------------- /ai/multi-model/src/test/java/com/howtodoinjava/ai/demo/MultiModelApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class MultiModelApplicationTest { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ai/ollama-local-setup/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.howtodoinjava.ai.demo 7 | ai 8 | 1.0.0 9 | 10 | 11 | ollama-local-setup 12 | jar 13 | ollama-local-setup 14 | http://maven.apache.org 15 | 16 | 17 | 18 | org.springframework.ai 19 | spring-ai-ollama-spring-boot-starter 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /ai/ollama-local-setup/src/main/java/com/howtodoinjava/ai/demo/App.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import java.util.List; 4 | import javax.swing.Spring; 5 | import org.springframework.ai.chat.model.ChatResponse; 6 | import org.springframework.ai.chat.prompt.Prompt; 7 | import org.springframework.ai.ollama.OllamaChatModel; 8 | import org.springframework.ai.ollama.api.OllamaApi; 9 | import org.springframework.ai.ollama.api.OllamaApi.ChatRequest; 10 | import org.springframework.ai.ollama.api.OllamaApi.Message; 11 | import org.springframework.ai.ollama.api.OllamaApi.Message.Role; 12 | import org.springframework.ai.ollama.api.OllamaModel; 13 | import org.springframework.ai.ollama.api.OllamaOptions; 14 | import org.springframework.beans.factory.annotation.Autowired; 15 | import org.springframework.beans.factory.annotation.Value; 16 | import org.springframework.boot.CommandLineRunner; 17 | import org.springframework.boot.SpringApplication; 18 | import org.springframework.boot.autoconfigure.SpringBootApplication; 19 | import org.springframework.context.annotation.Bean; 20 | 21 | @SpringBootApplication 22 | public class App implements CommandLineRunner { 23 | 24 | public static void main(String[] args) { 25 | SpringApplication.run(App.class); 26 | } 27 | 28 | @Autowired 29 | OllamaChatModel chatModel; 30 | 31 | @Override 32 | public void run(String... args) throws Exception { 33 | 34 | /*ChatResponse response = chatModel.call( 35 | new Prompt( 36 | "Generate the names of 5 famous pirates.", 37 | OllamaOptions.create() 38 | .withModel("gemma2") 39 | .withTemperature(0.4F) 40 | ));*/ 41 | 42 | chatModel.stream(new Prompt( 43 | "Generate the names of 5 famous pirates.", 44 | OllamaOptions.create() 45 | .withModel("gemma2") 46 | .withTemperature(0.4F) 47 | )).subscribe(chatResponse -> { 48 | System.out.print(chatResponse.getResult().getOutput().getContent()); 49 | }); 50 | 51 | /*response.getResults() 52 | .stream() 53 | .map(generation -> generation.getOutput().getContent()) 54 | .forEach(System.out::println);*/ 55 | } 56 | 57 | /*@Bean 58 | OllamaChatModel ollamaChatModel(@Value("spring.ai.ollama.base-url") String baseUrl) { 59 | return new OllamaChatModel(new OllamaApi(baseUrl), 60 | OllamaOptions.create() 61 | .withModel("gemma") 62 | .withTemperature(0.4f)); 63 | }*/ 64 | } 65 | 66 | -------------------------------------------------------------------------------- /ai/ollama-local-setup/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.ai.ollama.base-url=http://localhost:11434 2 | spring.ai.ollama.chat.options.model=gemma 3 | spring.ai.ollama.chat.options.temperature=0.4 -------------------------------------------------------------------------------- /ai/ollama-local-setup/src/test/java/com/howtodoinjava/ai/demo/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class AppTest { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ai/output-converters/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.howtodoinjava.ai.demo 7 | ai 8 | 1.0.0 9 | 10 | 11 | output-converters 12 | jar 13 | output-converters 14 | http://maven.apache.org 15 | 16 | 17 | 18 | org.springframework.ai 19 | spring-ai-openai-spring-boot-starter 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ai/output-converters/src/main/java/com/howtodoinjava/ai/demo/App.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class App { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(App.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ai/output-converters/src/main/java/com/howtodoinjava/ai/demo/ChatController.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.web.bind.annotation.PostMapping; 4 | import org.springframework.web.bind.annotation.RequestBody; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | @RestController 11 | class ChatController { 12 | 13 | private final ChatService chatService; 14 | 15 | ChatController(ChatService chatService) { 16 | this.chatService = chatService; 17 | } 18 | 19 | @PostMapping("/chat/bean") 20 | PlayerInfo chatWithBeanOutput(@RequestBody Question question) { 21 | return chatService.chatWithBeanOutput(question); 22 | } 23 | 24 | @PostMapping("/chat/map") 25 | Map chatWithMapOutput(@RequestBody Question question) { 26 | return chatService.chatWithMapOutput(question); 27 | } 28 | 29 | @PostMapping("/chat/list") 30 | List chatWithListOutput(@RequestBody Question question) { 31 | return chatService.chatWithListOutput(question); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /ai/output-converters/src/main/java/com/howtodoinjava/ai/demo/ChatService.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.ai.chat.client.ChatClient; 4 | import org.springframework.ai.converter.ListOutputConverter; 5 | import org.springframework.ai.converter.MapOutputConverter; 6 | import org.springframework.ai.openai.OpenAiChatOptions; 7 | import org.springframework.ai.openai.api.OpenAiApi; 8 | import org.springframework.core.convert.support.DefaultConversionService; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | @Service 15 | class ChatService { 16 | 17 | private final ChatClient chatClient; 18 | 19 | ChatService(ChatClient.Builder chatClientBuilder) { 20 | this.chatClient = chatClientBuilder.build(); 21 | } 22 | 23 | PlayerInfo chatWithBeanOutput(Question question) { 24 | var userPromptTemplate = """ 25 | Tell me name and team of one player famous for playing the {sportName}. 26 | Consider only the players that play for the {country}. 27 | """; 28 | 29 | return chatClient.prompt() 30 | .user(userSpec -> userSpec 31 | .text(userPromptTemplate) 32 | .param("sportName", question.sportName()) 33 | .param("country", question.country()) 34 | ) 35 | .options(OpenAiChatOptions.builder() 36 | .withResponseFormat(new OpenAiApi.ChatCompletionRequest.ResponseFormat("json_object")) 37 | .build()) 38 | .call() 39 | .entity(PlayerInfo.class); 40 | } 41 | 42 | Map chatWithMapOutput(Question question) { 43 | var outputConverter = new MapOutputConverter(); 44 | 45 | var userPromptTemplate = """ 46 | Tell me the name of three players famous for playing the {sportName}. 47 | Consider only the players that play for the {country}. 48 | {format} 49 | """; 50 | 51 | var result = chatClient.prompt() 52 | .user(userSpec -> userSpec 53 | .text(userPromptTemplate) 54 | .param("sportName", question.sportName()) 55 | .param("country", question.country()) 56 | .param("format", outputConverter.getFormat()) 57 | ) 58 | .call() 59 | .content(); 60 | return outputConverter.convert(result); 61 | } 62 | 63 | List chatWithListOutput(Question question) { 64 | var outputConverter = new ListOutputConverter(new DefaultConversionService()); 65 | 66 | var userPromptTemplate = """ 67 | Tell me the name of three players famous for playing the {sportName}. 68 | Consider only the players that play for the {country}. 69 | {format} 70 | """; 71 | 72 | var result = chatClient.prompt() 73 | .user(userSpec -> userSpec 74 | .text(userPromptTemplate) 75 | .param("sportName", question.sportName()) 76 | .param("country", question.country()) 77 | .param("format", outputConverter.getFormat()) 78 | ) 79 | .call() 80 | .content(); 81 | return outputConverter.convert(result); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /ai/output-converters/src/main/java/com/howtodoinjava/ai/demo/PlayerInfo.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | public record PlayerInfo(String name, String team) { 4 | } 5 | -------------------------------------------------------------------------------- /ai/output-converters/src/main/java/com/howtodoinjava/ai/demo/Question.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | public record Question(String sportName, String country) { 4 | } 5 | -------------------------------------------------------------------------------- /ai/output-converters/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.ai.openai.api-key=${OPENAI_API_KEY} 2 | -------------------------------------------------------------------------------- /ai/output-converters/src/test/java/com/howtodoinjava/ai/demo/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class AppTest { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ai/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.howtodoinjava.ai.demo 7 | spring-ai-examples 8 | 1.0.0 9 | 10 | 11 | ai 12 | pom 13 | 1.0.0 14 | ai 15 | 16 | 17 | function-callback 18 | image-gen 19 | multi-model 20 | prompt-template 21 | speech-to-text 22 | text-to-speech 23 | structured-data-extraction 24 | output-converters 25 | qa-advisor 26 | usecases 27 | similarity-search 28 | embedding-model 29 | embedding-transformer 30 | ollama-local-setup 31 | chat-ui-with-rag 32 | vector-stores 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /ai/prompt-template/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | 7 | com.howtodoinjava.ai.demo 8 | ai 9 | 1.0.0 10 | 11 | 12 | prompt-template 13 | jar 14 | prompt-template 15 | http://maven.apache.org 16 | 17 | 18 | 19 | org.springframework.ai 20 | spring-ai-openai-spring-boot-starter 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ai/prompt-template/src/main/java/com/howtodoinjava/ai/demo/demo/App.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class App { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(App.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ai/prompt-template/src/main/java/com/howtodoinjava/ai/demo/demo/QueryController.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo.demo; 2 | 3 | import org.springframework.ai.chat.client.ChatClient; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.core.io.Resource; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.RequestParam; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | @RestController 11 | public class QueryController { 12 | 13 | private final ChatClient chatClient; 14 | private final QueryService queryService; 15 | 16 | @Value("classpath:/query-template.st") 17 | private Resource queryTemplate; 18 | 19 | public QueryController(ChatClient.Builder chatClientBuilder, QueryService queryService) { 20 | this.chatClient = chatClientBuilder.build(); 21 | this.queryService = queryService; 22 | } 23 | 24 | @GetMapping("/explain") 25 | public QueryResponse query(@RequestParam("subject") String subject) { 26 | return queryService.query(subject); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ai/prompt-template/src/main/java/com/howtodoinjava/ai/demo/demo/QueryResponse.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo.demo; 2 | 3 | public record QueryResponse(String subject, String explanation) { 4 | } 5 | -------------------------------------------------------------------------------- /ai/prompt-template/src/main/java/com/howtodoinjava/ai/demo/demo/QueryService.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo.demo; 2 | 3 | import java.util.Map; 4 | import org.springframework.ai.chat.client.ChatClient; 5 | import org.springframework.ai.chat.messages.Message; 6 | import org.springframework.ai.chat.prompt.PromptTemplate; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.core.io.Resource; 9 | import org.springframework.stereotype.Service; 10 | 11 | @Service 12 | public class QueryService { 13 | 14 | private final ChatClient chatClient; 15 | 16 | @Value("classpath:/query-template.st") 17 | private Resource queryTemplate; 18 | 19 | public QueryService(ChatClient.Builder chatClientBuilder) { 20 | this.chatClient = chatClientBuilder.build(); 21 | } 22 | 23 | public QueryResponse query(String subject) { 24 | 25 | return chatClient.prompt() 26 | .user(userSpec -> userSpec 27 | .text(queryTemplate) 28 | .param("subject", subject)) 29 | .call() 30 | .entity(QueryResponse.class); 31 | } 32 | 33 | public QueryResponse query_v2(String subject) { 34 | 35 | PromptTemplate promptTemplate = new PromptTemplate(queryTemplate); 36 | Message message = promptTemplate.createMessage(Map.of("subject", subject)); 37 | 38 | return chatClient.prompt() 39 | .user(message.getContent()) 40 | .call() 41 | .entity(QueryResponse.class); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ai/prompt-template/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.ai.openai.api-key=${OPENAI_API_KEY} 2 | app.promptTemplate=Tell me a joke about {subject}. 3 | -------------------------------------------------------------------------------- /ai/prompt-template/src/main/resources/query-template.st: -------------------------------------------------------------------------------- 1 | Explain {subject} to a 10 yr old kid in simple words. 2 | -------------------------------------------------------------------------------- /ai/prompt-template/src/test/java/com/howtodoinjava/ai/demo/demo/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class AppTest { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ai/prompt-template/src/test/java/com/howtodoinjava/ai/demo/demo/QueryControllerTest.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo.demo; 2 | 3 | import static org.mockito.ArgumentMatchers.anyString; 4 | import static org.mockito.Mockito.when; 5 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 6 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; 7 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 8 | 9 | import org.junit.jupiter.api.Test; 10 | import org.springframework.ai.chat.client.ChatClient; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 13 | import org.springframework.boot.test.mock.mockito.MockBean; 14 | import org.springframework.test.web.servlet.MockMvc; 15 | 16 | @WebMvcTest(QueryController.class) 17 | public class QueryControllerTest { 18 | 19 | @Autowired 20 | private MockMvc mockMvc; 21 | 22 | @MockBean 23 | private QueryService queryService; 24 | 25 | @MockBean 26 | private ChatClient.Builder builder; 27 | 28 | @Test 29 | public void testQuery() throws Exception { 30 | 31 | String subject = "testSubject"; 32 | String explanation = "testExplanation"; 33 | QueryResponse mockResponse = new QueryResponse("Explanation for : " + subject, explanation); 34 | when(queryService.query(anyString())).thenReturn(mockResponse); 35 | 36 | mockMvc.perform(get("/explain") 37 | .param("subject", subject)) 38 | .andExpect(status().isOk()) 39 | .andExpect(jsonPath("$.explanation").value(mockResponse.explanation())); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ai/qa-advisor/README.md: -------------------------------------------------------------------------------- 1 | # Spring AI VectorStore 2 | 3 | In Spring AI, the role of a vector database is to store vector embeddings and facilitate similarity searches for these embeddings. A vector store does not generate the embeddings itself. For creating vector embeddings, the EmbeddingModel should be utilized. 4 | 5 | ## Related Tutorials 6 | 7 | - [Storing and Querying Embeddings in Spring VectorStore](https://howtodoinjava.com/spring-ai/vector-store-example/) -------------------------------------------------------------------------------- /ai/qa-advisor/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3.9' 2 | 3 | networks: 4 | net: 5 | driver: bridge 6 | services: 7 | server: 8 | image: ghcr.io/chroma-core/chroma:latest 9 | environment: 10 | - IS_PERSISTENT=TRUE 11 | volumes: 12 | - chroma-data:/chroma/chroma/ 13 | ports: 14 | - 8000:8000 15 | networks: 16 | - net 17 | 18 | volumes: 19 | chroma-data: 20 | driver: local 21 | -------------------------------------------------------------------------------- /ai/qa-advisor/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.howtodoinjava.ai.demo 7 | ai 8 | 1.0.0 9 | 10 | 11 | qa-advisor 12 | jar 13 | qa-advisor 14 | http://maven.apache.org 15 | 16 | 17 | 18 | org.springframework.ai 19 | spring-ai-openai-spring-boot-starter 20 | 21 | 22 | org.springframework.ai 23 | spring-ai-chroma-store-spring-boot-starter 24 | 25 | 26 | org.springframework.ai 27 | spring-ai-tika-document-reader 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-docker-compose 32 | runtime 33 | true 34 | 35 | 36 | org.testcontainers 37 | junit-jupiter 38 | test 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-testcontainers 43 | test 44 | 45 | 46 | org.springframework.ai 47 | spring-ai-spring-boot-testcontainers 48 | test 49 | 50 | 51 | org.testcontainers 52 | chromadb 53 | test 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /ai/qa-advisor/src/main/java/com/howtodoinjava/ai/demo/Answer.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | public record Answer(String answer) { 4 | } 5 | -------------------------------------------------------------------------------- /ai/qa-advisor/src/main/java/com/howtodoinjava/ai/demo/App.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import java.util.List; 4 | import org.springframework.ai.document.Document; 5 | import org.springframework.ai.vectorstore.VectorStore; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.CommandLineRunner; 8 | import org.springframework.boot.SpringApplication; 9 | import org.springframework.boot.autoconfigure.SpringBootApplication; 10 | 11 | @SpringBootApplication 12 | public class App implements CommandLineRunner { 13 | 14 | public static void main(String[] args) { 15 | SpringApplication.run(App.class, args); 16 | } 17 | 18 | @Autowired 19 | VectorStore vectorStore; 20 | 21 | @Override 22 | public void run(String... args) throws Exception { 23 | System.out.println("Running query..."); 24 | List documents = vectorStore.similaritySearch("investigation"); 25 | documents.stream().forEach(System.out::println); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ai/qa-advisor/src/main/java/com/howtodoinjava/ai/demo/QAController.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.ai.chat.model.ChatResponse; 4 | import org.springframework.web.bind.annotation.PostMapping; 5 | import org.springframework.web.bind.annotation.RequestBody; 6 | import org.springframework.web.bind.annotation.RestController; 7 | 8 | @RestController 9 | public class QAController { 10 | 11 | private final QAService qaService; 12 | 13 | public QAController(QAService qaService) { 14 | this.qaService = qaService; 15 | } 16 | 17 | @PostMapping("/ask") 18 | public Answer ask(@RequestBody Question question) { 19 | ChatResponse chatResponse = qaService.askQuestion(question.question()); 20 | return new Answer(chatResponse.getResult().getOutput().getContent()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ai/qa-advisor/src/main/java/com/howtodoinjava/ai/demo/QAService.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.ai.chat.client.ChatClient; 4 | import org.springframework.ai.chat.client.advisor.QuestionAnswerAdvisor; 5 | import org.springframework.ai.chat.model.ChatResponse; 6 | import org.springframework.ai.vectorstore.SearchRequest; 7 | import org.springframework.ai.vectorstore.VectorStore; 8 | import org.springframework.stereotype.Service; 9 | 10 | @Service 11 | public class QAService { 12 | 13 | private final ChatClient chatClient; 14 | 15 | public QAService(ChatClient.Builder chatClientBuilder, VectorStore vectorStore) { 16 | this.chatClient = chatClientBuilder 17 | .defaultAdvisors(new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults())) 18 | .build(); 19 | } 20 | 21 | public ChatResponse askQuestion(String question) { 22 | return chatClient 23 | .prompt() 24 | .user(question) 25 | .call() 26 | .chatResponse(); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /ai/qa-advisor/src/main/java/com/howtodoinjava/ai/demo/Question.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | public record Question(String question) { 4 | } 5 | -------------------------------------------------------------------------------- /ai/qa-advisor/src/main/java/com/howtodoinjava/ai/demo/VectorStoreLoader.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import java.nio.charset.Charset; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import org.springframework.ai.document.Document; 7 | import org.springframework.ai.reader.TextReader; 8 | import org.springframework.ai.reader.tika.TikaDocumentReader; 9 | import org.springframework.ai.transformer.splitter.TokenTextSplitter; 10 | import org.springframework.ai.vectorstore.VectorStore; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.beans.factory.annotation.Value; 13 | import org.springframework.boot.ApplicationArguments; 14 | import org.springframework.boot.ApplicationRunner; 15 | import org.springframework.core.io.Resource; 16 | import org.springframework.stereotype.Component; 17 | 18 | //@Component 19 | public class VectorStoreLoader implements ApplicationRunner { 20 | 21 | @Value("classpath:/CallingRates.pdf") 22 | Resource pdfResource; 23 | 24 | @Value("classpath:/story.txt") 25 | Resource txtResource; 26 | 27 | @Value("classpath:/story.md") 28 | Resource mdResource; 29 | 30 | @Autowired 31 | VectorStore vectorStore; 32 | 33 | @Override 34 | public void run(ApplicationArguments args) throws Exception { 35 | 36 | List documents = new ArrayList<>(); 37 | 38 | TikaDocumentReader reader = new TikaDocumentReader(pdfResource); 39 | documents.addAll(reader.get()); 40 | vectorStore.add(documents); 41 | 42 | var textReader1 = new TextReader(txtResource); 43 | textReader1.setCharset(Charset.defaultCharset()); 44 | documents.addAll(textReader1.get()); 45 | 46 | var textReader2 = new TextReader(mdResource); 47 | textReader2.setCharset(Charset.defaultCharset()); 48 | documents.addAll(textReader2.get()); 49 | 50 | vectorStore.add(new TokenTextSplitter(300, 300, 5, 1000, true).split(documents)); 51 | 52 | System.out.println("Added documents to vector store"); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ai/qa-advisor/src/main/resources/CallingRates.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokeshgupta1981/spring-ai-examples/182edc364610399dc41508e99bb605ba642b451f/ai/qa-advisor/src/main/resources/CallingRates.pdf -------------------------------------------------------------------------------- /ai/qa-advisor/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.ai.openai.api-key=${OPENAI_API_KEY} 2 | spring.docker.compose.file=./ai/qa-advisor/docker-compose.yaml 3 | spring.docker.compose.lifecycle-management=start-only 4 | spring.ai.openai.chat.options.model=gpt-4o 5 | spring.ai.openai.embedding.options.model=text-embedding-3-small 6 | 7 | logging.level.org.springframework.ai.chat.client.advisor=TRACE 8 | -------------------------------------------------------------------------------- /ai/qa-advisor/src/main/resources/story.txt: -------------------------------------------------------------------------------- 1 | To Sherlock Holmes she is always the woman. I have seldom heard him 2 | mention her under any other name. In his eyes she eclipses and 3 | predominates the whole of her sex. It was not that he felt any 4 | emotion akin to love for Irene Adler. All emotions, and that one 5 | particularly, were abhorrent to his cold, precise but admirably 6 | balanced mind. He was, I take it, the most perfect reasoning and 7 | observing machine that the world has seen, but as a lover he would 8 | have placed himself in a false position. He never spoke of the softer 9 | passions, save with a gibe and a sneer. They were admirable things 10 | for the observer--excellent for drawing the veil from men's motives 11 | and actions. But for the trained reasoner to admit such intrusions 12 | into his own delicate and finely adjusted temperament was to 13 | introduce a distracting factor which might throw a doubt upon all his 14 | mental results. Grit in a sensitive instrument, or a crack in one of 15 | his own high-power lenses, would not be more disturbing than a strong 16 | emotion in a nature such as his. And yet there was but one woman to 17 | him, and that woman was the late Irene Adler, of dubious and 18 | questionable memory. 19 | 20 | I had seen little of Holmes lately. My marriage had drifted us away 21 | from each other. My own complete happiness, and the home-centred 22 | interests which rise up around the man who first finds himself master 23 | of his own establishment, were sufficient to absorb all my attention, 24 | while Holmes, who loathed every form of society with his whole 25 | Bohemian soul, remained in our lodgings in Baker Street, buried among 26 | his old books, and alternating from week to week between cocaine and 27 | ambition, the drowsiness of the drug, and the fierce energy of his 28 | own keen nature. He was still, as ever, deeply attracted by the study 29 | of crime, and occupied his immense faculties and extraordinary powers 30 | of observation in following out those clues, and clearing up those 31 | mysteries which had been abandoned as hopeless by the official 32 | police. From time to time I heard some vague account of his doings: 33 | of his summons to Odessa in the case of the Trepoff murder, of his 34 | clearing up of the singular tragedy of the Atkinson brothers at 35 | Trincomalee, and finally of the mission which he had accomplished so 36 | delicately and successfully for the reigning family of Holland. 37 | Beyond these signs of his activity, however, which I merely shared 38 | with all the readers of the daily press, I knew little of my former 39 | friend and companion. 40 | 41 | One night--it was on the twentieth of March, 1888--I was returning 42 | from a journey to a patient (for I had now returned to civil 43 | practice), when my way led me through Baker Street. As I passed the 44 | well-remembered door, which must always be associated in my mind with 45 | my wooing, and with the dark incidents of the Study in Scarlet, I was 46 | seized with a keen desire to see Holmes again, and to know how he was 47 | employing his extraordinary powers. His rooms were brilliantly lit, 48 | and, even as I looked up, I saw his tall, spare figure pass twice in 49 | a dark silhouette against the blind. He was pacing the room swiftly, 50 | eagerly, with his head sunk upon his chest and his hands clasped 51 | behind him. To me, who knew his every mood and habit, his attitude 52 | and manner told their own story. He was at work again. He had risen 53 | out of his drug-created dreams and was hot upon the scent of some new 54 | problem. I rang the bell and was shown up to the chamber which had 55 | formerly been in part my own. 56 | 57 | His manner was not effusive. It seldom was; but he was glad, I think, 58 | to see me. With hardly a word spoken, but with a kindly eye, he waved 59 | me to an armchair, threw across his case of cigars, and indicated a 60 | spirit case and a gasogene in the corner. Then he stood before the 61 | fire and looked me over in his singular introspective fashion. 62 | 63 | "Wedlock suits you," he remarked. "I think, Watson, that you have put 64 | on seven and a half pounds since I saw you." 65 | 66 | "Seven!" I answered. 67 | 68 | "Indeed, I should have thought a little more. Just a trifle more, I 69 | fancy, Watson. And in practice again, I observe. You did not tell me 70 | that you intended to go into harness." 71 | 72 | "Then, how do you know?" 73 | 74 | "I see it, I deduce it. How do I know that you have been getting 75 | yourself very wet lately, and that you have a most clumsy and 76 | careless servant girl?" 77 | 78 | "My dear Holmes," said I, "this is too much. You would certainly have 79 | been burned, had you lived a few centuries ago. It is true that I had 80 | a country walk on Thursday and came home in a dreadful mess, but as I 81 | have changed my clothes I can't imagine how you deduce it. As to Mary 82 | Jane, she is incorrigible, and my wife has given her notice, but 83 | there, again, I fail to see how you work it out." 84 | 85 | He chuckled to himself and rubbed his long, nervous hands together. 86 | 87 | "It is simplicity itself," said he; "my eyes tell me that on the 88 | inside of your left shoe, just where the firelight strikes it, the 89 | leather is scored by six almost parallel cuts. Obviously they have 90 | been caused by someone who has very carelessly scraped round the 91 | edges of the sole in order to remove crusted mud from it. Hence, you 92 | see, my double deduction that you had been out in vile weather, and 93 | that you had a particularly malignant boot-slitting specimen of the 94 | London slavey. As to your practice, if a gentleman walks into my 95 | rooms smelling of iodoform, with a black mark of nitrate of silver 96 | upon his right forefinger, and a bulge on the right side of his 97 | top-hat to show where he has secreted his stethoscope, I must be 98 | dull, indeed, if I do not pronounce him to be an active member of the 99 | medical profession." 100 | 101 | I could not help laughing at the ease with which he explained his 102 | process of deduction. "When I hear you give your reasons," I 103 | remarked, "the thing always appears to me to be so ridiculously 104 | simple that I could easily do it myself, though at each successive 105 | instance of your reasoning I am baffled until you explain your 106 | process. And yet I believe that my eyes are as good as yours." 107 | 108 | "Quite so," he answered, lighting a cigarette, and throwing himself 109 | down into an armchair. "You see, but you do not observe. The 110 | distinction is clear. For example, you have frequently seen the steps 111 | which lead up from the hall to this room." -------------------------------------------------------------------------------- /ai/qa-advisor/src/test/java/com/howtodoinjava/ai/demo/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.junit.jupiter.api.Disabled; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | @SpringBootTest 8 | @Disabled 9 | class AppTest { 10 | 11 | @Test 12 | void contextLoads() { 13 | } 14 | } -------------------------------------------------------------------------------- /ai/qa-advisor/src/test/java/com/howtodoinjava/ai/demo/EvaluatorTests.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertTrue; 4 | 5 | import java.util.List; 6 | import org.junit.jupiter.api.Disabled; 7 | import org.junit.jupiter.api.Test; 8 | import org.springframework.ai.chat.client.ChatClient; 9 | import org.springframework.ai.chat.client.advisor.QuestionAnswerAdvisor; 10 | import org.springframework.ai.chat.model.ChatModel; 11 | import org.springframework.ai.chat.model.ChatResponse; 12 | import org.springframework.ai.evaluation.EvaluationRequest; 13 | import org.springframework.ai.evaluation.EvaluationResponse; 14 | import org.springframework.ai.evaluation.RelevancyEvaluator; 15 | import org.springframework.ai.model.Content; 16 | import org.springframework.ai.openai.OpenAiChatModel; 17 | import org.springframework.beans.factory.annotation.Autowired; 18 | import org.springframework.boot.test.context.SpringBootTest; 19 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection; 20 | import org.testcontainers.chromadb.ChromaDBContainer; 21 | import org.testcontainers.junit.jupiter.Container; 22 | import org.testcontainers.junit.jupiter.Testcontainers; 23 | 24 | @SpringBootTest 25 | @Testcontainers(disabledWithoutDocker = true) 26 | @Disabled 27 | public class EvaluatorTests { 28 | 29 | @Container 30 | @ServiceConnection 31 | static ChromaDBContainer chroma = new ChromaDBContainer("ghcr.io/chroma-core/chroma:latest"); 32 | 33 | @Autowired 34 | OpenAiChatModel chatModel; 35 | 36 | @Autowired 37 | QAService qaService; 38 | 39 | @Test 40 | void testEvaluation() { 41 | String question = "What is the grave digger card?"; 42 | ChatResponse response = qaService.askQuestion(question); 43 | String responseContent = response.getResult().getOutput().getContent(); 44 | var relevancyEvaluator = new RelevancyEvaluator(ChatClient.builder(chatModel)); 45 | 46 | @SuppressWarnings("unchecked") 47 | List responseDocuments = 48 | (List) response.getMetadata().get(QuestionAnswerAdvisor.RETRIEVED_DOCUMENTS); 49 | 50 | EvaluationRequest evaluationRequest = new EvaluationRequest(question, 51 | responseDocuments, response.getResult().getOutput().getContent()); 52 | 53 | EvaluationResponse evaluationResponse = relevancyEvaluator.evaluate(evaluationRequest); 54 | 55 | assertTrue(evaluationResponse.isPass(), 56 | "Response is not relevant to the asked question.\n" + 57 | "Question: " + question + "\n" + 58 | "Response: " + responseContent); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /ai/similarity-search/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.howtodoinjava.ai.demo 7 | ai 8 | 1.0.0 9 | 10 | 11 | similarity-search 12 | jar 13 | 14 | similarity-search 15 | http://maven.apache.org 16 | 17 | 18 | UTF-8 19 | 20 | 21 | 22 | 23 | org.springframework.ai 24 | spring-ai-openai-spring-boot-starter 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /ai/similarity-search/src/main/java/com/howtodoinjava/ai/demo/App.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class App { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(App.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ai/similarity-search/src/main/java/com/howtodoinjava/ai/demo/AppConfig.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.ai.embedding.EmbeddingModel; 4 | import org.springframework.ai.vectorstore.SimpleVectorStore; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | @Configuration 9 | public class AppConfig { 10 | 11 | @Bean 12 | SimpleVectorStore vectorStore(EmbeddingModel embeddingModel) { 13 | return new SimpleVectorStore(embeddingModel); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ai/similarity-search/src/main/java/com/howtodoinjava/ai/demo/IngestionPipeline.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import jakarta.annotation.PostConstruct; 4 | import java.util.List; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.ai.document.Document; 8 | import org.springframework.ai.vectorstore.VectorStore; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Component 12 | public class IngestionPipeline { 13 | 14 | private static final Logger logger = LoggerFactory.getLogger(IngestionPipeline.class); 15 | private final VectorStore vectorStore; 16 | 17 | public IngestionPipeline(VectorStore vectorStore) { 18 | this.vectorStore = vectorStore; 19 | } 20 | 21 | @PostConstruct 22 | public void run() { 23 | var instrumentNotes = List.of( 24 | new LogRecord("INFO - Processing request for user authentication."), 25 | new LogRecord("DEBUG - Validating user credentials against the database."), 26 | new LogRecord("WARN - User authentication failed due to invalid credentials."), 27 | new LogRecord("ERROR - Failed to connect to external service. Retrying..."), 28 | new LogRecord("INFO - Successfully processed payment transaction."), 29 | new LogRecord("DEBUG - Fetching data from backend API."), 30 | new LogRecord("WARN - Unauthorized access attempt detected."), 31 | new LogRecord("ERROR - Database connection timed out."), 32 | new LogRecord("INFO - Starting server initialization."), 33 | new LogRecord("DEBUG - Loading configuration settings."), 34 | new LogRecord("WARN - Resource not found. Returning 404."), 35 | new LogRecord("ERROR - Unexpected server error occurred."), 36 | new LogRecord("INFO - Sending email notification."), 37 | new LogRecord("DEBUG - Parsing JSON response from API."), 38 | new LogRecord("WARN - Deprecated API endpoint accessed."), 39 | new LogRecord("ERROR - Out of memory error. Consider optimizing resources."), 40 | new LogRecord("INFO - User registration successful."), 41 | new LogRecord("DEBUG - Executing batch job."), 42 | new LogRecord("WARN - Insufficient permissions for this operation."), 43 | new LogRecord("ERROR - Failed to process request due to unknown error.") 44 | ); 45 | 46 | logger.info("Creating InstrumentNotes as Documents"); 47 | List documents = instrumentNotes.stream() 48 | .map(note -> new Document(note.content())) 49 | .toList(); 50 | 51 | logger.info("Creating and storing Embeddings from Documents"); 52 | vectorStore.add(documents); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ai/similarity-search/src/main/java/com/howtodoinjava/ai/demo/LogRecord.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | public record LogRecord(String content) { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /ai/similarity-search/src/main/java/com/howtodoinjava/ai/demo/SemanticSearchController.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.web.bind.annotation.PostMapping; 4 | import org.springframework.web.bind.annotation.RequestBody; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | import java.util.List; 8 | 9 | @RestController 10 | class SemanticSearchController { 11 | 12 | private final SemanticSearchService semanticSearchService; 13 | 14 | SemanticSearchController(SemanticSearchService semanticSearchService) { 15 | this.semanticSearchService = semanticSearchService; 16 | } 17 | 18 | @PostMapping("/semantic-search") 19 | List semanticSearch(@RequestBody String query) { 20 | return semanticSearchService.semanticSearch(query); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /ai/similarity-search/src/main/java/com/howtodoinjava/ai/demo/SemanticSearchService.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import java.util.List; 4 | import org.springframework.ai.openai.OpenAiAudioSpeechModel; 5 | import org.springframework.ai.openai.OpenAiEmbeddingModel; 6 | import org.springframework.ai.openai.OpenAiImageModel; 7 | import org.springframework.ai.vectorstore.SearchRequest; 8 | import org.springframework.ai.vectorstore.VectorStore; 9 | import org.springframework.ai.vectorstore.filter.Filter; 10 | import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder; 11 | import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder.Op; 12 | import org.springframework.stereotype.Service; 13 | 14 | @Service 15 | public class SemanticSearchService { 16 | 17 | private final VectorStore vectorStore; 18 | 19 | SemanticSearchService(VectorStore vectorStore) { 20 | this.vectorStore = vectorStore; 21 | } 22 | 23 | List semanticSearch(String query) { 24 | 25 | // With default parameters 26 | SearchRequest searchRequest = SearchRequest.query(query); 27 | 28 | // Customize parameters 29 | /* SearchRequest searchRequest = SearchRequest.defaults() 30 | .withTopK(10) 31 | .withFilterExpression(new FilterExpressionBuilder().eq("LEVEL", "INFO").build()) 32 | .withSimilarityThreshold(0.5);*/ 33 | 34 | return vectorStore.similaritySearch(SearchRequest.query(query)) 35 | .stream() 36 | .map(document -> new LogRecord(document.getContent())) 37 | .toList(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ai/similarity-search/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.ai.openai.api-key=${OPENAI_API_KEY} 2 | -------------------------------------------------------------------------------- /ai/similarity-search/src/test/java/com/howtodoinjava/ai/demo/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class AppTest { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ai/speech-to-text/README.md: -------------------------------------------------------------------------------- 1 | # Spring AI Audio Transcription Example 2 | 3 | Spring AI currently supports only OpenAI's whisper model for speech transcription to JSON or TEXT files using ```OpenAiAudioTranscriptionModel``` class. 4 | 5 | ## Related Tutorials 6 | 7 | - [Spring AI Speech to Text Example](https://howtodoinjava.com/spring-ai/transcription-speech-to-text/) -------------------------------------------------------------------------------- /ai/speech-to-text/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.howtodoinjava.ai.demo 7 | ai 8 | 1.0.0 9 | 10 | 11 | speech-to-text 12 | jar 13 | speech-to-text 14 | http://maven.apache.org 15 | 16 | 17 | 18 | org.springframework.ai 19 | spring-ai-openai-spring-boot-starter 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /ai/speech-to-text/src/main/java/com/howtodoinjava/ai/demo/App.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class App { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(App.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ai/speech-to-text/src/main/java/com/howtodoinjava/ai/demo/TranscriptionController.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.ai.audio.transcription.AudioTranscriptionPrompt; 4 | import org.springframework.ai.openai.OpenAiAudioTranscriptionModel; 5 | import org.springframework.ai.openai.OpenAiAudioTranscriptionOptions; 6 | import org.springframework.ai.openai.api.OpenAiAudioApi.TranscriptResponseFormat; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.core.io.Resource; 9 | import org.springframework.web.bind.annotation.GetMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | @RestController 13 | class TranscriptionController { 14 | 15 | private final OpenAiAudioTranscriptionModel transcriptionModel; 16 | 17 | TranscriptionController(OpenAiAudioTranscriptionModel transcriptionModel) { 18 | this.transcriptionModel = transcriptionModel; 19 | } 20 | 21 | @GetMapping("/transcription") 22 | String speech(@Value("classpath:speech.mp3") Resource audioFile) { 23 | return transcriptionModel.call(new AudioTranscriptionPrompt(audioFile)) 24 | .getResult() 25 | .getOutput(); 26 | } 27 | 28 | @GetMapping("/transcription_v2") 29 | String speech_v2(@Value("classpath:speech.mp3") Resource audioFile) { 30 | 31 | var transcriptionResponse = transcriptionModel 32 | .call(new AudioTranscriptionPrompt(audioFile, OpenAiAudioTranscriptionOptions.builder() 33 | .withLanguage("en") 34 | .withPrompt("Create transcription for this audio file.") 35 | .withTemperature(0f) 36 | .withResponseFormat(TranscriptResponseFormat.TEXT) 37 | .build())); 38 | return transcriptionResponse.getResult().getOutput(); 39 | } 40 | } -------------------------------------------------------------------------------- /ai/speech-to-text/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.ai.openai.api-key=${OPENAI_API_KEY} 2 | spring.ai.openai.audio.transcription.options.model=whisper-1 3 | -------------------------------------------------------------------------------- /ai/speech-to-text/src/main/resources/speech.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokeshgupta1981/spring-ai-examples/182edc364610399dc41508e99bb605ba642b451f/ai/speech-to-text/src/main/resources/speech.mp3 -------------------------------------------------------------------------------- /ai/speech-to-text/src/test/java/com/howtodoinjava/ai/demo/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class AppTest { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ai/structured-data-extraction/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.howtodoinjava.ai.demo 7 | ai 8 | 1.0.0 9 | 10 | 11 | structured-data-extraction 12 | jar 13 | structured-data-extraction 14 | http://maven.apache.org 15 | 16 | 17 | 18 | org.springframework.ai 19 | spring-ai-openai-spring-boot-starter 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ai/structured-data-extraction/src/main/java/com/howtodoinjava/ai/demo/App.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class App { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(App.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ai/structured-data-extraction/src/main/java/com/howtodoinjava/ai/demo/HealthRecord.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import java.util.List; 4 | 5 | public record HealthRecord(String fullName, List observations, Diagnosis diagnosis) { 6 | public record Observation(String type, String content) {} 7 | public record Diagnosis(String content) {} 8 | } 9 | -------------------------------------------------------------------------------- /ai/structured-data-extraction/src/main/java/com/howtodoinjava/ai/demo/HealthRecordController.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.web.bind.annotation.PostMapping; 4 | import org.springframework.web.bind.annotation.RequestBody; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | @RestController 8 | public class HealthRecordController { 9 | 10 | private final HealthRecordService healthRecordService; 11 | 12 | HealthRecordController(HealthRecordService healthRecordService) { 13 | this.healthRecordService = healthRecordService; 14 | } 15 | 16 | @PostMapping("/extract-health-record") 17 | HealthRecord extract(@RequestBody String prompt) { 18 | return healthRecordService.extract(prompt); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ai/structured-data-extraction/src/main/java/com/howtodoinjava/ai/demo/HealthRecordService.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.ai.chat.client.ChatClient; 4 | import org.springframework.ai.chat.prompt.ChatOptionsBuilder; 5 | import org.springframework.stereotype.Service; 6 | 7 | @Service 8 | public class HealthRecordService { 9 | 10 | private final ChatClient chatClient; 11 | 12 | HealthRecordService(ChatClient.Builder chatClientBuilder) { 13 | this.chatClient = chatClientBuilder 14 | .defaultOptions(ChatOptionsBuilder.builder() 15 | .withTemperature(0.0f) 16 | .build()) 17 | .build(); 18 | } 19 | 20 | HealthRecord extract(String message) { 21 | return chatClient 22 | .prompt() 23 | .user(userSpec -> userSpec.text(""" 24 | Extract structured data from the provided text. 25 | If you do not know the value of a field asked to extract, 26 | do not include any value for the field in the result. 27 | Finally, save the object in the database. 28 | 29 | --------------------- 30 | TEXT: 31 | {text} 32 | --------------------- 33 | """) 34 | .param("text", message)) 35 | .call() 36 | .entity(HealthRecord.class); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ai/structured-data-extraction/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.ai.openai.api-key=${OPENAI_API_KEY} 2 | -------------------------------------------------------------------------------- /ai/structured-data-extraction/src/test/java/com/howtodoinjava/ai/demo/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class AppTest { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ai/text-to-speech/README.md: -------------------------------------------------------------------------------- 1 | # Spring AI Text-to-Speech (TTS) 2 | 3 | In Spring AI, the SpeechModel and StreamingSpeechModel interfaces allow to interact with a Text-to-Speech (TTS) APIs of sopported LLMs such as tts-1 and tts-1-hd by OpenAI. and It allows us to convert text transcripts into lifelike spoken audio. 4 | 5 | ## Related Tutorials 6 | 7 | - [Spring AI SpeechModel: Text to Speech Example](https://howtodoinjava.com/spring-ai/tts-text-to-speech/) -------------------------------------------------------------------------------- /ai/text-to-speech/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.howtodoinjava.ai.demo 7 | ai 8 | 1.0.0 9 | 10 | 11 | text-to-speech 12 | jar 13 | text-to-speech 14 | 15 | 16 | 17 | org.springframework.ai 18 | spring-ai-openai-spring-boot-starter 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /ai/text-to-speech/src/main/java/com/howtodoinjava/ai/demo/SpeechController.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.ai.openai.OpenAiAudioSpeechModel; 4 | import org.springframework.ai.openai.OpenAiAudioSpeechOptions; 5 | import org.springframework.ai.openai.api.OpenAiAudioApi; 6 | import org.springframework.ai.openai.api.OpenAiAudioApi.SpeechRequest.AudioResponseFormat; 7 | import org.springframework.ai.openai.audio.speech.SpeechPrompt; 8 | import org.springframework.ai.openai.audio.speech.StreamingSpeechModel; 9 | import org.springframework.web.bind.annotation.CrossOrigin; 10 | import org.springframework.web.bind.annotation.GetMapping; 11 | import org.springframework.web.bind.annotation.PostMapping; 12 | import org.springframework.web.bind.annotation.RequestBody; 13 | import org.springframework.web.bind.annotation.RequestParam; 14 | import org.springframework.web.bind.annotation.RestController; 15 | import reactor.core.publisher.Flux; 16 | 17 | @RestController 18 | @CrossOrigin 19 | class SpeechController { 20 | 21 | private final OpenAiAudioSpeechModel speechModel; 22 | 23 | SpeechController(OpenAiAudioSpeechModel speechModel) { 24 | this.speechModel = speechModel; 25 | } 26 | 27 | @PostMapping("/speech") 28 | byte[] speech(@RequestBody String message) { 29 | 30 | return speechModel.call(new SpeechPrompt(message)) 31 | .getResult() 32 | .getOutput(); 33 | } 34 | 35 | @GetMapping("/stream-speech") 36 | Flux streamingSpeech(@RequestParam String message) { 37 | 38 | OpenAiAudioSpeechOptions speechOptions = OpenAiAudioSpeechOptions.builder() 39 | .withResponseFormat(AudioResponseFormat.OPUS) 40 | .build(); 41 | 42 | OpenAiAudioApi openAiAudioApi = new OpenAiAudioApi(System.getenv("OPENAI_API_KEY")); 43 | StreamingSpeechModel streamingSpeechModel = new OpenAiAudioSpeechModel(openAiAudioApi, speechOptions); 44 | 45 | return streamingSpeechModel.stream(message); 46 | } 47 | 48 | @PostMapping("/speech_v2") 49 | byte[] speechWithOpenAiOptions(@RequestBody String message) { 50 | 51 | /*OpenAiAudioSpeechOptions speechOptions = OpenAiAudioSpeechOptions.builder() 52 | .withModel(OpenAiAudioApi.TtsModel.TTS_1.getValue()) 53 | .withResponseFormat(AudioResponseFormat.MP3) 54 | .withVoice(OpenAiAudioApi.SpeechRequest.Voice.ALLOY) 55 | .withSpeed(1.0f) 56 | .build(); 57 | 58 | var openAiAudioApi = new OpenAiAudioApi(System.getenv("OPENAI_API_KEY")); 59 | SpeechModel openAiAudioSpeechModel = new OpenAiAudioSpeechModel(openAiAudioApi, speechOptions);*/ 60 | 61 | var speechResponse = speechModel 62 | .call(new SpeechPrompt(message, OpenAiAudioSpeechOptions.builder() 63 | .withModel("tts-1") 64 | .withVoice(OpenAiAudioApi.SpeechRequest.Voice.ALLOY) 65 | .withResponseFormat(OpenAiAudioApi.SpeechRequest.AudioResponseFormat.MP3) 66 | .withSpeed(1.0f) 67 | .build())); 68 | return speechResponse.getResult().getOutput(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /ai/text-to-speech/src/main/java/com/howtodoinjava/ai/demo/SpringAiTextToSpeechApp.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringAiTextToSpeechApp { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringAiTextToSpeechApp.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ai/text-to-speech/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.ai.openai.api-key=${OPENAI_API_KEY} 2 | spring.ai.openai.audio.speech.options.model=tts-1 3 | -------------------------------------------------------------------------------- /ai/text-to-speech/src/test/java/com/howtodoinjava/ai/demo/SpringAiTextToSpeechAppTest.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | public class SpringAiTextToSpeechAppTest { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | } -------------------------------------------------------------------------------- /ai/usecases/etp-pipeline-spring-ai/README.md: -------------------------------------------------------------------------------- 1 | # ETL Pipeline 2 | 3 | The Extract, Transform, and Load (ETL) pipeline using Spring AI. 4 | 5 | ## Related Tutorials 6 | 7 | - [Spring AI ETL Pipeline Example for Data Ingestion](https://howtodoinjava.com/spring-ai/etl-pipeline-example/) -------------------------------------------------------------------------------- /ai/usecases/etp-pipeline-spring-ai/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3.9' 2 | 3 | networks: 4 | net: 5 | driver: bridge 6 | services: 7 | server: 8 | image: ghcr.io/chroma-core/chroma:latest 9 | environment: 10 | - IS_PERSISTENT=TRUE 11 | volumes: 12 | - chroma-data:/chroma/chroma/ 13 | ports: 14 | - 8000:8000 15 | networks: 16 | - net 17 | 18 | volumes: 19 | chroma-data: 20 | driver: local 21 | -------------------------------------------------------------------------------- /ai/usecases/etp-pipeline-spring-ai/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.howtodoinjava.ai.demo 7 | usecases 8 | 1.0.0 9 | 10 | 11 | etp-pipeline-spring-ai 12 | jar 13 | 14 | etp-pipeline-spring-ai 15 | http://maven.apache.org 16 | 17 | 18 | 19 | org.springframework.ai 20 | spring-ai-tika-document-reader 21 | 22 | 23 | org.springframework.ai 24 | spring-ai-openai-spring-boot-starter 25 | 26 | 27 | org.springframework.ai 28 | spring-ai-chroma-store-spring-boot-starter 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-docker-compose 33 | runtime 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /ai/usecases/etp-pipeline-spring-ai/src/main/java/com/howtodoinjava/ai/demo/App.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.ai.document.DocumentReader; 4 | import org.springframework.ai.transformer.splitter.TextSplitter; 5 | import org.springframework.ai.transformer.splitter.TokenTextSplitter; 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.autoconfigure.SpringBootApplication; 8 | import org.springframework.context.annotation.Bean; 9 | 10 | @SpringBootApplication 11 | public class App { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(App.class, args); 15 | } 16 | } -------------------------------------------------------------------------------- /ai/usecases/etp-pipeline-spring-ai/src/main/java/com/howtodoinjava/ai/demo/AppConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.ai.transformer.splitter.TextSplitter; 4 | import org.springframework.ai.transformer.splitter.TokenTextSplitter; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | @Configuration 9 | public class AppConfiguration { 10 | 11 | @Bean 12 | TextSplitter textSplitter() { 13 | return new TokenTextSplitter(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ai/usecases/etp-pipeline-spring-ai/src/main/java/com/howtodoinjava/ai/demo/CustomDocumentReader.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import lombok.SneakyThrows; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.ai.document.Document; 12 | import org.springframework.ai.document.DocumentReader; 13 | import org.springframework.ai.reader.tika.TikaDocumentReader; 14 | import org.springframework.beans.factory.annotation.Value; 15 | import org.springframework.core.io.ByteArrayResource; 16 | import org.springframework.stereotype.Component; 17 | 18 | @Component 19 | public class CustomDocumentReader implements DocumentReader { 20 | 21 | private static final Logger LOGGER = LoggerFactory.getLogger(CustomDocumentReader.class); 22 | 23 | @Value("${input.directory}") 24 | private String inputDir; 25 | 26 | @Value("${input.filename.regex}") 27 | private String pattern; 28 | 29 | @SneakyThrows 30 | @Override 31 | public List get() { 32 | 33 | List documentList = new ArrayList<>(); 34 | TikaDocumentReader tikaDocumentReader; 35 | 36 | Files.newDirectoryStream(Path.of(inputDir), pattern).forEach(path -> { 37 | List documents = null; 38 | try { 39 | documents = new TikaDocumentReader(new ByteArrayResource(Files.readAllBytes(path))).get() 40 | .stream().peek(document -> { 41 | document.getMetadata().put("source", path.getFileName()); 42 | LOGGER.info("Reading new document :: {}", path.getFileName()); 43 | }).toList(); 44 | } catch (IOException e) { 45 | throw new RuntimeException("Error while reading the file : " + path.toUri() + "::" + e); 46 | } 47 | documentList.addAll(documents); 48 | }); 49 | return documentList; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /ai/usecases/etp-pipeline-spring-ai/src/main/java/com/howtodoinjava/ai/demo/EtlPipeline.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.ai.transformer.splitter.TextSplitter; 6 | import org.springframework.ai.vectorstore.VectorStore; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | public class EtlPipeline { 11 | 12 | private static final Logger LOGGER = LoggerFactory.getLogger(EtlPipeline.class); 13 | 14 | private final CustomDocumentReader documentReader; 15 | private final VectorStore vectorStore; 16 | private final TextSplitter textSplitter; 17 | 18 | public EtlPipeline(VectorStore vectorStore, 19 | TextSplitter textSplitter, 20 | CustomDocumentReader documentReader) { 21 | 22 | this.vectorStore = vectorStore; 23 | this.textSplitter = textSplitter; 24 | this.documentReader = documentReader; 25 | } 26 | 27 | public void runIngestion() { 28 | LOGGER.info("RunIngestion() started"); 29 | vectorStore.write(textSplitter.apply(documentReader.get())); 30 | LOGGER.info("RunIngestion() finished"); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ai/usecases/etp-pipeline-spring-ai/src/main/java/com/howtodoinjava/ai/demo/IngestionController.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.http.ResponseEntity; 4 | import org.springframework.web.bind.annotation.PostMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | @RestController 8 | public class IngestionController { 9 | 10 | EtlPipeline etlPipeline; 11 | 12 | public IngestionController(EtlPipeline etlPipeline){ 13 | this.etlPipeline = etlPipeline; 14 | } 15 | 16 | @PostMapping("run-ingestion") 17 | public ResponseEntity run(){ 18 | etlPipeline.runIngestion(); 19 | return ResponseEntity.accepted().build(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ai/usecases/etp-pipeline-spring-ai/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.ai.openai.api-key=${OPENAI_API_KEY} 2 | spring.docker.compose.file=./ai/usecases/etp-pipeline-spring-ai/docker-compose.yaml 3 | spring.docker.compose.lifecycle-management=start-only 4 | 5 | input.directory=c:/temp/ingestion-files/ 6 | input.filename.regex=*.{pdf,docx,txt,pages,csv} -------------------------------------------------------------------------------- /ai/usecases/etp-pipeline-spring-ai/src/test/java/com/howtodoinjava/ai/demo/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class AppTest { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /ai/usecases/generate-sql-queries/README.md: -------------------------------------------------------------------------------- 1 | # Generate SQL using GenAI and Spring AI 2 | 3 | ## Related Tutorials 4 | 5 | - [Query Relation Database using GenAI and Spring AI](https://howtodoinjava.com/spring-ai/generate-sql-with-ai/) -------------------------------------------------------------------------------- /ai/usecases/generate-sql-queries/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.howtodoinjava.ai.demo 7 | usecases 8 | 1.0.0 9 | ../pom.xml 10 | 11 | 12 | generate-sql-queries 13 | jar 14 | generate-sql-queries 15 | http://maven.apache.org 16 | 17 | 18 | 19 | org.springframework.ai 20 | spring-ai-openai-spring-boot-starter 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-jdbc 25 | 26 | 27 | com.h2database 28 | h2 29 | runtime 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /ai/usecases/generate-sql-queries/src/main/java/com/howtodoinjava/ai/demo/AiException.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | public class AiException extends RuntimeException { 4 | 5 | public AiException(String response) { 6 | super(response); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /ai/usecases/generate-sql-queries/src/main/java/com/howtodoinjava/ai/demo/AiRequest.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | public record AiRequest(String text) { 4 | } -------------------------------------------------------------------------------- /ai/usecases/generate-sql-queries/src/main/java/com/howtodoinjava/ai/demo/AiResponse.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | public record AiResponse(String sqlQuery, List> results) { } 7 | -------------------------------------------------------------------------------- /ai/usecases/generate-sql-queries/src/main/java/com/howtodoinjava/ai/demo/CustomExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.http.ProblemDetail; 5 | import org.springframework.web.bind.annotation.ExceptionHandler; 6 | import org.springframework.web.bind.annotation.RestControllerAdvice; 7 | 8 | @RestControllerAdvice 9 | public class CustomExceptionHandler { 10 | 11 | @ExceptionHandler(AiException.class) 12 | public ProblemDetail handle(AiException ex) { 13 | return ProblemDetail.forStatusAndDetail(HttpStatus.EXPECTATION_FAILED, ex.getMessage()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ai/usecases/generate-sql-queries/src/main/java/com/howtodoinjava/ai/demo/GenerateSqlWithAiApplication.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class GenerateSqlWithAiApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(GenerateSqlWithAiApplication.class, args); 11 | } 12 | } -------------------------------------------------------------------------------- /ai/usecases/generate-sql-queries/src/main/java/com/howtodoinjava/ai/demo/SqlController.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import java.io.IOException; 4 | import java.nio.charset.Charset; 5 | import org.springframework.ai.chat.client.ChatClient; 6 | import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.core.io.Resource; 9 | import org.springframework.jdbc.core.JdbcTemplate; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.RequestBody; 12 | import org.springframework.web.bind.annotation.RestController; 13 | 14 | @RestController 15 | public class SqlController { 16 | 17 | @Value("classpath:/schema.sql") 18 | private Resource ddlResource; 19 | 20 | @Value("classpath:/sql-prompt-template.st") 21 | private Resource sqlPromptTemplateResource; 22 | 23 | private final ChatClient aiClient; 24 | private final JdbcTemplate jdbcTemplate; 25 | 26 | public SqlController(ChatClient.Builder aiClientBuilder, JdbcTemplate jdbcTemplate) { 27 | this.aiClient = aiClientBuilder.build(); 28 | this.jdbcTemplate = jdbcTemplate; 29 | } 30 | 31 | @PostMapping(path = "/sql") 32 | public AiResponse sql(@RequestBody AiRequest request) throws IOException { 33 | String schema = ddlResource.getContentAsString(Charset.defaultCharset()); 34 | 35 | String query = aiClient.prompt() 36 | .advisors(new SimpleLoggerAdvisor()) 37 | .user(userSpec -> userSpec 38 | .text(sqlPromptTemplateResource) 39 | .param("question", request.text()) 40 | .param("ddl", schema) 41 | ) 42 | .call() 43 | .content(); 44 | 45 | if (query.toLowerCase().startsWith("select")) { 46 | return new AiResponse(query, jdbcTemplate.queryForList(query)); 47 | } 48 | throw new AiException(query); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ai/usecases/generate-sql-queries/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.ai.openai.api-key=${OPENAI_API_KEY} 2 | -------------------------------------------------------------------------------- /ai/usecases/generate-sql-queries/src/main/resources/data.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO TBL_USER (username, email, password) 2 | VALUES 3 | ('user1', 'user1@example.com', 'password1'), 4 | ('user2', 'user2@example.com', 'password2'), 5 | ('user3', 'user3@example.com', 'password3'), 6 | ('user4', 'user4@example.com', 'password4'), 7 | ('user5', 'user5@example.com', 'password5'), 8 | ('user6', 'user6@example.com', 'password6'), 9 | ('user7', 'user7@example.com', 'password7'), 10 | ('user8', 'user8@example.com', 'password8'), 11 | ('user9', 'user9@example.com', 'password9'), 12 | ('user10', 'user10@example.com', 'password10'); 13 | 14 | INSERT INTO TBL_ACCOUNT (accountNumber, user_id, balance, openDate) 15 | VALUES 16 | ('ACC001', 1, 1000.00, '2024-07-09'), 17 | ('ACC002', 1, 500.00, '2024-07-10'), 18 | ('ACC003', 2, 1500.00, '2024-07-09'), 19 | ('ACC004', 2, 200.00, '2024-07-10'), 20 | ('ACC005', 3, 800.00, '2024-07-09'), 21 | ('ACC006', 4, 3000.00, '2024-07-09'), 22 | ('ACC007', 4, 100.00, '2024-07-10'), 23 | ('ACC008', 5, 250.00, '2024-07-09'), 24 | ('ACC009', 6, 1800.00, '2024-07-09'), 25 | ('ACC010', 6, 700.00, '2024-07-10'), 26 | ('ACC011', 7, 500.00, '2024-07-09'), 27 | ('ACC012', 8, 1200.00, '2024-07-09'), 28 | ('ACC013', 9, 900.00, '2024-07-09'), 29 | ('ACC014', 9, 300.00, '2024-07-10'), 30 | ('ACC015', 10, 2000.00, '2024-07-09'); -------------------------------------------------------------------------------- /ai/usecases/generate-sql-queries/src/main/resources/schema.sql: -------------------------------------------------------------------------------- 1 | create table TBL_USER ( 2 | id int not null auto_increment, 3 | username varchar(255) not null, 4 | email varchar(255) not null, 5 | password varchar(255) not null, 6 | primary key (id) 7 | ); 8 | 9 | create table TBL_ACCOUNT ( 10 | id int not null auto_increment, 11 | accountNumber varchar(255) not null, 12 | user_id int not null, 13 | balance decimal(10, 2) not null, 14 | openDate date not null, 15 | primary key (id), 16 | foreign key (user_id) references TBL_USER(id) 17 | ); 18 | -------------------------------------------------------------------------------- /ai/usecases/generate-sql-queries/src/main/resources/sql-prompt-template.st: -------------------------------------------------------------------------------- 1 | Given the DDL in the DDL section, write an SQL query that answers the asked question in the QUESTION section. 2 | Only produce select queries. Do not append any text or markup in the start or end of response. 3 | Remove the markups such as ``` , sql , \n as well. 4 | If the question would result in an insert, update, or delete, or if the query would alter the DDL in any way, say that the operation isn't supported. 5 | If the question can't be answered, say that the DDL doesn't support answering that question. 6 | 7 | QUESTION 8 | {question} 9 | 10 | DDL 11 | {ddl} 12 | -------------------------------------------------------------------------------- /ai/usecases/generate-sql-queries/src/test/java/com/howtodoinjava/ai/demo/GenerateSqlWithAiApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | import org.junit.jupiter.api.Test; 3 | import org.springframework.boot.test.context.SpringBootTest; 4 | 5 | @SpringBootTest 6 | public class GenerateSqlWithAiApplicationTest { 7 | 8 | @Test 9 | void contextLoads() { 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /ai/usecases/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.howtodoinjava.ai.demo 7 | ai 8 | 1.0.0 9 | 10 | 11 | com.howtodoinjava.ai.demo 12 | usecases 13 | pom 14 | usecases 15 | http://maven.apache.org 16 | 17 | 18 | generate-sql-queries 19 | text-classifier 20 | vector-store-pipeline 21 | etp-pipeline-spring-ai 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /ai/usecases/text-classifier/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.howtodoinjava.ai.demo 7 | usecases 8 | 1.0.0 9 | ../pom.xml 10 | 11 | 12 | text-classifier 13 | jar 14 | text-classifier 15 | http://maven.apache.org 16 | 17 | 18 | 19 | org.springframework.ai 20 | spring-ai-openai-spring-boot-starter 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ai/usecases/text-classifier/src/main/java/com/howtodoinjava/ai/demo/App.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class App { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(App.class, args); 11 | } 12 | } -------------------------------------------------------------------------------- /ai/usecases/text-classifier/src/main/java/com/howtodoinjava/ai/demo/ClassificationController.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.web.bind.annotation.PostMapping; 4 | import org.springframework.web.bind.annotation.RequestBody; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | @RestController 8 | class ClassificationController { 9 | 10 | private final TextClassifierService textClassifierService; 11 | 12 | ClassificationController(TextClassifierService textClassifierService) { 13 | this.textClassifierService = textClassifierService; 14 | } 15 | 16 | @PostMapping("/classify") 17 | String classify(@RequestBody String text) { 18 | return textClassifierService.classifyEmotion(text); 19 | } 20 | 21 | @PostMapping("/classify/structured-output") 22 | ClassificationType classifyStructured(@RequestBody String text) { 23 | return textClassifierService.classifyEmotionStructured(text); 24 | } 25 | 26 | @PostMapping("/classify/with-hints") 27 | String classifyWithHints(@RequestBody String text) { 28 | return textClassifierService.classifyEmotionWithHints(text); 29 | } 30 | 31 | @PostMapping("/classify/few-shots-prompt") 32 | String classifyFewShotsPrompt(@RequestBody String text) { 33 | return textClassifierService.classifyEmotionWithFewShotsPrompt(text); 34 | } 35 | 36 | @PostMapping("/classify/few-shots-history") 37 | String classifyFewShotsHistory(@RequestBody String text) { 38 | return textClassifierService.classifyEmotionWithFewShotsHistory(text); 39 | } 40 | 41 | 42 | } -------------------------------------------------------------------------------- /ai/usecases/text-classifier/src/main/java/com/howtodoinjava/ai/demo/ClassificationType.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | public enum ClassificationType { 4 | POSITIVE, NEGATIVE, NEUTRAL 5 | } -------------------------------------------------------------------------------- /ai/usecases/text-classifier/src/main/java/com/howtodoinjava/ai/demo/TextClassifierService.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import java.util.List; 4 | import org.springframework.stereotype.Service; 5 | import org.springframework.ai.chat.client.ChatClient; 6 | import org.springframework.ai.chat.messages.AssistantMessage; 7 | import org.springframework.ai.chat.messages.Message; 8 | import org.springframework.ai.chat.messages.SystemMessage; 9 | import org.springframework.ai.chat.messages.UserMessage; 10 | import org.springframework.ai.chat.prompt.ChatOptionsBuilder; 11 | 12 | @Service 13 | public class TextClassifierService { 14 | 15 | private final ChatClient chatClient; 16 | 17 | TextClassifierService(ChatClient.Builder chatClientBuilder) { 18 | this.chatClient = chatClientBuilder 19 | .defaultOptions(ChatOptionsBuilder.builder() 20 | .withTemperature(0.0f) 21 | .build()) 22 | .build(); 23 | } 24 | 25 | String classifyEmotion(String text) { 26 | return chatClient 27 | .prompt() 28 | .system(""" 29 | Classify the provided text into one of these classes: 30 | POSITIVE, NEGATIVE, NEUTRAL 31 | """) 32 | .user(text) 33 | .call() 34 | .content(); 35 | } 36 | 37 | ClassificationType classifyEmotionStructured(String text) { 38 | return chatClient 39 | .prompt() 40 | .system(""" 41 | Classify the provided text into one of these classes: 42 | POSITIVE, NEGATIVE, NEUTRAL 43 | """) 44 | .user(text) 45 | .call() 46 | .entity(ClassificationType.class); 47 | } 48 | 49 | String classifyEmotionWithHints(String text) { 50 | return chatClient 51 | .prompt() 52 | .system(""" 53 | Classify the provided text into one of these classes. 54 | 55 | POSITIVE: Happy, excellent, reliable, durable, efficient, innovative, intuitive, exceptional, high-quality, impressive, user-friendly. 56 | NEGATIVE: Unhappy, sad, Defective, unreliable, disappointing, frustrating, complicated, poor-quality 57 | NEUTRAL: OK, ordinary, plain, regular, standard, average, common, typical, normal, indifferent 58 | """) 59 | .user(text) 60 | .call() 61 | .content(); 62 | } 63 | 64 | String classifyEmotionWithFewShotsPrompt(String text) { 65 | return chatClient 66 | .prompt() 67 | .system(""" 68 | Classify the provided text into one of these classes. 69 | 70 | POSITIVE: Happy, excellent, reliable, durable, efficient, innovative, intuitive, exceptional, high-quality, impressive, user-friendly. 71 | NEGATIVE: Unhappy, sad, Defective, unreliable, disappointing, frustrating, complicated, poor-quality 72 | NEUTRAL: OK, ordinary, plain, regular, standard, average, common, typical, normal, indifferent 73 | 74 | --- 75 | 76 | Text: The product is highly efficient and user-friendly. 77 | Class: POSITIVE 78 | 79 | Text: The service was disappointing and frustrating. 80 | Class: NEGATIVE 81 | 82 | Text: The performance is average, just as expected. 83 | Class: NEUTRAL 84 | 85 | Text: The software is intuitive and impressive. 86 | Class: POSITIVE 87 | 88 | Text: The device is defective and complicated to use. 89 | Class: NEGATIVE 90 | 91 | Text: The overall experience was plain and ordinary. 92 | Class: NEUTRAL 93 | """) 94 | .user(text) 95 | .call() 96 | .content(); 97 | } 98 | 99 | String classifyEmotionWithFewShotsHistory(String text) { 100 | return chatClient 101 | .prompt() 102 | .messages(getPromptWithFewShotsHistory()) 103 | .user(text) 104 | .call() 105 | .content(); 106 | } 107 | 108 | private List getPromptWithFewShotsHistory() { 109 | return List.of( 110 | new SystemMessage(""" 111 | Classify the provided text into one of these classes. 112 | 113 | POSITIVE: Happy, excellent, reliable, durable, efficient, innovative, intuitive, exceptional, high-quality, impressive, user-friendly. 114 | NEGATIVE: Unhappy, sad, Defective, unreliable, disappointing, frustrating, complicated, poor-quality 115 | NEUTRAL: OK, ordinary, plain, regular, standard, average, common, typical, normal, indifferent 116 | """), 117 | 118 | new UserMessage("The product is highly efficient and user-friendly."), 119 | new AssistantMessage("POSITIVE"), 120 | 121 | new UserMessage("The service was disappointing and frustrating."), 122 | new AssistantMessage("NEGATIVE"), 123 | 124 | new UserMessage("The performance is average, just as expected."), 125 | new AssistantMessage("NEUTRAL"), 126 | 127 | new UserMessage("The software is intuitive and impressive."), 128 | new AssistantMessage("POSITIVE"), 129 | 130 | new UserMessage("The device is defective and complicated to use."), 131 | new AssistantMessage("NEGATIVE"), 132 | 133 | new UserMessage("The overall experience was plain and ordinary."), 134 | new AssistantMessage("NEUTRAL") 135 | ); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /ai/usecases/text-classifier/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.ai.openai.api-key=${OPENAI_API_KEY} -------------------------------------------------------------------------------- /ai/usecases/text-classifier/src/test/java/com/howtodoinjava/ai/demo/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | import org.junit.jupiter.api.Test; 3 | import org.springframework.boot.test.context.SpringBootTest; 4 | 5 | @SpringBootTest 6 | class AppTest { 7 | 8 | @Test 9 | void contextLoads() { 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /ai/usecases/vector-store-pipeline/README.md: -------------------------------------------------------------------------------- 1 | # Spring AI ETL Pipeline 2 | 3 | ## Related Tutorials 4 | 5 | * [ETL Pipeline using Spring Cloud Function](https://howtodoinjava.com/spring-ai/etl-pipeline-using-cloud-functions/) -------------------------------------------------------------------------------- /ai/usecases/vector-store-pipeline/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3.9' 2 | 3 | networks: 4 | net: 5 | driver: bridge 6 | services: 7 | server: 8 | image: ghcr.io/chroma-core/chroma:latest 9 | environment: 10 | - IS_PERSISTENT=TRUE 11 | volumes: 12 | - chroma-data:/chroma/chroma/ 13 | ports: 14 | - 8000:8000 15 | networks: 16 | - net 17 | 18 | volumes: 19 | chroma-data: 20 | driver: local 21 | -------------------------------------------------------------------------------- /ai/usecases/vector-store-pipeline/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.howtodoinjava.ai.demo 7 | usecases 8 | 1.0.0 9 | ../pom.xml 10 | 11 | 12 | vector-store-pipeline 13 | jar 14 | vector-store-pipeline 15 | http://maven.apache.org 16 | 17 | 18 | 2023.0.1 19 | 5.0.0-SNAPSHOT 20 | 21 | 22 | 23 | 24 | org.springframework.cloud 25 | spring-cloud-function-context 26 | 27 | 28 | org.springframework.cloud.fn 29 | spring-file-supplier 30 | 31 | 32 | org.springframework.ai 33 | spring-ai-tika-document-reader 34 | 35 | 36 | org.springframework.ai 37 | spring-ai-openai-spring-boot-starter 38 | 39 | 40 | org.springframework.ai 41 | spring-ai-chroma-store-spring-boot-starter 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-docker-compose 46 | runtime 47 | 48 | 49 | 50 | 51 | 52 | 53 | org.springframework.cloud 54 | spring-cloud-dependencies 55 | ${spring.cloud.version} 56 | pom 57 | import 58 | 59 | 60 | org.springframework.cloud.fn 61 | spring-functions-catalog-bom 62 | ${spring.functions.catalog.version} 63 | pom 64 | import 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /ai/usecases/vector-store-pipeline/src/main/java/com/howtodoinjava/ai/demo/App.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class App { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(App.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ai/usecases/vector-store-pipeline/src/main/java/com/howtodoinjava/ai/demo/CloudFunctionConfig.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import java.util.List; 4 | import java.util.function.Consumer; 5 | import java.util.function.Function; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.ai.document.Document; 9 | import org.springframework.ai.reader.tika.TikaDocumentReader; 10 | import org.springframework.ai.transformer.splitter.TokenTextSplitter; 11 | import org.springframework.ai.vectorstore.VectorStore; 12 | import org.springframework.context.annotation.Bean; 13 | import org.springframework.context.annotation.Configuration; 14 | import org.springframework.core.io.ByteArrayResource; 15 | import org.springframework.messaging.Message; 16 | import reactor.core.publisher.Flux; 17 | 18 | @Configuration 19 | public class CloudFunctionConfig { 20 | 21 | private static final Logger LOGGER = LoggerFactory.getLogger(CloudFunctionConfig.class); 22 | 23 | 24 | @Bean 25 | Function>, Flux>> documentReader() { 26 | return resourceFlux -> resourceFlux 27 | .map(message -> 28 | new TikaDocumentReader(new ByteArrayResource(message.getPayload())) 29 | .get() 30 | .stream() 31 | .peek(document -> { 32 | document.getMetadata() 33 | .put("source", message.getHeaders().get("file_name")); 34 | }) 35 | .toList() 36 | ); 37 | } 38 | 39 | @Bean 40 | Function>, Flux>> documentTransformer() { 41 | return documentListFlux -> 42 | documentListFlux 43 | .map(unsplitList -> new TokenTextSplitter().apply(unsplitList)); 44 | } 45 | 46 | @Bean 47 | Consumer>> documentWriter(VectorStore vectorStore) { 48 | return documentFlux -> documentFlux 49 | .doOnNext(documents -> { 50 | LOGGER.info("Writing {} documents to vector store.", documents.size()); 51 | vectorStore.accept(documents); 52 | LOGGER.info("{} documents have been written to vector store.", documents.size()); 53 | }) 54 | .subscribe(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ai/usecases/vector-store-pipeline/src/main/java/com/howtodoinjava/ai/demo/IngestionController.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.http.ResponseEntity; 4 | import org.springframework.web.bind.annotation.PostMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | @RestController 8 | public class IngestionController { 9 | 10 | IngestionService ingestionService; 11 | 12 | public IngestionController(IngestionService ingestionService){ 13 | this.ingestionService = ingestionService; 14 | } 15 | 16 | @PostMapping("run-ingestion") 17 | public ResponseEntity run(){ 18 | ingestionService.ingest(); 19 | return ResponseEntity.accepted().build(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ai/usecases/vector-store-pipeline/src/main/java/com/howtodoinjava/ai/demo/IngestionService.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.springframework.cloud.function.context.FunctionCatalog; 4 | import org.springframework.stereotype.Service; 5 | 6 | @Service 7 | public class IngestionService { 8 | 9 | private final FunctionCatalog catalog; 10 | 11 | public IngestionService(FunctionCatalog catalog) { 12 | this.catalog = catalog; 13 | } 14 | 15 | public void ingest() { 16 | Runnable composedFunction = catalog.lookup(null); 17 | composedFunction.run(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ai/usecases/vector-store-pipeline/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.cloud.function.definition=fileSupplier|documentReader|documentTransformer|documentWriter 2 | spring.ai.openai.api-key=${OPENAI_API_KEY} 3 | file.supplier.directory=c:/temp/ingestion-files 4 | file.supplier.filename-regex=.*\.(pdf|docx|txt|pages|csv) 5 | 6 | spring.docker.compose.file=./ai/usecases/vector-store-pipeline/docker-compose.yaml 7 | spring.docker.compose.lifecycle-management=none -------------------------------------------------------------------------------- /ai/usecases/vector-store-pipeline/src/test/java/com/howtodoinjava/ai/demo/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class AppTest { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /ai/vector-stores/README.md: -------------------------------------------------------------------------------- 1 | # Spring AI VectorStore Examples 2 | 3 | - [Spring AI and PgVectorStore Configurations](https://howtodoinjava.com/spring-ai/spring-ai-pgvectorstore-example/) -------------------------------------------------------------------------------- /ai/vector-stores/pgvector-ollama/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | ollama: 3 | image: ollama/ollama 4 | container_name: ollama-docker-compose 5 | ports: 6 | - "11434:11434" 7 | command: ollama run mistral 8 | 9 | postgres: 10 | image: pgvector/pgvector:pg16 11 | container_name: postgres 12 | ports: 13 | - "5432:5432" 14 | environment: 15 | POSTGRES_USER: postgres 16 | POSTGRES_PASSWORD: postgres 17 | VECTOR_STORE_TYPE: pgVector 18 | 19 | -------------------------------------------------------------------------------- /ai/vector-stores/pgvector-ollama/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.howtodoinjava.ai.demo 7 | vector-stores 8 | 1.0.0 9 | 10 | 11 | pgvector-ollama 12 | jar 13 | pgvector-ollama 14 | 15 | 16 | 17 | org.springframework.ai 18 | spring-ai-ollama-spring-boot-starter 19 | 20 | 21 | org.springframework.ai 22 | spring-ai-pgvector-store-spring-boot-starter 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-docker-compose 27 | true 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /ai/vector-stores/pgvector-ollama/src/main/java/com/howtodoinjava/ai/demo/PgVectorOllamaApp.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import org.springframework.ai.document.Document; 6 | import org.springframework.ai.vectorstore.SearchRequest; 7 | import org.springframework.ai.vectorstore.VectorStore; 8 | import org.springframework.boot.CommandLineRunner; 9 | import org.springframework.boot.SpringApplication; 10 | import org.springframework.boot.autoconfigure.SpringBootApplication; 11 | 12 | @SpringBootApplication 13 | public class PgVectorOllamaApp implements CommandLineRunner { 14 | 15 | public static void main(String[] args) { 16 | SpringApplication.run(PgVectorOllamaApp.class); 17 | } 18 | 19 | private final VectorStore vectorStore; 20 | 21 | public PgVectorOllamaApp(VectorStore vectorStore) { 22 | this.vectorStore = vectorStore; 23 | } 24 | 25 | @Override 26 | public void run(String... args) { 27 | List documents = List.of( 28 | new Document("Java is a high-level, object-oriented programming language known for its platform independence."), 29 | new Document("It is widely used for developing enterprise applications, Android apps, and big data processing systems."), 30 | new Document("Java's strong typing, automatic memory management, and extensive libraries contribute to its popularity.", Map.of("reason", "popularity"))); 31 | 32 | // Add the documents to PGVector 33 | vectorStore.add(documents); 34 | 35 | // Retrieve documents similar to a query 36 | List results = vectorStore.similaritySearch(SearchRequest.query("programming language").withTopK(1)); 37 | 38 | results.stream() 39 | .map(Document::getContent) 40 | .forEach(System.out::println); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ai/vector-stores/pgvector-ollama/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.ai.ollama.embedding.options.model=mistral 2 | spring.ai.ollama.chat.options.model=mistral 3 | 4 | spring.datasource.url=jdbc:postgresql://localhost:5432/postgres 5 | spring.datasource.username=postgres 6 | spring.datasource.password=postgres 7 | 8 | spring.ai.vectorstore.pgvector.initialize-schema=true 9 | spring.ai.vectorstore.pgvector.index-type=none 10 | spring.ai.vectorstore.pgvector.distance-type=COSINE_DISTANCE 11 | spring.ai.vectorstore.pgvector.dimensions=4096 12 | #spring.ai.vectorstore.pgvector.schema-validation=true 13 | #spring.ai.vectorstore.pgvector.remove-existing-vector-store-table=false 14 | 15 | spring.docker.compose.file=./ai/vector-stores/pgvector-ollama/compose.yaml 16 | spring.docker.compose.lifecycle-management = none 17 | -------------------------------------------------------------------------------- /ai/vector-stores/pgvector-ollama/src/test/java/com/howtodoinjava/ai/demo/PgVectorOllamaAppTest.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.junit.jupiter.api.Disabled; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | @SpringBootTest 8 | @Disabled 9 | public class PgVectorOllamaAppTest { 10 | 11 | @Test 12 | public void contextLoads() { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ai/vector-stores/pgvector-openai/README.md: -------------------------------------------------------------------------------- 1 | # Setting Up PgVectorStore and OpenAI Embedding Model with Spring AI 2 | 3 | ## Docker Images 4 | 5 | ``` 6 | docker run -it --rm --name postgres -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e VECTOR_STORE_TYPE=pgVector pgvector/pgvector:pg16 7 | ``` 8 | ## Maven 9 | 10 | ``` 11 | 12 | org.springframework.ai 13 | spring-ai-openai-spring-boot-starter 14 | 15 | 16 | org.springframework.ai 17 | spring-ai-pgvector-store-spring-boot-starter 18 | 19 | ``` 20 | 21 | ## Properties Configuration 22 | 23 | ```properties 24 | spring.ai.openai.api-key=${OPENAI_API_KEY} 25 | spring.ai.openai.chat.model=gpt-4 26 | spring.ai.openai.embedding.model=text-embedding-ada-002 27 | 28 | spring.datasource.url=jdbc:postgresql://localhost:5432/postgres 29 | spring.datasource.username=postgres 30 | spring.datasource.password=postgres 31 | 32 | spring.ai.vectorstore.pgvector.initialize-schema=true 33 | spring.ai.vectorstore.pgvector.index-type=HNSW 34 | spring.ai.vectorstore.pgvector.distance-type=COSINE_DISTANCE 35 | spring.ai.vectorstore.pgvector.dimensions=1536 36 | spring.ai.vectorstore.pgvector.schema-validation=true 37 | spring.ai.vectorstore.pgvector.remove-existing-vector-store-table=true 38 | ``` -------------------------------------------------------------------------------- /ai/vector-stores/pgvector-openai/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.howtodoinjava.ai.demo 7 | vector-stores 8 | 1.0.0 9 | 10 | 11 | pgvector-openai 12 | jar 13 | pgvector-openai 14 | 15 | 16 | 17 | org.springframework.ai 18 | spring-ai-openai-spring-boot-starter 19 | 20 | 21 | org.springframework.ai 22 | spring-ai-pgvector-store-spring-boot-starter 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ai/vector-stores/pgvector-openai/src/main/java/com/howtodoinjava/ai/demo/App.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import org.springframework.ai.document.Document; 6 | import org.springframework.ai.vectorstore.SearchRequest; 7 | import org.springframework.ai.vectorstore.VectorStore; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.boot.CommandLineRunner; 10 | import org.springframework.boot.SpringApplication; 11 | import org.springframework.boot.autoconfigure.SpringBootApplication; 12 | 13 | @SpringBootApplication 14 | public class App implements CommandLineRunner { 15 | 16 | public static void main(String[] args) { 17 | SpringApplication.run(App.class); 18 | } 19 | 20 | private final VectorStore vectorStore; 21 | 22 | public App(VectorStore vectorStore) { 23 | this.vectorStore = vectorStore; 24 | } 25 | 26 | @Override 27 | public void run(String... args) { 28 | List documents = List.of( 29 | new Document( 30 | "Java is a high-level, object-oriented programming language known for its platform independence."), 31 | new Document( 32 | "It is widely used for developing enterprise applications, Android apps, and big data processing systems."), 33 | new Document( 34 | "Java's strong typing, automatic memory management, and extensive libraries contribute to its popularity.", 35 | Map.of("reason", "popularity"))); 36 | 37 | // Add the documents to PGVector 38 | vectorStore.add(documents); 39 | 40 | // Retrieve documents similar to a query 41 | List results = vectorStore 42 | .similaritySearch(SearchRequest.query("programming language").withTopK(1)); 43 | 44 | results.stream() 45 | .map(Document::getContent) 46 | .forEach(System.out::println); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ai/vector-stores/pgvector-openai/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.ai.openai.api-key=${OPENAI_API_KEY} 2 | spring.ai.openai.embedding.model=text-embedding-ada-002 3 | 4 | spring.datasource.url=jdbc:postgresql://localhost:5432/postgres 5 | spring.datasource.username=postgres 6 | spring.datasource.password=postgres 7 | 8 | spring.ai.vectorstore.pgvector.initialize-schema=true 9 | spring.ai.vectorstore.pgvector.index-type=HNSW 10 | spring.ai.vectorstore.pgvector.distance-type=COSINE_DISTANCE 11 | spring.ai.vectorstore.pgvector.dimensions=1536 12 | #spring.ai.vectorstore.pgvector.schema-validation=true 13 | #spring.ai.vectorstore.pgvector.remove-existing-vector-store-table=false 14 | -------------------------------------------------------------------------------- /ai/vector-stores/pgvector-openai/src/test/java/com/howtodoinjava/ai/demo/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.demo; 2 | 3 | import org.junit.jupiter.api.Disabled; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | @SpringBootTest 8 | @Disabled 9 | public class AppTest { 10 | 11 | @Test 12 | public void contextLoads() { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ai/vector-stores/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.howtodoinjava.ai.demo 7 | ai 8 | 1.0.0 9 | 10 | 11 | vector-stores 12 | pom 13 | vector-stores 14 | http://maven.apache.org 15 | 16 | 17 | pgvector-openai 18 | pgvector-ollama 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Apache Maven Wrapper startup batch script, version 3.2.0 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 28 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 29 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 30 | @REM e.g. to debug Maven itself, use 31 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 32 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 33 | @REM ---------------------------------------------------------------------------- 34 | 35 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 36 | @echo off 37 | @REM set title of command window 38 | title %0 39 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 40 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 41 | 42 | @REM set %HOME% to equivalent of $HOME 43 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 44 | 45 | @REM Execute a user defined script before this one 46 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 47 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 48 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* 49 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* 50 | :skipRcPre 51 | 52 | @setlocal 53 | 54 | set ERROR_CODE=0 55 | 56 | @REM To isolate internal variables from possible post scripts, we use another setlocal 57 | @setlocal 58 | 59 | @REM ==== START VALIDATION ==== 60 | if not "%JAVA_HOME%" == "" goto OkJHome 61 | 62 | echo. 63 | echo Error: JAVA_HOME not found in your environment. >&2 64 | echo Please set the JAVA_HOME variable in your environment to match the >&2 65 | echo location of your Java installation. >&2 66 | echo. 67 | goto error 68 | 69 | :OkJHome 70 | if exist "%JAVA_HOME%\bin\java.exe" goto init 71 | 72 | echo. 73 | echo Error: JAVA_HOME is set to an invalid directory. >&2 74 | echo JAVA_HOME = "%JAVA_HOME%" >&2 75 | echo Please set the JAVA_HOME variable in your environment to match the >&2 76 | echo location of your Java installation. >&2 77 | echo. 78 | goto error 79 | 80 | @REM ==== END VALIDATION ==== 81 | 82 | :init 83 | 84 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 85 | @REM Fallback to current working directory if not found. 86 | 87 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 88 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 89 | 90 | set EXEC_DIR=%CD% 91 | set WDIR=%EXEC_DIR% 92 | :findBaseDir 93 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 94 | cd .. 95 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 96 | set WDIR=%CD% 97 | goto findBaseDir 98 | 99 | :baseDirFound 100 | set MAVEN_PROJECTBASEDIR=%WDIR% 101 | cd "%EXEC_DIR%" 102 | goto endDetectBaseDir 103 | 104 | :baseDirNotFound 105 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 106 | cd "%EXEC_DIR%" 107 | 108 | :endDetectBaseDir 109 | 110 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 111 | 112 | @setlocal EnableExtensions EnableDelayedExpansion 113 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 114 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 115 | 116 | :endReadAdditionalConfig 117 | 118 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 123 | 124 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 125 | IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | if "%MVNW_VERBOSE%" == "true" ( 132 | echo Found %WRAPPER_JAR% 133 | ) 134 | ) else ( 135 | if not "%MVNW_REPOURL%" == "" ( 136 | SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 137 | ) 138 | if "%MVNW_VERBOSE%" == "true" ( 139 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 140 | echo Downloading from: %WRAPPER_URL% 141 | ) 142 | 143 | powershell -Command "&{"^ 144 | "$webclient = new-object System.Net.WebClient;"^ 145 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 146 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 147 | "}"^ 148 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ 149 | "}" 150 | if "%MVNW_VERBOSE%" == "true" ( 151 | echo Finished downloading %WRAPPER_JAR% 152 | ) 153 | ) 154 | @REM End of extension 155 | 156 | @REM If specified, validate the SHA-256 sum of the Maven wrapper jar file 157 | SET WRAPPER_SHA_256_SUM="" 158 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 159 | IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B 160 | ) 161 | IF NOT %WRAPPER_SHA_256_SUM%=="" ( 162 | powershell -Command "&{"^ 163 | "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ 164 | "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ 165 | " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ 166 | " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ 167 | " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ 168 | " exit 1;"^ 169 | "}"^ 170 | "}" 171 | if ERRORLEVEL 1 goto error 172 | ) 173 | 174 | @REM Provide a "standardized" way to retrieve the CLI args that will 175 | @REM work with both Windows and non-Windows executions. 176 | set MAVEN_CMD_LINE_ARGS=%* 177 | 178 | %MAVEN_JAVA_EXE% ^ 179 | %JVM_CONFIG_MAVEN_PROPS% ^ 180 | %MAVEN_OPTS% ^ 181 | %MAVEN_DEBUG_OPTS% ^ 182 | -classpath %WRAPPER_JAR% ^ 183 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ 184 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 185 | if ERRORLEVEL 1 goto error 186 | goto end 187 | 188 | :error 189 | set ERROR_CODE=1 190 | 191 | :end 192 | @endlocal & set ERROR_CODE=%ERROR_CODE% 193 | 194 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost 195 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 196 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" 197 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" 198 | :skipRcPost 199 | 200 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 201 | if "%MAVEN_BATCH_PAUSE%"=="on" pause 202 | 203 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% 204 | 205 | cmd /C exit /B %ERROR_CODE% 206 | -------------------------------------------------------------------------------- /openai-quickstart/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Spring AI 2 | 3 | ## Prerequisites 4 | 5 | Create an account at OpenAI Signup and generate the security token at [API Keys](https://platform.openai.com/api-keys). 6 | 7 | Spring AI (for OpenAI integration) expects a configuration property named 'spring.ai.openai.api-key' that we should set to the value of the API Key obtained in teh above step. 8 | 9 | ``` 10 | export SPRING_AI_OPENAI_API_KEY= 11 | ``` 12 | 13 | ## Run the Project 14 | 15 | ``` 16 | ./mvnw spring-boot:run 17 | ``` 18 | 19 | ## Related Tutorials 20 | 21 | Refer to these tutorials for how to execute and what to expect in API calls. 22 | 23 | * [Spring AI Tutorial (with Examples)](https://howtodoinjava.com/spring-ai/spring-ai-tutorial/) 24 | * [Spring AI Example: Generate Image from Text](https://howtodoinjava.com/spring-ai/spring-ai-image-generation-example/) 25 | * [Spring AI Structured Output Converters (List, Map and Bean)](https://howtodoinjava.com/spring-ai/structured-output-converters/) 26 | * [Spring AI PromptTemplate Example](https://howtodoinjava.com/spring-ai/prompt-template-example/) -------------------------------------------------------------------------------- /openai-quickstart/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.howtodoinjava.ai.demo 7 | spring-ai-examples 8 | 1.0.0 9 | 10 | 11 | openai-quickstart 12 | jar 13 | openai-quickstart 14 | http://maven.apache.org 15 | 16 | 17 | UTF-8 18 | com.howtodoinjava.ai.chat.Application 19 | 20 | 21 | 22 | 23 | org.springframework.ai 24 | spring-ai-openai-spring-boot-starter 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /openai-quickstart/src/main/java/com/howtodoinjava/ai/chat/AppConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.chat; 2 | 3 | import org.springframework.ai.chat.model.ChatModel; 4 | import org.springframework.ai.openai.OpenAiChatModel; 5 | import org.springframework.ai.openai.api.OpenAiApi; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.context.annotation.Primary; 10 | 11 | @Configuration 12 | public class AppConfiguration { 13 | 14 | @Bean 15 | @Primary 16 | ChatModel chatModel(@Value("${spring.ai.openai.api-key}") String apiKey) { 17 | return new OpenAiChatModel(new OpenAiApi(apiKey)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /openai-quickstart/src/main/java/com/howtodoinjava/ai/chat/Application.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.chat; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.CommandLineRunner; 7 | import org.springframework.boot.SpringApplication; 8 | import org.springframework.boot.autoconfigure.SpringBootApplication; 9 | 10 | @SpringBootApplication 11 | public class Application implements CommandLineRunner { 12 | 13 | static Logger LOG = LoggerFactory.getLogger(Application.class); 14 | 15 | public static void main(String[] args) { 16 | SpringApplication.run(Application.class, args); 17 | } 18 | 19 | @Autowired 20 | OpenAiChatController chatController; 21 | 22 | @Override 23 | public void run(String... args) throws Exception { 24 | 25 | String response = chatController.tellSimpleJoke().get("generation"); 26 | LOG.info(response); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /openai-quickstart/src/main/java/com/howtodoinjava/ai/chat/JokeResponse.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.chat; 2 | 3 | public record JokeResponse(String subject, String language, String joke) { 4 | } 5 | -------------------------------------------------------------------------------- /openai-quickstart/src/main/java/com/howtodoinjava/ai/chat/OpenAiChatController.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.chat; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import org.springframework.ai.chat.model.ChatModel; 6 | import org.springframework.ai.chat.model.ChatResponse; 7 | import org.springframework.ai.chat.model.Generation; 8 | import org.springframework.ai.chat.prompt.Prompt; 9 | import org.springframework.ai.chat.prompt.PromptTemplate; 10 | import org.springframework.ai.converter.BeanOutputConverter; 11 | import org.springframework.ai.converter.ListOutputConverter; 12 | import org.springframework.ai.converter.MapOutputConverter; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.beans.factory.annotation.Value; 15 | import org.springframework.core.convert.support.DefaultConversionService; 16 | import org.springframework.web.bind.annotation.GetMapping; 17 | import org.springframework.web.bind.annotation.RequestParam; 18 | import org.springframework.web.bind.annotation.RestController; 19 | 20 | @RestController 21 | public class OpenAiChatController { 22 | 23 | private final ChatModel chatModel; 24 | private final String promptTemplate; 25 | private final String jsonPromptTemplate; 26 | 27 | @Autowired 28 | public OpenAiChatController(ChatModel chatModel, 29 | @Value("${app.joke.simple.promptTemplate}") String promptTemplate, 30 | @Value("${app.joke.formatted.promptTemplate}") String jsonPromptTemplate) { 31 | this.chatModel = chatModel; 32 | this.promptTemplate = promptTemplate; 33 | this.jsonPromptTemplate = jsonPromptTemplate; 34 | } 35 | 36 | @GetMapping("/joke-service/simple") 37 | public Map tellSimpleJoke() { 38 | return Map.of("generation", chatModel.call("Tell me a joke")); 39 | } 40 | 41 | @GetMapping("/joke-service/simple-with-prompt") 42 | public String tellSimpleJokeWithPrompt(@RequestParam("subject") String subject, 43 | @RequestParam("language") String language) { 44 | PromptTemplate pt = new PromptTemplate(promptTemplate); 45 | Prompt renderedPrompt = pt.create(Map.of("subject", subject, "language", language)); 46 | 47 | ChatResponse response = chatModel.call(renderedPrompt); 48 | return response.getResult().getOutput().getContent(); 49 | } 50 | 51 | /*@GetMapping("/joke-service/json-with-prompt") 52 | public JokeResponse tellSpecificJokeInJsonFormat(@RequestParam("subject") String subject, 53 | @RequestParam("language") String language) { 54 | 55 | BeanOutputConverter parser = new BeanOutputConverter<>(JokeResponse.class); 56 | *//* 57 | Your response should be in JSON format. 58 | Do not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation. 59 | Do not include Markdown code blocks in your response. 60 | Remove the ```json markdown from the output. 61 | Here is the JSON Schema instance your output must adhere to: 62 | ```{ 63 | "$schema" : "https://json-schema.org/draft/2020-12/schema", 64 | "type" : "object", 65 | "properties" : { 66 | "joke" : { 67 | "type" : "string" 68 | }, 69 | "language" : { 70 | "type" : "string" 71 | }, 72 | "subject" : { 73 | "type" : "string" 74 | } 75 | } 76 | }``` 77 | *//* 78 | String format = parser.getFormat(); 79 | 80 | PromptTemplate pt = new PromptTemplate(jsonPromptTemplate); 81 | Prompt renderedPrompt = pt.create( 82 | Map.of("subject", subject, "language", language, "format", format)); 83 | 84 | ChatResponse response = chatModel.call(renderedPrompt); 85 | 86 | *//*Usage usage = response.getMetadata().getUsage(); 87 | System.out.println("Usage: " + usage.getPromptTokens() + " " + usage.getGenerationTokens() + "; " + usage.getTotalTokens());*//* 88 | 89 | return parser.convert(response.getResult().getOutput().getContent()); 90 | }*/ 91 | 92 | @GetMapping("/country-capital-service/map") 93 | public Map getCapitalNamesInMap(@RequestParam String countryNamesCsv) { 94 | if (countryNamesCsv == null || countryNamesCsv.isEmpty()) { 95 | throw new IllegalArgumentException("Country names CSV cannot be null or empty"); 96 | } 97 | MapOutputConverter converter = new MapOutputConverter(); 98 | String format = converter.getFormat(); 99 | PromptTemplate pt = new PromptTemplate( 100 | "For these list of countries {countryNamesCsv}, return the list of capitals. {format}"); 101 | Prompt renderedPrompt = pt.create(Map.of("countryNamesCsv", countryNamesCsv, "format", format)); 102 | ChatResponse response = chatModel.call(renderedPrompt); 103 | Generation generation = response.getResult(); // call getResults() if multiple generations 104 | System.out.println(generation.getOutput().getContent()); 105 | return converter.convert(generation.getOutput().getContent()); 106 | } 107 | 108 | /*@GetMapping("/country-capital-service/bean") 109 | public Pair getCapitalNamesInPojo(@RequestParam String countryName) { 110 | 111 | if (countryName == null || countryName.isEmpty()) { 112 | throw new IllegalArgumentException("Country names CSV cannot be null or empty"); 113 | } 114 | 115 | BeanOutputConverter converter = new BeanOutputConverter<>(Pair.class); 116 | String format = converter.getFormat(); 117 | 118 | PromptTemplate pt = new PromptTemplate( 119 | "For these list of countries {countryName}, return the list of its 10 popular cities. {format}"); 120 | Prompt renderedPrompt = pt.create(Map.of("countryName", countryName, "format", format)); 121 | 122 | ChatResponse response = chatModel.call(renderedPrompt); 123 | Generation generation = response.getResult(); // call getResults() if multiple generations 124 | return converter.convert(generation.getOutput().getContent()); 125 | }*/ 126 | 127 | @GetMapping("/country-capital-service/list") 128 | public List getCapitalNamesInList(@RequestParam String countryNamesCsv) { 129 | if (countryNamesCsv == null || countryNamesCsv.isEmpty()) { 130 | throw new IllegalArgumentException("Country names CSV cannot be null or empty"); 131 | } 132 | ListOutputConverter converter = new ListOutputConverter(new DefaultConversionService()); 133 | String format = converter.getFormat(); 134 | PromptTemplate pt = new PromptTemplate( 135 | "For these list of countries {countryNamesCsv}, return the list of capitals. {format}"); 136 | Prompt renderedPrompt = pt.create(Map.of("countryNamesCsv", countryNamesCsv, "format", format)); 137 | ChatResponse response = chatModel.call(renderedPrompt); 138 | Generation generation = response.getResult(); // call getResults() if multiple generations 139 | System.out.println(generation.getOutput().getContent()); 140 | return converter.convert(generation.getOutput().getContent()); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /openai-quickstart/src/main/java/com/howtodoinjava/ai/chat/Pair.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.chat; 2 | 3 | import java.util.List; 4 | 5 | public record Pair(String countryName, List cities) { 6 | } 7 | -------------------------------------------------------------------------------- /openai-quickstart/src/main/java/com/howtodoinjava/ai/image/AppConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.image; 2 | 3 | import org.springframework.ai.image.ImageModel; 4 | import org.springframework.ai.openai.OpenAiImageModel; 5 | import org.springframework.ai.openai.api.OpenAiImageApi; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.context.annotation.Primary; 10 | 11 | @Configuration 12 | public class AppConfiguration { 13 | 14 | @Bean 15 | @Primary 16 | ImageModel imageModel(@Value("${spring.ai.openai.api-key}") String apiKey) { 17 | return new OpenAiImageModel(new OpenAiImageApi(apiKey)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /openai-quickstart/src/main/java/com/howtodoinjava/ai/image/Application.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.image; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Application { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(Application.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /openai-quickstart/src/main/java/com/howtodoinjava/ai/image/OpenAiImageController.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.image; 2 | 3 | 4 | import org.springframework.ai.image.*; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.RequestParam; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | @RestController 10 | public class OpenAiImageController { 11 | 12 | private final ImageModel imageModel; 13 | 14 | public OpenAiImageController(ImageModel imageModel) { 15 | this.imageModel = imageModel; 16 | } 17 | 18 | @GetMapping("/image-gen") 19 | public String imageGen(@RequestParam String message) { 20 | 21 | ImageOptions options = ImageOptionsBuilder.builder() 22 | .withModel("dall-e-3") 23 | .withHeight(1024) 24 | .withWidth(1024) 25 | .build(); 26 | 27 | ImagePrompt imagePrompt = new ImagePrompt(message, options); 28 | ImageResponse response = imageModel.call(imagePrompt); 29 | String imageUrl = response.getResult().getOutput().getUrl(); 30 | 31 | return "redirect:" + imageUrl; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /openai-quickstart/src/main/java/com/howtodoinjava/ai/promptTemplate/PromptTemplateApplication.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.promptTemplate; 2 | 3 | import org.springframework.ai.chat.messages.Message; 4 | import org.springframework.ai.chat.model.Generation; 5 | import org.springframework.ai.chat.prompt.Prompt; 6 | import org.springframework.ai.chat.prompt.PromptTemplate; 7 | import org.springframework.ai.chat.prompt.SystemPromptTemplate; 8 | import org.springframework.ai.openai.OpenAiChatModel; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.boot.ApplicationRunner; 11 | import org.springframework.boot.WebApplicationType; 12 | import org.springframework.boot.autoconfigure.SpringBootApplication; 13 | import org.springframework.boot.builder.SpringApplicationBuilder; 14 | import org.springframework.context.annotation.Bean; 15 | 16 | import java.util.List; 17 | import java.util.Map; 18 | import org.springframework.core.io.Resource; 19 | 20 | @SpringBootApplication 21 | public class PromptTemplateApplication { 22 | 23 | public static void main(String[] args) { 24 | new SpringApplicationBuilder(PromptTemplateApplication.class) 25 | .web(WebApplicationType.NONE) 26 | .run(args); 27 | } 28 | 29 | @Value("classpath:/prompts/system-message.st") 30 | private Resource promptSystemMessage; 31 | 32 | @Value("classpath:/prompts/user-message.st") 33 | private Resource promptUserMessage; 34 | 35 | @Value("classpath:/prompts/get-list-message.st") 36 | private Resource promptGetListMessage; 37 | 38 | @Bean(name = "promptTemplateApplicationRunner") 39 | ApplicationRunner applicationRunner(OpenAiChatModel chatModel) { 40 | return args -> { 41 | userPrompt(chatModel); 42 | systemAndUserPrompt(chatModel); 43 | }; 44 | } 45 | 46 | private void userPrompt(OpenAiChatModel chatModel) { 47 | 48 | PromptTemplate promptTemplate 49 | = new PromptTemplate("Tell me about {subject}. Explain if I am {age} years old."); 50 | 51 | //Obtain these values from user 52 | String subject = "USA Elections"; 53 | int age = 14; 54 | 55 | Prompt prompt = promptTemplate.create(Map.of("subject", subject, "age", age)); 56 | Generation generation = chatModel 57 | .call(prompt) 58 | .getResult(); 59 | 60 | System.out.println(prompt.getContents()); 61 | System.out.println(generation.getOutput().getContent()); 62 | } 63 | 64 | private void systemAndUserPrompt(OpenAiChatModel chatModel) { 65 | 66 | //1. Template from user message 67 | String userText = """ 68 | Tell me about five most famous tourist spots in {location}. 69 | Write at least a sentence for each spot. 70 | """; 71 | 72 | String location = "Dubai(UAE)"; //Get from user 73 | //PromptTemplate userPromptTemplate = new PromptTemplate(promptUserMessage); 74 | PromptTemplate userPromptTemplate = new PromptTemplate(userText); 75 | Message userMessage = userPromptTemplate.createMessage(Map.of("location", location)); 76 | 77 | //2. Template for system message 78 | String systemText = """ 79 | You are a helpful AI assistant that helps people find information. Your name is {name} 80 | In your first response, greet the user, quick summary of answer and then do not repeat it. 81 | Next, you should reply to the user's request. 82 | Finish with thanking the user for asking question in the end. 83 | """; 84 | 85 | //SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(promptSystemMessage); 86 | SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemText); 87 | Message systemMessage = systemPromptTemplate.createMessage(Map.of("name", "Alexa")); 88 | 89 | //3. Prompt message containing user and system messages 90 | Prompt prompt = new Prompt(List.of(userMessage, systemMessage)); 91 | 92 | //4. API invocation and result extraction 93 | Generation generation = chatModel.call(prompt).getResult(); 94 | System.out.println(generation.getOutput()); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /openai-quickstart/src/main/java/com/howtodoinjava/ai/structuredOutput/StructuredOutputDemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.structuredOutput; 2 | 3 | import org.springframework.ai.chat.model.Generation; 4 | import org.springframework.ai.chat.prompt.Prompt; 5 | import org.springframework.ai.chat.prompt.PromptTemplate; 6 | import org.springframework.ai.converter.BeanOutputConverter; 7 | import org.springframework.ai.converter.ListOutputConverter; 8 | import org.springframework.ai.converter.MapOutputConverter; 9 | import org.springframework.ai.openai.OpenAiChatModel; 10 | import org.springframework.boot.ApplicationRunner; 11 | import org.springframework.boot.WebApplicationType; 12 | import org.springframework.boot.autoconfigure.SpringBootApplication; 13 | import org.springframework.boot.builder.SpringApplicationBuilder; 14 | import org.springframework.core.convert.support.DefaultConversionService; 15 | 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | @SpringBootApplication 20 | public class StructuredOutputDemoApplication { 21 | 22 | //@Bean(name = "structuredOutputDemoApplicationRunner") 23 | ApplicationRunner applicationRunner(OpenAiChatModel chatModel) { 24 | return args -> { 25 | listOutputConverter(chatModel); 26 | mapOutputConverter(chatModel); 27 | beanOutputConverter(chatModel); 28 | }; 29 | } 30 | 31 | public void mapOutputConverter(OpenAiChatModel chatModel) { 32 | MapOutputConverter mapOutputConverter = new MapOutputConverter(); 33 | 34 | String format = mapOutputConverter.getFormat(); 35 | String template = """ 36 | Provide me a List of {subject} 37 | {format} 38 | """; 39 | PromptTemplate promptTemplate = new PromptTemplate(template, 40 | Map.of("subject", "an array of numbers from 1 to 9 under they key name 'numbers'", "format", format)); 41 | Prompt prompt = new Prompt(promptTemplate.createMessage()); 42 | Generation generation = chatModel.call(prompt).getResult(); 43 | 44 | Map result = mapOutputConverter.convert(generation.getOutput().getContent()); 45 | 46 | System.out.println(result); 47 | } 48 | 49 | public void listOutputConverter(OpenAiChatModel chatModel) { 50 | ListOutputConverter listOutputConverter = new ListOutputConverter(new DefaultConversionService()); 51 | 52 | String format = listOutputConverter.getFormat(); 53 | String template = """ 54 | List five {subject} 55 | {format} 56 | """; 57 | PromptTemplate promptTemplate = new PromptTemplate(template, 58 | Map.of("subject", "ice cream flavors", "format", format)); 59 | Prompt prompt = new Prompt(promptTemplate.createMessage()); 60 | Generation generation = chatModel.call(prompt).getResult(); 61 | 62 | List list = listOutputConverter.convert(generation.getOutput().getContent()); 63 | System.out.println(list); 64 | } 65 | 66 | public void beanOutputConverter(OpenAiChatModel chatModel) { 67 | 68 | record ActorsFilms(String actor, List movies) { 69 | } 70 | 71 | BeanOutputConverter beanOutputConverter = new BeanOutputConverter<>(ActorsFilms.class); 72 | 73 | String format = beanOutputConverter.getFormat(); 74 | 75 | String actor = "Tom Hanks"; 76 | 77 | String template = """ 78 | Generate the filmography of 5 movies for {actor}. 79 | {format} 80 | """; 81 | 82 | Generation generation = chatModel.call( 83 | new Prompt(new PromptTemplate(template, Map.of("actor", actor, "format", format)).createMessage())) 84 | .getResult(); 85 | 86 | ActorsFilms actorsFilms = beanOutputConverter.convert(generation.getOutput().getContent()); 87 | 88 | System.out.println(actorsFilms); 89 | } 90 | 91 | public static void main(String[] args) { 92 | new SpringApplicationBuilder(StructuredOutputDemoApplication.class) 93 | .web(WebApplicationType.NONE) 94 | .run(args); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /openai-quickstart/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.ai.openai.api-key=${OPENAI_API_KEY} -------------------------------------------------------------------------------- /openai-quickstart/src/main/resources/prompts/get-list-message.st: -------------------------------------------------------------------------------- 1 | Provide me a List of {subject} -------------------------------------------------------------------------------- /openai-quickstart/src/main/resources/prompts/system-message.st: -------------------------------------------------------------------------------- 1 | You are a helpful AI assistant that helps people find information. Your name is {name} 2 | In your first response, greet the user, quick summary of answer and then do not repeat it. 3 | Next, you should reply to the user's request. 4 | Finish with thanking the user for asking question in the end. -------------------------------------------------------------------------------- /openai-quickstart/src/main/resources/prompts/user-message.st: -------------------------------------------------------------------------------- 1 | Tell me about five most famous tourist spots in {location}. 2 | Write at least a sentence for each spot. -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 3.2.5 10 | 11 | 12 | com.howtodoinjava.ai.demo 13 | spring-ai-examples 14 | 1.0.0 15 | pom 16 | spring-ai-examples 17 | Demo AI projects built with Spring Boot 18 | 19 | openai-quickstart 20 | rag-examples 21 | ai 22 | 23 | 24 | 21 25 | 1.0.0-SNAPSHOT 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-webflux 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-actuator 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-devtools 43 | runtime 44 | true 45 | 46 | 47 | org.projectlombok 48 | lombok 49 | true 50 | 51 | 52 | org.springframework.boot 53 | spring-boot-starter-test 54 | test 55 | 56 | 57 | 58 | 59 | 60 | 61 | org.springframework.ai 62 | spring-ai-bom 63 | ${spring-ai.version} 64 | pom 65 | import 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | org.springframework.boot 75 | spring-boot-maven-plugin 76 | 77 | 78 | 79 | org.projectlombok 80 | lombok 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | spring-milestones 91 | Spring Milestones 92 | https://repo.spring.io/milestone 93 | 94 | false 95 | 96 | 97 | 98 | spring-snapshots 99 | Spring Snapshots 100 | https://repo.spring.io/snapshot 101 | 102 | false 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /rag-examples/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.9' 2 | 3 | services: 4 | chroma: 5 | image: ghcr.io/chroma-core/chroma:0.4.22 6 | volumes: 7 | - index_data:/chroma/chroma 8 | ports: 9 | - "8000:8000" 10 | environment: 11 | - ALLOW_RESET=true 12 | - ANONYMIZED_TELEMETRY=false 13 | - PERSIST_DIRECTORY=/chroma/chroma/persist 14 | - IS_PERSISTENT=true 15 | command: ["/bin/sh", "-c", "pip install numpy==1.23.5 && pip install chroma-hnswlib==0.7.3 && uvicorn chromadb.app:app --host 0.0.0.0 --port 8000 --workers 1"] 16 | 17 | volumes: 18 | index_data: 19 | driver: local -------------------------------------------------------------------------------- /rag-examples/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | com.howtodoinjava.ai.demo 7 | spring-ai-examples 8 | 1.0.0 9 | 10 | 11 | rag-examples 12 | jar 13 | rag-examples 14 | http://maven.apache.org 15 | 16 | 17 | UTF-8 18 | com.howtodoinjava.ai.chromaWithOpenAI.Application 19 | 20 | 21 | 22 | 23 | org.springframework.ai 24 | spring-ai-openai-spring-boot-starter 25 | 26 | 27 | org.springframework.ai 28 | spring-ai-pdf-document-reader 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-docker-compose 33 | true 34 | 35 | 36 | org.springframework.ai 37 | spring-ai-chroma-store-spring-boot-starter 38 | 39 | 40 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /rag-examples/src/main/java/com/howtodoinjava/ai/chat/redis/RagWithRedisAndOpenAI.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.chat.redis; 2 | 3 | import java.util.List; 4 | import org.springframework.ai.embedding.EmbeddingRequest; 5 | import org.springframework.ai.embedding.EmbeddingResponse; 6 | import org.springframework.ai.openai.OpenAiEmbeddingModel; 7 | import org.springframework.ai.openai.OpenAiEmbeddingOptions; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.boot.CommandLineRunner; 10 | import org.springframework.boot.autoconfigure.SpringBootApplication; 11 | import org.springframework.boot.builder.SpringApplicationBuilder; 12 | 13 | @SpringBootApplication 14 | public class RagWithRedisAndOpenAI implements CommandLineRunner { 15 | 16 | public static void main(String[] args) { 17 | new SpringApplicationBuilder(RagWithRedisAndOpenAI.class) 18 | .run(args); 19 | } 20 | 21 | @Autowired 22 | OpenAiEmbeddingModel embeddingModel; 23 | @Override 24 | public void run(String... args) throws Exception { 25 | EmbeddingResponse embeddingResponse = embeddingModel.call( 26 | new EmbeddingRequest(List.of("Hello World", "World is big and salvation is near"), 27 | OpenAiEmbeddingOptions.builder() 28 | .withModel("text-embedding-3-small") 29 | .build())); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /rag-examples/src/main/java/com/howtodoinjava/ai/chat/redis/controller/AiChatController.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.chat.redis.controller; 2 | 3 | import com.howtodoinjava.ai.chat.redis.service.DataIngestionService; 4 | import com.howtodoinjava.ai.chat.redis.service.SimilaritySearchService; 5 | import java.util.List; 6 | import java.util.Map; 7 | import org.springframework.ai.chat.model.ChatModel; 8 | import org.springframework.ai.chat.prompt.Prompt; 9 | import org.springframework.ai.chat.prompt.PromptTemplate; 10 | import org.springframework.ai.document.Document; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.http.ResponseEntity; 13 | import org.springframework.web.bind.annotation.GetMapping; 14 | import org.springframework.web.bind.annotation.PostMapping; 15 | import org.springframework.web.bind.annotation.RequestParam; 16 | 17 | //@RestController 18 | public class AiChatController { 19 | 20 | @Autowired 21 | DataIngestionService dataIngestionService; 22 | 23 | @Autowired 24 | SimilaritySearchService similaritySearchService; 25 | 26 | @Autowired 27 | private ChatModel chatModel; 28 | 29 | private final String INPUT_PROMPT = """ 30 | Answer the query strictly referring the provided context: 31 | {context} 32 | Query: 33 | {query} 34 | In case you don't have any answer from the context provided, just say: 35 | I'm sorry I don't have the information you are looking for. 36 | """; 37 | 38 | @GetMapping("/chat") 39 | public ResponseEntity chat(@RequestParam(name = "query") String query) { 40 | 41 | List documents = similaritySearchService.searchData(query); 42 | 43 | if (documents.size() > 0) { 44 | PromptTemplate promptTemplate = new PromptTemplate(INPUT_PROMPT); 45 | Prompt prompt = promptTemplate.create(Map.of("query", query, "context", documents)); 46 | String response = chatModel.call(prompt).getResult().getOutput().getContent(); 47 | return ResponseEntity.ok(response); 48 | } 49 | 50 | return ResponseEntity.status(201) 51 | .body("I'm sorry I don't have the information you are looking for"); 52 | } 53 | 54 | @PostMapping("/store-embeddings") 55 | public ResponseEntity ingestData() { 56 | try { 57 | //dataIngestionService.load(); 58 | return ResponseEntity.ok("Success"); 59 | } catch (Exception e) { 60 | return ResponseEntity.status(500).body(e.getLocalizedMessage()); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /rag-examples/src/main/java/com/howtodoinjava/ai/chat/redis/service/DataIngestionService.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.chat.redis.service; 2 | 3 | /*import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.ai.reader.ExtractedTextFormatter; 6 | import org.springframework.ai.reader.pdf.PagePdfDocumentReader; 7 | import org.springframework.ai.reader.pdf.config.PdfDocumentReaderConfig; 8 | import org.springframework.ai.transformer.splitter.TokenTextSplitter; 9 | import org.springframework.ai.vectorstore.VectorStore; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.beans.factory.annotation.Value; 12 | import org.springframework.core.io.Resource; 13 | import org.springframework.stereotype.Service;*/ 14 | 15 | //@Service 16 | public class DataIngestionService { 17 | 18 | /*private static final Logger logger = LoggerFactory.getLogger(DataIngestionService.class); 19 | 20 | @Value("classpath:/data/spring-boot-reference.pdf") 21 | Resource pdfFile; 22 | 23 | @Autowired 24 | VectorStore vectorStore; 25 | 26 | public void load() { 27 | PagePdfDocumentReader pdfReader = new PagePdfDocumentReader(this.pdfFile, 28 | PdfDocumentReaderConfig.builder() 29 | .withPageExtractedTextFormatter(ExtractedTextFormatter.builder() 30 | .withNumberOfBottomTextLinesToDelete(3) 31 | .withNumberOfTopPagesToSkipBeforeDelete(1) 32 | .build()) 33 | .withPagesPerDocument(1) 34 | .build()); 35 | 36 | var tokenTextSplitter = new TokenTextSplitter(); 37 | this.vectorStore.accept(tokenTextSplitter.apply(pdfReader.get())); 38 | }*/ 39 | } 40 | -------------------------------------------------------------------------------- /rag-examples/src/main/java/com/howtodoinjava/ai/chat/redis/service/SimilaritySearchService.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.chat.redis.service; 2 | 3 | import java.util.List; 4 | import org.springframework.ai.document.Document; 5 | import org.springframework.ai.vectorstore.VectorStore; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | 8 | //@Service 9 | public class SimilaritySearchService { 10 | 11 | @Autowired 12 | private VectorStore vectorStore; 13 | 14 | public List searchData(String query) { 15 | return vectorStore.similaritySearch(query); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rag-examples/src/main/java/com/howtodoinjava/ai/chromaWithOpenAI/Application.java: -------------------------------------------------------------------------------- 1 | package com.howtodoinjava.ai.chromaWithOpenAI; 2 | 3 | import java.util.List; 4 | import java.util.Objects; 5 | import org.springframework.ai.chat.client.ChatClient; 6 | import org.springframework.ai.chat.client.advisor.QuestionAnswerAdvisor; 7 | import org.springframework.ai.chat.model.ChatModel; 8 | import org.springframework.ai.chroma.ChromaApi; 9 | import org.springframework.ai.document.Document; 10 | import org.springframework.ai.embedding.EmbeddingModel; 11 | import org.springframework.ai.reader.ExtractedTextFormatter; 12 | import org.springframework.ai.reader.pdf.PagePdfDocumentReader; 13 | import org.springframework.ai.reader.pdf.config.PdfDocumentReaderConfig; 14 | import org.springframework.ai.vectorstore.ChromaVectorStore; 15 | import org.springframework.ai.vectorstore.SearchRequest; 16 | import org.springframework.ai.vectorstore.VectorStore; 17 | import org.springframework.beans.factory.annotation.Autowired; 18 | import org.springframework.beans.factory.annotation.Value; 19 | import org.springframework.boot.CommandLineRunner; 20 | import org.springframework.boot.autoconfigure.SpringBootApplication; 21 | import org.springframework.boot.builder.SpringApplicationBuilder; 22 | import org.springframework.context.annotation.Bean; 23 | import org.springframework.context.annotation.Configuration; 24 | import org.springframework.core.io.Resource; 25 | import org.springframework.http.client.SimpleClientHttpRequestFactory; 26 | import org.springframework.web.client.RestClient; 27 | 28 | @SpringBootApplication 29 | public class Application implements CommandLineRunner { 30 | 31 | // Start Chroma DB before running this example 32 | // docker run -it --rm --name chroma -p 8000:8000 ghcr.io/chroma-core/chroma:0.4.15 33 | 34 | public static void main(String[] args) { 35 | new SpringApplicationBuilder(Application.class) 36 | .run(args); 37 | } 38 | 39 | @Autowired 40 | EmbeddingModel embeddingModel; 41 | 42 | @Autowired 43 | ChatModel chatModel; 44 | 45 | @Autowired 46 | VectorStore vectorStore; 47 | 48 | @Value("classpath:/data/current-affairs.pdf") 49 | Resource pdfFile; 50 | 51 | @Value("classpath:/data/vectorStoreRawData.json") 52 | private String vectorStoreRawData; 53 | 54 | @Override 55 | public void run(String... args) throws Exception { 56 | 57 | Objects.requireNonNull(pdfFile); 58 | Objects.requireNonNull(vectorStore); 59 | Objects.requireNonNull(vectorStoreRawData); 60 | 61 | //1. Load Data 62 | 63 | PagePdfDocumentReader pdfReader = new PagePdfDocumentReader(this.pdfFile, 64 | PdfDocumentReaderConfig.builder() 65 | .withPageExtractedTextFormatter(ExtractedTextFormatter.builder() 66 | .withNumberOfBottomTextLinesToDelete(3) 67 | .withNumberOfTopPagesToSkipBeforeDelete(1) 68 | .build()) 69 | .withPagesPerDocument(1) 70 | .build()); 71 | 72 | List documents = pdfReader.read() 73 | .stream() 74 | .toList(); 75 | 76 | vectorStore.add(documents); 77 | 78 | System.out.println("=============VECTORS ARE LOADED SUCCESSFULLY============"); 79 | 80 | // Writing vectors to a file so that we do not need to generate from the embedded model 81 | /*var vectorStoreFile = new File("c:/temp/vectorStoreRawData.json"); 82 | var simpleVectorStore = new SimpleVectorStore(embeddingModel); 83 | simpleVectorStore.add(documents); 84 | simpleVectorStore.save(vectorStoreFile);*/ 85 | 86 | //2. Search Data 87 | 88 | ChatClient chatClient = ChatClient.builder(chatModel) 89 | .defaultAdvisors(new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults())) 90 | .build(); 91 | 92 | String aiResponse = chatClient.prompt() 93 | .user("India’s first undersea tunnel is being constructed in which city?") 94 | .call() 95 | .content(); 96 | 97 | System.out.println(aiResponse); 98 | } 99 | } 100 | 101 | @Configuration 102 | class ApplicationConfiguration { 103 | 104 | @Bean 105 | public RestClient.Builder builder() { 106 | return RestClient.builder().requestFactory(new SimpleClientHttpRequestFactory()); 107 | } 108 | 109 | @Bean 110 | public ChromaApi chromaApi(RestClient.Builder restClientBuilder) { 111 | String chromaUrl = "http://localhost:8000"; 112 | ChromaApi chromaApi = new ChromaApi(chromaUrl, restClientBuilder); 113 | return chromaApi; 114 | } 115 | 116 | @Bean 117 | public VectorStore chromaVectorStore(EmbeddingModel embeddingModel, ChromaApi chromaApi) { 118 | return new ChromaVectorStore(embeddingModel, chromaApi, "TestCollection", false); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /rag-examples/src/main/java/elasticSearch/RagWithElasticSearchAndOpenAI.java: -------------------------------------------------------------------------------- 1 | package elasticSearch; 2 | 3 | import java.util.Objects; 4 | import org.springframework.ai.openai.OpenAiEmbeddingModel; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.boot.CommandLineRunner; 8 | import org.springframework.boot.WebApplicationType; 9 | import org.springframework.boot.autoconfigure.SpringBootApplication; 10 | import org.springframework.boot.builder.SpringApplicationBuilder; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.core.io.Resource; 13 | 14 | @SpringBootApplication 15 | public class RagWithElasticSearchAndOpenAI implements CommandLineRunner { 16 | 17 | public static void main(String[] args) { 18 | new SpringApplicationBuilder(RagWithElasticSearchAndOpenAI.class) 19 | .web(WebApplicationType.NONE) 20 | .run(args); 21 | } 22 | 23 | @Value("classpath:/data/statement.pdf") 24 | Resource pdfFile; 25 | 26 | /*@Autowired 27 | private ElasticsearchVectorStore vectorStore;*/ 28 | 29 | @Autowired 30 | OpenAiEmbeddingModel embeddingModel; 31 | @Override 32 | public void run(String... args) throws Exception { 33 | 34 | Objects.requireNonNull(pdfFile); 35 | Objects.requireNonNull(embeddingModel); 36 | //Objects.requireNonNull(vectorStore); 37 | 38 | /*//1. Read Pdf Document 39 | 40 | PagePdfDocumentReader pdfReader = new PagePdfDocumentReader(this.pdfFile, 41 | PdfDocumentReaderConfig.builder() 42 | .withPageExtractedTextFormatter(ExtractedTextFormatter.builder() 43 | .withNumberOfBottomTextLinesToDelete(3) 44 | .withNumberOfTopPagesToSkipBeforeDelete(1) 45 | .build()) 46 | .withPagesPerDocument(1) 47 | .build()); 48 | 49 | List documents = pdfReader.read() 50 | .stream() 51 | .map(document -> document.getContent()) 52 | .toList(); 53 | 54 | // 2. Create embeddings 55 | 56 | EmbeddingResponse embeddingResponse = embeddingModel.call( 57 | new EmbeddingRequest(documents, 58 | OpenAiEmbeddingOptions.builder() 59 | .withModel("text-embedding-3-small") 60 | .build())); 61 | 62 | embeddingResponse.getResults().stream().forEach(System.out::println);*/ 63 | 64 | //3. Store embedding in elastic-search database 65 | //vectorStore. 66 | 67 | 68 | 69 | } 70 | } 71 | 72 | @Configuration 73 | class ApplicationConfiguration { 74 | 75 | } 76 | -------------------------------------------------------------------------------- /rag-examples/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | logging.level.web=debug 2 | 3 | spring.ai.openai.api-key=${OPENAI_API_KEY} 4 | spring.ai.openai.base-url=https://api.openai.com 5 | spring.ai.openai.chat.base-url=https://api.openai.com 6 | spring.ai.openai.chat.options.model=gpt-4o 7 | spring.ai.openai.embedding.options.model=text-embedding-3-small 8 | 9 | spring.docker.compose.file = ./rag-examples/docker-compose.yml 10 | spring.docker.compose.lifecycle-management = start-only -------------------------------------------------------------------------------- /rag-examples/src/main/resources/data/current-affairs.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lokeshgupta1981/spring-ai-examples/182edc364610399dc41508e99bb605ba642b451f/rag-examples/src/main/resources/data/current-affairs.pdf --------------------------------------------------------------------------------