Monday, 21 July 2025

How to Access Executed Tools in LangChain4j Java Using Result?

When working with LangChain4j in Java, it's often useful to inspect which tools were used during an AI interaction. This is especially important for logging, debugging, or dynamically responding based on tool usage. LangChain4j provides a convenient way to retrieve this information by wrapping your return type in the Result<T> class.

 

LangChain4j makes it straightforward to track the tools executed during a conversation with an AI service. To capture this metadata, you simply need to define your service to return a Result<T> instead of a plain value.

 

Here’s how you can do it:

public interface ChatAssistant {
    Result<String> chat(@UserMessage String message);
}

Now, when you call the method:

Result<String> chatResponse = chatAssistant.chat(userMessage);

You gain access to all tools invoked during the AI response. The snippet below extracts the list of tools and their arguments:

List<String> toolsUsed = new ArrayList<>();

List<ToolExecution> toolsExecuted = chatResponse.toolExecutions();
for (ToolExecution toolExecution : toolsExecuted) {
    ToolExecutionRequest toolExecutionRequest = toolExecution.request();

    String arguments = toolExecutionRequest.arguments();
    String toolName = toolExecutionRequest.name();

    toolsUsed.add(toolName + "(" + arguments + ")");
}

Follow below step-by-step procedure to build the working application.

 

Step 1: Create new maven project 'access-executed-tools'.

 

Step 2: Update pom.xml with maven dependencies.

 

pom.xml

 

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.sample.app</groupId>
    <artifactId>access-executed-tools</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <java.version>21</java.version>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.10</version>
    </parent>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>dev.langchain4j</groupId>
                <artifactId>langchain4j-bom</artifactId>
                <version>1.0.1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-spring-boot-starter</artifactId>
        </dependency>


        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-ollama-spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            <version>2.6.0</version>
        </dependency>

        <!--
        https://mvnrepository.com/artifact/jakarta.validation/jakarta.validation-api -->
        <dependency>
            <groupId>jakarta.validation</groupId>
            <artifactId>jakarta.validation-api</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal> <!-- Important -->
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

Step 3: Define ChatResponse class.

 

ChatResponse.java

 

package com.sample.app.dto;

import java.util.List;

public class ChatResponse {
    private String message;
    private List<String> tools;

    public ChatResponse() {
    }

    public ChatResponse(String message, List<String> tools) {
        this.message = message;
        this.tools = tools;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public List<String> getTools() {
        return tools;
    }

    public void setTools(List<String> tools) {
        this.tools = tools;
    }

}

Step 4: Define ChatAssistant.

 

ChatAssistant.java

 

package com.sample.app.interfaces;

import dev.langchain4j.service.Result;
import dev.langchain4j.service.UserMessage;

public interface ChatAssistant {
    Result<String> chat(@UserMessage String message);
}

Step 5: Define MathTools.

 

MathTools.java

 

package com.sample.app.tools;

import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.model.output.structured.Description;

@Description("All the tools related to mathematical operations are found here")
public class MathTools {

    @Tool("""
            Calculate the square root of a number, and return the result as a double value.
            """)
    public Double squareRoot(double number) {
        System.out.println("***Calculating the Square Root Of A Number***");
        return Math.sqrt(number);
    }

    @Tool("""
            Take radians as input and return the equivalant value in degrees
            """)
    public Double radiansToDegrees(double radians) {
        System.out.println("***Converting Radians to Degrees***");
        return Math.toDegrees(radians);
    }

    @Tool("""
            Take degress as input and return the equivalant value in radians
            """)
    public Double degreesToRadians(double degrees) {
        System.out.println("***Converting Degrees to Radians***");
        return Math.toRadians(degrees);
    }

}

Step 6: Define ChatService.

 

ChatService.java

 

package com.sample.app.service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.fasterxml.jackson.core.exc.StreamWriteException;
import com.fasterxml.jackson.databind.DatabindException;
import com.sample.app.dto.ChatResponse;
import com.sample.app.interfaces.ChatAssistant;
import com.sample.app.tools.MathTools;

import dev.langchain4j.agent.tool.ToolExecutionRequest;
import dev.langchain4j.model.ollama.OllamaChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.Result;
import dev.langchain4j.service.tool.ToolExecution;

@Service
public class ChatService {
    @Autowired
    private OllamaChatModel chatModel;

    private ChatAssistant chatAssistant;

    public ChatResponse chat(String userMessage) throws StreamWriteException, DatabindException, IOException {
        if (chatAssistant == null) {
            synchronized (ChatService.class) {
                if (chatAssistant == null) {
                    chatAssistant = AiServices.builder(ChatAssistant.class).chatModel(chatModel).tools(new MathTools())
                            .build();
                }
            }
        }

        Result<String> chatResponse = chatAssistant.chat(userMessage);

        List<String> toolsUsed = new ArrayList<>();

        List<ToolExecution> toolsExecuted = chatResponse.toolExecutions();
        for (ToolExecution toolExecution : toolsExecuted) {
            ToolExecutionRequest toolExecutionRequest = toolExecution.request();

            String arguments = toolExecutionRequest.arguments();
            String toolName = toolExecutionRequest.name();

            toolsUsed.add(toolName + "(" + arguments + ")");

        }

        return new ChatResponse(chatResponse.content(), toolsUsed);
    }
}

Step 7: Define MathTools.

 

MathTools.java

package com.sample.app.tools;

import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.model.output.structured.Description;

@Description("All the tools related to mathematical operations are found here")
public class MathTools {

    @Tool("""
            Calculate the square root of a number, and return the result as a double value.
            """)
    public Double squareRoot(double number) {
        System.out.println("***Calculating the Square Root Of A Number***");
        return Math.sqrt(number);
    }

    @Tool("""
            Take radians as input and return the equivalant value in degrees
            """)
    public Double radiansToDegrees(double radians) {
        System.out.println("***Converting Radians to Degrees***");
        return Math.toDegrees(radians);
    }

    @Tool("""
            Take degress as input and return the equivalant value in radians
            """)
    public Double degreesToRadians(double degrees) {
        System.out.println("***Converting Degrees to Radians***");
        return Math.toRadians(degrees);
    }

}

Step 8: Define LangchainConfig and SwaggerConfig classes.

 

LangchainConfig.java

package com.sample.app.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import dev.langchain4j.http.client.spring.restclient.SpringRestClientBuilderFactory;
import dev.langchain4j.model.ollama.OllamaChatModel;

@Configuration
public class LangchainConfig {

    @Bean
    public OllamaChatModel ollamaLanguageModel() {
        return OllamaChatModel.builder().baseUrl("http://localhost:11434")
                .modelName("llama3.2")
                .logRequests(true)
                .httpClientBuilder(new SpringRestClientBuilderFactory().create()) // explicitly use Spring's HTTP client
                .build();
    }
}

SwaggerConfig.java

package com.sample.app.config;

import org.springframework.context.annotation.Configuration;

import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Info;

@Configuration
@OpenAPIDefinition(info = @Info(title = "Chat service Application", version = "v1"))
public class SwaggerConfig {

}

Step 9: Define ChatController class.

 

ChatController.java

 

package com.sample.app.controller;

import java.io.IOException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.fasterxml.jackson.core.exc.StreamWriteException;
import com.fasterxml.jackson.databind.DatabindException;
import com.sample.app.dto.ChatResponse;
import com.sample.app.service.ChatService;

import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;

@RestController
@RequestMapping("/api/chat")
@CrossOrigin("*")
@Tag(name = "Chat Controller", description = "This section contains APIs related to Chat APIs Powered by Ollama")
public class ChatController {

    @Autowired
    private ChatService chatService;

    @PostMapping
    public ChatResponse chat(@RequestBody @Valid ChatRequestBody chatRequestBody) throws StreamWriteException, DatabindException, IOException {
        return chatService.chat(chatRequestBody.getMessage());
    }

    private static class ChatRequestBody {
        @NotEmpty
        private String message;

        public String getMessage() {
            return message;
        }

        public void setMessage(String message) {
            this.message = message;
        }



    }
}

 

Step 10: Define main application class.

 

App.java

package com.sample.app;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

}

Build the project

Navigate to the folder where pom.xml is located, and execute following command to generate an executable.

mvn clean install

Upon successful execution of the command, you can see access-executed-tools-0.0.1-SNAPSHOT.jar file in target folder.

$ ls ./target/
access-executed-tools-0.0.1-SNAPSHOT.jar        generated-test-sources
access-executed-tools-0.0.1-SNAPSHOT.jar.original   maven-archiver
classes                         maven-status
generated-sources                   test-classes

Run the Application

Execute following command to run the Application.

java -jar ./target/access-executed-tools-0.0.1-SNAPSHOT.jar --server.port=1235

 

Open the url http://localhost:1235/swagger-ui/index.html in browser, and execute the API

/api/chat with below payload.

{
  "message": "What is the square root of 4356"
}

You will see below response.

{
  "message": "The square root of 4356 is 66.0.",
  "tools": [
    "squareRoot({\n  \"number\" : \"4356\"\n})"
  ]
}

You can download the Application from this link. 

 

Previous                                                    Next                                                    Home

No comments:

Post a Comment