Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 94b0711

Browse files
abstract out common list request logic
1 parent bb8502e commit 94b0711

File tree

3 files changed

+202
-0
lines changed

3 files changed

+202
-0
lines changed
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package com.cjcrafter.openai
2+
3+
import com.cjcrafter.openai.assistants.Assistant
4+
5+
/**
6+
* An abstract builder type for list requests. Many objects stored by the OpenAI
7+
* API are stored in lists. This abstract request stores the data that each request
8+
* has in common.
9+
*/
10+
abstract class AbstractListRequestBuilder<T> {
11+
12+
protected var limit: Int? = null
13+
protected var order: ListOrder? = null
14+
protected var after: String? = null
15+
protected var before: String? = null
16+
17+
/**
18+
* The maximum number of results to return. This value must be between
19+
* 1 and 100 (inclusive).
20+
*
21+
* @param limit The maximum number of results to return
22+
* @throws IllegalArgumentException If the limit is not between 1 and 100
23+
*/
24+
fun limit(limit: Int?) = apply {
25+
if (limit != null && (limit < 1 || limit > 100))
26+
throw IllegalArgumentException("Limit must be between 1 and 100")
27+
this.limit = limit
28+
}
29+
30+
/**
31+
* How the returned list should be ordered. If not specified, the default
32+
* value is [ListOrder.DESCENDING]. Use the [ascending] and [descending]
33+
* methods.
34+
*
35+
* @param order The order to return the list in
36+
*/
37+
fun order(order: ListOrder?) = apply { this.order = order }
38+
39+
/**
40+
* A cursor for use in pagination. `after` is an object ID that defines
41+
* your place in the list. For instance, if you make a list request and
42+
* receive 100 objects, ending with `"obj_foo"`, your subsequent call
43+
* can include `after="obj_foo"` in order to fetch the next page of the
44+
* list.
45+
*
46+
* @param after The cursor to use for pagination
47+
*/
48+
fun after(after: String?) = apply { this.after = after }
49+
50+
/**
51+
* A cursor for use in pagination. `after` is an object ID that defines
52+
* your place in the list. For instance, if you make a list request and
53+
* receive 100 objects, ending with `"obj_foo"`, your subsequent call
54+
* can include `after="obj_foo"` in order to fetch the next page of the
55+
* list.
56+
*
57+
* @param after The cursor to use for pagination
58+
*/
59+
fun after(after: Assistant) = apply { this.after = after.id }
60+
61+
/**
62+
* A cursor for use in pagination. `before` is an object ID that defines
63+
* your place in the list. For instance, if you make a list request and
64+
* receive 100 objects, ending with `"obj_foo"`, your subsequent call can
65+
* include `before="obj_foo"` in order to fetch the previous page of the
66+
* list.
67+
*
68+
* @param before The cursor to use for pagination
69+
*/
70+
fun before(before: String?) = apply { this.before = before }
71+
72+
/**
73+
* A cursor for use in pagination. `before` is an object ID that defines
74+
* your place in the list. For instance, if you make a list request and
75+
* receive 100 objects, ending with `"obj_foo"`, your subsequent call can
76+
* include `before="obj_foo"` in order to fetch the previous page of the
77+
* list.
78+
*
79+
* @param before The cursor to use for pagination
80+
*/
81+
fun before(before: Assistant) = apply { this.before = before.id }
82+
83+
/**
84+
* Sets the order to [ListOrder.ASCENDING].
85+
*/
86+
fun ascending() = apply { this.order = ListOrder.ASCENDING }
87+
88+
/**
89+
* Sets the order to [ListOrder.DESCENDING].
90+
*/
91+
fun descending() = apply { this.order = ListOrder.DESCENDING }
92+
93+
abstract fun build(): T
94+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.cjcrafter.openai
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty
4+
5+
/**
6+
* Represents the order for a list, sorted by time created. In general, since
7+
* you probably want the most recent objects from the OpenAI API, you should
8+
* use [DESCENDING] (which is the default value for all requests).
9+
*
10+
* @property jsonProperty How each enum is represented as raw json string.
11+
*/
12+
enum class ListOrder(val jsonProperty: String) {
13+
14+
/**
15+
* Ascending order. Objects created a long time ago are ordered before
16+
* objects created more recently.
17+
*/
18+
@JsonProperty("asc")
19+
ASCENDING(jsonProperty = "asc"),
20+
21+
/**
22+
* Descending order. Objects created more recently are ordered before
23+
* objects created a long time ago. This is the default value for list
24+
* requests.
25+
*/
26+
@JsonProperty("desc")
27+
DESCENDING(jsonProperty = "desc"),
28+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package com.cjcrafter.openai
2+
3+
import okhttp3.HttpUrl.Companion.toHttpUrl
4+
import okhttp3.MediaType.Companion.toMediaType
5+
import okhttp3.MultipartBody
6+
import okhttp3.OkHttpClient
7+
import okhttp3.Request
8+
import okhttp3.RequestBody
9+
import okhttp3.RequestBody.Companion.toRequestBody
10+
import java.io.IOException
11+
12+
open class RequestHelper(
13+
protected val apiKey: String,
14+
protected val organization: String? = null,
15+
protected val client: OkHttpClient = OkHttpClient(),
16+
protected val baseUrl: String = "https://api.openai.com",
17+
) {
18+
protected val mediaType = "application/json; charset=utf-8".toMediaType()
19+
protected val objectMapper = OpenAI.createObjectMapper()
20+
21+
open fun buildRequest(request: Any, endpoint: String): Request.Builder {
22+
val json = objectMapper.writeValueAsString(request)
23+
val body: RequestBody = json.toRequestBody(mediaType)
24+
return Request.Builder()
25+
.url("$baseUrl/$endpoint")
26+
.addHeader("Content-Type", "application/json")
27+
.addHeader("Authorization", "Bearer $apiKey")
28+
.apply { if (organization != null) addHeader("OpenAI-Organization", organization) }
29+
.post(body)
30+
}
31+
32+
open fun buildRequestNoBody(endpoint: String, params: Map<String, Any>? = null): Request.Builder {
33+
val url = "$baseUrl/$endpoint".toHttpUrl().newBuilder()
34+
.apply {
35+
params?.forEach { (key, value) -> addQueryParameter(key, value.toString()) }
36+
}.build().toString()
37+
38+
return Request.Builder()
39+
.url(url)
40+
.addHeader("Authorization", "Bearer $apiKey")
41+
.apply { if (organization != null) addHeader("OpenAI-Organization", organization) }
42+
}
43+
44+
open fun buildMultipartRequest(
45+
endpoint: String,
46+
function: MultipartBody.Builder.() -> Unit,
47+
): Request {
48+
49+
val multipartBody = MultipartBody.Builder()
50+
.setType(MultipartBody.FORM)
51+
.apply(function)
52+
.build()
53+
54+
return Request.Builder()
55+
.url("$baseUrl/$endpoint")
56+
.addHeader("Authorization", "Bearer $apiKey")
57+
.apply { if (organization != null) addHeader("OpenAI-Organization", organization) }
58+
.post(multipartBody).build()
59+
}
60+
61+
open fun executeRequest(httpRequest: Request): String {
62+
val httpResponse = client.newCall(httpRequest).execute()
63+
if (!httpResponse.isSuccessful) {
64+
val json = httpResponse.body?.byteStream()?.bufferedReader()?.readText()
65+
httpResponse.close()
66+
throw IOException("Unexpected code $httpResponse, received: $json")
67+
}
68+
69+
val jsonReader = httpResponse.body?.byteStream()?.bufferedReader()
70+
?: throw IOException("Response body is null")
71+
val responseStr = jsonReader.readText()
72+
OpenAI.logger.debug(responseStr)
73+
return responseStr
74+
}
75+
76+
open fun <T> executeRequest(httpRequest: Request, responseType: Class<T>): T {
77+
val str = executeRequest(httpRequest)
78+
return objectMapper.readValue(str, responseType)
79+
}
80+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /