diff --git a/spring-ai-2/src/main/java/com/baeldung/groq/ChatAppConfiguration.java b/spring-ai-2/src/main/java/com/baeldung/groq/ChatAppConfiguration.java new file mode 100644 index 000000000000..6575f0d96ba2 --- /dev/null +++ b/spring-ai-2/src/main/java/com/baeldung/groq/ChatAppConfiguration.java @@ -0,0 +1,30 @@ +package com.baeldung.groq; + +import org.springframework.ai.openai.OpenAiChatModel; +import org.springframework.ai.openai.api.OpenAiApi; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +@Configuration(proxyBeanMethods = false) +@Profile("customgroq") +public class ChatAppConfiguration { + + @Value("${groq.api-key}") + private String GROQ_API_KEY; + + @Value("${groq.base-url}") + private String GROQ_API_URL; + + @Bean + public OpenAiChatModel customGroqChatClient() { + OpenAiApi groqOpenAiApi = new OpenAiApi.Builder() + .apiKey(GROQ_API_KEY) + .baseUrl(GROQ_API_URL) + .build(); + return OpenAiChatModel.builder() + .openAiApi(groqOpenAiApi) + .build(); + } +} diff --git a/spring-ai-2/src/main/java/com/baeldung/groq/CustomGroqChatService.java b/spring-ai-2/src/main/java/com/baeldung/groq/CustomGroqChatService.java new file mode 100644 index 000000000000..5177c77fa58c --- /dev/null +++ b/spring-ai-2/src/main/java/com/baeldung/groq/CustomGroqChatService.java @@ -0,0 +1,25 @@ +package com.baeldung.groq; + +import org.springframework.ai.chat.prompt.ChatOptions; +import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.openai.OpenAiChatModel; +import org.springframework.ai.openai.OpenAiChatOptions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class CustomGroqChatService { + @Autowired + private OpenAiChatModel customGroqChatClient; + + public String chat(String prompt, String model, Double temperature) { + ChatOptions chatOptions = OpenAiChatOptions.builder() + .model(model) + .temperature(temperature) + .build(); + return customGroqChatClient.call(new Prompt(prompt, chatOptions)) + .getResult() + .getOutput() + .getText(); + } +} diff --git a/spring-ai-2/src/main/java/com/baeldung/groq/GroqChatApplication.java b/spring-ai-2/src/main/java/com/baeldung/groq/GroqChatApplication.java new file mode 100644 index 000000000000..5248ea6d6e5e --- /dev/null +++ b/spring-ai-2/src/main/java/com/baeldung/groq/GroqChatApplication.java @@ -0,0 +1,24 @@ +package com.baeldung.groq; + +import org.springframework.ai.autoconfigure.anthropic.AnthropicAutoConfiguration; +import org.springframework.ai.autoconfigure.bedrock.converse.BedrockConverseProxyChatAutoConfiguration; +import org.springframework.ai.autoconfigure.ollama.OllamaAutoConfiguration; +import org.springframework.ai.autoconfigure.vectorstore.chroma.ChromaVectorStoreAutoConfiguration; +import org.springframework.ai.autoconfigure.vectorstore.pgvector.PgVectorStoreAutoConfiguration; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; + +@SpringBootApplication(exclude = { + OllamaAutoConfiguration.class, + AnthropicAutoConfiguration.class, + PgVectorStoreAutoConfiguration.class, + ChromaVectorStoreAutoConfiguration.class, + BedrockConverseProxyChatAutoConfiguration.class, + RedisAutoConfiguration.class +}) +public class GroqChatApplication { + public static void main(String[] args) { + SpringApplication.run(GroqChatApplication.class, args); + } +} diff --git a/spring-ai-2/src/main/java/com/baeldung/groq/GroqChatService.java b/spring-ai-2/src/main/java/com/baeldung/groq/GroqChatService.java new file mode 100644 index 000000000000..a75f36c39123 --- /dev/null +++ b/spring-ai-2/src/main/java/com/baeldung/groq/GroqChatService.java @@ -0,0 +1,21 @@ +package com.baeldung.groq; + +import org.springframework.ai.chat.prompt.ChatOptions; +import org.springframework.ai.openai.OpenAiChatModel; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class GroqChatService { + @Autowired + private OpenAiChatModel groqClient; + + public String chat(String prompt) { + + return groqClient.call(prompt); + } + + public ChatOptions getChatOptions() { + return groqClient.getDefaultOptions(); + } +} diff --git a/spring-ai-2/src/main/resources/application-customgroq.properties b/spring-ai-2/src/main/resources/application-customgroq.properties new file mode 100644 index 000000000000..1819433bdaa6 --- /dev/null +++ b/spring-ai-2/src/main/resources/application-customgroq.properties @@ -0,0 +1,4 @@ +spring.application.name=spring-ai-custom-groq-demo +groq.base-url=https://api.groq.com/openai +groq.api-key=gsk_XXXX +spring.autoconfigure.exclude=org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration \ No newline at end of file diff --git a/spring-ai-2/src/main/resources/application-groq.properties b/spring-ai-2/src/main/resources/application-groq.properties new file mode 100644 index 000000000000..941c1c7b4713 --- /dev/null +++ b/spring-ai-2/src/main/resources/application-groq.properties @@ -0,0 +1,8 @@ +spring.application.name=spring-ai-groq-demo +spring.ai.openai.base-url=https://api.groq.com/openai +spring.ai.openai.api-key=gsk_XXXX + +spring.ai.openai.chat.base-url=https://api.groq.com/openai +spring.ai.openai.chat.api-key=gsk_XXXX +spring.ai.openai.chat.options.temperature=0.7 +spring.ai.openai.chat.options.model=llama-3.3-70b-versatile diff --git a/spring-ai-2/src/test/java/com/baeldung/groq/GroqAutoconfiguredChatClientLiveTest.java b/spring-ai-2/src/test/java/com/baeldung/groq/GroqAutoconfiguredChatClientLiveTest.java new file mode 100644 index 000000000000..1bbe722921eb --- /dev/null +++ b/spring-ai-2/src/test/java/com/baeldung/groq/GroqAutoconfiguredChatClientLiveTest.java @@ -0,0 +1,52 @@ +package com.baeldung.groq; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ai.chat.prompt.ChatOptions; +import org.springframework.ai.openai.OpenAiChatOptions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +@SpringBootTest +@ActiveProfiles("groq") +public class GroqAutoconfiguredChatClientLiveTest { + final Logger logger = LoggerFactory.getLogger(GroqAutoconfiguredChatClientLiveTest.class); + @Autowired + private GroqChatService groqChatService; + + @Test + void whenCallOpenAIClient_thenReturnResponseFromGroq() { + + String prompt = """ + Context: + Support Ticket #98765: + Product: XYZ Wireless Mouse + Issue Description: The mouse connects intermittently to my laptop. + I've tried changing batteries and reinstalling drivers, + but the cursor still freezes randomly for a few seconds before resuming normal movement. + It affects productivity significantly. + Question: + Based on the support ticket, what is the primary technical issue + the user is experiencing with their 'XYZ Wireless Mouse'?; + """; + String response = groqChatService.chat(prompt); + + assertThat(response.toLowerCase()).isNotNull() + .isNotEmpty() + .containsAnyOf("laptop", "mouse", "connect"); + + ChatOptions openAiChatOptions = groqChatService.getChatOptions(); + String model = openAiChatOptions.getModel(); + Double temperature = openAiChatOptions.getTemperature(); + + assertThat(openAiChatOptions).isInstanceOf(OpenAiChatOptions.class); + assertThat(model).isEqualTo("llama-3.3-70b-versatile"); + assertThat(temperature).isEqualTo(Double.valueOf(0.7)); + logger.info("Response from Groq:{}", response); + } + +} diff --git a/spring-ai-2/src/test/java/com/baeldung/groq/GroqCustomChatClientLiveTest.java b/spring-ai-2/src/test/java/com/baeldung/groq/GroqCustomChatClientLiveTest.java new file mode 100644 index 000000000000..6778ba9e9800 --- /dev/null +++ b/spring-ai-2/src/test/java/com/baeldung/groq/GroqCustomChatClientLiveTest.java @@ -0,0 +1,38 @@ +package com.baeldung.groq; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +@SpringBootTest() +@ActiveProfiles("customgroq") +public class GroqCustomChatClientLiveTest { + private final Logger logger = LoggerFactory.getLogger(GroqCustomChatClientLiveTest.class); + + @Autowired + private CustomGroqChatService customGroqChatService; + + @Test + void whenCustomGroqClientCalled_thenReturnResponse() { + String prompt = """ + Context: + The Eiffel Tower is one of the most famous landmarks + in Paris, attracting millions of visitors each year. + Question: + In which city is the Eiffel Tower located? + """; + String response = customGroqChatService.chat(prompt, "llama-3.1-8b-instant", 0.8); + + assertThat(response) + .isNotNull() + .isNotEmpty() + .contains("Paris"); + logger.info("Response from custom Groq client: {}", response); + } + +}