Core runtime module for Quarkus LangChain4j integration with declarative AI services, guardrails, and observability
The authentication framework provides flexible authentication credential management for AI model providers. Implement the ModelAuthProvider interface to supply access tokens, API keys, or other authorization headers for model requests.
import io.quarkiverse.langchain4j.auth.ModelAuthProvider;
import io.quarkiverse.langchain4j.ModelName;
import jakarta.enterprise.context.ApplicationScoped;package io.quarkiverse.langchain4j.auth;
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public interface ModelAuthProvider {
String getAuthorization(Input input);
static Optional<ModelAuthProvider> resolve(String modelName);
interface Input {
String method();
URI uri();
Map<String, List<Object>> headers();
}
}String getAuthorization(Input input);Provides the authorization header value for model provider requests.
Parameters:
input: Request context containing HTTP method, URI, and headersReturns:
"Bearer token123", "Api-Key sk-...")null if no authorization is neededstatic Optional<ModelAuthProvider> resolve(String modelName);Resolves the appropriate ModelAuthProvider for a given model name.
Parameters:
modelName: The name of the model configurationReturns:
Resolution Logic:
public interface Input {
String method();
URI uri();
Map<String, List<Object>> headers();
}Provides context about the HTTP request being made to the model provider.
Methods:
method(): HTTP method (GET, POST, etc.)uri(): Full request URIheaders(): Request headers mapimport io.quarkiverse.langchain4j.auth.ModelAuthProvider;
import jakarta.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class OpenAIAuthProvider implements ModelAuthProvider {
@Override
public String getAuthorization(Input input) {
// Get API key from configuration or secret store
String apiKey = System.getenv("OPENAI_API_KEY");
return "Bearer " + apiKey;
}
}import io.quarkiverse.langchain4j.auth.ModelAuthProvider;
import io.quarkiverse.langchain4j.ModelName;
import jakarta.enterprise.context.ApplicationScoped;
// Provider for a specific named model
@ApplicationScoped
@ModelName("gpt4")
public class GPT4AuthProvider implements ModelAuthProvider {
@Override
public String getAuthorization(Input input) {
return "Bearer " + System.getenv("OPENAI_GPT4_API_KEY");
}
}
// Provider for another named model
@ApplicationScoped
@ModelName("claude")
public class ClaudeAuthProvider implements ModelAuthProvider {
@Override
public String getAuthorization(Input input) {
return "x-api-key " + System.getenv("ANTHROPIC_API_KEY");
}
}
// Global fallback provider
@ApplicationScoped
public class DefaultAuthProvider implements ModelAuthProvider {
@Override
public String getAuthorization(Input input) {
return "Bearer " + System.getenv("DEFAULT_API_KEY");
}
}import jakarta.inject.Inject;
@ApplicationScoped
public class DynamicTokenProvider implements ModelAuthProvider {
@Inject
TokenService tokenService;
@Override
public String getAuthorization(Input input) {
// Fetch fresh token on each request
String token = tokenService.getAccessToken();
return "Bearer " + token;
}
}import jakarta.enterprise.context.ApplicationScoped;
import java.time.Instant;
import java.util.concurrent.locks.ReentrantLock;
@ApplicationScoped
public class OAuth2TokenProvider implements ModelAuthProvider {
private String cachedToken;
private Instant tokenExpiry;
private final ReentrantLock lock = new ReentrantLock();
@Override
public String getAuthorization(Input input) {
String token = getCachedOrFreshToken();
return "Bearer " + token;
}
private String getCachedOrFreshToken() {
if (cachedToken != null && Instant.now().isBefore(tokenExpiry)) {
return cachedToken;
}
lock.lock();
try {
// Double-check after acquiring lock
if (cachedToken != null && Instant.now().isBefore(tokenExpiry)) {
return cachedToken;
}
// Fetch new token
OAuth2Token token = oauth2Client.requestToken();
cachedToken = token.accessToken();
tokenExpiry = Instant.now().plusSeconds(token.expiresIn() - 60); // 60s buffer
return cachedToken;
} finally {
lock.unlock();
}
}
}import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
@ApplicationScoped
public class MultiTenantAuthProvider implements ModelAuthProvider {
@Inject
TenantContext tenantContext;
@Inject
ApiKeyStore apiKeyStore;
@Override
public String getAuthorization(Input input) {
String tenantId = tenantContext.getCurrentTenant();
String apiKey = apiKeyStore.getApiKey(tenantId);
return "Bearer " + apiKey;
}
}@ApplicationScoped
public class RequestBasedAuthProvider implements ModelAuthProvider {
@Override
public String getAuthorization(Input input) {
// Different auth based on endpoint
if (input.uri().getPath().contains("/chat")) {
return "Bearer " + getChatApiKey();
} else if (input.uri().getPath().contains("/embeddings")) {
return "Bearer " + getEmbeddingsApiKey();
}
return "Bearer " + getDefaultApiKey();
}
}import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
@ApplicationScoped
public class SecretManagerAuthProvider implements ModelAuthProvider {
@Inject
SecretManager secretManager;
@Override
public String getAuthorization(Input input) {
// Retrieve API key from secret manager (Vault, AWS Secrets Manager, etc.)
String apiKey = secretManager.getSecret("ai-model-api-key");
return "Bearer " + apiKey;
}
}@ApplicationScoped
public class ConditionalAuthProvider implements ModelAuthProvider {
@Override
public String getAuthorization(Input input) {
// Some requests might not need auth (e.g., health checks)
if (input.uri().getPath().equals("/health")) {
return null; // No authorization needed
}
return "Bearer " + getApiKey();
}
}@ApplicationScoped
public class HeaderInspectingProvider implements ModelAuthProvider {
@Override
public String getAuthorization(Input input) {
// Inspect existing headers
Map<String, List<Object>> headers = input.headers();
// Add different auth based on request characteristics
if (headers.containsKey("X-Special-Request")) {
return "Bearer " + getSpecialApiKey();
}
return "Bearer " + getStandardApiKey();
}
}import io.quarkiverse.langchain4j.auth.ModelAuthProvider;
import java.util.Optional;
@ApplicationScoped
public class ModelService {
public void useModel(String modelName) {
Optional<ModelAuthProvider> provider = ModelAuthProvider.resolve(modelName);
if (provider.isPresent()) {
// Provider found for this model
ModelAuthProvider authProvider = provider.get();
// Use provider...
} else {
// No provider found, handle accordingly
throw new IllegalStateException("No auth provider for model: " + modelName);
}
}
}import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import io.quarkiverse.langchain4j.auth.ModelAuthProvider;
import io.quarkiverse.langchain4j.ModelName;
import org.jboss.logging.Logger;
import java.time.Instant;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
// Global provider with token caching and monitoring
@ApplicationScoped
public class ProductionAuthProvider implements ModelAuthProvider {
private static final Logger LOG = Logger.getLogger(ProductionAuthProvider.class);
@Inject
SecretManager secretManager;
@Inject
MetricsService metrics;
private final Map<String, TokenCache> tokenCache = new ConcurrentHashMap<>();
@Override
public String getAuthorization(Input input) {
String apiKey = getCachedToken();
// Log authorization request (without exposing key)
LOG.debugf("Providing authorization for: %s %s",
input.method(), input.uri().getPath());
// Track auth requests
metrics.incrementCounter("model.auth.requests");
return "Bearer " + apiKey;
}
private String getCachedToken() {
String cacheKey = "default";
TokenCache cache = tokenCache.computeIfAbsent(cacheKey,
k -> new TokenCache());
if (cache.isValid()) {
metrics.incrementCounter("model.auth.cache.hits");
return cache.token;
}
synchronized (cache) {
// Double-check after lock
if (cache.isValid()) {
return cache.token;
}
// Fetch fresh token
metrics.incrementCounter("model.auth.cache.misses");
String token = secretManager.getSecret("ai-model-api-key");
cache.update(token, Instant.now().plusSeconds(3600));
return token;
}
}
private static class TokenCache {
String token;
Instant expiry;
boolean isValid() {
return token != null && Instant.now().isBefore(expiry);
}
void update(String token, Instant expiry) {
this.token = token;
this.expiry = expiry;
}
}
}
// Named provider for premium model with special auth
@ApplicationScoped
@ModelName("premium-gpt4")
public class PremiumModelAuthProvider implements ModelAuthProvider {
private static final Logger LOG = Logger.getLogger(PremiumModelAuthProvider.class);
@Inject
PremiumAuthService premiumAuth;
@Override
public String getAuthorization(Input input) {
LOG.info("Using premium model authentication");
// Premium models use different auth mechanism
String premiumToken = premiumAuth.getPremiumToken();
return "X-Premium-Token " + premiumToken;
}
}While ModelAuthProvider implementations can retrieve credentials from any source, typical configuration might include:
# Environment variables
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
# Or Quarkus config
quarkus.langchain4j.openai.api-key=${OPENAI_API_KEY}Install with Tessl CLI
npx tessl i tessl/maven-io-quarkiverse-langchain4j--quarkus-langchain4j-core