fix: handle ollama error responses

fixes: #138

- Added error field to ModelPullResponse
  - Enhanced error handling in doPullModel to check for errors in response body and throw OllamaBaseException with the specific error message
This commit is contained in:
Travis Lyons
2025-08-20 11:51:49 -04:00
parent 1e17e258b6
commit bae903f8ca
16 changed files with 298 additions and 45 deletions

View File

@@ -420,16 +420,23 @@ public class OllamaAPI {
String line;
while ((line = reader.readLine()) != null) {
ModelPullResponse modelPullResponse = Utils.getObjectMapper().readValue(line, ModelPullResponse.class);
if (modelPullResponse != null && modelPullResponse.getStatus() != null) {
if (verbose) {
logger.info(modelName + ": " + modelPullResponse.getStatus());
if (modelPullResponse != null) {
// Check for error in response body first
if (modelPullResponse.getError() != null && !modelPullResponse.getError().trim().isEmpty()) {
throw new OllamaBaseException("Model pull failed: " + modelPullResponse.getError());
}
// Check if status is "success" and set success flag to true.
if ("success".equalsIgnoreCase(modelPullResponse.getStatus())) {
success = true;
if (modelPullResponse.getStatus() != null) {
if (verbose) {
logger.info(modelName + ": " + modelPullResponse.getStatus());
}
// Check if status is "success" and set success flag to true.
if ("success".equalsIgnoreCase(modelPullResponse.getStatus())) {
success = true;
}
}
} else {
logger.error("Received null or invalid status for model pull.");
logger.error("Received null response for model pull.");
}
}
}
@@ -755,7 +762,7 @@ public class OllamaAPI {
* @throws InterruptedException if the operation is interrupted
*/
public OllamaResult generate(String model, String prompt, boolean raw, Options options,
OllamaStreamHandler streamHandler) throws OllamaBaseException, IOException, InterruptedException {
OllamaStreamHandler streamHandler) throws OllamaBaseException, IOException, InterruptedException {
OllamaGenerateRequest ollamaRequestModel = new OllamaGenerateRequest(model, prompt);
ollamaRequestModel.setRaw(raw);
ollamaRequestModel.setOptions(options.getOptionsMap());
@@ -938,7 +945,7 @@ public class OllamaAPI {
* @throws InterruptedException if the operation is interrupted
*/
public OllamaResult generateWithImageFiles(String model, String prompt, List<File> imageFiles, Options options,
OllamaStreamHandler streamHandler) throws OllamaBaseException, IOException, InterruptedException {
OllamaStreamHandler streamHandler) throws OllamaBaseException, IOException, InterruptedException {
List<String> images = new ArrayList<>();
for (File imageFile : imageFiles) {
images.add(encodeFileToBase64(imageFile));
@@ -985,7 +992,7 @@ public class OllamaAPI {
* @throws URISyntaxException if the URI for the request is malformed
*/
public OllamaResult generateWithImageURLs(String model, String prompt, List<String> imageURLs, Options options,
OllamaStreamHandler streamHandler)
OllamaStreamHandler streamHandler)
throws OllamaBaseException, IOException, InterruptedException, URISyntaxException {
List<String> images = new ArrayList<>();
for (String imageURL : imageURLs) {
@@ -1247,7 +1254,7 @@ public class OllamaAPI {
registerAnnotatedTools(provider.getDeclaredConstructor().newInstance());
}
} catch (InstantiationException | NoSuchMethodException | IllegalAccessException
| InvocationTargetException e) {
| InvocationTargetException e) {
throw new RuntimeException(e);
}
}
@@ -1384,7 +1391,7 @@ public class OllamaAPI {
* @throws InterruptedException if the thread is interrupted during the request.
*/
private OllamaResult generateSyncForOllamaRequestModel(OllamaGenerateRequest ollamaRequestModel,
OllamaStreamHandler streamHandler) throws OllamaBaseException, IOException, InterruptedException {
OllamaStreamHandler streamHandler) throws OllamaBaseException, IOException, InterruptedException {
OllamaGenerateEndpointCaller requestCaller = new OllamaGenerateEndpointCaller(host, auth, requestTimeoutSeconds,
verbose);
OllamaResult result;

View File

@@ -26,7 +26,7 @@ public class OllamaGenerateRequestBuilder {
request.setPrompt(prompt);
return this;
}
public OllamaGenerateRequestBuilder withGetJsonResponse(){
this.request.setReturnFormatJson(true);
return this;

View File

@@ -13,8 +13,8 @@ import lombok.Data;
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public abstract class OllamaCommonRequest {
protected String model;
protected String model;
@JsonSerialize(using = BooleanToJsonFormatFlagSerializer.class)
@JsonProperty(value = "format")
protected Boolean returnFormatJson;
@@ -24,7 +24,7 @@ public abstract class OllamaCommonRequest {
@JsonProperty(value = "keep_alive")
protected String keepAlive;
public String toString() {
try {
return Utils.getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(this);

View File

@@ -10,4 +10,5 @@ public class ModelPullResponse {
private String digest;
private long total;
private long completed;
private String error;
}

View File

@@ -120,4 +120,3 @@ public class OllamaAsyncResultStreamer extends Thread {
}
}

View File

@@ -15,4 +15,4 @@ public class OllamaToolCallsFunction
{
private String name;
private Map<String,Object> arguments;
}
}

View File

@@ -13,4 +13,3 @@ public class ToolFunctionCallSpec {
private String name;
private Map<String, Object> arguments;
}

View File

@@ -18,4 +18,4 @@ public class FileToBase64Serializer extends JsonSerializer<Collection<byte[]>> {
}
jsonGenerator.writeEndArray();
}
}
}

View File

@@ -10,10 +10,10 @@ import com.fasterxml.jackson.core.JsonProcessingException;
* Interface to represent a OllamaRequest as HTTP-Request Body via {@link BodyPublishers}.
*/
public interface OllamaRequestBody {
/**
* Transforms the OllamaRequest Object to a JSON Object via Jackson.
*
*
* @return JSON representation of a OllamaRequest
*/
@JsonIgnore