├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .mvn └── wrapper │ └── maven-wrapper.properties ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── pom.xml └── src ├── main ├── java │ └── io │ │ └── github │ │ └── reactiveclown │ │ └── openaiwebfluxclient │ │ ├── ClientAutoConfiguration.java │ │ ├── ClientAutoConfigurationProperties.java │ │ ├── client │ │ ├── UsageData.java │ │ ├── audio │ │ │ ├── AudioService.java │ │ │ ├── AudioServiceImpl.java │ │ │ ├── CreateTranscriptionRequest.java │ │ │ ├── CreateTranscriptionResponse.java │ │ │ ├── CreateTranslationRequest.java │ │ │ └── CreateTranslationResponse.java │ │ ├── chat │ │ │ ├── ChatService.java │ │ │ ├── ChatServiceImpl.java │ │ │ ├── ChoiceData.java │ │ │ ├── CreateChatCompletionRequest.java │ │ │ ├── CreateChatCompletionResponse.java │ │ │ └── MessageData.java │ │ ├── completions │ │ │ ├── CompletionsService.java │ │ │ ├── CompletionsServiceImpl.java │ │ │ ├── CreateCompletionRequest.java │ │ │ └── CreateCompletionResponse.java │ │ ├── configuration │ │ │ └── ClientConfigurationProperties.java │ │ ├── edits │ │ │ ├── CreateEditRequest.java │ │ │ ├── CreateEditResponse.java │ │ │ ├── EditsService.java │ │ │ └── EditsServiceImpl.java │ │ ├── embeddings │ │ │ ├── CreateEmbeddingsRequest.java │ │ │ ├── CreateEmbeddingsResponse.java │ │ │ ├── EmbeddingsService.java │ │ │ └── EmbeddingsServiceImpl.java │ │ ├── files │ │ │ ├── DeleteFileResponse.java │ │ │ ├── FilesService.java │ │ │ ├── FilesServiceImpl.java │ │ │ ├── ListFilesResponse.java │ │ │ ├── RetrieveFileResponse.java │ │ │ ├── UploadFileRequest.java │ │ │ └── UploadFileResponse.java │ │ ├── finetunes │ │ │ ├── CancelFineTuneResponse.java │ │ │ ├── CreateFineTuneRequest.java │ │ │ ├── CreateFineTuneResponse.java │ │ │ ├── DeleteFineTuneModelResponse.java │ │ │ ├── FineTuneServiceImpl.java │ │ │ ├── FineTunesService.java │ │ │ ├── ListFineTuneEventsResponse.java │ │ │ ├── ListFineTunesResponse.java │ │ │ └── RetrieveFineTuneResponse.java │ │ ├── images │ │ │ ├── CreateImageEditRequest.java │ │ │ ├── CreateImageEditResponse.java │ │ │ ├── CreateImageRequest.java │ │ │ ├── CreateImageResponse.java │ │ │ ├── CreateImageVariationRequest.java │ │ │ ├── CreateImageVariationResponse.java │ │ │ ├── ImageServiceImpl.java │ │ │ └── ImagesService.java │ │ ├── models │ │ │ ├── ListModelsResponse.java │ │ │ ├── ModelsService.java │ │ │ ├── ModelsServiceImpl.java │ │ │ └── RetrieveModelResponse.java │ │ └── moderations │ │ │ ├── CreateModerationRequest.java │ │ │ ├── CreateModerationResponse.java │ │ │ ├── ModerationsService.java │ │ │ └── ModerationsServiceImpl.java │ │ └── exception │ │ └── OpenAiException.java └── resources │ └── META-INF │ └── spring │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports └── test └── java └── io └── github └── reactiveclown └── openaiwebfluxclient └── requests └── CreateCompletionRequestTest.java /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: '' 6 | assignees: reactiveclown 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Describe how can we reproduce the issue, if possible try to navigate us to the bugged piece of code :) 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Screenshots** 20 | If applicable, add screenshots to help explain your problem. 21 | 22 | **Desktop (please complete the following information):** 23 | - OS: [e.g. Windows] 24 | - Java version [e.g. Java 17] 25 | - Library version [e.g. 0.9.1] 26 | 27 | **Additional context** 28 | Add any other context about the problem here. 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FR]" 5 | labels: '' 6 | assignees: reactiveclown 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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | 14 | # Created by https://www.gitignore.io/api/git,java,maven,eclipse,windows 15 | 16 | ### Eclipse ### 17 | 18 | .metadata 19 | bin/ 20 | tmp/ 21 | *.tmp 22 | *.bak 23 | *.swp 24 | *~.nib 25 | local.properties 26 | .settings/ 27 | .loadpath 28 | .recommenders 29 | 30 | # External tool builders 31 | .externalToolBuilders/ 32 | 33 | # Locally stored "Eclipse launch configurations" 34 | *.launch 35 | 36 | # PyDev specific (Python IDE for Eclipse) 37 | *.pydevproject 38 | 39 | # CDT-specific (C/C++ Development Tooling) 40 | .cproject 41 | 42 | # CDT- autotools 43 | .autotools 44 | 45 | # Java annotation processor (APT) 46 | .factorypath 47 | 48 | # PDT-specific (PHP Development Tools) 49 | .buildpath 50 | 51 | # sbteclipse plugin 52 | .target 53 | 54 | # Tern plugin 55 | .tern-project 56 | 57 | # TeXlipse plugin 58 | .texlipse 59 | 60 | # STS (Spring Tool Suite) 61 | .springBeans 62 | 63 | # Code Recommenders 64 | .recommenders/ 65 | 66 | # Annotation Processing 67 | .apt_generated/ 68 | 69 | # Scala IDE specific (Scala & Java development for Eclipse) 70 | .cache-main 71 | .scala_dependencies 72 | .worksheet 73 | 74 | ### Eclipse Patch ### 75 | # Eclipse Core 76 | .project 77 | 78 | # JDT-specific (Eclipse Java Development Tools) 79 | .classpath 80 | 81 | # Annotation Processing 82 | .apt_generated 83 | 84 | .sts4-cache/ 85 | 86 | ### Git ### 87 | # Created by git for backups. To disable backups in Git: 88 | # $ git config --global mergetool.keepBackup false 89 | *.orig 90 | 91 | # Created by git when using merge tools for conflicts 92 | *.BACKUP.* 93 | *.BASE.* 94 | *.LOCAL.* 95 | *.REMOTE.* 96 | *_BACKUP_*.txt 97 | *_BASE_*.txt 98 | *_LOCAL_*.txt 99 | *_REMOTE_*.txt 100 | 101 | ### Java ### 102 | # Compiled class file 103 | *.class 104 | 105 | # Log file 106 | *.log 107 | 108 | # BlueJ files 109 | *.ctxt 110 | 111 | # Mobile Tools for Java (J2ME) 112 | .mtj.tmp/ 113 | 114 | # Package Files # 115 | *.jar 116 | *.war 117 | *.nar 118 | *.ear 119 | *.zip 120 | *.tar.gz 121 | *.rar 122 | 123 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 124 | hs_err_pid* 125 | 126 | ### Maven ### 127 | target/ 128 | pom.xml.tag 129 | pom.xml.releaseBackup 130 | pom.xml.versionsBackup 131 | pom.xml.next 132 | release.properties 133 | dependency-reduced-pom.xml 134 | buildNumber.properties 135 | .mvn/timing.properties 136 | .mvn/wrapper/maven-wrapper.jar 137 | 138 | ### Windows ### 139 | # Windows thumbnail cache files 140 | Thumbs.db 141 | ehthumbs.db 142 | ehthumbs_vista.db 143 | 144 | # Dump file 145 | *.stackdump 146 | 147 | # Folder config file 148 | [Dd]esktop.ini 149 | 150 | # Recycle Bin used on file shares 151 | $RECYCLE.BIN/ 152 | 153 | # Windows Installer files 154 | *.cab 155 | *.msi 156 | *.msix 157 | *.msm 158 | *.msp 159 | 160 | # Windows shortcuts 161 | *.lnk 162 | 163 | ### IntelliJ IDEA ### 164 | .idea 165 | *.iws 166 | *.iml 167 | *.ipr 168 | 169 | ### NetBeans ### 170 | /nbproject/private/ 171 | /nbbuild/ 172 | /dist/ 173 | /nbdist/ 174 | /.nb-gradle/ 175 | build/ 176 | !**/src/main/**/build/ 177 | !**/src/test/**/build/ 178 | 179 | ### VS Code ### 180 | .vscode/ 181 | 182 | ### Some additional ignores (sort later) 183 | *.DS_Store 184 | *.sw? 185 | .#* 186 | *# 187 | *~ 188 | .classpath 189 | .project 190 | .settings 191 | bin 192 | build 193 | target 194 | dependency-reduced-pom.xml 195 | *.sublime-* 196 | /scratch 197 | .gradle 198 | README.html 199 | *.iml 200 | .idea 201 | .exercism -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar 19 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | clown.reactive@gmail.com void.rozze@gmail.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributing to openai-webflux-java 3 | 4 | First off, thanks for taking the time to contribute! ❤️ 5 | 6 | All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution. It will make it a lot easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions. 🎉 7 | 8 | > And if you like the project, but just don't have time to contribute, that's fine. There are other easy ways to support the project and show your appreciation, which we would also be very happy about: 9 | > - Star the project 10 | > - Tweet about it 11 | > - Refer this project in your project's readme 12 | > - Mention the project at local meetups and tell your friends/colleagues 13 | 14 | 15 | ## Table of Contents 16 | 17 | - [I Have a Question](#i-have-a-question) 18 | - [I Want To Contribute](#i-want-to-contribute) 19 | - [Reporting Bugs](#reporting-bugs) 20 | - [Suggesting Enhancements](#suggesting-enhancements) 21 | - [Your First Code Contribution](#your-first-code-contribution) 22 | - [Improving The Documentation](#improving-the-documentation) 23 | - [Styleguides](#styleguides) 24 | - [Commit Messages](#commit-messages) 25 | - [Join The Project Team](#join-the-project-team) 26 | 27 | 28 | 29 | ## I Have a Question 30 | 31 | > If you want to ask a question, we assume that you have read the available [Documentation](https://platform.openai.com/docs/api-reference). 32 | 33 | Before you ask a question, it is best to search for existing [Issues](https://github.com/reactiveclown/openai-webflux-java/issues) that might help you. In case you have found a suitable issue and still need clarification, you can write your question in this issue. It is also advisable to search the internet for answers first. 34 | 35 | If you then still feel the need to ask a question and need clarification, we recommend the following: 36 | 37 | - Open an [Issue](https://github.com/reactiveclown/openai-webflux-java/issues/new). 38 | - Provide as much context as you can about what you're running into. 39 | - Provide project and platform versions (nodejs, npm, etc), depending on what seems relevant. 40 | 41 | We will then take care of the issue as soon as possible. 42 | 43 | 57 | 58 | ## I Want To Contribute 59 | 60 | > ### Legal Notice 61 | > When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project license. 62 | 63 | ### Reporting Bugs 64 | 65 | 66 | #### Before Submitting a Bug Report 67 | 68 | A good bug report shouldn't leave others needing to chase you up for more information. Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report. Please complete the following steps in advance to help us fix any potential bug as fast as possible. 69 | 70 | - Make sure that you are using the latest version. 71 | - Determine if your bug is really a bug and not an error on your side e.g. using incompatible environment components/versions (Make sure that you have read the [documentation](https://platform.openai.com/docs/api-reference). If you are looking for support, you might want to check [this section](#i-have-a-question)). 72 | - To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already a bug report existing for your bug or error in the [bug tracker](https://github.com/reactiveclown/openai-webflux-javaissues?q=label%3Abug). 73 | - Also make sure to search the internet (including Stack Overflow) to see if users outside of the GitHub community have discussed the issue. 74 | - Collect information about the bug: 75 | - Stack trace (Traceback) 76 | - OS, Platform and Version (Windows, Linux, macOS, x86, ARM) 77 | - Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on what seems relevant. 78 | - Possibly your input and the output 79 | - Can you reliably reproduce the issue? And can you also reproduce it with older versions? 80 | 81 | 82 | #### How Do I Submit a Good Bug Report? 83 | 84 | > You must never report security related issues, vulnerabilities or bugs including sensitive information to the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to . 85 | 86 | 87 | We use GitHub issues to track bugs and errors. If you run into an issue with the project: 88 | 89 | - Open an [Issue](https://github.com/reactiveclown/openai-webflux-java/issues/new). (Since we can't be sure at this point whether it is a bug or not, we ask you not to talk about a bug yet and not to label the issue.) 90 | - Explain the behavior you would expect and the actual behavior. 91 | - Please provide as much context as possible and describe the *reproduction steps* that someone else can follow to recreate the issue on their own. This usually includes your code. For good bug reports you should isolate the problem and create a reduced test case. 92 | - Provide the information you collected in the previous section. 93 | 94 | Once it's filed: 95 | 96 | - The project team will label the issue accordingly. 97 | - A team member will try to reproduce the issue with your provided steps. If there are no reproduction steps or no obvious way to reproduce the issue, the team will ask you for those steps and mark the issue as `needs-repro`. Bugs with the `needs-repro` tag will not be addressed until they are reproduced. 98 | - If the team is able to reproduce the issue, it will be marked `needs-fix`, as well as possibly other tags (such as `critical`), and the issue will be left to be [implemented by someone](#your-first-code-contribution). 99 | 100 | 101 | 102 | 103 | ### Suggesting Enhancements 104 | 105 | This section guides you through submitting an enhancement suggestion for openai-webflux-java, **including completely new features and minor improvements to existing functionality**. Following these guidelines will help maintainers and the community to understand your suggestion and find related suggestions. 106 | 107 | 108 | #### Before Submitting an Enhancement 109 | 110 | - Make sure that you are using the latest version. 111 | - Read the [documentation](https://platform.openai.com/docs/api-reference) carefully and find out if the functionality is already covered, maybe by an individual configuration. 112 | - Perform a [search](https://github.com/reactiveclown/openai-webflux-java/issues) to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. 113 | - Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the project's developers of the merits of this feature. Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing an add-on/plugin library. 114 | 115 | 116 | #### How Do I Submit a Good Enhancement Suggestion? 117 | 118 | Enhancement suggestions are tracked as [GitHub issues](https://github.com/reactiveclown/openai-webflux-java/issues). 119 | 120 | - Use a **clear and descriptive title** for the issue to identify the suggestion. 121 | - Provide a **step-by-step description of the suggested enhancement** in as many details as possible. 122 | - **Describe the current behavior** and **explain which behavior you expected to see instead** and why. At this point you can also tell which alternatives do not work for you. 123 | - You may want to **include screenshots and animated GIFs** which help you demonstrate the steps or point out the part which the suggestion is related to. You can use [this tool](https://www.cockos.com/licecap/) to record GIFs on macOS and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://github.com/GNOME/byzanz) on Linux. 124 | - **Explain why this enhancement would be useful** to most openai-webflux-java users. You may also want to point out the other projects that solved it better and which could serve as inspiration. 125 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Maksym Volkov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Maven Central](https://img.shields.io/maven-central/v/io.github.reactiveclown/openai-webflux-client-spring-boot-starter?color=green) 2 | ![Stars](https://img.shields.io/github/stars/reactiveclown/openai-webflux-java?style=social) 3 | # Reactive OpenAI API client library. 4 | 5 | This is the Reactive OpenAI client library. 6 | 7 | It was written using WebFlux and Spring Boot. The library also contains pre-configured Spring Boot starter for easier 8 | usage. 9 | 10 | The library is unofficial and community-maintained. Feel free to join the development. 11 | 12 | # Table of Contents 13 | 14 | - ### [Supported APIs](#supported) 15 | - ### [Installation](#installations) 16 | - ### [Usage](#usages) 17 | - ### [Contributing](#contributings) 18 | - ### [License](#licence) 19 | 20 | ## Supported APIs 21 | 22 | - [Models service](#models-service) 23 | - [Completions service](#completions-service) 24 | - [Chat service](#chat-service) 25 | - [Edits service](#edits-service) 26 | - [Images service](#images-service) 27 | - [Embeddings service](#embeddings-service) 28 | - [Audio service](#audio-service) 29 | - [Files service](#files-service) 30 | - [Fine-tunes service](#fine-tunes-service) 31 | - [Moderations service](#moderations-service) 32 | 33 | ## Installation 34 | 35 | Dependency import via **Maven**: 36 | 37 | ```xml 38 | 39 | 40 | io.github.reactiveclown 41 | openai-webflux-client-spring-boot-starter 42 | 0.9.1 43 | 44 | ``` 45 | 46 | Dependency import via **Gradle**: 47 | 48 | ```implementation 'io.github.reactiveclown:openai-webflux-client-spring-boot-starter:0.9.1'``` 49 | 50 | 51 | After adding the dependency remember to add yor OpenAI api key to the application.properties. 52 | ```properties 53 | com.github.reactiveclown.openai.apiKey=OPENAI_API_KEY 54 | ``` 55 | 56 | You can also add the Organization id and change baseUrl by providing next configuration variables. 57 | But this step is not mandatory one. 58 | ```properties 59 | com.github.reactiveclown.openai.organizationId=OPENAI_ORGANIZATION_ID 60 | com.github.reactiveclown.openai.baseUrl=OPENAI_CUSTOM_BASE_URL 61 | ``` 62 | 63 | ## Usage 64 | 65 | Here you can find some usage examples. 66 | Also, kindly use the official documentation in order to get more information about parameters. 67 | 68 | --- 69 | #### Models service 70 | 71 | [OpenAI Models Docs](https://platform.openai.com/docs/api-reference/models) 72 | 73 | Models service is used to retrieve the list of models or information about specific model. 74 | 75 | ```java 76 | @Service 77 | public class ExampleService { 78 | 79 | private final ModelsService service; 80 | 81 | public ExampleService(ModelsService service) { 82 | this.service = service; 83 | } 84 | 85 | public Mono listModels() { 86 | return service.listModels(); 87 | } 88 | 89 | public Mono retrieveModel(String modelName) { 90 | return service.retrieveModel(modelName); 91 | } 92 | } 93 | ``` 94 | --- 95 | 96 | #### Completions service 97 | 98 | [OpenAI Completions Docs](https://platform.openai.com/docs/api-reference/completions) 99 | 100 | Completions service is used to complete the text. Fell free to play around with builder parameters. 101 | 102 | ```java 103 | @Service 104 | public class ExampleService { 105 | 106 | private final CompletionsService service; 107 | 108 | public ExampleService(CompletionsService service) { 109 | this.service = service; 110 | } 111 | 112 | public Mono createCompletion() { 113 | return service.createCompletion( 114 | CreateCompletionRequest 115 | .builder("babbage") 116 | .n(2) 117 | .bestOf(1) 118 | .build()); 119 | } 120 | } 121 | ``` 122 | --- 123 | 124 | #### Chat service 125 | 126 | [OpenAI Chat Docs](https://platform.openai.com/docs/api-reference/chat) 127 | 128 | Chat service is used to chat with the chat models. Fell free to play around with builder parameters. 129 | 130 | ```java 131 | @Service 132 | public class ExampleService { 133 | 134 | private final ChatService service; 135 | 136 | public ExampleService(ChatService service) { 137 | this.service = service; 138 | } 139 | 140 | public Mono createChatCompletion() { 141 | return service.createChatCompletion( 142 | CreateChatCompletionRequest 143 | .builder("gpt-3.5-turbo", List.of(new MessageData("user","do something"))) 144 | .n(3) 145 | .build()); 146 | } 147 | } 148 | ``` 149 | --- 150 | 151 | #### Edits service 152 | 153 | [OpenAI Edits Docs](https://platform.openai.com/docs/api-reference/edits) 154 | 155 | Edits service is used to make an edits to the provided input. Fell free to play around with builder parameters. 156 | 157 | ```java 158 | @Service 159 | public class ExampleService { 160 | 161 | private final EditsService service; 162 | 163 | public ExampleService(EditsService service) { 164 | this.service = service; 165 | } 166 | 167 | public Mono createEdit() { 168 | return service.createEdit( 169 | CreateEditRequest 170 | .builder("babbage", "Add one digit after every word") 171 | .input("One Two Three") 172 | .build()); 173 | } 174 | } 175 | ``` 176 | --- 177 | 178 | #### Images service 179 | 180 | [OpenAI Images Docs](https://platform.openai.com/docs/api-reference/images) 181 | 182 | Image service is used to generate a different images and their transformations. 183 | > ⚠️ As for now, methods that are requiring images are blocking. In the nearest future it is planned to add async FilePart implementation. 184 | 185 | ```java 186 | @Service 187 | public class ExampleService { 188 | 189 | private final ImagesService service; 190 | 191 | public ExampleService(ImagesService service) { 192 | this.service = service; 193 | } 194 | 195 | //Non-blocking 196 | public Mono createImage() { 197 | return service.createImage( 198 | CreateImageRequest 199 | .builder("Generate a digital art of Ukraine") 200 | .build()); 201 | } 202 | 203 | //Blocking 204 | public Mono createImageVariation() { 205 | return service.createImageVariation( 206 | CreateImageVariationRequest 207 | .builder("src/main/resources/exampleImage.png") 208 | .size("512x512") 209 | .build()); 210 | } 211 | 212 | //Blocking 213 | public Mono createImageEdit() { 214 | return service.createImageEdit( 215 | CreateImageEditRequest 216 | .builder("src/main/resources/exampleImage.png", "Generate a green fields") 217 | .mask("src/main/resources/exampleMask.png") 218 | .build()); 219 | } 220 | } 221 | ``` 222 | --- 223 | 224 | #### Embeddings service 225 | 226 | [OpenAI Embeddings Docs](https://platform.openai.com/docs/api-reference/embeddings) 227 | 228 | Embedding service is used to create embeddings. Fell free to play around with builder parameters. 229 | 230 | ```java 231 | @Service 232 | public class ExampleService { 233 | 234 | private final EmbeddingsService service; 235 | 236 | public ExampleService(EmbeddingsService service) { 237 | this.service = service; 238 | } 239 | 240 | public Mono createEmbeddings(){ 241 | return service.createEmbeddings( 242 | CreateEmbeddingsRequest 243 | .builder("babbage", "example input") 244 | .build()); 245 | } 246 | } 247 | ``` 248 | --- 249 | 250 | #### Audio service 251 | 252 | [OpenAI Audio Docs](https://platform.openai.com/docs/api-reference/audio) 253 | 254 | Audio service is used to turn audio into text, also to make a translations into English. Fell free to play around with builder parameters. 255 | > ⚠️ As for now, methods that are requiring audio files are blocking. In the nearest future it is planned to add async FilePart implementation. 256 | 257 | ```java 258 | @Service 259 | public class ExampleService { 260 | 261 | private final AudioService service; 262 | 263 | public ExampleService(AudioService service) { 264 | this.service = service; 265 | } 266 | 267 | //Blocking 268 | public Mono createTranscription() { 269 | return service.createTranscription( 270 | CreateTranscriptionRequest 271 | .builder("src/main/resources/exampleAudio.mp3", "whisper-1") 272 | .build()); 273 | } 274 | 275 | //Blocking 276 | public Mono createTranslation(){ 277 | return service.createTranslation(CreateTranslationRequest 278 | .builder("src/main/resources/exampleAudio.mp3", "whisper-1") 279 | .build()); 280 | } 281 | } 282 | ``` 283 | --- 284 | 285 | #### Files service 286 | 287 | [OpenAI Files Docs](https://platform.openai.com/docs/api-reference/files) 288 | 289 | File service is used to upload and work with files. Fell free to play around with builder parameters. 290 | > ⚠️ As for now, methods that are requiring files are blocking. In the nearest future it is planned to add async FilePart implementation. 291 | 292 | ```java 293 | @Service 294 | public class ExampleService { 295 | 296 | private final FilesService service; 297 | 298 | public ExampleService(FilesService service) { 299 | this.service = service; 300 | } 301 | 302 | public Mono listFilesResponse() { 303 | return service.listFiles(); 304 | } 305 | 306 | //Blocking 307 | public Mono uploadFile() { 308 | return service.uploadFile( 309 | UploadFileRequest 310 | .builder("src/main/resources/exampleFile.jsonl", "finetune") 311 | .build()); 312 | } 313 | 314 | public Mono deleteFile() { 315 | return service.deleteFile("fileId"); 316 | } 317 | 318 | public Mono retrieveFile() { 319 | return service.retrieveFile("fileId"); 320 | } 321 | 322 | public Mono retrieveFileContent() { 323 | return service.retrieveFileContent("fileId"); 324 | } 325 | } 326 | ``` 327 | --- 328 | 329 | #### Fine-tunes service 330 | 331 | [OpenAI Fine-tunes Docs](https://platform.openai.com/docs/api-reference/fine-tunes) 332 | 333 | Fine-tunes service is used to work with fine-tune files and models. Fell free to play around with builder parameters. 334 | 335 | ```java 336 | @Service 337 | public class ExampleService { 338 | 339 | private final FineTunesService service; 340 | 341 | public ExampleService(FineTunesService service) { 342 | this.service = service; 343 | } 344 | 345 | public Mono createFineTune(){ 346 | return service.createFineTune( 347 | CreateFineTuneRequest 348 | .builder("trainingFileId") 349 | .build()); 350 | } 351 | 352 | public Mono listFineTunes(){ 353 | return service.listFineTunes(); 354 | } 355 | 356 | public Mono retrieveFineTune(){ 357 | return service.retrieveFineTunes("fineTuneId"); 358 | } 359 | 360 | public Mono cancelFineTune(){ 361 | return service.cancelFineTune("fineTuneId"); 362 | } 363 | 364 | public Mono listFineTuneEvents(){ 365 | return service.listFineTuneEvents("fineTuneId"); 366 | } 367 | 368 | public Mono deleteFineTuneModel(){ 369 | return service.deleteFineTuneModel("modelId"); 370 | } 371 | } 372 | ``` 373 | --- 374 | 375 | #### Moderations service 376 | 377 | [OpenAI Moderations Docs](https://platform.openai.com/docs/api-reference/moderations) 378 | 379 | Moderations service is used to check for moderation violations. Fell free to play around with builder parameters. 380 | 381 | ```java 382 | @Service 383 | public class ExampleService { 384 | 385 | private final ModerationsService service; 386 | 387 | public ExampleService(ModerationsService service) { 388 | this.service = service; 389 | } 390 | 391 | public Mono createModeration() { 392 | return service.createModeration( 393 | CreateModerationRequest.builder("violating input").build()); 394 | } 395 | } 396 | ``` 397 | 398 | ## How can I help? 399 | 400 | This is the open-source library, any help will be much appreciated. If you can see that there is a way to improve the code or functionality, 401 | kindly fill out the issue or make a pull request with your changes. Also feel free to look at opened issues and submit a pull request if you have your solution. 402 | 403 | ## License 404 | 405 | ```text 406 | MIT License 407 | 408 | Copyright (c) 2023 Maksym Volkov 409 | 410 | Permission is hereby granted, free of charge, to any person obtaining a copy 411 | of this software and associated documentation files (the "Software"), to deal 412 | in the Software without restriction, including without limitation the rights 413 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 414 | copies of the Software, and to permit persons to whom the Software is 415 | furnished to do so, subject to the following conditions: 416 | 417 | The above copyright notice and this permission notice shall be included in all 418 | copies or substantial portions of the Software. 419 | 420 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 421 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 422 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 423 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 424 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 425 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 426 | SOFTWARE. 427 | ``` -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 3.0.5 10 | 11 | 12 | 13 | 14 | 17 15 | 16 | 17 | io.github.reactiveclown 18 | openai-webflux-client-spring-boot-starter 19 | 0.9.1 20 | ${project.groupId}:${project.artifactId} 21 | OpenAI Webflux API client with spring boot starter 22 | https://github.com/reactiveclown/openai-webflux-java 23 | 24 | 25 | 26 | MIT License 27 | http://www.opensource.org/licenses/mit-license.php 28 | 29 | 30 | 31 | 32 | scm:git:git://github.com/reactiveclown/openai-webflux-java.git 33 | scm:git:ssh://github.com:reactiveclown/openai-webflux-java.git 34 | https://github.com/reactiveclown/openai-webflux-java 35 | 36 | 37 | 38 | 39 | Maksym Volkov 40 | clown.reactive@gmail.com 41 | io.github.reactiveclown 42 | https://github.com/reactiveclown 43 | 44 | 45 | 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-starter 50 | 51 | 52 | org.springframework.boot 53 | spring-boot-starter-webflux 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-autoconfigure-processor 58 | 59 | 60 | 61 | org.springframework.boot 62 | spring-boot-starter-test 63 | test 64 | 65 | 66 | io.projectreactor 67 | reactor-test 68 | test 69 | 70 | 71 | 72 | 73 | 74 | ossrh 75 | https://s01.oss.sonatype.org/content/repositories/snapshots 76 | 77 | 78 | ossrh 79 | https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ 80 | 81 | 82 | 83 | 84 | 85 | org.sonatype.plugins 86 | nexus-staging-maven-plugin 87 | 1.6.13 88 | true 89 | 90 | ossrh 91 | https://s01.oss.sonatype.org/ 92 | true 93 | 94 | 95 | 96 | org.apache.maven.plugins 97 | maven-source-plugin 98 | 3.2.1 99 | 100 | 101 | attach-sources 102 | 103 | jar-no-fork 104 | 105 | 106 | 107 | 108 | 109 | org.apache.maven.plugins 110 | maven-javadoc-plugin 111 | 3.5.0 112 | 113 | 114 | attach-javadocs 115 | 116 | jar 117 | 118 | 119 | 120 | 121 | 122 | org.apache.maven.plugins 123 | maven-gpg-plugin 124 | 3.0.1 125 | 126 | 127 | sign-artifacts 128 | verify 129 | 130 | sign 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/ClientAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient; 2 | 3 | import io.github.reactiveclown.openaiwebfluxclient.client.audio.AudioService; 4 | import io.github.reactiveclown.openaiwebfluxclient.client.audio.AudioServiceImpl; 5 | import io.github.reactiveclown.openaiwebfluxclient.client.chat.ChatService; 6 | import io.github.reactiveclown.openaiwebfluxclient.client.chat.ChatServiceImpl; 7 | import io.github.reactiveclown.openaiwebfluxclient.client.completions.CompletionsService; 8 | import io.github.reactiveclown.openaiwebfluxclient.client.completions.CompletionsServiceImpl; 9 | import io.github.reactiveclown.openaiwebfluxclient.client.edits.EditsService; 10 | import io.github.reactiveclown.openaiwebfluxclient.client.edits.EditsServiceImpl; 11 | import io.github.reactiveclown.openaiwebfluxclient.client.embeddings.EmbeddingsService; 12 | import io.github.reactiveclown.openaiwebfluxclient.client.embeddings.EmbeddingsServiceImpl; 13 | import io.github.reactiveclown.openaiwebfluxclient.client.files.FilesService; 14 | import io.github.reactiveclown.openaiwebfluxclient.client.files.FilesServiceImpl; 15 | import io.github.reactiveclown.openaiwebfluxclient.client.finetunes.FineTuneServiceImpl; 16 | import io.github.reactiveclown.openaiwebfluxclient.client.finetunes.FineTunesService; 17 | import io.github.reactiveclown.openaiwebfluxclient.client.images.ImageServiceImpl; 18 | import io.github.reactiveclown.openaiwebfluxclient.client.images.ImagesService; 19 | import io.github.reactiveclown.openaiwebfluxclient.client.models.ModelsService; 20 | import io.github.reactiveclown.openaiwebfluxclient.client.models.ModelsServiceImpl; 21 | import io.github.reactiveclown.openaiwebfluxclient.client.moderations.ModerationsService; 22 | import io.github.reactiveclown.openaiwebfluxclient.client.moderations.ModerationsServiceImpl; 23 | import io.github.reactiveclown.openaiwebfluxclient.exception.OpenAiException; 24 | import org.springframework.beans.factory.annotation.Qualifier; 25 | import org.springframework.boot.autoconfigure.AutoConfiguration; 26 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 27 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 28 | import org.springframework.context.annotation.Bean; 29 | import org.springframework.context.annotation.Configuration; 30 | import org.springframework.http.HttpHeaders; 31 | import org.springframework.web.reactive.function.client.ExchangeFilterFunction; 32 | import org.springframework.web.reactive.function.client.WebClient; 33 | import reactor.core.publisher.Mono; 34 | 35 | @Configuration 36 | @AutoConfiguration 37 | @EnableConfigurationProperties(ClientAutoConfigurationProperties.class) 38 | public class ClientAutoConfiguration { 39 | 40 | 41 | private final ClientAutoConfigurationProperties properties; 42 | 43 | public ClientAutoConfiguration(ClientAutoConfigurationProperties properties) { 44 | this.properties = properties; 45 | } 46 | 47 | public ExchangeFilterFunction errorHandlerExchangeFilterFunction() { 48 | return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> { 49 | if (clientResponse.statusCode().isError()) { 50 | return clientResponse.bodyToMono(String.class) 51 | .flatMap(errorMessage -> Mono.error( 52 | new OpenAiException(clientResponse.statusCode().value(), 53 | errorMessage))); 54 | } 55 | return Mono.just(clientResponse); 56 | }); 57 | } 58 | 59 | @Bean 60 | @Qualifier("OpenAIClient") 61 | public WebClient webClient() { 62 | return WebClient.builder() 63 | .baseUrl(properties.baseUrl()) 64 | .defaultHeaders(httpHeaders -> { 65 | httpHeaders.add(HttpHeaders.AUTHORIZATION, String.format("Bearer %s", properties.apiKey())); 66 | if (properties.organizationId() != null) 67 | httpHeaders.add("OpenAI-Organization", properties.organizationId()); 68 | }) 69 | .filter(errorHandlerExchangeFilterFunction()) 70 | .build(); 71 | } 72 | 73 | @Bean 74 | @ConditionalOnMissingBean 75 | public AudioService audioService(@Qualifier("OpenAIClient") WebClient client) { 76 | return new AudioServiceImpl(client); 77 | } 78 | 79 | @Bean 80 | @ConditionalOnMissingBean 81 | public ChatService chatService(@Qualifier("OpenAIClient") WebClient client) { 82 | return new ChatServiceImpl(client); 83 | } 84 | 85 | @Bean 86 | @ConditionalOnMissingBean 87 | public CompletionsService completionsService(@Qualifier("OpenAIClient") WebClient client) { 88 | return new CompletionsServiceImpl(client); 89 | } 90 | 91 | @Bean 92 | @ConditionalOnMissingBean 93 | public EditsService editsService(@Qualifier("OpenAIClient") WebClient client) { 94 | return new EditsServiceImpl(client); 95 | } 96 | 97 | @Bean 98 | @ConditionalOnMissingBean 99 | public EmbeddingsService embeddingsService(@Qualifier("OpenAIClient") WebClient client) { 100 | return new EmbeddingsServiceImpl(client); 101 | } 102 | 103 | @Bean 104 | @ConditionalOnMissingBean 105 | public FilesService filesService(@Qualifier("OpenAIClient") WebClient client) { 106 | return new FilesServiceImpl(client); 107 | } 108 | 109 | @Bean 110 | @ConditionalOnMissingBean 111 | public FineTunesService fineTunesService(@Qualifier("OpenAIClient") WebClient client) { 112 | return new FineTuneServiceImpl(client); 113 | } 114 | 115 | @Bean 116 | @ConditionalOnMissingBean 117 | public ImagesService imagesService(@Qualifier("OpenAIClient") WebClient client) { 118 | return new ImageServiceImpl(client); 119 | } 120 | 121 | @Bean 122 | @ConditionalOnMissingBean 123 | public ModelsService modelsService(@Qualifier("OpenAIClient") WebClient client) { 124 | return new ModelsServiceImpl(client); 125 | } 126 | 127 | @Bean 128 | @ConditionalOnMissingBean 129 | public ModerationsService moderationsService(@Qualifier("OpenAIClient") WebClient client){ 130 | return new ModerationsServiceImpl(client); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/ClientAutoConfigurationProperties.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | @ConfigurationProperties(prefix = "com.github.reactiveclown.openai") 6 | public record ClientAutoConfigurationProperties(String apiKey, 7 | String organizationId, 8 | String baseUrl) { 9 | public ClientAutoConfigurationProperties{ 10 | if (apiKey == null) 11 | throw new IllegalArgumentException("apiKey can't be null," 12 | + " please add com.github.reactiveclown.openai.apiKey property" + 13 | " to the configuration"); 14 | if (baseUrl == null) 15 | baseUrl = "https://api.openai.com/v1"; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/UsageData.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public record UsageData(@JsonProperty("prompt_tokens") Integer promptTokens, 6 | @JsonProperty("completion_tokens") Integer completionTokens, 7 | @JsonProperty("total_tokens") Integer total_tokens) { 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/audio/AudioService.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.audio; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | /** 6 | * Service to turn audio into text. 7 | */ 8 | public interface AudioService { 9 | 10 | /** 11 | * Transcribes audio into the input language. 12 | * 13 | * @param request {@link CreateTranscriptionRequest} 14 | * @return A Mono of {@link CreateTranscriptionResponse} 15 | */ 16 | Mono createTranscription(CreateTranscriptionRequest request); 17 | 18 | /** 19 | * Translates audio into English. 20 | * 21 | * @param request {@link CreateTranslationRequest} 22 | * @return A Mono of {@link CreateTranslationResponse} 23 | */ 24 | Mono createTranslation(CreateTranslationRequest request); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/audio/AudioServiceImpl.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.audio; 2 | 3 | import org.springframework.core.io.PathResource; 4 | import org.springframework.http.client.MultipartBodyBuilder; 5 | import org.springframework.web.reactive.function.client.WebClient; 6 | import reactor.core.publisher.Mono; 7 | 8 | /** 9 | * {@link AudioService} implementation. 10 | */ 11 | public class AudioServiceImpl implements AudioService { 12 | 13 | private final WebClient client; 14 | 15 | /** 16 | * AudioServiceImpl constructor, accepts configured {@link WebClient} 17 | * @param client configured {@link WebClient} 18 | */ 19 | public AudioServiceImpl(WebClient client) { 20 | this.client = client; 21 | } 22 | 23 | @Override 24 | public Mono createTranscription(CreateTranscriptionRequest request) { 25 | String createTranscriptionUrl = "/audio/transcriptions"; 26 | var multipartBodyBuilder = new MultipartBodyBuilder(); 27 | multipartBodyBuilder.part("file", new PathResource(request.file())); 28 | multipartBodyBuilder.part("model", request.model()); 29 | 30 | if (request.prompt() != null) 31 | multipartBodyBuilder.part("prompt", request.prompt()); 32 | 33 | if (request.responseFormat() != null) 34 | multipartBodyBuilder.part("response_format", request.responseFormat()); 35 | 36 | if (request.temperature() != null) 37 | multipartBodyBuilder.part("temperature", request.temperature()); 38 | 39 | if (request.language() != null) 40 | multipartBodyBuilder.part("language", request.language()); 41 | 42 | 43 | return client.post() 44 | .uri(createTranscriptionUrl) 45 | .bodyValue(multipartBodyBuilder.build()) 46 | .retrieve() 47 | .bodyToMono(CreateTranscriptionResponse.class); 48 | } 49 | 50 | @Override 51 | public Mono createTranslation(CreateTranslationRequest request) { 52 | String createTranslationUrl = "/audio/transcriptions"; 53 | var multipartBodyBuilder = new MultipartBodyBuilder(); 54 | multipartBodyBuilder.part("file", new PathResource(request.file())); 55 | multipartBodyBuilder.part("model", request.model()); 56 | 57 | if (request.prompt() != null) 58 | multipartBodyBuilder.part("prompt", request.prompt()); 59 | 60 | if (request.responseFormat() != null) 61 | multipartBodyBuilder.part("response_format", request.responseFormat()); 62 | 63 | if (request.temperature() != null) 64 | multipartBodyBuilder.part("temperature", request.temperature()); 65 | 66 | return client.post() 67 | .uri(createTranslationUrl) 68 | .bodyValue(multipartBodyBuilder.build()) 69 | .retrieve() 70 | .bodyToMono(CreateTranslationResponse.class); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/audio/CreateTranscriptionRequest.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.audio; 2 | 3 | /** 4 | * CreateTranscriptionRequest. 5 | * 6 | * @param file - The audio file to transcribe, 7 | * in one of these formats: mp3, mp4, mpeg, mpga, m4a, wav, or webm. 8 | * @param model - ID of the model to use. Only whisper-1 is currently available. 9 | * @param prompt - An optional text to guide the model's style or continue a previous audio segment. 10 | * The prompt should match the audio language. 11 | * @param responseFormat - The format of the transcript output, 12 | * in one of these options: json, text, srt, verbose_json, or vtt. 13 | * @param temperature - The sampling temperature, between 0 and 1. 14 | * Higher values like 0.8 will make the output more random, 15 | * while lower values like 0.2 will make it more focused and deterministic. 16 | * If set to 0, the model will use log probability to automatically increase 17 | * the temperature until certain thresholds are hit. 18 | * @param language - The language of the input audio. 19 | * Supplying the input language in ISO-639-1 format will improve accuracy and latency. 20 | */ 21 | public record CreateTranscriptionRequest(String file, 22 | String model, 23 | String prompt, 24 | String responseFormat, 25 | Double temperature, 26 | String language) { 27 | 28 | public CreateTranscriptionRequest { 29 | if (file == null || file.isEmpty()) 30 | throw new IllegalArgumentException("file value can't be null or empty"); 31 | 32 | if (model == null || model.isBlank()) 33 | throw new IllegalArgumentException("model value can't be null or blank"); 34 | } 35 | 36 | public static Builder builder(String file, String model) { 37 | return new Builder(file, model); 38 | } 39 | 40 | public static final class Builder { 41 | String file; 42 | String model; 43 | String prompt; 44 | String responseFormat; 45 | Double temperature; 46 | String language; 47 | 48 | public CreateTranscriptionRequest build() { 49 | return new CreateTranscriptionRequest( 50 | file, model, prompt, 51 | responseFormat, temperature, language); 52 | } 53 | 54 | public Builder(String file, String model) { 55 | this.file = file; 56 | this.model = model; 57 | } 58 | 59 | public Builder prompt(String prompt) { 60 | this.prompt = prompt; 61 | return this; 62 | } 63 | 64 | public Builder responseFormat(String responseFormat) { 65 | this.responseFormat = responseFormat; 66 | return this; 67 | } 68 | 69 | 70 | public Builder temperature(Double temperature) { 71 | this.temperature = temperature; 72 | return this; 73 | } 74 | 75 | public Builder language(String language) { 76 | this.language = language; 77 | return this; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/audio/CreateTranscriptionResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.audio; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public record CreateTranscriptionResponse(@JsonProperty("text") String text) { 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/audio/CreateTranslationRequest.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.audio; 2 | 3 | /** 4 | * CreateTranslationRequest. 5 | * 6 | * @param file - The audio file to translate, 7 | * in one of these formats: mp3, mp4, mpeg, mpga, m4a, wav, or webm. 8 | * @param model - ID of the model to use. Only whisper-1 is currently available. 9 | * @param prompt - An optional text to guide the model's style or continue a previous audio segment. 10 | * The prompt should be in English. 11 | * @param responseFormat - The format of the transcript output, 12 | * in one of these options: json, text, srt, verbose_json, or vtt. 13 | * @param temperature - The sampling temperature, between 0 and 1. 14 | * Higher values like 0.8 will make the output more random, 15 | * while lower values like 0.2 will make it more focused and deterministic. 16 | * If set to 0, the model will use log probability 17 | * to automatically increase the temperature until certain thresholds are hit. 18 | */ 19 | public record CreateTranslationRequest(String file, 20 | String model, 21 | String prompt, 22 | String responseFormat, 23 | Double temperature) { 24 | public CreateTranslationRequest { 25 | if (file == null || file.isEmpty()) 26 | throw new IllegalArgumentException("file value can't be null or empty"); 27 | 28 | if (model == null || model.isBlank()) 29 | throw new IllegalArgumentException("model value can't be null or blank"); 30 | } 31 | 32 | public static Builder builder(String file, String model) { 33 | return new Builder(file, model); 34 | } 35 | 36 | public static final class Builder { 37 | String file; 38 | String model; 39 | String prompt; 40 | String responseFormat; 41 | Double temperature; 42 | 43 | public CreateTranslationRequest build() { 44 | return new CreateTranslationRequest( 45 | file, model, prompt, 46 | responseFormat, temperature); 47 | } 48 | 49 | public Builder(String file, String model) { 50 | this.file = file; 51 | this.model = model; 52 | } 53 | 54 | public Builder prompt(String prompt) { 55 | this.prompt = prompt; 56 | return this; 57 | } 58 | 59 | public Builder responseFormat(String responseFormat) { 60 | this.responseFormat = responseFormat; 61 | return this; 62 | } 63 | 64 | 65 | public Builder temperature(Double temperature) { 66 | this.temperature = temperature; 67 | return this; 68 | } 69 | } 70 | 71 | } -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/audio/CreateTranslationResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.audio; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public record CreateTranslationResponse(@JsonProperty("text") String text) { 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/chat/ChatService.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.chat; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | public interface ChatService { 6 | 7 | /** 8 | * Creates a completion for the chat message. 9 | * 10 | * @param request {@link CreateChatCompletionRequest } 11 | * @return A Mono of {@link CreateChatCompletionResponse} 12 | */ 13 | Mono createChatCompletion(CreateChatCompletionRequest request); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/chat/ChatServiceImpl.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.chat; 2 | 3 | import org.springframework.stereotype.Service; 4 | import org.springframework.web.reactive.function.client.WebClient; 5 | import reactor.core.publisher.Mono; 6 | 7 | @Service 8 | public class ChatServiceImpl implements ChatService{ 9 | 10 | private final WebClient client; 11 | public ChatServiceImpl(WebClient client){ 12 | this.client = client; 13 | } 14 | 15 | @Override 16 | public Mono createChatCompletion(CreateChatCompletionRequest request) { 17 | String createChatCompletionUrl = "/chat/completions"; 18 | return client.post() 19 | .uri(createChatCompletionUrl) 20 | .bodyValue(request) 21 | .retrieve() 22 | .bodyToMono(CreateChatCompletionResponse.class); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/chat/ChoiceData.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public record ChoiceData(@JsonProperty("index") Integer index, 6 | @JsonProperty("message") MessageData message, 7 | @JsonProperty("finish_reason") String finishReason) { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/chat/CreateChatCompletionRequest.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonInclude.Include; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | /** 11 | * CreateChatCompletionRequest. 12 | * 13 | * @param model - ID of the model to use. 14 | * See the model endpoint compatibility table for details on which models work with the Chat API. 15 | * @param messages - The messages to generate chat completions for, in the chat format. 16 | * @param temperature - What sampling temperature to use, between 0 and 2. 17 | * Higher values like 0.8 will make the output more random, 18 | * while lower values like 0.2 will make it more focused and deterministic. 19 | *

20 | * We generally recommend altering this or top_p but not both. 21 | * @param topP - An alternative to sampling with temperature, 22 | * called nucleus sampling, 23 | * where the model considers the results of the tokens with top_p probability mass. 24 | * So 0.1 means only the tokens comprising the top 10% probability mass are considered. 25 | *

26 | * We generally recommend altering this or temperature but not both. 27 | * @param n - How many chat completion choices to generate for each input message. 28 | * @param stop - Up to 4 sequences where the API will stop generating further tokens. 29 | * @param maxTokens - The maximum number of tokens to generate in the chat completion. 30 | *

31 | * The total length of input tokens and generated tokens 32 | * is limited by the model's context length. 33 | * @param presencePenalty - Number between -2.0 and 2.0. 34 | * Positive values penalize new tokens based on whether they appear in the text so far, 35 | * increasing the model's likelihood to talk about new topics. 36 | * @param frequencyPenalty - Number between -2.0 and 2.0. 37 | * Positive values penalize new tokens based on their existing frequency in the text so far, 38 | * decreasing the model's likelihood to repeat the same line verbatim. 39 | * @param logitBias - Modify the likelihood of specified tokens appearing in the completion. 40 | *

41 | * Accepts a json object that maps tokens (specified by their token ID in the tokenizer) 42 | * to an associated bias value from -100 to 100. Mathematically, 43 | * the bias is added to the logits generated by the model prior to sampling. 44 | * The exact effect will vary per model, 45 | * but values between -1 and 1 should decrease or increase likelihood of selection; 46 | * values like -100 or 100 should result in a ban or exclusive selection of the relevant token. 47 | * @param user - A unique identifier representing your end-user, 48 | * which can help OpenAI to monitor and detect abuse 49 | */ 50 | @JsonInclude(Include.NON_NULL) 51 | public record CreateChatCompletionRequest(@JsonProperty("model") String model, 52 | @JsonProperty("messages") List messages, 53 | @JsonProperty("temperature") Double temperature, 54 | @JsonProperty("top_p") Double topP, 55 | @JsonProperty("n") Integer n, 56 | @JsonProperty("stop") List stop, 57 | @JsonProperty("max_tokens") Integer maxTokens, 58 | @JsonProperty("presence_penalty") Double presencePenalty, 59 | @JsonProperty("frequency_penalty") Double frequencyPenalty, 60 | @JsonProperty("logit_bias") Map logitBias, 61 | @JsonProperty("user") String user) { 62 | public CreateChatCompletionRequest { 63 | if (model == null || model.isBlank()) 64 | throw new IllegalArgumentException("model value can't be null or blank"); 65 | 66 | if (messages == null || messages.isEmpty()) 67 | throw new IllegalArgumentException("messages can't be null or empty"); 68 | } 69 | 70 | public static Builder builder(String model, List messages) { 71 | return new Builder(model, messages); 72 | } 73 | 74 | public static final class Builder { 75 | private final String model; 76 | private final List messages; 77 | private Double temperature; 78 | private Double topP; 79 | private Integer n; 80 | private List stop; 81 | private Integer maxTokens; 82 | private Double presencePenalty; 83 | private Double frequencyPenalty; 84 | private Map logitBias; 85 | private String user; 86 | 87 | public CreateChatCompletionRequest build() { 88 | return new CreateChatCompletionRequest( 89 | model, messages, temperature, 90 | topP, n, stop, maxTokens, 91 | presencePenalty, frequencyPenalty, logitBias, 92 | user); 93 | } 94 | 95 | public Builder(String model, List messages) { 96 | this.model = model; 97 | this.messages = messages; 98 | } 99 | 100 | public Builder temperature(Double temperature) { 101 | this.temperature = temperature; 102 | return this; 103 | } 104 | 105 | public Builder topP(Double topP) { 106 | this.topP = topP; 107 | return this; 108 | } 109 | 110 | public Builder n(Integer n) { 111 | this.n = n; 112 | return this; 113 | } 114 | 115 | public Builder stop(String stop) { 116 | this.stop = List.of(stop); 117 | return this; 118 | } 119 | 120 | public Builder stop(List stop) { 121 | this.stop = stop; 122 | return this; 123 | } 124 | 125 | public Builder maxTokens(Integer maxTokens) { 126 | this.maxTokens = maxTokens; 127 | return this; 128 | } 129 | 130 | public Builder presencePenalty(Double presencePenalty) { 131 | this.presencePenalty = presencePenalty; 132 | return this; 133 | } 134 | 135 | public Builder frequencyPenalty(Double frequencyPenalty) { 136 | this.frequencyPenalty = frequencyPenalty; 137 | return this; 138 | } 139 | 140 | public Builder logitBias(Map logitBias) { 141 | this.logitBias = logitBias; 142 | return this; 143 | } 144 | 145 | public Builder user(String user) { 146 | this.user = user; 147 | return this; 148 | } 149 | 150 | } 151 | } -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/chat/CreateChatCompletionResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.github.reactiveclown.openaiwebfluxclient.client.UsageData; 5 | 6 | import java.util.List; 7 | 8 | public record CreateChatCompletionResponse(@JsonProperty("id") String id, 9 | @JsonProperty("object") String object, 10 | @JsonProperty("created") Long created, 11 | @JsonProperty("model") String model, 12 | @JsonProperty("choices") List choices, 13 | @JsonProperty("usage") UsageData usage) { 14 | } 15 | 16 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/chat/MessageData.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.chat; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public record MessageData(@JsonProperty("role") String role, 6 | @JsonProperty("content") String content){} 7 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/completions/CompletionsService.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.completions; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | public interface CompletionsService { 6 | 7 | /** 8 | * Creates a completion for the provided prompt and parameters. 9 | * 10 | * @param request {@link CreateCompletionRequest} 11 | * @return A Mono of {@link CreateCompletionResponse} 12 | */ 13 | Mono createCompletion(CreateCompletionRequest request); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/completions/CompletionsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.completions; 2 | 3 | import org.springframework.stereotype.Service; 4 | import org.springframework.web.reactive.function.client.WebClient; 5 | import reactor.core.publisher.Mono; 6 | 7 | @Service 8 | public class CompletionsServiceImpl implements CompletionsService{ 9 | 10 | private final WebClient client; 11 | 12 | public CompletionsServiceImpl(WebClient client){ 13 | this.client = client; 14 | } 15 | 16 | @Override 17 | public Mono createCompletion(CreateCompletionRequest request) { 18 | String createCompletionUri = "/completions"; 19 | return client.post() 20 | .uri(createCompletionUri) 21 | .bodyValue(request) 22 | .retrieve() 23 | .bodyToMono(CreateCompletionResponse.class); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/completions/CreateCompletionRequest.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.completions; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonInclude.Include; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | /** 11 | * CreateCompletionRequest. 12 | * 13 | * @param model - ID of the model to use. 14 | * You can use the List models API to see all of your available models, 15 | * or see our Model overview for descriptions of them. 16 | * @param prompt - The prompt(s) to generate completions for, 17 | * encoded as a string, array of strings, array of tokens, or array of token arrays. 18 | *

19 | * Note that {@literal <}|endoftext|{@literal >} is the document separator that the model sees during training, 20 | * so if a prompt is not specified the model will generate as if 21 | * from the beginning of a new document. 22 | * @param suffix - The suffix that comes after a completion of inserted text. 23 | * @param maxTokens - The maximum number of tokens to generate in the completion. 24 | *

25 | * The token count of your prompt plus max_tokens cannot exceed the model's context length. 26 | * Most models have a context length of 2048 tokens 27 | * (except for the newest models, which support 4096). 28 | * @param temperature - What sampling temperature to use, between 0 and 2. 29 | * Higher values like 0.8 will make the output more random, 30 | * while lower values like 0.2 will make it more focused and deterministic. 31 | *

32 | * We generally recommend altering this or top_p but not both. 33 | * @param topP - An alternative to sampling with temperature, called nucleus sampling, 34 | * where the model considers the results of the tokens with top_p probability mass. 35 | * So 0.1 means only the tokens comprising the top 10% probability mass are considered. 36 | *

37 | * We generally recommend altering this or temperature but not both. 38 | * @param n - How many completions to generate for each prompt. 39 | *

40 | * Note: Because this parameter generates many completions, 41 | * it can quickly consume your token quota. 42 | * Use carefully and ensure that you have reasonable settings for max_tokens and stop. 43 | * @param logprobs - Include the log probabilities on the logprobs most likely tokens, 44 | * as well the chosen tokens. For example, if logprobs is 5, 45 | * the API will return a list of the 5 most likely tokens. 46 | * The API will always return the logprob of the sampled token, 47 | * so there may be up to logprobs+1 elements in the response. 48 | *

49 | * The maximum value for logprobs is 5. If you need more than this, 50 | * please contact us through our Help center and describe your use case. 51 | * @param echo - Echo back the prompt in addition to the completion 52 | * @param stop - Up to 4 sequences where the API will stop generating further tokens. 53 | * The returned text will not contain the stop sequence 54 | * @param presencePenalty - Number between -2.0 and 2.0. 55 | * Positive values penalize new tokens based on whether they appear in the text so far, 56 | * increasing the model's likelihood to talk about new topics. 57 | * @param frequencyPenalty - Number between -2.0 and 2.0. 58 | * Positive values penalize new tokens based on their existing frequency in the text so far, 59 | * decreasing the model's likelihood to repeat the same line verbatim. 60 | * @param bestOf - Generates best_of completions server-side and returns the "best" 61 | * (the one with the highest log probability per token). Results cannot be streamed. 62 | *

63 | * When used with n, best_of controls 64 | * the number of candidate completions 65 | * and n specifies how many to return – best_of must be greater than n. 66 | *

67 | * Note: Because this parameter generates many completions, 68 | * it can quickly consume your token quota. Use carefully and ensure 69 | * that you have reasonable settings for max_tokens and stop. 70 | * @param logitBias - Modify the likelihood of specified tokens appearing in the completion. 71 | *

72 | * Accepts a json object that maps tokens 73 | * (specified by their token ID in the GPT tokenizer) 74 | * to an associated bias value from -100 to 100. 75 | * Mathematically, the bias is added to the logits generated by the model prior to sampling. 76 | * The exact effect will vary per model, 77 | * but values between -1 and 1 should decrease or increase likelihood of selection; 78 | * values like -100 or 100 should result in a ban or exclusive selection of the relevant token. 79 | *

80 | * As an example, 81 | * you can pass {"50256": -100} to prevent the {@literal <}|endoftext|{@literal >} token from being generated. 82 | * @param user - A unique identifier representing your end-user, 83 | * which can help OpenAI to monitor and detect abuse. 84 | */ 85 | @JsonInclude(Include.NON_NULL) 86 | public record CreateCompletionRequest(@JsonProperty("model") String model, 87 | @JsonProperty("prompt") List> prompt, 88 | @JsonProperty("suffix") String suffix, 89 | @JsonProperty("max_tokens") Integer maxTokens, 90 | @JsonProperty("temperature") Double temperature, 91 | @JsonProperty("top_p") Double topP, 92 | @JsonProperty("n") Integer n, 93 | @JsonProperty("logprobs") Integer logprobs, 94 | @JsonProperty("echo") Boolean echo, 95 | @JsonProperty("stop") List stop, 96 | @JsonProperty("presence_penalty") Double presencePenalty, 97 | @JsonProperty("frequency_penalty") Double frequencyPenalty, 98 | @JsonProperty("best_of") Integer bestOf, 99 | @JsonProperty("logit_bias") Map logitBias, 100 | @JsonProperty("user") String user) { 101 | public CreateCompletionRequest { 102 | if (model == null || model.isBlank()) 103 | throw new IllegalArgumentException("model value can't be null or blank"); 104 | } 105 | 106 | public static Builder builder(String model){ 107 | return new Builder(model); 108 | } 109 | 110 | public static final class Builder { 111 | private final String model; 112 | private List> prompt; 113 | private String suffix; 114 | private Integer maxTokens; 115 | private Double temperature; 116 | private Double topP; 117 | private Integer n; 118 | private Integer logprobs; 119 | private Boolean echo; 120 | private List stop; 121 | private Double presencePenalty; 122 | private Double frequencyPenalty; 123 | private Integer bestOf; 124 | private Map logitBias; 125 | private String user; 126 | 127 | public CreateCompletionRequest build() { 128 | return new CreateCompletionRequest( 129 | model, prompt, suffix, 130 | maxTokens, temperature, topP, 131 | n, logprobs, 132 | echo, stop, presencePenalty, 133 | frequencyPenalty, bestOf, logitBias, 134 | user); 135 | } 136 | 137 | public Builder(String model) { 138 | this.model = model; 139 | } 140 | 141 | public Builder prompt(String prompt) { 142 | this.prompt = List.of(List.of(prompt)); 143 | return this; 144 | } 145 | 146 | public Builder promptList(List prompt) { 147 | this.prompt = List.of(prompt); 148 | return this; 149 | } 150 | 151 | public Builder promptListOfLists(List> prompt) { 152 | this.prompt = prompt; 153 | return this; 154 | } 155 | 156 | public Builder suffix(String suffix) { 157 | this.suffix = suffix; 158 | return this; 159 | } 160 | 161 | public Builder maxTokens(Integer maxTokens) { 162 | this.maxTokens = maxTokens; 163 | return this; 164 | } 165 | 166 | public Builder temperature(Double temperature) { 167 | this.temperature = temperature; 168 | return this; 169 | } 170 | 171 | public Builder topP(Double topP) { 172 | this.topP = topP; 173 | return this; 174 | } 175 | 176 | public Builder n(Integer n) { 177 | this.n = n; 178 | return this; 179 | } 180 | 181 | public Builder logprobs(Integer logprobs) { 182 | this.logprobs = logprobs; 183 | return this; 184 | } 185 | 186 | public Builder echo(Boolean echo) { 187 | this.echo = echo; 188 | return this; 189 | } 190 | 191 | public Builder stop(String stop) { 192 | this.stop = List.of(stop); 193 | return this; 194 | } 195 | 196 | public Builder stop(List stop) { 197 | this.stop = stop; 198 | return this; 199 | } 200 | 201 | public Builder presencePenalty(Double presencePenalty) { 202 | this.presencePenalty = presencePenalty; 203 | return this; 204 | } 205 | 206 | public Builder frequencyPenalty(Double frequencyPenalty) { 207 | this.frequencyPenalty = frequencyPenalty; 208 | return this; 209 | } 210 | 211 | public Builder bestOf(Integer bestOf) { 212 | this.bestOf = bestOf; 213 | return this; 214 | } 215 | 216 | public Builder logitBias(Map logitBias) { 217 | this.logitBias = logitBias; 218 | return this; 219 | } 220 | 221 | public Builder user(String user) { 222 | this.user = user; 223 | return this; 224 | } 225 | 226 | } 227 | } 228 | 229 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/completions/CreateCompletionResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.completions; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.github.reactiveclown.openaiwebfluxclient.client.UsageData; 5 | 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | public record CreateCompletionResponse(@JsonProperty("id") String id, 10 | @JsonProperty("object") String object, 11 | @JsonProperty("created") Long created, 12 | @JsonProperty("model") String model, 13 | @JsonProperty("choices") List choices, 14 | @JsonProperty("usage") UsageData usage) { 15 | } 16 | 17 | record Choice(@JsonProperty("text") String text, 18 | @JsonProperty("index") Integer index, 19 | @JsonProperty("logprobs") Logprobs logprobs, 20 | @JsonProperty("finish_reason") String finishReason) { 21 | } 22 | 23 | record Logprobs(@JsonProperty("tokens") List tokens, 24 | @JsonProperty("token_logprobs") List tokenLogprobs, 25 | @JsonProperty("top_logprobs") List> topLogprobs, 26 | @JsonProperty("text_offset") List textOffset) { 27 | } -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/configuration/ClientConfigurationProperties.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.configuration; 2 | 3 | //@ConfigurationProperties(prefix = "openai.api") 4 | public record ClientConfigurationProperties(String apiKey, 5 | String organizationId, 6 | String baseUrl) { 7 | public ClientConfigurationProperties { 8 | if (baseUrl == null) 9 | baseUrl = "https://api.openai.com/v1"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/edits/CreateEditRequest.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.edits; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonInclude.Include; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | 7 | /** 8 | * CreateEditRequest. 9 | * 10 | * @param model - ID of the model to use. 11 | * You can use the text-davinci-edit-001 or code-davinci-edit-001 model with this endpoint. 12 | * @param input - The input text to use as a starting point for the edit. 13 | * @param instruction - The instruction that tells the model how to edit the prompt. 14 | * @param n - How many edits to generate for the input and instruction. 15 | * @param temperature - What sampling temperature to use, 16 | * between 0 and 2. Higher values like 0.8 will make the output more random, 17 | * while lower values like 0.2 will make it more focused and deterministic. 18 | *

19 | * We generally recommend altering this or top_p but not both. 20 | * @param topP - An alternative to sampling with temperature, called nucleus sampling, 21 | * where the model considers the results of the tokens with top_p probability mass. 22 | * So 0.1 means only the tokens comprising the top 10% probability mass are considered. 23 | *

24 | * We generally recommend altering this or temperature but not both. 25 | */ 26 | @JsonInclude(Include.NON_NULL) 27 | public record CreateEditRequest(@JsonProperty("model") String model, 28 | @JsonProperty("input") String input, 29 | @JsonProperty("instruction") String instruction, 30 | @JsonProperty("n") Integer n, 31 | @JsonProperty("temperature") Double temperature, 32 | @JsonProperty("top_p") Double topP) { 33 | public CreateEditRequest { 34 | if (model == null || model.isBlank()) 35 | throw new IllegalArgumentException("model value can't be null or blank"); 36 | 37 | if (instruction == null || instruction.isBlank()) 38 | throw new IllegalArgumentException("instruction value can't be null or blank"); 39 | 40 | } 41 | 42 | public static Builder builder(String model, String instruction) { 43 | return new Builder(model, instruction); 44 | } 45 | 46 | public static final class Builder { 47 | String model; 48 | String input; 49 | String instruction; 50 | Integer n; 51 | Double temperature; 52 | Double topP; 53 | 54 | public CreateEditRequest build() { 55 | return new CreateEditRequest( 56 | model, input, instruction, 57 | n, temperature, topP); 58 | } 59 | 60 | public Builder(String model, String instruction) { 61 | this.model = model; 62 | this.instruction = instruction; 63 | } 64 | 65 | public Builder input(String input) { 66 | this.input = input; 67 | return this; 68 | } 69 | 70 | public Builder n(Integer n) { 71 | this.n = n; 72 | return this; 73 | } 74 | 75 | public Builder temperature(Double temperature) { 76 | this.temperature = temperature; 77 | return this; 78 | } 79 | 80 | public Builder topP(Double topP) { 81 | this.topP = topP; 82 | return this; 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/edits/CreateEditResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.edits; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.github.reactiveclown.openaiwebfluxclient.client.UsageData; 5 | 6 | import java.util.List; 7 | 8 | public record CreateEditResponse(@JsonProperty("object") String object, 9 | @JsonProperty("created") Long created, 10 | @JsonProperty("choices") List choices, 11 | @JsonProperty("usage") UsageData usage) { 12 | } 13 | 14 | record ChoiceData(@JsonProperty("text") String text, 15 | @JsonProperty("index") Integer index) { 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/edits/EditsService.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.edits; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | public interface EditsService { 6 | /** 7 | * Creates a new edit for the provided input, instruction, and parameters. 8 | * 9 | * @param request {@link CreateEditRequest} 10 | * @return A Mono of {@link CreateEditResponse} 11 | */ 12 | Mono createEdit(CreateEditRequest request); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/edits/EditsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.edits; 2 | 3 | import org.springframework.stereotype.Service; 4 | import org.springframework.web.reactive.function.client.WebClient; 5 | import reactor.core.publisher.Mono; 6 | 7 | @Service 8 | public class EditsServiceImpl implements EditsService { 9 | 10 | private final WebClient client; 11 | 12 | public EditsServiceImpl(WebClient client) { 13 | this.client = client; 14 | } 15 | 16 | @Override 17 | public Mono createEdit(CreateEditRequest request) { 18 | String createEditUrl = "/edits"; 19 | return client.post() 20 | .uri(createEditUrl) 21 | .bodyValue(request) 22 | .retrieve() 23 | .bodyToMono(CreateEditResponse.class); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/embeddings/CreateEmbeddingsRequest.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.embeddings; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonInclude.Include; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * CreateEmbeddingsRequest. 11 | * 12 | * @param model - ID of the model to use. You can use the List models API to see all of your available models, 13 | * or see our Model overview for descriptions of them. 14 | * @param input - Input text to get embeddings for, 15 | * encoded as a string or array of tokens. 16 | * To get embeddings for multiple inputs in a single request, 17 | * pass an array of strings or array of token arrays. 18 | * Each input must not exceed 8192 tokens in length. 19 | * @param user - A unique identifier representing your end-user, 20 | * which can help OpenAI to monitor and detect abuse 21 | */ 22 | @JsonInclude(Include.NON_NULL) 23 | public record CreateEmbeddingsRequest(@JsonProperty("model") String model, 24 | @JsonProperty("input") List input, 25 | @JsonProperty("user") String user) { 26 | public CreateEmbeddingsRequest { 27 | if (model == null || model.isBlank()) 28 | throw new IllegalArgumentException("model value can't be null or blank"); 29 | 30 | if (input == null || input.isEmpty()) 31 | throw new IllegalArgumentException("input can't be null or empty"); 32 | } 33 | 34 | public static Builder builder(String model, List input) { 35 | return new Builder(model, input); 36 | } 37 | 38 | public static Builder builder(String model, String input) { 39 | return new Builder(model, input); 40 | } 41 | 42 | public static final class Builder { 43 | String model; 44 | 45 | List input; 46 | 47 | String user; 48 | 49 | public CreateEmbeddingsRequest build() { 50 | return new CreateEmbeddingsRequest( 51 | model, input, user); 52 | } 53 | 54 | public Builder(String model, List input) { 55 | this.model = model; 56 | this.input = input; 57 | } 58 | 59 | public Builder(String model, String input) { 60 | this.model = model; 61 | this.input = List.of(input); 62 | } 63 | 64 | public Builder user(String user) { 65 | this.user = user; 66 | return this; 67 | } 68 | 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/embeddings/CreateEmbeddingsResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.embeddings; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import io.github.reactiveclown.openaiwebfluxclient.client.UsageData; 5 | 6 | import java.util.List; 7 | 8 | public record CreateEmbeddingsResponse(@JsonProperty("object") String object, 9 | @JsonProperty("data") List data, 10 | @JsonProperty("model") String model, 11 | @JsonProperty("usage") UsageData usage) { 12 | } 13 | 14 | record EmbeddingData(@JsonProperty("object") String object, 15 | @JsonProperty("embedding") List embedding, 16 | @JsonProperty("index") Integer index) { 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/embeddings/EmbeddingsService.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.embeddings; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | public interface EmbeddingsService { 6 | 7 | /** 8 | * Creates an embedding vector representing the input text. 9 | * 10 | * @param request {@link CreateEmbeddingsRequest} 11 | * @return A Mono of {@link CreateEmbeddingsResponse} 12 | */ 13 | Mono createEmbeddings(CreateEmbeddingsRequest request); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/embeddings/EmbeddingsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.embeddings; 2 | 3 | import org.springframework.stereotype.Service; 4 | import org.springframework.web.reactive.function.client.WebClient; 5 | import reactor.core.publisher.Mono; 6 | 7 | @Service 8 | public class EmbeddingsServiceImpl implements EmbeddingsService { 9 | 10 | private final WebClient client; 11 | public EmbeddingsServiceImpl(WebClient client) { 12 | this.client = client; 13 | } 14 | 15 | @Override 16 | public Mono createEmbeddings(CreateEmbeddingsRequest request) { 17 | String createEmbeddings = "/embeddings"; 18 | return client.post() 19 | .uri(createEmbeddings) 20 | .bodyValue(request) 21 | .retrieve() 22 | .bodyToMono(CreateEmbeddingsResponse.class); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/files/DeleteFileResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.files; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public record DeleteFileResponse(@JsonProperty("id") String id, 6 | @JsonProperty("object") String object, 7 | @JsonProperty("deleted") Boolean deleted) { 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/files/FilesService.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.files; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | public interface FilesService { 6 | /** 7 | * Returns a list of files that belong to the user's organization. 8 | * 9 | * @return A Mono of {@link ListFilesResponse} 10 | */ 11 | Mono listFiles(); 12 | 13 | /** 14 | * Upload a file that contains document(s) to be used across various endpoints/features. 15 | * Currently, the size of all the files uploaded by one organization can be up to 1 GB. 16 | * 17 | * @param request {@link UploadFileRequest} 18 | * @return A Mono of {@link UploadFileResponse} 19 | */ 20 | Mono uploadFile(UploadFileRequest request); 21 | 22 | /** 23 | * Delete a file. 24 | * 25 | * @param fileId The ID of the file to use for this request 26 | * @return A Mono of {@link DeleteFileResponse} 27 | */ 28 | Mono deleteFile(String fileId); 29 | 30 | /** 31 | * Returns information about a specific file. 32 | * 33 | * @param fileId The ID of the file to use for this request 34 | * @return A Mono of {@link RetrieveFileResponse} 35 | */ 36 | Mono retrieveFile(String fileId); 37 | 38 | /** 39 | * Returns the contents of the specified file 40 | * 41 | * @param fileId The ID of the file to use for this request 42 | * @return A Mono of {@link String} 43 | */ 44 | Mono retrieveFileContent(String fileId); 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/files/FilesServiceImpl.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.files; 2 | 3 | import org.springframework.core.io.PathResource; 4 | import org.springframework.http.client.MultipartBodyBuilder; 5 | import org.springframework.web.reactive.function.client.WebClient; 6 | import reactor.core.publisher.Mono; 7 | 8 | public class FilesServiceImpl implements FilesService { 9 | 10 | private final WebClient client; 11 | 12 | public FilesServiceImpl(WebClient client) { 13 | this.client = client; 14 | } 15 | 16 | @Override 17 | public Mono listFiles() { 18 | String listFilesUrl = "/files"; 19 | return client.get() 20 | .uri(listFilesUrl) 21 | .retrieve() 22 | .bodyToMono(ListFilesResponse.class); 23 | } 24 | 25 | @Override 26 | public Mono uploadFile(UploadFileRequest request) { 27 | String uploadFileUrl = "/files"; 28 | var multipartBodyBuilder = new MultipartBodyBuilder(); 29 | multipartBodyBuilder.part("file", new PathResource(request.file())); 30 | multipartBodyBuilder.part("purpose", request.purpose()); 31 | return client.post() 32 | .uri(uploadFileUrl) 33 | .bodyValue(multipartBodyBuilder.build()) 34 | .retrieve() 35 | .bodyToMono(UploadFileResponse.class); 36 | } 37 | 38 | @Override 39 | public Mono deleteFile(String fileId) { 40 | String deleteFileUrl = String.format("/files/%s", fileId); 41 | return client.delete() 42 | .uri(deleteFileUrl) 43 | .retrieve() 44 | .bodyToMono(DeleteFileResponse.class); 45 | } 46 | 47 | @Override 48 | public Mono retrieveFile(String fileId) { 49 | String retrieveFileUrl = String.format("/files/%s",fileId); 50 | return client.get() 51 | .uri(retrieveFileUrl) 52 | .retrieve() 53 | .bodyToMono(RetrieveFileResponse.class); 54 | } 55 | 56 | @Override 57 | public Mono retrieveFileContent(String fileId) { 58 | String retrieveFileContentUrl = String.format("/files/%s/content", fileId); 59 | return client.get() 60 | .uri(retrieveFileContentUrl) 61 | .retrieve() 62 | .bodyToMono(String.class); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/files/ListFilesResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.files; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import java.util.List; 6 | 7 | public record ListFilesResponse(@JsonProperty("data") List data, 8 | @JsonProperty("object") String object) { 9 | } 10 | 11 | record FileData(@JsonProperty("id") String id, 12 | @JsonProperty("object") String object, 13 | @JsonProperty("bytes") Integer bytes, 14 | @JsonProperty("created_at") Long createdAt, 15 | @JsonProperty("filename") String filename, 16 | @JsonProperty("purpose") String purpose) { 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/files/RetrieveFileResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.files; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public record RetrieveFileResponse(@JsonProperty("id") String id, 6 | @JsonProperty("object") String object, 7 | @JsonProperty("bytes") Integer bytes, 8 | @JsonProperty("created_at") Long createdAt, 9 | @JsonProperty("filename") String filename, 10 | @JsonProperty("purpose") String purpose) { 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/files/UploadFileRequest.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.files; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | /** 6 | * UploadFileRequest. 7 | * 8 | * @param file - Name of the JSON Lines file to be uploaded. 9 | *

10 | * If the purpose is set to "fine-tune", 11 | * each line is a JSON record with "prompt" and "completion" fields representing your training examples. 12 | * @param purpose - The intended purpose of the uploaded documents. 13 | *

14 | * Use "fine-tune" for Fine-tuning. This allows us to validate the format of the uploaded file. 15 | */ 16 | public record UploadFileRequest(@JsonProperty("file") String file, 17 | @JsonProperty("purpose") String purpose) { 18 | public UploadFileRequest { 19 | if (file == null || file.isEmpty()) 20 | throw new IllegalArgumentException("file value can't be null or empty"); 21 | 22 | if (purpose == null || purpose.isEmpty()) 23 | throw new IllegalArgumentException("purpose value can't be null or empty"); 24 | } 25 | 26 | public static Builder builder(String file, String purpose) { 27 | return new Builder(file, purpose); 28 | } 29 | 30 | public static final class Builder { 31 | String file; 32 | String purpose; 33 | 34 | public UploadFileRequest build() { 35 | return new UploadFileRequest(file, purpose); 36 | } 37 | 38 | public Builder(String file, String purpose) { 39 | this.file = file; 40 | this.purpose = purpose; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/files/UploadFileResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.files; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public record UploadFileResponse(@JsonProperty("id") String id, 6 | @JsonProperty("object") String object, 7 | @JsonProperty("bytes") Integer bytes, 8 | @JsonProperty("created_at") Long createdAt, 9 | @JsonProperty("filename") String filename, 10 | @JsonProperty("purpose") String purpose) { 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/finetunes/CancelFineTuneResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.finetunes; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import java.util.List; 6 | 7 | public record CancelFineTuneResponse(@JsonProperty("id") String id, 8 | @JsonProperty("object") String object, 9 | @JsonProperty("model") String model, 10 | @JsonProperty("created_at") Long createdAt, 11 | @JsonProperty("events") List events, 12 | @JsonProperty("fine_tuned_model") String fineTunedModel, 13 | @JsonProperty("hyperparams") HyperParametersData hyperparams, 14 | @JsonProperty("organization_id") String organizationId, 15 | @JsonProperty("result_files") List resultFiles, 16 | @JsonProperty("status") String status, 17 | @JsonProperty("validation_files") List validationFiles, 18 | @JsonProperty("training_files") List trainingFiles, 19 | @JsonProperty("updated_at") Long updatedAt) { 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/finetunes/CreateFineTuneRequest.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.finetunes; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonInclude.Include; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @param trainingFile The ID of an uploaded file that contains training data. 11 | * @param validationFile The ID of an uploaded file that contains validation data. 12 | * @param model The name of the base model to fine-tune. 13 | * You can select one of "ada", "babbage", "curie", "davinci", 14 | * or a fine-tuned model created after 2022-04-21. 15 | * @param nEpochs The number of epochs to train the model for. 16 | * An epoch refers to one full cycle through the training dataset. 17 | * @param batchSize The batch size to use for training. 18 | * The batch size is the number of training examples 19 | * used to train a single forward and backward pass. 20 | *

21 | * By default, the batch size will be dynamically configured to be ~0.2% 22 | * of the number of examples in the training set, capped at 256 - in general, 23 | * we've found that larger batch sizes tend to work better for larger datasets. 24 | * @param learningRateMultiplier The learning rate multiplier to use for training. 25 | * The fine-tuning learning rate is the original 26 | * learning rate used for pretraining multiplied by this value. 27 | *

28 | * By default, the learning rate multiplier is the 0.05, 0.1, or 0.2 29 | * depending on final batch_size 30 | * (larger learning rates tend to perform better with larger batch sizes). 31 | * We recommend experimenting with values in the range 0.02 to 0.2 32 | * to see what produces the best results. 33 | * @param promptLossWeight The weight to use for loss on the prompt tokens. 34 | * This controls how much the model tries to learn to generate the prompt 35 | * (as compared to the completion which always has a weight of 1.0), 36 | * and can add a stabilizing effect to training when completions are short. 37 | *

38 | * If prompts are extremely long (relative to completions), 39 | * it may make sense to reduce this weight so as to 40 | * avoid over-prioritizing learning the prompt. 41 | * @param computeClassificationMetrics If set, we calculate classification-specific metrics 42 | * such as accuracy and F-1 score using the validation set at the end of every epoch. 43 | * These metrics can be viewed in the results file. 44 | *

45 | * In order to compute classification metrics, 46 | * you must provide a validation_file. 47 | * Additionally, you must specify classification_n_classes for multiclass 48 | * classification or classification_positive_class for binary classification. 49 | * @param classificationNClasses The number of classes in a classification task. 50 | *

51 | * This parameter is required for multiclass classification. 52 | * @param classificationPositiveClass The positive class in binary classification. 53 | *

54 | * This parameter is needed to generate precision, recall, 55 | * and F1 metrics when doing binary classification. 56 | * @param classificationBetas If this is provided, we calculate F-beta scores at the specified beta values. 57 | * The F-beta score is a generalization of F-1 score. 58 | * This is only used for binary classification. 59 | *

60 | * With a beta of 1 (i.e. the F-1 score), 61 | * precision and recall are given the same weight. 62 | * A larger beta score puts more weight on recall and less on precision. 63 | * A smaller beta score puts more weight on precision and less on recall. 64 | * @param suffix A string of up to 40 characters that will be added to your fine-tuned model name. 65 | *

66 | * For example, a suffix of "custom-model-name" 67 | * would produce a model name like 68 | * ada:ft-your-org:custom-model-name-2022-02-15-04-21-04. 69 | */ 70 | @JsonInclude(Include.NON_NULL) 71 | public record CreateFineTuneRequest(@JsonProperty("training_file") String trainingFile, 72 | @JsonProperty("validation_file") String validationFile, 73 | @JsonProperty("model") String model, 74 | @JsonProperty("n_epochs") Integer nEpochs, 75 | @JsonProperty("batch_size") Integer batchSize, 76 | @JsonProperty("learning_rate_multiplier") Double learningRateMultiplier, 77 | @JsonProperty("prompt_loss_weight") Double promptLossWeight, 78 | @JsonProperty("compute_classification_metrics") Boolean computeClassificationMetrics, 79 | @JsonProperty("classification_n_classes") Integer classificationNClasses, 80 | @JsonProperty("classification_positive_class") String classificationPositiveClass, 81 | @JsonProperty("classification_betas") List classificationBetas, 82 | @JsonProperty("suffix") String suffix) { 83 | public CreateFineTuneRequest { 84 | if (trainingFile == null || trainingFile.isEmpty()) 85 | throw new IllegalArgumentException("training file can't be null or empty"); 86 | } 87 | 88 | public static Builder builder(String trainingFile) { 89 | return new Builder(trainingFile); 90 | } 91 | 92 | public static final class Builder { 93 | String trainingFile; 94 | String validationFile; 95 | String model; 96 | Integer nEpochs; 97 | Integer batchSize; 98 | Double learningRateMultiplier; 99 | Double promptLossWeight; 100 | Boolean computeClassificationMetrics; 101 | Integer classificationNClasses; 102 | String classificationPositiveClass; 103 | List classificationBetas; 104 | String suffix; 105 | 106 | public CreateFineTuneRequest build() { 107 | return new CreateFineTuneRequest( 108 | trainingFile, validationFile, model, 109 | nEpochs, batchSize, learningRateMultiplier, 110 | promptLossWeight, computeClassificationMetrics, classificationNClasses, 111 | classificationPositiveClass, classificationBetas, suffix); 112 | } 113 | 114 | public Builder(String trainingFile) { 115 | this.trainingFile = trainingFile; 116 | } 117 | 118 | public Builder validationFile(String validationFile) { 119 | this.validationFile = validationFile; 120 | return this; 121 | } 122 | 123 | public Builder model(String model) { 124 | this.model = model; 125 | return this; 126 | } 127 | 128 | public Builder nEpochs(Integer nEpochs) { 129 | this.nEpochs = nEpochs; 130 | return this; 131 | } 132 | 133 | public Builder batchSize(Integer batchSize) { 134 | this.batchSize = batchSize; 135 | return this; 136 | } 137 | 138 | public Builder learningRateMultiplier(Double learningRateMultiplier) { 139 | this.learningRateMultiplier = learningRateMultiplier; 140 | return this; 141 | } 142 | 143 | public Builder promptLossWeight(Double promptLossWeight) { 144 | this.promptLossWeight = promptLossWeight; 145 | return this; 146 | } 147 | 148 | public Builder computeClassificationMetrics(Boolean computeClassificationMetrics) { 149 | this.computeClassificationMetrics = computeClassificationMetrics; 150 | return this; 151 | } 152 | 153 | public Builder classificationNClasses(Integer classificationNClasses) { 154 | this.classificationNClasses = classificationNClasses; 155 | return this; 156 | } 157 | 158 | public Builder classificationPositiveClass(String classificationPositiveClass) { 159 | this.classificationPositiveClass = classificationPositiveClass; 160 | return this; 161 | } 162 | 163 | public Builder classificationBetas(List classificationBetas) { 164 | this.classificationBetas = classificationBetas; 165 | return this; 166 | } 167 | 168 | public Builder suffix(String suffix) { 169 | this.suffix = suffix; 170 | return this; 171 | } 172 | 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/finetunes/CreateFineTuneResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.finetunes; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import java.util.List; 6 | 7 | public record CreateFineTuneResponse(@JsonProperty("id") String id, 8 | @JsonProperty("object") String object, 9 | @JsonProperty("model") String model, 10 | @JsonProperty("created_at") Long createdAt, 11 | @JsonProperty("events") List events, 12 | @JsonProperty("fine_tune_model") String fineTunedModel, 13 | @JsonProperty("hyperparams") HyperParametersData hyperparams, 14 | @JsonProperty("organization_id") String organizationId, 15 | @JsonProperty("result_files") List resultFiles, 16 | @JsonProperty("status") String status, 17 | @JsonProperty("training_files") List trainingFiles, 18 | @JsonProperty("updated_at") Long updatedAt, 19 | @JsonProperty("validation_files") List validationFiles) { 20 | } 21 | 22 | record EventData(@JsonProperty("object") String object, 23 | @JsonProperty("created_at") Long createdAt, 24 | @JsonProperty("level") String level, 25 | @JsonProperty("message") String message) { 26 | } 27 | 28 | record HyperParametersData(@JsonProperty("batch_size") Integer batchSize, 29 | @JsonProperty("learning_rate_multiplier") Double learningRateMultiplier, 30 | @JsonProperty("n_epochs") Integer nEpochs, 31 | @JsonProperty("prompt_loss_weight") Double promptLossWeight) { 32 | } 33 | 34 | record FileData(@JsonProperty("id") String id, 35 | @JsonProperty("object") String object, 36 | @JsonProperty("bytes") Integer bytes, 37 | @JsonProperty("created_at") Long createdAt, 38 | @JsonProperty("filename") String filename, 39 | @JsonProperty("purpose") String purpose) { 40 | } -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/finetunes/DeleteFineTuneModelResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.finetunes; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | public record DeleteFineTuneModelResponse(@JsonProperty("id") String id, 6 | @JsonProperty("object") String object, 7 | @JsonProperty("deleted") Boolean deleted) { 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/finetunes/FineTuneServiceImpl.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.finetunes; 2 | 3 | import org.springframework.web.reactive.function.client.WebClient; 4 | import reactor.core.publisher.Mono; 5 | 6 | public class FineTuneServiceImpl implements FineTunesService { 7 | 8 | private final WebClient client; 9 | 10 | public FineTuneServiceImpl(WebClient client) { 11 | this.client = client; 12 | } 13 | 14 | @Override 15 | public Mono createFineTune(CreateFineTuneRequest request) { 16 | String createFineTuneUrl = "/fine-tunes"; 17 | return client.post() 18 | .uri(createFineTuneUrl) 19 | .bodyValue(request) 20 | .retrieve() 21 | .bodyToMono(CreateFineTuneResponse.class); 22 | } 23 | 24 | @Override 25 | public Mono listFineTunes() { 26 | String listFineTunesUrl = "/fine-tunes"; 27 | return client.get() 28 | .uri(listFineTunesUrl) 29 | .retrieve() 30 | .bodyToMono(ListFineTunesResponse.class); 31 | } 32 | 33 | @Override 34 | public Mono retrieveFineTunes(String fineTuneId) { 35 | String retrieveFineTunesUrl = String.format("/fine-tunes/%s", fineTuneId); 36 | return client.get() 37 | .uri(retrieveFineTunesUrl) 38 | .retrieve() 39 | .bodyToMono(RetrieveFineTuneResponse.class); 40 | } 41 | 42 | @Override 43 | public Mono cancelFineTune(String fineTuneId) { 44 | String cancelFineTuneUrl = String.format("/fine-tunes/%s/cancel", fineTuneId); 45 | return client.post() 46 | .uri(cancelFineTuneUrl) 47 | .retrieve() 48 | .bodyToMono(CancelFineTuneResponse.class); 49 | } 50 | 51 | @Override 52 | public Mono listFineTuneEvents(String fineTuneId) { 53 | String listFineTuneEvents = String.format("/fine-tunes/%s/events", fineTuneId); 54 | return client.get() 55 | .uri(listFineTuneEvents) 56 | .retrieve() 57 | .bodyToMono(ListFineTuneEventsResponse.class); 58 | } 59 | 60 | @Override 61 | public Mono deleteFineTuneModel(String model) { 62 | String deleteFineTuneModelUrl = String.format("/models/%s", model); 63 | return client.delete() 64 | .uri(deleteFineTuneModelUrl) 65 | .retrieve() 66 | .bodyToMono(DeleteFineTuneModelResponse.class); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/finetunes/FineTunesService.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.finetunes; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | /** 6 | * Manage fine-tuning jobs to tailor a model to your specific training data. 7 | */ 8 | public interface FineTunesService { 9 | /** 10 | * Creates a job that fine-tunes a specified model from a given dataset. 11 | *

12 | * Response includes details of the enqueued job including job status and the name of the fine-tuned models once complete. 13 | * 14 | * @param request {@link CreateFineTuneRequest} 15 | * @return A Mono of {@link CreateFineTuneResponse} 16 | */ 17 | Mono createFineTune(CreateFineTuneRequest request); 18 | 19 | /** 20 | * List your organization's fine-tuning jobs 21 | * 22 | * @return A Mono of {@link ListFineTunesResponse} 23 | */ 24 | Mono listFineTunes(); 25 | 26 | /** 27 | * Gets info about the fine-tune job. 28 | * 29 | * @param fineTuneId The ID of the fine-tune job 30 | * @return A Mono of {RetrieveFineTuneResponse} 31 | */ 32 | Mono retrieveFineTunes(String fineTuneId); 33 | 34 | /** 35 | * Immediately cancel a fine-tune job. 36 | * 37 | * @param fineTuneId The ID of the fine-tune job to cancel 38 | * @return A Mono of {@link CancelFineTuneResponse} 39 | */ 40 | Mono cancelFineTune(String fineTuneId); 41 | 42 | /** 43 | * Get fine-grained status updates for a fine-tune job. 44 | * 45 | * @param fineTuneId The ID of the fine-tune job to get events for. 46 | * @return A Mono of {@link ListFineTuneEventsResponse} 47 | */ 48 | Mono listFineTuneEvents(String fineTuneId); 49 | 50 | /** 51 | * Delete a fine-tuned model. You must have the Owner role in your organization. 52 | * 53 | * @param model The model to delete 54 | * @return A Mono of {@link DeleteFineTuneModelResponse} 55 | */ 56 | Mono deleteFineTuneModel(String model); 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/finetunes/ListFineTuneEventsResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.finetunes; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import java.util.List; 6 | 7 | public record ListFineTuneEventsResponse(@JsonProperty("object") String object, 8 | @JsonProperty("data") List data) { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/finetunes/ListFineTunesResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.finetunes; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import java.util.List; 6 | 7 | public record ListFineTunesResponse(@JsonProperty("object") String object, 8 | @JsonProperty("data") List data) { 9 | } 10 | 11 | record FineTuneData(@JsonProperty("id") String id, 12 | @JsonProperty("object") String object, 13 | @JsonProperty("model") String model, 14 | @JsonProperty("created_at") Long createdAt, 15 | @JsonProperty("fine_tuned_model") String fineTunedModel, 16 | @JsonProperty("hyperparams") HyperParametersData hyperparams, 17 | @JsonProperty("organizationId") String organizationId, 18 | @JsonProperty("result_files") List resultFiles, 19 | @JsonProperty("status") String status, 20 | @JsonProperty("validation_files") List validationFiles, 21 | @JsonProperty("training_files") List trainingFiles, 22 | @JsonProperty("updated_at") Long updatedAt 23 | ) { 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/finetunes/RetrieveFineTuneResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.finetunes; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import java.util.List; 6 | 7 | public record RetrieveFineTuneResponse(@JsonProperty("id") String id, 8 | @JsonProperty("object") String object, 9 | @JsonProperty("model") String model, 10 | @JsonProperty("created_at") Long createdAt, 11 | @JsonProperty("events") List events) { 12 | } 13 | 14 | record FineTuneEventData(@JsonProperty("object") String object, 15 | @JsonProperty("created_at") Long createdAt, 16 | @JsonProperty("level") String level, 17 | @JsonProperty("message") String message) { 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/images/CreateImageEditRequest.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.images; 2 | 3 | /** 4 | * CreateImageEditRequest. 5 | * 6 | * @param image - The image to edit. Must be a valid PNG file, less than 4MB, and square. 7 | * If mask is not provided, image must have transparency, which will be used as the mask. 8 | * @param mask - An additional image whose fully transparent areas (e.g. where alpha is zero) 9 | * indicate where image should be edited. Must be a valid PNG file, 10 | * less than 4MB, and have the same dimensions as image. 11 | * @param prompt - A text description of the desired image(s). The maximum length is 1000 characters. 12 | * @param n - The number of images to generate. Must be between 1 and 10. 13 | * @param size - The size of the generated images. Must be one of 256x256, 512x512, or 1024x1024. 14 | * @param responseFormat - The format in which the generated images are returned. Must be one of url or b64_json. 15 | * @param user - A unique identifier representing your end-user, 16 | * which can help OpenAI to monitor and detect abuse 17 | */ 18 | public record CreateImageEditRequest(String image, 19 | String mask, 20 | String prompt, 21 | Integer n, 22 | String size, 23 | String responseFormat, 24 | String user) { 25 | 26 | public CreateImageEditRequest { 27 | if (image == null || image.isBlank()) 28 | throw new IllegalArgumentException("image value can't be null or blank"); 29 | 30 | if (prompt == null || prompt.isEmpty()) 31 | throw new IllegalArgumentException("prompt value can't be null or empty"); 32 | } 33 | 34 | public static Builder builder(String image, String prompt) { 35 | return new Builder(image, prompt); 36 | } 37 | 38 | public static final class Builder { 39 | String image; 40 | String mask; 41 | String prompt; 42 | Integer n; 43 | String size; 44 | String responseFormat; 45 | String user; 46 | 47 | public CreateImageEditRequest build() { 48 | return new CreateImageEditRequest( 49 | image, mask, prompt, 50 | n, size, responseFormat, 51 | user); 52 | } 53 | 54 | public Builder(String image, String prompt) { 55 | this.image = image; 56 | this.prompt = prompt; 57 | } 58 | 59 | public Builder mask(String mask) { 60 | this.mask = mask; 61 | return this; 62 | } 63 | 64 | public Builder n(Integer n) { 65 | this.n = n; 66 | return this; 67 | } 68 | 69 | public Builder size(String size) { 70 | this.size = size; 71 | return this; 72 | } 73 | 74 | public Builder responseFormat(String responseFormat) { 75 | this.responseFormat = responseFormat; 76 | return this; 77 | } 78 | 79 | public Builder user(String user) { 80 | this.user = user; 81 | return this; 82 | } 83 | } 84 | } 85 | 86 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/images/CreateImageEditResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.images; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | public record CreateImageEditResponse(@JsonProperty("created") Long created, 9 | @JsonProperty("data") List> data) { 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/images/CreateImageRequest.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.images; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonInclude.Include; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | 7 | /** 8 | * CreateImageRequest. 9 | * 10 | * @param prompt - A text description of the desired image(s). 11 | * The maximum length is 1000 characters. 12 | * @param n - The number of images to generate. 13 | * Must be between 1 and 10. 14 | * @param size - The size of the generated images. 15 | * Must be one of 256x256, 512x512, or 1024x1024. 16 | * @param responseFormat - The format in which the generated images are returned. 17 | * Must be one of url or b64_json. 18 | * @param user - A unique identifier representing your end-user, 19 | * which can help OpenAI to monitor and detect abuse. 20 | */ 21 | @JsonInclude(Include.NON_NULL) 22 | public record CreateImageRequest(@JsonProperty("prompt") String prompt, 23 | @JsonProperty("n") Integer n, 24 | @JsonProperty("size") String size, 25 | @JsonProperty("response_format") String responseFormat, 26 | @JsonProperty("user") String user) { 27 | public CreateImageRequest { 28 | if (prompt == null || prompt.isEmpty()) 29 | throw new IllegalArgumentException("prompt value can't be null or empty"); 30 | } 31 | 32 | public static Builder builder(String prompt) { 33 | return new Builder(prompt); 34 | } 35 | 36 | public static final class Builder { 37 | String prompt; 38 | Integer n; 39 | String size; 40 | String responseFormat; 41 | String user; 42 | 43 | public CreateImageRequest build() { 44 | return new CreateImageRequest( 45 | prompt, n, size, 46 | responseFormat, user); 47 | } 48 | 49 | public Builder(String prompt) { 50 | this.prompt = prompt; 51 | } 52 | 53 | public Builder n(Integer n) { 54 | this.n = n; 55 | return this; 56 | } 57 | 58 | public Builder size(String size) { 59 | this.size = size; 60 | return this; 61 | } 62 | 63 | public Builder responseFormat(String responseFormat) { 64 | this.responseFormat = responseFormat; 65 | return this; 66 | } 67 | 68 | public Builder user(String user) { 69 | this.user = user; 70 | return this; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/images/CreateImageResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.images; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | public record CreateImageResponse(@JsonProperty("created") Long created, 9 | @JsonProperty("data") List> data) { 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/images/CreateImageVariationRequest.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.images; 2 | 3 | /** 4 | * CreateImageVariationRequest. 5 | * 6 | * @param image - The image to use as the basis for the variation(s). 7 | * Must be a valid PNG file, less than 4MB, and square. 8 | * @param n - The number of images to generate. Must be between 1 and 10. 9 | * @param size - The size of the generated images. 10 | * Must be one of 256x256, 512x512, or 1024x1024. 11 | * @param responseFormat - The format in which the generated images are returned. 12 | * Must be one of url or b64_json. 13 | * @param user - A unique identifier representing your end-user, 14 | * which can help OpenAI to monitor and detect abuse. 15 | */ 16 | public record CreateImageVariationRequest(String image, 17 | Integer n, 18 | String size, 19 | String responseFormat, 20 | String user) { 21 | 22 | public CreateImageVariationRequest { 23 | if (image == null || image.isBlank()) 24 | throw new IllegalArgumentException("image value can't be null or blank"); 25 | } 26 | 27 | public static Builder builder(String image) { 28 | return new Builder(image); 29 | } 30 | 31 | public static final class Builder { 32 | String image; 33 | Integer n; 34 | String size; 35 | String responseFormat; 36 | String user; 37 | 38 | public CreateImageVariationRequest build() { 39 | return new CreateImageVariationRequest( 40 | image, n, size, 41 | responseFormat, user); 42 | } 43 | 44 | public Builder(String image) { 45 | this.image = image; 46 | } 47 | 48 | public Builder n(Integer n) { 49 | this.n = n; 50 | return this; 51 | } 52 | 53 | public Builder size(String size) { 54 | this.size = size; 55 | return this; 56 | } 57 | 58 | public Builder responseFormat(String responseFormat) { 59 | this.responseFormat = responseFormat; 60 | return this; 61 | } 62 | 63 | public Builder user(String user) { 64 | this.user = user; 65 | return this; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/images/CreateImageVariationResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.images; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | public record CreateImageVariationResponse(@JsonProperty("created") Long created, 9 | @JsonProperty("data") List> data) { 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/images/ImageServiceImpl.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.images; 2 | 3 | import org.springframework.core.io.PathResource; 4 | import org.springframework.http.client.MultipartBodyBuilder; 5 | import org.springframework.stereotype.Service; 6 | import org.springframework.web.reactive.function.client.WebClient; 7 | import reactor.core.publisher.Mono; 8 | 9 | @Service 10 | public class ImageServiceImpl implements ImagesService { 11 | 12 | private final WebClient client; 13 | 14 | public ImageServiceImpl(WebClient client) { 15 | this.client = client; 16 | } 17 | 18 | @Override 19 | public Mono createImage(CreateImageRequest request) { 20 | String createImageUrl = "/images/generations"; 21 | return client.post() 22 | .uri(createImageUrl) 23 | .bodyValue(request) 24 | .retrieve() 25 | .bodyToMono(CreateImageResponse.class); 26 | } 27 | 28 | @Override 29 | public Mono createImageEdit(CreateImageEditRequest request) { 30 | String createImageEditUrl = "/images/edits"; 31 | var multipartBodyBuilder = new MultipartBodyBuilder(); 32 | multipartBodyBuilder.part("image", new PathResource(request.image())); 33 | multipartBodyBuilder.part("prompt", request.prompt()); 34 | 35 | if (request.mask() != null) 36 | multipartBodyBuilder.part("mask", new PathResource(request.mask())); 37 | 38 | if (request.n() != null) 39 | multipartBodyBuilder.part("n", request.n()); 40 | 41 | if (request.size() != null) 42 | multipartBodyBuilder.part("size", request.size()); 43 | 44 | if (request.responseFormat() != null) 45 | multipartBodyBuilder.part("response_format", request.responseFormat()); 46 | 47 | if (request.user() != null) 48 | multipartBodyBuilder.part("user", request.user()); 49 | 50 | return client.post() 51 | .uri(createImageEditUrl) 52 | .bodyValue(multipartBodyBuilder.build()) 53 | .retrieve() 54 | .bodyToMono(CreateImageEditResponse.class); 55 | } 56 | 57 | @Override 58 | public Mono createImageVariation(CreateImageVariationRequest request) { 59 | String createImageVariation = "/images/variations"; 60 | var multipartBodyBuilder = new MultipartBodyBuilder(); 61 | multipartBodyBuilder.part("image", new PathResource(request.image())); 62 | 63 | if (request.n() != null) 64 | multipartBodyBuilder.part("n", request.n()); 65 | 66 | if (request.size() != null) 67 | multipartBodyBuilder.part("size", request.size()); 68 | 69 | if (request.responseFormat() != null) 70 | multipartBodyBuilder.part("response_format", request.responseFormat()); 71 | 72 | if (request.user() != null) 73 | multipartBodyBuilder.part("user", request.user()); 74 | 75 | return client.post() 76 | .uri(createImageVariation) 77 | .bodyValue(multipartBodyBuilder.build()) 78 | .retrieve() 79 | .bodyToMono(CreateImageVariationResponse.class); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/images/ImagesService.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.images; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | public interface ImagesService { 6 | 7 | /** 8 | * Creates an image given a prompt. 9 | * 10 | * @param request {@link CreateImageRequest} 11 | * @return A Mono of {@link CreateImageResponse} 12 | */ 13 | Mono createImage(CreateImageRequest request); 14 | 15 | /** 16 | * Creates an edited or extended image given an original image and a prompt. 17 | * 18 | * @param request {@link CreateImageEditRequest} 19 | * @return A Mono of {@link CreateImageEditResponse} 20 | */ 21 | Mono createImageEdit(CreateImageEditRequest request); 22 | 23 | /** 24 | * Creates a variation of a given image. 25 | * 26 | * @param request {@link CreateImageVariationRequest} 27 | * @return A Mono of {@link CreateImageVariationResponse} 28 | */ 29 | Mono createImageVariation(CreateImageVariationRequest request); 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/models/ListModelsResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.models; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import java.util.List; 6 | 7 | public record ListModelsResponse( 8 | @JsonProperty("object") String object, 9 | @JsonProperty("data") List data 10 | ) {} -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/models/ModelsService.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.models; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | public interface ModelsService { 6 | 7 | /** 8 | * Lists the currently available models, 9 | * and provides basic information about each one such as the owner and availability. 10 | * 11 | * @return {@link ListModelsResponse} 12 | */ 13 | Mono listModels(); 14 | 15 | /** 16 | * Retrieves a model instance, 17 | * providing basic information about the model such as the owner and permissioning. 18 | * 19 | * @param model - The ID of the model to use for this request. 20 | * @return {@link RetrieveModelResponse} 21 | */ 22 | Mono retrieveModel(String model); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/models/ModelsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.models; 2 | 3 | import org.springframework.stereotype.Service; 4 | import org.springframework.web.reactive.function.client.WebClient; 5 | import reactor.core.publisher.Mono; 6 | 7 | @Service 8 | public class ModelsServiceImpl implements ModelsService{ 9 | private final WebClient client; 10 | public ModelsServiceImpl(WebClient client){ 11 | this.client = client; 12 | } 13 | @Override 14 | public Mono listModels() { 15 | String listModelsUri = "/models"; 16 | return client.get() 17 | .uri(listModelsUri) 18 | .retrieve() 19 | .bodyToMono(ListModelsResponse.class); 20 | } 21 | 22 | @Override 23 | public Mono retrieveModel(String model) { 24 | String retrieveModelUri = String.format("/models/%s", model); 25 | return client.get() 26 | .uri(retrieveModelUri) 27 | .retrieve() 28 | .bodyToMono(RetrieveModelResponse.class); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/models/RetrieveModelResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.models; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import java.util.List; 6 | 7 | public record RetrieveModelResponse( 8 | @JsonProperty("id") String id, 9 | @JsonProperty("object") String object, 10 | @JsonProperty("created") Long created, 11 | @JsonProperty("owned_by") String ownedBy, 12 | @JsonProperty("permission") List permission, 13 | @JsonProperty("root") String root, 14 | @JsonProperty("parent") String parent 15 | ) {} 16 | 17 | record Permission( 18 | @JsonProperty("id") String id, 19 | @JsonProperty("object") String object, 20 | @JsonProperty("created") Long created, 21 | @JsonProperty("allow_create_engine") Boolean allowCreateEngine, 22 | @JsonProperty("allow_sampling") Boolean allowSampling, 23 | @JsonProperty("allow_logprobs") Boolean allowLogprobs, 24 | @JsonProperty("allow_search_indices") Boolean allowSearchIndices, 25 | @JsonProperty("allow_view") Boolean allowView, 26 | @JsonProperty("allow_fine_tuning") Boolean allowFineTuning, 27 | @JsonProperty("organization") String organization, 28 | @JsonProperty("group") String group, 29 | @JsonProperty("is_blocking") Boolean isBlocking 30 | ) {} 31 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/moderations/CreateModerationRequest.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.moderations; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | /** 6 | * CreateModerationRequest. 7 | * 8 | * @param input The input text to classify 9 | * @param model Two content moderations models are available: text-moderation-stable and text-moderation-latest. 10 | *

11 | * The default is text-moderation-latest which will be automatically upgraded over time. 12 | * This ensures you are always using our most accurate model. 13 | * If you use text-moderation-stable, we will provide advanced notice before updating the model. 14 | * Accuracy of text-moderation-stable may be slightly lower than for text-moderation-latest. 15 | */ 16 | public record CreateModerationRequest(@JsonProperty("input") String input, 17 | @JsonProperty("model") String model) { 18 | public CreateModerationRequest{ 19 | if (input == null || input.isEmpty()) 20 | throw new IllegalArgumentException("input can't be null or empty"); 21 | } 22 | public static Builder builder(String model){ 23 | return new Builder(model); 24 | } 25 | public static final class Builder{ 26 | String input; 27 | 28 | String model; 29 | 30 | public CreateModerationRequest build(){ 31 | return new CreateModerationRequest(input, model); 32 | } 33 | public Builder(String input){ 34 | this.input = input; 35 | } 36 | 37 | public Builder model(String model){ 38 | this.model = model; 39 | return this; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/moderations/CreateModerationResponse.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.moderations; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import java.util.List; 6 | 7 | public record CreateModerationResponse(@JsonProperty("id") String id, 8 | @JsonProperty("model")String model, 9 | @JsonProperty("results")List results) { 10 | } 11 | 12 | record ModerationData(@JsonProperty("categories")ModerationCategoriesData categories, 13 | @JsonProperty("category_scores")ModerationCategoriesScoresData categoryScores, 14 | @JsonProperty("flagged")Boolean flagged) { 15 | } 16 | 17 | record ModerationCategoriesData(@JsonProperty("hate")Boolean hate, 18 | @JsonProperty("hate/threatening")Boolean hateThreatening, 19 | @JsonProperty("self-harm")Boolean selfHarm, 20 | @JsonProperty("sexual")Boolean sexual, 21 | @JsonProperty("sexual/minors")Boolean sexualMinors, 22 | @JsonProperty("violence")Boolean violence, 23 | @JsonProperty("violence/graphic")Boolean violenceGraphic) { 24 | 25 | } 26 | 27 | record ModerationCategoriesScoresData(@JsonProperty("hate")Double hate, 28 | @JsonProperty("hate/threatening")Double hateThreatening, 29 | @JsonProperty("self-harm")Double selfHarm, 30 | @JsonProperty("sexual")Double sexual, 31 | @JsonProperty("sexual/minors")Double sexualMinors, 32 | @JsonProperty("violence")Double violence, 33 | @JsonProperty("violence/graphic")Double violenceGraphic) { 34 | } -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/moderations/ModerationsService.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.moderations; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | /** 6 | * Given a input text, outputs if the model classifies it as violating OpenAI's content policy 7 | */ 8 | public interface ModerationsService { 9 | /** 10 | * Classifies if text violates OpenAI's Content Policy 11 | * @param request {@link CreateModerationRequest} 12 | * @return A Mono of {@link CreateModerationResponse} 13 | */ 14 | Mono createModeration(CreateModerationRequest request); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/client/moderations/ModerationsServiceImpl.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.client.moderations; 2 | 3 | import org.springframework.web.reactive.function.client.WebClient; 4 | import reactor.core.publisher.Mono; 5 | 6 | public class ModerationsServiceImpl implements ModerationsService{ 7 | 8 | private final WebClient client; 9 | public ModerationsServiceImpl(WebClient client){ 10 | this.client = client; 11 | } 12 | @Override 13 | public Mono createModeration(CreateModerationRequest request) { 14 | String createModerationUrl = "/moderations"; 15 | return client.post() 16 | .uri(createModerationUrl) 17 | .bodyValue(request) 18 | .retrieve() 19 | .bodyToMono(CreateModerationResponse.class); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/io/github/reactiveclown/openaiwebfluxclient/exception/OpenAiException.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.exception; 2 | 3 | import org.springframework.web.reactive.function.client.WebClientException; 4 | 5 | public class OpenAiException extends WebClientException { 6 | public OpenAiException(Integer statusCode, String errorMessage) { 7 | super(String.format(""" 8 | 9 | OpenAi Service exception occurred. 10 | HTTP error code: %s 11 | Detailed error message: 12 | %s 13 | """, 14 | statusCode, errorMessage)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | io.github.reactiveclown.openaiwebfluxclient.ClientAutoConfiguration -------------------------------------------------------------------------------- /src/test/java/io/github/reactiveclown/openaiwebfluxclient/requests/CreateCompletionRequestTest.java: -------------------------------------------------------------------------------- 1 | package io.github.reactiveclown.openaiwebfluxclient.requests; 2 | 3 | import io.github.reactiveclown.openaiwebfluxclient.client.completions.CreateCompletionRequest; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static org.junit.jupiter.api.Assertions.assertAll; 7 | import static org.junit.jupiter.api.Assertions.assertEquals; 8 | 9 | public class CreateCompletionRequestTest { 10 | @Test 11 | public void CreateCompletionRequestBuilder_test() { 12 | var builderRequest = new CreateCompletionRequest.Builder("model").build(); 13 | var request = new CreateCompletionRequest( 14 | "model", null, null, 15 | null, null, null, 16 | null, null, 17 | null, null, null, 18 | null, null, null, 19 | null); 20 | 21 | assertAll(() -> { 22 | assertEquals(request.prompt(), builderRequest.prompt()); 23 | assertEquals(request.suffix(), builderRequest.suffix()); 24 | assertEquals(request.maxTokens(), builderRequest.maxTokens()); 25 | assertEquals(request.temperature(), builderRequest.temperature()); 26 | assertEquals(request.topP(), builderRequest.topP()); 27 | assertEquals(request.n(), builderRequest.n()); 28 | assertEquals(request.logprobs(), builderRequest.logprobs()); 29 | assertEquals(request.echo(), builderRequest.echo()); 30 | assertEquals(request.stop(), builderRequest.stop()); 31 | assertEquals(request.presencePenalty(), builderRequest.presencePenalty()); 32 | assertEquals(request.frequencyPenalty(), builderRequest.frequencyPenalty()); 33 | assertEquals(request.bestOf(), builderRequest.bestOf()); 34 | assertEquals(request.logitBias(), builderRequest.logitBias()); 35 | assertEquals(request.user(), builderRequest.user()); 36 | }); 37 | } 38 | 39 | 40 | } 41 | --------------------------------------------------------------------------------