├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── main.yaml │ └── release.yml ├── .gitignore ├── .prettierrc ├── LICENSE ├── README.md ├── pom.xml ├── src ├── main │ ├── java │ │ └── dev │ │ │ └── ai4j │ │ │ └── openai4j │ │ │ ├── ApiKeyHeaderInjector.java │ │ │ ├── AsyncRequestExecutor.java │ │ │ ├── AsyncResponseHandling.java │ │ │ ├── AuthorizationHeaderInjector.java │ │ │ ├── DefaultOpenAiClient.java │ │ │ ├── ErrorHandling.java │ │ │ ├── FilePersistor.java │ │ │ ├── GenericHeaderInjector.java │ │ │ ├── Json.java │ │ │ ├── LogLevel.java │ │ │ ├── OpenAiApi.java │ │ │ ├── OpenAiClient.java │ │ │ ├── OpenAiHttpException.java │ │ │ ├── PersistorConverterFactory.java │ │ │ ├── RequestExecutor.java │ │ │ ├── RequestLoggingInterceptor.java │ │ │ ├── ResponseHandle.java │ │ │ ├── ResponseLoggingInterceptor.java │ │ │ ├── StreamingCompletionHandling.java │ │ │ ├── StreamingRequestExecutor.java │ │ │ ├── StreamingResponseHandling.java │ │ │ ├── SyncOrAsync.java │ │ │ ├── SyncOrAsyncOrStreaming.java │ │ │ ├── SyncRequestExecutor.java │ │ │ ├── Utils.java │ │ │ ├── chat │ │ │ ├── AssistantMessage.java │ │ │ ├── ChatCompletionChoice.java │ │ │ ├── ChatCompletionModel.java │ │ │ ├── ChatCompletionRequest.java │ │ │ ├── ChatCompletionResponse.java │ │ │ ├── Content.java │ │ │ ├── ContentType.java │ │ │ ├── Delta.java │ │ │ ├── Function.java │ │ │ ├── FunctionCall.java │ │ │ ├── FunctionCallUtil.java │ │ │ ├── FunctionMessage.java │ │ │ ├── ImageDetail.java │ │ │ ├── ImageUrl.java │ │ │ ├── InputAudio.java │ │ │ ├── JsonAnyOfSchema.java │ │ │ ├── JsonArraySchema.java │ │ │ ├── JsonBooleanSchema.java │ │ │ ├── JsonEnumSchema.java │ │ │ ├── JsonIntegerSchema.java │ │ │ ├── JsonNumberSchema.java │ │ │ ├── JsonObjectSchema.java │ │ │ ├── JsonReferenceSchema.java │ │ │ ├── JsonSchema.java │ │ │ ├── JsonSchemaElement.java │ │ │ ├── JsonStringSchema.java │ │ │ ├── Message.java │ │ │ ├── ResponseFormat.java │ │ │ ├── ResponseFormatType.java │ │ │ ├── Role.java │ │ │ ├── SystemMessage.java │ │ │ ├── Tool.java │ │ │ ├── ToolCall.java │ │ │ ├── ToolChoice.java │ │ │ ├── ToolChoiceMode.java │ │ │ ├── ToolMessage.java │ │ │ ├── ToolType.java │ │ │ └── UserMessage.java │ │ │ ├── completion │ │ │ ├── CompletionChoice.java │ │ │ ├── CompletionModel.java │ │ │ ├── CompletionRequest.java │ │ │ ├── CompletionResponse.java │ │ │ └── Logprobs.java │ │ │ ├── embedding │ │ │ ├── Embedding.java │ │ │ ├── EmbeddingModel.java │ │ │ ├── EmbeddingRequest.java │ │ │ └── EmbeddingResponse.java │ │ │ ├── image │ │ │ ├── GenerateImagesRequest.java │ │ │ ├── GenerateImagesResponse.java │ │ │ ├── ImageData.java │ │ │ └── ImageModel.java │ │ │ ├── moderation │ │ │ ├── Categories.java │ │ │ ├── CategoryScores.java │ │ │ ├── ModerationModel.java │ │ │ ├── ModerationRequest.java │ │ │ ├── ModerationResponse.java │ │ │ └── ModerationResult.java │ │ │ ├── shared │ │ │ ├── CompletionTokensDetails.java │ │ │ ├── PromptTokensDetails.java │ │ │ ├── StreamOptions.java │ │ │ └── Usage.java │ │ │ └── spi │ │ │ ├── OpenAiClientBuilderFactory.java │ │ │ └── ServiceHelper.java │ └── resources │ │ └── META-INF │ │ └── native-image │ │ └── dev.ai4j │ │ └── openai4j │ │ ├── proxy-config.json │ │ ├── reflect-config.json │ │ └── resource-config.json └── test │ ├── java │ └── dev │ │ └── ai4j │ │ └── openai4j │ │ ├── FilePersistorTest.java │ │ ├── JsonTest.java │ │ ├── RateLimitAwareTest.java │ │ ├── RequestLoggingInterceptorTest.java │ │ ├── chat │ │ ├── ChatCompletionAsyncTest.java │ │ ├── ChatCompletionRequestTest.java │ │ ├── ChatCompletionStreamingTest.java │ │ └── ChatCompletionTest.java │ │ ├── completion │ │ ├── CompletionAsyncTest.java │ │ ├── CompletionStreamingTest.java │ │ └── CompletionTest.java │ │ ├── embedding │ │ ├── EmbeddingsAsyncTest.java │ │ └── EmbeddingsTest.java │ │ ├── image │ │ └── ImagesGenerationTest.java │ │ └── moderation │ │ ├── ModerationAsyncTest.java │ │ └── ModerationTest.java │ └── resources │ ├── ChatCompletionResponse.json │ ├── ModerationResponse.json │ └── sample.b64 └── todo.txt /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Code to reproduce the behavior: 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Actual behavior** 20 | A clear and concise description of what actually happened. 21 | 22 | **Environment** 23 | - Java version [e.g. 17] 24 | 25 | **Additional context** 26 | Add any other context about the problem here. 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/main.yaml: -------------------------------------------------------------------------------- 1 | name: Java CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | java_build: 13 | strategy: 14 | matrix: 15 | java_version: [ 8, 11, 17, 21 ] 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Set up JDK ${{ matrix.java_version }} 20 | uses: actions/setup-java@v4 21 | with: 22 | java-version: ${{ matrix.java_version }} 23 | distribution: 'temurin' 24 | cache: 'maven' 25 | - name: Build with JDK ${{ matrix.java_version }} 26 | run: mvn -B clean test ${{ matrix.included_modules }} 27 | env: 28 | OPENAI_BASE_URL: 'http://langchain4j.dev:8082/v1' 29 | OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | release: 8 | 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v3 13 | 14 | - name: Set up JDK 8 15 | uses: actions/setup-java@v3 16 | with: 17 | java-version: '8' 18 | distribution: 'temurin' 19 | cache: maven 20 | server-id: ossrh 21 | server-username: OSSRH_USERNAME 22 | server-password: OSSRH_PASSWORD 23 | gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} 24 | gpg-passphrase: GPG_PASSPHRASE 25 | 26 | - name: release 27 | run: mvn -B clean deploy -Psign -DskipTests -DskipITs 28 | env: 29 | OPENAI_BASE_URL: 'http://langchain4j.dev:8082/v1' 30 | OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} 31 | GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} 32 | OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} 33 | OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | replay_pid* 25 | 26 | **/target/** 27 | 28 | .idea 29 | *.iml 30 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "printWidth": 120 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/ApiKeyHeaderInjector.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j; 2 | 3 | import java.util.Collections; 4 | 5 | class ApiKeyHeaderInjector extends GenericHeaderInjector { 6 | 7 | ApiKeyHeaderInjector(String apiKey) { 8 | super(Collections.singletonMap("api-key", apiKey)); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/AsyncRequestExecutor.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j; 2 | 3 | import retrofit2.Call; 4 | 5 | import java.io.IOException; 6 | import java.util.function.Consumer; 7 | import java.util.function.Function; 8 | 9 | import static dev.ai4j.openai4j.Utils.toException; 10 | 11 | class AsyncRequestExecutor { 12 | 13 | private final Call call; 14 | private final Function responseContentExtractor; 15 | 16 | AsyncRequestExecutor(Call call, 17 | Function responseContentExtractor) { 18 | this.call = call; 19 | this.responseContentExtractor = responseContentExtractor; 20 | } 21 | 22 | AsyncResponseHandling onResponse(Consumer responseHandler) { 23 | return new AsyncResponseHandling() { 24 | 25 | @Override 26 | public ErrorHandling onError(Consumer errorHandler) { 27 | return new ErrorHandling() { 28 | 29 | @Override 30 | public ResponseHandle execute() { 31 | try { 32 | retrofit2.Response retrofitResponse = call.execute(); 33 | if (retrofitResponse.isSuccessful()) { 34 | Response response = retrofitResponse.body(); 35 | ResponseContent responseContent = responseContentExtractor.apply(response); 36 | responseHandler.accept(responseContent); 37 | } else { 38 | errorHandler.accept(toException(retrofitResponse)); 39 | } 40 | } catch (IOException e) { 41 | errorHandler.accept(e); 42 | } 43 | return new ResponseHandle(); 44 | } 45 | }; 46 | } 47 | 48 | @Override 49 | public ErrorHandling ignoreErrors() { 50 | return new ErrorHandling() { 51 | 52 | @Override 53 | public ResponseHandle execute() { 54 | try { 55 | retrofit2.Response retrofitResponse = call.execute(); 56 | if (retrofitResponse.isSuccessful()) { 57 | Response response = retrofitResponse.body(); 58 | ResponseContent responseContent = responseContentExtractor.apply(response); 59 | responseHandler.accept(responseContent); 60 | } 61 | } catch (IOException e) { 62 | // intentionally ignoring, because user called ignoreErrors() 63 | } 64 | return new ResponseHandle(); 65 | } 66 | }; 67 | } 68 | }; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/AsyncResponseHandling.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j; 2 | 3 | import java.util.function.Consumer; 4 | 5 | public interface AsyncResponseHandling { 6 | 7 | ErrorHandling onError(Consumer errorHandler); 8 | 9 | ErrorHandling ignoreErrors(); 10 | } -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/AuthorizationHeaderInjector.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j; 2 | 3 | import java.util.Collections; 4 | 5 | class AuthorizationHeaderInjector extends GenericHeaderInjector { 6 | 7 | AuthorizationHeaderInjector(String apiKey) { 8 | super(Collections.singletonMap("Authorization", "Bearer " + apiKey)); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/ErrorHandling.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j; 2 | 3 | public interface ErrorHandling { 4 | 5 | ResponseHandle execute(); 6 | } -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/FilePersistor.java: -------------------------------------------------------------------------------- 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 | } 43 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/GenericHeaderInjector.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j; 2 | 3 | import java.io.IOException; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import java.util.Optional; 7 | 8 | import okhttp3.Interceptor; 9 | import okhttp3.Request.Builder; 10 | import okhttp3.Response; 11 | 12 | class GenericHeaderInjector implements Interceptor { 13 | private final Map headers = new HashMap<>(); 14 | 15 | GenericHeaderInjector(Map headers) { 16 | Optional.ofNullable(headers) 17 | .ifPresent(this.headers::putAll); 18 | } 19 | 20 | @Override 21 | public Response intercept(Chain chain) throws IOException { 22 | Builder builder = chain.request().newBuilder(); 23 | 24 | // Add headers 25 | this.headers.forEach(builder::addHeader); 26 | 27 | return chain.proceed(builder.build()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/Json.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.databind.SerializationFeature; 6 | 7 | public class Json { 8 | 9 | static final ObjectMapper OBJECT_MAPPER = new ObjectMapper() 10 | .enable(SerializationFeature.INDENT_OUTPUT); 11 | 12 | public static String toJson(Object o) { 13 | try { 14 | return OBJECT_MAPPER.writeValueAsString(o); 15 | } catch (JsonProcessingException jpe) { 16 | throw new RuntimeException(jpe); 17 | } 18 | } 19 | 20 | public static T fromJson(String json, Class type) { 21 | try { 22 | return OBJECT_MAPPER.readValue(json, type); 23 | } catch (JsonProcessingException jpe) { 24 | throw new RuntimeException(jpe); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/LogLevel.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j; 2 | 3 | public enum LogLevel { 4 | 5 | INFO, 6 | WARN, 7 | ERROR, 8 | DEBUG 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/OpenAiApi.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j; 2 | 3 | import dev.ai4j.openai4j.chat.ChatCompletionRequest; 4 | import dev.ai4j.openai4j.chat.ChatCompletionResponse; 5 | import dev.ai4j.openai4j.completion.CompletionRequest; 6 | import dev.ai4j.openai4j.completion.CompletionResponse; 7 | import dev.ai4j.openai4j.embedding.EmbeddingRequest; 8 | import dev.ai4j.openai4j.embedding.EmbeddingResponse; 9 | import dev.ai4j.openai4j.image.GenerateImagesRequest; 10 | import dev.ai4j.openai4j.image.GenerateImagesResponse; 11 | import dev.ai4j.openai4j.moderation.ModerationRequest; 12 | import dev.ai4j.openai4j.moderation.ModerationResponse; 13 | import java.util.Map; 14 | import retrofit2.Call; 15 | import retrofit2.http.Body; 16 | import retrofit2.http.HeaderMap; 17 | import retrofit2.http.Headers; 18 | import retrofit2.http.POST; 19 | import retrofit2.http.Query; 20 | 21 | interface OpenAiApi { 22 | @POST("completions") 23 | @Headers("Content-Type: application/json") 24 | Call completions(@Body CompletionRequest request, 25 | @Query("api-version") String apiVersion); 26 | 27 | @POST("completions") 28 | @Headers("Content-Type: application/json") 29 | Call completions( 30 | @HeaderMap Map headers, 31 | @Body CompletionRequest request, 32 | @Query("api-version") String apiVersion); 33 | 34 | @POST("chat/completions") 35 | @Headers("Content-Type: application/json") 36 | Call chatCompletions( 37 | @Body ChatCompletionRequest request, 38 | @Query("api-version") String apiVersion 39 | ); 40 | 41 | @POST("chat/completions") 42 | @Headers("Content-Type: application/json") 43 | Call chatCompletions( 44 | @HeaderMap Map headers, 45 | @Body ChatCompletionRequest request, 46 | @Query("api-version") String apiVersion 47 | ); 48 | 49 | @POST("embeddings") 50 | @Headers("Content-Type: application/json") 51 | Call embeddings( 52 | @Body EmbeddingRequest request, 53 | @Query("api-version") String apiVersion); 54 | 55 | @POST("embeddings") 56 | @Headers("Content-Type: application/json") 57 | Call embeddings( 58 | @HeaderMap Map headers, 59 | @Body EmbeddingRequest request, 60 | @Query("api-version") String apiVersion); 61 | 62 | @POST("moderations") 63 | @Headers("Content-Type: application/json") 64 | Call moderations( 65 | @Body ModerationRequest request, 66 | @Query("api-version") String apiVersion); 67 | 68 | @POST("moderations") 69 | @Headers("Content-Type: application/json") 70 | Call moderations( 71 | @HeaderMap Map headers, 72 | @Body ModerationRequest request, 73 | @Query("api-version") String apiVersion); 74 | 75 | @POST("images/generations") 76 | @Headers({"Content-Type: application/json"}) 77 | Call imagesGenerations( 78 | @Body GenerateImagesRequest request, 79 | @Query("api-version") String apiVersion 80 | ); 81 | 82 | @POST("images/generations") 83 | @Headers({"Content-Type: application/json"}) 84 | Call imagesGenerations( 85 | @HeaderMap Map headers, 86 | @Body GenerateImagesRequest request, 87 | @Query("api-version") String apiVersion 88 | ); 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/OpenAiHttpException.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j; 2 | 3 | public class OpenAiHttpException extends RuntimeException { 4 | 5 | private final int code; 6 | 7 | public OpenAiHttpException(int code, String message) { 8 | super(message); 9 | this.code = code; 10 | } 11 | 12 | public int code() { 13 | return code; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/PersistorConverterFactory.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j; 2 | 3 | import dev.ai4j.openai4j.image.GenerateImagesResponse; 4 | import java.io.IOException; 5 | import java.lang.annotation.Annotation; 6 | import java.lang.reflect.Type; 7 | import java.nio.file.Path; 8 | import okhttp3.ResponseBody; 9 | import retrofit2.Converter; 10 | import retrofit2.Retrofit; 11 | 12 | class PersistorConverterFactory extends Converter.Factory { 13 | 14 | private final Path persistTo; 15 | 16 | PersistorConverterFactory(Path persistTo) { 17 | this.persistTo = persistTo; 18 | } 19 | 20 | @Override 21 | public Converter responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) { 22 | return new PersistorConverter<>(retrofit.nextResponseBodyConverter(this, type, annotations)); 23 | } 24 | 25 | private class PersistorConverter implements Converter { 26 | 27 | private final Converter delegate; 28 | 29 | PersistorConverter(Converter delegate) { 30 | this.delegate = delegate; 31 | } 32 | 33 | @Override 34 | public T convert(ResponseBody value) throws IOException { 35 | T response = delegate.convert(value); 36 | 37 | if (response instanceof GenerateImagesResponse) { 38 | ((GenerateImagesResponse) response).data() 39 | .forEach(data -> { 40 | try { 41 | data.url( 42 | data.url() != null 43 | ? FilePersistor.persistFromUri(data.url(), persistTo).toUri() 44 | : FilePersistor.persistFromBase64String(data.b64Json(), persistTo).toUri() 45 | ); 46 | } catch (IOException e) { 47 | throw new RuntimeException(e); 48 | } 49 | }); 50 | } 51 | 52 | return response; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/RequestExecutor.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j; 2 | 3 | import okhttp3.OkHttpClient; 4 | import retrofit2.Call; 5 | 6 | import java.util.function.Consumer; 7 | import java.util.function.Function; 8 | import java.util.function.Supplier; 9 | 10 | class RequestExecutor 11 | implements SyncOrAsyncOrStreaming { 12 | 13 | private final Call call; 14 | private final Function responseContentExtractor; 15 | 16 | private final OkHttpClient okHttpClient; 17 | private final String endpointUrl; 18 | private final Supplier requestWithStreamSupplier; 19 | private final Class responseClass; 20 | private final Function streamEventContentExtractor; 21 | private final boolean logStreamingResponses; 22 | 23 | RequestExecutor(Call call, 24 | Function responseContentExtractor, 25 | 26 | OkHttpClient okHttpClient, 27 | String endpointUrl, 28 | Supplier requestWithStreamSupplier, 29 | Class responseClass, 30 | Function streamEventContentExtractor, 31 | boolean logStreamingResponses 32 | ) { 33 | this.call = call; 34 | this.responseContentExtractor = responseContentExtractor; 35 | 36 | this.okHttpClient = okHttpClient; 37 | this.endpointUrl = endpointUrl; 38 | this.requestWithStreamSupplier = requestWithStreamSupplier; 39 | this.responseClass = responseClass; 40 | this.streamEventContentExtractor = streamEventContentExtractor; 41 | this.logStreamingResponses = logStreamingResponses; 42 | } 43 | 44 | RequestExecutor(Call call, Function responseContentExtractor) { 45 | this.call = call; 46 | this.responseContentExtractor = responseContentExtractor; 47 | 48 | this.okHttpClient = null; 49 | this.endpointUrl = null; 50 | this.requestWithStreamSupplier = null; 51 | this.responseClass = null; 52 | this.streamEventContentExtractor = null; 53 | this.logStreamingResponses = false; 54 | } 55 | 56 | @Override 57 | public ResponseContent execute() { 58 | 59 | return new SyncRequestExecutor<>(call, responseContentExtractor).execute(); 60 | } 61 | 62 | @Override 63 | public AsyncResponseHandling onResponse(Consumer responseHandler) { 64 | 65 | return new AsyncRequestExecutor<>(call, responseContentExtractor).onResponse(responseHandler); 66 | } 67 | 68 | @Override 69 | public StreamingResponseHandling onPartialResponse(Consumer partialResponseHandler) { 70 | 71 | return new StreamingRequestExecutor<>( 72 | okHttpClient, 73 | endpointUrl, 74 | requestWithStreamSupplier, 75 | responseClass, 76 | streamEventContentExtractor, 77 | logStreamingResponses 78 | ).onPartialResponse(partialResponseHandler); 79 | } 80 | } -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/RequestLoggingInterceptor.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j; 2 | 3 | import okhttp3.Headers; 4 | import okhttp3.Interceptor; 5 | import okhttp3.Request; 6 | import okhttp3.Response; 7 | import okio.Buffer; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | import java.io.IOException; 12 | import java.util.HashSet; 13 | import java.util.Set; 14 | 15 | import static java.util.Arrays.asList; 16 | import static java.util.stream.Collectors.joining; 17 | import static java.util.stream.StreamSupport.stream; 18 | 19 | class RequestLoggingInterceptor implements Interceptor { 20 | 21 | private static final Logger log = LoggerFactory.getLogger(RequestLoggingInterceptor.class); 22 | 23 | private static final Set COMMON_SECRET_HEADERS = 24 | new HashSet<>(asList("authorization", "x-api-key", "x-auth-token")); 25 | private static final String BEARER = "Bearer"; 26 | private LogLevel logLevel = LogLevel.DEBUG; 27 | 28 | public RequestLoggingInterceptor() { 29 | } 30 | 31 | public RequestLoggingInterceptor(LogLevel logLevel) { 32 | this.logLevel = logLevel; 33 | } 34 | 35 | @Override 36 | public Response intercept(Chain chain) throws IOException { 37 | Request request = chain.request(); 38 | 39 | log(request); 40 | 41 | return chain.proceed(request); 42 | } 43 | 44 | private void log(Request request) { 45 | String message = "Request:\n- method: {}\n- url: {}\n- headers: {}\n- body: {}"; 46 | 47 | try { 48 | switch (logLevel) { 49 | case INFO: 50 | logInfo(request, message); 51 | break; 52 | case WARN: 53 | logWarn(request, message); 54 | break; 55 | case ERROR: 56 | logError(request, message); 57 | break; 58 | default: 59 | logDebug(request, message); 60 | } 61 | } catch (Exception e) { 62 | log.warn("Failed to log request", e); 63 | } 64 | } 65 | 66 | private void logInfo(Request request, String message) { 67 | log.info( 68 | message, 69 | request.method(), 70 | request.url(), 71 | inOneLine(request.headers()), 72 | getBody(request) 73 | ); 74 | } 75 | 76 | private void logWarn(Request request, String message) { 77 | log.warn( 78 | message, 79 | request.method(), 80 | request.url(), 81 | inOneLine(request.headers()), 82 | getBody(request) 83 | ); 84 | } 85 | 86 | private void logError(Request request, String message) { 87 | log.error( 88 | message, 89 | request.method(), 90 | request.url(), 91 | inOneLine(request.headers()), 92 | getBody(request) 93 | ); 94 | } 95 | 96 | private void logDebug(Request request, String message) { 97 | log.debug( 98 | message, 99 | request.method(), 100 | request.url(), 101 | inOneLine(request.headers()), 102 | getBody(request) 103 | ); 104 | } 105 | 106 | static String inOneLine(Headers headers) { 107 | 108 | return stream(headers.spliterator(), false) 109 | .map(header -> format(header.component1(), header.component2())) 110 | .collect(joining(", ")); 111 | } 112 | 113 | static String format(String headerKey, String headerValue) { 114 | if (COMMON_SECRET_HEADERS.contains(headerKey.toLowerCase())) { 115 | headerValue = maskSecretKey(headerValue); 116 | } 117 | return String.format("[%s: %s]", headerKey, headerValue); 118 | } 119 | 120 | static String maskSecretKey(String key) { 121 | if (key == null || key.trim().isEmpty()) { 122 | return key; 123 | } 124 | 125 | try { 126 | if (key.startsWith(BEARER)) { 127 | return BEARER + " " + mask(key.substring(BEARER.length() + 1)); 128 | } else { 129 | return mask(key); 130 | } 131 | } catch (Exception e) { 132 | return "Failed to mask the API key."; 133 | } 134 | } 135 | 136 | private static String mask(String key) { 137 | if (key.length() >= 7) { 138 | return key.substring(0, 5) + "..." + key.substring(key.length() - 2); 139 | } else { 140 | return "..."; 141 | } 142 | } 143 | 144 | private static String getBody(Request request) { 145 | try { 146 | Buffer buffer = new Buffer(); 147 | request.body().writeTo(buffer); 148 | return buffer.readUtf8(); 149 | } catch (Exception e) { 150 | log.warn("Exception happened while reading request body", e); 151 | return "[Exception happened while reading request body. Check logs for more details.]"; 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/ResponseHandle.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j; 2 | 3 | /** 4 | * Provides a mechanism to cancel the response after a request has been initiated. 5 | */ 6 | public class ResponseHandle { 7 | 8 | volatile boolean cancelled = false; 9 | 10 | /** 11 | * Cancels the response. Currently, this only works for streaming. It has no effect on regular asynchronous responses. 12 | */ 13 | public void cancel() { 14 | cancelled = true; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/ResponseLoggingInterceptor.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j; 2 | 3 | import okhttp3.Interceptor; 4 | import okhttp3.Request; 5 | import okhttp3.Response; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.io.IOException; 10 | 11 | import static dev.ai4j.openai4j.RequestLoggingInterceptor.inOneLine; 12 | 13 | class ResponseLoggingInterceptor implements Interceptor { 14 | 15 | private static final Logger log = LoggerFactory.getLogger(ResponseLoggingInterceptor.class); 16 | 17 | private LogLevel logLevel = LogLevel.DEBUG; 18 | 19 | public ResponseLoggingInterceptor() { 20 | } 21 | 22 | public ResponseLoggingInterceptor(LogLevel logLevel) { 23 | this.logLevel = logLevel; 24 | } 25 | 26 | @Override 27 | public Response intercept(Chain chain) throws IOException { 28 | Request request = chain.request(); 29 | Response response = chain.proceed(request); 30 | 31 | log(response); 32 | 33 | return response; 34 | } 35 | 36 | void log(Response response) { 37 | String message = "Response:\n- status code: {}\n- headers: {}\n- body: {}"; 38 | 39 | try { 40 | switch (logLevel) { 41 | case INFO: 42 | logInfo(response, message); 43 | break; 44 | case WARN: 45 | logWarn(response, message); 46 | break; 47 | case ERROR: 48 | logError(response, message); 49 | break; 50 | default: 51 | logDebug(response, message); 52 | } 53 | 54 | } catch (IOException e) { 55 | log.warn("Failed to log response", e); 56 | } 57 | } 58 | 59 | private void logError(Response response, String message) throws IOException { 60 | log.error( 61 | message, 62 | response.code(), 63 | inOneLine(response.headers()), 64 | getBody(response) 65 | ); 66 | } 67 | 68 | private void logWarn(Response response, String message) throws IOException { 69 | log.warn( 70 | message, 71 | response.code(), 72 | inOneLine(response.headers()), 73 | getBody(response) 74 | ); 75 | } 76 | 77 | private void logInfo(Response response, String message) throws IOException { 78 | log.info( 79 | message, 80 | response.code(), 81 | inOneLine(response.headers()), 82 | getBody(response) 83 | ); 84 | } 85 | 86 | private void logDebug(Response response, String message) throws IOException { 87 | log.debug( 88 | message, 89 | response.code(), 90 | inOneLine(response.headers()), 91 | getBody(response) 92 | ); 93 | } 94 | 95 | private String getBody(Response response) throws IOException { 96 | if (isEventStream(response)) { 97 | return "[skipping response body due to streaming]"; 98 | } else { 99 | return response.peekBody(Long.MAX_VALUE).string(); 100 | } 101 | } 102 | 103 | private boolean isEventStream(Response response) { 104 | String contentType = response.header("content-type"); 105 | return contentType != null && contentType.contains("event-stream"); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/StreamingCompletionHandling.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j; 2 | 3 | import java.util.function.Consumer; 4 | 5 | public interface StreamingCompletionHandling { 6 | 7 | ErrorHandling onError(Consumer errorHandler); 8 | 9 | ErrorHandling ignoreErrors(); 10 | } -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/StreamingResponseHandling.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j; 2 | 3 | public interface StreamingResponseHandling extends AsyncResponseHandling { 4 | 5 | StreamingCompletionHandling onComplete(Runnable streamingCompletionCallback); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/SyncOrAsync.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j; 2 | 3 | import java.util.function.Consumer; 4 | 5 | public interface SyncOrAsync { 6 | 7 | ResponseContent execute(); 8 | 9 | AsyncResponseHandling onResponse(Consumer responseHandler); 10 | } -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/SyncOrAsyncOrStreaming.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j; 2 | 3 | import java.util.function.Consumer; 4 | 5 | public interface SyncOrAsyncOrStreaming extends SyncOrAsync { 6 | 7 | StreamingResponseHandling onPartialResponse(Consumer partialResponseHandler); 8 | } -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/SyncRequestExecutor.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j; 2 | 3 | import retrofit2.Call; 4 | 5 | import java.io.IOException; 6 | import java.util.function.Function; 7 | 8 | import static dev.ai4j.openai4j.Utils.toException; 9 | 10 | class SyncRequestExecutor { 11 | 12 | private final Call call; 13 | private final Function responseContentExtractor; 14 | 15 | SyncRequestExecutor(Call call, 16 | Function responseContentExtractor) { 17 | this.call = call; 18 | this.responseContentExtractor = responseContentExtractor; 19 | } 20 | 21 | ResponseContent execute() { 22 | try { 23 | retrofit2.Response retrofitResponse = call.execute(); 24 | if (retrofitResponse.isSuccessful()) { 25 | Response response = retrofitResponse.body(); 26 | return responseContentExtractor.apply(response); 27 | } else { 28 | throw toException(retrofitResponse); 29 | } 30 | } catch (IOException e) { 31 | throw new RuntimeException(e); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/Utils.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j; 2 | 3 | import java.io.IOException; 4 | 5 | class Utils { 6 | 7 | static RuntimeException toException(retrofit2.Response response) throws IOException { 8 | return new OpenAiHttpException(response.code(), response.errorBody().string()); 9 | } 10 | 11 | static RuntimeException toException(okhttp3.Response response) throws IOException { 12 | return new OpenAiHttpException(response.code(), response.body().string()); 13 | } 14 | 15 | static T getOrDefault(T value, T defaultValue) { 16 | return value != null ? value : defaultValue; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/AssistantMessage.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.annotation.JsonSetter; 7 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 8 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 9 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 10 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 11 | 12 | import java.util.List; 13 | import java.util.Objects; 14 | 15 | import static dev.ai4j.openai4j.chat.Role.ASSISTANT; 16 | import static java.util.Arrays.asList; 17 | import static java.util.Collections.unmodifiableList; 18 | 19 | @JsonDeserialize(builder = AssistantMessage.Builder.class) 20 | @JsonInclude(JsonInclude.Include.NON_NULL) 21 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 22 | public final class AssistantMessage implements Message { 23 | 24 | @JsonProperty 25 | private final Role role = ASSISTANT; 26 | @JsonProperty 27 | private final String content; 28 | @JsonProperty 29 | private final String name; 30 | @JsonProperty 31 | private final List toolCalls; 32 | @JsonProperty 33 | private final Boolean refusal; 34 | @JsonProperty 35 | @Deprecated 36 | private final FunctionCall functionCall; 37 | 38 | private AssistantMessage(Builder builder) { 39 | this.content = builder.content; 40 | this.name = builder.name; 41 | this.toolCalls = builder.toolCalls; 42 | this.refusal = builder.refusal; 43 | this.functionCall = builder.functionCall; 44 | } 45 | 46 | public Role role() { 47 | return role; 48 | } 49 | 50 | public String content() { 51 | return content; 52 | } 53 | 54 | public String name() { 55 | return name; 56 | } 57 | 58 | public List toolCalls() { 59 | return toolCalls; 60 | } 61 | 62 | public Boolean refusal() { 63 | return refusal; 64 | } 65 | 66 | @Deprecated 67 | public FunctionCall functionCall() { 68 | return functionCall; 69 | } 70 | 71 | @Override 72 | public boolean equals(Object another) { 73 | if (this == another) return true; 74 | return another instanceof AssistantMessage 75 | && equalTo((AssistantMessage) another); 76 | } 77 | 78 | private boolean equalTo(AssistantMessage another) { 79 | return Objects.equals(role, another.role) 80 | && Objects.equals(content, another.content) 81 | && Objects.equals(name, another.name) 82 | && Objects.equals(toolCalls, another.toolCalls) 83 | && Objects.equals(refusal, another.refusal) 84 | && Objects.equals(functionCall, another.functionCall); 85 | } 86 | 87 | @Override 88 | public int hashCode() { 89 | int h = 5381; 90 | h += (h << 5) + Objects.hashCode(role); 91 | h += (h << 5) + Objects.hashCode(content); 92 | h += (h << 5) + Objects.hashCode(name); 93 | h += (h << 5) + Objects.hashCode(toolCalls); 94 | h += (h << 5) + Objects.hashCode(refusal); 95 | h += (h << 5) + Objects.hashCode(functionCall); 96 | return h; 97 | } 98 | 99 | @Override 100 | public String toString() { 101 | return "AssistantMessage{" 102 | + "role=" + role 103 | + ", content=" + content 104 | + ", name=" + name 105 | + ", toolCalls=" + toolCalls 106 | + ", refusal=" + refusal 107 | + ", functionCall=" + functionCall 108 | + "}"; 109 | } 110 | 111 | public static AssistantMessage from(String content) { 112 | return AssistantMessage.builder() 113 | .content(content) 114 | .build(); 115 | } 116 | 117 | public static Builder builder() { 118 | return new Builder(); 119 | } 120 | 121 | @JsonPOJOBuilder(withPrefix = "") 122 | @JsonIgnoreProperties(ignoreUnknown = true) 123 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 124 | public static final class Builder { 125 | 126 | private String content; 127 | private String name; 128 | private List toolCalls; 129 | private Boolean refusal; 130 | @Deprecated 131 | private FunctionCall functionCall; 132 | 133 | private Builder() { 134 | } 135 | 136 | public Builder content(String content) { 137 | this.content = content; 138 | return this; 139 | } 140 | 141 | public Builder name(String name) { 142 | this.name = name; 143 | return this; 144 | } 145 | 146 | public Builder toolCalls(ToolCall... toolCalls) { 147 | return toolCalls(asList(toolCalls)); 148 | } 149 | 150 | @JsonSetter 151 | public Builder toolCalls(List toolCalls) { 152 | if (toolCalls != null) { 153 | this.toolCalls = unmodifiableList(toolCalls); 154 | } 155 | return this; 156 | } 157 | 158 | public Builder refusal(Boolean refusal) { 159 | this.refusal = refusal; 160 | return this; 161 | } 162 | 163 | @Deprecated 164 | public Builder functionCall(FunctionCall functionCall) { 165 | this.functionCall = functionCall; 166 | return this; 167 | } 168 | 169 | public AssistantMessage build() { 170 | return new AssistantMessage(this); 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/ChatCompletionChoice.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.Objects; 12 | 13 | @JsonDeserialize(builder = ChatCompletionChoice.Builder.class) 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 16 | public final class ChatCompletionChoice { 17 | 18 | @JsonProperty 19 | private final Integer index; 20 | @JsonProperty 21 | private final AssistantMessage message; 22 | @JsonProperty 23 | private final Delta delta; 24 | @JsonProperty 25 | private final String finishReason; 26 | 27 | private ChatCompletionChoice(Builder builder) { 28 | this.index = builder.index; 29 | this.message = builder.message; 30 | this.delta = builder.delta; 31 | this.finishReason = builder.finishReason; 32 | } 33 | 34 | public Integer index() { 35 | return index; 36 | } 37 | 38 | public AssistantMessage message() { 39 | return message; 40 | } 41 | 42 | public Delta delta() { 43 | return delta; 44 | } 45 | 46 | public String finishReason() { 47 | return finishReason; 48 | } 49 | 50 | @Override 51 | public boolean equals(Object another) { 52 | if (this == another) return true; 53 | return another instanceof ChatCompletionChoice 54 | && equalTo((ChatCompletionChoice) another); 55 | } 56 | 57 | private boolean equalTo(ChatCompletionChoice another) { 58 | return Objects.equals(index, another.index) 59 | && Objects.equals(message, another.message) 60 | && Objects.equals(delta, another.delta) 61 | && Objects.equals(finishReason, another.finishReason); 62 | } 63 | 64 | @Override 65 | public int hashCode() { 66 | int h = 5381; 67 | h += (h << 5) + Objects.hashCode(index); 68 | h += (h << 5) + Objects.hashCode(message); 69 | h += (h << 5) + Objects.hashCode(delta); 70 | h += (h << 5) + Objects.hashCode(finishReason); 71 | return h; 72 | } 73 | 74 | @Override 75 | public String toString() { 76 | return "ChatCompletionChoice{" 77 | + "index=" + index 78 | + ", message=" + message 79 | + ", delta=" + delta 80 | + ", finishReason=" + finishReason 81 | + "}"; 82 | } 83 | 84 | public static Builder builder() { 85 | return new Builder(); 86 | } 87 | 88 | @JsonPOJOBuilder(withPrefix = "") 89 | @JsonIgnoreProperties(ignoreUnknown = true) 90 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 91 | public static final class Builder { 92 | 93 | private Integer index; 94 | private AssistantMessage message; 95 | private Delta delta; 96 | private String finishReason; 97 | 98 | private Builder() { 99 | } 100 | 101 | public Builder index(Integer index) { 102 | this.index = index; 103 | return this; 104 | } 105 | 106 | public Builder message(AssistantMessage message) { 107 | this.message = message; 108 | return this; 109 | } 110 | 111 | public Builder delta(Delta delta) { 112 | this.delta = delta; 113 | return this; 114 | } 115 | 116 | public Builder finishReason(String finishReason) { 117 | this.finishReason = finishReason; 118 | return this; 119 | } 120 | 121 | public ChatCompletionChoice build() { 122 | return new ChatCompletionChoice(this); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/ChatCompletionModel.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | public enum ChatCompletionModel { 4 | 5 | GPT_4O("gpt-4o"), // alias 6 | GPT_4O_2024_05_13("gpt-4o-2024-05-13"), 7 | GPT_4O_2024_08_06("gpt-4o-2024-08-06"), // structured outputs in json mode 8 | 9 | GPT_4O_MINI("gpt-4o-mini"), // alias 10 | GPT_4O_MINI_2024_07_18("gpt-4o-mini-2024-07-18"), // structured outputs in json mode 11 | 12 | GPT_3_5_TURBO("gpt-3.5-turbo"), // alias 13 | GPT_3_5_TURBO_1106("gpt-3.5-turbo-1106"), 14 | GPT_3_5_TURBO_0125("gpt-3.5-turbo-0125"), 15 | 16 | GPT_4("gpt-4"), // alias 17 | GPT_4_0613("gpt-4-0613"), 18 | 19 | GPT_4_TURBO("gpt-4-turbo"), // alias 20 | GPT_4_TURBO_2024_04_09("gpt-4-turbo-2024-04-09"), // With vision support 21 | GPT_4_TURBO_PREVIEW("gpt-4-turbo-preview"), // alias 22 | GPT_4_1106_PREVIEW("gpt-4-1106-preview"), 23 | GPT_4_0125_PREVIEW("gpt-4-0125-preview"), 24 | 25 | GPT_4_32K("gpt-4-32k"), // alias 26 | GPT_4_32K_0314("gpt-4-32k-0314"), 27 | GPT_4_32K_0613("gpt-4-32k-0613"); 28 | 29 | private final String value; 30 | 31 | ChatCompletionModel(String value) { 32 | this.value = value; 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return value; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/Content.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.Objects; 12 | 13 | @JsonDeserialize(builder = Content.Builder.class) 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 16 | public final class Content { 17 | 18 | @JsonProperty 19 | private final ContentType type; 20 | @JsonProperty 21 | private final String text; 22 | @JsonProperty 23 | private final ImageUrl imageUrl; 24 | @JsonProperty 25 | private final InputAudio inputAudio; 26 | 27 | public Content(Builder builder) { 28 | this.type = builder.type; 29 | this.text = builder.text; 30 | this.imageUrl = builder.imageUrl; 31 | this.inputAudio = builder.inputAudio; 32 | } 33 | 34 | public ContentType type() { 35 | return type; 36 | } 37 | 38 | public String text() { 39 | return text; 40 | } 41 | 42 | public ImageUrl imageUrl() { 43 | return imageUrl; 44 | } 45 | 46 | public InputAudio inputAudio() { 47 | return inputAudio; 48 | } 49 | 50 | @Override 51 | public boolean equals(Object another) { 52 | if (this == another) return true; 53 | return another instanceof Content 54 | && equalTo((Content) another); 55 | } 56 | 57 | private boolean equalTo(Content another) { 58 | return Objects.equals(type, another.type) 59 | && Objects.equals(text, another.text) 60 | && Objects.equals(imageUrl, another.imageUrl) 61 | && Objects.equals(inputAudio, another.inputAudio); 62 | } 63 | 64 | @Override 65 | public int hashCode() { 66 | int h = 5381; 67 | h += (h << 5) + Objects.hashCode(type); 68 | h += (h << 5) + Objects.hashCode(text); 69 | h += (h << 5) + Objects.hashCode(imageUrl); 70 | h += (h << 5) + Objects.hashCode(inputAudio); 71 | return h; 72 | } 73 | 74 | @Override 75 | public String toString() { 76 | return "Content{" + 77 | "type=" + type + 78 | ", text=" + text + 79 | ", imageUrl=" + imageUrl + 80 | ", inputAudio=" + inputAudio + 81 | "}"; 82 | } 83 | 84 | public static Builder builder() { 85 | return new Builder(); 86 | } 87 | 88 | @JsonPOJOBuilder(withPrefix = "") 89 | @JsonIgnoreProperties(ignoreUnknown = true) 90 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 91 | public static final class Builder { 92 | 93 | private ContentType type; 94 | private String text; 95 | private ImageUrl imageUrl; 96 | private InputAudio inputAudio; 97 | 98 | public Builder type(ContentType type) { 99 | this.type = type; 100 | return this; 101 | } 102 | 103 | public Builder text(String text) { 104 | this.text = text; 105 | return this; 106 | } 107 | 108 | public Builder imageUrl(ImageUrl imageUrl) { 109 | this.imageUrl = imageUrl; 110 | return this; 111 | } 112 | 113 | public Builder inputAudio(InputAudio inputAudio) { 114 | this.inputAudio = inputAudio; 115 | return this; 116 | } 117 | 118 | public Content build() { 119 | return new Content(this); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/ContentType.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public enum ContentType { 6 | 7 | @JsonProperty("text") 8 | TEXT, 9 | @JsonProperty("image_url") 10 | IMAGE_URL, 11 | @JsonProperty("input_audio") 12 | AUDIO 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/Delta.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.List; 12 | import java.util.Objects; 13 | 14 | import static java.util.Collections.unmodifiableList; 15 | 16 | @JsonDeserialize(builder = Delta.Builder.class) 17 | @JsonInclude(JsonInclude.Include.NON_NULL) 18 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 19 | public final class Delta { 20 | 21 | @JsonProperty 22 | private final Role role; 23 | @JsonProperty 24 | private final String content; 25 | @JsonProperty 26 | private final List toolCalls; 27 | @JsonProperty 28 | @Deprecated 29 | private final FunctionCall functionCall; 30 | 31 | private Delta(Builder builder) { 32 | this.role = builder.role; 33 | this.content = builder.content; 34 | this.toolCalls = builder.toolCalls; 35 | this.functionCall = builder.functionCall; 36 | } 37 | 38 | public Role role() { 39 | return role; 40 | } 41 | 42 | public String content() { 43 | return content; 44 | } 45 | 46 | public List toolCalls() { 47 | return toolCalls; 48 | } 49 | 50 | @Deprecated 51 | public FunctionCall functionCall() { 52 | return functionCall; 53 | } 54 | 55 | @Override 56 | public boolean equals(Object another) { 57 | if (this == another) return true; 58 | return another instanceof Delta 59 | && equalTo((Delta) another); 60 | } 61 | 62 | private boolean equalTo(Delta another) { 63 | return Objects.equals(role, another.role) 64 | && Objects.equals(content, another.content) 65 | && Objects.equals(toolCalls, another.toolCalls) 66 | && Objects.equals(functionCall, another.functionCall); 67 | } 68 | 69 | @Override 70 | public int hashCode() { 71 | int h = 5381; 72 | h += (h << 5) + Objects.hashCode(role); 73 | h += (h << 5) + Objects.hashCode(content); 74 | h += (h << 5) + Objects.hashCode(toolCalls); 75 | h += (h << 5) + Objects.hashCode(functionCall); 76 | return h; 77 | } 78 | 79 | @Override 80 | public String toString() { 81 | return "Delta{" 82 | + "role=" + role 83 | + ", content=" + content 84 | + ", toolCalls=" + toolCalls 85 | + ", functionCall=" + functionCall 86 | + "}"; 87 | } 88 | 89 | public static Builder builder() { 90 | return new Builder(); 91 | } 92 | 93 | @JsonPOJOBuilder(withPrefix = "") 94 | @JsonIgnoreProperties(ignoreUnknown = true) 95 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 96 | public static final class Builder { 97 | 98 | private Role role; 99 | private String content; 100 | private List toolCalls; 101 | @Deprecated 102 | private FunctionCall functionCall; 103 | 104 | private Builder() { 105 | } 106 | 107 | public Builder role(Role role) { 108 | this.role = role; 109 | return this; 110 | } 111 | 112 | public Builder content(String content) { 113 | this.content = content; 114 | return this; 115 | } 116 | 117 | public Builder toolCalls(List toolCalls) { 118 | if (toolCalls != null) { 119 | this.toolCalls = unmodifiableList(toolCalls); 120 | } 121 | return this; 122 | } 123 | 124 | @Deprecated 125 | public Builder functionCall(FunctionCall functionCall) { 126 | this.functionCall = functionCall; 127 | return this; 128 | } 129 | 130 | 131 | public Delta build() { 132 | return new Delta(this); 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/Function.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.Objects; 12 | 13 | @JsonDeserialize(builder = Function.Builder.class) 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 16 | public class Function { 17 | 18 | @JsonProperty 19 | private final String name; 20 | @JsonProperty 21 | private final String description; 22 | @JsonProperty 23 | private final Boolean strict; 24 | @JsonProperty 25 | private final JsonObjectSchema parameters; 26 | 27 | private Function(Builder builder) { 28 | this.name = builder.name; 29 | this.description = builder.description; 30 | this.strict = builder.strict; 31 | this.parameters = builder.parameters; 32 | } 33 | 34 | public String name() { 35 | return name; 36 | } 37 | 38 | public String description() { 39 | return description; 40 | } 41 | 42 | public Boolean strict() { 43 | return strict; 44 | } 45 | 46 | public JsonObjectSchema parameters() { 47 | return parameters; 48 | } 49 | 50 | @Override 51 | public boolean equals(Object another) { 52 | if (this == another) return true; 53 | return another instanceof Function 54 | && equalTo((Function) another); 55 | } 56 | 57 | private boolean equalTo(Function another) { 58 | return Objects.equals(name, another.name) 59 | && Objects.equals(description, another.description) 60 | && Objects.equals(strict, another.strict) 61 | && Objects.equals(parameters, another.parameters); 62 | } 63 | 64 | @Override 65 | public int hashCode() { 66 | int h = 5381; 67 | h += (h << 5) + Objects.hashCode(name); 68 | h += (h << 5) + Objects.hashCode(description); 69 | h += (h << 5) + Objects.hashCode(strict); 70 | h += (h << 5) + Objects.hashCode(parameters); 71 | return h; 72 | } 73 | 74 | @Override 75 | public String toString() { 76 | return "Function{" 77 | + "name=" + name 78 | + ", description=" + description 79 | + ", strict=" + strict 80 | + ", parameters=" + parameters 81 | + "}"; 82 | } 83 | 84 | public static Builder builder() { 85 | return new Builder(); 86 | } 87 | 88 | @JsonPOJOBuilder(withPrefix = "") 89 | @JsonIgnoreProperties(ignoreUnknown = true) 90 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 91 | public static final class Builder { 92 | 93 | private String name; 94 | private String description; 95 | private Boolean strict; 96 | private JsonObjectSchema parameters = JsonObjectSchema.builder().build(); 97 | 98 | private Builder() { 99 | } 100 | 101 | public Builder name(String name) { 102 | this.name = name; 103 | return this; 104 | } 105 | 106 | public Builder description(String description) { 107 | this.description = description; 108 | return this; 109 | } 110 | 111 | public Builder strict(Boolean strict) { 112 | this.strict = strict; 113 | return this; 114 | } 115 | 116 | public Builder parameters(JsonObjectSchema parameters) { 117 | this.parameters = parameters; 118 | return this; 119 | } 120 | 121 | public Function build() { 122 | return new Function(this); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/FunctionCall.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.Objects; 12 | 13 | @JsonDeserialize(builder = FunctionCall.Builder.class) 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 16 | public class FunctionCall { 17 | 18 | @JsonProperty 19 | private final String name; 20 | @JsonProperty 21 | private final String arguments; 22 | 23 | private FunctionCall(Builder builder) { 24 | this.name = builder.name; 25 | this.arguments = builder.arguments; 26 | } 27 | 28 | public String name() { 29 | return name; 30 | } 31 | 32 | public String arguments() { 33 | return arguments; 34 | } 35 | 36 | @Override 37 | public boolean equals(Object another) { 38 | if (this == another) return true; 39 | return another instanceof FunctionCall 40 | && equalTo((FunctionCall) another); 41 | } 42 | 43 | private boolean equalTo(FunctionCall another) { 44 | return Objects.equals(name, another.name) 45 | && Objects.equals(arguments, another.arguments); 46 | } 47 | 48 | @Override 49 | public int hashCode() { 50 | int h = 5381; 51 | h += (h << 5) + Objects.hashCode(name); 52 | h += (h << 5) + Objects.hashCode(arguments); 53 | return h; 54 | } 55 | 56 | @Override 57 | public String toString() { 58 | return "FunctionCall{" 59 | + "name=" + name 60 | + ", arguments=" + arguments 61 | + "}"; 62 | } 63 | 64 | public static Builder builder() { 65 | return new Builder(); 66 | } 67 | 68 | @JsonPOJOBuilder(withPrefix = "") 69 | @JsonIgnoreProperties(ignoreUnknown = true) 70 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 71 | public static final class Builder { 72 | 73 | private String name; 74 | private String arguments; 75 | 76 | private Builder() { 77 | } 78 | 79 | public Builder name(String name) { 80 | this.name = name; 81 | return this; 82 | } 83 | 84 | public Builder arguments(String arguments) { 85 | this.arguments = arguments; 86 | return this; 87 | } 88 | 89 | public FunctionCall build() { 90 | return new FunctionCall(this); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/FunctionCallUtil.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import dev.ai4j.openai4j.Json; 4 | 5 | import java.util.Map; 6 | 7 | public class FunctionCallUtil { 8 | 9 | public static T argument(String name, FunctionCall function) { 10 | Map arguments = argumentsAsMap(function.arguments()); // TODO cache? 11 | return (T) arguments.get(name); 12 | } 13 | 14 | public static Map argumentsAsMap(String arguments) { 15 | return Json.fromJson(arguments, Map.class); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/FunctionMessage.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.Objects; 12 | 13 | import static dev.ai4j.openai4j.chat.Role.FUNCTION; 14 | 15 | @JsonDeserialize(builder = FunctionMessage.Builder.class) 16 | @JsonInclude(JsonInclude.Include.NON_NULL) 17 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 18 | @Deprecated 19 | public final class FunctionMessage implements Message { 20 | 21 | @JsonProperty 22 | private final Role role = FUNCTION; 23 | @JsonProperty 24 | private final String name; 25 | @JsonProperty 26 | private final String content; 27 | 28 | private FunctionMessage(Builder builder) { 29 | this.name = builder.name; 30 | this.content = builder.content; 31 | } 32 | 33 | public Role role() { 34 | return role; 35 | } 36 | 37 | public String name() { 38 | return name; 39 | } 40 | 41 | public String content() { 42 | return content; 43 | } 44 | 45 | @Override 46 | public boolean equals(Object another) { 47 | if (this == another) return true; 48 | return another instanceof FunctionMessage 49 | && equalTo((FunctionMessage) another); 50 | } 51 | 52 | private boolean equalTo(FunctionMessage another) { 53 | return Objects.equals(role, another.role) 54 | && Objects.equals(name, another.name) 55 | && Objects.equals(content, another.content); 56 | } 57 | 58 | @Override 59 | public int hashCode() { 60 | int h = 5381; 61 | h += (h << 5) + Objects.hashCode(role); 62 | h += (h << 5) + Objects.hashCode(name); 63 | h += (h << 5) + Objects.hashCode(content); 64 | return h; 65 | } 66 | 67 | @Override 68 | public String toString() { 69 | return "FunctionMessage{" 70 | + "role=" + role 71 | + ", name=" + name 72 | + ", content=" + content 73 | + "}"; 74 | } 75 | 76 | @Deprecated 77 | public static FunctionMessage from(String name, String content) { 78 | return FunctionMessage.builder() 79 | .name(name) 80 | .content(content) 81 | .build(); 82 | } 83 | 84 | @Deprecated 85 | public static Builder builder() { 86 | return new Builder(); 87 | } 88 | 89 | @JsonPOJOBuilder(withPrefix = "") 90 | @JsonIgnoreProperties(ignoreUnknown = true) 91 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 92 | public static final class Builder { 93 | 94 | private String name; 95 | private String content; 96 | 97 | private Builder() { 98 | } 99 | 100 | public Builder name(String name) { 101 | this.name = name; 102 | return this; 103 | } 104 | 105 | public Builder content(String content) { 106 | this.content = content; 107 | return this; 108 | } 109 | 110 | public FunctionMessage build() { 111 | return new FunctionMessage(this); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/ImageDetail.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public enum ImageDetail { 6 | 7 | @JsonProperty("low") 8 | LOW, 9 | @JsonProperty("high") 10 | HIGH, 11 | @JsonProperty("auto") 12 | AUTO 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/ImageUrl.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.Objects; 12 | 13 | @JsonDeserialize(builder = ImageUrl.Builder.class) 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 16 | public class ImageUrl { 17 | 18 | @JsonProperty 19 | private final String url; 20 | @JsonProperty 21 | private final ImageDetail detail; 22 | 23 | private ImageUrl(Builder builder) { 24 | this.url = builder.url; 25 | this.detail = builder.detail; 26 | } 27 | 28 | @Override 29 | public boolean equals(Object another) { 30 | if (this == another) return true; 31 | return another instanceof ImageUrl 32 | && equalTo((ImageUrl) another); 33 | } 34 | 35 | private boolean equalTo(ImageUrl another) { 36 | return Objects.equals(url, another.url) 37 | && Objects.equals(detail, another.detail); 38 | } 39 | 40 | @Override 41 | public int hashCode() { 42 | int h = 5381; 43 | h += (h << 5) + Objects.hashCode(url); 44 | h += (h << 5) + Objects.hashCode(detail); 45 | return h; 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return "ImageUrl{" + 51 | "url=" + url + 52 | ", detail=" + detail + 53 | "}"; 54 | } 55 | 56 | public static Builder builder() { 57 | return new Builder(); 58 | } 59 | 60 | @JsonPOJOBuilder(withPrefix = "") 61 | @JsonIgnoreProperties(ignoreUnknown = true) 62 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 63 | public static final class Builder { 64 | 65 | private String url; 66 | private ImageDetail detail; 67 | 68 | private Builder() { 69 | } 70 | 71 | public Builder url(String url) { 72 | this.url = url; 73 | return this; 74 | } 75 | 76 | public Builder detail(ImageDetail detail) { 77 | this.detail = detail; 78 | return this; 79 | } 80 | 81 | public ImageUrl build() { 82 | return new ImageUrl(this); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/InputAudio.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 7 | 8 | import java.util.Objects; 9 | 10 | @JsonDeserialize(builder = InputAudio.Builder.class) 11 | @JsonInclude(JsonInclude.Include.NON_NULL) 12 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 13 | public class InputAudio { 14 | 15 | private final String data; 16 | private final String format; 17 | 18 | private InputAudio(Builder builder) { 19 | data = builder.data; 20 | format = builder.format; 21 | } 22 | 23 | public String getData() { 24 | return data; 25 | } 26 | 27 | public String getFormat() { 28 | return format; 29 | } 30 | 31 | @Override 32 | public boolean equals(Object another) { 33 | if (this == another) return true; 34 | return another instanceof InputAudio 35 | && equalTo((InputAudio) another); 36 | } 37 | 38 | private boolean equalTo(InputAudio another) { 39 | return Objects.equals(data, another.data) 40 | && Objects.equals(format, another.format); 41 | } 42 | 43 | @Override 44 | public int hashCode() { 45 | int h = 5381; 46 | h += (h << 5) + Objects.hashCode(data); 47 | h += (h << 5) + Objects.hashCode(format); 48 | return h; 49 | } 50 | 51 | @Override 52 | public String toString() { 53 | return "InputAudio{" + 54 | "data=" + data + 55 | ", format=" + format + 56 | "}"; 57 | } 58 | 59 | public static Builder builder() { 60 | return new Builder(); 61 | } 62 | 63 | public static final class Builder { 64 | 65 | private String data; 66 | private String format; 67 | 68 | public Builder data(String data) { 69 | this.data = data; 70 | return this; 71 | } 72 | 73 | public Builder format(String format) { 74 | this.format = format; 75 | return this; 76 | } 77 | 78 | public static Builder builder() { 79 | return new Builder(); 80 | } 81 | 82 | public InputAudio build() { 83 | return new InputAudio(this); 84 | } 85 | 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/JsonAnyOfSchema.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.List; 12 | import java.util.Objects; 13 | 14 | @JsonDeserialize(builder = JsonAnyOfSchema.Builder.class) 15 | @JsonInclude(JsonInclude.Include.NON_NULL) 16 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 17 | public class JsonAnyOfSchema extends JsonSchemaElement { 18 | 19 | @JsonProperty 20 | private final String description; 21 | @JsonProperty("anyOf") 22 | private final List anyOf; 23 | 24 | public JsonAnyOfSchema(Builder builder) { 25 | super(null); 26 | this.description = builder.description; 27 | this.anyOf = builder.anyOf; 28 | } 29 | 30 | @Override 31 | public boolean equals(final Object another) { 32 | if (this == another) return true; 33 | return another instanceof JsonAnyOfSchema 34 | && equalTo((JsonAnyOfSchema) another); 35 | } 36 | 37 | private boolean equalTo(final JsonAnyOfSchema another) { 38 | return Objects.equals(description, another.description) 39 | && Objects.equals(anyOf, another.anyOf); 40 | } 41 | 42 | @Override 43 | public int hashCode() { 44 | int h = 5381; 45 | h += (h << 5) + Objects.hashCode(description); 46 | h += (h << 5) + Objects.hashCode(anyOf); 47 | return h; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return "JsonAnyOfSchema{" + 53 | "description='" + description + '\'' + 54 | ", anyOf=" + anyOf + 55 | '}'; 56 | } 57 | 58 | public static Builder builder(){ 59 | return new Builder(); 60 | } 61 | 62 | @JsonPOJOBuilder(withPrefix = "") 63 | @JsonIgnoreProperties(ignoreUnknown = true) 64 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 65 | public static class Builder { 66 | 67 | private String description; 68 | private List anyOf; 69 | 70 | public Builder description(String description) { 71 | this.description = description; 72 | return this; 73 | } 74 | 75 | public Builder anyOf(List anyOf) { 76 | this.anyOf = anyOf; 77 | return this; 78 | } 79 | 80 | public JsonAnyOfSchema build() { 81 | return new JsonAnyOfSchema(this); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/JsonArraySchema.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.Objects; 12 | 13 | @JsonDeserialize(builder = JsonArraySchema.Builder.class) 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 16 | public class JsonArraySchema extends JsonSchemaElement { 17 | 18 | @JsonProperty 19 | private final String description; 20 | @JsonProperty 21 | private final JsonSchemaElement items; 22 | 23 | public JsonArraySchema(Builder builder) { 24 | super("array"); 25 | this.description = builder.description; 26 | this.items = builder.items; 27 | } 28 | 29 | @Override 30 | public boolean equals(Object another) { 31 | if (this == another) return true; 32 | return another instanceof JsonArraySchema 33 | && equalTo((JsonArraySchema) another); 34 | } 35 | 36 | private boolean equalTo(JsonArraySchema another) { 37 | return Objects.equals(description, another.description) 38 | && Objects.equals(items, another.items); 39 | } 40 | 41 | @Override 42 | public int hashCode() { 43 | int h = 5381; 44 | h += (h << 5) + Objects.hashCode(description); 45 | h += (h << 5) + Objects.hashCode(items); 46 | return h; 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | return "JsonArraySchema{" + 52 | "description=" + description + 53 | ", items=" + items + 54 | "}"; 55 | } 56 | 57 | public static Builder builder() { 58 | return new Builder(); 59 | } 60 | 61 | @JsonPOJOBuilder(withPrefix = "") 62 | @JsonIgnoreProperties(ignoreUnknown = true) 63 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 64 | public static class Builder { 65 | 66 | private String description; 67 | private JsonSchemaElement items; 68 | 69 | public Builder description(String description) { 70 | this.description = description; 71 | return this; 72 | } 73 | 74 | public Builder items(JsonSchemaElement items) { 75 | this.items = items; 76 | return this; 77 | } 78 | 79 | public JsonArraySchema build() { 80 | return new JsonArraySchema(this); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/JsonBooleanSchema.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.Objects; 12 | 13 | @JsonDeserialize(builder = JsonBooleanSchema.Builder.class) 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 16 | public class JsonBooleanSchema extends JsonSchemaElement { 17 | 18 | @JsonProperty 19 | private final String description; 20 | 21 | public JsonBooleanSchema(Builder builder) { 22 | super("boolean"); 23 | this.description = builder.description; 24 | } 25 | 26 | @Override 27 | public boolean equals(Object another) { 28 | if (this == another) return true; 29 | return another instanceof JsonBooleanSchema 30 | && equalTo((JsonBooleanSchema) another); 31 | } 32 | 33 | private boolean equalTo(JsonBooleanSchema another) { 34 | return Objects.equals(description, another.description); 35 | } 36 | 37 | @Override 38 | public int hashCode() { 39 | int h = 5381; 40 | h += (h << 5) + Objects.hashCode(description); 41 | return h; 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "JsonBooleanSchema{" + 47 | "description=" + description + 48 | "}"; 49 | } 50 | 51 | public static Builder builder() { 52 | return new Builder(); 53 | } 54 | 55 | @JsonPOJOBuilder(withPrefix = "") 56 | @JsonIgnoreProperties(ignoreUnknown = true) 57 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 58 | public static class Builder { 59 | 60 | private String description; 61 | 62 | public Builder description(String description) { 63 | this.description = description; 64 | return this; 65 | } 66 | 67 | public JsonBooleanSchema build() { 68 | return new JsonBooleanSchema(this); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/JsonEnumSchema.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.Objects; 14 | 15 | import static java.util.Arrays.stream; 16 | import static java.util.stream.Collectors.toList; 17 | 18 | @JsonDeserialize(builder = JsonEnumSchema.Builder.class) 19 | @JsonInclude(JsonInclude.Include.NON_NULL) 20 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 21 | public class JsonEnumSchema extends JsonSchemaElement { 22 | 23 | @JsonProperty 24 | private final String description; 25 | @JsonProperty("enum") 26 | private final List enumValues; 27 | 28 | public JsonEnumSchema(Builder builder) { 29 | super("string"); 30 | this.description = builder.description; 31 | this.enumValues = new ArrayList<>(builder.enumValues); 32 | } 33 | 34 | @Override 35 | public boolean equals(Object another) { 36 | if (this == another) return true; 37 | return another instanceof JsonEnumSchema 38 | && equalTo((JsonEnumSchema) another); 39 | } 40 | 41 | private boolean equalTo(JsonEnumSchema another) { 42 | return Objects.equals(description, another.description) 43 | && Objects.equals(enumValues, another.enumValues); 44 | } 45 | 46 | @Override 47 | public int hashCode() { 48 | int h = 5381; 49 | h += (h << 5) + Objects.hashCode(description); 50 | h += (h << 5) + Objects.hashCode(enumValues); 51 | return h; 52 | } 53 | 54 | @Override 55 | public String toString() { 56 | return "JsonEnumSchema{" + 57 | "description=" + description + 58 | ", enumValues=" + enumValues + 59 | "}"; 60 | } 61 | 62 | public static Builder builder() { 63 | return new Builder(); 64 | } 65 | 66 | @JsonPOJOBuilder(withPrefix = "") 67 | @JsonIgnoreProperties(ignoreUnknown = true) 68 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 69 | public static class Builder { 70 | 71 | private String description; 72 | private List enumValues; 73 | 74 | public Builder description(String description) { 75 | this.description = description; 76 | return this; 77 | } 78 | 79 | public Builder enumValues(List enumValues) { 80 | this.enumValues = enumValues; 81 | return this; 82 | } 83 | 84 | public Builder enumValues(Class enumClass) { 85 | if (!enumClass.isEnum()) { 86 | throw new RuntimeException("Class " + enumClass.getName() + " must be enum"); 87 | } 88 | 89 | List enumValues = stream(enumClass.getEnumConstants()) 90 | .map(Object::toString) 91 | .collect(toList()); 92 | 93 | return enumValues(enumValues); 94 | } 95 | 96 | public JsonEnumSchema build() { 97 | return new JsonEnumSchema(this); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/JsonIntegerSchema.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.Objects; 12 | 13 | @JsonDeserialize(builder = JsonIntegerSchema.Builder.class) 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 16 | public class JsonIntegerSchema extends JsonSchemaElement { 17 | 18 | @JsonProperty 19 | private final String description; 20 | 21 | public JsonIntegerSchema(Builder builder) { 22 | super("integer"); 23 | this.description = builder.description; 24 | } 25 | 26 | @Override 27 | public boolean equals(Object another) { 28 | if (this == another) return true; 29 | return another instanceof JsonIntegerSchema 30 | && equalTo((JsonIntegerSchema) another); 31 | } 32 | 33 | private boolean equalTo(JsonIntegerSchema another) { 34 | return Objects.equals(description, another.description); 35 | } 36 | 37 | @Override 38 | public int hashCode() { 39 | int h = 5381; 40 | h += (h << 5) + Objects.hashCode(description); 41 | return h; 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "JsonIntegerSchema{" + 47 | "description=" + description + 48 | "}"; 49 | } 50 | 51 | public static Builder builder() { 52 | return new Builder(); 53 | } 54 | 55 | @JsonPOJOBuilder(withPrefix = "") 56 | @JsonIgnoreProperties(ignoreUnknown = true) 57 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 58 | public static class Builder { 59 | 60 | private String description; 61 | 62 | public Builder description(String description) { 63 | this.description = description; 64 | return this; 65 | } 66 | 67 | public JsonIntegerSchema build() { 68 | return new JsonIntegerSchema(this); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/JsonNumberSchema.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.Objects; 12 | 13 | @JsonDeserialize(builder = JsonNumberSchema.Builder.class) 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 16 | public class JsonNumberSchema extends JsonSchemaElement { 17 | 18 | @JsonProperty 19 | private final String description; 20 | 21 | public JsonNumberSchema(Builder builder) { 22 | super("number"); 23 | this.description = builder.description; 24 | } 25 | 26 | @Override 27 | public boolean equals(Object another) { 28 | if (this == another) return true; 29 | return another instanceof JsonNumberSchema 30 | && equalTo((JsonNumberSchema) another); 31 | } 32 | 33 | private boolean equalTo(JsonNumberSchema another) { 34 | return Objects.equals(description, another.description); 35 | } 36 | 37 | @Override 38 | public int hashCode() { 39 | int h = 5381; 40 | h += (h << 5) + Objects.hashCode(description); 41 | return h; 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "JsonNumberSchema{" + 47 | "description=" + description + 48 | "}"; 49 | } 50 | 51 | public static Builder builder() { 52 | return new Builder(); 53 | } 54 | 55 | @JsonPOJOBuilder(withPrefix = "") 56 | @JsonIgnoreProperties(ignoreUnknown = true) 57 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 58 | public static class Builder { 59 | 60 | private String description; 61 | 62 | public Builder description(String description) { 63 | this.description = description; 64 | return this; 65 | } 66 | 67 | public JsonNumberSchema build() { 68 | return new JsonNumberSchema(this); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/JsonObjectSchema.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.*; 12 | 13 | @JsonDeserialize(builder = JsonObjectSchema.Builder.class) 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 16 | public class JsonObjectSchema extends JsonSchemaElement { 17 | 18 | @JsonProperty 19 | private final String description; 20 | @JsonProperty 21 | private final Map properties; 22 | @JsonProperty 23 | private final List required; 24 | @JsonProperty("additionalProperties") 25 | private final Boolean additionalProperties; 26 | @JsonProperty("$defs") 27 | private final Map definitions; 28 | 29 | public JsonObjectSchema(Builder builder) { 30 | super("object"); 31 | this.description = builder.description; 32 | this.properties = new LinkedHashMap<>(builder.properties); 33 | this.required = new ArrayList<>(builder.required); 34 | this.additionalProperties = builder.additionalProperties; 35 | this.definitions = builder.definitions == null ? null : new LinkedHashMap<>(builder.definitions); 36 | } 37 | 38 | @Override 39 | public boolean equals(Object another) { 40 | if (this == another) return true; 41 | return another instanceof JsonObjectSchema 42 | && equalTo((JsonObjectSchema) another); 43 | } 44 | 45 | private boolean equalTo(JsonObjectSchema another) { 46 | return Objects.equals(description, another.description) 47 | && Objects.equals(properties, another.properties) 48 | && Objects.equals(required, another.required) 49 | && Objects.equals(additionalProperties, another.additionalProperties) 50 | && Objects.equals(definitions, another.definitions); 51 | } 52 | 53 | @Override 54 | public int hashCode() { 55 | int h = 5381; 56 | h += (h << 5) + Objects.hashCode(description); 57 | h += (h << 5) + Objects.hashCode(properties); 58 | h += (h << 5) + Objects.hashCode(required); 59 | h += (h << 5) + Objects.hashCode(additionalProperties); 60 | h += (h << 5) + Objects.hashCode(definitions); 61 | return h; 62 | } 63 | 64 | @Override 65 | public String toString() { 66 | return "JsonObjectSchema{" + 67 | "description=" + description + 68 | ", properties=" + properties + 69 | ", required=" + required + 70 | ", additionalProperties=" + additionalProperties + 71 | ", definitions=" + definitions + 72 | "}"; 73 | } 74 | 75 | public static Builder builder() { 76 | return new Builder(); 77 | } 78 | 79 | @JsonPOJOBuilder(withPrefix = "") 80 | @JsonIgnoreProperties(ignoreUnknown = true) 81 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 82 | public static class Builder { 83 | 84 | private String description; 85 | private Map properties = new LinkedHashMap<>(); 86 | private List required = new ArrayList<>(); 87 | private Boolean additionalProperties; 88 | private Map definitions; 89 | 90 | public Builder description(String description) { 91 | this.description = description; 92 | return this; 93 | } 94 | 95 | public Builder properties(Map properties) { 96 | this.properties = properties; 97 | return this; 98 | } 99 | 100 | public Builder required(List required) { 101 | this.required = required; 102 | return this; 103 | } 104 | 105 | public Builder additionalProperties(Boolean additionalProperties) { 106 | this.additionalProperties = additionalProperties; 107 | return this; 108 | } 109 | 110 | public Builder definitions(Map definitions) { 111 | this.definitions = definitions; 112 | return this; 113 | } 114 | 115 | public JsonObjectSchema build() { 116 | return new JsonObjectSchema(this); 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/JsonReferenceSchema.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.Objects; 12 | 13 | @JsonDeserialize(builder = JsonReferenceSchema.Builder.class) 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 16 | public class JsonReferenceSchema extends JsonSchemaElement { 17 | 18 | @JsonProperty("$ref") 19 | private final String reference; 20 | 21 | public JsonReferenceSchema(Builder builder) { 22 | super(null); 23 | this.reference = builder.reference; 24 | } 25 | 26 | @Override 27 | public boolean equals(Object another) { 28 | if (this == another) return true; 29 | return another instanceof JsonReferenceSchema 30 | && equalTo((JsonReferenceSchema) another); 31 | } 32 | 33 | private boolean equalTo(JsonReferenceSchema another) { 34 | return Objects.equals(reference, another.reference); 35 | } 36 | 37 | @Override 38 | public int hashCode() { 39 | int h = 5381; 40 | h += (h << 5) + Objects.hashCode(reference); 41 | return h; 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "JsonReferenceSchema{" + 47 | "reference=" + reference + 48 | "}"; 49 | } 50 | 51 | public static Builder builder() { 52 | return new Builder(); 53 | } 54 | 55 | @JsonPOJOBuilder(withPrefix = "") 56 | @JsonIgnoreProperties(ignoreUnknown = true) 57 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 58 | public static class Builder { 59 | 60 | private String reference; 61 | 62 | public Builder reference(String reference) { 63 | this.reference = reference; 64 | return this; 65 | } 66 | 67 | public JsonReferenceSchema build() { 68 | return new JsonReferenceSchema(this); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/JsonSchema.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.Objects; 12 | 13 | @JsonDeserialize(builder = JsonSchema.Builder.class) 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 16 | public class JsonSchema { 17 | 18 | @JsonProperty 19 | private final String name; 20 | @JsonProperty 21 | private final Boolean strict; 22 | @JsonProperty 23 | private final JsonObjectSchema schema; 24 | 25 | public JsonSchema(Builder builder) { 26 | this.name = builder.name; 27 | this.strict = builder.strict; 28 | this.schema = builder.schema; 29 | } 30 | 31 | @Override 32 | public boolean equals(Object another) { 33 | if (this == another) return true; 34 | return another instanceof JsonSchema 35 | && equalTo((JsonSchema) another); 36 | } 37 | 38 | private boolean equalTo(JsonSchema another) { 39 | return Objects.equals(name, another.name) 40 | && Objects.equals(strict, another.strict) 41 | && Objects.equals(schema, another.schema); 42 | } 43 | 44 | @Override 45 | public int hashCode() { 46 | int h = 5381; 47 | h += (h << 5) + Objects.hashCode(name); 48 | h += (h << 5) + Objects.hashCode(strict); 49 | h += (h << 5) + Objects.hashCode(schema); 50 | return h; 51 | } 52 | 53 | @Override 54 | public String toString() { 55 | return "JsonSchema{" + 56 | "name=" + name + 57 | ", strict=" + strict + 58 | ", schema=" + schema + 59 | "}"; 60 | } 61 | 62 | public static Builder builder() { 63 | return new Builder(); 64 | } 65 | 66 | @JsonPOJOBuilder(withPrefix = "") 67 | @JsonIgnoreProperties(ignoreUnknown = true) 68 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 69 | public static class Builder { 70 | 71 | private String name; 72 | private Boolean strict; 73 | private JsonObjectSchema schema; 74 | 75 | public Builder name(String name) { 76 | this.name = name; 77 | return this; 78 | } 79 | 80 | public Builder strict(Boolean strict) { 81 | this.strict = strict; 82 | return this; 83 | } 84 | 85 | public Builder schema(JsonObjectSchema schema) { 86 | this.schema = schema; 87 | return this; 88 | } 89 | 90 | public JsonSchema build() { 91 | return new JsonSchema(this); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/JsonSchemaElement.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 6 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 7 | 8 | @JsonInclude(JsonInclude.Include.NON_NULL) 9 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 10 | public abstract class JsonSchemaElement { 11 | 12 | @JsonProperty 13 | private final String type; 14 | 15 | protected JsonSchemaElement(String type) { 16 | this.type = type; 17 | } 18 | 19 | public String type() { 20 | return type; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/JsonStringSchema.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.Objects; 12 | 13 | @JsonDeserialize(builder = JsonStringSchema.Builder.class) 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 16 | public class JsonStringSchema extends JsonSchemaElement { 17 | 18 | @JsonProperty 19 | private final String description; 20 | 21 | public JsonStringSchema(Builder builder) { 22 | super("string"); 23 | this.description = builder.description; 24 | } 25 | 26 | @Override 27 | public boolean equals(Object another) { 28 | if (this == another) return true; 29 | return another instanceof JsonStringSchema 30 | && equalTo((JsonStringSchema) another); 31 | } 32 | 33 | private boolean equalTo(JsonStringSchema another) { 34 | return Objects.equals(description, another.description); 35 | } 36 | 37 | @Override 38 | public int hashCode() { 39 | int h = 5381; 40 | h += (h << 5) + Objects.hashCode(description); 41 | return h; 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "JsonStringSchema{" + 47 | "description=" + description + 48 | "}"; 49 | } 50 | 51 | public static Builder builder() { 52 | return new Builder(); 53 | } 54 | 55 | @JsonPOJOBuilder(withPrefix = "") 56 | @JsonIgnoreProperties(ignoreUnknown = true) 57 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 58 | public static class Builder { 59 | 60 | private String description; 61 | 62 | public Builder description(String description) { 63 | this.description = description; 64 | return this; 65 | } 66 | 67 | public JsonStringSchema build() { 68 | return new JsonStringSchema(this); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/Message.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | public interface Message { 4 | 5 | Role role(); 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/ResponseFormat.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonInclude; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 8 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 9 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 10 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 11 | 12 | import java.util.Objects; 13 | 14 | @JsonDeserialize(builder = ResponseFormat.Builder.class) 15 | @JsonInclude(JsonInclude.Include.NON_NULL) 16 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 17 | public class ResponseFormat { 18 | 19 | @JsonProperty 20 | private final ResponseFormatType type; 21 | @JsonProperty 22 | private final JsonSchema jsonSchema; 23 | 24 | @JsonCreator 25 | public ResponseFormat(Builder builder) { 26 | this.type = builder.type; 27 | this.jsonSchema = builder.jsonSchema; 28 | } 29 | 30 | public ResponseFormatType type() { 31 | return type; 32 | } 33 | 34 | public JsonSchema jsonSchema() { 35 | return jsonSchema; 36 | } 37 | 38 | @Override 39 | public boolean equals(Object another) { 40 | if (this == another) return true; 41 | return another instanceof ResponseFormat 42 | && equalTo((ResponseFormat) another); 43 | } 44 | 45 | private boolean equalTo(ResponseFormat another) { 46 | return Objects.equals(type, another.type) 47 | && Objects.equals(jsonSchema, another.jsonSchema); 48 | } 49 | 50 | @Override 51 | public int hashCode() { 52 | int h = 5381; 53 | h += (h << 5) + Objects.hashCode(type); 54 | h += (h << 5) + Objects.hashCode(jsonSchema); 55 | return h; 56 | } 57 | 58 | @Override 59 | public String toString() { 60 | return "ResponseFormat{" + 61 | "type=" + type + 62 | ", jsonSchema=" + jsonSchema + 63 | "}"; 64 | } 65 | 66 | public static ResponseFormat.Builder builder() { 67 | return new ResponseFormat.Builder(); 68 | } 69 | 70 | @JsonPOJOBuilder(withPrefix = "") 71 | @JsonIgnoreProperties(ignoreUnknown = true) 72 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 73 | public static class Builder { 74 | 75 | private ResponseFormatType type; 76 | private JsonSchema jsonSchema; 77 | 78 | public ResponseFormat.Builder type(ResponseFormatType type) { 79 | this.type = type; 80 | return this; 81 | } 82 | 83 | public ResponseFormat.Builder jsonSchema(JsonSchema jsonSchema) { 84 | this.jsonSchema = jsonSchema; 85 | return this; 86 | } 87 | 88 | public ResponseFormat build() { 89 | return new ResponseFormat(this); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/ResponseFormatType.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public enum ResponseFormatType { 6 | 7 | @JsonProperty("text") 8 | TEXT, 9 | @JsonProperty("json_object") 10 | JSON_OBJECT, 11 | @JsonProperty("json_schema") 12 | JSON_SCHEMA 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/Role.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public enum Role { 6 | 7 | @JsonProperty("system") 8 | SYSTEM, 9 | @JsonProperty("user") 10 | USER, 11 | @JsonProperty("assistant") 12 | ASSISTANT, 13 | @JsonProperty("tool") 14 | TOOL, 15 | @JsonProperty("function") 16 | @Deprecated 17 | FUNCTION 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/SystemMessage.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.Objects; 12 | 13 | import static dev.ai4j.openai4j.chat.Role.SYSTEM; 14 | 15 | @JsonDeserialize(builder = SystemMessage.Builder.class) 16 | @JsonInclude(JsonInclude.Include.NON_NULL) 17 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 18 | public final class SystemMessage implements Message { 19 | 20 | @JsonProperty 21 | private final Role role = SYSTEM; 22 | @JsonProperty 23 | private final String content; 24 | @JsonProperty 25 | private final String name; 26 | 27 | private SystemMessage(Builder builder) { 28 | this.content = builder.content; 29 | this.name = builder.name; 30 | } 31 | 32 | public Role role() { 33 | return role; 34 | } 35 | 36 | public String content() { 37 | return content; 38 | } 39 | 40 | public String name() { 41 | return name; 42 | } 43 | 44 | @Override 45 | public boolean equals(Object another) { 46 | if (this == another) return true; 47 | return another instanceof SystemMessage 48 | && equalTo((SystemMessage) another); 49 | } 50 | 51 | private boolean equalTo(SystemMessage another) { 52 | return Objects.equals(role, another.role) 53 | && Objects.equals(content, another.content) 54 | && Objects.equals(name, another.name); 55 | } 56 | 57 | @Override 58 | public int hashCode() { 59 | int h = 5381; 60 | h += (h << 5) + Objects.hashCode(role); 61 | h += (h << 5) + Objects.hashCode(content); 62 | h += (h << 5) + Objects.hashCode(name); 63 | return h; 64 | } 65 | 66 | @Override 67 | public String toString() { 68 | return "SystemMessage{" 69 | + "role=" + role 70 | + ", content=" + content 71 | + ", name=" + name 72 | + "}"; 73 | } 74 | 75 | public static SystemMessage from(String content) { 76 | return SystemMessage.builder() 77 | .content(content) 78 | .build(); 79 | } 80 | 81 | public static Builder builder() { 82 | return new Builder(); 83 | } 84 | 85 | @JsonPOJOBuilder(withPrefix = "") 86 | @JsonIgnoreProperties(ignoreUnknown = true) 87 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 88 | public static final class Builder { 89 | 90 | private String content; 91 | private String name; 92 | 93 | private Builder() { 94 | } 95 | 96 | public Builder content(String content) { 97 | this.content = content; 98 | return this; 99 | } 100 | 101 | public Builder name(String name) { 102 | this.name = name; 103 | return this; 104 | } 105 | 106 | public SystemMessage build() { 107 | return new SystemMessage(this); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/Tool.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.Objects; 12 | 13 | import static dev.ai4j.openai4j.chat.ToolType.FUNCTION; 14 | 15 | @JsonDeserialize(builder = Tool.Builder.class) 16 | @JsonInclude(JsonInclude.Include.NON_NULL) 17 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 18 | public class Tool { 19 | 20 | @JsonProperty 21 | private final ToolType type = FUNCTION; 22 | @JsonProperty 23 | private final Function function; 24 | 25 | private Tool(Builder builder) { 26 | this.function = builder.function; 27 | } 28 | 29 | public ToolType type() { 30 | return this.type; 31 | } 32 | 33 | public Function function() { 34 | return this.function; 35 | } 36 | 37 | @Override 38 | public boolean equals(Object another) { 39 | if (this == another) return true; 40 | return another instanceof Tool 41 | && equalTo((Tool) another); 42 | } 43 | 44 | private boolean equalTo(Tool another) { 45 | return Objects.equals(type, another.type) 46 | && Objects.equals(function, another.function); 47 | } 48 | 49 | @Override 50 | public int hashCode() { 51 | int h = 5381; 52 | h += (h << 5) + Objects.hashCode(type); 53 | h += (h << 5) + Objects.hashCode(function); 54 | return h; 55 | } 56 | 57 | @Override 58 | public String toString() { 59 | return "Tool{" 60 | + "type=" + type 61 | + ", function=" + function 62 | + "}"; 63 | } 64 | 65 | public static Tool from(Function function) { 66 | return new Builder() 67 | .function(function) 68 | .build(); 69 | } 70 | 71 | public static Tool.Builder builder() { 72 | return new Tool.Builder(); 73 | } 74 | 75 | @JsonPOJOBuilder(withPrefix = "") 76 | @JsonIgnoreProperties(ignoreUnknown = true) 77 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 78 | public static final class Builder { 79 | private Function function; 80 | 81 | private Builder() { 82 | } 83 | 84 | public Tool.Builder function(Function function) { 85 | this.function = function; 86 | return this; 87 | } 88 | 89 | public Tool build() { 90 | return new Tool(this); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/ToolCall.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.Objects; 12 | 13 | @JsonDeserialize(builder = ToolCall.Builder.class) 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 16 | public class ToolCall { 17 | 18 | @JsonProperty 19 | private final String id; 20 | @JsonProperty 21 | private final Integer index; 22 | @JsonProperty 23 | private final ToolType type; 24 | @JsonProperty 25 | private final FunctionCall function; 26 | 27 | private ToolCall(Builder builder) { 28 | this.id = builder.id; 29 | this.index = builder.index; 30 | this.type = builder.type; 31 | this.function = builder.function; 32 | } 33 | 34 | public String id() { 35 | return id; 36 | } 37 | 38 | public Integer index() { 39 | return index; 40 | } 41 | 42 | public ToolType type() { 43 | return type; 44 | } 45 | 46 | public FunctionCall function() { 47 | return function; 48 | } 49 | 50 | @Override 51 | public boolean equals(Object another) { 52 | if (this == another) return true; 53 | return another instanceof ToolCall 54 | && equalTo((ToolCall) another); 55 | } 56 | 57 | private boolean equalTo(ToolCall another) { 58 | return Objects.equals(id, another.id) 59 | && Objects.equals(index, another.index) 60 | && Objects.equals(type, another.type) 61 | && Objects.equals(function, another.function); 62 | } 63 | 64 | @Override 65 | public int hashCode() { 66 | int h = 5381; 67 | h += (h << 5) + Objects.hashCode(id); 68 | h += (h << 5) + Objects.hashCode(index); 69 | h += (h << 5) + Objects.hashCode(type); 70 | h += (h << 5) + Objects.hashCode(function); 71 | return h; 72 | } 73 | 74 | @Override 75 | public String toString() { 76 | return "ToolCall{" 77 | + "id=" + id 78 | + ", index=" + index 79 | + ", type=" + type 80 | + ", function=" + function 81 | + "}"; 82 | } 83 | 84 | public static Builder builder() { 85 | return new Builder(); 86 | } 87 | 88 | @JsonPOJOBuilder(withPrefix = "") 89 | @JsonIgnoreProperties(ignoreUnknown = true) 90 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 91 | public static final class Builder { 92 | 93 | private String id; 94 | private Integer index; 95 | private ToolType type; 96 | private FunctionCall function; 97 | 98 | private Builder() { 99 | } 100 | 101 | public Builder id(String id) { 102 | this.id = id; 103 | return this; 104 | } 105 | 106 | public Builder index(Integer index) { 107 | this.index = index; 108 | return this; 109 | } 110 | 111 | public Builder type(ToolType type) { 112 | this.type = type; 113 | return this; 114 | } 115 | 116 | public Builder function(FunctionCall function) { 117 | this.function = function; 118 | return this; 119 | } 120 | 121 | public ToolCall build() { 122 | return new ToolCall(this); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/ToolChoice.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.Objects; 12 | 13 | import static dev.ai4j.openai4j.chat.ToolType.FUNCTION; 14 | 15 | @JsonDeserialize(builder = ToolChoice.Builder.class) 16 | @JsonInclude(JsonInclude.Include.NON_NULL) 17 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 18 | public class ToolChoice { 19 | 20 | @JsonProperty 21 | private final ToolType type = FUNCTION; 22 | @JsonProperty 23 | private final Function function; 24 | 25 | private ToolChoice(Builder builder) { 26 | function = builder.function; 27 | } 28 | 29 | @Override 30 | public boolean equals(Object another) { 31 | if (this == another) return true; 32 | return another instanceof ToolChoice 33 | && equalTo((ToolChoice) another); 34 | } 35 | 36 | private boolean equalTo(ToolChoice another) { 37 | return Objects.equals(type, another.type) 38 | && Objects.equals(function, another.function); 39 | } 40 | 41 | @Override 42 | public int hashCode() { 43 | int h = 5381; 44 | h += (h << 5) + Objects.hashCode(type); 45 | h += (h << 5) + Objects.hashCode(function); 46 | return h; 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | return "ToolChoice{" + 52 | "type=" + type + 53 | ", function=" + function + 54 | "}"; 55 | } 56 | 57 | public static ToolChoice from(String functionName) { 58 | return new Builder() 59 | .function(Function.builder() 60 | .name(functionName).build()) 61 | .build(); 62 | } 63 | 64 | @JsonPOJOBuilder(withPrefix = "") 65 | @JsonIgnoreProperties(ignoreUnknown = true) 66 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 67 | public static final class Builder { 68 | private Function function; 69 | 70 | private Builder() { 71 | } 72 | 73 | public ToolChoice.Builder function(Function function) { 74 | this.function = function; 75 | return this; 76 | } 77 | 78 | public ToolChoice build() { 79 | return new ToolChoice(this); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/ToolChoiceMode.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public enum ToolChoiceMode { 6 | 7 | @JsonProperty("none") 8 | NONE, 9 | @JsonProperty("auto") 10 | AUTO, 11 | @JsonProperty("required") 12 | REQUIRED 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/ToolMessage.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.Objects; 12 | 13 | import static dev.ai4j.openai4j.chat.Role.TOOL; 14 | 15 | @JsonDeserialize(builder = ToolMessage.Builder.class) 16 | @JsonInclude(JsonInclude.Include.NON_NULL) 17 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 18 | public final class ToolMessage implements Message { 19 | 20 | @JsonProperty 21 | private final Role role = TOOL; 22 | @JsonProperty 23 | private final String toolCallId; 24 | @JsonProperty 25 | private final String content; 26 | 27 | private ToolMessage(Builder builder) { 28 | this.toolCallId = builder.toolCallId; 29 | this.content = builder.content; 30 | } 31 | 32 | public Role role() { 33 | return role; 34 | } 35 | 36 | public String toolCallId() { 37 | return toolCallId; 38 | } 39 | 40 | public String content() { 41 | return content; 42 | } 43 | 44 | @Override 45 | public boolean equals(Object another) { 46 | if (this == another) return true; 47 | return another instanceof ToolMessage 48 | && equalTo((ToolMessage) another); 49 | } 50 | 51 | private boolean equalTo(ToolMessage another) { 52 | return Objects.equals(role, another.role) 53 | && Objects.equals(toolCallId, another.toolCallId) 54 | && Objects.equals(content, another.content); 55 | } 56 | 57 | @Override 58 | public int hashCode() { 59 | int h = 5381; 60 | h += (h << 5) + Objects.hashCode(role); 61 | h += (h << 5) + Objects.hashCode(toolCallId); 62 | h += (h << 5) + Objects.hashCode(content); 63 | return h; 64 | } 65 | 66 | @Override 67 | public String toString() { 68 | return "ToolMessage{" 69 | + "role=" + role 70 | + ", toolCallId=" + toolCallId 71 | + ", content=" + content 72 | + "}"; 73 | } 74 | 75 | public static ToolMessage from(String toolCallId, String content) { 76 | return ToolMessage.builder() 77 | .toolCallId(toolCallId) 78 | .content(content) 79 | .build(); 80 | } 81 | 82 | public static Builder builder() { 83 | return new Builder(); 84 | } 85 | 86 | @JsonPOJOBuilder(withPrefix = "") 87 | @JsonIgnoreProperties(ignoreUnknown = true) 88 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 89 | public static final class Builder { 90 | 91 | private String toolCallId; 92 | private String content; 93 | 94 | private Builder() { 95 | } 96 | 97 | public Builder toolCallId(String toolCallId) { 98 | this.toolCallId = toolCallId; 99 | return this; 100 | } 101 | 102 | public Builder content(String content) { 103 | this.content = content; 104 | return this; 105 | } 106 | 107 | public ToolMessage build() { 108 | return new ToolMessage(this); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/chat/ToolType.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public enum ToolType { 6 | 7 | @JsonProperty("function") 8 | FUNCTION 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/completion/CompletionChoice.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.completion; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.Objects; 12 | 13 | @JsonDeserialize(builder = CompletionChoice.Builder.class) 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 16 | public final class CompletionChoice { 17 | 18 | @JsonProperty 19 | private final String text; 20 | @JsonProperty 21 | private final Integer index; 22 | @JsonProperty 23 | private final Logprobs logprobs; 24 | @JsonProperty 25 | private final String finishReason; 26 | 27 | private CompletionChoice(Builder builder) { 28 | this.text = builder.text; 29 | this.index = builder.index; 30 | this.logprobs = builder.logprobs; 31 | this.finishReason = builder.finishReason; 32 | } 33 | 34 | public String text() { 35 | return text; 36 | } 37 | 38 | public Integer index() { 39 | return index; 40 | } 41 | 42 | public Logprobs logprobs() { 43 | return logprobs; 44 | } 45 | 46 | public String finishReason() { 47 | return finishReason; 48 | } 49 | 50 | @Override 51 | public boolean equals(Object another) { 52 | if (this == another) return true; 53 | return another instanceof CompletionChoice 54 | && equalTo((CompletionChoice) another); 55 | } 56 | 57 | private boolean equalTo(CompletionChoice another) { 58 | return Objects.equals(text, another.text) 59 | && Objects.equals(index, another.index) 60 | && Objects.equals(logprobs, another.logprobs) 61 | && Objects.equals(finishReason, another.finishReason); 62 | } 63 | 64 | @Override 65 | public int hashCode() { 66 | int h = 5381; 67 | h += (h << 5) + Objects.hashCode(text); 68 | h += (h << 5) + Objects.hashCode(index); 69 | h += (h << 5) + Objects.hashCode(logprobs); 70 | h += (h << 5) + Objects.hashCode(finishReason); 71 | return h; 72 | } 73 | 74 | @Override 75 | public String toString() { 76 | return "CompletionChoice{" 77 | + "text=" + text 78 | + ", index=" + index 79 | + ", logprobs=" + logprobs 80 | + ", finishReason=" + finishReason 81 | + "}"; 82 | } 83 | 84 | public static Builder builder() { 85 | return new Builder(); 86 | } 87 | 88 | @JsonPOJOBuilder(withPrefix = "") 89 | @JsonIgnoreProperties(ignoreUnknown = true) 90 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 91 | public static final class Builder { 92 | 93 | @JsonProperty 94 | private String text; 95 | @JsonProperty 96 | private Integer index; 97 | @JsonProperty 98 | private Logprobs logprobs; 99 | @JsonProperty 100 | private String finishReason; 101 | 102 | private Builder() { 103 | } 104 | 105 | public Builder text(String text) { 106 | this.text = text; 107 | return this; 108 | } 109 | 110 | public Builder index(Integer index) { 111 | this.index = index; 112 | return this; 113 | } 114 | 115 | public Builder logprobs(Logprobs logprobs) { 116 | this.logprobs = logprobs; 117 | return this; 118 | } 119 | 120 | public Builder finishReason(String finishReason) { 121 | this.finishReason = finishReason; 122 | return this; 123 | } 124 | 125 | public CompletionChoice build() { 126 | return new CompletionChoice(this); 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/completion/CompletionModel.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.completion; 2 | 3 | public enum CompletionModel { 4 | 5 | GPT_3_5_TURBO_INSTRUCT("gpt-3.5-turbo-instruct"); 6 | 7 | private final String value; 8 | 9 | CompletionModel(String value) { 10 | this.value = value; 11 | } 12 | 13 | @Override 14 | public String toString() { 15 | return value; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/completion/CompletionResponse.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.completion; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | import dev.ai4j.openai4j.shared.Usage; 11 | 12 | import java.util.List; 13 | import java.util.Objects; 14 | 15 | import static java.util.Collections.unmodifiableList; 16 | 17 | @JsonDeserialize(builder = CompletionResponse.Builder.class) 18 | @JsonInclude(JsonInclude.Include.NON_NULL) 19 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 20 | public final class CompletionResponse { 21 | 22 | @JsonProperty 23 | private final String id; 24 | @JsonProperty 25 | private final Integer created; 26 | @JsonProperty 27 | private final String model; 28 | @JsonProperty 29 | private final List choices; 30 | @JsonProperty 31 | private final Usage usage; 32 | 33 | private CompletionResponse(Builder builder) { 34 | this.id = builder.id; 35 | this.created = builder.created; 36 | this.model = builder.model; 37 | this.choices = builder.choices; 38 | this.usage = builder.usage; 39 | } 40 | 41 | public String id() { 42 | return id; 43 | } 44 | 45 | public Integer created() { 46 | return created; 47 | } 48 | 49 | public String model() { 50 | return model; 51 | } 52 | 53 | public List choices() { 54 | return choices; 55 | } 56 | 57 | public Usage usage() { 58 | return usage; 59 | } 60 | 61 | /** 62 | * Convenience method to get the text from the first choice. 63 | */ 64 | public String text() { 65 | return choices().get(0).text(); 66 | } 67 | 68 | @Override 69 | public boolean equals(Object another) { 70 | if (this == another) return true; 71 | return another instanceof CompletionResponse 72 | && equalTo((CompletionResponse) another); 73 | } 74 | 75 | private boolean equalTo(CompletionResponse another) { 76 | return Objects.equals(id, another.id) 77 | && Objects.equals(created, another.created) 78 | && Objects.equals(model, another.model) 79 | && Objects.equals(choices, another.choices) 80 | && Objects.equals(usage, another.usage); 81 | } 82 | 83 | @Override 84 | public int hashCode() { 85 | int h = 5381; 86 | h += (h << 5) + Objects.hashCode(id); 87 | h += (h << 5) + Objects.hashCode(created); 88 | h += (h << 5) + Objects.hashCode(model); 89 | h += (h << 5) + Objects.hashCode(choices); 90 | h += (h << 5) + Objects.hashCode(usage); 91 | return h; 92 | } 93 | 94 | @Override 95 | public String toString() { 96 | return "CompletionResponse{" 97 | + "id=" + id 98 | + ", created=" + created 99 | + ", model=" + model 100 | + ", choices=" + choices 101 | + ", usage=" + usage 102 | + "}"; 103 | } 104 | 105 | public static Builder builder() { 106 | return new Builder(); 107 | } 108 | 109 | @JsonPOJOBuilder(withPrefix = "") 110 | @JsonIgnoreProperties(ignoreUnknown = true) 111 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 112 | public static final class Builder { 113 | 114 | private String id; 115 | private Integer created; 116 | private String model; 117 | private List choices; 118 | private Usage usage; 119 | 120 | private Builder() { 121 | } 122 | 123 | public Builder id(String id) { 124 | this.id = id; 125 | return this; 126 | } 127 | 128 | public Builder created(Integer created) { 129 | this.created = created; 130 | return this; 131 | } 132 | 133 | public Builder model(String model) { 134 | this.model = model; 135 | return this; 136 | } 137 | 138 | public Builder choices(List choices) { 139 | if (choices != null) { 140 | this.choices = unmodifiableList(choices); 141 | } 142 | return this; 143 | } 144 | 145 | public Builder usage(Usage usage) { 146 | this.usage = usage; 147 | return this; 148 | } 149 | 150 | public CompletionResponse build() { 151 | return new CompletionResponse(this); 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/completion/Logprobs.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.completion; 2 | 3 | 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonInclude; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 8 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 9 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 10 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.Map; 15 | import java.util.Objects; 16 | 17 | import static java.util.Collections.unmodifiableList; 18 | import static java.util.Collections.unmodifiableMap; 19 | 20 | @JsonDeserialize(builder = Logprobs.Builder.class) 21 | @JsonInclude(JsonInclude.Include.NON_NULL) 22 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 23 | public final class Logprobs { 24 | 25 | @JsonProperty 26 | private final List tokens; 27 | @JsonProperty 28 | private final List tokenLogprobs; 29 | @JsonProperty 30 | private final List> topLogprobs; 31 | @JsonProperty 32 | private final List textOffset; 33 | 34 | private Logprobs(Builder builder) { 35 | this.tokens = builder.tokens; 36 | this.tokenLogprobs = builder.tokenLogprobs; 37 | this.topLogprobs = builder.topLogprobs; 38 | this.textOffset = builder.textOffset; 39 | } 40 | 41 | public List tokens() { 42 | return tokens; 43 | } 44 | 45 | public List tokenLogprobs() { 46 | return tokenLogprobs; 47 | } 48 | 49 | public List> topLogprobs() { 50 | return topLogprobs; 51 | } 52 | 53 | public List textOffset() { 54 | return textOffset; 55 | } 56 | 57 | @Override 58 | public boolean equals(Object another) { 59 | if (this == another) return true; 60 | return another instanceof Logprobs 61 | && equalTo((Logprobs) another); 62 | } 63 | 64 | private boolean equalTo(Logprobs another) { 65 | return Objects.equals(tokens, another.tokens) 66 | && Objects.equals(tokenLogprobs, another.tokenLogprobs) 67 | && Objects.equals(topLogprobs, another.topLogprobs) 68 | && Objects.equals(textOffset, another.textOffset); 69 | } 70 | 71 | @Override 72 | public int hashCode() { 73 | int h = 5381; 74 | h += (h << 5) + Objects.hashCode(tokens); 75 | h += (h << 5) + Objects.hashCode(tokenLogprobs); 76 | h += (h << 5) + Objects.hashCode(topLogprobs); 77 | h += (h << 5) + Objects.hashCode(textOffset); 78 | return h; 79 | } 80 | 81 | @Override 82 | public String toString() { 83 | return "Logprobs{" 84 | + "tokens=" + tokens 85 | + ", tokenLogprobs=" + tokenLogprobs 86 | + ", topLogprobs=" + topLogprobs 87 | + ", textOffset=" + textOffset 88 | + "}"; 89 | } 90 | 91 | public static Builder builder() { 92 | return new Builder(); 93 | } 94 | 95 | @JsonPOJOBuilder(withPrefix = "") 96 | @JsonIgnoreProperties(ignoreUnknown = true) 97 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 98 | public static final class Builder { 99 | 100 | private List tokens; 101 | private List tokenLogprobs; 102 | private List> topLogprobs; 103 | private List textOffset; 104 | 105 | private Builder() { 106 | } 107 | 108 | public Builder tokens(List tokens) { 109 | if (tokens != null) { 110 | this.tokens = unmodifiableList(tokens); 111 | } 112 | return this; 113 | } 114 | 115 | public Builder tokenLogprobs(List tokenLogprobs) { 116 | if (tokenLogprobs != null) { 117 | this.tokenLogprobs = unmodifiableList(tokenLogprobs); 118 | } 119 | return this; 120 | } 121 | 122 | public Builder topLogprobs(List> topLogprobs) { 123 | if (topLogprobs != null) { 124 | List> topLogprobsCopy = new ArrayList<>(); 125 | for (Map map : topLogprobs) { 126 | topLogprobsCopy.add(unmodifiableMap(map)); 127 | } 128 | this.topLogprobs = unmodifiableList(topLogprobsCopy); 129 | } 130 | 131 | return this; 132 | } 133 | 134 | public Builder textOffset(List textOffset) { 135 | if (textOffset != null) { 136 | this.textOffset = unmodifiableList(textOffset); 137 | } 138 | return this; 139 | } 140 | 141 | public Logprobs build() { 142 | return new Logprobs(this); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/embedding/Embedding.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.embedding; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | import dev.ai4j.openai4j.completion.Logprobs; 11 | 12 | import java.util.List; 13 | import java.util.Objects; 14 | 15 | import static java.util.Collections.unmodifiableList; 16 | 17 | @JsonDeserialize(builder = Embedding.Builder.class) 18 | @JsonInclude(JsonInclude.Include.NON_NULL) 19 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 20 | public final class Embedding { 21 | 22 | @JsonProperty 23 | private final List embedding; 24 | @JsonProperty 25 | private final Integer index; 26 | 27 | private Embedding(Builder builder) { 28 | this.embedding = builder.embedding; 29 | this.index = builder.index; 30 | } 31 | 32 | public List embedding() { 33 | return embedding; 34 | } 35 | 36 | public Integer index() { 37 | return index; 38 | } 39 | 40 | @Override 41 | public boolean equals(Object another) { 42 | if (this == another) return true; 43 | return another instanceof Embedding 44 | && equalTo((Embedding) another); 45 | } 46 | 47 | private boolean equalTo(Embedding another) { 48 | return Objects.equals(embedding, another.embedding) 49 | && Objects.equals(index, another.index); 50 | } 51 | 52 | @Override 53 | public int hashCode() { 54 | int h = 5381; 55 | h += (h << 5) + Objects.hashCode(embedding); 56 | h += (h << 5) + Objects.hashCode(index); 57 | return h; 58 | } 59 | 60 | @Override 61 | public String toString() { 62 | return "Embedding{" 63 | + "embedding=" + embedding 64 | + ", index=" + index 65 | + "}"; 66 | } 67 | 68 | public static Builder builder() { 69 | return new Builder(); 70 | } 71 | 72 | @JsonPOJOBuilder(withPrefix = "") 73 | @JsonIgnoreProperties(ignoreUnknown = true) 74 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 75 | public static final class Builder { 76 | 77 | private List embedding; 78 | private Integer index; 79 | 80 | private Builder() { 81 | } 82 | 83 | public Builder embedding(List embedding) { 84 | if (embedding != null) { 85 | this.embedding = unmodifiableList(embedding); 86 | } 87 | return this; 88 | } 89 | 90 | public Builder index(Integer index) { 91 | this.index = index; 92 | return this; 93 | } 94 | 95 | public Embedding build() { 96 | return new Embedding(this); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/embedding/EmbeddingModel.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.embedding; 2 | 3 | public enum EmbeddingModel { 4 | 5 | TEXT_EMBEDDING_ADA_002("text-embedding-ada-002"), 6 | 7 | TEXT_EMBEDDING_3_SMALL("text-embedding-3-small"), 8 | TEXT_EMBEDDING_3_LARGE("text-embedding-3-large"); 9 | 10 | private final String value; 11 | 12 | EmbeddingModel(String value) { 13 | this.value = value; 14 | } 15 | 16 | @Override 17 | public String toString() { 18 | return value; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/embedding/EmbeddingRequest.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.embedding; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.List; 12 | import java.util.Objects; 13 | 14 | import static dev.ai4j.openai4j.embedding.EmbeddingModel.TEXT_EMBEDDING_ADA_002; 15 | import static java.util.Arrays.asList; 16 | import static java.util.Collections.unmodifiableList; 17 | 18 | @JsonDeserialize(builder = EmbeddingRequest.Builder.class) 19 | @JsonInclude(JsonInclude.Include.NON_NULL) 20 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 21 | public final class EmbeddingRequest { 22 | 23 | @JsonProperty 24 | private final String model; 25 | @JsonProperty 26 | private final List input; 27 | @JsonProperty 28 | private final Integer dimensions; 29 | @JsonProperty 30 | private final String user; 31 | 32 | private EmbeddingRequest(Builder builder) { 33 | this.model = builder.model; 34 | this.input = builder.input; 35 | this.dimensions = builder.dimensions; 36 | this.user = builder.user; 37 | } 38 | 39 | public String model() { 40 | return model; 41 | } 42 | 43 | public List input() { 44 | return input; 45 | } 46 | 47 | public Integer dimensions() { 48 | return dimensions; 49 | } 50 | 51 | public String user() { 52 | return user; 53 | } 54 | 55 | @Override 56 | public boolean equals(Object another) { 57 | if (this == another) return true; 58 | return another instanceof EmbeddingRequest 59 | && equalTo((EmbeddingRequest) another); 60 | } 61 | 62 | private boolean equalTo(EmbeddingRequest another) { 63 | return Objects.equals(model, another.model) 64 | && Objects.equals(input, another.input) 65 | && Objects.equals(dimensions, another.dimensions) 66 | && Objects.equals(user, another.user); 67 | } 68 | 69 | @Override 70 | public int hashCode() { 71 | int h = 5381; 72 | h += (h << 5) + Objects.hashCode(model); 73 | h += (h << 5) + Objects.hashCode(input); 74 | h += (h << 5) + Objects.hashCode(dimensions); 75 | h += (h << 5) + Objects.hashCode(user); 76 | return h; 77 | } 78 | 79 | @Override 80 | public String toString() { 81 | return "EmbeddingRequest{" 82 | + "model=" + model 83 | + ", input=" + input 84 | + ", dimensions=" + dimensions 85 | + ", user=" + user 86 | + "}"; 87 | } 88 | 89 | public static Builder builder() { 90 | return new Builder(); 91 | } 92 | 93 | @JsonPOJOBuilder(withPrefix = "") 94 | @JsonIgnoreProperties(ignoreUnknown = true) 95 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 96 | public static final class Builder { 97 | 98 | private String model = TEXT_EMBEDDING_ADA_002.toString(); 99 | private List input; 100 | private Integer dimensions; 101 | private String user; 102 | 103 | private Builder() { 104 | } 105 | 106 | public Builder model(EmbeddingModel model) { 107 | return model(model.toString()); 108 | } 109 | 110 | public Builder model(String model) { 111 | this.model = model; 112 | return this; 113 | } 114 | 115 | public Builder input(String... input) { 116 | return input(asList(input)); 117 | } 118 | 119 | public Builder input(List input) { 120 | if (input != null) { 121 | this.input = unmodifiableList(input); 122 | } 123 | return this; 124 | } 125 | 126 | public Builder dimensions(Integer dimensions) { 127 | this.dimensions = dimensions; 128 | return this; 129 | } 130 | 131 | public Builder user(String user) { 132 | this.user = user; 133 | return this; 134 | } 135 | 136 | public EmbeddingRequest build() { 137 | return new EmbeddingRequest(this); 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/embedding/EmbeddingResponse.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.embedding; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | import dev.ai4j.openai4j.shared.Usage; 11 | 12 | import java.util.List; 13 | import java.util.Objects; 14 | 15 | import static java.util.Collections.unmodifiableList; 16 | 17 | @JsonDeserialize(builder = EmbeddingResponse.Builder.class) 18 | @JsonInclude(JsonInclude.Include.NON_NULL) 19 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 20 | public final class EmbeddingResponse { 21 | 22 | @JsonProperty 23 | private final String model; 24 | @JsonProperty 25 | private final List data; 26 | @JsonProperty 27 | private final Usage usage; 28 | 29 | private EmbeddingResponse(Builder builder) { 30 | this.model = builder.model; 31 | this.data = builder.data; 32 | this.usage = builder.usage; 33 | } 34 | 35 | public String model() { 36 | return model; 37 | } 38 | 39 | public List data() { 40 | return data; 41 | } 42 | 43 | public Usage usage() { 44 | return usage; 45 | } 46 | 47 | /** 48 | * Convenience method to get the embedding from the first data. 49 | */ 50 | public List embedding() { 51 | return data.get(0).embedding(); 52 | } 53 | 54 | @Override 55 | public boolean equals(Object another) { 56 | if (this == another) return true; 57 | return another instanceof EmbeddingResponse 58 | && equalTo((EmbeddingResponse) another); 59 | } 60 | 61 | private boolean equalTo(EmbeddingResponse another) { 62 | return Objects.equals(model, another.model) 63 | && Objects.equals(data, another.data) 64 | && Objects.equals(usage, another.usage); 65 | } 66 | 67 | @Override 68 | public int hashCode() { 69 | int h = 5381; 70 | h += (h << 5) + Objects.hashCode(model); 71 | h += (h << 5) + Objects.hashCode(data); 72 | h += (h << 5) + Objects.hashCode(usage); 73 | return h; 74 | } 75 | 76 | @Override 77 | public String toString() { 78 | return "EmbeddingResponse{" 79 | + "model=" + model 80 | + ", data=" + data 81 | + ", usage=" + usage 82 | + "}"; 83 | } 84 | 85 | public static Builder builder() { 86 | return new Builder(); 87 | } 88 | 89 | @JsonPOJOBuilder(withPrefix = "") 90 | @JsonIgnoreProperties(ignoreUnknown = true) 91 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 92 | public static final class Builder { 93 | 94 | private String model; 95 | private List data; 96 | private Usage usage; 97 | 98 | private Builder() { 99 | } 100 | 101 | public Builder model(String model) { 102 | this.model = model; 103 | return this; 104 | } 105 | 106 | public Builder data(List data) { 107 | if (data != null) { 108 | this.data = unmodifiableList(data); 109 | } 110 | return this; 111 | } 112 | 113 | public Builder usage(Usage usage) { 114 | this.usage = usage; 115 | return this; 116 | } 117 | 118 | public EmbeddingResponse build() { 119 | return new EmbeddingResponse(this); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/image/GenerateImagesRequest.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.image; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import static dev.ai4j.openai4j.image.ImageModel.DALL_E_3; 12 | import static dev.ai4j.openai4j.image.ImageModel.DALL_E_RESPONSE_FORMAT_URL; 13 | 14 | import java.util.Objects; 15 | 16 | /** 17 | * Represents the request from the OpenAI DALL·E API when generating images. 18 | * Find description of parameters here. 19 | */ 20 | @JsonDeserialize(builder = GenerateImagesRequest.Builder.class) 21 | @JsonInclude(JsonInclude.Include.NON_NULL) 22 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 23 | public class GenerateImagesRequest { 24 | 25 | @JsonProperty 26 | private final String model; 27 | @JsonProperty 28 | private final String prompt; 29 | @JsonProperty 30 | private final int n; 31 | @JsonProperty 32 | private final String size; 33 | @JsonProperty 34 | private final String quality; 35 | @JsonProperty 36 | private final String style; 37 | @JsonProperty 38 | private final String user; 39 | @JsonProperty 40 | private final String responseFormat; 41 | 42 | private GenerateImagesRequest(Builder builder) { 43 | this.model = builder.model.toString(); 44 | this.prompt = builder.prompt; 45 | this.n = builder.n; 46 | this.size = builder.size; 47 | this.quality = builder.quality; 48 | this.style = builder.style; 49 | this.user = builder.user; 50 | this.responseFormat = builder.responseFormat; 51 | } 52 | 53 | public int hashCode() { 54 | int h = 2381; 55 | h += (h << 5) + Objects.hashCode(model); 56 | h += (h << 5) + Objects.hashCode(prompt); 57 | h += (h << 5) + n; 58 | h += (h << 5) + Objects.hashCode(size); 59 | h += (h << 5) + Objects.hashCode(quality); 60 | h += (h << 5) + Objects.hashCode(style); 61 | h += (h << 5) + Objects.hashCode(user); 62 | h += (h << 5) + Objects.hashCode(responseFormat); 63 | return h; 64 | } 65 | 66 | public String toString() { 67 | return ( 68 | "GenerateImagesRequest{" + 69 | "model=" + 70 | model + 71 | ", prompt=" + 72 | prompt + 73 | ", n=" + 74 | n + 75 | ", size=" + 76 | size + 77 | ", quality=" + 78 | quality + 79 | ", style=" + 80 | style + 81 | ", user=" + 82 | user + 83 | ", responseFormat=" + 84 | responseFormat + 85 | '}' 86 | ); 87 | } 88 | 89 | public static Builder builder() { 90 | return new Builder(); 91 | } 92 | 93 | @JsonPOJOBuilder(withPrefix = "") 94 | @JsonIgnoreProperties(ignoreUnknown = true) 95 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 96 | public static class Builder { 97 | 98 | private String model = DALL_E_3.toString(); 99 | private String prompt; 100 | private int n = 1; 101 | private String size; 102 | private String quality; 103 | private String style; 104 | private String user; 105 | private String responseFormat = DALL_E_RESPONSE_FORMAT_URL; 106 | 107 | public Builder model(String model) { 108 | this.model = model; 109 | return this; 110 | } 111 | 112 | public Builder model(ImageModel model) { 113 | this.model = model.toString(); 114 | return this; 115 | } 116 | 117 | public Builder prompt(String prompt) { 118 | this.prompt = prompt; 119 | return this; 120 | } 121 | 122 | public Builder n(int n) { 123 | this.n = n; 124 | return this; 125 | } 126 | 127 | public Builder size(String size) { 128 | this.size = size; 129 | return this; 130 | } 131 | 132 | public Builder quality(String quality) { 133 | this.quality = quality; 134 | return this; 135 | } 136 | 137 | public Builder style(String style) { 138 | this.style = style; 139 | return this; 140 | } 141 | 142 | public Builder user(String user) { 143 | this.user = user; 144 | return this; 145 | } 146 | 147 | public Builder responseFormat(String responseFormat) { 148 | this.responseFormat = responseFormat; 149 | return this; 150 | } 151 | 152 | public GenerateImagesRequest build() { 153 | return new GenerateImagesRequest(this); 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/image/GenerateImagesResponse.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.image; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.net.URI; 12 | import java.util.List; 13 | import java.util.Objects; 14 | 15 | /** 16 | * Represents the response from the OpenAI DALL·E API when generating images. 17 | * Find description of parameters here. 18 | */ 19 | @JsonDeserialize(builder = GenerateImagesResponse.Builder.class) 20 | @JsonInclude(JsonInclude.Include.NON_NULL) 21 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 22 | public class GenerateImagesResponse { 23 | 24 | @JsonProperty 25 | private final List data; 26 | 27 | public GenerateImagesResponse(Builder builder) { 28 | this.data = builder.data; 29 | } 30 | 31 | public static Builder builder() { 32 | return new Builder(); 33 | } 34 | 35 | public List data() { 36 | return data; 37 | } 38 | 39 | @Override 40 | public String toString() { 41 | return "GenerateImagesResponse{" + "data=" + data + '}'; 42 | } 43 | 44 | @Override 45 | public boolean equals(Object another) { 46 | if (this == another) return true; 47 | if (another == null || getClass() != another.getClass()) return false; 48 | GenerateImagesResponse anotherGenerateImagesResponse = (GenerateImagesResponse) another; 49 | return Objects.equals(data, anotherGenerateImagesResponse.data); 50 | } 51 | 52 | @Override 53 | public int hashCode() { 54 | return Objects.hash(data); 55 | } 56 | 57 | @JsonPOJOBuilder(withPrefix = "") 58 | @JsonIgnoreProperties(ignoreUnknown = true) 59 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 60 | public static class Builder { 61 | 62 | private List data; 63 | 64 | public Builder data(List data) { 65 | this.data = data; 66 | return this; 67 | } 68 | 69 | public GenerateImagesResponse build() { 70 | return new GenerateImagesResponse(this); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/image/ImageData.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.image; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.net.URI; 12 | import java.util.Objects; 13 | 14 | @JsonDeserialize(builder = ImageData.Builder.class) 15 | @JsonInclude(JsonInclude.Include.NON_NULL) 16 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 17 | public class ImageData { 18 | 19 | @JsonProperty 20 | private URI url; 21 | @JsonProperty 22 | private final String b64Json; 23 | @JsonProperty 24 | private final String revisedPrompt; 25 | 26 | private ImageData(Builder builder) { 27 | url = builder.url; 28 | b64Json = builder.b64Json; 29 | revisedPrompt = builder.revisedPrompt; 30 | } 31 | 32 | public URI url() { 33 | return url; 34 | } 35 | 36 | public String b64Json() { 37 | return b64Json; 38 | } 39 | 40 | public String revisedPrompt() { 41 | return revisedPrompt; 42 | } 43 | 44 | public void url(URI url) { 45 | this.url = url; 46 | } 47 | 48 | @Override 49 | public boolean equals(Object another) { 50 | if (this == another) return true; 51 | if (another == null || getClass() != another.getClass()) return false; 52 | ImageData anotherImageData = (ImageData) another; 53 | return ( 54 | Objects.equals(url, anotherImageData.url) && 55 | Objects.equals(b64Json, anotherImageData.b64Json) && 56 | Objects.equals(revisedPrompt, anotherImageData.revisedPrompt) 57 | ); 58 | } 59 | 60 | @Override 61 | public int hashCode() { 62 | return Objects.hash(url, b64Json, revisedPrompt); 63 | } 64 | 65 | @Override 66 | public String toString() { 67 | return ( 68 | "ImageData{" + 69 | "url='" + 70 | url + 71 | '\'' + 72 | ", b64Json='" + 73 | b64Json + 74 | '\'' + 75 | ", revisedPrompt='" + 76 | revisedPrompt + 77 | '\'' + 78 | '}' 79 | ); 80 | } 81 | 82 | public static Builder builder() { 83 | return new Builder(); 84 | } 85 | 86 | @JsonPOJOBuilder(withPrefix = "") 87 | @JsonIgnoreProperties(ignoreUnknown = true) 88 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 89 | public static class Builder { 90 | 91 | private URI url; 92 | private String b64Json; 93 | private String revisedPrompt; 94 | 95 | public Builder url(URI url) { 96 | this.url = url; 97 | return this; 98 | } 99 | 100 | public Builder b64Json(String b64Json) { 101 | this.b64Json = b64Json; 102 | return this; 103 | } 104 | 105 | public Builder revisedPrompt(String revisedPrompt) { 106 | this.revisedPrompt = revisedPrompt; 107 | return this; 108 | } 109 | 110 | public ImageData build() { 111 | return new ImageData(this); 112 | } 113 | } 114 | } 115 | 116 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/image/ImageModel.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.image; 2 | 3 | /** 4 | * Represents an OpenAI DALL·E models to generate artistic images. 5 | */ 6 | public enum ImageModel { 7 | 8 | DALL_E_2("dall-e-2"), 9 | DALL_E_3("dall-e-3"); 10 | 11 | public static final String DALL_E_SIZE_256_x_256 = "256x256"; // for 2 only 12 | public static final String DALL_E_SIZE_512_x_512 = "512x512"; // for 2 only 13 | public static final String DALL_E_SIZE_1024_x_1024 = "1024x1024"; // for 2 & 3 14 | public static final String DALL_E_SIZE_1792_x_1024 = "1792x1024"; // for 3 only 15 | public static final String DALL_E_SIZE_1024_x_1792 = "1024x1792"; // for 3 only 16 | public static final String DALL_E_QUALITY_STANDARD = "standard"; 17 | public static final String DALL_E_QUALITY_HD = "hd"; 18 | public static final String DALL_E_STYLE_VIVID = "vivid"; 19 | public static final String DALL_E_STYLE_NATURAL = "natural"; 20 | 21 | public static final String DALL_E_RESPONSE_FORMAT_URL = "url"; 22 | public static final String DALL_E_RESPONSE_FORMAT_B64_JSON = "b64_json"; 23 | 24 | private final String value; 25 | 26 | ImageModel(String value) { 27 | this.value = value; 28 | } 29 | 30 | @Override 31 | public String toString() { 32 | return value; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/moderation/ModerationModel.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.moderation; 2 | 3 | public enum ModerationModel { 4 | 5 | TEXT_MODERATION_STABLE("text-moderation-stable"), 6 | TEXT_MODERATION_LATEST("text-moderation-latest"); 7 | 8 | private final String value; 9 | 10 | ModerationModel(String value) { 11 | this.value = value; 12 | } 13 | 14 | @Override 15 | public String toString() { 16 | return value; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/moderation/ModerationRequest.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.moderation; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | import dev.ai4j.openai4j.embedding.EmbeddingResponse; 11 | 12 | import java.util.List; 13 | import java.util.Objects; 14 | 15 | import static java.util.Collections.singletonList; 16 | import static java.util.Collections.unmodifiableList; 17 | 18 | @JsonDeserialize(builder = ModerationRequest.Builder.class) 19 | @JsonInclude(JsonInclude.Include.NON_NULL) 20 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 21 | public class ModerationRequest { 22 | 23 | @JsonProperty 24 | private final String model; 25 | @JsonProperty 26 | private final List input; 27 | 28 | private ModerationRequest(Builder builder) { 29 | this.model = builder.model; 30 | this.input = builder.input; 31 | } 32 | 33 | public String model() { 34 | return model; 35 | } 36 | 37 | public List input() { 38 | return input; 39 | } 40 | 41 | @Override 42 | public boolean equals(Object another) { 43 | if (this == another) return true; 44 | return another instanceof ModerationRequest 45 | && equalTo((ModerationRequest) another); 46 | } 47 | 48 | private boolean equalTo(ModerationRequest another) { 49 | return Objects.equals(model, another.model) 50 | && Objects.equals(input, another.input); 51 | } 52 | 53 | @Override 54 | public int hashCode() { 55 | int h = 5381; 56 | h += (h << 5) + Objects.hashCode(model); 57 | h += (h << 5) + Objects.hashCode(input); 58 | return h; 59 | } 60 | 61 | @Override 62 | public String toString() { 63 | return "ModerationRequest{" 64 | + "model=" + model 65 | + ", input=" + input 66 | + "}"; 67 | } 68 | 69 | public static Builder builder() { 70 | return new Builder(); 71 | } 72 | 73 | @JsonPOJOBuilder(withPrefix = "") 74 | @JsonIgnoreProperties(ignoreUnknown = true) 75 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 76 | public static final class Builder { 77 | 78 | private String model; 79 | private List input; 80 | 81 | private Builder() { 82 | } 83 | 84 | public Builder model(ModerationModel model) { 85 | return model(model.toString()); 86 | } 87 | 88 | public Builder model(String model) { 89 | this.model = model; 90 | return this; 91 | } 92 | 93 | public Builder input(List input) { 94 | if (input != null) { 95 | this.input = unmodifiableList(input); 96 | } 97 | return this; 98 | } 99 | 100 | public Builder input(String input) { 101 | return input(singletonList(input)); 102 | } 103 | 104 | public ModerationRequest build() { 105 | return new ModerationRequest(this); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/moderation/ModerationResponse.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.moderation; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.List; 12 | import java.util.Objects; 13 | 14 | import static java.util.Collections.unmodifiableList; 15 | 16 | @JsonDeserialize(builder = ModerationResponse.Builder.class) 17 | @JsonInclude(JsonInclude.Include.NON_NULL) 18 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 19 | public final class ModerationResponse { 20 | 21 | @JsonProperty 22 | private final String id; 23 | @JsonProperty 24 | private final String model; 25 | @JsonProperty 26 | private final List results; 27 | 28 | private ModerationResponse(Builder builder) { 29 | this.id = builder.id; 30 | this.model = builder.model; 31 | this.results = builder.results; 32 | } 33 | 34 | public String id() { 35 | return id; 36 | } 37 | 38 | public String model() { 39 | return model; 40 | } 41 | 42 | public List results() { 43 | return results; 44 | } 45 | 46 | @Override 47 | public boolean equals(Object another) { 48 | if (this == another) return true; 49 | return another instanceof ModerationResponse 50 | && equalTo((ModerationResponse) another); 51 | } 52 | 53 | private boolean equalTo(ModerationResponse another) { 54 | return Objects.equals(id, another.id) 55 | && Objects.equals(model, another.model) 56 | && Objects.equals(results, another.results); 57 | } 58 | 59 | @Override 60 | public int hashCode() { 61 | int h = 5381; 62 | h += (h << 5) + Objects.hashCode(id); 63 | h += (h << 5) + Objects.hashCode(model); 64 | h += (h << 5) + Objects.hashCode(results); 65 | return h; 66 | } 67 | 68 | @Override 69 | public String toString() { 70 | return "ModerationResponse{" 71 | + "id=" + id 72 | + ", model=" + model 73 | + ", results=" + results 74 | + "}"; 75 | } 76 | 77 | public static Builder builder() { 78 | return new Builder(); 79 | } 80 | 81 | @JsonPOJOBuilder(withPrefix = "") 82 | @JsonIgnoreProperties(ignoreUnknown = true) 83 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 84 | public static final class Builder { 85 | 86 | public String id; 87 | public String model; 88 | public List results; 89 | 90 | private Builder() { 91 | } 92 | 93 | public Builder id(String id) { 94 | this.id = id; 95 | return this; 96 | } 97 | 98 | public Builder model(String model) { 99 | this.model = model; 100 | return this; 101 | } 102 | 103 | public Builder results(List results) { 104 | if (results != null) { 105 | this.results = unmodifiableList(results); 106 | } 107 | return this; 108 | } 109 | 110 | public ModerationResponse build() { 111 | return new ModerationResponse(this); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/moderation/ModerationResult.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.moderation; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.Objects; 12 | 13 | @JsonDeserialize(builder = ModerationResult.Builder.class) 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 16 | public final class ModerationResult { 17 | 18 | @JsonProperty 19 | private final Categories categories; 20 | @JsonProperty 21 | private final CategoryScores categoryScores; 22 | @JsonProperty 23 | private final Boolean flagged; 24 | 25 | private ModerationResult(Builder builder) { 26 | this.categories = builder.categories; 27 | this.categoryScores = builder.categoryScores; 28 | this.flagged = builder.flagged; 29 | } 30 | 31 | public Categories categories() { 32 | return categories; 33 | } 34 | 35 | public CategoryScores categoryScores() { 36 | return categoryScores; 37 | } 38 | 39 | public Boolean isFlagged() { 40 | return flagged; 41 | } 42 | 43 | @Override 44 | public boolean equals(Object another) { 45 | if (this == another) return true; 46 | return another instanceof ModerationResult 47 | && equalTo((ModerationResult) another); 48 | } 49 | 50 | private boolean equalTo(ModerationResult another) { 51 | return Objects.equals(categories, another.categories) 52 | && Objects.equals(categoryScores, another.categoryScores) 53 | && Objects.equals(flagged, another.flagged); 54 | } 55 | 56 | @Override 57 | public int hashCode() { 58 | int h = 5381; 59 | h += (h << 5) + Objects.hashCode(categories); 60 | h += (h << 5) + Objects.hashCode(categoryScores); 61 | h += (h << 5) + Objects.hashCode(flagged); 62 | return h; 63 | } 64 | 65 | @Override 66 | public String toString() { 67 | return "ModerationResult{" 68 | + "categories=" + categories 69 | + ", categoryScores=" + categoryScores 70 | + ", flagged=" + flagged 71 | + "}"; 72 | } 73 | 74 | public static Builder builder() { 75 | return new Builder(); 76 | } 77 | 78 | @JsonPOJOBuilder(withPrefix = "") 79 | @JsonIgnoreProperties(ignoreUnknown = true) 80 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 81 | public static final class Builder { 82 | 83 | private Categories categories; 84 | private CategoryScores categoryScores; 85 | private Boolean flagged; 86 | 87 | private Builder() { 88 | } 89 | 90 | public Builder categories(Categories categories) { 91 | this.categories = categories; 92 | return this; 93 | } 94 | 95 | public Builder categoryScores(CategoryScores categoryScores) { 96 | this.categoryScores = categoryScores; 97 | return this; 98 | } 99 | 100 | public Builder flagged(Boolean flagged) { 101 | this.flagged = flagged; 102 | return this; 103 | } 104 | 105 | public ModerationResult build() { 106 | return new ModerationResult(this); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/shared/CompletionTokensDetails.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.shared; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.Objects; 12 | 13 | @JsonDeserialize(builder = CompletionTokensDetails.Builder.class) 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 16 | public final class CompletionTokensDetails { 17 | 18 | @JsonProperty 19 | private final Integer reasoningTokens; 20 | 21 | private CompletionTokensDetails(Builder builder) { 22 | this.reasoningTokens = builder.reasoningTokens; 23 | } 24 | 25 | public Integer reasoningTokens() { 26 | return reasoningTokens; 27 | } 28 | 29 | @Override 30 | public boolean equals(Object another) { 31 | if (this == another) return true; 32 | return another instanceof CompletionTokensDetails 33 | && equalTo((CompletionTokensDetails) another); 34 | } 35 | 36 | private boolean equalTo(CompletionTokensDetails another) { 37 | return Objects.equals(reasoningTokens, another.reasoningTokens); 38 | } 39 | 40 | @Override 41 | public int hashCode() { 42 | int h = 5381; 43 | h += (h << 5) + Objects.hashCode(reasoningTokens); 44 | return h; 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return "CompletionTokensDetails{" 50 | + "reasoningTokens=" + reasoningTokens 51 | + "}"; 52 | } 53 | 54 | public static Builder builder() { 55 | return new Builder(); 56 | } 57 | 58 | @JsonPOJOBuilder(withPrefix = "") 59 | @JsonIgnoreProperties(ignoreUnknown = true) 60 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 61 | public static final class Builder { 62 | 63 | private Integer reasoningTokens; 64 | 65 | private Builder() { 66 | } 67 | 68 | public Builder reasoningTokens(Integer reasoningTokens) { 69 | this.reasoningTokens = reasoningTokens; 70 | return this; 71 | } 72 | 73 | public CompletionTokensDetails build() { 74 | return new CompletionTokensDetails(this); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/shared/PromptTokensDetails.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.shared; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.Objects; 12 | 13 | @JsonDeserialize(builder = PromptTokensDetails.Builder.class) 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 16 | public final class PromptTokensDetails { 17 | 18 | @JsonProperty 19 | private final Integer cachedTokens; 20 | 21 | private PromptTokensDetails(Builder builder) { 22 | this.cachedTokens = builder.cachedTokens; 23 | } 24 | 25 | public Integer cachedTokens() { 26 | return cachedTokens; 27 | } 28 | 29 | @Override 30 | public boolean equals(Object another) { 31 | if (this == another) return true; 32 | return another instanceof PromptTokensDetails 33 | && equalTo((PromptTokensDetails) another); 34 | } 35 | 36 | private boolean equalTo(PromptTokensDetails another) { 37 | return Objects.equals(cachedTokens, another.cachedTokens); 38 | } 39 | 40 | @Override 41 | public int hashCode() { 42 | int h = 5381; 43 | h += (h << 5) + Objects.hashCode(cachedTokens); 44 | return h; 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return "PromptTokensDetails{" 50 | + "cachedTokens=" + cachedTokens 51 | + "}"; 52 | } 53 | 54 | public static Builder builder() { 55 | return new Builder(); 56 | } 57 | 58 | @JsonPOJOBuilder(withPrefix = "") 59 | @JsonIgnoreProperties(ignoreUnknown = true) 60 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 61 | public static final class Builder { 62 | 63 | private Integer cachedTokens; 64 | 65 | private Builder() { 66 | } 67 | 68 | public Builder cachedTokens(Integer cachedTokens) { 69 | this.cachedTokens = cachedTokens; 70 | return this; 71 | } 72 | 73 | public PromptTokensDetails build() { 74 | return new PromptTokensDetails(this); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/shared/StreamOptions.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.shared; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.Objects; 12 | 13 | @JsonDeserialize(builder = StreamOptions.Builder.class) 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 16 | public final class StreamOptions { 17 | 18 | @JsonProperty 19 | private final Boolean includeUsage; 20 | 21 | public StreamOptions(Builder builder) { 22 | this.includeUsage = builder.includeUsage; 23 | } 24 | 25 | public Boolean includeUsage() { 26 | return includeUsage; 27 | } 28 | 29 | @Override 30 | public boolean equals(Object another) { 31 | if (this == another) return true; 32 | return another instanceof StreamOptions 33 | && equalTo((StreamOptions) another); 34 | } 35 | 36 | private boolean equalTo(StreamOptions another) { 37 | return Objects.equals(includeUsage, another.includeUsage); 38 | } 39 | 40 | @Override 41 | public int hashCode() { 42 | int h = 5381; 43 | h += (h << 5) + Objects.hashCode(includeUsage); 44 | return h; 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return "StreamOptions{" + 50 | "includeUsage=" + includeUsage + 51 | "}"; 52 | } 53 | 54 | public static StreamOptions.Builder builder() { 55 | return new StreamOptions.Builder(); 56 | } 57 | 58 | @JsonPOJOBuilder(withPrefix = "") 59 | @JsonIgnoreProperties(ignoreUnknown = true) 60 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 61 | public static class Builder { 62 | 63 | private Boolean includeUsage; 64 | 65 | public StreamOptions.Builder includeUsage(Boolean includeUsage) { 66 | this.includeUsage = includeUsage; 67 | return this; 68 | } 69 | 70 | public StreamOptions build() { 71 | return new StreamOptions(this); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/shared/Usage.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.shared; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.PropertyNamingStrategies; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 9 | import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 10 | 11 | import java.util.Objects; 12 | 13 | @JsonDeserialize(builder = Usage.Builder.class) 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 16 | public final class Usage { 17 | 18 | @JsonProperty 19 | private final Integer totalTokens; 20 | @JsonProperty 21 | private final Integer promptTokens; 22 | @JsonProperty 23 | private final PromptTokensDetails promptTokensDetails; 24 | @JsonProperty 25 | private final Integer completionTokens; 26 | @JsonProperty 27 | private final CompletionTokensDetails completionTokensDetails; 28 | 29 | private Usage(Builder builder) { 30 | this.totalTokens = builder.totalTokens; 31 | this.promptTokens = builder.promptTokens; 32 | this.promptTokensDetails = builder.promptTokensDetails; 33 | this.completionTokens = builder.completionTokens; 34 | this.completionTokensDetails = builder.completionTokensDetails; 35 | } 36 | 37 | public Integer totalTokens() { 38 | return totalTokens; 39 | } 40 | 41 | public Integer promptTokens() { 42 | return promptTokens; 43 | } 44 | 45 | public PromptTokensDetails promptTokensDetails() { 46 | return promptTokensDetails; 47 | } 48 | 49 | public Integer completionTokens() { 50 | return completionTokens; 51 | } 52 | 53 | public CompletionTokensDetails completionTokensDetails() { 54 | return completionTokensDetails; 55 | } 56 | 57 | @Override 58 | public boolean equals(Object another) { 59 | if (this == another) return true; 60 | return another instanceof Usage 61 | && equalTo((Usage) another); 62 | } 63 | 64 | private boolean equalTo(Usage another) { 65 | return Objects.equals(totalTokens, another.totalTokens) 66 | && Objects.equals(promptTokens, another.promptTokens) 67 | && Objects.equals(promptTokensDetails, another.promptTokensDetails) 68 | && Objects.equals(completionTokens, another.completionTokens) 69 | && Objects.equals(completionTokensDetails, another.completionTokensDetails); 70 | } 71 | 72 | @Override 73 | public int hashCode() { 74 | int h = 5381; 75 | h += (h << 5) + Objects.hashCode(totalTokens); 76 | h += (h << 5) + Objects.hashCode(promptTokens); 77 | h += (h << 5) + Objects.hashCode(promptTokensDetails); 78 | h += (h << 5) + Objects.hashCode(completionTokens); 79 | h += (h << 5) + Objects.hashCode(completionTokensDetails); 80 | return h; 81 | } 82 | 83 | @Override 84 | public String toString() { 85 | return "Usage{" 86 | + "totalTokens=" + totalTokens 87 | + ", promptTokens=" + promptTokens 88 | + ", promptTokensDetails=" + promptTokensDetails 89 | + ", completionTokens=" + completionTokens 90 | + ", completionTokensDetails=" + completionTokensDetails 91 | + "}"; 92 | } 93 | 94 | public static Builder builder() { 95 | return new Builder(); 96 | } 97 | 98 | @JsonPOJOBuilder(withPrefix = "") 99 | @JsonIgnoreProperties(ignoreUnknown = true) 100 | @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) 101 | public static final class Builder { 102 | 103 | private Integer totalTokens; 104 | private Integer promptTokens; 105 | private PromptTokensDetails promptTokensDetails; 106 | private Integer completionTokens; 107 | private CompletionTokensDetails completionTokensDetails; 108 | 109 | private Builder() { 110 | } 111 | 112 | public Builder totalTokens(Integer totalTokens) { 113 | this.totalTokens = totalTokens; 114 | return this; 115 | } 116 | 117 | public Builder promptTokens(Integer promptTokens) { 118 | this.promptTokens = promptTokens; 119 | return this; 120 | } 121 | 122 | public Builder promptTokensDetails(PromptTokensDetails promptTokensDetails) { 123 | this.promptTokensDetails = promptTokensDetails; 124 | return this; 125 | } 126 | 127 | public Builder completionTokens(Integer completionTokens) { 128 | this.completionTokens = completionTokens; 129 | return this; 130 | } 131 | 132 | public Builder completionTokensDetails(CompletionTokensDetails completionTokensDetails) { 133 | this.completionTokensDetails = completionTokensDetails; 134 | return this; 135 | } 136 | 137 | public Usage build() { 138 | return new Usage(this); 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/spi/OpenAiClientBuilderFactory.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.spi; 2 | 3 | import dev.ai4j.openai4j.OpenAiClient; 4 | 5 | import java.util.function.Supplier; 6 | 7 | @SuppressWarnings("rawtypes") 8 | public interface OpenAiClientBuilderFactory extends Supplier { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/dev/ai4j/openai4j/spi/ServiceHelper.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.spi; 2 | 3 | import java.util.*; 4 | 5 | public class ServiceHelper { 6 | 7 | public static Collection loadFactories(Class clazz) { 8 | return loadFactories(clazz, null); 9 | } 10 | 11 | public static Collection loadFactories(Class clazz, ClassLoader classLoader) { 12 | List list = new ArrayList<>(); 13 | ServiceLoader factories; 14 | if (classLoader != null) { 15 | factories = ServiceLoader.load(clazz, classLoader); 16 | } else { 17 | // this is equivalent to: 18 | // ServiceLoader.load(clazz, TCCL); 19 | factories = ServiceLoader.load(clazz); 20 | } 21 | if (factories.iterator().hasNext()) { 22 | factories.iterator().forEachRemaining(list::add); 23 | return list; 24 | } else { 25 | // By default, ServiceLoader.load uses the TCCL, this may not be enough in environment dealing with 26 | // classloaders differently such as OSGi. So we should try to use the classloader having loaded this 27 | // class. In OSGi it would be the bundle exposing vert.x and so have access to all its classes. 28 | factories = ServiceLoader.load(clazz, ServiceHelper.class.getClassLoader()); 29 | if (factories.iterator().hasNext()) { 30 | factories.iterator().forEachRemaining(list::add); 31 | return list; 32 | } else { 33 | return Collections.emptyList(); 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/native-image/dev.ai4j/openai4j/proxy-config.json: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | "dev.ai4j.openai4j.OpenAiApi" 4 | ] 5 | ] -------------------------------------------------------------------------------- /src/main/resources/META-INF/native-image/dev.ai4j/openai4j/resource-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "resources": { 3 | "includes": [ 4 | { 5 | "pattern": ".*/.*tiktoken$" 6 | } 7 | ] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/test/java/dev/ai4j/openai4j/FilePersistorTest.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | import static org.assertj.core.api.Assertions.assertThatThrownBy; 5 | 6 | import java.io.IOException; 7 | import java.net.URI; 8 | import java.net.URISyntaxException; 9 | import java.nio.file.Path; 10 | import java.nio.file.Paths; 11 | import org.junit.jupiter.api.Test; 12 | 13 | class FilePersistorTest { 14 | 15 | private static final Path TEMP_DIR = Paths.get(System.getProperty("java.io.tmpdir")); 16 | 17 | @Test 18 | void shouldDownloadFileAndReturnFilePath() throws URISyntaxException { 19 | String fileUrl = "https://www.wikipedia.org/portal/wikipedia.org/assets/img/Wikipedia-logo-v2.png"; 20 | Path filePath = FilePersistor.persistFromUri(new URI(fileUrl), TEMP_DIR); 21 | 22 | assertThat(filePath).isNotNull(); 23 | assertThat(filePath).startsWith(TEMP_DIR); 24 | assertThat(filePath).exists(); 25 | } 26 | 27 | @Test 28 | void shouldThrowExceptionOnInvalidUrl() { 29 | assertThatThrownBy(() -> FilePersistor.persistFromUri(new URI("invalid_url"), Paths.get("destination"))) 30 | .isInstanceOf(RuntimeException.class); 31 | } 32 | 33 | @Test 34 | public void shouldDownloadFileWithCorrectContent() { 35 | 36 | URI uri = URI.create("https://raw.githubusercontent.com/langchain4j/langchain4j/main/langchain4j/src/test/resources/test-file-utf8.txt"); 37 | 38 | Path filePath = FilePersistor.persistFromUri(uri, TEMP_DIR); 39 | 40 | assertThat(filePath).isNotNull(); 41 | assertThat(filePath).startsWith(TEMP_DIR); 42 | assertThat(filePath).exists().hasContent("test\ncontent\n"); 43 | } 44 | 45 | @Test 46 | void shouldPersistFromBase64String() throws IOException { 47 | String base64EncodedString = "SGVsbG8gd29ybGQh"; // Sample "Hello world!" in base64 48 | 49 | Path filePath = FilePersistor.persistFromBase64String(base64EncodedString, TEMP_DIR); 50 | 51 | assertThat(filePath).isNotNull(); 52 | assertThat(filePath).startsWith(TEMP_DIR); 53 | assertThat(filePath).exists().hasContent("Hello world!"); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/dev/ai4j/openai4j/JsonTest.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import dev.ai4j.openai4j.chat.*; 6 | import dev.ai4j.openai4j.moderation.ModerationResponse; 7 | import dev.ai4j.openai4j.shared.Usage; 8 | import org.junit.jupiter.api.Test; 9 | 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | import static org.assertj.core.api.Assertions.assertThat; 16 | import static org.junit.jupiter.api.Assertions.assertEquals; 17 | import static org.junit.jupiter.api.Assertions.assertNotNull; 18 | 19 | public class JsonTest { 20 | 21 | private final ObjectMapper objectMapper = new ObjectMapper(); 22 | 23 | @Test 24 | public void chatCompletionResponse() throws JsonProcessingException { 25 | String content = "Paris"; 26 | 27 | List toolCalls = new ArrayList<>(); 28 | ToolCall toolCall = ToolCall.builder() 29 | .id("call_123") 30 | .type(ToolType.FUNCTION) 31 | .function(FunctionCall.builder() 32 | .name("get_capital") 33 | .arguments("{\"country\": \"France\"}") 34 | .build()) 35 | .build(); 36 | toolCalls.add(toolCall); 37 | 38 | List choices = new ArrayList<>(); 39 | ChatCompletionChoice choice = ChatCompletionChoice.builder() 40 | .index(0) 41 | .delta(Delta.builder() 42 | .content(content) 43 | .toolCalls(toolCalls) 44 | .role(Role.ASSISTANT) 45 | .build()) 46 | .message(AssistantMessage.builder() 47 | .toolCalls(toolCalls) 48 | .build()) 49 | .build(); 50 | choices.add(choice); 51 | 52 | ChatCompletionResponse response = ChatCompletionResponse.builder() 53 | .id("chatcmpl-123") 54 | .created(1234567890) 55 | .model("gpt-3.5-turbo") 56 | .choices(choices) 57 | .usage(Usage.builder() 58 | .promptTokens(10) 59 | .completionTokens(2) 60 | .totalTokens(12) 61 | .build() 62 | ) 63 | .build(); 64 | 65 | String json = objectMapper.writeValueAsString(response); 66 | assertThat(json).contains(content); 67 | assertThat(json).contains("tool_calls"); 68 | 69 | ChatCompletionResponse duplicate = objectMapper.readValue(json, ChatCompletionResponse.class); 70 | assertEquals(response, duplicate); 71 | } 72 | 73 | @Test 74 | public void deserializeChatCompletionResponse() throws IOException { 75 | InputStream inputStream = getClass().getResourceAsStream("/ChatCompletionResponse.json"); 76 | ChatCompletionResponse chatCompletionResponse = objectMapper.readValue(inputStream, ChatCompletionResponse.class); 77 | AssistantMessage message = chatCompletionResponse.choices().get(0).message(); 78 | assertNotNull(message.toolCalls()); 79 | } 80 | 81 | @Test 82 | public void deserializeModerationResponse() throws IOException { 83 | InputStream inputStream = getClass().getResourceAsStream("/ModerationResponse.json"); 84 | ModerationResponse moderationResponse = objectMapper.readValue(inputStream, ModerationResponse.class); 85 | assertThat(moderationResponse.results().get(0).categories().hateThreatening()).isFalse(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/test/java/dev/ai4j/openai4j/RateLimitAwareTest.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | 5 | public abstract class RateLimitAwareTest { 6 | 7 | @BeforeEach 8 | void waitInOrderToAvoid429ResponseFromOpenAi() { 9 | try { 10 | Thread.sleep(1000); 11 | } catch (InterruptedException e) { 12 | // ignoring intentionally 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/dev/ai4j/openai4j/RequestLoggingInterceptorTest.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static dev.ai4j.openai4j.RequestLoggingInterceptor.format; 6 | import static dev.ai4j.openai4j.RequestLoggingInterceptor.maskSecretKey; 7 | import static org.assertj.core.api.Assertions.assertThat; 8 | 9 | class RequestLoggingInterceptorTest { 10 | 11 | @Test 12 | void should_mask_secret_headers() { 13 | 14 | assertThat(format("Authorization", null)) 15 | .isEqualTo("[Authorization: null]"); 16 | assertThat(format("Authorization", "Bearer 1234567890")) 17 | .isEqualTo("[Authorization: Bearer 12345...90]"); 18 | assertThat(format("authorization", "Bearer 1234567890")) 19 | .isEqualTo("[authorization: Bearer 12345...90]"); 20 | 21 | assertThat(format("x-api-key", null)) 22 | .isEqualTo("[x-api-key: null]"); 23 | assertThat(format("x-api-key", "1234567890")) 24 | .isEqualTo("[x-api-key: 12345...90]"); 25 | assertThat(format("X-API-KEY", "1234567890")) 26 | .isEqualTo("[X-API-KEY: 12345...90]"); 27 | 28 | assertThat(format("X-Auth-Token", null)) 29 | .isEqualTo("[X-Auth-Token: null]"); 30 | assertThat(format("X-Auth-Token", "1234567890")) 31 | .isEqualTo("[X-Auth-Token: 12345...90]"); 32 | assertThat(format("x-auth-token", "1234567890")) 33 | .isEqualTo("[x-auth-token: 12345...90]"); 34 | } 35 | 36 | @Test 37 | void should_mask_secret() { 38 | 39 | assertThat(maskSecretKey(null)).isNull(); 40 | assertThat(maskSecretKey("")).isEqualTo(""); 41 | assertThat(maskSecretKey(" ")).isEqualTo(" "); 42 | assertThat(maskSecretKey("key")).isEqualTo("..."); 43 | assertThat(maskSecretKey("Bearer sk-1234567890")).isEqualTo("Bearer sk-12...90"); 44 | assertThat(maskSecretKey("sk-1234567890")).isEqualTo("sk-12...90"); 45 | assertThat(maskSecretKey("gsk_1234567890")).isEqualTo("gsk_1...90"); 46 | assertThat(maskSecretKey("Bearer gsk_1234567890")).isEqualTo("Bearer gsk_1...90"); 47 | } 48 | } -------------------------------------------------------------------------------- /src/test/java/dev/ai4j/openai4j/chat/ChatCompletionRequestTest.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.chat; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static java.util.Arrays.asList; 6 | import static org.assertj.core.api.Assertions.assertThat; 7 | 8 | class ChatCompletionRequestTest { 9 | 10 | @Test 11 | void should_add_messages_as_list() { 12 | 13 | SystemMessage systemMessage = SystemMessage.from("system"); 14 | UserMessage userMessage = UserMessage.from("user"); 15 | AssistantMessage assistantMessage = AssistantMessage.from("user"); 16 | ToolMessage toolMessage = ToolMessage.from("id", "tool"); 17 | 18 | ChatCompletionRequest request = ChatCompletionRequest.builder() 19 | .messages(asList(systemMessage, userMessage, assistantMessage, toolMessage)) 20 | .build(); 21 | 22 | assertThat(request.messages()).containsExactly(systemMessage, userMessage, assistantMessage, toolMessage); 23 | } 24 | 25 | @Test 26 | void should_add_messages_as_varargs() { 27 | 28 | SystemMessage systemMessage = SystemMessage.from("system"); 29 | UserMessage userMessage = UserMessage.from("user"); 30 | AssistantMessage assistantMessage = AssistantMessage.from("assistant"); 31 | ToolMessage toolMessage = ToolMessage.from("id", "tool"); 32 | 33 | ChatCompletionRequest request = ChatCompletionRequest.builder() 34 | .messages(systemMessage, userMessage, assistantMessage, toolMessage) 35 | .build(); 36 | 37 | assertThat(request.messages()).containsExactly(systemMessage, userMessage, assistantMessage, toolMessage); 38 | } 39 | 40 | @Test 41 | void should_add_messages_using_convenience_methods() { 42 | 43 | ChatCompletionRequest request = ChatCompletionRequest.builder() 44 | .addSystemMessage("system") 45 | .addUserMessage("user") 46 | .addAssistantMessage("assistant") 47 | .addToolMessage("id", "tool") 48 | .build(); 49 | 50 | assertThat(request.messages()).containsExactly( 51 | SystemMessage.from("system"), 52 | UserMessage.from("user"), 53 | AssistantMessage.from("assistant"), 54 | ToolMessage.from("id", "tool") 55 | ); 56 | } 57 | } -------------------------------------------------------------------------------- /src/test/java/dev/ai4j/openai4j/completion/CompletionAsyncTest.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.completion; 2 | 3 | import dev.ai4j.openai4j.OpenAiClient; 4 | import dev.ai4j.openai4j.RateLimitAwareTest; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.util.concurrent.CompletableFuture; 8 | 9 | import static java.util.concurrent.TimeUnit.SECONDS; 10 | import static org.assertj.core.api.Assertions.assertThat; 11 | 12 | class CompletionAsyncTest extends RateLimitAwareTest { 13 | 14 | private static final String PROMPT = "write exactly the following 2 words: 'hello world'"; 15 | 16 | private final OpenAiClient client = OpenAiClient.builder() 17 | .baseUrl(System.getenv("OPENAI_BASE_URL")) 18 | .openAiApiKey(System.getenv("OPENAI_API_KEY")) 19 | .logRequests() 20 | .logResponses() 21 | .build(); 22 | 23 | @Test 24 | void testSimpleApi() throws Exception { 25 | 26 | CompletableFuture future = new CompletableFuture<>(); 27 | 28 | 29 | client.completion(PROMPT) 30 | .onResponse(future::complete) 31 | .onError(future::completeExceptionally) 32 | .execute(); 33 | 34 | 35 | String response = future.get(30, SECONDS); 36 | 37 | assertThat(response).containsIgnoringCase("hello world"); 38 | } 39 | 40 | @Test 41 | void testCustomizableApi() throws Exception { 42 | 43 | CompletionRequest request = CompletionRequest.builder() 44 | .prompt(PROMPT) 45 | .build(); 46 | 47 | CompletableFuture future = new CompletableFuture<>(); 48 | 49 | 50 | client.completion(request) 51 | .onResponse(future::complete) 52 | .onError(future::completeExceptionally) 53 | .execute(); 54 | 55 | 56 | CompletionResponse response = future.get(30, SECONDS); 57 | 58 | assertThat(response.choices()).hasSize(1); 59 | assertThat(response.choices().get(0).text()).containsIgnoringCase("hello world"); 60 | 61 | assertThat(response.text()).containsIgnoringCase("hello world"); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/dev/ai4j/openai4j/completion/CompletionTest.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.completion; 2 | 3 | import dev.ai4j.openai4j.OpenAiClient; 4 | import dev.ai4j.openai4j.RateLimitAwareTest; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import static org.assertj.core.api.Assertions.assertThat; 8 | 9 | class CompletionTest extends RateLimitAwareTest { 10 | 11 | private static final String PROMPT = "write exactly the following 2 words: 'hello world'"; 12 | 13 | private final OpenAiClient client = OpenAiClient.builder() 14 | .baseUrl(System.getenv("OPENAI_BASE_URL")) 15 | .openAiApiKey(System.getenv("OPENAI_API_KEY")) 16 | .logRequests() 17 | .logResponses() 18 | .build(); 19 | 20 | @Test 21 | void testSimpleApi() { 22 | 23 | String response = client.completion(PROMPT).execute(); 24 | 25 | assertThat(response).containsIgnoringCase("hello world"); 26 | } 27 | 28 | @Test 29 | void testCustomizableApi() { 30 | 31 | CompletionRequest request = CompletionRequest.builder() 32 | .prompt(PROMPT) 33 | .build(); 34 | 35 | 36 | CompletionResponse response = client.completion(request).execute(); 37 | 38 | 39 | assertThat(response.choices()).hasSize(1); 40 | assertThat(response.choices().get(0).text()).containsIgnoringCase("hello world"); 41 | 42 | assertThat(response.text()).containsIgnoringCase("hello world"); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/dev/ai4j/openai4j/embedding/EmbeddingsAsyncTest.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.embedding; 2 | 3 | import dev.ai4j.openai4j.OpenAiClient; 4 | import dev.ai4j.openai4j.RateLimitAwareTest; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.util.List; 8 | import java.util.concurrent.CompletableFuture; 9 | 10 | import static java.util.concurrent.TimeUnit.SECONDS; 11 | import static org.assertj.core.api.Assertions.assertThat; 12 | 13 | public class EmbeddingsAsyncTest extends RateLimitAwareTest { 14 | 15 | private static final String INPUT = "hello"; 16 | 17 | private final OpenAiClient client = OpenAiClient.builder() 18 | .baseUrl(System.getenv("OPENAI_BASE_URL")) 19 | .openAiApiKey(System.getenv("OPENAI_API_KEY")) 20 | .logRequests() 21 | .logResponses() 22 | .build(); 23 | 24 | @Test 25 | void testSimpleApi() throws Exception { 26 | 27 | CompletableFuture> future = new CompletableFuture<>(); 28 | 29 | 30 | client.embedding(INPUT) 31 | .onResponse(future::complete) 32 | .onError(future::completeExceptionally) 33 | .execute(); 34 | 35 | 36 | List response = future.get(30, SECONDS); 37 | 38 | assertThat(response).hasSize(1536); 39 | } 40 | 41 | @Test 42 | void testCustomizableApi() throws Exception { 43 | 44 | EmbeddingRequest request = EmbeddingRequest.builder() 45 | .input(INPUT) 46 | .build(); 47 | 48 | CompletableFuture future = new CompletableFuture<>(); 49 | 50 | 51 | client.embedding(request) 52 | .onResponse(future::complete) 53 | .onError(future::completeExceptionally) 54 | .execute(); 55 | 56 | 57 | EmbeddingResponse response = future.get(30, SECONDS); 58 | 59 | assertThat(response.data()).hasSize(1); 60 | assertThat(response.data().get(0).embedding()).hasSize(1536); 61 | 62 | assertThat(response.embedding()).hasSize(1536); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/test/java/dev/ai4j/openai4j/embedding/EmbeddingsTest.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.embedding; 2 | 3 | import dev.ai4j.openai4j.OpenAiClient; 4 | import dev.ai4j.openai4j.RateLimitAwareTest; 5 | import org.junit.jupiter.api.Test; 6 | import org.junit.jupiter.params.ParameterizedTest; 7 | import org.junit.jupiter.params.provider.Arguments; 8 | import org.junit.jupiter.params.provider.EnumSource; 9 | import org.junit.jupiter.params.provider.MethodSource; 10 | 11 | import java.util.List; 12 | import java.util.stream.Stream; 13 | 14 | import static java.util.Collections.singletonList; 15 | import static org.assertj.core.api.Assertions.assertThat; 16 | import static org.junit.jupiter.params.provider.EnumSource.Mode.EXCLUDE; 17 | 18 | public class EmbeddingsTest extends RateLimitAwareTest { 19 | 20 | private static final String INPUT = "hello"; 21 | private static final String INPUT_2 = "hi"; 22 | 23 | private final OpenAiClient client = OpenAiClient.builder() 24 | .baseUrl(System.getenv("OPENAI_BASE_URL")) 25 | .openAiApiKey(System.getenv("OPENAI_API_KEY")) 26 | .logRequests() 27 | .logResponses() 28 | .build(); 29 | 30 | @Test 31 | void testSimpleApi() { 32 | 33 | List embedding = client.embedding("hello").execute(); 34 | 35 | assertThat(embedding).hasSize(1536); 36 | } 37 | 38 | @MethodSource 39 | @ParameterizedTest 40 | void testCustomizableApi(EmbeddingRequest request) { 41 | 42 | EmbeddingResponse response = client.embedding(request).execute(); 43 | 44 | 45 | assertThat(response.data()).hasSize(1); 46 | assertThat(response.data().get(0).embedding()).hasSize(1536); 47 | 48 | assertThat(response.embedding()).hasSize(1536); 49 | } 50 | 51 | static Stream testCustomizableApi() { 52 | return Stream.of( 53 | Arguments.of( 54 | EmbeddingRequest.builder() 55 | .input(singletonList(INPUT)) 56 | .build() 57 | ), 58 | Arguments.of( 59 | EmbeddingRequest.builder() 60 | .input(INPUT) 61 | .build() 62 | ) 63 | ); 64 | } 65 | 66 | @ParameterizedTest 67 | @EnumSource(EmbeddingModel.class) 68 | void testCustomizableApiSingleEmbedding(EmbeddingModel model) { 69 | 70 | // given 71 | EmbeddingRequest request = EmbeddingRequest.builder() 72 | .model(model) 73 | .input(INPUT) 74 | .build(); 75 | 76 | // when 77 | EmbeddingResponse response = client.embedding(request).execute(); 78 | 79 | // then 80 | assertThat(response.data()).hasSize(1); 81 | assertThat(response.data().get(0).embedding()).isNotEmpty(); 82 | 83 | assertThat(response.embedding()).isNotEmpty(); 84 | } 85 | 86 | @ParameterizedTest 87 | @EnumSource(EmbeddingModel.class) 88 | void testCustomizableApiMultipleEmbeddings(EmbeddingModel model) { 89 | 90 | // given 91 | EmbeddingRequest request = EmbeddingRequest.builder() 92 | .model(model) 93 | .input(INPUT, INPUT_2) 94 | .build(); 95 | 96 | // when 97 | EmbeddingResponse response = client.embedding(request).execute(); 98 | 99 | // then 100 | assertThat(response.data()).hasSize(2); 101 | assertThat(response.data().get(0).embedding()).isNotEmpty(); 102 | assertThat(response.data().get(1).embedding()).isNotEmpty(); 103 | } 104 | 105 | @ParameterizedTest 106 | @EnumSource(value = EmbeddingModel.class, mode = EXCLUDE, names = {"TEXT_EMBEDDING_ADA_002"}) 107 | void testShortenedDimensions(EmbeddingModel model) { 108 | 109 | // given 110 | int dimensions = 128; 111 | 112 | EmbeddingRequest request = EmbeddingRequest.builder() 113 | .model(model) 114 | .input(INPUT) 115 | .dimensions(dimensions) 116 | .build(); 117 | 118 | // when 119 | EmbeddingResponse response = client.embedding(request).execute(); 120 | 121 | // then 122 | assertThat(response.data()).hasSize(1); 123 | assertThat(response.data().get(0).embedding()).hasSize(dimensions); 124 | 125 | assertThat(response.embedding()).hasSize(dimensions); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/test/java/dev/ai4j/openai4j/image/ImagesGenerationTest.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.image; 2 | 3 | import static dev.ai4j.openai4j.image.ImageModel.DALL_E_2; 4 | import static dev.ai4j.openai4j.image.ImageModel.DALL_E_RESPONSE_FORMAT_B64_JSON; 5 | import static dev.ai4j.openai4j.image.ImageModel.DALL_E_SIZE_256_x_256; 6 | import static org.assertj.core.api.Assertions.assertThat; 7 | 8 | import dev.ai4j.openai4j.OpenAiClient; 9 | import java.io.File; 10 | import java.net.URI; 11 | import org.junit.jupiter.api.Test; 12 | 13 | public class ImagesGenerationTest { 14 | 15 | @Test 16 | void generationShouldWork() { 17 | OpenAiClient client = OpenAiClient 18 | .builder() 19 | .openAiApiKey(System.getenv("OPENAI_API_KEY")) 20 | .logRequests() 21 | .logResponses() 22 | .build(); 23 | 24 | GenerateImagesRequest request = GenerateImagesRequest 25 | .builder() 26 | .model(DALL_E_2) // so that you pay not much :) 27 | .size(DALL_E_SIZE_256_x_256) 28 | .prompt("Beautiful house on country side") 29 | .build(); 30 | 31 | GenerateImagesResponse response = client.imagesGeneration(request).execute(); 32 | 33 | URI remoteImage = response.data().get(0).url(); 34 | 35 | System.out.println("Your remote image is here: " + remoteImage); 36 | 37 | assertThat(response.data()).hasSize(1); 38 | assertThat(response.data().get(0).url()).isNotNull(); 39 | } 40 | 41 | @Test 42 | void generationWithDownloadShouldWork() { 43 | OpenAiClient client = OpenAiClient 44 | .builder() 45 | .openAiApiKey(System.getenv("OPENAI_API_KEY")) 46 | .logRequests() 47 | .logResponses() 48 | .withPersisting() 49 | .build(); 50 | 51 | GenerateImagesRequest request = GenerateImagesRequest 52 | .builder() 53 | .model("dall-e-2") // so that you pay not much :) 54 | .size(DALL_E_SIZE_256_x_256) 55 | .prompt("Bird flying in the sky") 56 | .build(); 57 | 58 | GenerateImagesResponse response = client.imagesGeneration(request).execute(); 59 | 60 | URI localImage = response.data().get(0).url(); 61 | 62 | System.out.println("Your local image is here: " + localImage); 63 | 64 | assertThat(new File(localImage)).exists(); 65 | } 66 | 67 | @Test 68 | void shouldPersistImageFromBase64Json() { 69 | OpenAiClient client = OpenAiClient 70 | .builder() 71 | .openAiApiKey(System.getenv("OPENAI_API_KEY")) 72 | .withPersisting() 73 | .logRequests() 74 | .logResponses() 75 | .build(); 76 | 77 | GenerateImagesRequest request = GenerateImagesRequest 78 | .builder() 79 | .model(DALL_E_2) // so that you pay not much :) 80 | .size(DALL_E_SIZE_256_x_256) 81 | .responseFormat(DALL_E_RESPONSE_FORMAT_B64_JSON) 82 | .prompt("Beautiful house on country side") 83 | .build(); 84 | 85 | GenerateImagesResponse response = client.imagesGeneration(request).execute(); 86 | 87 | URI localImage = response.data().get(0).url(); 88 | 89 | System.out.println("Your local image is here: " + localImage); 90 | 91 | assertThat(new File(localImage)).exists(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/test/java/dev/ai4j/openai4j/moderation/ModerationAsyncTest.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.moderation; 2 | 3 | import dev.ai4j.openai4j.OpenAiClient; 4 | import dev.ai4j.openai4j.RateLimitAwareTest; 5 | import org.junit.jupiter.api.Test; 6 | import org.junit.jupiter.params.ParameterizedTest; 7 | import org.junit.jupiter.params.provider.EnumSource; 8 | 9 | import java.util.concurrent.CompletableFuture; 10 | 11 | import static dev.ai4j.openai4j.moderation.ModerationTest.assertAllFields; 12 | import static java.util.concurrent.TimeUnit.SECONDS; 13 | import static org.assertj.core.api.Assertions.assertThat; 14 | 15 | public class ModerationAsyncTest extends RateLimitAwareTest { 16 | 17 | private static final String INPUT = "hello"; 18 | 19 | private final OpenAiClient client = OpenAiClient.builder() 20 | .baseUrl(System.getenv("OPENAI_BASE_URL")) 21 | .openAiApiKey(System.getenv("OPENAI_API_KEY")) 22 | .logRequests() 23 | .logResponses() 24 | .build(); 25 | 26 | @Test 27 | void testSimpleApi() throws Exception { 28 | 29 | CompletableFuture future = new CompletableFuture<>(); 30 | 31 | 32 | client.moderation(INPUT) 33 | .onResponse(future::complete) 34 | .onError(future::completeExceptionally) 35 | .execute(); 36 | 37 | 38 | ModerationResult response = future.get(30, SECONDS); 39 | 40 | assertAllFields(response); 41 | } 42 | 43 | @ParameterizedTest 44 | @EnumSource(ModerationModel.class) 45 | void testCustomizableApi(ModerationModel model) throws Exception { 46 | 47 | ModerationRequest request = ModerationRequest.builder() 48 | .model(model) 49 | .input(INPUT) 50 | .build(); 51 | 52 | CompletableFuture future = new CompletableFuture<>(); 53 | 54 | 55 | client.moderation(request) 56 | .onResponse(future::complete) 57 | .onError(future::completeExceptionally) 58 | .execute(); 59 | 60 | 61 | ModerationResponse response = future.get(30, SECONDS); 62 | 63 | assertThat(response.results()).isNotEmpty(); 64 | assertThat(response.results()).hasSize(1); 65 | 66 | ModerationResult moderationResult = response.results().get(0); 67 | 68 | assertAllFields(moderationResult); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/test/java/dev/ai4j/openai4j/moderation/ModerationTest.java: -------------------------------------------------------------------------------- 1 | package dev.ai4j.openai4j.moderation; 2 | 3 | import dev.ai4j.openai4j.OpenAiClient; 4 | import dev.ai4j.openai4j.RateLimitAwareTest; 5 | import org.junit.jupiter.api.Test; 6 | import org.junit.jupiter.params.ParameterizedTest; 7 | import org.junit.jupiter.params.provider.EnumSource; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | 11 | public class ModerationTest extends RateLimitAwareTest { 12 | 13 | private static final String INPUT = "hello"; 14 | 15 | private final OpenAiClient client = OpenAiClient.builder() 16 | .baseUrl(System.getenv("OPENAI_BASE_URL")) 17 | .openAiApiKey(System.getenv("OPENAI_API_KEY")) 18 | .logRequests() 19 | .logResponses() 20 | .build(); 21 | 22 | @Test 23 | void testSimpleApi() { 24 | 25 | ModerationResult moderationResult = client.moderation(INPUT).execute(); 26 | 27 | 28 | assertAllFields(moderationResult); 29 | } 30 | 31 | @ParameterizedTest 32 | @EnumSource(ModerationModel.class) 33 | void testCustomizableApi(ModerationModel model) { 34 | 35 | ModerationRequest request = ModerationRequest.builder() 36 | .model(model) 37 | .input(INPUT) 38 | .build(); 39 | 40 | 41 | ModerationResponse response = client.moderation(request).execute(); 42 | 43 | 44 | assertThat(response.results()).isNotEmpty(); 45 | assertThat(response.results()).hasSize(1); 46 | 47 | ModerationResult moderationResult = response.results().get(0); 48 | 49 | assertAllFields(moderationResult); 50 | } 51 | 52 | static void assertAllFields(ModerationResult moderationResult) { 53 | assertThat(moderationResult.categories().hate()).isFalse(); 54 | assertThat(moderationResult.categories().hateThreatening()).isFalse(); 55 | assertThat(moderationResult.categories().selfHarm()).isFalse(); 56 | assertThat(moderationResult.categories().sexual()).isFalse(); 57 | assertThat(moderationResult.categories().sexualMinors()).isFalse(); 58 | assertThat(moderationResult.categories().violence()).isFalse(); 59 | assertThat(moderationResult.categories().violenceGraphic()).isFalse(); 60 | 61 | assertThat(moderationResult.categoryScores().hate()).isNotNull(); 62 | assertThat(moderationResult.categoryScores().hateThreatening()).isNotNull(); 63 | assertThat(moderationResult.categoryScores().selfHarm()).isNotNull(); 64 | assertThat(moderationResult.categoryScores().sexual()).isNotNull(); 65 | assertThat(moderationResult.categoryScores().sexualMinors()).isNotNull(); 66 | assertThat(moderationResult.categoryScores().violence()).isNotNull(); 67 | assertThat(moderationResult.categoryScores().violenceGraphic()).isNotNull(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/test/resources/ChatCompletionResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "chatcmpl-A0Xrul5c1eG3NdRfoHvmQS5f1qymN", 3 | "object": "chat.completion", 4 | "created": 1724693554, 5 | "model": "gpt-4o-2024-05-13", 6 | "choices": [ 7 | { 8 | "index": 0, 9 | "message": { 10 | "role": "assistant", 11 | "content": null, 12 | "tool_calls": [ 13 | { 14 | "id": "call_ezsB42qA2ag598BT3maL8Arv", 15 | "type": "function", 16 | "function": { 17 | "name": "get_current_weather", 18 | "arguments": "{\"location\":\"Boston, MA\",\"unit\":\"FAHRENHEIT\"}" 19 | } 20 | } 21 | ], 22 | "refusal": null 23 | }, 24 | "logprobs": null, 25 | "finish_reason": "tool_calls" 26 | } 27 | ], 28 | "usage": { 29 | "prompt_tokens": 83, 30 | "completion_tokens": 25, 31 | "total_tokens": 108 32 | }, 33 | "system_fingerprint": "fp_3aa7262c27" 34 | } -------------------------------------------------------------------------------- /src/test/resources/ModerationResponse.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "modr-A0gYG102tVzYczejH97g8NGmyBxNT", 3 | "model": "text-moderation-007", 4 | "results": [ 5 | { 6 | "flagged": false, 7 | "categories": { 8 | "sexual": false, 9 | "hate": false, 10 | "harassment": false, 11 | "self-harm": false, 12 | "sexual/minors": false, 13 | "hate/threatening": false, 14 | "violence/graphic": false, 15 | "self-harm/intent": false, 16 | "self-harm/instructions": false, 17 | "harassment/threatening": false, 18 | "violence": false 19 | }, 20 | "category_scores": { 21 | "sexual": 0.0001711429504211992, 22 | "hate": 7.521297789025994e-7, 23 | "harassment": 7.250432190630818e-6, 24 | "self-harm": 0.00006592276622541249, 25 | "sexual/minors": 5.8848841035796795e-6, 26 | "hate/threatening": 1.0636680869424708e-8, 27 | "violence/graphic": 5.037931600782031e-7, 28 | "self-harm/intent": 0.00011194113176316023, 29 | "self-harm/instructions": 0.000012192058420623653, 30 | "harassment/threatening": 5.685025143975508e-7, 31 | "violence": 4.7343964979518205e-6 32 | } 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /todo.txt: -------------------------------------------------------------------------------- 1 | - javadocs 2 | - explicitly validate all request fields according to OpenAi spec 3 | - automatically handle 429 (wait and retry?) 4 | - propagate complete error message in exception if !200 5 | - mask all keys, not only sk-... 6 | - support custom thread pools 7 | - custom exceptions 8 | - option to define defaults in service builder: model, temperature, etc 9 | - chat completion methods in service with a list/varargs of messages 10 | - way to stop async/streaming 11 | - validate stream field for sync/async/streaming --------------------------------------------------------------------------------