tessl install tessl/maven-io-quarkiverse-langchain4j--quarkus-langchain4j-core@1.5.0Quarkus LangChain4j Core provides runtime integration for LangChain4j with the Quarkus framework, enabling declarative AI service creation through CDI annotations.
Cost Estimation provides extensible support for tracking API call costs based on token usage, with implementations for different model providers.
Note: This feature is experimental and the API may change in future versions.
Interface for estimating costs of model API calls.
// Package: io.quarkiverse.langchain4j.cost
/**
* Interface for estimating API call costs.
* EXPERIMENTAL: API may change in future versions.
*/
@Experimental("Cost estimation is experimental and may change")
public interface CostEstimator {
/**
* Check if this estimator supports the given model.
*
* @param context Context containing model name
* @return true if this estimator can estimate costs for this model
*/
boolean supports(SupportsContext context);
/**
* Estimate cost for the given context.
*
* @param context Context containing model name and token counts
* @return Cost result with input/output token costs and currency
*/
CostResult estimate(CostContext context);
/**
* Context for checking support.
*/
interface SupportsContext {
/**
* Get model name.
*/
String model();
}
/**
* Context for cost estimation.
*/
interface CostContext extends SupportsContext {
/**
* Get input token count.
*/
Integer inputTokens();
/**
* Get output token count.
*/
Integer outputTokens();
}
/**
* Result of cost estimation.
*
* @param inputTokensCost Cost for input tokens
* @param outputTokensCost Cost for output tokens
* @param currency Currency code (e.g., "USD", "EUR")
*/
record CostResult(
BigDecimal inputTokensCost,
BigDecimal outputTokensCost,
String currency
) {}
}import io.quarkiverse.langchain4j.cost.CostEstimator;
import jakarta.enterprise.context.ApplicationScoped;
import java.math.BigDecimal;
import java.math.RoundingMode;
@ApplicationScoped
public class OpenAICostEstimator implements CostEstimator {
private static final BigDecimal GPT4_INPUT_COST_PER_1K = new BigDecimal("0.03");
private static final BigDecimal GPT4_OUTPUT_COST_PER_1K = new BigDecimal("0.06");
private static final BigDecimal GPT35_INPUT_COST_PER_1K = new BigDecimal("0.0015");
private static final BigDecimal GPT35_OUTPUT_COST_PER_1K = new BigDecimal("0.002");
@Override
public boolean supports(SupportsContext context) {
String modelName = context.model();
return modelName != null &&
(modelName.contains("gpt-4") || modelName.contains("gpt-3.5"));
}
@Override
public CostResult estimate(CostContext context) {
String modelName = context.model();
boolean isGpt4 = modelName.contains("gpt-4");
BigDecimal inputCostPer1K = isGpt4 ? GPT4_INPUT_COST_PER_1K : GPT35_INPUT_COST_PER_1K;
BigDecimal outputCostPer1K = isGpt4 ? GPT4_OUTPUT_COST_PER_1K : GPT35_OUTPUT_COST_PER_1K;
BigDecimal inputTokens = new BigDecimal(context.inputTokens());
BigDecimal outputTokens = new BigDecimal(context.outputTokens());
BigDecimal inputCost = inputTokens
.divide(new BigDecimal("1000"), 6, RoundingMode.HALF_UP)
.multiply(inputCostPer1K);
BigDecimal outputCost = outputTokens
.divide(new BigDecimal("1000"), 6, RoundingMode.HALF_UP)
.multiply(outputCostPer1K);
return new CostResult(inputCost, outputCost, "USD");
}
}import io.quarkiverse.langchain4j.cost.CostEstimator;
import jakarta.enterprise.context.ApplicationScoped;
import java.math.BigDecimal;
import java.math.RoundingMode;
@ApplicationScoped
public class AnthropicCostEstimator implements CostEstimator {
private static final BigDecimal CLAUDE_3_OPUS_INPUT_PER_1M = new BigDecimal("15");
private static final BigDecimal CLAUDE_3_OPUS_OUTPUT_PER_1M = new BigDecimal("75");
private static final BigDecimal CLAUDE_3_SONNET_INPUT_PER_1M = new BigDecimal("3");
private static final BigDecimal CLAUDE_3_SONNET_OUTPUT_PER_1M = new BigDecimal("15");
@Override
public boolean supports(SupportsContext context) {
String modelName = context.model();
return modelName != null && modelName.contains("claude");
}
@Override
public CostResult estimate(CostContext context) {
String modelName = context.model();
boolean isOpus = modelName.contains("opus");
BigDecimal inputCostPer1M = isOpus ? CLAUDE_3_OPUS_INPUT_PER_1M : CLAUDE_3_SONNET_INPUT_PER_1M;
BigDecimal outputCostPer1M = isOpus ? CLAUDE_3_OPUS_OUTPUT_PER_1M : CLAUDE_3_SONNET_OUTPUT_PER_1M;
BigDecimal inputTokens = new BigDecimal(context.inputTokens());
BigDecimal outputTokens = new BigDecimal(context.outputTokens());
BigDecimal inputCost = inputTokens
.divide(new BigDecimal("1000000"), 6, RoundingMode.HALF_UP)
.multiply(inputCostPer1M);
BigDecimal outputCost = outputTokens
.divide(new BigDecimal("1000000"), 6, RoundingMode.HALF_UP)
.multiply(outputCostPer1M);
return new CostResult(inputCost, outputCost, "USD");
}
}Immutable record representing a cost amount.
// Package: io.quarkiverse.langchain4j.cost
/**
* Represents a cost amount with currency.
*/
public record Cost(
BigDecimal number,
String currencyCode
) {
/**
* Create a Cost instance.
*
* @param number The cost amount
* @param currencyCode The currency code (e.g., "USD", "EUR")
* @return Cost instance
*/
public static Cost of(BigDecimal number, String currencyCode);
/**
* Format cost as string: "{number}{currency}".
*
* @return Formatted cost string
*/
@Override
public String toString();
}Service that aggregates all cost estimators and provides estimation.
// Package: io.quarkiverse.langchain4j.cost
/**
* Service for cost estimation.
* Aggregates all CostEstimator beans and selects appropriate estimator.
*/
@ApplicationScoped
public class CostEstimatorService {
/**
* Constructor accepting all available cost estimators.
*
* @param estimators List of all CostEstimator CDI beans
*/
@Inject
public CostEstimatorService(@All List<CostEstimator> estimators);
/**
* Estimate cost from chat model response context.
*
* @param context The chat model response context
* @return Cost estimate, or null if no estimator supports this model
*/
public Cost estimate(ChatModelResponseContext context);
}When cost estimators are available, costs are automatically tracked:
import io.quarkiverse.langchain4j.RegisterAiService;
import io.quarkiverse.langchain4j.cost.Cost;
import io.quarkiverse.langchain4j.cost.CostEstimatorService;
import jakarta.inject.Inject;
@RegisterAiService(modelName = "gpt-4")
public interface Assistant {
String chat(String message);
}
@Inject
Assistant assistant;
@Inject
CostEstimatorService costEstimator;
public void example() {
String response = assistant.chat("Explain quantum computing");
// Cost can be retrieved from observability events or metrics
}import io.quarkiverse.langchain4j.cost.CostEstimator;
import io.quarkiverse.langchain4j.cost.CostEstimatorService;
import io.quarkiverse.langchain4j.cost.Cost;
import jakarta.inject.Inject;
import java.math.BigDecimal;
public class CostAnalyzer {
@Inject
CostEstimatorService service;
public void estimateCost(String modelName, int inputTokens, int outputTokens) {
CostEstimator.CostContext context = new CostEstimator.CostContext() {
@Override
public String model() { return modelName; }
@Override
public Integer inputTokens() { return inputTokens; }
@Override
public Integer outputTokens() { return outputTokens; }
};
// Find appropriate estimator
Cost cost = service.estimate(context);
if (cost != null) {
System.out.println("Estimated cost: " + cost);
}
}
}import io.quarkiverse.langchain4j.observability.AiServiceSelector;
import io.quarkiverse.langchain4j.cost.CostEstimatorService;
import io.quarkiverse.langchain4j.cost.Cost;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject;
import java.math.BigDecimal;
import java.util.concurrent.atomic.AtomicReference;
@ApplicationScoped
public class CostAccumulator {
@Inject
CostEstimatorService costEstimator;
private final AtomicReference<BigDecimal> totalCost = new AtomicReference<>(BigDecimal.ZERO);
public void onCompletion(
@Observes @AiServiceSelector(MyAssistant.class) AiServiceCompletedEvent event
) {
// Extract token counts from event and estimate cost
Cost cost = costEstimator.estimate(event.responseContext());
if (cost != null) {
totalCost.updateAndGet(current ->
current.add(cost.number())
);
}
}
public BigDecimal getTotalCost() {
return totalCost.get();
}
}Cost estimation is useful for: