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

tools.mddocs/

Tools

Tools enable AI services to perform actions and access external systems. The Quarkus LangChain4j Core module provides annotations for tool integration, method-level tool configuration, and comprehensive error handling.

Core Imports

import io.quarkiverse.langchain4j.ToolBox;
import io.quarkiverse.langchain4j.HandleToolArgumentError;
import io.quarkiverse.langchain4j.HandleToolExecutionError;
import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.service.tool.ToolErrorHandlerResult;
import dev.langchain4j.service.tool.ToolErrorContext;

@ToolBox Annotation

package io.quarkiverse.langchain4j;

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)
public @interface ToolBox {
    Class<?>[] value();
}

Usage

The @ToolBox annotation overrides the service-level tool configuration for a specific method. All specified tool classes must be CDI beans.

import io.quarkiverse.langchain4j.RegisterAiService;
import io.quarkiverse.langchain4j.ToolBox;

@RegisterAiService(tools = { EmailTool.class, DatabaseTool.class })
public interface AssistantService {
    // Uses service-level tools: EmailTool and DatabaseTool
    String help(String request);
    
    // Overrides to use only WebSearchTool for this method
    @ToolBox(WebSearchTool.class)
    String searchAndRespond(String query);
    
    // Overrides to use no tools for this method
    @ToolBox({})
    String chatWithoutTools(String message);
}

@HandleToolArgumentError Annotation

package io.quarkiverse.langchain4j;

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)
public @interface HandleToolArgumentError {
}

Usage

Handles ToolArgumentsException thrown during tool invocation (when the AI provides invalid arguments). The annotated method must be:

  • Static
  • Declared on the AiService interface
  • Accept parameters: Throwable and/or ToolErrorContext (in any order)
  • Return: String or ToolErrorHandlerResult

The returned string/result is sent back to the AI model as an error message.

import io.quarkiverse.langchain4j.RegisterAiService;
import io.quarkiverse.langchain4j.HandleToolArgumentError;
import dev.langchain4j.service.tool.ToolErrorContext;
import dev.langchain4j.service.tool.ToolErrorHandlerResult;

@RegisterAiService(tools = { EmailTool.class })
public interface AssistantWithErrorHandling {
    String chat(String message);
    
    @HandleToolArgumentError
    static String handleArgumentError(Throwable error, ToolErrorContext context) {
        return "Invalid arguments for tool " + context.toolName() + 
               ": " + error.getMessage() + ". Please try again with correct arguments.";
    }
}

Using ToolErrorHandlerResult for more control:

@RegisterAiService(tools = { EmailTool.class })
public interface AdvancedAssistant {
    String chat(String message);
    
    @HandleToolArgumentError
    static ToolErrorHandlerResult handleArgumentError(Throwable error, ToolErrorContext context) {
        if (error.getMessage().contains("missing email")) {
            return ToolErrorHandlerResult.builder()
                .errorMessage("Email address is required. Please provide a valid email.")
                .shouldRetry(true)
                .build();
        }
        return ToolErrorHandlerResult.builder()
            .errorMessage("Invalid arguments: " + error.getMessage())
            .shouldRetry(false)
            .build();
    }
}

@HandleToolExecutionError Annotation

package io.quarkiverse.langchain4j;

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)
public @interface HandleToolExecutionError {
}

Usage

Handles ToolExecutionException thrown during tool execution (when the tool itself fails). Same signature requirements as @HandleToolArgumentError.

import io.quarkiverse.langchain4j.RegisterAiService;
import io.quarkiverse.langchain4j.HandleToolExecutionError;
import dev.langchain4j.service.tool.ToolErrorContext;

@RegisterAiService(tools = { DatabaseTool.class })
public interface DataAssistant {
    String query(String request);
    
    @HandleToolExecutionError
    static String handleExecutionError(Throwable error, ToolErrorContext context) {
        if (error instanceof java.sql.SQLException) {
            return "Database is temporarily unavailable. Please try again later.";
        }
        return "Failed to execute " + context.toolName() + ": " + error.getMessage();
    }
}

ToolErrorContext

package dev.langchain4j.service.tool;

public interface ToolErrorContext {
    String toolName();
    String arguments();
    Object memoryId();
}

Provides context information about the tool error for use in error handlers.

Usage Examples

Basic Tool Integration

import jakarta.enterprise.context.ApplicationScoped;
import dev.langchain4j.agent.tool.Tool;

@ApplicationScoped
public class EmailTool {
    @Tool("Send an email to a recipient with subject and body")
    public String sendEmail(String to, String subject, String body) {
        // Implementation
        emailService.send(to, subject, body);
        return "Email sent successfully to " + to;
    }
}

@RegisterAiService(tools = { EmailTool.class })
public interface EmailAssistant {
    String help(String request);
}

Method-Level Tool Override

@ApplicationScoped
public class EmailTool {
    @Tool("Send an email")
    public String sendEmail(String to, String subject, String body) {
        return "Email sent";
    }
}

@ApplicationScoped
public class SearchTool {
    @Tool("Search the web")
    public String search(String query) {
        return "Search results for: " + query;
    }
}

@ApplicationScoped
public class DatabaseTool {
    @Tool("Query the database")
    public String query(String sql) {
        return "Query results";
    }
}

@RegisterAiService(tools = { EmailTool.class, DatabaseTool.class })
public interface MultiToolAssistant {
    // Uses EmailTool and DatabaseTool
    String help(String request);
    
    // Uses only SearchTool
    @ToolBox(SearchTool.class)
    String search(String query);
    
    // Uses all three tools
    @ToolBox({ EmailTool.class, DatabaseTool.class, SearchTool.class })
    String helpWithSearch(String request);
    
    // Uses no tools
    @ToolBox({})
    String simpleChat(String message);
}

Comprehensive Error Handling

import io.quarkiverse.langchain4j.RegisterAiService;
import io.quarkiverse.langchain4j.HandleToolArgumentError;
import io.quarkiverse.langchain4j.HandleToolExecutionError;
import dev.langchain4j.service.tool.ToolErrorContext;
import dev.langchain4j.service.tool.ToolErrorHandlerResult;

@RegisterAiService(tools = { PaymentTool.class, EmailTool.class })
public interface PaymentAssistant {
    String processRequest(String request);
    
    @HandleToolArgumentError
    static ToolErrorHandlerResult handleArgumentError(Throwable error, ToolErrorContext context) {
        String toolName = context.toolName();
        String message = error.getMessage();
        
        if (toolName.equals("processPayment")) {
            if (message.contains("amount")) {
                return ToolErrorHandlerResult.builder()
                    .errorMessage("Invalid payment amount. Please provide a positive number.")
                    .shouldRetry(true)
                    .build();
            }
            if (message.contains("account")) {
                return ToolErrorHandlerResult.builder()
                    .errorMessage("Invalid account number format. Use format: XXXX-XXXX-XXXX")
                    .shouldRetry(true)
                    .build();
            }
        }
        
        return ToolErrorHandlerResult.builder()
            .errorMessage("Invalid arguments for " + toolName + ": " + message)
            .shouldRetry(false)
            .build();
    }
    
    @HandleToolExecutionError
    static String handleExecutionError(Throwable error, ToolErrorContext context) {
        String toolName = context.toolName();
        
        if (toolName.equals("processPayment")) {
            if (error instanceof InsufficientFundsException) {
                return "Payment failed: Insufficient funds in account.";
            }
            if (error instanceof AccountLockedException) {
                return "Payment failed: Account is locked. Please contact support.";
            }
            return "Payment processing failed: " + error.getMessage();
        }
        
        if (toolName.equals("sendEmail")) {
            return "Failed to send notification email. Payment was processed successfully.";
        }
        
        return "Tool execution failed: " + error.getMessage();
    }
}

Error Handling with Only Throwable

@RegisterAiService(tools = { WeatherTool.class })
public interface WeatherAssistant {
    String getWeather(String location);
    
    @HandleToolExecutionError
    static String handleError(Throwable error) {
        if (error instanceof java.net.UnknownHostException) {
            return "Weather service is currently unavailable. Please try again later.";
        }
        return "Failed to retrieve weather data: " + error.getMessage();
    }
}

Error Handling with Only Context

@RegisterAiService(tools = { CalculatorTool.class })
public interface CalculatorAssistant {
    String calculate(String expression);
    
    @HandleToolArgumentError
    static String handleArgumentError(ToolErrorContext context) {
        return "Invalid mathematical expression provided for " + context.toolName() + 
               ". Please use valid numbers and operators.";
    }
}

Tool with Maximum Invocations

@RegisterAiService(
    tools = { RecursiveTool.class },
    maxSequentialToolInvocations = 3
)
public interface LimitedAssistant {
    String process(String request);
}

If the AI tries to call tools more than 3 times sequentially, the request fails with an error.

Dynamic Tool Provider

import dev.langchain4j.service.tool.ToolProvider;
import java.util.function.Supplier;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class DynamicToolProviderSupplier implements Supplier<ToolProvider> {
    @Override
    public ToolProvider get() {
        return new ToolProvider() {
            @Override
            public java.util.List<dev.langchain4j.agent.tool.ToolSpecification> provideTools(
                Object memoryId) {
                // Return different tools based on user context
                if (isAdminUser(memoryId)) {
                    return adminTools();
                }
                return standardTools();
            }
        };
    }
}

@RegisterAiService(
    toolProviderSupplier = DynamicToolProviderSupplier.class
)
public interface ContextualAssistant {
    String help(String request);
}

Combining Static and Dynamic Tools

@RegisterAiService(
    tools = { EmailTool.class, CalendarTool.class },
    toolProviderSupplier = RegisterAiService.BeanIfExistsToolProviderSupplier.class
)
public interface HybridAssistant {
    String help(String request);
}

This service has access to both the static tools (EmailTool, CalendarTool) and any tools provided dynamically by the ToolProvider CDI bean.

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