class DefaultRetrievalAugmentor implements RetrievalAugmentor {

    private static final Logger log = LoggerFactory.getLogger(DefaultRetrievalAugmentor.class);

    private final QueryTransformer queryTransformer;
    private final QueryRouter queryRouter;
    private final ContentAggregator contentAggregator;
    private final ContentInjector contentInjector;
    private final Executor executor;

    @Builder
    public DefaultRetrievalAugmentor(QueryTransformer queryTransformer,
                                     QueryRouter queryRouter,
                                     ContentAggregator contentAggregator,
                                     ContentInjector contentInjector,
                                     Executor executor) {
        this.queryTransformer = getOrDefault(queryTransformer, DefaultQueryTransformer::new);
        this.queryRouter = ensureNotNull(queryRouter, "queryRouter");
        this.contentAggregator = getOrDefault(contentAggregator, DefaultContentAggregator::new);
        this.contentInjector = getOrDefault(contentInjector, DefaultContentInjector::new);
        this.executor = getOrDefault(executor, Executors::newCachedThreadPool);
    }

    @Override
    public UserMessage augment(UserMessage userMessage, Metadata metadata) {

        Query originalQuery = Query.from(userMessage.text(), metadata);

        Collection<Query> queries = queryTransformer.transform(originalQuery);
        logQueries(originalQuery, queries);

        Map<Query, CompletableFuture<Collection<List<Content>>>> queryToFutureContents = new ConcurrentHashMap<>();
        queries.forEach(query -> {
            CompletableFuture<Collection<List<Content>>> futureContents =
                    supplyAsync(() -> {
                                Collection<ContentRetriever> retrievers = queryRouter.route(query);
                                log(query, retrievers);
                                return retrievers;
                            },
                            executor
                    ).thenCompose(retrievers -> retrieveFromAll(retrievers, query));
            queryToFutureContents.put(query, futureContents);
        });

        Map<Query, Collection<List<Content>>> queryToContents = join(queryToFutureContents);

        List<Content> contents = contentAggregator.aggregate(queryToContents);
        log(queryToContents, contents);

        UserMessage augmentedUserMessage = contentInjector.inject(contents, userMessage);
        log(augmentedUserMessage);

        return augmentedUserMessage;
    }

    private CompletableFuture<Collection<List<Content>>> retrieveFromAll(Collection<ContentRetriever> retrievers,
                                                                         Query query) {
        List<CompletableFuture<List<Content>>> futureContents = retrievers.stream()
                .map(retriever -> supplyAsync(() -> retrieve(retriever, query), executor))
                .collect(toList());

        return allOf(futureContents.toArray(new CompletableFuture[0]))
                .thenApply(ignored ->
                        futureContents.stream()
                                .map(CompletableFuture::join)
                                .collect(toList())
                );
    }

    private static List<Content> retrieve(ContentRetriever retriever, Query query) {
        List<Content> contents = retriever.retrieve(query);
        log(query, retriever, contents);
        return contents;
    }

    private static Map<Query, Collection<List<Content>>> join(
            Map<Query, CompletableFuture<Collection<List<Content>>>> queryToFutureContents) {
        return allOf(queryToFutureContents.values().toArray(new CompletableFuture[0]))
                .thenApply(ignored ->
                        queryToFutureContents.entrySet().stream()
                                .collect(toMap(
                                        Map.Entry::getKey,
                                        entry -> entry.getValue().join()
                                ))
                ).join();
    }

    private static void logQueries(Query originalQuery, Collection<Query> queries) {
        if (queries.size() == 1) {
            Query transformedQuery = queries.iterator().next();
            if (!transformedQuery.equals(originalQuery)) {
                log.debug("Transformed original query '{}' into '{}'",
                        originalQuery.text(), transformedQuery.text());
            }
        } else {
            log.debug("Transformed original query '{}' into the following queries:\n{}",
                    originalQuery.text(), queries.stream()
                            .map(Query::text)
                            .map(query -> "- '" + query + "'")
                            .collect(joining("\n")));
        }
    }

    private static void log(Query query, Collection<ContentRetriever> retrievers) {
        // TODO use retriever id
        if (retrievers.size() == 1) {
            log.debug("Routing query '{}' to the following retriever: {}",
                    query.text(), retrievers.iterator().next());
        } else {
            log.debug("Routing query '{}' to the following retrievers:\n{}",
                    query.text(), retrievers.stream()
                            .map(retriever -> "- " + retriever.toString())
                            .collect(joining("\n")));
        }
    }

    private static void log(Query query, ContentRetriever retriever, List<Content> contents) {<FILL_FUNCTION_BODY>}

    private static void log(Map<Query, Collection<List<Content>>> queryToContents, List<Content> contents) {

        int contentCount = 0;
        for (Map.Entry<Query, Collection<List<Content>>> entry : queryToContents.entrySet()) {
            for (List<Content> contentList : entry.getValue()) {
                contentCount += contentList.size();
            }
        }
        if (contentCount == contents.size()) {
            return;
        }

        log.debug("Aggregated {} content(s) into {}", contentCount, contents.size());

        log.trace("Aggregated {} content(s) into:\n{}",
                contentCount, contents.stream()
                        .map(Content::textSegment)
                        .map(segment -> "- " + escapeNewlines(segment.text()))
                        .collect(joining("\n")));
    }

    private static void log(UserMessage augmentedUserMessage) {
        log.trace("Augmented user message: " + escapeNewlines(augmentedUserMessage.singleText()));
    }

    private static String escapeNewlines(String text) {
        return text.replace("\n", "\\n");
    }

    public static DefaultRetrievalAugmentorBuilder builder() {
        return new DefaultRetrievalAugmentorBuilder();
    }

    public static class DefaultRetrievalAugmentorBuilder {

        public DefaultRetrievalAugmentorBuilder contentRetriever(ContentRetriever contentRetriever) {
            this.queryRouter = new DefaultQueryRouter(ensureNotNull(contentRetriever, "contentRetriever"));
            return this;
        }
    }
}

        // TODO use retriever id
        log.debug("Retrieved {} contents using query '{}' and retriever '{}'",
                contents.size(), query.text(), retriever);

        if (contents.size() > 0) {
            log.trace("Retrieved {} contents using query '{}' and retriever '{}':\n{}",
                    contents.size(), query.text(), retriever, contents.stream()
                            .map(Content::textSegment)
                            .map(segment -> "- " + escapeNewlines(segment.text()))
                            .collect(joining("\n")));
        }
    
<no_super_class>