Examples
Table of Contents
This section provides comprehensive examples of using Spring AI Watsonx.ai integration in various scenarios, from simple use cases to complex enterprise applications.
Getting Started Example
A simple Spring Boot application demonstrating basic chat functionality:
@SpringBootApplication
public class WatsonxAiDemoApplication {
public static void main(String[] args) {
SpringApplication.run(WatsonxAiDemoApplication.class, args);
}
}
@RestController
public class ChatController {
private final WatsonxAiChatModel chatModel;
public ChatController(WatsonxAiChatModel chatModel) {
this.chatModel = chatModel;
}
@GetMapping("/chat")
public String chat(@RequestParam String message) {
return chatModel.call(message);
}
@GetMapping("/chat/stream")
public Flux<String> chatStream(@RequestParam String message) {
return chatModel.stream(new Prompt(message))
.map(response -> response.getResult().getOutput().getContent());
}
}
Configuration:
spring:
ai:
watsonx:
ai:
api-key: ${WATSONX_AI_API_KEY}
url: ${WATSONX_AI_URL}
project-id: ${WATSONX_AI_PROJECT_ID}
Customer Support Chatbot
A more advanced example implementing a customer support chatbot with context and function calling:
@Service
public class CustomerSupportService {
private final WatsonxAiChatModel chatModel;
private final CustomerRepository customerRepository;
private final OrderService orderService;
public CustomerSupportService(WatsonxAiChatModel chatModel,
CustomerRepository customerRepository,
OrderService orderService) {
this.chatModel = chatModel;
this.customerRepository = customerRepository;
this.orderService = orderService;
}
public String handleCustomerQuery(String customerId, String query,
List<String> conversationHistory) {
// Get customer context
Customer customer = customerRepository.findById(customerId);
// Build conversation context
List<Message> messages = new ArrayList<>();
// System message with customer context
messages.add(new SystemMessage(buildSystemPrompt(customer)));
// Add conversation history
conversationHistory.forEach(msg ->
messages.add(new UserMessage(msg)));
// Add current query
messages.add(new UserMessage(query));
// Configure with function calling
var options = WatsonxAiChatOptions.builder()
.withModel("ibm/granite-13b-chat-v2")
.withTemperature(0.3)
.withFunction("getOrderStatus")
.withFunction("getCustomerInfo")
.withFunction("createSupportTicket")
.build();
ChatResponse response = chatModel.call(new Prompt(messages, options));
return response.getResult().getOutput().getContent();
}
private String buildSystemPrompt(Customer customer) {
return String.format("""
You are a helpful customer support assistant for TechCorp.
Customer Information:
- Name: %s
- Customer ID: %s
- Tier: %s
- Account Status: %s
Guidelines:
- Be helpful and professional
- Use the available functions to get accurate information
- If you cannot resolve an issue, create a support ticket
- Always verify customer identity before sharing sensitive information
""",
customer.getName(),
customer.getId(),
customer.getTier(),
customer.getStatus());
}
}
@Component
public class CustomerSupportFunctions {
@Autowired
private OrderService orderService;
@Bean
@Description("Get the status of a customer order")
public Function<OrderStatusRequest, OrderStatusResponse> getOrderStatus() {
return request -> {
Order order = orderService.findByOrderNumber(request.orderNumber());
return new OrderStatusResponse(
order.getOrderNumber(),
order.getStatus(),
order.getEstimatedDelivery(),
order.getTrackingNumber()
);
};
}
@Bean
@Description("Create a support ticket for customer issues")
public Function<SupportTicketRequest, SupportTicketResponse> createSupportTicket() {
return request -> {
String ticketId = supportTicketService.createTicket(
request.customerId(),
request.issue(),
request.priority()
);
return new SupportTicketResponse(ticketId, "Ticket created successfully");
};
}
public record OrderStatusRequest(String orderNumber) {}
public record OrderStatusResponse(String orderNumber, String status,
LocalDate estimatedDelivery, String trackingNumber) {}
public record SupportTicketRequest(String customerId, String issue, String priority) {}
public record SupportTicketResponse(String ticketId, String message) {}
}
Document Analysis Service
An example using embedding models for document similarity and chat models for analysis:
@Service
public class DocumentAnalysisService {
private final WatsonxAiChatModel chatModel;
private final WatsonxAiEmbeddingModel embeddingModel;
private final DocumentRepository documentRepository;
public DocumentAnalysisService(WatsonxAiChatModel chatModel,
WatsonxAiEmbeddingModel embeddingModel,
DocumentRepository documentRepository) {
this.chatModel = chatModel;
this.embeddingModel = embeddingModel;
this.documentRepository = documentRepository;
}
public DocumentAnalysisResult analyzeDocument(String documentContent) {
// Generate summary using chat model
String summary = generateSummary(documentContent);
// Extract key topics
List<String> keyTopics = extractKeyTopics(documentContent);
// Find similar documents using embeddings
List<Document> similarDocuments = findSimilarDocuments(documentContent);
// Generate insights
String insights = generateInsights(documentContent, summary, similarDocuments);
return new DocumentAnalysisResult(summary, keyTopics, similarDocuments, insights);
}
private String generateSummary(String content) {
var options = WatsonxAiChatOptions.builder()
.withModel("ibm/granite-13b-chat-v2")
.withTemperature(0.3)
.withMaxNewTokens(500)
.build();
String prompt = String.format("""
Please provide a concise summary of the following document.
Focus on the main points, key findings, and important conclusions.
Document:
%s
Summary:
""", content);
return chatModel.call(new Prompt(prompt, options));
}
private List<String> extractKeyTopics(String content) {
var options = WatsonxAiChatOptions.builder()
.withModel("ibm/granite-13b-chat-v2")
.withTemperature(0.2)
.withMaxNewTokens(200)
.build();
String prompt = String.format("""
Extract the key topics from this document.
Return only the topics as a comma-separated list.
Document:
%s
Key topics:
""", content);
String response = chatModel.call(new Prompt(prompt, options));
return Arrays.stream(response.split(","))
.map(String::trim)
.collect(Collectors.toList());
}
private List<Document> findSimilarDocuments(String content) {
// Generate embedding for the input document
List<Double> contentEmbedding = embeddingModel.embed(content);
// Get all document embeddings from repository
List<Document> allDocuments = documentRepository.findAll();
// Calculate similarities and return top matches
return allDocuments.stream()
.map(doc -> {
List<Double> docEmbedding = parseEmbedding(doc.getEmbedding());
double similarity = calculateCosineSimilarity(contentEmbedding, docEmbedding);
return new ScoredDocument(doc, similarity);
})
.sorted((a, b) -> Double.compare(b.getScore(), a.getScore()))
.limit(5)
.map(ScoredDocument::getDocument)
.collect(Collectors.toList());
}
private String generateInsights(String content, String summary,
List<Document> similarDocuments) {
String similarDocsContext = similarDocuments.stream()
.map(doc -> "- " + doc.getTitle() + ": " + doc.getSummary())
.collect(Collectors.joining("\n"));
var options = WatsonxAiChatOptions.builder()
.withModel("ibm/granite-13b-chat-v2")
.withTemperature(0.4)
.withMaxNewTokens(800)
.build();
String prompt = String.format("""
Based on the document summary and similar documents, provide insights about:
1. Trends and patterns
2. Unique aspects of this document
3. Recommendations for further analysis
Document Summary:
%s
Similar Documents:
%s
Insights:
""", summary, similarDocsContext);
return chatModel.call(new Prompt(prompt, options));
}
// Helper method for cosine similarity calculation
private double calculateCosineSimilarity(List<Double> a, List<Double> b) {
double dotProduct = 0.0;
double normA = 0.0;
double normB = 0.0;
for (int i = 0; i < a.size(); i++) {
dotProduct += a.get(i) * b.get(i);
normA += Math.pow(a.get(i), 2);
normB += Math.pow(b.get(i), 2);
}
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
}
}
Batch Processing Example
Processing large volumes of documents using Spring Batch and Watsonx.ai:
@Configuration
@EnableBatchProcessing
public class DocumentProcessingBatchConfig {
@Autowired
private WatsonxAiChatModel chatModel;
@Bean
public Job documentProcessingJob(JobRepository jobRepository,
Step documentProcessingStep) {
return new JobBuilder("documentProcessingJob", jobRepository)
.start(documentProcessingStep)
.build();
}
@Bean
public Step documentProcessingStep(JobRepository jobRepository,
PlatformTransactionManager transactionManager,
ItemReader<Document> reader,
ItemProcessor<Document, ProcessedDocument> processor,
ItemWriter<ProcessedDocument> writer) {
return new StepBuilder("documentProcessingStep", jobRepository)
.<Document, ProcessedDocument>chunk(10, transactionManager)
.reader(reader)
.processor(processor)
.writer(writer)
.build();
}
@Bean
@StepScope
public FlatFileItemReader<Document> documentReader() {
return new FlatFileItemReaderBuilder<Document>()
.name("documentReader")
.resource(new ClassPathResource("documents.csv"))
.delimited()
.names("id", "title", "content", "category")
.targetType(Document.class)
.build();
}
@Bean
public ItemProcessor<Document, ProcessedDocument> documentProcessor() {
return new DocumentProcessor(chatModel);
}
@Bean
public ItemWriter<ProcessedDocument> documentWriter() {
return new JdbcBatchItemWriterBuilder<ProcessedDocument>()
.dataSource(dataSource)
.sql("INSERT INTO processed_documents (id, title, summary, sentiment, category) " +
"VALUES (:id, :title, :summary, :sentiment, :category)")
.beanMapped()
.build();
}
}
@Component
public class DocumentProcessor implements ItemProcessor<Document, ProcessedDocument> {
private final WatsonxAiChatModel chatModel;
public DocumentProcessor(WatsonxAiChatModel chatModel) {
this.chatModel = chatModel;
}
@Override
public ProcessedDocument process(Document document) throws Exception {
// Generate summary
String summary = generateSummary(document.getContent());
// Analyze sentiment
String sentiment = analyzeSentiment(document.getContent());
// Categorize document
String category = categorizeDocument(document.getContent());
return new ProcessedDocument(
document.getId(),
document.getTitle(),
summary,
sentiment,
category
);
}
private String generateSummary(String content) {
var options = WatsonxAiChatOptions.builder()
.withModel("ibm/granite-13b-chat-v2")
.withTemperature(0.3)
.withMaxNewTokens(200)
.build();
String prompt = "Summarize this document in 2-3 sentences:\n" + content;
return chatModel.call(new Prompt(prompt, options));
}
private String analyzeSentiment(String content) {
var options = WatsonxAiChatOptions.builder()
.withModel("ibm/granite-13b-chat-v2")
.withTemperature(0.1)
.withMaxNewTokens(10)
.build();
String prompt = "Analyze the sentiment of this text. " +
"Respond with only: positive, negative, or neutral.\n" + content;
return chatModel.call(new Prompt(prompt, options));
}
private String categorizeDocument(String content) {
var options = WatsonxAiChatOptions.builder()
.withModel("ibm/granite-13b-chat-v2")
.withTemperature(0.2)
.withMaxNewTokens(20)
.build();
String prompt = "Categorize this document into one of: " +
"technology, business, science, politics, sports, entertainment.\n" + content;
return chatModel.call(new Prompt(prompt, options));
}
}
Reactive Streaming Example
Using Spring WebFlux for real-time AI-powered chat:
@RestController
public class StreamingChatController {
private final WatsonxAiChatModel chatModel;
public StreamingChatController(WatsonxAiChatModel chatModel) {
this.chatModel = chatModel;
}
@GetMapping(value = "/stream/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> streamChat(@RequestParam String message) {
return chatModel.stream(new Prompt(message))
.map(response -> response.getResult().getOutput().getContent())
.map(content -> ServerSentEvent.<String>builder()
.data(content)
.build());
}
@PostMapping(value = "/stream/conversation", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<ConversationResponse>> streamConversation(
@