Core runtime module for Quarkus LangChain4j integration with declarative AI services, guardrails, and observability
AI Services are declarative interfaces that enable interaction with language models through CDI-managed beans. The @RegisterAiService annotation transforms a simple Java interface into a fully-functional AI service with automatic CDI bean registration, model integration, and lifecycle management.
import io.quarkiverse.langchain4j.RegisterAiService;
import io.quarkiverse.langchain4j.ModelName;
import io.quarkiverse.langchain4j.CreatedAware;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.StreamingChatModel;package io.quarkiverse.langchain4j;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.function.Supplier;
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.StreamingChatModel;
import dev.langchain4j.model.moderation.ModerationModel;
import dev.langchain4j.rag.RetrievalAugmentor;
import dev.langchain4j.service.tool.ToolProvider;
import io.quarkiverse.langchain4j.runtime.aiservice.SystemMessageProvider;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface RegisterAiService {
// Model configuration
Class<? extends Supplier<ChatModel>> chatLanguageModelSupplier()
default BeanChatLanguageModelSupplier.class;
Class<? extends Supplier<StreamingChatModel>> streamingChatLanguageModelSupplier()
default BeanStreamingChatLanguageModelSupplier.class;
String modelName() default "<default>";
// Tool configuration
Class<?>[] tools() default {};
int maxSequentialToolInvocations() default 0;
boolean allowContinuousForcedToolCalling() default false;
Class<? extends java.util.function.Function<dev.langchain4j.agent.tool.ToolExecutionRequest,
dev.langchain4j.data.message.ToolExecutionResultMessage>> toolHallucinationStrategy()
default BeanIfExistsToolHallucinationStrategy.class;
Class<? extends Supplier<ToolProvider>> toolProviderSupplier()
default BeanIfExistsToolProviderSupplier.class;
// Memory configuration
Class<? extends Supplier<ChatMemoryProvider>> chatMemoryProviderSupplier()
default BeanChatMemoryProviderSupplier.class;
// RAG configuration
Class<? extends Supplier<RetrievalAugmentor>> retrievalAugmentor()
default BeanIfExistsRetrievalAugmentorSupplier.class;
// Moderation
Class<? extends Supplier<ModerationModel>> moderationModelSupplier()
default BeanIfExistsModerationModelSupplier.class;
// System message provider
Class<? extends SystemMessageProvider> systemMessageProviderSupplier()
default NoSystemMessageProviderSupplier.class;
// Marker classes
final class BeanChatLanguageModelSupplier implements Supplier<ChatModel> {
public ChatModel get();
}
final class BeanStreamingChatLanguageModelSupplier implements Supplier<StreamingChatModel> {
public StreamingChatModel get();
}
final class BeanChatMemoryProviderSupplier implements Supplier<ChatMemoryProvider> {
public ChatMemoryProvider get();
}
final class NoChatMemoryProviderSupplier implements Supplier<ChatMemoryProvider> {
public ChatMemoryProvider get();
}
final class BeanIfExistsRetrievalAugmentorSupplier implements Supplier<RetrievalAugmentor> {
public RetrievalAugmentor get();
}
final class NoRetrievalAugmentorSupplier implements Supplier<RetrievalAugmentor> {
public RetrievalAugmentor get();
}
final class BeanIfExistsModerationModelSupplier implements Supplier<ModerationModel> {
public ModerationModel get();
}
final class BeanIfExistsImageModelSupplier implements Supplier<dev.langchain4j.model.image.ImageModel> {
public dev.langchain4j.model.image.ImageModel get();
}
final class BeanIfExistsToolProviderSupplier implements Supplier<ToolProvider> {
public ToolProvider get();
}
final class BeanIfExistsToolHallucinationStrategy
implements java.util.function.Function<dev.langchain4j.agent.tool.ToolExecutionRequest,
dev.langchain4j.data.message.ToolExecutionResultMessage> {
public dev.langchain4j.data.message.ToolExecutionResultMessage apply(
dev.langchain4j.agent.tool.ToolExecutionRequest request);
}
final class NoSystemMessageProviderSupplier implements SystemMessageProvider {
public java.util.Optional<String> getSystemMessage(Object memoryId);
}
final class BeanIfExistsSystemMessageProviderSupplier implements SystemMessageProvider {
public java.util.Optional<String> getSystemMessage(Object memoryId);
}
final class NoToolProviderSupplier implements Supplier<ToolProvider> {
public ToolProvider get();
}
final class NoRetriever implements dev.langchain4j.rag.content.retriever.ContentRetriever {
public java.util.List<dev.langchain4j.data.document.Content> retrieve(
dev.langchain4j.rag.query.Query query);
}
}"<default>")Multi<String> or similar typesquarkus-micrometer extension is presentpackage io.quarkiverse.langchain4j;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import jakarta.inject.Qualifier;
@Qualifier
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface ModelName {
String value() default "";
final class Literal extends jakarta.enterprise.util.AnnotationLiteral<ModelName>
implements ModelName {
private final String value;
public static Literal of(String value);
public String value();
}
}The @ModelName annotation qualifies CDI beans to select a specific named model configuration.
On injection points:
import jakarta.inject.Inject;
import io.quarkiverse.langchain4j.ModelName;
import dev.langchain4j.model.chat.ChatModel;
@Inject
@ModelName("gpt4")
ChatModel model;On @RegisterAiService:
@RegisterAiService(modelName = "gpt4")
public interface AdvancedService {
String process(String input);
}As a method parameter (runtime model selection):
@RegisterAiService
public interface FlexibleService {
String chat(@ModelName String modelName, String userMessage);
}package io.quarkiverse.langchain4j;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface CreatedAware {
}Use @CreatedAware on AI service interfaces that are created programmatically (not via @RegisterAiService) to enable Quarkus build-time processing:
@CreatedAware
public interface ProgrammaticAiService {
String chat(String message);
}
// Create instance programmatically
ProgrammaticAiService service = AiServices.builder(ProgrammaticAiService.class)
.chatLanguageModel(chatModel)
.build();package io.quarkiverse.langchain4j.runtime.aiservice;
import java.util.Optional;
public interface SystemMessageProvider {
Optional<String> getSystemMessage(Object memoryId);
}Implement this interface to dynamically provide system messages based on the memory ID (typically user ID):
import jakarta.enterprise.context.ApplicationScoped;
import io.quarkiverse.langchain4j.runtime.aiservice.SystemMessageProvider;
import java.util.Optional;
@ApplicationScoped
public class DynamicSystemMessageProvider implements SystemMessageProvider {
@Override
public Optional<String> getSystemMessage(Object memoryId) {
if (memoryId instanceof String userId) {
UserProfile profile = loadUserProfile(userId);
return Optional.of("You are an assistant for " + profile.getName() +
". User preferences: " + profile.getPreferences());
}
return Optional.empty();
}
}Then reference it in @RegisterAiService:
@RegisterAiService(systemMessageProviderSupplier = DynamicSystemMessageProvider.class)
public interface PersonalizedService {
String chat(String message);
}Or use the marker class to auto-discover a CDI bean:
@RegisterAiService(
systemMessageProviderSupplier = RegisterAiService.BeanIfExistsSystemMessageProviderSupplier.class
)
public interface AutoDiscoveredService {
String chat(String message);
}import io.quarkiverse.langchain4j.RegisterAiService;
import dev.langchain4j.service.SystemMessage;
@RegisterAiService
public interface BasicAssistant {
@SystemMessage("You are a helpful assistant")
String chat(String userMessage);
}@RegisterAiService(modelName = "gpt4")
public interface AdvancedAssistant {
String process(String input);
}Configuration in application.properties:
# Default model
quarkus.langchain4j.openai.chat-model.model-name=gpt-3.5-turbo
quarkus.langchain4j.openai.api-key=sk-...
# Named model "gpt4"
quarkus.langchain4j.gpt4.openai.chat-model.model-name=gpt-4-turbo-preview
quarkus.langchain4j.gpt4.openai.api-key=sk-...import io.quarkiverse.langchain4j.RegisterAiService;
import dev.langchain4j.agent.tool.Tool;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class EmailTool {
@Tool("Send an email to a recipient")
public String sendEmail(String to, String subject, String body) {
// Implementation
return "Email sent to " + to;
}
}
@RegisterAiService(
tools = { EmailTool.class },
maxSequentialToolInvocations = 5
)
public interface AssistantWithTools {
String help(String request);
}@RegisterAiService(
chatMemoryProviderSupplier = RegisterAiService.NoChatMemoryProviderSupplier.class
)
public interface StatelessService {
String processOneOff(String input);
}import io.smallrye.mutiny.Multi;
@RegisterAiService
public interface StreamingAssistant {
@SystemMessage("You are a helpful assistant")
Multi<String> chatStreaming(String userMessage);
}import jakarta.enterprise.context.ApplicationScoped;
import io.quarkiverse.langchain4j.RegisterAiService;
@ApplicationScoped
@RegisterAiService(
chatMemoryProviderSupplier = RegisterAiService.NoChatMemoryProviderSupplier.class
)
public interface SingletonAssistant {
String chat(String message);
}Note: When using non-request scopes, be careful with chat memory implementation to avoid memory leaks.
@RegisterAiService
public interface FlexibleAssistant {
String chat(@ModelName String modelName, String userMessage);
}
// Usage
@Inject
FlexibleAssistant assistant;
public void example() {
String response1 = assistant.chat("gpt4", "Complex question");
String response2 = assistant.chat("gpt-3.5-turbo", "Simple question");
}import dev.langchain4j.rag.RetrievalAugmentor;
import java.util.function.Supplier;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class MyRetrievalAugmentorSupplier implements Supplier<RetrievalAugmentor> {
@Override
public RetrievalAugmentor get() {
// Configure and return your RAG setup
return DefaultRetrievalAugmentor.builder()
.contentRetriever(myContentRetriever)
.build();
}
}
@RegisterAiService(
retrievalAugmentor = MyRetrievalAugmentorSupplier.class
)
public interface RagAssistant {
String answer(String question);
}import dev.langchain4j.service.Moderate;
@RegisterAiService(
moderationModelSupplier = RegisterAiService.BeanIfExistsModerationModelSupplier.class
)
public interface ModeratedService {
@Moderate
String chat(String userMessage);
}import io.quarkiverse.langchain4j.RegisterAiService;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;
import jakarta.inject.Inject;
import jakarta.enterprise.context.ApplicationScoped;
// AI Service definition
@RegisterAiService(
modelName = "gpt4",
tools = { EmailTool.class, DatabaseTool.class },
maxSequentialToolInvocations = 5
)
public interface BusinessAssistant {
@SystemMessage("You are a business assistant that helps with emails and data queries")
String help(String request);
@UserMessage("Summarize the following report: {report}")
String summarizeReport(@V("report") String reportText);
}
// Application using the service
@ApplicationScoped
public class BusinessApp {
@Inject
BusinessAssistant assistant;
public void processUserRequest(String userInput) {
String response = assistant.help(userInput);
System.out.println("Assistant: " + response);
}
public String getReportSummary(String report) {
return assistant.summarizeReport(report);
}
}Install with Tessl CLI
npx tessl i tessl/maven-io-quarkiverse-langchain4j--quarkus-langchain4j-core