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

response-augmentation.mddocs/

Response Augmentation

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

Core Imports

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;

@ResponseAugmenter Annotation

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:

  • Augmenter invoked after output guardrails
  • Augmenter invoked after response parsing/mapping
  • Can update model output before sending to caller
  • Implementation must be a CDI bean

Targets:

  • TYPE: Applies to all methods in the interface
  • METHOD: Applies to specific method (overrides type-level)

AiResponseAugmenter Interface

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.

Methods

augment(T, ResponseAugmenterParams)

default T augment(T response, ResponseAugmenterParams params)

Augments a synchronous response.

Parameters:

  • response: The AI model response
  • params: Context parameters for augmentation

Returns:

  • Augmented response

Default: Returns response unchanged

augment(Multi, ResponseAugmenterParams)

default Multi<T> augment(Multi<T> stream, ResponseAugmenterParams params)

Augments a streaming response.

Parameters:

  • stream: The streaming AI model response
  • params: Context parameters for augmentation

Returns:

  • Augmented response stream

Default: Returns stream unchanged

ResponseAugmenterParams Record

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)

Usage Examples

Basic Response Formatting

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

Adding Metadata

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

Streaming Response Transformation

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

Content Filtering

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

Response Translation

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

Response Summarization

@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] + ".";
    }
}

Class-Level Augmentation

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

Context-Aware Augmentation

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

Structured Response Augmentation

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

Markdown Enhancement

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

Streaming with Buffering

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

Comprehensive Example

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

Execution Order

1. AI Model generates response
   ↓
2. Output Guardrails execute
   ↓
3. Response parsed/mapped
   ↓
4. Response Augmenter executes ← You are here
   ↓
5. Result returned to caller

Notes

  • Experimental: API may change in future versions
  • CDI Required: Augmenter implementations must be CDI beans
  • After Guardrails: Augmentation happens after output guardrails
  • Type-Safe: Generic type T matches AI service method return type
  • Streaming Support: Separate method for streaming responses
  • Context Available: Access to user message, memory, RAG results, and variables
  • Chaining: Only one augmenter per method (no automatic chaining)
  • Method Override: Method-level @ResponseAugmenter overrides type-level
  • Default Behavior: Default implementations return unchanged response/stream

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