Skip to content

Commit

Permalink
[Beta] Add support for threads endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
StefanBratanov committed Jan 7, 2024
1 parent 147097e commit 6cef5a7
Show file tree
Hide file tree
Showing 15 changed files with 346 additions and 18 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ Images images = imagesClient.createImage(createImageRequest);
| [Models](https://platform.openai.com/docs/api-reference/models) | ✔️ |
| [Moderations](https://platform.openai.com/docs/api-reference/moderations) | ✔️ |

Beta APIs are currently not supported
#### Beta APIs

| API | Status |
|-------------------------------------------------------------------------|:------:|
| [Assistants](https://platform.openai.com/docs/api-reference/assistants) | |
| [Threads](https://platform.openai.com/docs/api-reference/threads) | ️ ✔️ |
| [Messages](https://platform.openai.com/docs/api-reference/messages) | |
| [Runs](https://platform.openai.com/docs/api-reference/runs) | |


10 changes: 10 additions & 0 deletions src/main/java/io/github/stefanbratanov/chatjpt/ChatJPT.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public final class ChatJPT {
private final ImagesClient imagesClient;
private final ModelsClient modelsClient;
private final ModerationsClient moderationsClient;
private final ThreadsClient threadsClient;

private ChatJPT(
URI baseUrl, String apiKey, Optional<String> organization, HttpClient httpClient) {
Expand All @@ -47,6 +48,7 @@ private ChatJPT(
modelsClient = new ModelsClient(baseUrl, apiKey, organization, httpClient, OBJECT_MAPPER);
moderationsClient =
new ModerationsClient(baseUrl, apiKey, organization, httpClient, OBJECT_MAPPER);
threadsClient = new ThreadsClient(baseUrl, apiKey, organization, httpClient, OBJECT_MAPPER);
}

/**
Expand Down Expand Up @@ -113,6 +115,14 @@ public ModerationsClient moderationsClient() {
return moderationsClient;
}

/**
* @return a client based on <a
* href="https://platform.openai.com/docs/api-reference/threads">Threads</a>
*/
public ThreadsClient threadsClient() {
return threadsClient;
}

/**
* @param apiKey the API key used for authentication
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.github.stefanbratanov.chatjpt;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import java.util.List;
Expand All @@ -15,15 +14,12 @@
name = Constants.ASSISTANT_MESSAGE_ROLE),
@JsonSubTypes.Type(value = ChatMessage.ToolMessage.class, name = Constants.TOOL_MESSAGE_ROLE)
})
public sealed interface ChatMessage
public sealed interface ChatMessage extends Message
permits ChatMessage.SystemMessage,
ChatMessage.UserMessage,
ChatMessage.AssistantMessage,
ChatMessage.ToolMessage {

@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
String role();

String content();

record SystemMessage(String content, Optional<String> name) implements ChatMessage {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package io.github.stefanbratanov.chatjpt;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public record CreateThreadRequest(
Optional<List<ThreadMessage>> messages, Optional<Map<String, String>> metadata) {

public static Builder newBuilder() {
return new Builder();
}

public static class Builder {

private final List<ThreadMessage> messages = new LinkedList<>();

private Optional<Map<String, String>> metadata = Optional.empty();

/**
* @param message message to append to the list of messages to start the thread with.
*/
public Builder message(ThreadMessage message) {
messages.add(message);
return this;
}

/**
* @param messages messages to append to the list of messages to start the thread with.
*/
public Builder messages(List<ThreadMessage> messages) {
this.messages.addAll(messages);
return this;
}

/**
* @param metadata Set of 16 key-value pairs that can be attached to an object. This can be
* useful for storing additional information about the object in a structured format. Keys
* can be a maximum of 64 characters long and values can be a maxium of 512 characters long.
*/
public Builder metadata(Map<String, String> metadata) {
this.metadata = Optional.of(metadata);
return this;
}

public CreateThreadRequest build() {
return new CreateThreadRequest(
messages.isEmpty() ? Optional.empty() : Optional.of(messages), metadata);
}
}
}
4 changes: 3 additions & 1 deletion src/main/java/io/github/stefanbratanov/chatjpt/Endpoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ enum Endpoint {
MODERATIONS("moderations"),
EMBEDDINCS("embeddings"),
FILES("files"),
FINE_TUNING("fine_tuning/jobs");
FINE_TUNING("fine_tuning/jobs"),
// Beta
THREADS("threads");

private final String path;

Expand Down
8 changes: 8 additions & 0 deletions src/main/java/io/github/stefanbratanov/chatjpt/Message.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.github.stefanbratanov.chatjpt;

import com.fasterxml.jackson.annotation.JsonProperty;

public sealed interface Message permits ChatMessage, ThreadMessage {
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
String role();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.github.stefanbratanov.chatjpt;

import java.util.Map;
import java.util.Optional;

public record ModifyThreadRequest(Optional<Map<String, String>> metadata) {

public static Builder newBuilder() {
return new Builder();
}

public static class Builder {

private Optional<Map<String, String>> metadata = Optional.empty();

/**
* @param metadata Set of 16 key-value pairs that can be attached to an object. This can be
* useful for storing additional information about the object in a structured format. Keys
* can be a maximum of 64 characters long and values can be a maxium of 512 characters long.
*/
public Builder metadata(Map<String, String> metadata) {
this.metadata = Optional.of(metadata);
return this;
}

public ModifyThreadRequest build() {
return new ModifyThreadRequest(metadata);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package io.github.stefanbratanov.chatjpt;

import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.util.Optional;

/**
* Subclasses should be based on the <a
* href="https://platform.openai.com/docs/assistants/overview">Assistants API</a>
*/
class OpenAIAssistantsClient extends OpenAIClient {

OpenAIAssistantsClient(
String apiKey,
Optional<String> organization,
HttpClient httpClient,
ObjectMapper objectMapper) {
super(apiKey, organization, httpClient, objectMapper);
}

@Override
HttpRequest.Builder newHttpRequestBuilder(String... headers) {
return super.newHttpRequestBuilder(headers).header("OpenAI-Beta", "assistants=v1");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ <T> HttpResponse<T> sendHttpRequest(
} catch (IOException ex) {
throw new UncheckedIOException(ex);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
java.lang.Thread.currentThread().interrupt();
throw new RuntimeException("Operation was interrupted", ex);
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/io/github/stefanbratanov/chatjpt/Thread.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.github.stefanbratanov.chatjpt;

import java.util.Map;

/**
* A Thread represents a conversation. It is recommended creating one Thread per user as soon as the
* user initiates the conversation.
*/
public record Thread(String id, long createdAt, Map<String, String> metadata) {}
60 changes: 60 additions & 0 deletions src/main/java/io/github/stefanbratanov/chatjpt/ThreadMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package io.github.stefanbratanov.chatjpt;

import java.util.List;
import java.util.Map;
import java.util.Optional;

public record ThreadMessage(
String content, Optional<List<String>> fileIds, Optional<Map<String, String>> metadata)
implements Message {
@Override
public String role() {
return Constants.USER_MESSAGE_ROLE;
}

public static Builder newBuilder() {
return new Builder();
}

public static class Builder {

private String content;
private Optional<List<String>> fileIds = Optional.empty();
private Optional<Map<String, String>> metadata = Optional.empty();

/**
* @param content The content of the message.
*/
public Builder content(String content) {
this.content = content;
return this;
}

/**
* @param fileIds A list of File IDs that the message should use. There can be a maximum of 10
* files attached to a message. Useful for tools like retrieval and code_interpreter that
* can access and use files.
*/
public Builder fileIds(List<String> fileIds) {
this.fileIds = Optional.of(fileIds);
return this;
}

/**
* @param metadata Set of 16 key-value pairs that can be attached to an object. This can be
* useful for storing additional information about the object in a structured format. Keys
* can be a maximum of 64 characters long and values can be a maxium of 512 characters long.
*/
public Builder metadata(Map<String, String> metadata) {
this.metadata = Optional.of(metadata);
return this;
}

public ThreadMessage build() {
if (content == null) {
throw new IllegalStateException("content must be set");
}
return new ThreadMessage(content, fileIds, metadata);
}
}
}
84 changes: 84 additions & 0 deletions src/main/java/io/github/stefanbratanov/chatjpt/ThreadsClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package io.github.stefanbratanov.chatjpt;

import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Optional;

/** Based on <a href="https://platform.openai.com/docs/api-reference/threads">Threads</a> */
public class ThreadsClient extends OpenAIAssistantsClient {

private final URI baseUrl;

ThreadsClient(
URI baseUrl,
String apiKey,
Optional<String> organization,
HttpClient httpClient,
ObjectMapper objectMapper) {
super(apiKey, organization, httpClient, objectMapper);
this.baseUrl = baseUrl;
}

/**
* Create a thread.
*
* @throws OpenAIException in case of API errors
*/
public Thread createThread(CreateThreadRequest request) {
HttpRequest httpRequest =
newHttpRequestBuilder()
.uri(baseUrl.resolve(Endpoint.THREADS.getPath()))
.POST(createBodyPublisher(request))
.build();
HttpResponse<byte[]> httpResponse = sendHttpRequest(httpRequest);
return deserializeResponse(httpResponse.body(), Thread.class);
}

/**
* Retrieves a thread.
*
* @throws OpenAIException in case of API errors
*/
public Thread retrieveThread(String threadId) {
HttpRequest httpRequest =
newHttpRequestBuilder()
.uri(baseUrl.resolve(Endpoint.THREADS.getPath() + "/" + threadId))
.GET()
.build();
HttpResponse<byte[]> httpResponse = sendHttpRequest(httpRequest);
return deserializeResponse(httpResponse.body(), Thread.class);
}

/**
* Modifies a thread.
*
* @throws OpenAIException in case of API errors
*/
public Thread modifyThread(String threadId, ModifyThreadRequest request) {
HttpRequest httpRequest =
newHttpRequestBuilder()
.uri(baseUrl.resolve(Endpoint.THREADS.getPath() + "/" + threadId))
.POST(createBodyPublisher(request))
.build();
HttpResponse<byte[]> httpResponse = sendHttpRequest(httpRequest);
return deserializeResponse(httpResponse.body(), Thread.class);
}

/**
* Delete a thread.
*
* @throws OpenAIException in case of API errors
*/
public DeletionStatus deleteThread(String threadId) {
HttpRequest httpRequest =
newHttpRequestBuilder()
.uri(baseUrl.resolve(Endpoint.THREADS.getPath() + "/" + threadId))
.DELETE()
.build();
HttpResponse<byte[]> httpResponse = sendHttpRequest(httpRequest);
return deserializeResponse(httpResponse.body(), DeletionStatus.class);
}
}
Loading

0 comments on commit 6cef5a7

Please sign in to comment.