Quarkus extension deployment module for OpenAI integration with LangChain4j providing build-time processing and CDI bean generation
Build items are Quarkus extension APIs that enable communication between build processors during application build. This deployment module produces and consumes build items to integrate OpenAI models into the Quarkus ecosystem.
This reference is primarily for Quarkus extension authors who want to integrate with the OpenAI deployment module.
Build items created by this deployment module that other extensions can consume for integration and coordination.
Build items indicating OpenAI is available as a provider for various model types, allowing the core LangChain4j extension to select appropriate providers.
package io.quarkiverse.langchain4j.deployment.items;
/**
* Build item indicating OpenAI is available as a chat model provider.
* Produced during build to participate in provider selection.
*/
public final class ChatModelProviderCandidateBuildItem extends MultiBuildItem {
private final String provider;
/**
* Create a chat model provider candidate.
*
* @param provider The provider name ("openai")
*/
public ChatModelProviderCandidateBuildItem(String provider) {
this.provider = provider;
}
/**
* Get the provider name.
*
* @return The provider name
*/
public String getProvider() {
return provider;
}
}When Produced: When quarkus.langchain4j.openai.chat-model.enabled=true (default)
Similar Build Items:
EmbeddingModelProviderCandidateBuildItem - Embedding model provider candidateModerationModelProviderCandidateBuildItem - Moderation model provider candidateImageModelProviderCandidateBuildItem - Image model provider candidateUsage by Other Extensions:
import io.quarkiverse.langchain4j.deployment.items.ChatModelProviderCandidateBuildItem;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.BuildProducer;
import java.util.List;
@BuildStep
void checkOpenAiAvailability(
List<ChatModelProviderCandidateBuildItem> candidates,
BuildProducer<MyCustomBuildItem> producer) {
boolean hasOpenAi = candidates.stream()
.anyMatch(c -> "openai".equals(c.getProvider()));
if (hasOpenAi) {
// Configure integration with OpenAI
producer.produce(new MyCustomBuildItem());
}
}Build item registering the "langchain4j-openai" feature for tracking and display.
package io.quarkus.deployment.builditem;
/**
* Build item representing a Quarkus feature.
* Used for feature tracking and display in build output.
*/
public final class FeatureBuildItem extends SimpleBuildItem {
private final String info;
/**
* Create a feature build item.
*
* @param info The feature name
*/
public FeatureBuildItem(String info) {
this.info = info;
}
public String getInfo() {
return info;
}
}Feature Name: "langchain4j-openai"
Purpose: Appears in build output and feature lists, helping track which Quarkus extensions are active.
Build items registering SPI implementations for Java ServiceLoader mechanism and native image support.
package io.quarkus.deployment.builditem.nativeimage;
/**
* Build item registering a service provider interface implementation.
* Ensures SPI mechanism works in native images.
*/
public final class ServiceProviderBuildItem extends MultiBuildItem {
private final String serviceInterface;
private final String provider;
/**
* Create a service provider build item.
*
* @param serviceInterface The service interface (e.g., "dev.langchain4j.model.openai.spi.OpenAiChatModelBuilderFactory")
* @param provider The provider implementation (e.g., "io.quarkiverse.langchain4j.openai.QuarkusOpenAiChatModelBuilderFactory")
*/
public ServiceProviderBuildItem(String serviceInterface, String provider) {
this.serviceInterface = serviceInterface;
this.provider = provider;
}
public String getServiceInterface() {
return serviceInterface;
}
public List<String> providers() {
return Collections.singletonList(provider);
}
}Registered Service Providers:
OpenAiChatModelBuilderFactory → QuarkusOpenAiChatModelBuilderFactoryOpenAiStreamingChatModelBuilderFactory → QuarkusOpenAiStreamingChatModelBuilderFactoryOpenAiEmbeddingModelBuilderFactory → QuarkusOpenAiEmbeddingModelBuilderFactoryOpenAiModerationModelBuilderFactory → QuarkusOpenAiModerationModelBuilderFactoryBuild items registering classes that require reflection support in native images.
package io.quarkus.deployment.builditem.nativeimage;
/**
* Build item registering a class for reflection in native image.
* Ensures runtime reflection works in GraalVM native executables.
*/
public final class ReflectiveClassBuildItem extends MultiBuildItem {
private final List<String> className;
private final boolean methods;
private final boolean fields;
private final boolean constructors;
/**
* Create via builder pattern.
*/
public static Builder builder(String... classNames) {
return new Builder().classNames(classNames);
}
public static class Builder {
public Builder methods(boolean methods);
public Builder fields(boolean fields);
public Builder constructors(boolean constructors);
public ReflectiveClassBuildItem build();
}
}Registered Reflective Classes:
com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy - JSON serializationdev.langchain4j.model.openai.internal.embedding.OpenAiEmbeddingDeserializer - Embedding deserializationBuild items creating CDI beans programmatically without corresponding Java classes.
package io.quarkus.arc.deployment;
/**
* Build item for creating synthetic CDI beans.
* Allows programmatic bean definition without concrete classes.
*/
public final class SyntheticBeanBuildItem extends MultiBuildItem {
/**
* Configure a synthetic bean.
*
* @param beanType The bean type (e.g., DotName for ChatModel interface)
* @return Bean configurator for fluent configuration
*/
public static ExtendedBeanConfigurator configure(DotName beanType) {
return new ExtendedBeanConfigurator(beanType);
}
public static class ExtendedBeanConfigurator {
public ExtendedBeanConfigurator scope(Class<? extends Annotation> scope);
public ExtendedBeanConfigurator defaultBean();
public ExtendedBeanConfigurator unremovable();
public ExtendedBeanConfigurator setRuntimeInit();
public ExtendedBeanConfigurator addQualifier(AnnotationInstance qualifier);
public ExtendedBeanConfigurator addInjectionPoint(Type type);
public ExtendedBeanConfigurator createWith(Function<?, ?> creationFunction);
public ExtendedBeanConfigurator supplier(Supplier<?> supplier);
public SyntheticBeanBuildItem done();
}
}Generated Synthetic Beans:
For each selected OpenAI provider:
ChatModel - ApplicationScoped, default bean, runtime init, with ChatModelListener injectionStreamingChatModel - ApplicationScoped, default bean, runtime init, with ChatModelListener injectionEmbeddingModel - ApplicationScoped, default bean, unremovable, runtime initModerationModel - ApplicationScoped, default bean, runtime initImageModel - ApplicationScoped, default bean, runtime initNamed configurations add @ModelName(value) qualifier to beans.
Example Creation:
var builder = SyntheticBeanBuildItem
.configure(CHAT_MODEL)
.setRuntimeInit()
.defaultBean()
.scope(ApplicationScoped.class)
.addInjectionPoint(ParameterizedType.create(
DotNames.CDI_INSTANCE,
new Type[] { ClassType.create(DotNames.CHAT_MODEL_LISTENER) },
null))
.createWith(recorder.chatModel(configName));
if (!NamedConfigUtil.isDefault(configName)) {
builder.addQualifier(
AnnotationInstance.builder(ModelName.class)
.add("value", configName)
.build());
}
beanProducer.produce(builder.done());Build items for Quarkus Development UI integration.
package io.quarkus.devui.spi.page;
/**
* Build item for adding a card page to Dev UI.
* Only produced in development mode.
*/
public final class CardPageBuildItem extends SimpleBuildItem {
/**
* Add build-time data available to Dev UI pages.
*/
public void addBuildTimeData(String key, Object value);
/**
* Add a web component page to the card.
*/
public void addPage(Page page);
}Dev UI Pages Added:
package io.quarkus.devui.spi;
/**
* Build item registering JSON RPC providers for Dev UI.
*/
public final class JsonRPCProvidersBuildItem extends MultiBuildItem {
private final Class<?> providerClass;
public JsonRPCProvidersBuildItem(Class<?> providerClass) {
this.providerClass = providerClass;
}
public Class<?> getProviderClass() {
return providerClass;
}
}Registered RPC Providers:
OpenAiImagesJsonRPCService - Image generation RPC serviceOpenAiModerationModelsJsonRPCService - Moderation RPC serviceBuild items consumed by this deployment module from other extensions.
Build items indicating which model provider was selected by the core LangChain4j extension after evaluating all candidates.
package io.quarkiverse.langchain4j.deployment.items;
/**
* Build item indicating the selected chat model provider.
* Consumed to determine whether to generate OpenAI chat model beans.
*/
public final class SelectedChatModelProviderBuildItem extends SimpleBuildItem {
private final String provider;
private final String configName;
public SelectedChatModelProviderBuildItem(String provider, String configName) {
this.provider = provider;
this.configName = configName;
}
/**
* Get the selected provider name.
*
* @return Provider name (e.g., "openai")
*/
public String getProvider() {
return provider;
}
/**
* Get the configuration name.
*
* @return Configuration name ("default" or named configuration)
*/
public String getConfigName() {
return configName;
}
}Similar Build Items:
SelectedEmbeddingModelCandidateBuildItem - Selected embedding model providerSelectedModerationModelProviderBuildItem - Selected moderation model providerSelectedImageModelProviderBuildItem - Selected image model providerUsage:
import io.quarkiverse.langchain4j.deployment.items.SelectedChatModelProviderBuildItem;
import io.quarkiverse.langchain4j.openai.runtime.OpenAiRecorder;
import io.quarkus.arc.deployment.SyntheticBeanBuildItem;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.builditem.BuildProducer;
import org.jboss.jandex.DotName;
import java.util.List;
@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
void generateBeans(
OpenAiRecorder recorder,
List<SelectedChatModelProviderBuildItem> selectedChatItem,
BuildProducer<SyntheticBeanBuildItem> beanProducer) {
DotName CHAT_MODEL = DotName.createSimple("dev.langchain4j.model.chat.ChatLanguageModel");
for (var selected : selectedChatItem) {
if ("openai".equals(selected.getProvider())) {
String configName = selected.getConfigName();
// Generate CDI bean for this configuration
var builder = SyntheticBeanBuildItem
.configure(CHAT_MODEL)
.createWith(recorder.chatModel(configName));
beanProducer.produce(builder.done());
}
}
}Build item for registering application shutdown handlers.
package io.quarkus.deployment.builditem;
/**
* Build item providing access to shutdown context.
* Used to register cleanup handlers that run on application shutdown.
*/
public final class ShutdownContextBuildItem extends SimpleBuildItem {
private final ShutdownContext shutdownContext;
public ShutdownContextBuildItem(ShutdownContext shutdownContext) {
this.shutdownContext = shutdownContext;
}
public ShutdownContext getShutdownContext() {
return shutdownContext;
}
}Usage:
@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
void cleanUp(OpenAiRecorder recorder, ShutdownContextBuildItem shutdown) {
recorder.cleanUp(shutdown.getShutdownContext());
}Build step that produces provider candidate build items based on configuration.
package io.quarkiverse.langchain4j.openai.deployment;
public class OpenAiProcessor {
/**
* Register OpenAI as a candidate provider for enabled model types.
*
* @param chatProducer Producer for chat model candidates
* @param embeddingProducer Producer for embedding model candidates
* @param moderationProducer Producer for moderation model candidates
* @param imageProducer Producer for image model candidates
* @param config Build-time configuration
*/
@BuildStep
public void providerCandidates(
BuildProducer<ChatModelProviderCandidateBuildItem> chatProducer,
BuildProducer<EmbeddingModelProviderCandidateBuildItem> embeddingProducer,
BuildProducer<ModerationModelProviderCandidateBuildItem> moderationProducer,
BuildProducer<ImageModelProviderCandidateBuildItem> imageProducer,
LangChain4jOpenAiBuildConfig config) {
if (config.chatModel().enabled().isEmpty() || config.chatModel().enabled().get()) {
chatProducer.produce(new ChatModelProviderCandidateBuildItem("openai"));
}
// Similar for other model types
}
}Execution Phase: BUILD_TIME Purpose: Declare OpenAI as available provider
Build step that creates synthetic CDI beans for selected providers.
/**
* Generate CDI beans for selected OpenAI model providers.
*
* @param recorder Runtime recorder for creating model instances
* @param selectedChatItem Selected chat model providers
* @param selectedEmbedding Selected embedding model providers
* @param selectedModeration Selected moderation model providers
* @param selectedImage Selected image model providers
* @param beanProducer Producer for synthetic beans
*/
@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
void generateBeans(
OpenAiRecorder recorder,
List<SelectedChatModelProviderBuildItem> selectedChatItem,
List<SelectedEmbeddingModelCandidateBuildItem> selectedEmbedding,
List<SelectedModerationModelProviderBuildItem> selectedModeration,
List<SelectedImageModelProviderBuildItem> selectedImage,
BuildProducer<SyntheticBeanBuildItem> beanProducer) {
// Bean generation logic
}Execution Phase: RUNTIME_INIT Purpose: Create injectable model beans
Build step that registers service providers and reflective classes.
/**
* Register service providers and reflective classes for native image support.
*
* @param serviceProviderProducer Producer for service provider registrations
* @param reflectiveClassProducer Producer for reflective class registrations
*/
@BuildStep
void nativeSupport(
BuildProducer<ServiceProviderBuildItem> serviceProviderProducer,
BuildProducer<ReflectiveClassBuildItem> reflectiveClassProducer) {
serviceProviderProducer.produce(new ServiceProviderBuildItem(
OpenAiChatModelBuilderFactory.class.getName(),
QuarkusOpenAiChatModelBuilderFactory.class.getName()));
reflectiveClassProducer.produce(
ReflectiveClassBuildItem.builder(PropertyNamingStrategies.SnakeCaseStrategy.class)
.build());
}Execution Phase: BUILD_TIME Purpose: Enable native compilation
Build step that registers shutdown handlers.
/**
* Register cleanup handlers for application shutdown.
*
* @param recorder Runtime recorder
* @param shutdown Shutdown context
*/
@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
public void cleanUp(OpenAiRecorder recorder, ShutdownContextBuildItem shutdown) {
recorder.cleanUp(shutdown.getShutdownContext());
}Execution Phase: RUNTIME_INIT Purpose: Clean resource cleanup on shutdown
Other extensions can check if OpenAI is available:
import io.quarkiverse.langchain4j.deployment.items.ChatModelProviderCandidateBuildItem;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.BuildProducer;
import java.util.List;
@BuildStep
void integrateWithOpenAi(
List<ChatModelProviderCandidateBuildItem> candidates,
BuildProducer<MyFeatureBuildItem> producer) {
boolean hasOpenAi = candidates.stream()
.anyMatch(c -> "openai".equals(c.getProvider()));
if (hasOpenAi) {
// Enable OpenAI-specific features
producer.produce(new MyFeatureBuildItem("openai-integration"));
}
}React to OpenAI being selected as the provider:
import io.quarkiverse.langchain4j.deployment.items.SelectedChatModelProviderBuildItem;
import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.BuildProducer;
import java.util.List;
@BuildStep
void onOpenAiSelected(
List<SelectedChatModelProviderBuildItem> selected,
BuildProducer<AdditionalBeanBuildItem> beanProducer) {
for (var item : selected) {
if ("openai".equals(item.getProvider())) {
// Register additional beans or configuration
beanProducer.produce(AdditionalBeanBuildItem.builder()
.addBeanClass(OpenAiMetricsCollector.class)
.build());
}
}
}Register chat model listeners that will be automatically injected:
import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.BuildProducer;
@BuildStep
void registerListener(BuildProducer<AdditionalBeanBuildItem> producer) {
producer.produce(AdditionalBeanBuildItem.builder()
.addBeanClass(CustomChatModelListener.class)
.setUnremovable()
.build());
}The listener will be automatically discovered and injected into OpenAI chat model beans.
Build items flow through the build process in a specific order:
Extensions can hook into any phase by consuming appropriate build items.
When testing Quarkus extensions that integrate with OpenAI deployment module:
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.pkg.builditem.NativeBuild;
import dev.langchain4j.model.openai.spi.OpenAiChatModelBuilderFactory;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertTrue;
@QuarkusTest
public class OpenAiIntegrationTest {
@BuildStep(onlyIf = NativeBuild.class)
void checkNativeImageSupport(
List<ServiceProviderBuildItem> serviceProviders) {
boolean hasOpenAiChatFactory = serviceProviders.stream()
.anyMatch(sp -> sp.getServiceInterface()
.equals(OpenAiChatModelBuilderFactory.class.getName()));
assertTrue(hasOpenAiChatFactory, "OpenAI chat factory should be registered");
}
}Use Quarkus testing utilities to verify build items are produced and consumed correctly.
Install with Tessl CLI
npx tessl i tessl/maven-io-quarkiverse-langchain4j--quarkus-langchain4j-openai-deployment