Skip to content
This repository was archived by the owner on Feb 11, 2025. It is now read-only.

Commit 88a4e67

Browse files
authored
Merge branch 'main' into add-organizationId
2 parents dc15a93 + b42a9d2 commit 88a4e67

File tree

13 files changed

+698
-89
lines changed

13 files changed

+698
-89
lines changed

.prettierrc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"tabWidth": 4,
3+
"printWidth": 120
4+
}

README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,10 +332,60 @@ client.moderation(request)
332332
.execute();
333333
```
334334

335+
## Images Generations with DALL·E
336+
337+
Simple way:
338+
339+
```
340+
OpenAiClient client = OpenAiClient
341+
.builder()
342+
.openAiApiKey(System.getenv("OPENAI_API_KEY"))
343+
.build();
344+
345+
ImageRequest request = ImageRequest
346+
.builder()
347+
.prompt("Beautiful house on country side")
348+
.build();
349+
350+
ImageResponse response = client.imagesGenerations(request).execute();
351+
352+
// remote image generated with model DALL·E 3 and resolution 1024x1024
353+
String remoteImage = response.data().get(0).url();
354+
```
355+
356+
Customizable way:
357+
358+
```
359+
OpenAiClient client = OpenAiClient
360+
.builder()
361+
.openAiApiKey(System.getenv("OPENAI_API_KEY"))
362+
.logRequests()
363+
.logResponses()
364+
.withPersisting()
365+
.build();
366+
367+
ImageRequest request = ImageRequest
368+
.builder()
369+
.model(DALL_E_3)
370+
.size(DALL_E_SIZE_1792_x_1024)
371+
.quality(DALL_E_QUALITY_HD)
372+
.responseFormat(DALL_E_RESPONSE_FORMAT_B64_JSON) // if you like to get the image within the response body
373+
.prompt("Cute red parrot flying in the sky")
374+
.build();
375+
376+
ImageResponse response = client.imagesGenerations(request).execute();
377+
378+
// your generated image is here locally:
379+
String localImage = response.data().get(0).url();
380+
```
381+
382+
### Asynchronously
383+
335384
# Useful materials
336385

337386
- How to get best results form AI: https://www.deeplearning.ai/short-courses/chatgpt-prompt-engineering-for-developers/
338387
- Text version of above course: https://platform.openai.com/docs/guides/gpt-best-practices
339388
- How to build software powered by
340389
OpenAI/ChatGPT: https://www.deeplearning.ai/short-courses/building-systems-with-chatgpt/
341390
- Cookbook with examples of how to use OpenAI API: https://github.com/openai/openai-cookbook
391+
- ChatGPT DALL-E 3: Complete Guide (Generate Images with Text) https://www.akkio.com/post/chatgpt-dall-e-3

src/main/java/dev/ai4j/openai4j/DefaultOpenAiClient.java

Lines changed: 64 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@
22

33
import static dev.ai4j.openai4j.Json.GSON;
44

5-
import java.io.IOException;
6-
import java.util.Collections;
7-
import java.util.List;
8-
95
import org.slf4j.Logger;
106
import org.slf4j.LoggerFactory;
117

@@ -15,9 +11,14 @@
1511
import dev.ai4j.openai4j.completion.CompletionResponse;
1612
import dev.ai4j.openai4j.embedding.EmbeddingRequest;
1713
import dev.ai4j.openai4j.embedding.EmbeddingResponse;
14+
import dev.ai4j.openai4j.image.GenerateImagesRequest;
15+
import dev.ai4j.openai4j.image.GenerateImagesResponse;
1816
import dev.ai4j.openai4j.moderation.ModerationRequest;
1917
import dev.ai4j.openai4j.moderation.ModerationResponse;
2018
import dev.ai4j.openai4j.moderation.ModerationResult;
19+
import java.io.IOException;
20+
import java.util.Collections;
21+
import java.util.List;
2122
import okhttp3.Cache;
2223
import okhttp3.OkHttpClient;
2324
import retrofit2.Retrofit;
@@ -38,15 +39,14 @@ public DefaultOpenAiClient(String apiKey) {
3839
}
3940

4041
private DefaultOpenAiClient(Builder serviceBuilder) {
41-
4242
this.baseUrl = serviceBuilder.baseUrl;
4343
this.apiVersion = serviceBuilder.apiVersion;
4444

4545
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder()
46-
.callTimeout(serviceBuilder.callTimeout)
47-
.connectTimeout(serviceBuilder.connectTimeout)
48-
.readTimeout(serviceBuilder.readTimeout)
49-
.writeTimeout(serviceBuilder.writeTimeout);
46+
.callTimeout(serviceBuilder.callTimeout)
47+
.connectTimeout(serviceBuilder.connectTimeout)
48+
.readTimeout(serviceBuilder.readTimeout)
49+
.writeTimeout(serviceBuilder.writeTimeout);
5050

5151
if (serviceBuilder.openAiApiKey == null && serviceBuilder.azureApiKey == null) {
5252
throw new IllegalArgumentException("openAiApiKey OR azureApiKey must be defined");
@@ -78,17 +78,18 @@ private DefaultOpenAiClient(Builder serviceBuilder) {
7878

7979
this.okHttpClient = okHttpClientBuilder.build();
8080

81-
Retrofit retrofit = new Retrofit.Builder()
82-
.baseUrl(serviceBuilder.baseUrl)
83-
.client(okHttpClient)
84-
.addConverterFactory(GsonConverterFactory.create(GSON))
85-
.build();
81+
Retrofit.Builder retrofitBuilder = new Retrofit.Builder().baseUrl(serviceBuilder.baseUrl).client(okHttpClient);
82+
83+
if (serviceBuilder.persistTo != null) {
84+
retrofitBuilder.addConverterFactory(new PersistorConverterFactory(serviceBuilder.persistTo));
85+
}
86+
87+
retrofitBuilder.addConverterFactory(GsonConverterFactory.create(GSON));
8688

87-
this.openAiApi = retrofit.create(OpenAiApi.class);
89+
this.openAiApi = retrofitBuilder.build().create(OpenAiApi.class);
8890
}
8991

9092
public void shutdown() {
91-
9293
okHttpClient.dispatcher().executorService().shutdown();
9394

9495
okHttpClient.connectionPool().evictAll();
@@ -116,122 +117,99 @@ public DefaultOpenAiClient build() {
116117

117118
@Override
118119
public SyncOrAsyncOrStreaming<CompletionResponse> completion(CompletionRequest request) {
119-
120-
CompletionRequest syncRequest = CompletionRequest.builder()
121-
.from(request)
122-
.stream(null)
123-
.build();
120+
CompletionRequest syncRequest = CompletionRequest.builder().from(request).stream(null).build();
124121

125122
return new RequestExecutor<>(
126-
openAiApi.completions(syncRequest, apiVersion),
127-
(r) -> r,
128-
okHttpClient,
129-
formatUrl("completions"),
130-
() -> CompletionRequest.builder().from(request).stream(true).build(),
131-
CompletionResponse.class,
132-
(r) -> r,
133-
logStreamingResponses
123+
openAiApi.completions(syncRequest, apiVersion),
124+
r -> r,
125+
okHttpClient,
126+
formatUrl("completions"),
127+
() -> CompletionRequest.builder().from(request).stream(true).build(),
128+
CompletionResponse.class,
129+
r -> r,
130+
logStreamingResponses
134131
);
135132
}
136133

137134
@Override
138135
public SyncOrAsyncOrStreaming<String> completion(String prompt) {
136+
CompletionRequest request = CompletionRequest.builder().prompt(prompt).build();
139137

140-
CompletionRequest request = CompletionRequest.builder()
141-
.prompt(prompt)
142-
.build();
143-
144-
CompletionRequest syncRequest = CompletionRequest.builder()
145-
.from(request)
146-
.stream(null)
147-
.build();
138+
CompletionRequest syncRequest = CompletionRequest.builder().from(request).stream(null).build();
148139

149140
return new RequestExecutor<>(
150-
openAiApi.completions(syncRequest, apiVersion),
151-
CompletionResponse::text,
152-
okHttpClient,
153-
formatUrl("completions"),
154-
() -> CompletionRequest.builder().from(request).stream(true).build(),
155-
CompletionResponse.class,
156-
CompletionResponse::text,
157-
logStreamingResponses
141+
openAiApi.completions(syncRequest, apiVersion),
142+
CompletionResponse::text,
143+
okHttpClient,
144+
formatUrl("completions"),
145+
() -> CompletionRequest.builder().from(request).stream(true).build(),
146+
CompletionResponse.class,
147+
CompletionResponse::text,
148+
logStreamingResponses
158149
);
159150
}
160151

161152
@Override
162153
public SyncOrAsyncOrStreaming<ChatCompletionResponse> chatCompletion(ChatCompletionRequest request) {
163-
164-
ChatCompletionRequest syncRequest = ChatCompletionRequest.builder()
165-
.from(request)
166-
.stream(null)
167-
.build();
154+
ChatCompletionRequest syncRequest = ChatCompletionRequest.builder().from(request).stream(null).build();
168155

169156
return new RequestExecutor<>(
170-
openAiApi.chatCompletions(syncRequest, apiVersion),
171-
(r) -> r,
172-
okHttpClient,
173-
formatUrl("chat/completions"),
174-
() -> ChatCompletionRequest.builder().from(request).stream(true).build(),
175-
ChatCompletionResponse.class,
176-
(r) -> r,
177-
logStreamingResponses
157+
openAiApi.chatCompletions(syncRequest, apiVersion),
158+
r -> r,
159+
okHttpClient,
160+
formatUrl("chat/completions"),
161+
() -> ChatCompletionRequest.builder().from(request).stream(true).build(),
162+
ChatCompletionResponse.class,
163+
r -> r,
164+
logStreamingResponses
178165
);
179166
}
180167

181168
@Override
182169
public SyncOrAsyncOrStreaming<String> chatCompletion(String userMessage) {
170+
ChatCompletionRequest request = ChatCompletionRequest.builder().addUserMessage(userMessage).build();
183171

184-
ChatCompletionRequest request = ChatCompletionRequest.builder()
185-
.addUserMessage(userMessage)
186-
.build();
187-
188-
ChatCompletionRequest syncRequest = ChatCompletionRequest.builder()
189-
.from(request)
190-
.stream(null)
191-
.build();
172+
ChatCompletionRequest syncRequest = ChatCompletionRequest.builder().from(request).stream(null).build();
192173

193174
return new RequestExecutor<>(
194-
openAiApi.chatCompletions(syncRequest, apiVersion),
195-
ChatCompletionResponse::content,
196-
okHttpClient,
197-
formatUrl("chat/completions"),
198-
() -> ChatCompletionRequest.builder().from(request).stream(true).build(),
199-
ChatCompletionResponse.class,
200-
(r) -> r.choices().get(0).delta().content(),
201-
logStreamingResponses
175+
openAiApi.chatCompletions(syncRequest, apiVersion),
176+
ChatCompletionResponse::content,
177+
okHttpClient,
178+
formatUrl("chat/completions"),
179+
() -> ChatCompletionRequest.builder().from(request).stream(true).build(),
180+
ChatCompletionResponse.class,
181+
r -> r.choices().get(0).delta().content(),
182+
logStreamingResponses
202183
);
203184
}
204185

205186
@Override
206187
public SyncOrAsync<EmbeddingResponse> embedding(EmbeddingRequest request) {
207-
208-
return new RequestExecutor<>(openAiApi.embeddings(request, apiVersion), (r) -> r);
188+
return new RequestExecutor<>(openAiApi.embeddings(request, apiVersion), r -> r);
209189
}
210190

211191
@Override
212192
public SyncOrAsync<List<Float>> embedding(String input) {
213-
214-
EmbeddingRequest request = EmbeddingRequest.builder()
215-
.input(input)
216-
.build();
193+
EmbeddingRequest request = EmbeddingRequest.builder().input(input).build();
217194

218195
return new RequestExecutor<>(openAiApi.embeddings(request, apiVersion), EmbeddingResponse::embedding);
219196
}
220197

221198
@Override
222199
public SyncOrAsync<ModerationResponse> moderation(ModerationRequest request) {
223-
224-
return new RequestExecutor<>(openAiApi.moderations(request, apiVersion), (r) -> r);
200+
return new RequestExecutor<>(openAiApi.moderations(request, apiVersion), r -> r);
225201
}
226202

227203
@Override
228204
public SyncOrAsync<ModerationResult> moderation(String input) {
205+
ModerationRequest request = ModerationRequest.builder().input(input).build();
229206

230-
ModerationRequest request = ModerationRequest.builder()
231-
.input(input)
232-
.build();
207+
return new RequestExecutor<>(openAiApi.moderations(request, apiVersion), r -> r.results().get(0));
208+
}
233209

234-
return new RequestExecutor<>(openAiApi.moderations(request, apiVersion), (r) -> r.results().get(0));
210+
@Override
211+
public SyncOrAsync<GenerateImagesResponse> imagesGeneration(GenerateImagesRequest request) {
212+
return new RequestExecutor<>(openAiApi.imagesGenerations(request, apiVersion), r -> r);
235213
}
236214

237215
private String formatUrl(String endpoint) {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package dev.ai4j.openai4j;
2+
3+
import java.io.IOException;
4+
import java.io.InputStream;
5+
import java.net.URI;
6+
import java.nio.file.Files;
7+
import java.nio.file.Path;
8+
import java.nio.file.Paths;
9+
import java.nio.file.StandardCopyOption;
10+
import java.nio.file.StandardOpenOption;
11+
import java.util.Base64;
12+
import java.util.UUID;
13+
14+
class FilePersistor {
15+
16+
static Path persistFromUri(URI uri, Path destinationFolder) {
17+
try {
18+
Path fileName = Paths.get(uri.getPath()).getFileName();
19+
Path destinationFilePath = destinationFolder.resolve(fileName);
20+
try (InputStream inputStream = uri.toURL().openStream()) {
21+
java.nio.file.Files.copy(inputStream, destinationFilePath, StandardCopyOption.REPLACE_EXISTING);
22+
}
23+
24+
return destinationFilePath;
25+
} catch (IOException e) {
26+
throw new RuntimeException("Error persisting file from URI: " + uri, e);
27+
}
28+
}
29+
30+
public static Path persistFromBase64String(String base64EncodedString, Path destinationFolder) throws IOException {
31+
byte[] decodedBytes = Base64.getDecoder().decode(base64EncodedString);
32+
Path destinationFile = destinationFolder.resolve(randomFileName());
33+
34+
Files.write(destinationFile, decodedBytes, StandardOpenOption.CREATE);
35+
36+
return destinationFile;
37+
}
38+
39+
private static String randomFileName() {
40+
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 20);
41+
}
42+
}

src/main/java/dev/ai4j/openai4j/OpenAiApi.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import dev.ai4j.openai4j.completion.CompletionResponse;
77
import dev.ai4j.openai4j.embedding.EmbeddingRequest;
88
import dev.ai4j.openai4j.embedding.EmbeddingResponse;
9+
import dev.ai4j.openai4j.image.GenerateImagesRequest;
10+
import dev.ai4j.openai4j.image.GenerateImagesResponse;
911
import dev.ai4j.openai4j.moderation.ModerationRequest;
1012
import dev.ai4j.openai4j.moderation.ModerationResponse;
1113
import retrofit2.Call;
@@ -15,14 +17,16 @@
1517
import retrofit2.http.Query;
1618

1719
interface OpenAiApi {
18-
1920
@POST("completions")
2021
@Headers("Content-Type: application/json")
2122
Call<CompletionResponse> completions(@Body CompletionRequest request, @Query("api-version") String apiVersion);
2223

2324
@POST("chat/completions")
2425
@Headers("Content-Type: application/json")
25-
Call<ChatCompletionResponse> chatCompletions(@Body ChatCompletionRequest request, @Query("api-version") String apiVersion);
26+
Call<ChatCompletionResponse> chatCompletions(
27+
@Body ChatCompletionRequest request,
28+
@Query("api-version") String apiVersion
29+
);
2630

2731
@POST("embeddings")
2832
@Headers("Content-Type: application/json")
@@ -31,4 +35,11 @@ interface OpenAiApi {
3135
@POST("moderations")
3236
@Headers("Content-Type: application/json")
3337
Call<ModerationResponse> moderations(@Body ModerationRequest request, @Query("api-version") String apiVersion);
38+
39+
@POST("images/generations")
40+
@Headers({ "Content-Type: application/json" })
41+
Call<GenerateImagesResponse> imagesGenerations(
42+
@Body GenerateImagesRequest request,
43+
@Query("api-version") String apiVersion
44+
);
3445
}

0 commit comments

Comments
 (0)