Skip to content

Commit 34ce645

Browse files
committed
[OpenAi] Support complete
1 parent 75517e3 commit 34ce645

14 files changed

Lines changed: 423 additions & 10 deletions

File tree

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,34 @@
11
package org.devlive.sdk.openai;
22

33
import io.reactivex.Single;
4+
import org.devlive.sdk.openai.entity.CompleteEntity;
45
import org.devlive.sdk.openai.entity.ModelEntity;
6+
import org.devlive.sdk.openai.response.CompleteResponse;
57
import org.devlive.sdk.openai.response.ModelResponse;
8+
import retrofit2.http.Body;
69
import retrofit2.http.GET;
10+
import retrofit2.http.POST;
711
import retrofit2.http.Path;
812

913
public interface DefaultApi
1014
{
1115
/**
1216
* Lists the currently available models
1317
*/
14-
@GET("v1/models")
18+
@GET(value = "v1/models")
1519
Single<ModelResponse> fetchModels();
1620

1721
/**
1822
* Retrieves a model instance, providing basic information about the model such as the owner and permissioning.
1923
*
2024
* @param model The ID of the model to use for this request
2125
*/
22-
@GET("v1/models/{model}")
26+
@GET(value = "v1/models/{model}")
2327
Single<ModelEntity> fetchModel(@Path("model") String model);
28+
29+
/**
30+
* Creates a completion for the provided prompt and parameters.
31+
*/
32+
@POST(value = "v1/completions")
33+
Single<CompleteResponse> fetchCompletions(@Body CompleteEntity configure);
2434
}

src/main/java/org/devlive/sdk/openai/DefaultClient.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package org.devlive.sdk.openai;
22

3+
import org.devlive.sdk.openai.entity.CompleteEntity;
34
import org.devlive.sdk.openai.entity.ModelEntity;
5+
import org.devlive.sdk.openai.response.CompleteResponse;
46
import org.devlive.sdk.openai.response.ModelResponse;
57

68
public abstract class DefaultClient
@@ -18,4 +20,10 @@ ModelEntity getModel(String model)
1820
return this.api.fetchModel(model)
1921
.blockingGet();
2022
}
23+
24+
CompleteResponse createComplete(CompleteEntity configure)
25+
{
26+
return this.api.fetchCompletions(configure)
27+
.blockingGet();
28+
}
2129
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package org.devlive.sdk.openai.entity;
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4+
import com.fasterxml.jackson.annotation.JsonProperty;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Builder;
7+
import lombok.Data;
8+
import lombok.NoArgsConstructor;
9+
import lombok.ToString;
10+
11+
@Data
12+
@Builder
13+
@ToString
14+
@NoArgsConstructor
15+
@AllArgsConstructor
16+
@JsonIgnoreProperties(ignoreUnknown = true)
17+
public class ChoiceEntity
18+
{
19+
@JsonProperty(value = "text")
20+
private String content;
21+
22+
@JsonProperty(value = "index")
23+
private long index;
24+
25+
@JsonProperty(value = "logprobs")
26+
private String logProb;
27+
28+
@JsonProperty(value = "finish_reason")
29+
private String finishReason;
30+
}

src/main/java/org/devlive/sdk/openai/entity/CompleteEntity.java

Lines changed: 114 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66
import lombok.Builder;
77
import lombok.Data;
88
import lombok.ToString;
9+
import org.apache.commons.lang3.ObjectUtils;
10+
import org.apache.commons.lang3.StringUtils;
11+
import org.devlive.sdk.openai.exception.ParamException;
912
import org.devlive.sdk.openai.model.CompleteModel;
13+
import org.devlive.sdk.openai.utils.EnumsUtils;
1014

1115
import java.util.List;
1216

@@ -17,9 +21,8 @@
1721
@JsonIgnoreProperties(ignoreUnknown = true)
1822
public class CompleteEntity
1923
{
20-
@JsonProperty(value = "model", defaultValue = "TEXT_DAVINCI_003")
21-
@Builder.Default
22-
private CompleteModel model = CompleteModel.TEXT_DAVINCI_003;
24+
@JsonProperty(value = "model")
25+
private String model;
2326

2427
@JsonProperty(value = "prompt", required = true)
2528
private String prompt;
@@ -34,7 +37,7 @@ public class CompleteEntity
3437
private Double topP;
3538

3639
@JsonProperty(value = "best_of")
37-
private Double bestOf;
40+
private Integer bestOf;
3841

3942
@JsonProperty(value = "frequency_penalty")
4043
private Double frequencyPenalty;
@@ -44,4 +47,111 @@ public class CompleteEntity
4447

4548
@JsonProperty(value = "stop")
4649
private List<String> stop;
50+
51+
private CompleteEntity(CompleteEntityBuilder builder)
52+
{
53+
if (ObjectUtils.isEmpty(builder.model)) {
54+
builder.model(CompleteModel.TEXT_DAVINCI_003.getName());
55+
}
56+
this.model = builder.model;
57+
58+
if (ObjectUtils.isEmpty(builder.prompt)) {
59+
builder.prompt(null);
60+
}
61+
this.prompt = builder.prompt;
62+
63+
if (ObjectUtils.isEmpty(builder.temperature)) {
64+
builder.temperature(1D);
65+
}
66+
this.temperature = builder.temperature;
67+
68+
if (ObjectUtils.isEmpty(builder.maxTokens)) {
69+
builder.maxTokens(16);
70+
}
71+
this.maxTokens = builder.maxTokens;
72+
73+
if (ObjectUtils.isEmpty(builder.topP)) {
74+
builder.topP(1D);
75+
}
76+
this.topP = builder.topP;
77+
78+
if (ObjectUtils.isEmpty(builder.bestOf)) {
79+
builder.bestOf(1);
80+
}
81+
this.bestOf = builder.bestOf;
82+
83+
if (ObjectUtils.isEmpty(builder.frequencyPenalty)) {
84+
builder.frequencyPenalty(0D);
85+
}
86+
this.frequencyPenalty = builder.frequencyPenalty;
87+
88+
if (ObjectUtils.isEmpty(builder.presencePenalty)) {
89+
builder.presencePenalty(0D);
90+
}
91+
this.presencePenalty = builder.presencePenalty;
92+
this.stop = builder.stop;
93+
}
94+
95+
public static class CompleteEntityBuilder
96+
{
97+
public CompleteEntityBuilder model(String model)
98+
{
99+
if (StringUtils.isEmpty(model)) {
100+
model = CompleteModel.TEXT_DAVINCI_003.getName();
101+
}
102+
this.model = model;
103+
return this;
104+
}
105+
106+
public CompleteEntityBuilder prompt(String prompt)
107+
{
108+
if (StringUtils.isEmpty(prompt)) {
109+
throw new ParamException("Invalid prompt must not be empty");
110+
}
111+
this.prompt = prompt;
112+
return this;
113+
}
114+
115+
public CompleteEntityBuilder temperature(Double temperature)
116+
{
117+
if (temperature < 0 || temperature > 2) {
118+
throw new ParamException(String.format("Invalid temperature: %s , between 0 and 2", temperature));
119+
}
120+
this.temperature = temperature;
121+
return this;
122+
}
123+
124+
public CompleteEntityBuilder maxTokens(Integer maxTokens)
125+
{
126+
CompleteModel completeModel = EnumsUtils.getCompleteModel(this.model);
127+
if (ObjectUtils.isNotEmpty(this.model) && maxTokens > completeModel.getMaxTokens()) {
128+
throw new ParamException(String.format("Invalid maxTokens: %s, Cannot be larger than the model default configuration %s", maxTokens, completeModel.getMaxTokens()));
129+
}
130+
this.maxTokens = maxTokens;
131+
return this;
132+
}
133+
134+
public CompleteEntityBuilder frequencyPenalty(Double frequencyPenalty)
135+
{
136+
if (frequencyPenalty < -2.0 || frequencyPenalty > 2.0) {
137+
throw new ParamException(String.format("Invalid frequencyPenalty: %s , between -2.0 and 2.0", frequencyPenalty));
138+
}
139+
this.frequencyPenalty = frequencyPenalty;
140+
return this;
141+
}
142+
143+
public CompleteEntityBuilder presencePenalty(Double presencePenalty)
144+
{
145+
if (presencePenalty < -2.0 || presencePenalty > 2.0) {
146+
throw new ParamException(String.format("Invalid presencePenalty: %s , between -2.0 and 2.0", presencePenalty));
147+
}
148+
this.presencePenalty = presencePenalty;
149+
return this;
150+
}
151+
152+
public CompleteEntity build()
153+
{
154+
return new CompleteEntity(this);
155+
}
156+
}
47157
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package org.devlive.sdk.openai.entity;
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4+
import com.fasterxml.jackson.annotation.JsonProperty;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Builder;
7+
import lombok.Data;
8+
import lombok.NoArgsConstructor;
9+
import lombok.ToString;
10+
11+
@Data
12+
@Builder
13+
@ToString
14+
@NoArgsConstructor
15+
@AllArgsConstructor
16+
@JsonIgnoreProperties(ignoreUnknown = true)
17+
public class UsageEntity
18+
{
19+
@JsonProperty(value = "prompt_tokens")
20+
public long promptTokens;
21+
22+
@JsonProperty(value = "completion_tokens")
23+
public long completionTokens;
24+
25+
@JsonProperty(value = "total_tokens")
26+
public long totalTokens;
27+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.devlive.sdk.openai.exception;
2+
3+
public class ParamException
4+
extends DefaultException
5+
{
6+
public ParamException(String message)
7+
{
8+
super(message);
9+
}
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.devlive.sdk.openai.exception;
2+
3+
public class RequestException
4+
extends DefaultException
5+
{
6+
public RequestException(String message)
7+
{
8+
super(message);
9+
}
10+
}

src/main/java/org/devlive/sdk/openai/interceptor/DefaultInterceptor.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import okhttp3.ResponseBody;
1111
import org.apache.commons.lang3.ObjectUtils;
1212
import org.devlive.sdk.openai.exception.AuthorizedException;
13+
import org.devlive.sdk.openai.exception.RequestException;
1314
import org.devlive.sdk.openai.response.DefaultResponse;
1415
import org.devlive.sdk.openai.utils.JsonUtils;
1516

@@ -40,6 +41,8 @@ public Request headers(Request original)
4041
@Override
4142
public Response intercept(Chain chain) throws IOException
4243
{
44+
JsonUtils<DefaultResponse> jsonInstance = JsonUtils.getInstance();
45+
4346
Request original = chain.request();
4447
Request request = this.headers(original);
4548
Response response = chain.proceed(request);
@@ -54,15 +57,25 @@ public Response intercept(Chain chain) throws IOException
5457

5558
// Unauthorized
5659
if (response.code() == 401) {
57-
log.error("Failure to intercept request because not authorized");
58-
JsonUtils<DefaultResponse> jsonUtils = JsonUtils.getInstance();
5960
ResponseBody body = response.body();
6061
if (ObjectUtils.isEmpty(body)) {
6162
throw new NullPointerException("Failed to intercept request because no body");
6263
}
63-
DefaultResponse defaultResponse = jsonUtils.getObject(body.string(), DefaultResponse.class);
64+
DefaultResponse defaultResponse = jsonInstance.getObject(body.string(), DefaultResponse.class);
65+
log.error("Failure to intercept request because not authorized {}", defaultResponse.getError().getMessage());
6466
throw new AuthorizedException(defaultResponse.getError().getMessage());
6567
}
68+
69+
// Has error
70+
if (response.code() == 404 || response.code() == 400) {
71+
ResponseBody body = response.body();
72+
if (ObjectUtils.isEmpty(body)) {
73+
throw new NullPointerException("Failed to intercept request because no body");
74+
}
75+
DefaultResponse defaultResponse = jsonInstance.getObject(body.string(), DefaultResponse.class);
76+
log.error("Failure to intercept request because {}", defaultResponse.getError().getMessage());
77+
throw new RequestException(defaultResponse.getError().getMessage());
78+
}
6679
return response;
6780
}
6881
}

src/main/java/org/devlive/sdk/openai/model/CompleteModel.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ public enum CompleteModel
55
TEXT_DAVINCI_003("text-davinci-003",
66
"Most capable model in the GPT-3 series. Can perform any task the other GPT-3 models can, often with higher quality, longer output and better instruction-following. It can process up to 4,000 tokens per request.",
77
"Complex intent, cause and effect, creative generation, search, summarization for audience",
8-
4096);
8+
4096),
9+
TEXT_CURIE_001("text-curie-001",
10+
"Very capable, but faster and lower cost than text-davinci-003.",
11+
"Language translation, complex classification, sentiment, summarization",
12+
2049);
913

1014
private final String name;
1115
private final String description;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package org.devlive.sdk.openai.response;
2+
3+
import com.fasterxml.jackson.annotation.JsonFormat;
4+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
5+
import com.fasterxml.jackson.annotation.JsonProperty;
6+
import lombok.AllArgsConstructor;
7+
import lombok.Builder;
8+
import lombok.Data;
9+
import lombok.NoArgsConstructor;
10+
import lombok.ToString;
11+
import org.devlive.sdk.openai.entity.ChoiceEntity;
12+
import org.devlive.sdk.openai.entity.UsageEntity;
13+
14+
import java.util.List;
15+
16+
@Data
17+
@Builder
18+
@ToString
19+
@NoArgsConstructor
20+
@AllArgsConstructor
21+
@JsonIgnoreProperties(ignoreUnknown = true)
22+
public class CompleteResponse
23+
{
24+
@JsonProperty(value = "id")
25+
private String name;
26+
27+
@JsonProperty(value = "object")
28+
private String object;
29+
30+
@JsonProperty(value = "created")
31+
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
32+
private String createTime;
33+
34+
@JsonProperty(value = "model")
35+
private String model;
36+
37+
@JsonProperty(value = "choices")
38+
private List<ChoiceEntity> choices;
39+
40+
@JsonProperty(value = "usage")
41+
private UsageEntity usage;
42+
}

0 commit comments

Comments
 (0)