@@ -6,13 +6,17 @@ import com.cjcrafter.openai.completions.CompletionResponse
66import com.cjcrafter.openai.completions.CompletionResponseChunk
77import com.cjcrafter.openai.embeddings.EmbeddingsRequest
88import com.cjcrafter.openai.embeddings.EmbeddingsResponse
9+ import com.cjcrafter.openai.files.*
910import com.fasterxml.jackson.databind.JavaType
1011import com.fasterxml.jackson.databind.node.ObjectNode
1112import okhttp3.*
13+ import okhttp3.HttpUrl.Companion.toHttpUrl
1214import okhttp3.MediaType.Companion.toMediaType
1315import okhttp3.RequestBody.Companion.toRequestBody
16+ import org.intellij.lang.annotations.Language
1417import org.jetbrains.annotations.ApiStatus
1518import java.io.BufferedReader
19+ import java.io.File
1620import java.io.IOException
1721
1822open class OpenAIImpl @ApiStatus.Internal constructor(
@@ -35,7 +39,36 @@ open class OpenAIImpl @ApiStatus.Internal constructor(
3539 .post(body).build()
3640 }
3741
38- protected open fun <T > executeRequest (httpRequest : Request , responseType : Class <T >): T {
42+ protected open fun buildRequestNoBody (endpoint : String , params : Map <String , Any >? = null): Request .Builder {
43+ val url = " $baseUrl /$endpoint " .toHttpUrl().newBuilder()
44+ .apply {
45+ params?.forEach { (key, value) -> addQueryParameter(key, value.toString()) }
46+ }.build().toString()
47+
48+ return Request .Builder ()
49+ .url(url)
50+ .addHeader(" Authorization" , " Bearer $apiKey " )
51+ .apply { if (organization != null ) addHeader(" OpenAI-Organization" , organization) }
52+ }
53+
54+ protected open fun buildMultipartRequest (
55+ endpoint : String ,
56+ function : MultipartBody .Builder .() -> Unit ,
57+ ): Request {
58+
59+ val multipartBody = MultipartBody .Builder ()
60+ .setType(MultipartBody .FORM )
61+ .apply (function)
62+ .build()
63+
64+ return Request .Builder ()
65+ .url(" $baseUrl /$endpoint " )
66+ .addHeader(" Authorization" , " Bearer $apiKey " )
67+ .apply { if (organization != null ) addHeader(" OpenAI-Organization" , organization) }
68+ .post(multipartBody).build()
69+ }
70+
71+ protected open fun executeRequest (httpRequest : Request ): String {
3972 val httpResponse = client.newCall(httpRequest).execute()
4073 if (! httpResponse.isSuccessful) {
4174 val json = httpResponse.body?.byteStream()?.bufferedReader()?.readText()
@@ -47,7 +80,12 @@ open class OpenAIImpl @ApiStatus.Internal constructor(
4780 ? : throw IOException (" Response body is null" )
4881 val responseStr = jsonReader.readText()
4982 OpenAI .logger.debug(responseStr)
50- return objectMapper.readValue(responseStr, responseType)
83+ return responseStr
84+ }
85+
86+ protected open fun <T > executeRequest (httpRequest : Request , responseType : Class <T >): T {
87+ val str = executeRequest(httpRequest)
88+ return objectMapper.readValue(str, responseType)
5189 }
5290
5391 private fun <T > streamResponses (
@@ -145,9 +183,39 @@ open class OpenAIImpl @ApiStatus.Internal constructor(
145183 return executeRequest(httpRequest, EmbeddingsResponse ::class .java)
146184 }
147185
186+ override fun listFiles (request : ListFilesRequest ): ListFilesResponse {
187+ val httpRequest = buildRequestNoBody(FILES_ENDPOINT , request.toMap()).get().build()
188+ return executeRequest(httpRequest, ListFilesResponse ::class .java)
189+ }
190+
191+ override fun uploadFile (request : FileUploadRequest ): FileObject {
192+ val httpRequest = buildMultipartRequest(FILES_ENDPOINT ) {
193+ addFormDataPart(" purpose" , OpenAI .createObjectMapper().writeValueAsString(request.purpose).trim(' "' ))
194+ addFormDataPart(" file" , request.fileName, request.requestBody)
195+ }
196+ return executeRequest(httpRequest, FileObject ::class .java)
197+ }
198+
199+ override fun deleteFile (fileId : String ): FileDeletionStatus {
200+ val httpRequest = buildRequestNoBody(" $FILES_ENDPOINT /$fileId " ).delete().build()
201+ return executeRequest(httpRequest, FileDeletionStatus ::class .java)
202+ }
203+
204+ override fun retrieveFile (fileId : String ): FileObject {
205+ val httpRequest = buildRequestNoBody(" $FILES_ENDPOINT /$fileId " ).get().build()
206+ return executeRequest(httpRequest, FileObject ::class .java)
207+ }
208+
209+ override fun retrieveFileContents (fileId : String ): String {
210+ val httpRequest = buildRequestNoBody(" $FILES_ENDPOINT /$fileId /content" ).get().build()
211+ return executeRequest(httpRequest)
212+ }
213+
214+
148215 companion object {
149216 const val COMPLETIONS_ENDPOINT = " v1/completions"
150217 const val CHAT_ENDPOINT = " v1/chat/completions"
151218 const val EMBEDDINGS_ENDPOINT = " v1/embeddings"
219+ const val FILES_ENDPOINT = " v1/files"
152220 }
153221}
0 commit comments