Core runtime module for Quarkus LangChain4j integration with declarative AI services, guardrails, and observability
Response augmentation enables transformation and enhancement of AI model responses before they reach the caller. This experimental feature supports both synchronous and streaming responses.
Status: Experimental - API subject to change
import io.quarkiverse.langchain4j.response.ResponseAugmenter;
import io.quarkiverse.langchain4j.response.AiResponseAugmenter;
import io.quarkiverse.langchain4j.response.ResponseAugmenterParams;
import jakarta.enterprise.context.ApplicationScoped;
import io.smallrye.mutiny.Multi;package io.quarkiverse.langchain4j.response;
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.METHOD, ElementType.TYPE })
public @interface ResponseAugmenter {
Class<? extends AiResponseAugmenter<?>> value();
}Configures response augmentation for AI service methods or classes.
Behavior:
Targets:
TYPE: Applies to all methods in the interfaceMETHOD: Applies to specific method (overrides type-level)package io.quarkiverse.langchain4j.response;
import io.smallrye.mutiny.Multi;
public interface AiResponseAugmenter<T> {
default T augment(T response, ResponseAugmenterParams params);
default Multi<T> augment(Multi<T> stream, ResponseAugmenterParams params);
}CDI bean interface for manipulating AI model responses.
default T augment(T response, ResponseAugmenterParams params)Augments a synchronous response.
Parameters:
response: The AI model responseparams: Context parameters for augmentationReturns:
Default: Returns response unchanged
default Multi<T> augment(Multi<T> stream, ResponseAugmenterParams params)Augments a streaming response.
Parameters:
stream: The streaming AI model responseparams: Context parameters for augmentationReturns:
Default: Returns stream unchanged
package io.quarkiverse.langchain4j.response;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.rag.content.injector.ContentInjector.AugmentationResult;
import java.util.Map;
public record ResponseAugmenterParams(
UserMessage userMessage,
ChatMemory memory,
AugmentationResult augmentationResult,
String userMessageTemplate,
Map<String, Object> variables
) {}Parameters passed to AiResponseAugmenter methods.
Components:
userMessage: The user message (required)memory: Chat memory (optional, can be empty)augmentationResult: RAG augmentation result (optional)userMessageTemplate: User message template (required)variables: Template variables (required)import io.quarkiverse.langchain4j.response.AiResponseAugmenter;
import io.quarkiverse.langchain4j.response.ResponseAugmenterParams;
import io.quarkiverse.langchain4j.response.ResponseAugmenter;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class ResponseFormatter implements AiResponseAugmenter<String> {
@Override
public String augment(String response, ResponseAugmenterParams params) {
return "**AI Response:**\n" + response + "\n\n*Generated by AI Assistant*";
}
}
@RegisterAiService
public interface FormattedAssistant {
@ResponseAugmenter(ResponseFormatter.class)
String chat(String message);
}@ApplicationScoped
public class MetadataAugmenter implements AiResponseAugmenter<String> {
@Override
public String augment(String response, ResponseAugmenterParams params) {
StringBuilder augmented = new StringBuilder(response);
augmented.append("\n\n---\n");
augmented.append("Timestamp: ").append(Instant.now()).append("\n");
augmented.append("Model: ").append(getModelName()).append("\n");
if (params.augmentationResult() != null) {
augmented.append("Sources: RAG-augmented\n");
}
return augmented.toString();
}
}@ApplicationScoped
public class StreamingAugmenter implements AiResponseAugmenter<String> {
@Override
public Multi<String> augment(Multi<String> stream, ResponseAugmenterParams params) {
return Multi.createFrom().items(
Multi.createFrom().item("**AI Response:**\n"),
stream,
Multi.createFrom().item("\n\n*End of response*")
).flatMap(s -> s);
}
}
@RegisterAiService
public interface StreamingAssistant {
@ResponseAugmenter(StreamingAugmenter.class)
Multi<String> chatStreaming(String message);
}@ApplicationScoped
public class ContentFilter implements AiResponseAugmenter<String> {
private static final List<String> FORBIDDEN_WORDS = List.of("password", "secret", "token");
@Override
public String augment(String response, ResponseAugmenterParams params) {
String filtered = response;
for (String word : FORBIDDEN_WORDS) {
filtered = filtered.replaceAll(
"(?i)" + word,
"***"
);
}
return filtered;
}
}@ApplicationScoped
public class TranslationAugmenter implements AiResponseAugmenter<String> {
@Inject
TranslationService translator;
@Override
public String augment(String response, ResponseAugmenterParams params) {
// Extract target language from variables
String targetLang = (String) params.variables().get("targetLanguage");
if (targetLang != null && !targetLang.equals("en")) {
return translator.translate(response, "en", targetLang);
}
return response;
}
}
@RegisterAiService
public interface MultilingualAssistant {
@ResponseAugmenter(TranslationAugmenter.class)
String chat(@V("targetLanguage") String targetLanguage, String message);
}@ApplicationScoped
public class SummarizationAugmenter implements AiResponseAugmenter<String> {
@Override
public String augment(String response, ResponseAugmenterParams params) {
if (response.length() > 500) {
String summary = generateSummary(response);
return summary + "\n\n[Full response truncated - " + response.length() + " chars]";
}
return response;
}
private String generateSummary(String text) {
// Simple summarization logic
String[] sentences = text.split("\\. ");
return sentences[0] + ".";
}
}@ApplicationScoped
public class GlobalFormatter implements AiResponseAugmenter<String> {
@Override
public String augment(String response, ResponseAugmenterParams params) {
return "=== Response ===\n" + response + "\n=== End ===";
}
}
@RegisterAiService
@ResponseAugmenter(GlobalFormatter.class)
public interface FormattedService {
// All methods use GlobalFormatter
String methodOne(String input);
String methodTwo(String input);
// Override for specific method
@ResponseAugmenter(SpecialFormatter.class)
String methodThree(String input);
}@ApplicationScoped
public class ContextAwareAugmenter implements AiResponseAugmenter<String> {
@Override
public String augment(String response, ResponseAugmenterParams params) {
StringBuilder augmented = new StringBuilder(response);
// Add context from memory
if (params.memory() != null && !params.memory().messages().isEmpty()) {
int messageCount = params.memory().messages().size();
augmented.append("\n\n[Conversation turn: ").append(messageCount).append("]");
}
// Add RAG context
if (params.augmentationResult() != null) {
augmented.append("\n[Response based on retrieved context]");
}
// Add user context
String userName = (String) params.variables().get("userName");
if (userName != null) {
augmented.append("\n[Personalized for: ").append(userName).append("]");
}
return augmented.toString();
}
}@ApplicationScoped
public class JsonAugmenter implements AiResponseAugmenter<String> {
@Inject
ObjectMapper objectMapper;
@Override
public String augment(String response, ResponseAugmenterParams params) {
try {
Map<String, Object> structured = Map.of(
"response", response,
"timestamp", Instant.now().toString(),
"conversationId", params.memory() != null ?
params.memory().id() : "unknown",
"userMessage", params.userMessage().text()
);
return objectMapper.writeValueAsString(structured);
} catch (Exception e) {
return response; // Fallback to original
}
}
}@ApplicationScoped
public class MarkdownEnhancer implements AiResponseAugmenter<String> {
@Override
public String augment(String response, ResponseAugmenterParams params) {
// Add markdown formatting
String enhanced = response
.replaceAll("(?m)^([^\\n]+)$", "- $1") // Add bullet points
.replaceAll("(?m)^## ", "\n## "); // Add spacing around headers
// Add code block detection
if (enhanced.contains("```")) {
enhanced = "Response contains code blocks:\n\n" + enhanced;
}
return enhanced;
}
}@ApplicationScoped
public class BufferedStreamAugmenter implements AiResponseAugmenter<String> {
@Override
public Multi<String> augment(Multi<String> stream, ResponseAugmenterParams params) {
return stream
.onItem().transform(chunk -> {
// Transform each chunk
return chunk.toUpperCase();
})
.onCompletion().call(() -> {
// Do something when stream completes
log.info("Stream completed");
return Uni.createFrom().voidItem();
});
}
}@ApplicationScoped
public class ComprehensiveAugmenter implements AiResponseAugmenter<String> {
@Inject
ContentModerator moderator;
@Inject
MetricsService metrics;
@Inject
TranslationService translator;
@Override
public String augment(String response, ResponseAugmenterParams params) {
metrics.incrementCounter("response.augmentation");
// Step 1: Moderate content
String moderated = moderator.moderate(response);
// Step 2: Translate if needed
String targetLang = (String) params.variables().get("language");
String localized = targetLang != null ?
translator.translate(moderated, "en", targetLang) : moderated;
// Step 3: Add context
StringBuilder augmented = new StringBuilder(localized);
// Add timestamp
augmented.append("\n\n---\n");
augmented.append("Generated: ").append(Instant.now()).append("\n");
// Add conversation context
if (params.memory() != null) {
int turns = params.memory().messages().size();
augmented.append("Conversation turn: ").append(turns / 2).append("\n");
}
// Add RAG indicator
if (params.augmentationResult() != null) {
augmented.append("Source: Knowledge Base\n");
}
// Add user context
String userId = (String) params.variables().get("userId");
if (userId != null) {
augmented.append("User: ").append(userId).append("\n");
}
return augmented.toString();
}
@Override
public Multi<String> augment(Multi<String> stream, ResponseAugmenterParams params) {
return Multi.createFrom().items(
// Header
Multi.createFrom().item("=== AI Response Start ===\n\n"),
// Process stream
stream.onItem().transform(chunk -> {
metrics.incrementCounter("response.stream.chunks");
return moderator.moderate(chunk);
}),
// Footer
Multi.createFrom().item("\n\n=== AI Response End ===")
).flatMap(s -> s);
}
}1. AI Model generates response
↓
2. Output Guardrails execute
↓
3. Response parsed/mapped
↓
4. Response Augmenter executes ← You are here
↓
5. Result returned to callerInstall with Tessl CLI
npx tessl i tessl/maven-io-quarkiverse-langchain4j--quarkus-langchain4j-core@1.7.0