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
Comprehensive guide to handling errors in langchain4j-github-models.
import com.azure.core.exception.HttpResponseException;
try {
ChatResponse response = model.chat(request);
} catch (HttpResponseException e) {
int statusCode = e.getResponse().getStatusCode();
String message = e.getMessage();
System.err.println("HTTP " + statusCode + ": " + message);
}| Code | Meaning | Handling |
|---|---|---|
| 400 | Bad Request | Check request format, parameters |
| 401 | Unauthorized | Verify GitHub token is valid |
| 403 | Forbidden | Check token permissions |
| 404 | Not Found | Verify model name, endpoint |
| 429 | Rate Limit | Implement backoff, retry |
| 500 | Server Error | Retry with backoff |
| 503 | Service Unavailable | Retry with backoff |
try {
ChatResponse response = model.chat(request);
} catch (HttpResponseException e) {
if (e.getResponse().getStatusCode() == 401) {
System.err.println("Authentication failed. Check your GitHub token.");
// Token is invalid or expired
} else if (e.getResponse().getStatusCode() == 403) {
System.err.println("Access forbidden. Token may lack required permissions.");
// Token lacks necessary permissions
}
}import java.util.concurrent.TimeUnit;
int maxAttempts = 5;
int attempt = 0;
while (attempt < maxAttempts) {
try {
ChatResponse response = model.chat(request);
// Success
break;
} catch (HttpResponseException e) {
if (e.getResponse().getStatusCode() == 429) {
attempt++;
if (attempt < maxAttempts) {
long backoffMs = (long) Math.pow(2, attempt) * 1000;
System.out.println("Rate limited. Retrying in " + backoffMs + "ms...");
TimeUnit.MILLISECONDS.sleep(backoffMs);
} else {
System.err.println("Rate limit exceeded after " + maxAttempts + " attempts");
throw e;
}
} else {
throw e;
}
}
}try {
ChatResponse response = model.chat(request);
} catch (HttpResponseException e) {
int status = e.getResponse().getStatusCode();
if (status >= 500 && status < 600) {
System.err.println("Server error. The service may be experiencing issues.");
// Retry with exponential backoff
// The model's maxRetries setting handles this automatically
}
}Content filter violations return FinishReason.CONTENT_FILTER instead of throwing exceptions.
ChatResponse response = model.chat(request);
if (response.metadata().finishReason() == FinishReason.CONTENT_FILTER) {
String filterMessage = response.aiMessage().text();
System.out.println("Content was filtered: " + filterMessage);
// Handle filtered content gracefully
}model.chat(request, new StreamingChatResponseHandler() {
@Override
public void onPartialResponse(String token) {
System.out.print(token);
}
@Override
public void onCompleteResponse(ChatResponse response) {
if (response.metadata().finishReason() == FinishReason.CONTENT_FILTER) {
System.out.println("\n[Content was filtered by responsible AI policies]");
}
}
@Override
public void onError(Throwable error) {
System.err.println("Error: " + error.getMessage());
}
});ChatResponse response = model.chat(request);
switch (response.metadata().finishReason()) {
case STOP:
// Normal completion
return response.aiMessage().text();
case CONTENT_FILTER:
// Content filtered
System.out.println("Content filter triggered");
return "Response unavailable due to content policy";
case LENGTH:
// Stopped due to max_tokens
System.out.println("Response truncated");
return response.aiMessage().text() + "...";
case TOOL_EXECUTION:
// Stopped to execute tool
// Process tool execution
return processToolExecution(response);
default:
return response.aiMessage().text();
}Thrown when attempting to use features not supported by the model or API.
import dev.langchain4j.model.UnsupportedFeatureException;
try {
// Trying to use REQUIRED with multiple tools (not supported)
ChatResponse response = model.chat(request);
} catch (UnsupportedFeatureException e) {
System.err.println("Unsupported feature: " + e.getMessage());
// Adjust request to use supported features
}ToolChoice.REQUIRED with multiple tools:
// ❌ This throws UnsupportedFeatureException
ChatRequest request = ChatRequest.builder()
.messages(UserMessage.from("Help me"))
.parameters(ChatRequestParameters.builder()
.toolSpecifications(tool1, tool2, tool3)
.toolChoice(ToolChoice.REQUIRED) // Only supports single tool
.build())
.build();
// ✅ Use with single tool or AUTO
ChatRequest request = ChatRequest.builder()
.messages(UserMessage.from("Help me"))
.parameters(ChatRequestParameters.builder()
.toolSpecifications(tool1)
.toolChoice(ToolChoice.REQUIRED) // OK with single tool
.build())
.build();import java.net.SocketTimeoutException;
import java.util.concurrent.TimeoutException;
try {
ChatResponse response = model.chat(request);
} catch (Exception e) {
Throwable cause = e.getCause();
if (cause instanceof SocketTimeoutException ||
cause instanceof TimeoutException) {
System.err.println("Request timed out");
// Consider:
// - Increasing timeout
// - Using smaller model
// - Reducing max_tokens
// - Simplifying prompt
}
}public ChatResponse chatWithTimeoutHandling(ChatRequest request) {
try {
return model.chat(request);
} catch (Exception e) {
if (isTimeoutError(e)) {
System.err.println("Request timed out. Retrying with smaller max_tokens...");
// Retry with more constrained parameters
ChatRequest retryRequest = ChatRequest.builder()
.messages(request.messages())
.parameters(ChatRequestParameters.builder()
.maxTokens(500) // Reduce from potentially larger value
.build())
.build();
return model.chat(retryRequest);
}
throw e;
}
}
private boolean isTimeoutError(Exception e) {
Throwable cause = e.getCause();
return cause instanceof SocketTimeoutException ||
cause instanceof TimeoutException;
}import java.net.ConnectException;
import java.net.UnknownHostException;
try {
ChatResponse response = model.chat(request);
} catch (Exception e) {
Throwable cause = e.getCause();
if (cause instanceof ConnectException) {
System.err.println("Connection failed. Check network connectivity.");
} else if (cause instanceof UnknownHostException) {
System.err.println("Host not found. Check endpoint configuration.");
}
}try {
ChatResponse response = model.chat(request);
} catch (Exception e) {
String message = e.getMessage();
if (message != null && message.toLowerCase().contains("proxy")) {
System.err.println("Proxy connection failed. Check proxy configuration.");
}
}try {
GitHubModelsChatModel model = GitHubModelsChatModel.builder()
// Missing gitHubToken
.modelName("gpt-4o")
.build();
} catch (IllegalStateException e) {
System.err.println("Configuration error: " + e.getMessage());
// Ensure all required fields are set
}try {
GitHubModelsChatModel model = GitHubModelsChatModel.builder()
.gitHubToken(token)
.modelName("gpt-4o")
.temperature(-1.0) // Invalid: outside 0.0-2.0 range
.build();
} catch (IllegalArgumentException e) {
System.err.println("Invalid configuration: " + e.getMessage());
}List<TextSegment> emptyList = new ArrayList<>();
Response<List<Embedding>> response = model.embedAll(emptyList);
System.out.println("Embeddings: " + response.content().size()); // 0
// Empty input returns empty result, not an errorList<TextSegment> segments = loadSegments();
try {
Response<List<Embedding>> response = model.embedAll(segments);
} catch (HttpResponseException e) {
System.err.println("Embedding failed: " + e.getMessage());
// Retry with smaller batch if possible
int halfSize = segments.size() / 2;
List<TextSegment> firstHalf = segments.subList(0, halfSize);
List<TextSegment> secondHalf = segments.subList(halfSize, segments.size());
Response<List<Embedding>> response1 = model.embedAll(firstHalf);
Response<List<Embedding>> response2 = model.embedAll(secondHalf);
List<Embedding> allEmbeddings = new ArrayList<>();
allEmbeddings.addAll(response1.content());
allEmbeddings.addAll(response2.content());
}model.chat(request, new StreamingChatResponseHandler() {
@Override
public void onPartialResponse(String token) {
System.out.print(token);
}
@Override
public void onCompleteResponse(ChatResponse response) {
System.out.println("\nComplete");
}
@Override
public void onError(Throwable error) {
System.err.println("\nStreaming error: " + error.getMessage());
if (error instanceof HttpResponseException) {
HttpResponseException httpError = (HttpResponseException) error;
System.err.println("Status: " + httpError.getResponse().getStatusCode());
}
// Handle error appropriately
// Note: Error is delivered here, not thrown
}
});AtomicReference<String> partialContent = new AtomicReference<>("");
model.chat(request, new StreamingChatResponseHandler() {
@Override
public void onPartialResponse(String token) {
partialContent.updateAndGet(current -> current + token);
System.out.print(token);
}
@Override
public void onCompleteResponse(ChatResponse response) {
System.out.println("\nComplete");
}
@Override
public void onError(Throwable error) {
String partial = partialContent.get();
if (!partial.isEmpty()) {
System.err.println("\nError occurred, but got partial response:");
System.out.println(partial);
// Use partial response if acceptable
} else {
System.err.println("\nError with no partial response");
// Fallback strategy
}
}
});public class ModelErrorHandler {
public static void handleChatError(Exception e, ChatRequest request) {
if (e instanceof HttpResponseException) {
HttpResponseException httpError = (HttpResponseException) e;
int status = httpError.getResponse().getStatusCode();
switch (status) {
case 400:
System.err.println("Bad request. Check request format.");
break;
case 401:
System.err.println("Unauthorized. Check GitHub token.");
break;
case 403:
System.err.println("Forbidden. Check token permissions.");
break;
case 404:
System.err.println("Not found. Check model name.");
break;
case 429:
System.err.println("Rate limited. Implement backoff.");
break;
case 500:
case 503:
System.err.println("Server error. Retry with backoff.");
break;
default:
System.err.println("HTTP " + status + ": " + httpError.getMessage());
}
} else if (e instanceof UnsupportedFeatureException) {
System.err.println("Unsupported feature: " + e.getMessage());
} else if (isTimeoutError(e)) {
System.err.println("Request timed out.");
} else if (isNetworkError(e)) {
System.err.println("Network error: " + e.getMessage());
} else {
System.err.println("Unexpected error: " + e.getMessage());
e.printStackTrace();
}
}
private static boolean isTimeoutError(Exception e) {
Throwable cause = e.getCause();
return cause instanceof SocketTimeoutException ||
cause instanceof TimeoutException;
}
private static boolean isNetworkError(Exception e) {
Throwable cause = e.getCause();
return cause instanceof ConnectException ||
cause instanceof UnknownHostException;
}
}
// Usage
try {
ChatResponse response = model.chat(request);
} catch (Exception e) {
ModelErrorHandler.handleChatError(e, request);
}// ✅ Good - proper error handling
try {
ChatResponse response = model.chat(request);
return response.aiMessage().text();
} catch (HttpResponseException e) {
System.err.println("API error: " + e.getMessage());
return "Sorry, I encountered an error.";
}
// ❌ Bad - ignoring errors
ChatResponse response = model.chat(request);
return response.aiMessage().text();// ✅ Good - check finish reason
ChatResponse response = model.chat(request);
if (response.metadata().finishReason() == FinishReason.LENGTH) {
System.out.println("Warning: Response was truncated");
}
return response.aiMessage().text();// ✅ Good - structured logging
try {
ChatResponse response = model.chat(request);
} catch (HttpResponseException e) {
logger.error("Chat API error: status={}, message={}",
e.getResponse().getStatusCode(),
e.getMessage());
}// ✅ Good - retry transient errors
int maxRetries = 3;
for (int i = 0; i < maxRetries; i++) {
try {
return model.chat(request);
} catch (HttpResponseException e) {
if (i == maxRetries - 1 || !isRetryable(e)) {
throw e;
}
Thread.sleep(1000 * (i + 1));
}
}Install with Tessl CLI
npx tessl i tessl/maven-dev-langchain4j--langchain4j-github-modelsdocs