12 KiB
12 KiB
LangChain4j Spring Boot Integration - Examples
Comprehensive implementation examples for Spring Boot integration with LangChain4j.
Basic Setup Example
Complete Spring Boot Application
@SpringBootApplication
public class Langchain4jApplication {
public static void main(String[] args) {
SpringApplication.run(Langchain4jApplication.class, args);
}
}
@Configuration
public class AiConfiguration {
@Bean
@Profile("openai")
public ChatModel openAiChatModel(@Value("${langchain4j.open-ai.chat-model.api-key}") String apiKey) {
return OpenAiChatModel.builder()
.apiKey(apiKey)
.modelName("gpt-4o-mini")
.temperature(0.7)
.maxTokens(1000)
.logRequests(true)
.logResponses(true)
.build();
}
@Bean
public EmbeddingModel openAiEmbeddingModel(@Value("${langchain4j.open-ai.embedding-model.api-key}") String apiKey) {
return OpenAiEmbeddingModel.builder()
.apiKey(apiKey)
.modelName("text-embedding-3-small")
.dimensions(1536)
.build();
}
}
@AiService
interface CustomerSupportAssistant {
@SystemMessage("You are a helpful customer support agent for TechCorp. " +
"Be polite, professional, and try to resolve customer issues efficiently. " +
"If you cannot resolve an issue, escalate to a human agent.")
String handleInquiry(String customerMessage);
@UserMessage("Analyze this customer feedback and extract sentiment: {{feedback}}")
@SystemMessage("Return only: POSITIVE, NEGATIVE, or NEUTRAL")
String analyzeSentiment(String feedback);
@UserMessage("Extract key entities from this text: {{text}}")
@SystemMessage("Return a JSON object with entities as keys and their types as values")
String extractEntities(String text);
}
@RestController
@RequestMapping("/api/support")
@RequiredArgsConstructor
public class CustomerSupportController {
private final CustomerSupportAssistant assistant;
@PostMapping("/inquiry")
public ResponseEntity<SupportResponse> handleInquiry(@RequestBody @Valid SupportRequest request) {
String response = assistant.handleInquiry(request.getMessage());
return ResponseEntity.ok(new SupportResponse(response, Instant.now()));
}
@PostMapping("/sentiment")
public ResponseEntity<SentimentResponse> analyzeSentiment(@RequestBody @Valid SentimentRequest request) {
String sentiment = assistant.analyzeSentiment(request.getFeedback());
return ResponseEntity.ok(new SentimentResponse(sentiment, Instant.now()));
}
@PostMapping("/entities")
public ResponseEntity<EntitiesResponse> extractEntities(@RequestBody @Valid EntitiesRequest request) {
String entities = assistant.extractEntities(request.getText());
return ResponseEntity.ok(new EntitiesResponse(entities, Instant.now()));
}
}
// DTO Classes
record SupportRequest(String message) {}
record SupportResponse(String response, Instant timestamp) {}
record SentimentRequest(String feedback) {}
record SentimentResponse(String sentiment, Instant timestamp) {}
record EntitiesRequest(String text) {}
record EntitiesResponse(String entities, Instant timestamp) {}
2. Custom AI Service Bean Configuration
Scenario: Configure AI services as Spring beans.
@Configuration
public class AiConfig {
@Bean
public ChatModel chatModel() {
return OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4o-mini")
.temperature(0.7)
.build();
}
@Bean
public EmbeddingModel embeddingModel() {
return OpenAiEmbeddingModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("text-embedding-3-small")
.build();
}
@Bean
public DocumentAssistant documentAssistant(ChatModel chatModel) {
return AiServices.builder(DocumentAssistant.class)
.chatModel(chatModel)
.chatMemory(MessageWindowChatMemory.withMaxMessages(10))
.build();
}
}
interface DocumentAssistant {
String chat(String message);
}
3. REST API with AI Service
Scenario: Expose AI functionality via REST endpoints.
@RestController
@RequestMapping("/api/chat")
public class ChatController {
private final ChatAssistant assistant;
@Autowired
public ChatController(ChatAssistant assistant) {
this.assistant = assistant;
}
@PostMapping
public ResponseEntity<ChatResponse> chat(@RequestBody ChatRequest request) {
try {
String response = assistant.chat(request.getMessage());
return ResponseEntity.ok(new ChatResponse(response));
} catch (Exception e) {
return ResponseEntity.internalServerError()
.body(new ChatResponse("Error: " + e.getMessage()));
}
}
@PostMapping("/stream")
public ResponseEntity<StreamingResponseBody> streamChat(@RequestBody ChatRequest request) {
return ResponseEntity.ok(outputStream -> {
var streamAssistant = streamingAssistant;
var stream = streamAssistant.streamChat(request.getMessage());
stream.onNext(token -> {
try {
outputStream.write(token.getBytes());
outputStream.flush();
} catch (IOException e) {
// Handle write error
}
}).start();
});
}
}
@Data
class ChatRequest {
private String message;
}
@Data
class ChatResponse {
private String response;
}
4. Service with RAG Integration
Scenario: Service layer with document search and retrieval.
@Service
public class KnowledgeBaseService {
private final DocumentAssistant assistant;
private final EmbeddingStore<TextSegment> embeddingStore;
private final EmbeddingModel embeddingModel;
@Autowired
public KnowledgeBaseService(
DocumentAssistant assistant,
EmbeddingStore<TextSegment> embeddingStore,
EmbeddingModel embeddingModel) {
this.assistant = assistant;
this.embeddingStore = embeddingStore;
this.embeddingModel = embeddingModel;
}
public void ingestDocument(String content, Map<String, Object> metadata) {
var document = Document.from(content);
document.metadata().putAll(metadata);
var ingestor = EmbeddingStoreIngestor.builder()
.embeddingModel(embeddingModel)
.embeddingStore(embeddingStore)
.documentSplitter(DocumentSplitters.recursive(500, 50))
.build();
ingestor.ingest(document);
}
public String answerQuestion(String question) {
return assistant.answerAbout(question);
}
}
interface DocumentAssistant {
String answerAbout(String question);
}
5. Scheduled Task for Document Updates
Scenario: Periodically update knowledge base.
@Service
public class DocumentUpdateService {
private final EmbeddingStore<TextSegment> embeddingStore;
private final EmbeddingModel embeddingModel;
@Autowired
public DocumentUpdateService(
EmbeddingStore<TextSegment> embeddingStore,
EmbeddingModel embeddingModel) {
this.embeddingStore = embeddingStore;
this.embeddingModel = embeddingModel;
}
@Scheduled(fixedRate = 86400000) // Daily
public void updateDocuments() {
var documents = fetchLatestDocuments();
var ingestor = EmbeddingStoreIngestor.builder()
.embeddingModel(embeddingModel)
.embeddingStore(embeddingStore)
.build();
documents.forEach(ingestor::ingest);
logger.info("Documents updated successfully");
}
private List<Document> fetchLatestDocuments() {
// Fetch from database or external API
return Collections.emptyList();
}
}
6. Controller with Tool Integration
Scenario: AI service with business logic tools.
@Service
public class BusinessLogicService {
@Tool("Get user by ID")
public User getUser(@P("user ID") String userId) {
// Implementation
return new User(userId);
}
@Tool("Calculate discount")
public double calculateDiscount(@P("purchase amount") double amount) {
if (amount > 1000) return 0.15;
if (amount > 500) return 0.10;
return 0.05;
}
}
@Service
public class ToolAssistant {
private final ChatModel chatModel;
private final BusinessLogicService businessLogic;
@Autowired
public ToolAssistant(ChatModel chatModel, BusinessLogicService businessLogic) {
this.chatModel = chatModel;
this.businessLogic = businessLogic;
}
public String processRequest(String request) {
return AiServices.builder(Assistant.class)
.chatModel(chatModel)
.tools(businessLogic)
.build()
.chat(request);
}
}
interface Assistant {
String chat(String message);
}
7. Error Handling with Spring Exception Handler
Scenario: Centralized error handling for AI services.
@ControllerAdvice
public class AiExceptionHandler {
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ErrorResponse> handleBadRequest(IllegalArgumentException e) {
return ResponseEntity.badRequest()
.body(new ErrorResponse("Invalid input: " + e.getMessage()));
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleError(Exception e) {
logger.error("Error in AI service", e);
return ResponseEntity.internalServerError()
.body(new ErrorResponse("An error occurred: " + e.getMessage()));
}
}
@Data
class ErrorResponse {
private String message;
}
8. Configuration Properties
Scenario: Externalize AI configuration.
@Configuration
@ConfigurationProperties(prefix = "app.ai")
@Data
public class AiProperties {
private String openaiApiKey;
private String openaiModel = "gpt-4o-mini";
private double temperature = 0.7;
private int maxTokens = 2000;
private String embeddingModel = "text-embedding-3-small";
private int memorySize = 10;
private String vectorStoreType = "in-memory";
}
// application.yml
app:
ai:
openai-api-key: ${OPENAI_API_KEY}
openai-model: gpt-4o-mini
temperature: 0.7
max-tokens: 2000
embedding-model: text-embedding-3-small
memory-size: 10
vector-store-type: pinecone
9. Integration Testing
Scenario: Test AI services with Spring Boot Test.
@SpringBootTest
class ChatServiceTest {
@MockBean
private ChatModel chatModel;
@Autowired
private ChatService chatService;
@Test
void testChat() {
when(chatModel.chat("Hello"))
.thenReturn("Hi there!");
String response = chatService.chat("Hello");
assertEquals("Hi there!", response);
}
}
10. Async Processing with CompletableFuture
Scenario: Non-blocking AI service calls.
@Service
@EnableAsync
public class AsyncChatService {
private final ChatModel chatModel;
@Autowired
public AsyncChatService(ChatModel chatModel) {
this.chatModel = chatModel;
}
@Async
public CompletableFuture<String> chatAsync(String message) {
try {
String response = chatModel.chat(message);
return CompletableFuture.completedFuture(response);
} catch (Exception e) {
return CompletableFuture.failedFuture(e);
}
}
}
// Usage in controller
@RestController
public class AsyncController {
@Autowired
private AsyncChatService asyncChatService;
@PostMapping("/chat/async")
public CompletableFuture<ResponseEntity<String>> chatAsync(@RequestBody ChatRequest request) {
return asyncChatService.chatAsync(request.getMessage())
.thenApply(ResponseEntity::ok)
.exceptionally(e -> ResponseEntity.internalServerError().build());
}
}
Configuration Examples
Maven Dependency
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-spring-boot-starter</artifactId>
<version>0.27.0</version>
</dependency>
Gradle
implementation 'dev.langchain4j:langchain4j-spring-boot-starter:0.27.0'