Skip to content

Commit

Permalink
Adds Ollama support
Browse files Browse the repository at this point in the history
  • Loading branch information
ChristophScn committed Jul 14, 2024
1 parent ed07279 commit c44a850
Show file tree
Hide file tree
Showing 15 changed files with 575 additions and 6 deletions.
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,16 @@ Interweb currently supports the following data providers:
6. **Ipernity**:
- Search: discover photos and images within one of the largest non-commercial clubs.
- Describe: obtain photo information using its url.
7. **OpenAI**:
7. **Ollama**:
- Interact with open-source LLMs for natural language understanding and generation.
8. **OpenAI**:
- Interact with OpenAI's ChatGPT for natural language understanding and generation.
8. **SlideShare**:
9. **SlideShare**:
- Search: find presentations and documents for various topics.
9. **Vimeo**:
- Search: locate videos created by creative content creators.
- Describe: obtain video information using its url.
10. **YouTube**:
10. **Vimeo**:
- Search: locate videos created by creative content creators.
- Describe: obtain video information using its url.
11. **YouTube**:
- Search: for videos in the largest video hosting platform.
- Describe: obtain detailed information about a video using its url.

Expand Down
51 changes: 51 additions & 0 deletions connectors/OllamaConnector/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>de.l3s.interweb</groupId>
<artifactId>interweb-parent</artifactId>
<version>4.0.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

<artifactId>connector-ollama</artifactId>
<version>4.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

<dependencies>
<dependency>
<groupId>de.l3s.interweb</groupId>
<artifactId>interweb-core</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client-reactive-jackson</artifactId>
</dependency>

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>io.smallrye</groupId>
<artifactId>jandex-maven-plugin</artifactId>
<executions>
<execution>
<id>make-index</id>
<goals>
<goal>jandex</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package de.l3s.interweb.connector.ollama;

import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;

import io.quarkus.rest.client.reactive.ClientExceptionMapper;
import io.smallrye.mutiny.Uni;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

import de.l3s.interweb.connector.ollama.entity.CompletionResponse;
import de.l3s.interweb.connector.ollama.entity.TagsResponse;
import de.l3s.interweb.connector.ollama.entity.CompletionBody;
import de.l3s.interweb.core.ConnectorException;

@Path("")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@RegisterRestClient(configKey = "ollama")
public interface OllamaClient {

/**
* Ollama Completion API
* https://github.com/ollama/ollama/blob/main/docs/api.md
*/
@POST
@Path("/api/chat")
Uni<CompletionResponse> chatCompletions(CompletionBody body);

@GET
@Path("/api/tags")
Uni<TagsResponse> tags();

@ClientExceptionMapper
static RuntimeException toException(Response response) {
return new ConnectorException("Remote service responded with HTTP " + response.getStatus(), response.readEntity(String.class));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package de.l3s.interweb.connector.ollama;

import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.HashMap;

import jakarta.enterprise.context.Dependent;

import io.smallrye.mutiny.Uni;
import org.eclipse.microprofile.rest.client.inject.RestClient;

import de.l3s.interweb.connector.ollama.entity.TagsResponse;
import de.l3s.interweb.connector.ollama.entity.CompletionBody;
import de.l3s.interweb.core.ConnectorException;
import de.l3s.interweb.core.completion.CompletionConnector;
import de.l3s.interweb.core.completion.CompletionQuery;
import de.l3s.interweb.core.completion.CompletionResults;
import de.l3s.interweb.core.completion.Message;
import de.l3s.interweb.core.completion.UsagePrice;
import de.l3s.interweb.core.completion.Usage;
import de.l3s.interweb.core.completion.Choice;

import org.jboss.logging.Logger;

@Dependent
public class OllamaConnector implements CompletionConnector {
private static final Logger log = Logger.getLogger(OllamaConnector.class);

private static final Map<String, UsagePrice> models = new HashMap<>();

@Override
public String getName() {
return "Ollama";
}

@Override
public String getBaseUrl() {
return "https://ollama.com/";
}

@Override
public String[] getModels() {
return models.keySet().toArray(new String[0]);
}

@Override
public UsagePrice getPrice(String model) {
return models.get(model);
}

@RestClient
OllamaClient ollama;

@Override
public Uni<CompletionResults> complete(CompletionQuery query) throws ConnectorException {
return ollama.chatCompletions(new CompletionBody(query)).map(response -> {
Usage usage = new Usage(
response.getPromptEvalCount(),
response.getEvalCount()
);

List<Choice> choices = List.of(
new Choice(
0,
response.getDoneReason(),
new Message(
Message.Role.assistant,
response.getMessage().getContent()
)
)
);

CompletionResults results = new CompletionResults();
results.setModel(response.getModel());
results.setUsage(usage);
results.setChoices(choices);
results.setCreated(Instant.now());
return results;
});
}

@Override
public boolean validate() {
TagsResponse tags;
try {
tags = ollama.tags().await().indefinitely();
} catch (Exception e) {
log.error("Failed to validate Ollama connector", e);
return false;
}

List<String> models = tags.getModels().stream().map(model -> model.getName()).toList();
if (models.isEmpty()) {
log.warn("No models found in Ollama connector");
return false;
}

OllamaConnector.models.clear();
for (String model : models) {
OllamaConnector.models.put(model, new UsagePrice(0, 0));
}

return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package de.l3s.interweb.connector.ollama.entity;

import java.util.List;

import com.fasterxml.jackson.annotation.JsonInclude;

import io.quarkus.runtime.annotations.RegisterForReflection;

import de.l3s.interweb.core.completion.CompletionQuery;

@JsonInclude(JsonInclude.Include.NON_NULL)
@RegisterForReflection
public final class CompletionBody {

private String model;

private List<CompletionMessage> messages;

private CompletionOptions options;

private final Boolean stream = false;

public CompletionBody(CompletionQuery query) {
this.model = query.getModel();

this.messages = query.getMessages().stream()
.map(CompletionMessage::new)
.toList();

this.options = new CompletionOptions(query);
}

public String getModel() {
return model;
}

public List<CompletionMessage> getMessages() {
return messages;
}

public CompletionOptions getOptions() {
return options;
}

public Boolean getStream() {
return stream;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package de.l3s.interweb.connector.ollama.entity;

import io.quarkus.runtime.annotations.RegisterForReflection;

import de.l3s.interweb.core.completion.Message;

@RegisterForReflection
public final class CompletionMessage {
private String role;
private String content;

public CompletionMessage() {
}

public CompletionMessage(Message message) {
this.role = message.getRole().name();
this.content = message.getContent();
}

public String getRole() {
return role;
}

public void setRole(String role) {
this.role = role;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package de.l3s.interweb.connector.ollama.entity;

import com.fasterxml.jackson.annotation.JsonProperty;

import de.l3s.interweb.core.completion.CompletionQuery;


public class CompletionOptions {
private Integer seed;
private String stop;
@JsonProperty("num_predict")
private Integer numPredict;
private Double temperature;
@JsonProperty("top_p")
private Double topP;

public CompletionOptions() {
}

public CompletionOptions(CompletionQuery query) {
this.seed = query.getSeed();
if (query.getStop() != null && query.getStop().length > 0) {
this.stop = query.getStop()[0];
}
this.numPredict = query.getMaxTokens();
this.temperature = query.getTemperature();
this.topP = query.getTopP();
}

public Integer getSeed() {
return seed;
}

public void setSeed(Integer seed) {
this.seed = seed;
}

public String getStop() {
return stop;
}

public void setStop(String stop) {
this.stop = stop;
}

public Integer getNumPredict() {
return numPredict;
}

public void setNumPredict(Integer numPredict) {
this.numPredict = numPredict;
}

public Double getTemperature() {
return temperature;
}

public void setTemperature(Double temperature) {
this.temperature = temperature;
}

public Double getTopP() {
return topP;
}

public void setTopP(Double topP) {
this.topP = topP;
}
}
Loading

0 comments on commit c44a850

Please sign in to comment.