CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-quarkiverse-langchain4j--quarkus-langchain4j-anthropic

Quarkus extension for integrating Anthropic Claude LLM models into Quarkus applications via LangChain4j

Overview
Eval results
Files

client-api.mddocs/

Low-Level Client API

The quarkus-langchain4j-anthropic extension provides low-level client APIs for advanced use cases requiring direct control over API requests, beta features, or custom request handling.

QuarkusAnthropicClient

The core client class that extends LangChain4j's AnthropicClient with Quarkus-specific REST client integration.

package io.quarkiverse.langchain4j.anthropic;

class QuarkusAnthropicClient extends dev.langchain4j.model.anthropic.internal.client.AnthropicClient {
    /**
     * Default tools beta header value
     */
    public static final String BETA = "tools-2024-04-04";

    /**
     * Create synchronous message completion
     *
     * @param request The message request
     * @return Complete message response
     */
    public dev.langchain4j.model.anthropic.internal.api.AnthropicCreateMessageResponse
        createMessage(
            dev.langchain4j.model.anthropic.internal.api.AnthropicCreateMessageRequest request
        );

    /**
     * Create streaming message completion with options
     *
     * @param request The message request
     * @param options Streaming options (e.g., return thinking)
     * @param handler Streaming response handler
     */
    public void createMessage(
        dev.langchain4j.model.anthropic.internal.api.AnthropicCreateMessageRequest request,
        dev.langchain4j.model.anthropic.internal.client.AnthropicCreateMessageOptions options,
        dev.langchain4j.model.chat.response.StreamingChatResponseHandler handler
    );

    /**
     * Create streaming message completion without options
     *
     * @param request The message request
     * @param handler Streaming response handler
     */
    public void createMessage(
        dev.langchain4j.model.anthropic.internal.api.AnthropicCreateMessageRequest request,
        dev.langchain4j.model.chat.response.StreamingChatResponseHandler handler
    );

    /**
     * Set thread-local hint to enable cURL request logging
     *
     * @param logCurl Whether to log cURL commands
     */
    public static void setLogCurlHint(boolean logCurl);

    /**
     * Set thread-local hint to disable beta headers
     *
     * @param disableBetaHint Whether to disable beta headers
     */
    public static void setDisableBetaHint(boolean disableBetaHint);
}

QuarkusAnthropicClient.Builder

Fluent builder for constructing QuarkusAnthropicClient instances.

class QuarkusAnthropicClient.Builder
    extends dev.langchain4j.model.anthropic.internal.client.AnthropicClient.Builder<
        dev.langchain4j.model.anthropic.internal.client.AnthropicClient,
        QuarkusAnthropicClient.Builder
    > {
    /**
     * Enable cURL command logging
     */
    public boolean logCurl;

    /**
     * Disable beta headers in requests
     */
    public boolean disableBetaHeader;

    /**
     * Build the client instance
     *
     * @return Configured AnthropicClient (parent class)
     */
    public dev.langchain4j.model.anthropic.internal.client.AnthropicClient build();
}

Builder Usage Example

import io.quarkiverse.langchain4j.anthropic.QuarkusAnthropicClient;
import dev.langchain4j.model.anthropic.internal.client.AnthropicClient;
import java.time.Duration;

// Note: logCurl and disableBetaHeader are public fields, not methods
// They are set via thread-local hints before creating the client
QuarkusAnthropicClient.setLogCurlHint(true);
QuarkusAnthropicClient.setDisableBetaHint(false);

// Note: build() returns AnthropicClient (parent class), not QuarkusAnthropicClient
AnthropicClient client = QuarkusAnthropicClient.builder()
    .apiKey("sk-ant-...")
    .baseUrl("https://api.anthropic.com/v1/")
    .version("2023-06-01")
    .timeout(Duration.ofSeconds(30))
    .logRequests(true)
    .logResponses(true)
    .build();

Inherited Builder Methods

The QuarkusAnthropicClient.Builder extends AnthropicClient.Builder from LangChain4j and inherits the following configuration methods:

/**
 * Set the Anthropic API key (required)
 *
 * @param apiKey Your Anthropic API key (e.g., "sk-ant-...")
 * @return This builder for method chaining
 */
QuarkusAnthropicClient.Builder apiKey(String apiKey);

/**
 * Set the base URL for Anthropic API
 *
 * @param baseUrl Base URL (default: "https://api.anthropic.com/v1/")
 * @return This builder for method chaining
 */
QuarkusAnthropicClient.Builder baseUrl(String baseUrl);

/**
 * Set the Anthropic API version header
 *
 * @param version API version string (default: "2023-06-01")
 * @return This builder for method chaining
 */
QuarkusAnthropicClient.Builder version(String version);

/**
 * Set the beta features header value
 *
 * @param beta Beta header value (e.g., "tools-2024-04-04" or comma-separated list)
 * @return This builder for method chaining
 */
QuarkusAnthropicClient.Builder beta(String beta);

/**
 * Set request timeout duration
 *
 * @param timeout Timeout duration (e.g., Duration.ofSeconds(30))
 * @return This builder for method chaining
 */
QuarkusAnthropicClient.Builder timeout(java.time.Duration timeout);

/**
 * Enable HTTP request logging
 *
 * @param logRequests true to log requests, false otherwise
 * @return This builder for method chaining
 */
QuarkusAnthropicClient.Builder logRequests(boolean logRequests);

/**
 * Enable HTTP response logging
 *
 * @param logResponses true to log responses, false otherwise
 * @return This builder for method chaining
 */
QuarkusAnthropicClient.Builder logResponses(boolean logResponses);

Note: All inherited methods return QuarkusAnthropicClient.Builder for fluent method chaining.

AnthropicRestApi

JAX-RS REST client interface for direct Anthropic API access.

package io.quarkiverse.langchain4j.anthropic;

@Path("")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
interface AnthropicRestApi {
    /**
     * API key header name
     */
    String API_KEY_HEADER = "x-api-key";

    /**
     * Create message completion (synchronous)
     *
     * @param request The message request
     * @param apiMetadata HTTP headers (API key, version, beta)
     * @return Complete message response
     */
    @Path("/messages")
    @POST
    dev.langchain4j.model.anthropic.internal.api.AnthropicCreateMessageResponse
        createMessage(
            dev.langchain4j.model.anthropic.internal.api.AnthropicCreateMessageRequest request,
            @BeanParam ApiMetadata apiMetadata
        );

    /**
     * Stream message completion (SSE)
     *
     * @param request The message request
     * @param apiMetadata HTTP headers (API key, version, beta)
     * @return Reactive stream of message data
     */
    @Path("/messages")
    @POST
    @RestStreamElementType(MediaType.APPLICATION_JSON)
    io.smallrye.mutiny.Multi<dev.langchain4j.model.anthropic.internal.api.AnthropicStreamingData>
        streamMessage(
            dev.langchain4j.model.anthropic.internal.api.AnthropicCreateMessageRequest request,
            @BeanParam ApiMetadata apiMetadata
        );
}

AnthropicRestApi.ApiMetadata

Immutable HTTP header metadata for API requests.

class ApiMetadata {
    /**
     * API key (header: x-api-key)
     */
    public final String apiKey;

    /**
     * Anthropic API version (header: anthropic-version)
     */
    public final String anthropicVersion;

    /**
     * Beta features (header: anthropic-beta)
     * Optional, can be null
     */
    public final String beta;

    /**
     * Create a builder
     */
    public static ApiMetadata.Builder builder();
}

AnthropicRestApi.ApiMetadata.Builder

class ApiMetadata.Builder {
    /**
     * Set API key
     */
    public Builder apiKey(String apiKey);

    /**
     * Set Anthropic API version
     */
    public Builder anthropicVersion(String anthropicVersion);

    /**
     * Set beta header value
     */
    public Builder beta(String beta);

    /**
     * Build immutable metadata
     */
    public ApiMetadata build();
}

ApiMetadata Usage Example

import io.quarkiverse.langchain4j.anthropic.AnthropicRestApi;

AnthropicRestApi.ApiMetadata metadata = AnthropicRestApi.ApiMetadata.builder()
    .apiKey("sk-ant-...")
    .anthropicVersion("2023-06-01")
    .beta("tools-2024-04-04")
    .build();

Request/Response Classes

These classes are from LangChain4j's Anthropic module:

AnthropicCreateMessageRequest

package dev.langchain4j.model.anthropic.internal.api;

class AnthropicCreateMessageRequest {
    /**
     * Create a builder
     */
    public static Builder builder();

    /**
     * Convert to builder for modification
     */
    public Builder toBuilder();

    /**
     * Get the stream flag
     */
    public Boolean isStream();

    /**
     * Get the messages
     */
    public List<AnthropicMessage> getMessages();

    /**
     * Get the tools
     */
    public List<AnthropicTool> getTools();
}

AnthropicCreateMessageResponse

package dev.langchain4j.model.anthropic.internal.api;

class AnthropicCreateMessageResponse {
    /**
     * Response ID
     */
    public String id;

    /**
     * Response type (always "message")
     */
    public String type;

    /**
     * Role (always "assistant")
     */
    public String role;

    /**
     * Response content blocks
     */
    public List<AnthropicMessageContent> content;

    /**
     * Model used
     */
    public String model;

    /**
     * Stop reason
     */
    public String stopReason;

    /**
     * Stop sequence (if applicable)
     */
    public String stopSequence;

    /**
     * Token usage statistics
     */
    public AnthropicUsage usage;
}

AnthropicStreamingData

package dev.langchain4j.model.anthropic.internal.api;

class AnthropicStreamingData {
    /**
     * Event type:
     * - "message_start"
     * - "content_block_start"
     * - "content_block_delta"
     * - "content_block_stop"
     * - "message_delta"
     * - "message_stop"
     * - "error"
     */
    public String type;

    /**
     * Message data (for message_start)
     */
    public AnthropicMessage message;

    /**
     * Content block (for content_block_start)
     */
    public AnthropicMessageContent contentBlock;

    /**
     * Delta data (for content_block_delta and message_delta)
     */
    public Delta delta;

    /**
     * Token usage (for message_start and message_delta)
     */
    public AnthropicUsage usage;

    /**
     * Error information (for error events)
     */
    public AnthropicError error;
}

AnthropicMessage

package dev.langchain4j.model.anthropic.internal.api;

class AnthropicMessage {
    /**
     * Create a builder
     */
    public static Builder builder();

    /**
     * Get the role
     */
    public AnthropicRole getRole();

    /**
     * Get the content
     */
    public List<AnthropicMessageContent> getContent();
}

AnthropicMessage.Builder

class AnthropicMessage.Builder {
    /**
     * Set the role (USER or ASSISTANT)
     */
    public Builder role(AnthropicRole role);

    /**
     * Set the content (list of content blocks)
     */
    public Builder content(List<AnthropicMessageContent> content);

    /**
     * Build the message
     */
    public AnthropicMessage build();
}

AnthropicRole

package dev.langchain4j.model.anthropic.internal.api;

enum AnthropicRole {
    /**
     * User role for user messages
     */
    USER,

    /**
     * Assistant role for Claude responses
     */
    ASSISTANT
}

AnthropicTextContent

package dev.langchain4j.model.anthropic.internal.api;

class AnthropicTextContent extends AnthropicMessageContent {
    /**
     * Create a builder
     */
    public static Builder builder();

    /**
     * Get the text content
     */
    public String getText();
}

AnthropicTextContent.Builder

class AnthropicTextContent.Builder {
    /**
     * Set the text content
     */
    public Builder text(String text);

    /**
     * Build the text content
     */
    public AnthropicTextContent build();
}

Custom Client Usage

Direct Client Creation

Note: The Quarkus extension automatically creates and configures AnthropicChatModel instances via CDI. Direct client creation is typically only needed for advanced use cases where you need to bypass CDI or make direct API calls.

import io.quarkiverse.langchain4j.anthropic.QuarkusAnthropicClient;
import dev.langchain4j.model.anthropic.internal.client.AnthropicClient;
import java.time.Duration;

// Create custom client for direct API calls
// Note: build() returns AnthropicClient, not QuarkusAnthropicClient
AnthropicClient client = QuarkusAnthropicClient.builder()
    .apiKey("sk-ant-...")
    .baseUrl("https://api.anthropic.com/v1/")
    .timeout(Duration.ofSeconds(60))
    .build();

// Use the client to make direct API calls (see examples below)

Direct API Calls

import io.quarkiverse.langchain4j.anthropic.QuarkusAnthropicClient;
import dev.langchain4j.model.anthropic.internal.client.AnthropicClient;
import dev.langchain4j.model.anthropic.internal.api.*;
import java.util.List;

AnthropicClient client = QuarkusAnthropicClient.builder()
    .apiKey("sk-ant-...")
    .build();

// Build request with message content
AnthropicCreateMessageRequest request = AnthropicCreateMessageRequest.builder()
    .model("claude-opus-4-20250514")
    .maxTokens(1024)
    .messages(List.of(
        AnthropicMessage.builder()
            .role(dev.langchain4j.model.anthropic.internal.api.AnthropicRole.USER)
            .content(List.of(
                AnthropicTextContent.builder()
                    .text("Hello, Claude!")
                    .build()
            ))
            .build()
    ))
    .build();

// Make synchronous call
AnthropicCreateMessageResponse response = client.createMessage(request);
System.out.println(response.content.get(0).text);

Streaming with Custom Client

import io.quarkiverse.langchain4j.anthropic.QuarkusAnthropicClient;
import dev.langchain4j.model.anthropic.internal.client.AnthropicClient;
import dev.langchain4j.model.anthropic.internal.api.*;
import dev.langchain4j.model.anthropic.internal.client.AnthropicCreateMessageOptions;
import dev.langchain4j.model.chat.response.*;
import java.util.List;

AnthropicClient client = QuarkusAnthropicClient.builder()
    .apiKey("sk-ant-...")
    .build();

AnthropicCreateMessageRequest request = AnthropicCreateMessageRequest.builder()
    .model("claude-opus-4-20250514")
    .maxTokens(1024)
    .messages(List.of(
        AnthropicMessage.builder()
            .role(dev.langchain4j.model.anthropic.internal.api.AnthropicRole.USER)
            .content(List.of(
                AnthropicTextContent.builder()
                    .text("Count to 10")
                    .build()
            ))
            .build()
    ))
    .build();

// Enable thinking in streaming
AnthropicCreateMessageOptions options = new AnthropicCreateMessageOptions(true);

client.createMessage(request, options, new StreamingChatResponseHandler() {
    @Override
    public void onPartialResponse(PartialResponse response,
                                 PartialResponseContext context) {
        System.out.print(response.text());
    }

    @Override
    public void onCompleteResponse(ChatResponse response) {
        System.out.println("\nComplete!");
    }

    @Override
    public void onError(Throwable error) {
        error.printStackTrace();
    }
});

Beta Features

Enabling Beta Headers

Beta headers are automatically managed but can be controlled:

import io.quarkiverse.langchain4j.anthropic.QuarkusAnthropicClient;
import dev.langchain4j.model.anthropic.internal.client.AnthropicClient;

// Disable beta headers using thread-local hint
QuarkusAnthropicClient.setDisableBetaHint(true);
AnthropicClient client = QuarkusAnthropicClient.builder()
    .apiKey("sk-ant-...")
    .build();

// Or set custom beta header via builder method
AnthropicClient client = QuarkusAnthropicClient.builder()
    .apiKey("sk-ant-...")
    .beta("tools-2024-04-04,prompt-caching-2024-07-31")
    .build();

Thread-Local Beta Control

For fine-grained control per request:

// Disable beta headers for next request only
QuarkusAnthropicClient.setDisableBetaHint(true);
response = client.createMessage(request);

// Beta headers are re-enabled for subsequent requests

Custom Logging

Enable cURL Logging

import io.quarkiverse.langchain4j.anthropic.QuarkusAnthropicClient;
import dev.langchain4j.model.anthropic.internal.client.AnthropicClient;

// Via thread-local hint before creating client
QuarkusAnthropicClient.setLogCurlHint(true);
AnthropicClient client = QuarkusAnthropicClient.builder()
    .apiKey("sk-ant-...")
    .build();

// For single request, set hint before the call
QuarkusAnthropicClient.setLogCurlHint(true);
client.createMessage(request);

Custom Logger

The AnthropicClientLogger class provides custom HTTP logging functionality.

package io.quarkiverse.langchain4j.anthropic;

class QuarkusAnthropicClient.AnthropicClientLogger
    implements org.jboss.resteasy.reactive.client.api.ClientLogger {

    /**
     * Constructor
     *
     * @param logRequests Whether to log requests
     * @param logResponses Whether to log responses
     */
    public AnthropicClientLogger(boolean logRequests, boolean logResponses);

    /**
     * Constructor with cURL support
     *
     * @param logRequests Whether to log requests
     * @param logResponses Whether to log responses
     * @param logCurl Whether to log as cURL commands
     */
    public AnthropicClientLogger(boolean logRequests, boolean logResponses, boolean logCurl);
}

The logger implementation handles HTTP request and response logging at INFO level, automatically masking sensitive headers like API keys. When cURL logging is enabled, it generates executable cURL commands for debugging purposes.

## REST Client Factory

For even more control, create a custom REST client:

```java
import io.quarkiverse.langchain4j.anthropic.AnthropicRestApi;
import io.quarkus.rest.client.reactive.QuarkusRestClientBuilder;
import java.net.URI;
import java.util.concurrent.TimeUnit;

AnthropicRestApi restApi = QuarkusRestClientBuilder.newBuilder()
    .baseUri(URI.create("https://api.anthropic.com/v1/"))
    .connectTimeout(30, TimeUnit.SECONDS)
    .readTimeout(30, TimeUnit.SECONDS)
    .build(AnthropicRestApi.class);

// Use REST API directly
AnthropicRestApi.ApiMetadata metadata = AnthropicRestApi.ApiMetadata.builder()
    .apiKey("sk-ant-...")
    .anthropicVersion("2023-06-01")
    .build();

AnthropicCreateMessageResponse response = restApi.createMessage(request, metadata);

Integration with AnthropicChatModel

The Quarkus extension automatically creates AnthropicChatModel instances via CDI with configuration from application.properties. For most use cases, you should use CDI injection rather than creating models manually.

If you need to create an AnthropicChatModel programmatically, configure it using the builder's configuration methods (the builder does not accept a custom client instance):

import dev.langchain4j.model.anthropic.AnthropicChatModel;

// Create chat model with configuration
// Note: This bypasses the Quarkus extension's automatic configuration
AnthropicChatModel chatModel = AnthropicChatModel.builder()
    .apiKey("sk-ant-...")
    .baseUrl("https://api.anthropic.com/v1/")
    .version("2023-06-01")
    .modelName("claude-opus-4-20250514")
    .maxTokens(2048)
    .temperature(0.7)
    .topP(1.0)
    .topK(40)
    .cacheSystemMessages(true)
    .thinking(thinking -> thinking
        .type("enabled")
        .budgetTokens(10000)
        .returnThinking(true)
    )
    .build();

For CDI-managed models with automatic configuration, use the injection patterns documented in CDI Injection.

Error Handling

The client throws exceptions for error cases:

try {
    AnthropicCreateMessageResponse response = client.createMessage(request);
} catch (jakarta.ws.rs.WebApplicationException e) {
    // HTTP error (4xx, 5xx)
    int status = e.getResponse().getStatus();
    String body = e.getResponse().readEntity(String.class);
    System.err.println("HTTP " + status + ": " + body);
} catch (Exception e) {
    // Network error, timeout, etc.
    e.printStackTrace();
}

Common error codes:

  • 400: Invalid request
  • 401: Invalid API key
  • 403: Permission denied
  • 404: Resource not found
  • 429: Rate limit exceeded
  • 500: Server error
  • 529: Overloaded

Advanced Request Building

With System Messages

AnthropicCreateMessageRequest request = AnthropicCreateMessageRequest.builder()
    .model("claude-opus-4-20250514")
    .maxTokens(2048)
    .system("You are a helpful coding assistant.")
    .messages(List.of(
        AnthropicMessage.builder()
            .role(dev.langchain4j.model.anthropic.internal.api.AnthropicRole.USER)
            .content(List.of(
                AnthropicTextContent.builder()
                    .text("Explain recursion")
                    .build()
            ))
            .build()
    ))
    .build();

With Extended Thinking

AnthropicCreateMessageRequest request = AnthropicCreateMessageRequest.builder()
    .model("claude-opus-4-20250514")
    .maxTokens(4096)
    .thinking(AnthropicThinking.builder()
        .type("enabled")
        .budgetTokens(10000)
        .build())
    .messages(List.of(
        AnthropicMessage.builder()
            .role(dev.langchain4j.model.anthropic.internal.api.AnthropicRole.USER)
            .content(List.of(
                AnthropicTextContent.builder()
                    .text("Solve this complex problem: ...")
                    .build()
            ))
            .build()
    ))
    .build();

With Tool Definitions

AnthropicCreateMessageRequest request = AnthropicCreateMessageRequest.builder()
    .model("claude-opus-4-20250514")
    .maxTokens(2048)
    .tools(List.of(
        AnthropicTool.builder()
            .name("get_weather")
            .description("Get current weather for a location")
            .inputSchema(Map.of(
                "type", "object",
                "properties", Map.of(
                    "location", Map.of("type", "string")
                ),
                "required", List.of("location")
            ))
            .build()
    ))
    .messages(List.of(
        AnthropicMessage.builder()
            .role(dev.langchain4j.model.anthropic.internal.api.AnthropicRole.USER)
            .content(List.of(
                AnthropicTextContent.builder()
                    .text("What's the weather in Paris?")
                    .build()
            ))
            .build()
    ))
    .build();

When to Use Low-Level Client API

Use the low-level client API when you need:

  • Direct API control: Custom request parameters not exposed in high-level APIs
  • Beta features: Early access to experimental features
  • Custom error handling: Application-specific retry logic or error recovery
  • Performance optimization: Fine-tuned timeout and connection settings
  • Advanced streaming: Custom streaming logic with partial response handling
  • Request interception: Logging, metrics, or request modification

For most use cases, prefer the high-level CDI injection pattern for better integration with Quarkus features and simpler code.

Install with Tessl CLI

npx tessl i tessl/maven-io-quarkiverse-langchain4j--quarkus-langchain4j-anthropic@1.7.0

docs

advanced-features.md

cdi-injection.md

client-api.md

configuration.md

index.md

tile.json