Spring Boot Starter for OpenAI integration providing auto-configuration for chat completion, embeddings, image generation, audio speech synthesis, audio transcription, and content moderation models. Includes high-level ChatClient API and conversation memory support.
Practical examples showing how to use Spring AI OpenAI Starter in production applications.
Build an intelligent customer support chatbot with conversation memory:
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.memory.ChatMemory;
import org.springframework.stereotype.Service;
@Service
public class CustomerSupportBot {
private final ChatClient chatClient;
public CustomerSupportBot(ChatClient.Builder builder, ChatMemory chatMemory) {
this.chatClient = builder
.defaultSystem("""
You are a helpful customer support agent.
Be polite, concise, and helpful.
If you don't know the answer, admit it and offer to escalate.
""")
.defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory))
.build();
}
public String handleCustomerQuery(String customerId, String query) {
return chatClient.prompt()
.user(query)
.advisors(advisor -> advisor.param("conversationId", customerId))
.call()
.content();
}
}Implement Retrieval-Augmented Generation for document Q&A:
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.openai.OpenAiEmbeddingModel;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.document.Document;
import org.springframework.stereotype.Service;
@Service
public class DocumentAnalyzer {
private final ChatClient chatClient;
private final OpenAiEmbeddingModel embeddingModel;
private final VectorStore vectorStore;
public DocumentAnalyzer(
ChatClient.Builder builder,
OpenAiEmbeddingModel embeddingModel,
VectorStore vectorStore) {
this.chatClient = builder.build();
this.embeddingModel = embeddingModel;
this.vectorStore = vectorStore;
}
public void indexDocument(String content, Map<String, Object> metadata) {
Document document = new Document(content, metadata);
float[] embedding = embeddingModel.embed(document);
document.setEmbedding(embedding);
vectorStore.add(List.of(document));
}
public String queryDocuments(String question) {
// Find relevant documents
List<Document> similarDocs = vectorStore.similaritySearch(
SearchRequest.query(question).withTopK(3)
);
// Build context from similar documents
String context = similarDocs.stream()
.map(Document::getContent)
.collect(Collectors.joining("\n\n"));
// Query with context
return chatClient.prompt()
.system("Answer based on this context: " + context)
.user(question)
.call()
.content();
}
}Build a content moderation system for user-generated content:
import org.springframework.ai.openai.moderation.OpenAiModerationModel;
import org.springframework.ai.model.moderation.*;
import org.springframework.stereotype.Service;
@Service
public class ContentModerationService {
private final OpenAiModerationModel moderationModel;
public ContentModerationService(OpenAiModerationModel moderationModel) {
this.moderationModel = moderationModel;
}
public ModerationDecision moderateContent(String content) {
ModerationPrompt prompt = new ModerationPrompt(content);
ModerationResponse response = moderationModel.call(prompt);
Moderation moderation = response.getResult().getOutput();
for (ModerationResult result : moderation.getResults()) {
if (result.isFlagged()) {
return new ModerationDecision(
false,
"Content violates policy",
getFlaggedCategories(result.getCategories())
);
}
}
return new ModerationDecision(true, "Content approved", List.of());
}
private List<String> getFlaggedCategories(Categories categories) {
List<String> flagged = new ArrayList<>();
if (categories.isHate()) flagged.add("hate");
if (categories.isHarassment()) flagged.add("harassment");
if (categories.isSelfHarm()) flagged.add("self-harm");
if (categories.isSexual()) flagged.add("sexual");
if (categories.isViolence()) flagged.add("violence");
return flagged;
}
record ModerationDecision(boolean approved, String reason, List<String> categories) {}
}Create a service for generating marketing images:
import org.springframework.ai.openai.OpenAiImageModel;
import org.springframework.ai.image.*;
import org.springframework.stereotype.Service;
import java.util.Base64;
@Service
public class MarketingImageService {
private final OpenAiImageModel imageModel;
public MarketingImageService(OpenAiImageModel imageModel) {
this.imageModel = imageModel;
}
public byte[] generateProductImage(String description, ImageStyle style) {
OpenAiImageOptions options = OpenAiImageOptions.builder()
.model("dall-e-3")
.quality("hd")
.size("1024x1024")
.style(style == ImageStyle.REALISTIC ? "natural" : "vivid")
.responseFormat("b64_json")
.build();
ImagePrompt prompt = new ImagePrompt(
"Product image: " + description,
options
);
ImageResponse response = imageModel.call(prompt);
String base64Image = response.getResult().getOutput().getB64Json();
return Base64.getDecoder().decode(base64Image);
}
enum ImageStyle { REALISTIC, VIVID }
}Build a translation service with context awareness:
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.stereotype.Service;
@Service
public class TranslationService {
private final ChatClient chatClient;
public TranslationService(ChatClient.Builder builder) {
this.chatClient = builder
.defaultOptions(OpenAiChatOptions.builder()
.temperature(0.3) // Lower temperature for consistency
.build())
.build();
}
public String translate(String text, String targetLanguage, String context) {
return chatClient.prompt()
.system("""
You are a professional translator.
Translate accurately while preserving meaning and tone.
Context: {context}
""")
.user("Translate to {language}: {text}")
.advisors(advisor -> advisor
.param("language", targetLanguage)
.param("text", text)
.param("context", context))
.call()
.content();
}
}Transcribe audio and generate summaries:
import org.springframework.ai.openai.audio.transcription.OpenAiAudioTranscriptionModel;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
@Service
public class PodcastProcessor {
private final OpenAiAudioTranscriptionModel transcriptionModel;
private final ChatClient chatClient;
public PodcastProcessor(
OpenAiAudioTranscriptionModel transcriptionModel,
ChatClient.Builder builder) {
this.transcriptionModel = transcriptionModel;
this.chatClient = builder.build();
}
public PodcastSummary processEpisode(Resource audioFile) {
// Transcribe audio
TranscriptionResponse transcription = transcriptionModel.call(audioFile);
String transcript = transcription.getResult().getOutput();
// Generate summary
String summary = chatClient.prompt()
.system("Summarize this podcast transcript in 2-3 paragraphs")
.user(transcript)
.call()
.content();
// Extract key points
String keyPoints = chatClient.prompt()
.system("Extract 5 key points as a bullet list")
.user(transcript)
.call()
.content();
return new PodcastSummary(transcript, summary, keyPoints);
}
record PodcastSummary(String transcript, String summary, String keyPoints) {}
}AI-powered code review with specific guidelines:
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.stereotype.Service;
@Service
public class CodeReviewAssistant {
private final ChatClient chatClient;
public CodeReviewAssistant(ChatClient.Builder builder) {
this.chatClient = builder
.defaultSystem("""
You are an expert code reviewer.
Focus on:
- Security vulnerabilities
- Performance issues
- Code maintainability
- Best practices
Provide specific, actionable feedback.
""")
.defaultOptions(OpenAiChatOptions.builder()
.model("gpt-4o")
.temperature(0.3)
.build())
.build();
}
public CodeReview reviewCode(String code, String language) {
String review = chatClient.prompt()
.user("Review this {language} code:\n\n{code}")
.advisors(advisor -> advisor
.param("language", language)
.param("code", code))
.call()
.content();
return new CodeReview(review);
}
record CodeReview(String feedback) {}
}Implement streaming responses for better UX:
import org.springframework.ai.chat.client.ChatClient;
import reactor.core.publisher.Flux;
import org.springframework.stereotype.Service;
@Service
public class StreamingChatService {
private final ChatClient chatClient;
public StreamingChatService(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
public Flux<String> streamResponse(String userMessage) {
return chatClient.prompt()
.user(userMessage)
.stream()
.content();
}
// For Server-Sent Events endpoint
public Flux<String> streamChatSSE(String message) {
return streamResponse(message)
.map(chunk -> "data: " + chunk + "\n\n")
.concatWith(Flux.just("data: [DONE]\n\n"));
}
}Integrate external APIs using function calling:
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.model.function.FunctionCallback;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ToolConfiguration {
@Bean
public FunctionCallback weatherTool(RestTemplate restTemplate) {
return FunctionCallback.builder()
.function("getCurrentWeather", (WeatherRequest request) -> {
// Call actual weather API
return restTemplate.getForObject(
"https://api.weather.com?location=" + request.location(),
WeatherResponse.class
);
})
.description("Get current weather for a location")
.inputType(WeatherRequest.class)
.build();
}
record WeatherRequest(String location) {}
record WeatherResponse(String temperature, String conditions) {}
}
@Service
public class WeatherChatService {
private final ChatClient chatClient;
public WeatherChatService(ChatClient.Builder builder, FunctionCallback weatherTool) {
this.chatClient = builder
.defaultTools(weatherTool)
.build();
}
public String chatWithWeather(String userMessage) {
return chatClient.prompt()
.user(userMessage)
.call()
.content();
}
}Extract structured data from unstructured text:
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.stereotype.Service;
@Service
public class DataExtractionService {
private final ChatClient chatClient;
public DataExtractionService(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
public Person extractPersonInfo(String text) {
return chatClient.prompt()
.user("Extract person information from: " + text)
.call()
.entity(Person.class);
}
public List<Product> extractProducts(String invoice) {
return chatClient.prompt()
.user("Extract all products from this invoice: " + invoice)
.call()
.entity(new ParameterizedTypeReference<List<Product>>() {});
}
record Person(String name, Integer age, String occupation, String location) {}
record Product(String name, double price, int quantity) {}
}tessl i tessl/maven-org-springframework-ai--spring-ai-starter-model-openai@1.1.1