CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-quarkiverse-langchain4j--quarkus-langchain4j-core

Core runtime module for Quarkus LangChain4j integration with declarative AI services, guardrails, and observability

Overview
Eval results
Files

ai-services.mddocs/

AI Services

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.

Core Imports

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;

@RegisterAiService Annotation

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);
    }
}

Parameters

  • chatLanguageModelSupplier: Supplier for the ChatModel (default: uses CDI bean)
  • streamingChatLanguageModelSupplier: Supplier for StreamingChatModel (default: uses CDI bean)
  • modelName: Named model configuration to use (default: "<default>")
  • tools: Array of tool classes to make available (all must be CDI beans)
  • maxSequentialToolInvocations: Maximum tool execution chain length (default: 0, uses global config)
  • allowContinuousForcedToolCalling: Allow continuous forced tool calling (default: false, dangerous)
  • toolHallucinationStrategy: Strategy for handling hallucinated tool names
  • toolProviderSupplier: Supplier for dynamic tool provider
  • chatMemoryProviderSupplier: Supplier for chat memory (default: uses CDI bean)
  • retrievalAugmentor: Supplier for RAG retrieval augmentor
  • moderationModelSupplier: Supplier for moderation model
  • systemMessageProviderSupplier: Dynamic system message provider (default: none)

Behavior

  • The annotated interface becomes a RequestScoped CDI bean by default
  • Methods can use LangChain4j annotations like @SystemMessage, @UserMessage, @V
  • Automatically integrates with CDI beans for ChatModel, tools, memory, and more
  • Supports streaming responses when methods return Multi<String> or similar types
  • Enables metrics when quarkus-micrometer extension is present

@ModelName Annotation

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 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();
    }
}

Usage

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);
}

@CreatedAware Annotation

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 {
}

Usage

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();

SystemMessageProvider Interface

package io.quarkiverse.langchain4j.runtime.aiservice;

import java.util.Optional;

public interface SystemMessageProvider {
    Optional<String> getSystemMessage(Object memoryId);
}

Usage

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);
}

Usage Examples

Basic AI Service

import io.quarkiverse.langchain4j.RegisterAiService;
import dev.langchain4j.service.SystemMessage;

@RegisterAiService
public interface BasicAssistant {
    @SystemMessage("You are a helpful assistant")
    String chat(String userMessage);
}

Named Model Configuration

@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-...

Service with Tools

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);
}

Service Without Memory

@RegisterAiService(
    chatMemoryProviderSupplier = RegisterAiService.NoChatMemoryProviderSupplier.class
)
public interface StatelessService {
    String processOneOff(String input);
}

Streaming AI Service

import io.smallrye.mutiny.Multi;

@RegisterAiService
public interface StreamingAssistant {
    @SystemMessage("You are a helpful assistant")
    Multi<String> chatStreaming(String userMessage);
}

Service with Custom Scope

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.

Runtime Model Selection

@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");
}

Service with RAG

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);
}

Service with Moderation

import dev.langchain4j.service.Moderate;

@RegisterAiService(
    moderationModelSupplier = RegisterAiService.BeanIfExistsModerationModelSupplier.class
)
public interface ModeratedService {
    @Moderate
    String chat(String userMessage);
}

Complete Example

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

docs

ai-services.md

authentication.md

cost-estimation.md

guardrails.md

index.md

media-content.md

memory.md

observability.md

response-augmentation.md

tools.md

tile.json