Skip to content

Commit

Permalink
feat: Available Ollama models support for
Browse files Browse the repository at this point in the history
quarkus.langchain4j.ollama.chat-model.model-id property value

Signed-off-by: azerr <azerr@redhat.com>
  • Loading branch information
angelozerr committed Aug 21, 2024
1 parent 9f4c520 commit fa24e22
Show file tree
Hide file tree
Showing 6 changed files with 285 additions and 2 deletions.
4 changes: 2 additions & 2 deletions quarkus.ls.ext/com.redhat.quarkus.ls/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>11</maven.compiler.source>
<maven.build.timestamp.format>yyyyMMdd-HHmm</maven.build.timestamp.format>
<dev.build.timestamp>${maven.build.timestamp}</dev.build.timestamp>
<lsp4j.version>0.14.0</lsp4j.version>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/*******************************************************************************
* Copyright (c) 2024 Red Hat Inc. and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package com.redhat.quarkus.extensions.ollama;

import java.io.StringReader;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import org.eclipse.lsp4mp.commons.metadata.ItemHint;
import org.eclipse.lsp4mp.commons.metadata.ItemMetadata;
import org.eclipse.lsp4mp.commons.metadata.ValueHint;
import org.eclipse.lsp4mp.commons.utils.StringUtils;
import org.eclipse.lsp4mp.extensions.ExtendedMicroProfileProjectInfo;
import org.eclipse.lsp4mp.extensions.ItemMetadataProvider;
import org.eclipse.lsp4mp.model.Node;
import org.eclipse.lsp4mp.model.Node.NodeType;
import org.eclipse.lsp4mp.model.PropertiesModel;
import org.eclipse.lsp4mp.model.Property;

import com.google.gson.GsonBuilder;

/**
* Properties provider to collect available Ollama model values for the property
* 'quarkus.langchain4j.ollama.chat-model.model-id'.
*
* The Ollama models are collected by using Ollama Search Api by using the
* following base url:
*
* <ul>
* <li>defined with 'quarkus.langchain4j.ollama.base-url' property.
* <li>otherwise with 'http://localhost:11434'</li>
* </ul>
*/
public class OllamaItemMetadataProvider implements ItemMetadataProvider {

private static final Logger LOGGER = Logger.getLogger(OllamaItemMetadataProvider.class.getName());

private static final String QUARKUS_LANGCHAIN4J_OLLAMA_CHAT_MODEL_MODEL_ID_KEY = "quarkus.langchain4j.ollama.chat-model.model-id";

private static final String QUARKUS_LANGCHAIN4J_OLLAMA_BASE_URL_KEY = "quarkus.langchain4j.ollama.base-url";

private static final String DEFAULT_OLLAMA_BASE_URL = "http://localhost:11434";

private ExtendedMicroProfileProjectInfo projectInfo;
private boolean available;

private String currentSearchUrl;

public OllamaItemMetadataProvider(ExtendedMicroProfileProjectInfo projectInfo) {
this.projectInfo = projectInfo;
// the Ollama models are collected only if
// 'quarkus.langchain4j.ollama.chat-model.model-id' property exists.
this.available = this.projectInfo.getProperties().stream()
.anyMatch(p -> QUARKUS_LANGCHAIN4J_OLLAMA_CHAT_MODEL_MODEL_ID_KEY.equals(p.getName()));
}

@Override
public boolean isAvailable() {
return available;
}

@Override
public void update(PropertiesModel document) {
// Called when application.properties file is opened or updated (when user type
// something in the file).
boolean hasModelProperty = getProperty(document, QUARKUS_LANGCHAIN4J_OLLAMA_CHAT_MODEL_MODEL_ID_KEY) != null;
if (hasModelProperty) {
// The application.properties declare the
// 'quarkus.langchain4j.ollama.chat-model.model-id' property,
// we need to collect available Ollama models by using Ollama Search Api
String searchUrl = getOllamaSearchUrl(document);
if (!Objects.equals(searchUrl, currentSearchUrl)) {
// The current search url is different with the new, collect Ollama models.
ItemHint hint = projectInfo.getHint(QUARKUS_LANGCHAIN4J_OLLAMA_CHAT_MODEL_MODEL_ID_KEY);
if (hint == null) {
hint = new ItemHint();
hint.setName(QUARKUS_LANGCHAIN4J_OLLAMA_CHAT_MODEL_MODEL_ID_KEY);
projectInfo.getHints().add(hint);
}
// Update the available models values for the property
// 'quarkus.langchain4j.ollama.chat-model.model-id'
hint.setValues(collectOllamaModels(searchUrl));
currentSearchUrl = searchUrl;
}
}
}

@Override
public List<ItemMetadata> getProperties() {
// This provider doesn't contribute to add/remove properties
return null;
}

private static List<ValueHint> collectOllamaModels(String searchUrl) {
try {
// Call Http search Url (ex : http://localhost:11434/v1/models)
HttpRequest request = HttpRequest.newBuilder() //
.uri(new URI(searchUrl)) //
.GET() //
.build();

HttpResponse<String> response = HttpClient.newBuilder() //
.build() //
.send(request, BodyHandlers.ofString());
String result = response.body();

// ex :
// {"object":"list","data":[{"id":"qwen2:latest","object":"model","created":1724172988,"owned_by":"library"}]}
OllamaModelsResult r = new GsonBuilder() //
.create()//
.fromJson(new StringReader(result), OllamaModelsResult.class);
if (r != null && r.getData() != null) {
return r.getData() //
.stream() //
.map(m -> {
ValueHint model = new ValueHint();
String modelId = m.getId();
if (modelId.endsWith(":latest")) {
modelId = modelId.substring(0, modelId.length() - ":latest".length());
}
model.setValue(modelId);
return model;
}) //
.collect(Collectors.toList());

}

} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Error while collecting Ollama Models with '" + searchUrl + "'.", e);
}
return Collections.emptyList();
}

private static String getOllamaSearchUrl(PropertiesModel document) {
return getSearchBaseUrl(document) + "/v1/models";
}

private static String getSearchBaseUrl(PropertiesModel document) {
Property baseUrlProperty = getProperty(document, QUARKUS_LANGCHAIN4J_OLLAMA_BASE_URL_KEY);
if (baseUrlProperty != null) {
String propertyValue = baseUrlProperty.getPropertyValue();
if (StringUtils.hasText(propertyValue)) {
return propertyValue;
}
}
return DEFAULT_OLLAMA_BASE_URL;
}

private static Property getProperty(PropertiesModel document, String propertyName) {
for (Node node : document.getChildren()) {
if (node.getNodeType() == NodeType.PROPERTY) {
Property property = (Property) node;
if (propertyName.equals(property.getPropertyName())) {
return property;
}
}
}
return null;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*******************************************************************************
* Copyright (c) 2024 Red Hat Inc. and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package com.redhat.quarkus.extensions.ollama;

import org.eclipse.lsp4mp.extensions.ExtendedMicroProfileProjectInfo;
import org.eclipse.lsp4mp.extensions.ItemMetadataProvider;
import org.eclipse.lsp4mp.extensions.ItemMetadataProviderFactory;

/**
* Factory for creating {@link ItemMetadataProvider} instance to collect
* available Ollama models.
*
* @author Angelo ZERR
*
*/
public class OllamaItemMetadataProviderFactory implements ItemMetadataProviderFactory {

@Override
public ItemMetadataProvider create(ExtendedMicroProfileProjectInfo projectInfo) {
return new OllamaItemMetadataProvider(projectInfo);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*******************************************************************************
* Copyright (c) 2024 Red Hat Inc. and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package com.redhat.quarkus.extensions.ollama;

/**
* Ollama model.
*/
public class OllamaModel {

private String id;

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*******************************************************************************
* Copyright (c) 2024 Red Hat Inc. and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package com.redhat.quarkus.extensions.ollama;

import java.util.List;

/**
* Bean class which maps the HTTP response of http://localhost:11434/v1/models
*
* Example:
* <p>
* {"object":"list","data":[{"id":"qwen2:latest","object":"model","created":1724172988,"owned_by":"library"}]}
* </p>
*/
public class OllamaModelsResult {

private List<OllamaModel> data;

public List<OllamaModel> getData() {
return data;
}

public void setData(List<OllamaModel> data) {
this.data = data;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
com.redhat.quarkus.extensions.ollama.OllamaItemMetadataProviderFactory

0 comments on commit fa24e22

Please sign in to comment.