Quarkus extension deployment module for OpenAI integration with LangChain4j providing build-time processing and CDI bean generation
The deployment module automatically generates CDI beans for OpenAI models based on configuration, allowing injection of model instances throughout your Quarkus application without manual instantiation.
Injectable bean for OpenAI chat models (GPT models) supporting conversational AI with single-turn and multi-turn interactions.
package dev.langchain4j.model.chat;
/**
* Chat model interface for conversational AI.
* Automatically created as CDI bean when chat model is enabled and selected.
*/
public interface ChatLanguageModel {
/**
* Generate a response for a single user message.
*
* @param userMessage The user's message
* @return The model's response
*/
String generate(String userMessage);
/**
* Generate a response for a chat conversation.
*
* @param messages The conversation history
* @return The model's response
*/
Response<AiMessage> generate(List<ChatMessage> messages);
}Bean Characteristics:
@ApplicationScoped@Default for default configuration, @ModelName("name") for named configurationsquarkus.langchain4j.openai.chat-model.enabled=true (default)Usage:
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.model.output.Response;
import jakarta.inject.Inject;
import jakarta.enterprise.context.ApplicationScoped;
import java.util.ArrayList;
import java.util.List;
@ApplicationScoped
public class ChatService {
@Inject
ChatLanguageModel chatModel;
public String chat(String message) {
return chatModel.generate(message);
}
public String chatWithHistory(List<ChatMessage> messages) {
Response<AiMessage> response = chatModel.generate(messages);
return response.content().text();
}
}Injectable bean for OpenAI streaming chat models supporting real-time token-by-token response streaming.
package dev.langchain4j.model.chat;
/**
* Streaming chat model interface for real-time conversational AI.
* Automatically created as CDI bean when chat model is enabled and selected.
*/
public interface StreamingChatLanguageModel {
/**
* Generate a streaming response for a user message.
*
* @param userMessage The user's message
* @param handler Callback for streaming tokens
*/
void generate(String userMessage, StreamingResponseHandler<AiMessage> handler);
/**
* Generate a streaming response for a chat conversation.
*
* @param messages The conversation history
* @param handler Callback for streaming tokens
*/
void generate(List<ChatMessage> messages, StreamingResponseHandler<AiMessage> handler);
}Bean Characteristics:
@ApplicationScoped@Default for default configuration, @ModelName("name") for named configurationsquarkus.langchain4j.openai.chat-model.enabled=true (default)Usage:
import dev.langchain4j.model.chat.StreamingChatLanguageModel;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.model.output.Response;
import dev.langchain4j.model.output.StreamingResponseHandler;
import jakarta.inject.Inject;
import jakarta.enterprise.context.ApplicationScoped;
import java.util.function.Consumer;
@ApplicationScoped
public class StreamingChatService {
@Inject
StreamingChatLanguageModel streamingModel;
public void streamChat(String message, Consumer<String> onToken) {
streamingModel.generate(message, new StreamingResponseHandler<AiMessage>() {
@Override
public void onNext(String token) {
onToken.accept(token);
}
@Override
public void onComplete(Response<AiMessage> response) {
// Streaming complete
System.out.println("Total tokens: " + response.tokenUsage().totalTokenCount());
}
@Override
public void onError(Throwable error) {
// Handle error
error.printStackTrace();
}
});
}
}Injectable bean for OpenAI embedding models converting text into vector representations for semantic search and similarity comparisons.
package dev.langchain4j.model.embedding;
/**
* Embedding model interface for text vectorization.
* Automatically created as CDI bean when embedding model is enabled and selected.
*/
public interface EmbeddingModel {
/**
* Embed a single text into a vector.
*
* @param text The text to embed
* @return The embedding vector
*/
Response<Embedding> embed(String text);
/**
* Embed a text segment into a vector.
*
* @param textSegment The text segment to embed
* @return The embedding vector
*/
Response<Embedding> embed(TextSegment textSegment);
/**
* Embed multiple texts into vectors in a single API call.
*
* @param textSegments The text segments to embed
* @return List of embedding vectors
*/
Response<List<Embedding>> embedAll(List<TextSegment> textSegments);
}Bean Characteristics:
@ApplicationScoped@Default for default configuration, @ModelName("name") for named configurationsquarkus.langchain4j.openai.embedding-model.enabled=true (default)Usage:
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.output.Response;
import jakarta.inject.Inject;
import jakarta.enterprise.context.ApplicationScoped;
import java.util.List;
import java.util.stream.Collectors;
@ApplicationScoped
public class EmbeddingService {
@Inject
EmbeddingModel embeddingModel;
public float[] getEmbedding(String text) {
Response<Embedding> response = embeddingModel.embed(text);
return response.content().vector();
}
public List<float[]> getEmbeddings(List<String> texts) {
List<TextSegment> segments = texts.stream()
.map(TextSegment::from)
.collect(Collectors.toList());
Response<List<Embedding>> response = embeddingModel.embedAll(segments);
return response.content().stream()
.map(Embedding::vector)
.collect(Collectors.toList());
}
}Injectable bean for OpenAI moderation models detecting potentially harmful content across multiple safety categories.
package dev.langchain4j.model.moderation;
/**
* Moderation model interface for content safety checks.
* Automatically created as CDI bean when moderation model is enabled and selected.
*/
public interface ModerationModel {
/**
* Moderate a single text for harmful content.
*
* @param text The text to moderate
* @return Moderation results with category scores
*/
Response<Moderation> moderate(String text);
/**
* Moderate multiple texts for harmful content.
*
* @param messages The texts to moderate
* @return List of moderation results
*/
Response<List<Moderation>> moderateAll(List<String> messages);
}Bean Characteristics:
@ApplicationScoped@Default for default configuration, @ModelName("name") for named configurationsquarkus.langchain4j.openai.moderation-model.enabled=true (default)Usage:
import dev.langchain4j.model.moderation.Moderation;
import dev.langchain4j.model.moderation.ModerationModel;
import dev.langchain4j.model.output.Response;
import jakarta.inject.Inject;
import jakarta.enterprise.context.ApplicationScoped;
import java.util.Map;
import java.util.HashMap;
@ApplicationScoped
public class ModerationService {
@Inject
ModerationModel moderationModel;
public boolean isContentSafe(String text) {
Response<Moderation> response = moderationModel.moderate(text);
Moderation moderation = response.content();
return !moderation.flagged();
}
public Map<String, Boolean> getCategoryFlags(String text) {
Response<Moderation> response = moderationModel.moderate(text);
Moderation moderation = response.content();
Map<String, Boolean> flags = new HashMap<>();
flags.put("hate", moderation.hate());
flags.put("hateThreatening", moderation.hateThreatening());
flags.put("selfHarm", moderation.selfHarm());
flags.put("sexual", moderation.sexual());
flags.put("sexualMinors", moderation.sexualMinors());
flags.put("violence", moderation.violence());
flags.put("violenceGraphic", moderation.violenceGraphic());
return flags;
}
}Injectable bean for OpenAI image generation models (DALL-E) creating images from text descriptions.
package dev.langchain4j.model.image;
/**
* Image model interface for AI image generation.
* Automatically created as CDI bean when image model is enabled and selected.
*/
public interface ImageModel {
/**
* Generate an image from a text description.
*
* @param prompt The text description of the image to generate
* @return The generated image
*/
Response<Image> generate(String prompt);
/**
* Edit an existing image based on a text description.
*
* @param image The image to edit
* @param prompt The description of the desired edit
* @return The edited image
*/
Response<Image> edit(Image image, String prompt);
}Bean Characteristics:
@ApplicationScoped@Default for default configuration, @ModelName("name") for named configurationsquarkus.langchain4j.openai.image-model.enabled=true (default)Usage:
import dev.langchain4j.data.image.Image;
import dev.langchain4j.model.image.ImageModel;
import dev.langchain4j.model.output.Response;
import jakarta.inject.Inject;
import jakarta.enterprise.context.ApplicationScoped;
import java.net.URI;
@ApplicationScoped
public class ImageService {
@Inject
ImageModel imageModel;
public String generateImage(String description) {
Response<Image> response = imageModel.generate(description);
Image image = response.content();
// URL to the generated image
URI url = image.url();
return url != null ? url.toString() : null;
}
public String generateImageBase64(String description) {
Response<Image> response = imageModel.generate(description);
Image image = response.content();
// Base64-encoded image data (when response-format=b64_json)
return image.base64Data();
}
}Inject models from named configurations using the @ModelName qualifier annotation.
package io.quarkiverse.langchain4j;
/**
* Qualifier annotation for named model configurations.
* Used to inject models from specific named configurations.
*/
@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface ModelName {
/**
* The name of the model configuration.
* Corresponds to the configuration name in properties.
*/
String value();
}Usage with Multiple Configurations:
Configuration in application.properties:
# Default configuration
quarkus.langchain4j.openai.api-key=sk-default
quarkus.langchain4j.openai.chat-model.model-name=gpt-4o-mini
# Premium configuration
quarkus.langchain4j.openai.premium.api-key=sk-premium
quarkus.langchain4j.openai.premium.chat-model.model-name=gpt-4o
# Fast configuration
quarkus.langchain4j.openai.fast.api-key=sk-fast
quarkus.langchain4j.openai.fast.chat-model.model-name=gpt-3.5-turboInjection in code:
import dev.langchain4j.model.chat.ChatLanguageModel;
import io.quarkiverse.langchain4j.ModelName;
import jakarta.inject.Inject;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class MultiModelService {
@Inject
ChatLanguageModel defaultModel; // Uses default configuration
@Inject
@ModelName("premium")
ChatLanguageModel premiumModel; // Uses premium configuration
@Inject
@ModelName("fast")
ChatLanguageModel fastModel; // Uses fast configuration
public String processWithBestModel(String query, boolean premium) {
if (premium) {
return premiumModel.generate(query);
} else {
return fastModel.generate(query);
}
}
}Beans are created lazily at first injection by default (ApplicationScoped behavior). The deployment module configures them to be created during runtime initialization phase for validation and startup time optimization.
During bean creation:
During application shutdown:
The deployment module registers shutdown handlers to ensure clean resource cleanup.
Chat and streaming chat model beans automatically inject all available ChatModelListener implementations for observability and monitoring.
package dev.langchain4j.model.chat.listener;
/**
* Listener interface for chat model events.
* All CDI beans implementing this interface are automatically injected into chat models.
*/
public interface ChatModelListener {
/**
* Called when a request is sent to the chat model.
*/
void onRequest(ChatModelRequest request);
/**
* Called when a response is received from the chat model.
*/
void onResponse(ChatModelResponse response);
/**
* Called when an error occurs.
*/
void onError(ChatModelErrorEvent errorEvent);
}Creating a Listener:
import dev.langchain4j.model.chat.listener.ChatModelListener;
import dev.langchain4j.model.chat.listener.ChatModelRequest;
import dev.langchain4j.model.chat.listener.ChatModelResponse;
import dev.langchain4j.model.chat.listener.ChatModelErrorEvent;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class CustomChatListener implements ChatModelListener {
@Override
public void onRequest(ChatModelRequest request) {
// Log request, collect metrics, etc.
System.out.println("Chat request: " + request.messages().size() + " messages");
}
@Override
public void onResponse(ChatModelResponse response) {
// Log response, track token usage, etc.
System.out.println("Chat response: " + response.tokenUsage().totalTokenCount() + " tokens");
}
@Override
public void onError(ChatModelErrorEvent errorEvent) {
// Handle errors, send alerts, etc.
System.err.println("Chat error: " + errorEvent.error().getMessage());
}
}The listener is automatically discovered and injected into all chat and streaming chat model beans.
When quarkus.langchain4j.openai.enable-integration=false, the deployment module creates "disabled" model instances that throw descriptive exceptions on use.
@Inject
ChatLanguageModel chatModel;
public void example() {
// Throws: RuntimeException: ChatModel is disabled because quarkus.langchain4j.openai.enable-integration is set to false
chatModel.generate("Hello");
}This allows applications to compile and tests to run without valid OpenAI credentials, while clearly indicating when models are accessed in a disabled state.
You can override the default beans by producing your own custom beans. The default beans are marked with @DefaultBean, allowing custom beans to take precedence.
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
import java.time.Duration;
public class CustomModelProducer {
@Produces
@ApplicationScoped
public ChatLanguageModel customChatModel() {
return OpenAiChatModel.builder()
.apiKey(System.getenv("CUSTOM_API_KEY"))
.modelName("gpt-4o")
.temperature(0.5)
.timeout(Duration.ofSeconds(30))
.build();
}
}Your custom bean will be used instead of the auto-generated default bean.
Produce beans conditionally based on configuration or runtime conditions:
import io.quarkus.arc.profile.IfBuildProfile;
import dev.langchain4j.model.chat.ChatLanguageModel;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
public class ConditionalModelProducer {
@Produces
@ApplicationScoped
@IfBuildProfile("test")
public ChatLanguageModel testChatModel() {
// Return mock or test implementation in test profile
return new MockChatModel();
}
}All injected beans are type-safe with full IDE support for:
The deployment module uses proper Java types from LangChain4j library, ensuring compile-time safety and preventing runtime errors from type mismatches.
All generated model beans use @ApplicationScoped scope, meaning:
Concurrency: OpenAI model implementations are thread-safe and can handle concurrent requests safely. Multiple threads can inject and use the same bean instance simultaneously.
The ApplicationScoped beans effectively implement the singleton pattern, but with CDI benefits:
In addition to injection, beans can be looked up programmatically using CDI's Instance API:
import dev.langchain4j.model.chat.ChatLanguageModel;
import io.quarkiverse.langchain4j.ModelName;
import jakarta.enterprise.inject.Instance;
import jakarta.inject.Inject;
public class DynamicModelSelector {
@Inject
Instance<ChatLanguageModel> allChatModels;
public ChatLanguageModel selectModel(String configName) {
if ("default".equals(configName)) {
return allChatModels.select().get();
} else {
return allChatModels.select(ModelName.Literal.of(configName)).get();
}
}
}This allows runtime selection of models based on dynamic conditions.
Install with Tessl CLI
npx tessl i tessl/maven-io-quarkiverse-langchain4j--quarkus-langchain4j-openai-deployment@1.7.0