CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-quarkiverse-langchain4j--quarkus-langchain4j-gemini-common

Common shared infrastructure for integrating Google Gemini AI models with Quarkus applications through the LangChain4j framework, providing base chat model functionality, schema mapping, and embedding model support.

Overview
Eval results
Files

function-calling.mddocs/

Function Calling

Complete support for Gemini's function calling capabilities, including function declarations with parameter schemas, function calls from the model, and function responses back to the model. Function calling enables the model to interact with external tools and APIs.

Capabilities

Function Declaration

Declares a function that the model can call, including its name, description, and parameter schema. Function declarations are provided to the model in the request to enable tool use.

/**
 * Declares a function available for the model to call.
 *
 * @param name The function name
 * @param description Human-readable description of what the function does
 * @param parameters Schema defining the function's parameters
 */
public record FunctionDeclaration(String name, String description, Parameters parameters);

Function Declaration Parameters

Defines the parameter schema for a function using JSON Schema-like structure.

/**
 * Parameter schema for a function.
 *
 * @param type The parameter type (typically "object")
 * @param properties Map of parameter names to their schema definitions
 * @param required List of required parameter names
 */
public record Parameters(
    String type,
    Map<String, Map<String, Object>> properties,
    List<String> required
) {
    /**
     * Creates an object-type parameter schema.
     *
     * @param properties Map of parameter names to their schemas
     * @param required List of required parameter names
     * @return Parameters instance with type "object"
     */
    public static Parameters objectType(
        Map<String, Map<String, Object>> properties,
        List<String> required
    );

    /**
     * Creates an empty parameter schema (for functions with no parameters).
     *
     * @return Parameters instance with no properties
     */
    public static Parameters empty();
}

Function Call

Represents a function call made by the model. When the model decides to use a tool, it returns a FunctionCall in the response.

/**
 * A function call from the model.
 *
 * @param name The name of the function to call
 * @param args Map of argument names to values provided by the model
 */
public record FunctionCall(String name, Map<String, Object> args);

Function Response

Represents the result of executing a function. The application executes the function and returns the result to the model in this format.

/**
 * A function execution response to send back to the model.
 *
 * @param name The name of the function that was executed
 * @param response The response data from the function execution
 */
public record FunctionResponse(String name, Response response);

/**
 * Response data from function execution.
 *
 * @param name The function name
 * @param content The function's return value (any JSON-serializable object)
 */
public record Response(String name, Object content);

Usage Examples

Declaring a Simple Function

// Declare a weather function
FunctionDeclaration getWeather = new FunctionDeclaration(
    "get_weather",
    "Get the current weather for a location",
    FunctionDeclaration.Parameters.objectType(
        Map.of(
            "location", Map.of(
                "type", "string",
                "description", "City and state, e.g., San Francisco, CA"
            ),
            "unit", Map.of(
                "type", "string",
                "enum", List.of("celsius", "fahrenheit"),
                "description", "Temperature unit"
            )
        ),
        List.of("location")  // only location is required
    )
);

Declaring a Function with No Parameters

FunctionDeclaration getCurrentTime = new FunctionDeclaration(
    "get_current_time",
    "Returns the current date and time",
    FunctionDeclaration.Parameters.empty()
);

Declaring a Function with Complex Parameters

// Function with nested object parameters
FunctionDeclaration createEvent = new FunctionDeclaration(
    "create_calendar_event",
    "Creates a new event in the user's calendar",
    FunctionDeclaration.Parameters.objectType(
        Map.of(
            "title", Map.of(
                "type", "string",
                "description", "Event title"
            ),
            "start_time", Map.of(
                "type", "string",
                "description", "Start time in ISO 8601 format"
            ),
            "end_time", Map.of(
                "type", "string",
                "description", "End time in ISO 8601 format"
            ),
            "attendees", Map.of(
                "type", "array",
                "items", Map.of("type", "string"),
                "description", "List of attendee email addresses"
            ),
            "reminder_minutes", Map.of(
                "type", "integer",
                "description", "Minutes before event to send reminder"
            )
        ),
        List.of("title", "start_time", "end_time")
    )
);

Complete Function Calling Flow

// Step 1: Declare functions and create a tool
FunctionDeclaration getWeather = new FunctionDeclaration(
    "get_weather",
    "Get current weather for a location",
    FunctionDeclaration.Parameters.objectType(
        Map.of(
            "location", Map.of("type", "string", "description", "City name")
        ),
        List.of("location")
    )
);

GenerateContentRequest.Tool weatherTool =
    GenerateContentRequest.Tool.ofFunctionDeclarations(List.of(getWeather));

// Step 2: Send request with tools
GenerateContentRequest request = new GenerateContentRequest(
    List.of(Content.ofPart(
        Content.Part.ofText("What's the weather in Paris?")
    )),
    null,
    List.of(weatherTool),
    null
);

GenerateContentResponse response = chatModel.generateContext(request);

// Step 3: Check if model wants to call a function
List<ToolExecutionRequest> toolCalls =
    GenerateContentResponseHandler.getToolExecutionRequests(response);

if (!toolCalls.isEmpty()) {
    // Step 4: Extract function call details
    GenerateContentResponse.Candidate candidate = response.candidates().get(0);
    Content.Part callPart = candidate.content().parts().get(0);
    FunctionCall call = callPart.functionCall();

    System.out.println("Function: " + call.name());
    System.out.println("Arguments: " + call.args());

    // Step 5: Execute the function
    Map<String, Object> weatherData = executeWeatherFunction(
        (String) call.args().get("location")
    );

    // Step 6: Create function response
    FunctionResponse.Response responseData = new FunctionResponse.Response(
        "get_weather",
        weatherData
    );
    FunctionResponse functionResponse = new FunctionResponse(
        "get_weather",
        responseData
    );

    // Step 7: Send function result back to model
    List<Content> conversationHistory = new ArrayList<>(request.contents());
    conversationHistory.add(candidate.content());  // Add model's function call
    conversationHistory.add(Content.ofPart(
        Content.Part.ofFunctionResponse(functionResponse)
    ));

    GenerateContentRequest followUpRequest = new GenerateContentRequest(
        conversationHistory,
        null,
        List.of(weatherTool),
        null
    );

    GenerateContentResponse finalResponse = chatModel.generateContext(followUpRequest);
    String answer = GenerateContentResponseHandler.getText(finalResponse);
    System.out.println("Final answer: " + answer);
}

// Helper method
private Map<String, Object> executeWeatherFunction(String location) {
    // Call actual weather API
    return Map.of(
        "temperature", 22,
        "condition", "sunny",
        "humidity", 65,
        "location", location
    );
}

Multiple Function Declarations

// Declare multiple functions
FunctionDeclaration getWeather = new FunctionDeclaration(
    "get_weather",
    "Get current weather",
    FunctionDeclaration.Parameters.objectType(
        Map.of("location", Map.of("type", "string")),
        List.of("location")
    )
);

FunctionDeclaration getForecast = new FunctionDeclaration(
    "get_forecast",
    "Get weather forecast for the next 7 days",
    FunctionDeclaration.Parameters.objectType(
        Map.of(
            "location", Map.of("type", "string"),
            "days", Map.of("type", "integer", "description", "Number of days (1-7)")
        ),
        List.of("location")
    )
);

FunctionDeclaration getAirQuality = new FunctionDeclaration(
    "get_air_quality",
    "Get current air quality index",
    FunctionDeclaration.Parameters.objectType(
        Map.of("location", Map.of("type", "string")),
        List.of("location")
    )
);

// Create tool with multiple functions
GenerateContentRequest.Tool weatherTools =
    GenerateContentRequest.Tool.ofFunctionDeclarations(
        List.of(getWeather, getForecast, getAirQuality)
    );

// Model can now choose which function(s) to call

Handling Multiple Function Calls

GenerateContentResponse response = chatModel.generateContext(request);

// Model might call multiple functions in one response
for (GenerateContentResponse.Candidate candidate : response.candidates()) {
    for (GenerateContentResponse.Candidate.Part part : candidate.content().parts()) {
        if (part.functionCall() != null) {
            FunctionCall call = part.functionCall();

            // Execute each function
            Object result = executeFunctionByName(call.name(), call.args());

            // Collect function responses
            // ...
        }
    }
}

Error Handling in Function Responses

try {
    Map<String, Object> result = executeFunction(call.name(), call.args());

    FunctionResponse.Response response = new FunctionResponse.Response(
        call.name(),
        result
    );
    functionResponse = new FunctionResponse(call.name(), response);

} catch (Exception e) {
    // Return error information to the model
    FunctionResponse.Response errorResponse = new FunctionResponse.Response(
        call.name(),
        Map.of(
            "error", true,
            "message", e.getMessage(),
            "type", e.getClass().getSimpleName()
        )
    );
    functionResponse = new FunctionResponse(call.name(), errorResponse);
}

Parameter Schema Details

Supported Types

  • string: Text values
  • integer: Whole numbers
  • number: Floating point numbers
  • boolean: true/false values
  • array: Lists of values
  • object: Nested objects

Common Schema Properties

  • type: The data type
  • description: Human-readable description
  • enum: List of allowed values (for string/integer types)
  • items: Schema for array elements
  • properties: Nested properties for objects
  • required: List of required property names

Example Complex Schema

Map<String, Map<String, Object>> complexProperties = Map.of(
    "user", Map.of(
        "type", "object",
        "properties", Map.of(
            "name", Map.of("type", "string"),
            "age", Map.of("type", "integer")
        ),
        "required", List.of("name")
    ),
    "preferences", Map.of(
        "type", "array",
        "items", Map.of("type", "string")
    ),
    "priority", Map.of(
        "type", "string",
        "enum", List.of("low", "medium", "high")
    )
);

FunctionDeclaration.Parameters params =
    FunctionDeclaration.Parameters.objectType(complexProperties, List.of("user"));

Integration with LangChain4j

When using these types with LangChain4j's abstractions:

  • ContentMapper converts ToolSpecification to FunctionDeclaration
  • GenerateContentResponseHandler extracts ToolExecutionRequest from FunctionCall
  • Function responses are automatically formatted for the next model turn

Best Practices

  1. Clear Descriptions: Provide detailed descriptions to help the model understand when and how to use functions
  2. Required Parameters: Mark essential parameters as required
  3. Enums for Constraints: Use enum to limit possible values
  4. Descriptive Names: Use clear, action-oriented function names
  5. Error Handling: Always return meaningful error information in function responses
  6. Type Safety: Ensure function implementation matches declared parameter types
  7. Validation: Validate model-provided arguments before execution

Install with Tessl CLI

npx tessl i tessl/maven-io-quarkiverse-langchain4j--quarkus-langchain4j-gemini-common

docs

chat-models.md

configuration.md

content-types.md

embedding-models.md

function-calling.md

index.md

requests-responses.md

utilities.md

tile.json