Merge pull request #81 from AgentSchmecker/bugfix/79

Fixes for #78 and #79
This commit is contained in:
Amith Koujalgi 2024-12-05 16:42:30 +05:30 committed by GitHub
commit 4a91918e84
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 90 additions and 16 deletions

View File

@ -193,7 +193,7 @@ public class OllamaAPI {
Elements modelSections = doc.selectXpath("//*[@id='repo']/ul/li/a"); Elements modelSections = doc.selectXpath("//*[@id='repo']/ul/li/a");
for (Element e : modelSections) { for (Element e : modelSections) {
LibraryModel model = new LibraryModel(); LibraryModel model = new LibraryModel();
Elements names = e.select("div > h2 > span"); Elements names = e.select("div > h2 > div > span");
Elements desc = e.select("div > p"); Elements desc = e.select("div > p");
Elements pullCounts = e.select("div:nth-of-type(2) > p > span:first-of-type > span:first-of-type"); Elements pullCounts = e.select("div:nth-of-type(2) > p > span:first-of-type > span:first-of-type");
Elements popularTags = e.select("div > div > span"); Elements popularTags = e.select("div > div > span");

View File

@ -45,7 +45,7 @@ public class OllamaChatRequestBuilder {
try { try {
return Files.readAllBytes(file.toPath()); return Files.readAllBytes(file.toPath());
} catch (IOException e) { } catch (IOException e) {
LOG.warn(String.format("File '%s' could not be accessed, will not add to message!", file.toPath()), e); LOG.warn("File '{}' could not be accessed, will not add to message!", file.toPath(), e);
return new byte[0]; return new byte[0];
} }
}).collect(Collectors.toList()); }).collect(Collectors.toList());
@ -63,9 +63,9 @@ public class OllamaChatRequestBuilder {
try { try {
binaryImages.add(Utils.loadImageBytesFromUrl(imageUrl)); binaryImages.add(Utils.loadImageBytesFromUrl(imageUrl));
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
LOG.warn(String.format("URL '%s' could not be accessed, will not add to message!", imageUrl), e); LOG.warn("URL '{}' could not be accessed, will not add to message!", imageUrl, e);
} catch (IOException e) { } catch (IOException e) {
LOG.warn(String.format("Content of URL '%s' could not be read, will not add to message!", imageUrl), e); LOG.warn("Content of URL '{}' could not be read, will not add to message!", imageUrl, e);
} }
} }
} }

View File

@ -0,0 +1,40 @@
package io.github.ollama4j.models.embeddings;
import io.github.ollama4j.utils.Options;
import java.util.List;
/**
* Builderclass to easily create Requests for Embedding models using ollama.
*/
public class OllamaEmbedRequestBuilder {
private final OllamaEmbedRequestModel request;
private OllamaEmbedRequestBuilder(String model, List<String> input) {
this.request = new OllamaEmbedRequestModel(model,input);
}
public static OllamaEmbedRequestBuilder getInstance(String model, String... input){
return new OllamaEmbedRequestBuilder(model, List.of(input));
}
public OllamaEmbedRequestBuilder withOptions(Options options){
this.request.setOptions(options.getOptionsMap());
return this;
}
public OllamaEmbedRequestBuilder withKeepAlive(String keepAlive){
this.request.setKeepAlive(keepAlive);
return this;
}
public OllamaEmbedRequestBuilder withoutTruncate(){
this.request.setTruncate(false);
return this;
}
public OllamaEmbedRequestModel build() {
return this.request;
}
}

View File

@ -7,6 +7,7 @@ import lombok.Data;
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Data @Data
@Deprecated(since="1.0.90")
public class OllamaEmbeddingResponseModel { public class OllamaEmbeddingResponseModel {
@JsonProperty("embedding") @JsonProperty("embedding")
private List<Double> embedding; private List<Double> embedding;

View File

@ -2,6 +2,7 @@ package io.github.ollama4j.models.embeddings;
import io.github.ollama4j.utils.Options; import io.github.ollama4j.utils.Options;
@Deprecated(since="1.0.90")
public class OllamaEmbeddingsRequestBuilder { public class OllamaEmbeddingsRequestBuilder {
private OllamaEmbeddingsRequestBuilder(String model, String prompt){ private OllamaEmbeddingsRequestBuilder(String model, String prompt){

View File

@ -12,6 +12,7 @@ import lombok.RequiredArgsConstructor;
@Data @Data
@RequiredArgsConstructor @RequiredArgsConstructor
@NoArgsConstructor @NoArgsConstructor
@Deprecated(since="1.0.90")
public class OllamaEmbeddingsRequestModel { public class OllamaEmbeddingsRequestModel {
@NonNull @NonNull
private String model; private String model;

View File

@ -1,7 +1,9 @@
package io.github.ollama4j.models.request; package io.github.ollama4j.models.request;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import io.github.ollama4j.exceptions.OllamaBaseException; import io.github.ollama4j.exceptions.OllamaBaseException;
import io.github.ollama4j.models.chat.OllamaChatMessage;
import io.github.ollama4j.models.response.OllamaResult; import io.github.ollama4j.models.response.OllamaResult;
import io.github.ollama4j.models.chat.OllamaChatResponseModel; import io.github.ollama4j.models.chat.OllamaChatResponseModel;
import io.github.ollama4j.models.chat.OllamaChatStreamObserver; import io.github.ollama4j.models.chat.OllamaChatStreamObserver;
@ -31,13 +33,29 @@ public class OllamaChatEndpointCaller extends OllamaEndpointCaller {
return "/api/chat"; return "/api/chat";
} }
/**
* Parses streamed Response line from ollama chat.
* Using {@link com.fasterxml.jackson.databind.ObjectMapper#readValue(String, TypeReference)} should throw
* {@link IllegalArgumentException} in case of null line or {@link com.fasterxml.jackson.core.JsonParseException}
* in case the JSON Object cannot be parsed to a {@link OllamaChatResponseModel}. Thus, the ResponseModel should
* never be null.
*
* @param line streamed line of ollama stream response
* @param responseBuffer Stringbuffer to add latest response message part to
* @return TRUE, if ollama-Response has 'done' state
*/
@Override @Override
protected boolean parseResponseAndAddToBuffer(String line, StringBuilder responseBuffer) { protected boolean parseResponseAndAddToBuffer(String line, StringBuilder responseBuffer) {
try { try {
OllamaChatResponseModel ollamaResponseModel = Utils.getObjectMapper().readValue(line, OllamaChatResponseModel.class); OllamaChatResponseModel ollamaResponseModel = Utils.getObjectMapper().readValue(line, OllamaChatResponseModel.class);
responseBuffer.append(ollamaResponseModel.getMessage().getContent()); // it seems that under heavy load ollama responds with an empty chat message part in the streamed response
if (streamObserver != null) { // thus, we null check the message and hope that the next streamed response has some message content again
streamObserver.notify(ollamaResponseModel); OllamaChatMessage message = ollamaResponseModel.getMessage();
if(message != null) {
responseBuffer.append(message.getContent());
if (streamObserver != null) {
streamObserver.notify(ollamaResponseModel);
}
} }
return ollamaResponseModel.isDone(); return ollamaResponseModel.isDone();
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {

View File

@ -80,6 +80,18 @@ class TestRealAPIs {
} }
} }
@Test
@Order(2)
void testListModelsFromLibrary() {
testEndpointReachability();
try {
assertNotNull(ollamaAPI.listModelsFromLibrary());
ollamaAPI.listModelsFromLibrary().forEach(System.out::println);
} catch (IOException | OllamaBaseException | InterruptedException | URISyntaxException e) {
fail(e);
}
}
@Test @Test
@Order(2) @Order(2)
void testPullModel() { void testPullModel() {

View File

@ -1,36 +1,37 @@
package io.github.ollama4j.unittests.jackson; package io.github.ollama4j.unittests.jackson;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import io.github.ollama4j.models.embeddings.OllamaEmbedRequestBuilder;
import io.github.ollama4j.models.embeddings.OllamaEmbedRequestModel;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import io.github.ollama4j.models.embeddings.OllamaEmbeddingsRequestModel;
import io.github.ollama4j.models.embeddings.OllamaEmbeddingsRequestBuilder;
import io.github.ollama4j.utils.OptionsBuilder; import io.github.ollama4j.utils.OptionsBuilder;
public class TestEmbeddingsRequestSerialization extends AbstractSerializationTest<OllamaEmbeddingsRequestModel> { public class TestEmbedRequestSerialization extends AbstractSerializationTest<OllamaEmbedRequestModel> {
private OllamaEmbeddingsRequestBuilder builder; private OllamaEmbedRequestBuilder builder;
@BeforeEach @BeforeEach
public void init() { public void init() {
builder = OllamaEmbeddingsRequestBuilder.getInstance("DummyModel","DummyPrompt"); builder = OllamaEmbedRequestBuilder.getInstance("DummyModel","DummyPrompt");
} }
@Test @Test
public void testRequestOnlyMandatoryFields() { public void testRequestOnlyMandatoryFields() {
OllamaEmbeddingsRequestModel req = builder.build(); OllamaEmbedRequestModel req = builder.build();
String jsonRequest = serialize(req); String jsonRequest = serialize(req);
assertEqualsAfterUnmarshalling(deserialize(jsonRequest,OllamaEmbeddingsRequestModel.class), req); assertEqualsAfterUnmarshalling(deserialize(jsonRequest,OllamaEmbedRequestModel.class), req);
} }
@Test @Test
public void testRequestWithOptions() { public void testRequestWithOptions() {
OptionsBuilder b = new OptionsBuilder(); OptionsBuilder b = new OptionsBuilder();
OllamaEmbeddingsRequestModel req = builder OllamaEmbedRequestModel req = builder
.withOptions(b.setMirostat(1).build()).build(); .withOptions(b.setMirostat(1).build()).build();
String jsonRequest = serialize(req); String jsonRequest = serialize(req);
OllamaEmbeddingsRequestModel deserializeRequest = deserialize(jsonRequest,OllamaEmbeddingsRequestModel.class); OllamaEmbedRequestModel deserializeRequest = deserialize(jsonRequest,OllamaEmbedRequestModel.class);
assertEqualsAfterUnmarshalling(deserializeRequest, req); assertEqualsAfterUnmarshalling(deserializeRequest, req);
assertEquals(1, deserializeRequest.getOptions().get("mirostat")); assertEquals(1, deserializeRequest.getOptions().get("mirostat"));
} }