This package provides a deprecated integration module that enables Java applications to interact with GitHub Models through the LangChain4j framework. It offers chat models (both synchronous and streaming), embedding models, and support for AI services with tool integration, JSON schema responses, and responsible AI features. The module wraps Azure AI Inference SDK to provide a unified API for accessing various language models hosted on GitHub Models, including chat completion capabilities, embeddings generation, and content filtering management. As of version 1.10.0, this module has been marked for deprecation and future removal, with users recommended to migrate to the langchain4j-openai-official module for enhanced functionality and better integration. The library is designed for reusability as a foundational component in LLM-powered Java applications that need to leverage GitHub-hosted AI models, offering builder patterns for configuration, support for proxy options, custom timeouts, and comprehensive model service versioning capabilities.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Practical guide for implementing custom builder factories using Java Service Provider Interface.
SPI factories allow you to customize how model builders are created without modifying library code. When you call Model.builder(), the library uses Java's ServiceLoader to find your custom factory implementation.
package com.example.config;
import dev.langchain4j.model.github.GitHubModelsChatModel;
import dev.langchain4j.model.github.spi.GitHubModelsChatModelBuilderFactory;
public class CustomChatModelBuilderFactory implements GitHubModelsChatModelBuilderFactory {
@Override
public GitHubModelsChatModel.Builder get() {
return GitHubModelsChatModel.builder()
.gitHubToken(System.getenv("GITHUB_TOKEN"))
.timeout(Duration.ofSeconds(60))
.maxRetries(3);
}
}Create file: src/main/resources/META-INF/services/dev.langchain4j.model.github.spi.GitHubModelsChatModelBuilderFactory
Content:
com.example.config.CustomChatModelBuilderFactory// Factory automatically discovered and used
GitHubModelsChatModel model = GitHubModelsChatModel.builder()
.modelName("gpt-4o") // Additional configuration
.temperature(0.7)
.build();
// The builder already has token, timeout, and retries pre-configuredpackage com.example.spring;
import dev.langchain4j.model.github.GitHubModelsChatModel;
import dev.langchain4j.model.github.spi.GitHubModelsChatModelBuilderFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class SpringChatModelBuilderFactory implements GitHubModelsChatModelBuilderFactory {
@Value("${github.models.token}")
private String token;
@Value("${github.models.endpoint:https://models.inference.ai.azure.com}")
private String endpoint;
@Value("${github.models.timeout:60}")
private int timeoutSeconds;
@Value("${github.models.max-retries:3}")
private int maxRetries;
@Value("${github.models.log-requests:false}")
private boolean logRequests;
@Override
public GitHubModelsChatModel.Builder get() {
return GitHubModelsChatModel.builder()
.gitHubToken(token)
.endpoint(endpoint)
.timeout(Duration.ofSeconds(timeoutSeconds))
.maxRetries(maxRetries)
.logRequestsAndResponses(logRequests);
}
}github.models.token=${GITHUB_TOKEN}
github.models.endpoint=https://models.inference.ai.azure.com
github.models.timeout=60
github.models.max-retries=3
github.models.log-requests=false@Service
public class ChatService {
public String chat(String message) {
// Factory provides pre-configured builder from Spring properties
GitHubModelsChatModel model = GitHubModelsChatModel.builder()
.modelName("gpt-4o")
.build();
ChatResponse response = model.chat(ChatRequest.builder()
.messages(UserMessage.from(message))
.build());
return response.aiMessage().text();
}
}package com.example.env;
import dev.langchain4j.model.github.GitHubModelsChatModel;
import dev.langchain4j.model.github.spi.GitHubModelsChatModelBuilderFactory;
import java.time.Duration;
public class EnvironmentAwareChatModelBuilderFactory implements GitHubModelsChatModelBuilderFactory {
private enum Environment {
DEV, STAGING, PRODUCTION
}
private static final Environment ENV = detectEnvironment();
@Override
public GitHubModelsChatModel.Builder get() {
String token = getTokenForEnvironment();
GitHubModelsChatModel.Builder builder = GitHubModelsChatModel.builder()
.gitHubToken(token);
switch (ENV) {
case DEV:
return builder
.logRequestsAndResponses(true)
.timeout(Duration.ofMinutes(5)); // Longer for debugging
case STAGING:
return builder
.maxRetries(3)
.timeout(Duration.ofSeconds(60))
.logRequestsAndResponses(false);
case PRODUCTION:
return builder
.maxRetries(5)
.timeout(Duration.ofSeconds(30))
.logRequestsAndResponses(false);
default:
throw new IllegalStateException("Unknown environment");
}
}
private static Environment detectEnvironment() {
String env = System.getenv("APP_ENV");
if (env == null) {
return Environment.DEV;
}
try {
return Environment.valueOf(env.toUpperCase());
} catch (IllegalArgumentException e) {
return Environment.DEV;
}
}
private static String getTokenForEnvironment() {
String envToken = System.getenv("GITHUB_TOKEN_" + ENV.name());
if (envToken != null) {
return envToken;
}
return System.getenv("GITHUB_TOKEN");
}
}package com.example.test;
import dev.langchain4j.model.github.GitHubModelsChatModel;
import dev.langchain4j.model.github.spi.GitHubModelsChatModelBuilderFactory;
import com.azure.ai.inference.ChatCompletionsClient;
public class TestChatModelBuilderFactory implements GitHubModelsChatModelBuilderFactory {
private static ChatCompletionsClient mockClient;
public static void setMockClient(ChatCompletionsClient client) {
mockClient = client;
}
public static void clearMockClient() {
mockClient = null;
}
@Override
public GitHubModelsChatModel.Builder get() {
if (mockClient != null) {
return GitHubModelsChatModel.builder()
.chatCompletionsClient(mockClient)
.modelName("test-model");
}
// Fallback to test token
return GitHubModelsChatModel.builder()
.gitHubToken("test-token-12345")
.modelName("test-model")
.timeout(Duration.ofSeconds(5));
}
}import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;
public class ChatServiceTest {
private ChatCompletionsClient mockClient;
@BeforeEach
public void setup() {
mockClient = mock(ChatCompletionsClient.class);
TestChatModelBuilderFactory.setMockClient(mockClient);
}
@AfterEach
public void teardown() {
TestChatModelBuilderFactory.clearMockClient();
}
@Test
public void testChat() {
// Model will use mock client through SPI
GitHubModelsChatModel model = GitHubModelsChatModel.builder().build();
// Configure mock behavior
when(mockClient.complete(any())).thenReturn(/* mock response */);
// Test your code
ChatResponse response = model.chat(request);
// Verify
verify(mockClient).complete(any());
}
}package com.example.factories;
import dev.langchain4j.model.github.*;
import dev.langchain4j.model.github.spi.*;
import java.time.Duration;
// Chat Model Factory
public class CustomChatModelBuilderFactory implements GitHubModelsChatModelBuilderFactory {
@Override
public GitHubModelsChatModel.Builder get() {
return GitHubModelsChatModel.builder()
.gitHubToken(Config.getToken())
.timeout(Duration.ofSeconds(60))
.maxRetries(3);
}
}
// Streaming Chat Model Factory
public class CustomStreamingChatModelBuilderFactory implements GitHubModelsStreamingChatModelBuilderFactory {
@Override
public GitHubModelsStreamingChatModel.Builder get() {
return GitHubModelsStreamingChatModel.builder()
.gitHubToken(Config.getToken())
.timeout(Duration.ofSeconds(90))
.maxRetries(3);
}
}
// Embedding Model Factory
public class CustomEmbeddingModelBuilderFactory implements GitHubModelsEmbeddingModelBuilderFactory {
@Override
public GitHubModelsEmbeddingModel.Builder get() {
return GitHubModelsEmbeddingModel.builder()
.gitHubToken(Config.getToken())
.timeout(Duration.ofSeconds(30))
.maxRetries(5);
}
}Create three SPI files:
META-INF/services/dev.langchain4j.model.github.spi.GitHubModelsChatModelBuilderFactory:
com.example.factories.CustomChatModelBuilderFactoryMETA-INF/services/dev.langchain4j.model.github.spi.GitHubModelsStreamingChatModelBuilderFactory:
com.example.factories.CustomStreamingChatModelBuilderFactoryMETA-INF/services/dev.langchain4j.model.github.spi.GitHubModelsEmbeddingModelBuilderFactory:
com.example.factories.CustomEmbeddingModelBuilderFactorypublic class VaultConfiguredBuilderFactory implements GitHubModelsChatModelBuilderFactory {
private static final SecretVault vault = initializeVault();
@Override
public GitHubModelsChatModel.Builder get() {
String token = vault.getSecret("github-token");
String endpoint = vault.getSecret("github-endpoint");
return GitHubModelsChatModel.builder()
.gitHubToken(token)
.endpoint(endpoint)
.timeout(Duration.ofSeconds(60));
}
private static SecretVault initializeVault() {
// Initialize connection to HashiCorp Vault, AWS Secrets Manager, etc.
return new SecretVault();
}
}public class ObservableBuilderFactory implements GitHubModelsChatModelBuilderFactory {
private static final MetricsRegistry metrics = new MetricsRegistry();
private static final LoggingListener loggingListener = new LoggingListener();
@Override
public GitHubModelsChatModel.Builder get() {
return GitHubModelsChatModel.builder()
.gitHubToken(getToken())
.listeners(Arrays.asList(
new MetricsCollectingListener(metrics),
loggingListener,
new TracingListener()
))
.userAgentSuffix("my-app/1.0");
}
}public class ConditionalBuilderFactory implements GitHubModelsChatModelBuilderFactory {
@Override
public GitHubModelsChatModel.Builder get() {
GitHubModelsChatModel.Builder builder = GitHubModelsChatModel.builder()
.gitHubToken(getToken());
// Enable logging in development
if (isDevelopment()) {
builder.logRequestsAndResponses(true);
}
// Use proxy in corporate network
if (isCorporateNetwork()) {
builder.proxyOptions(getCorporateProxy());
}
// Add custom headers for tracking
if (hasTrackingEnabled()) {
builder.customHeaders(getTrackingHeaders());
}
return builder;
}
private boolean isDevelopment() {
return "dev".equals(System.getenv("APP_ENV"));
}
private boolean isCorporateNetwork() {
// Detect corporate network
return System.getProperty("http.proxyHost") != null;
}
private ProxyOptions getCorporateProxy() {
String host = System.getProperty("http.proxyHost");
int port = Integer.parseInt(System.getProperty("http.proxyPort", "8080"));
return new ProxyOptions(ProxyOptions.Type.HTTP,
new InetSocketAddress(host, port));
}
}// ✅ Good - stateless
public class GoodFactory implements GitHubModelsChatModelBuilderFactory {
@Override
public GitHubModelsChatModel.Builder get() {
return GitHubModelsChatModel.builder()
.gitHubToken(loadToken()); // Load each time
}
}
// ❌ Bad - stateful (not thread-safe)
public class BadFactory implements GitHubModelsChatModelBuilderFactory {
private String token; // Don't store state
@Override
public GitHubModelsChatModel.Builder get() {
return GitHubModelsChatModel.builder()
.gitHubToken(token);
}
}public class ValidatingFactory implements GitHubModelsChatModelBuilderFactory {
@Override
public GitHubModelsChatModel.Builder get() {
String token = System.getenv("GITHUB_TOKEN");
if (token == null || token.trim().isEmpty()) {
throw new IllegalStateException(
"GITHUB_TOKEN environment variable is required");
}
return GitHubModelsChatModel.builder()
.gitHubToken(token);
}
}public class HelpfulFactory implements GitHubModelsChatModelBuilderFactory {
@Override
public GitHubModelsChatModel.Builder get() {
String token = System.getenv("GITHUB_TOKEN");
if (token == null) {
throw new IllegalStateException(
"Missing GITHUB_TOKEN environment variable. " +
"Set it with: export GITHUB_TOKEN=your-token-here"
);
}
return GitHubModelsChatModel.builder()
.gitHubToken(token);
}
}/**
* Custom factory that pre-configures ChatModel builders with:
* <ul>
* <li>GitHub token from GITHUB_TOKEN environment variable</li>
* <li>60-second timeout</li>
* <li>3 retry attempts</li>
* <li>Request/response logging in development mode</li>
* </ul>
*
* <p>Environment detection:
* <ul>
* <li>Development: APP_ENV=dev or unset</li>
* <li>Production: APP_ENV=prod</li>
* </ul>
*
* @throws IllegalStateException if GITHUB_TOKEN is not set
*/
public class DocumentedFactory implements GitHubModelsChatModelBuilderFactory {
@Override
public GitHubModelsChatModel.Builder get() {
// Implementation
}
}// Factory provides sensible defaults
public class OverridableFactory implements GitHubModelsChatModelBuilderFactory {
@Override
public GitHubModelsChatModel.Builder get() {
return GitHubModelsChatModel.builder()
.gitHubToken(Config.getToken())
.timeout(Duration.ofSeconds(60))
.maxRetries(3);
}
}
// Users can still override individual settings
GitHubModelsChatModel model = GitHubModelsChatModel.builder()
.timeout(Duration.ofSeconds(120)) // Override factory default
.modelName("gpt-4o")
.build();META-INF/services/dev.langchain4j.model.github.spi.*If multiple implementations exist, ServiceLoader returns the first found. Control order by:
public class DebuggingFactory implements GitHubModelsChatModelBuilderFactory {
@Override
public GitHubModelsChatModel.Builder get() {
System.err.println("DebuggingFactory.get() called");
System.err.println("Token available: " + (System.getenv("GITHUB_TOKEN") != null));
return GitHubModelsChatModel.builder()
.gitHubToken(System.getenv("GITHUB_TOKEN"));
}
}Install with Tessl CLI
npx tessl i tessl/maven-dev-langchain4j--langchain4j-github-modelsdocs