|
|
|
|
@@ -194,16 +194,23 @@ public class OllamaAPI {
|
|
|
|
|
for (Element e : modelSections) {
|
|
|
|
|
LibraryModel model = new LibraryModel();
|
|
|
|
|
Elements names = e.select("div > h2 > span");
|
|
|
|
|
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 popularTags = e.select("div > div > span");
|
|
|
|
|
Elements tagCount = e.select("div:nth-of-type(2) > p > span:nth-of-type(2) > span:first-of-type");
|
|
|
|
|
Elements updatedAt = e.select("div:nth-of-type(2) > p > span:nth-of-type(3) > span:nth-of-type(2)");
|
|
|
|
|
Elements totalTags = e.select("div:nth-of-type(2) > p > span:nth-of-type(2) > span:first-of-type");
|
|
|
|
|
Elements lastUpdatedTime = e.select("div:nth-of-type(2) > p > span:nth-of-type(3) > span:nth-of-type(2)");
|
|
|
|
|
|
|
|
|
|
if (names.first() == null || names.isEmpty()) {
|
|
|
|
|
// if name cannot be extracted, skip.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
Optional.ofNullable(names.first()).map(Element::text).ifPresent(model::setName);
|
|
|
|
|
model.setDescription(Optional.ofNullable(desc.first()).map(Element::text).orElse(""));
|
|
|
|
|
model.setPopularTags(Optional.of(popularTags).map(tags -> tags.stream().map(Element::text).collect(Collectors.toList())).orElse(new ArrayList<>()));
|
|
|
|
|
model.setPullCount(Optional.ofNullable(pullCounts.first()).map(Element::text).orElse(""));
|
|
|
|
|
model.setTotalTags(Optional.ofNullable(totalTags.first()).map(Element::text).map(Integer::parseInt).orElse(0));
|
|
|
|
|
model.setLastUpdated(Optional.ofNullable(lastUpdatedTime.first()).map(Element::text).orElse(""));
|
|
|
|
|
|
|
|
|
|
model.setName(names.first().text());
|
|
|
|
|
model.setPullCount(pullCounts.first().text());
|
|
|
|
|
model.setPopularTags(popularTags.stream().map(Element::text).collect(Collectors.toList()));
|
|
|
|
|
model.setNumTags(Integer.parseInt(tagCount.first().text()));
|
|
|
|
|
model.setUpdatedAt(updatedAt.first().text());
|
|
|
|
|
models.add(model);
|
|
|
|
|
}
|
|
|
|
|
return models;
|
|
|
|
|
@@ -212,6 +219,81 @@ public class OllamaAPI {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Fetches the tags associated with a specific model from Ollama library.
|
|
|
|
|
* This method fetches the available model tags directly from Ollama library model page, including model tag name, size and time when model was last updated
|
|
|
|
|
* into a list of {@link LibraryModelTag} objects.
|
|
|
|
|
*
|
|
|
|
|
* @param libraryModel the {@link LibraryModel} object which contains the name of the library model
|
|
|
|
|
* for which the tags need to be fetched.
|
|
|
|
|
* @return a list of {@link LibraryModelTag} objects containing the extracted tags and their associated metadata.
|
|
|
|
|
* @throws OllamaBaseException if the HTTP response status code indicates an error (i.e., not 200 OK),
|
|
|
|
|
* or if there is any other issue during the request or response processing.
|
|
|
|
|
* @throws IOException if an input/output exception occurs during the HTTP request or response handling.
|
|
|
|
|
* @throws InterruptedException if the thread is interrupted while waiting for the HTTP response.
|
|
|
|
|
* @throws URISyntaxException if the URI format is incorrect or invalid.
|
|
|
|
|
*/
|
|
|
|
|
public LibraryModelDetail getLibraryModelDetails(LibraryModel libraryModel) throws OllamaBaseException, IOException, InterruptedException, URISyntaxException {
|
|
|
|
|
String url = String.format("https://ollama.com/library/%s/tags", libraryModel.getName());
|
|
|
|
|
HttpClient httpClient = HttpClient.newHttpClient();
|
|
|
|
|
HttpRequest httpRequest = getRequestBuilderDefault(new URI(url)).header("Accept", "application/json").header("Content-type", "application/json").GET().build();
|
|
|
|
|
HttpResponse<String> response = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
|
|
|
|
|
int statusCode = response.statusCode();
|
|
|
|
|
String responseString = response.body();
|
|
|
|
|
|
|
|
|
|
List<LibraryModelTag> libraryModelTags = new ArrayList<>();
|
|
|
|
|
if (statusCode == 200) {
|
|
|
|
|
Document doc = Jsoup.parse(responseString);
|
|
|
|
|
Elements tagSections = doc.select("html > body > main > div > section > div > div > div:nth-child(n+2) > div");
|
|
|
|
|
for (Element e : tagSections) {
|
|
|
|
|
Elements tags = e.select("div > a > div");
|
|
|
|
|
Elements tagsMetas = e.select("div > span");
|
|
|
|
|
|
|
|
|
|
LibraryModelTag libraryModelTag = new LibraryModelTag();
|
|
|
|
|
|
|
|
|
|
if (tags.first() == null || tags.isEmpty()) {
|
|
|
|
|
// if tag cannot be extracted, skip.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
libraryModelTag.setName(libraryModel.getName());
|
|
|
|
|
Optional.ofNullable(tags.first()).map(Element::text).ifPresent(libraryModelTag::setTag);
|
|
|
|
|
libraryModelTag.setSize(Optional.ofNullable(tagsMetas.first()).map(element -> element.text().split("•")).filter(parts -> parts.length > 1).map(parts -> parts[1].trim()).orElse(""));
|
|
|
|
|
libraryModelTag.setLastUpdated(Optional.ofNullable(tagsMetas.first()).map(element -> element.text().split("•")).filter(parts -> parts.length > 1).map(parts -> parts[2].trim()).orElse(""));
|
|
|
|
|
libraryModelTags.add(libraryModelTag);
|
|
|
|
|
}
|
|
|
|
|
LibraryModelDetail libraryModelDetail = new LibraryModelDetail();
|
|
|
|
|
libraryModelDetail.setModel(libraryModel);
|
|
|
|
|
libraryModelDetail.setTags(libraryModelTags);
|
|
|
|
|
return libraryModelDetail;
|
|
|
|
|
} else {
|
|
|
|
|
throw new OllamaBaseException(statusCode + " - " + responseString);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Finds a specific model using model name and tag from Ollama library.
|
|
|
|
|
* <p>
|
|
|
|
|
* This method retrieves the model from the Ollama library by its name, then fetches its tags.
|
|
|
|
|
* It searches through the tags of the model to find one that matches the specified tag name.
|
|
|
|
|
* If the model or the tag is not found, it throws a {@link NoSuchElementException}.
|
|
|
|
|
*
|
|
|
|
|
* @param modelName The name of the model to search for in the library.
|
|
|
|
|
* @param tag The tag name to search for within the specified model.
|
|
|
|
|
* @return The {@link LibraryModelTag} associated with the specified model and tag.
|
|
|
|
|
* @throws OllamaBaseException If there is a problem with the Ollama library operations.
|
|
|
|
|
* @throws IOException If an I/O error occurs during the operation.
|
|
|
|
|
* @throws URISyntaxException If there is an error with the URI syntax.
|
|
|
|
|
* @throws InterruptedException If the operation is interrupted.
|
|
|
|
|
* @throws NoSuchElementException If the model or the tag is not found.
|
|
|
|
|
*/
|
|
|
|
|
public LibraryModelTag findModelTagFromLibrary(String modelName, String tag) throws OllamaBaseException, IOException, URISyntaxException, InterruptedException {
|
|
|
|
|
List<LibraryModel> libraryModels = this.listModelsFromLibrary();
|
|
|
|
|
LibraryModel libraryModel = libraryModels.stream().filter(model -> model.getName().equals(modelName)).findFirst().orElseThrow(() -> new NoSuchElementException(String.format("Model by name '%s' not found", modelName)));
|
|
|
|
|
LibraryModelDetail libraryModelDetail = this.getLibraryModelDetails(libraryModel);
|
|
|
|
|
LibraryModelTag libraryModelTag = libraryModelDetail.getTags().stream().filter(tagName -> tagName.getTag().equals(tag)).findFirst().orElseThrow(() -> new NoSuchElementException(String.format("Tag '%s' for model '%s' not found", tag, modelName)));
|
|
|
|
|
return libraryModelTag;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Pull a model on the Ollama server from the list of <a
|
|
|
|
|
* href="https://ollama.ai/library">available models</a>.
|
|
|
|
|
@@ -245,6 +327,23 @@ public class OllamaAPI {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Pulls a model using the specified Ollama library model tag.
|
|
|
|
|
* The model is identified by a name and a tag, which are combined into a single identifier
|
|
|
|
|
* in the format "name:tag" to pull the corresponding model.
|
|
|
|
|
*
|
|
|
|
|
* @param libraryModelTag the {@link LibraryModelTag} object containing the name and tag
|
|
|
|
|
* of the model to be pulled.
|
|
|
|
|
* @throws OllamaBaseException if the response indicates an error status
|
|
|
|
|
* @throws IOException if an I/O error occurs during the HTTP request
|
|
|
|
|
* @throws InterruptedException if the operation is interrupted
|
|
|
|
|
* @throws URISyntaxException if the URI for the request is malformed
|
|
|
|
|
*/
|
|
|
|
|
public void pullModel(LibraryModelTag libraryModelTag) throws OllamaBaseException, IOException, URISyntaxException, InterruptedException {
|
|
|
|
|
String tagToPull = String.format("%s:%s", libraryModelTag.getName(), libraryModelTag.getTag());
|
|
|
|
|
pullModel(tagToPull);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Gets model details from the Ollama server.
|
|
|
|
|
*
|
|
|
|
|
|