Skip to main content
Version: 1.0.2

Examples

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-3-3-8b-instruct")
.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-3-3-8b-instruct")
.withTemperature(0.3)
.withMaxCompletionTokens(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-3-3-8b-instruct")
.withTemperature(0.2)
.withMaxCompletionTokens(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-3-3-8b-instruct")
.withTemperature(0.4)
.withMaxCompletionTokens(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-3-3-8b-instruct")
.withTemperature(0.3)
.withMaxCompletionTokens(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-3-3-8b-instruct")
.withTemperature(0.1)
.withMaxCompletionTokens(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-3-3-8b-instruct")
.withTemperature(0.2)
.withMaxCompletionTokens(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());
}
}