Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds Anthropic Connector for Anthropic LLM support #79

Merged
merged 4 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,29 +40,31 @@ Interweb can be used in a wide range of applications, but was designed with the

Interweb currently supports the following data providers:

1. **Bing**:
1. **Anthropic**:
- Interact with Anthropic's LLMs for natural language understanding and generation.
2. **Bing**:
- Search: the web for images, videos, news, and more.
- Suggest: get related queries to enhance user search experience.
2. **Flickr**:
3. **Flickr**:
- Search: for photos and images.
- Describe: a media resource by url.
3. **Giphy**:
4. **Giphy**:
- Search: for variety of gifs in one of the largest gif libraries.
4. **Google**:
5. **Google**:
- Suggest: access related search queries from one of the world's leading search engines.
5. **Ipernity**:
6. **Ipernity**:
- Search: discover photos and images within one of the largest non-commercial clubs.
- Describe: obtain photo information using its url.
6. **OpenAI**:
7. **OpenAI**:
- Interact with OpenAI's ChatGPT for natural language understanding and generation.
7. **SlideShare**:
8. **SlideShare**:
- Search: find presentations and documents for various topics.
8. **Vimeo**:
9. **Vimeo**:
- Search: locate videos created by creative content creators.
- Describe: obtain video information using its url.
9. **YouTube**:
- Search: for videos in the largest video hosting platform.
- Describe: obtain detailed information about a video using its url.
10. **YouTube**:
- Search: for videos in the largest video hosting platform.
- Describe: obtain detailed information about a video using its url.

## How to Use Interweb

Expand Down
51 changes: 51 additions & 0 deletions connectors/AnthropicConnector/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-anthropic</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,36 @@
package de.l3s.interweb.connector.anthropic;

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.annotation.ClientHeaderParam;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

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

@Path("")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@RegisterRestClient(configKey = "anthropic")
@ClientHeaderParam(name = "x-api-key", value = "${connector.anthropic.apikey}")
@ClientHeaderParam(name = "anthropic-version", value = "2023-06-01")
public interface AnthropicClient {

/**
* Anthropic Completion API
* https://docs.anthropic.com/en/api/messages
*/
@POST
@Path("/v1/messages")
Uni<CompletionResponse> chatCompletions(CompletionBody body);

@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,80 @@
package de.l3s.interweb.connector.anthropic;

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

import jakarta.enterprise.context.Dependent;

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

import de.l3s.interweb.connector.anthropic.entity.AnthropicContent;
import de.l3s.interweb.connector.anthropic.entity.AnthropicUsage;
import de.l3s.interweb.connector.anthropic.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;

@Dependent
public class AnthropicConnector implements CompletionConnector {

private static final Map<String, UsagePrice> models = Map.of(
"claude-3-opus-20240229", new UsagePrice(0.015, 0.075),
"claude-3-sonnet-20240229", new UsagePrice(0.003, 0.015),
"claude-3-haiku-20240307", new UsagePrice(0.00025, 0.00125)
);

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

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

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

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

@RestClient
AnthropicClient anthropic;

@Override
public Uni<CompletionResults> complete(CompletionQuery query) throws ConnectorException {
return anthropic.chatCompletions(new CompletionBody(query)).map(response -> {
AnthropicUsage anthropicUsage = response.getUsage();
Usage usage = new Usage(
anthropicUsage.getInputTokens(),
anthropicUsage.getOutputTokens()
);


AnthropicContent content = response.getContent().get(0);
astappiev marked this conversation as resolved.
Show resolved Hide resolved
Message message = new Message(Message.Role.user, content.getText());
Choice choice = new Choice(0, response.getStopReason(), message);


CompletionResults results = new CompletionResults(
query.getModel(),
usage,
choice,
Instant.now()
);

return results;
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package de.l3s.interweb.connector.anthropic.entity;

import io.quarkus.runtime.annotations.RegisterForReflection;


@RegisterForReflection
public class AnthropicContent {
private String type;
private String text;

public String getType() {
return type;
}

public void setType(String type) {
this.type = type;
}

public String getText() {
return text;
}

public void setText(String text) {
this.text = text;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package de.l3s.interweb.connector.anthropic.entity;

import io.quarkus.runtime.annotations.RegisterForReflection;

import com.fasterxml.jackson.annotation.JsonProperty;

@RegisterForReflection
public class AnthropicUsage {

@JsonProperty("input_tokens")
private int inputTokens;
@JsonProperty("output_tokens")
private int outputTokens;

public int getInputTokens() {
return inputTokens;
}

public void setInputTokens(int inputTokens) {
this.inputTokens = inputTokens;
}

public int getOutputTokens() {
return outputTokens;
}

public void setOutputTokens(int outputTokens) {
this.outputTokens = outputTokens;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package de.l3s.interweb.connector.anthropic.entity;

import java.util.List;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

import io.quarkus.runtime.annotations.RegisterForReflection;

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

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

private List<CompletionMessage> messages;

private String model;

private String system;

private Double temperature;

@JsonProperty("top_p")
private Double topP;

@JsonProperty("max_tokens")
private Integer maxTokens;

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

this.messages = query.getMessages().stream()
.filter(m -> m.getRole() != Role.system)
.map(CompletionMessage::new)
.toList();
this.system = query.getMessages().stream()
.filter(m -> m.getRole() == Role.system)
.findFirst()
.map(Message::getContent)
.orElse(null);

this.temperature = query.getTemperature();
this.topP = query.getTopP();
this.maxTokens = query.getMaxTokens();

if (this.maxTokens == null) {
this.maxTokens = 800;
}
}

public String getModel() {
return model;
}

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

public String getSystem() {
return system;
}

public Double getTemperature() {
return temperature;
}

public Double getTopP() {
return topP;
}

public Integer getMaxTokens() {
return maxTokens;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package de.l3s.interweb.connector.anthropic.entity;

import com.fasterxml.jackson.annotation.JsonIgnore;

import io.quarkus.runtime.annotations.RegisterForReflection;

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

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

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

public String getRole() {
return role;
}

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

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}
}
Loading