Quarkus deployment extension for LangChain4j integration providing build-time processing, BuildItem APIs, and configuration for integrating Large Language Models into Quarkus applications
The quarkus-langchain4j-core-deployment module is a Quarkus deployment extension that provides build-time processing for LangChain4j integration. This module runs exclusively at build time and enables other Quarkus extensions to integrate LangChain4j capabilities into their applications.
Package: io.quarkiverse.langchain4j.deployment
Type: Quarkus Deployment Extension
Language: Java (Maven)
Group ID: io.quarkiverse.langchain4j
Artifact ID: quarkus-langchain4j-core-deployment
Version: 1.7.4
To use this deployment extension in your Quarkus extension, add the following dependency:
<dependency>
<groupId>io.quarkiverse.langchain4j</groupId>
<artifactId>quarkus-langchain4j-core-deployment</artifactId>
<version>1.7.4</version>
</dependency>This deployment module enables Quarkus extensions to:
@RegisterAiServiceQuarkus follows a build-time/runtime split architecture:
This deployment module runs at build time and uses the following key abstractions:
BuildItems are data carriers used in the Quarkus build step chain. They are the primary API for communication between extensions during the build process.
Methods annotated with @BuildStep that execute during the build process. BuildSteps:
BuildProducer<T> parametersA parameter type used in BuildStep methods to produce BuildItems:
@BuildStep
void myBuildStep(BuildProducer<MyBuildItem> producer) {
producer.produce(new MyBuildItem(...));
}Implicitly used when a BuildStep method accepts a BuildItem as a parameter:
@BuildStep
void myBuildStep(MyBuildItem item) {
// Consume the BuildItem
}Here's a simple processor that registers a custom chat model provider:
package com.example.deployment;
import io.quarkiverse.langchain4j.deployment.items.ChatModelProviderCandidateBuildItem;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
public class MyModelProcessor {
@BuildStep
public void registerChatModelProvider(
BuildProducer<ChatModelProviderCandidateBuildItem> chatModelProvider) {
// Register "my-provider" as a candidate chat model provider
chatModelProvider.produce(
new ChatModelProviderCandidateBuildItem("my-provider")
);
}
}The deployment extension follows a multi-phase build process:
// BuildItem base classes
import io.quarkus.builder.item.MultiBuildItem;
import io.quarkus.builder.item.SimpleBuildItem;
// Build step annotations
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.annotations.ExecutionTime;
// Configuration
import io.quarkiverse.langchain4j.deployment.config.LangChain4jBuildConfig;
// Common BuildItems
import io.quarkiverse.langchain4j.deployment.items.ChatModelProviderCandidateBuildItem;
import io.quarkiverse.langchain4j.deployment.items.SelectedChatModelProviderBuildItem;
import io.quarkiverse.langchain4j.deployment.DeclarativeAiServiceBuildItem;
// Utility classes
import io.quarkiverse.langchain4j.deployment.DotNames;
import io.quarkiverse.langchain4j.deployment.LangChain4jDotNames;The deployment extension provides 73 public API elements organized into the following categories:
BuildItems are the primary extension API. See BuildItems Documentation for complete details.
Key BuildItems:
ChatModelProviderCandidateBuildItem - Register chat model providerSelectedChatModelProviderBuildItem - Access selected providerExample: Registering a model provider
@BuildStep
void registerProvider(BuildProducer<ChatModelProviderCandidateBuildItem> producer) {
producer.produce(new ChatModelProviderCandidateBuildItem("openai"));
}DeclarativeAiServiceBuildItem - AI service metadata from @RegisterAiServiceExample: Consuming AI service metadata
@BuildStep
void processAiServices(List<DeclarativeAiServiceBuildItem> aiServices) {
for (DeclarativeAiServiceBuildItem service : aiServices) {
String modelName = service.getChatModelName();
// Process service configuration
}
}Tools: Tool discovery and metadata
ToolMethodBuildItem - Methods annotated with @ToolToolsMetadataBuildItem - Consolidated tool metadataBehavior Customization: Control extension behavior
RequestChatModelBeanBuildItem - Force bean creationAnnotationsImpliesAiServiceBuildItem - Custom AI service detectionSkipOutputFormatInstructionsBuildItem - Control output formattingSee BuildItems Documentation for all 37 BuildItem classes.
Processors contain @BuildStep methods that execute during the build. See Processors Documentation.
Key Processors:
AiServicesProcessor - Main AI service registrationBeansProcessor - Model provider selection and bean creationToolProcessor - Tool discovery and validationChatMemoryProcessor - Chat memory provider setupInProcessEmbeddingProcessor - Local embedding modelsSee Processors Documentation for all processors and build step patterns.
Build-time configuration classes using SmallRye Config. See Configuration Documentation.
Root Configuration:
@ConfigRoot(phase = BUILD_TIME)
@ConfigMapping(prefix = "quarkus.langchain4j")
public interface LangChain4jBuildConfig {
BaseConfig defaultConfig();
Map<String, BaseConfig> namedConfig();
DevServicesConfig devservices();
boolean responseSchema();
}Example: Accessing configuration in a BuildStep
@BuildStep
void useConfig(LangChain4jBuildConfig config) {
boolean devServicesEnabled = config.devservices().enabled();
String provider = config.defaultConfig().chatModel().provider().orElse("default");
}See Configuration Documentation for all configuration classes.
Utility classes for common tasks. See Utilities Documentation.
Key Utilities:
DotNames - Constants for common Java types (50+ DotName constants)LangChain4jDotNames - Constants for LangChain4j types (100+ constants)TemplateUtil - Template parsing and validationMethodUtil - Method signature utilitiesHashUtil - SHA-1 hashingExample: Using DotNames
import io.quarkiverse.langchain4j.deployment.DotNames;
import io.quarkiverse.langchain4j.deployment.LangChain4jDotNames;
// Check if a type is a String
if (DotNames.STRING.equals(methodParameter.type().name())) {
// Handle string parameter
}
// Check if a method is annotated with @Tool
if (method.hasAnnotation(LangChain4jDotNames.TOOL)) {
// Process tool method
}See Utilities Documentation for all utility classes.
Extensions can integrate with this deployment module by:
Register new capabilities:
@BuildStep
void registerCapabilities(
BuildProducer<ChatModelProviderCandidateBuildItem> chatProvider,
BuildProducer<EmbeddingModelProviderCandidateBuildItem> embeddingProvider) {
// Register as a chat model provider
chatProvider.produce(new ChatModelProviderCandidateBuildItem("my-provider"));
// Register as an embedding model provider
embeddingProvider.produce(new EmbeddingModelProviderCandidateBuildItem("my-provider"));
}React to configuration decisions:
@BuildStep
void reactToSelection(List<SelectedChatModelProviderBuildItem> selected) {
for (SelectedChatModelProviderBuildItem item : selected) {
if ("my-provider".equals(item.getProvider())) {
String configName = item.getConfigName();
// Create beans for this configuration
}
}
}Control how AI services behave:
@BuildStep
void customizeBehavior(
BuildProducer<AnnotationsImpliesAiServiceBuildItem> annotations,
BuildProducer<SkipOutputFormatInstructionsBuildItem> skipFormat) {
// Make @MyAnnotation imply AI service
annotations.produce(new AnnotationsImpliesAiServiceBuildItem(
Set.of(DotName.createSimple("com.example.MyAnnotation"))
));
// Skip output format for certain methods
skipFormat.produce(new SkipOutputFormatInstructionsBuildItem(
method -> method.hasAnnotation(DotName.createSimple("com.example.NoFormat"))
));
}BuildSteps execute in an order determined by their BuildItem dependencies. The core execution flow is:
@RegisterAiService interfaces are processed@Tool methods are discoveredOnly run when a capability is present:
@BuildStep(onlyIf = MyCondition.class)
void conditionalStep() {
// Only runs if MyCondition.getAsBoolean() returns true
}Pass build-time decisions to runtime:
@BuildStep
@Record(ExecutionTime.STATIC_INIT)
void recordValues(MyRecorder recorder, List<SelectedChatModelProviderBuildItem> selected) {
for (SelectedChatModelProviderBuildItem item : selected) {
recorder.recordProvider(item.getProvider(), item.getConfigName());
}
}@BuildStep
void combineData(
List<DeclarativeAiServiceBuildItem> aiServices,
List<ToolMethodBuildItem> tools,
ToolsMetadataBuildItem metadata) {
// Combine multiple sources of information
Map<String, List<ToolMethodCreateInfo>> toolsByService = metadata.getMetadata();
for (DeclarativeAiServiceBuildItem service : aiServices) {
String serviceName = service.getServiceClassInfo().name().toString();
List<ToolMethodCreateInfo> servicTools = toolsByService.get(serviceName);
// Process combined data
}
}BuildProducer<T> to produce BuildItems, method parameters to consume them@ConfigRoot and @ConfigMapping@BuildSteps(onlyIfNot = IsNormal.class) to run only in dev/test modeInstall with Tessl CLI
npx tessl i tessl/maven-io-quarkiverse-langchain4j--quarkus-langchain4j-core-deployment@1.7.0